diff --git a/platformio.ini b/platformio.ini index bdd1757b7..d719875f1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -71,8 +71,7 @@ lib_deps = adafruit/Adafruit TouchScreen adafruit/Adafruit MQTT Library bblanchon/ArduinoJson - https://github.com/PaulStoffregen/OneWire.git - https://github.com/milesburton/Arduino-Temperature-Control-Library.git + https://github.com/pstolarz/OneWireNg.git https://github.com/Sensirion/arduino-sht.git https://github.com/Sensirion/arduino-i2c-scd4x.git https://github.com/Sensirion/arduino-i2c-sen5x.git @@ -180,7 +179,8 @@ extends = common:esp32 board = adafruit_feather_esp32s3 build_flags = -DARDUINO_ADAFRUIT_FEATHER_ESP32S3 -DBOARD_HAS_PSRAM ;set partition to tinyuf2-partitions-4MB.csv as of idf 5.1 -board_build.partitions = tinyuf2-partitions-4MB.csv +; board_build.partitions = tinyuf2-partitions-4MB.csv +board_build.partitions = tinyuf2-partitions-4MB-noota.csv extra_scripts = pre:rename_usb_config.py ; Adafruit Feather ESP32-S3 NO PSRAM diff --git a/src/Wippersnapper.cpp b/src/Wippersnapper.cpp index 90c443a8f..e34d893a8 100644 --- a/src/Wippersnapper.cpp +++ b/src/Wippersnapper.cpp @@ -66,9 +66,6 @@ Wippersnapper::Wippersnapper() { // UART WS._uartComponent = new ws_uart(); - - // DallasSemi (OneWire) - WS._ds18x20Component = new ws_ds18x20(); }; /**************************************************************************/ @@ -1319,43 +1316,6 @@ void cbPWMMsg(char *data, uint16_t len) { bool cbDecodeDs18x20Msg(pb_istream_t *stream, const pb_field_t *field, void **arg) { (void)arg; // marking unused parameter to avoid compiler warning - if (field->tag == - wippersnapper_signal_v1_Ds18x20Request_req_ds18x20_init_tag) { - WS_DEBUG_PRINTLN("[Message Type] Init. DS Sensor"); - // Attempt to decode contents of DS18x20 message - wippersnapper_ds18x20_v1_Ds18x20InitRequest msgDS18xInitReq = - wippersnapper_ds18x20_v1_Ds18x20InitRequest_init_zero; - - if (!ws_pb_decode(stream, - wippersnapper_ds18x20_v1_Ds18x20InitRequest_fields, - &msgDS18xInitReq)) { - WS_DEBUG_PRINTLN("ERROR: Could not decode " - "wippersnapper_ds18x20_v1_Ds18x20InitRequest"); - return false; // fail out if we can't decode the request - } - WS_DEBUG_PRINT("Adding DS18x20 Component..."); - if (!WS._ds18x20Component->addDS18x20(&msgDS18xInitReq)) - return false; - WS_DEBUG_PRINTLN("Added!"); - } else if (field->tag == - wippersnapper_signal_v1_Ds18x20Request_req_ds18x20_deinit_tag) { - WS_DEBUG_PRINTLN("[Message Type] De-init. DS Sensor"); - // Attempt to decode contents of message - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest msgDS18xDeInitReq = - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_init_zero; - if (!ws_pb_decode(stream, - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest_fields, - &msgDS18xDeInitReq)) { - WS_DEBUG_PRINTLN("ERROR: Could not decode " - "wippersnapper_ds18x20_v1_Ds18x20DeInitRequest"); - return false; // fail out if we can't decode the request - } - // exec. deinit request - WS._ds18x20Component->deleteDS18x20(&msgDS18xDeInitReq); - } else { - WS_DEBUG_PRINTLN("ERROR: DS Message type not found!"); - return false; - } return true; } @@ -2876,10 +2836,6 @@ ws_status_t Wippersnapper::run() { WS._i2cPort0->update(); WS.feedWDT(); - // Process DS18x20 sensor events - WS._ds18x20Component->update(); - WS.feedWDT(); - // Process UART sensor events WS._uartComponent->update(); WS.feedWDT(); diff --git a/src/Wippersnapper.h b/src/Wippersnapper.h index 7f4ae8790..a79beebd4 100644 --- a/src/Wippersnapper.h +++ b/src/Wippersnapper.h @@ -64,7 +64,6 @@ #include "display/ws_display_ui_helper.h" #endif -#include "components/ds18x20/ws_ds18x20.h" #include "components/pixels/ws_pixels.h" #include "components/pwm/ws_pwm.h" #include "components/servo/ws_servo.h" @@ -104,7 +103,6 @@ class ws_ledc; class WipperSnapper_Component_I2C; class ws_servo; class ws_pwm; -class ws_ds18x20; class ws_pixels; class ws_uart; @@ -227,7 +225,6 @@ class Wippersnapper { ws_pixels *_ws_pixelsComponent; ///< ptr to instance of ws_pixels class ws_pwm *_pwmComponent; ///< Instance of pwm class ws_servo *_servoComponent; ///< Instance of servo class - ws_ds18x20 *_ds18x20Component; ///< Instance of DS18x20 class ws_uart *_uartComponent; ///< Instance of UART class // TODO: does this really need to be global? diff --git a/src/Wippersnapper_V2.cpp b/src/Wippersnapper_V2.cpp index 83b7a16a1..f1763d649 100644 --- a/src/Wippersnapper_V2.cpp +++ b/src/Wippersnapper_V2.cpp @@ -65,15 +65,13 @@ Wippersnapper_V2::Wippersnapper_V2() { // UART WsV2._uartComponentV2 = new ws_uart(); - // DallasSemi (OneWire) - WsV2._ds18x20ComponentV2 = new ws_ds18x20(); - // Initialize model classes WsV2.sensorModel = new SensorModel(); // Initialize controller classes WsV2.digital_io_controller = new DigitalIOController(); WsV2.analogio_controller = new AnalogIOController(); + WsV2._ds18x20_controller = new DS18X20Controller(); }; /**************************************************************************/ @@ -374,6 +372,20 @@ bool cbDecodeBrokerToDevice(pb_istream_t *stream, const pb_field_t *field, return false; } break; + case wippersnapper_signal_BrokerToDevice_ds18x20_add_tag: + WS_DEBUG_PRINTLN("-> DS18X20 Add Message Type"); + if (!WsV2._ds18x20_controller->Handle_Ds18x20Add(stream)) { + WS_DEBUG_PRINTLN("ERROR: Unable to add DS18X20 sensor!"); + return false; + } + break; + case wippersnapper_signal_BrokerToDevice_ds18x20_remove_tag: + WS_DEBUG_PRINTLN("-> DS18X20 Remove Message Type"); + if (!WsV2._ds18x20_controller->Handle_Ds18x20Remove(stream)) { + WS_DEBUG_PRINTLN("ERROR: Unable to remove DS18X20 sensor!"); + return false; + } + break; default: WS_DEBUG_PRINTLN("ERROR: BrokerToDevice message type not found!"); return false; @@ -857,33 +869,53 @@ void Wippersnapper_V2::haltErrorV2(String error, */ /**************************************************************************/ bool Wippersnapper_V2::PublishSignal(pb_size_t which_payload, void *payload) { + + #ifdef DEBUG_PROFILE + unsigned long total_start_time = micros(); + #endif + size_t szMessageBuf; wippersnapper_signal_DeviceToBroker MsgSignal = wippersnapper_signal_DeviceToBroker_init_default; + // Fill generic signal payload with the payload from the args. WS_DEBUG_PRINT("Signal Payload Type: "); switch (which_payload) { case wippersnapper_signal_DeviceToBroker_checkin_request_tag: - WS_DEBUG_PRINTLN("Checkin Request"); + WS_DEBUG_PRINTLN("CheckinRequest"); MsgSignal.which_payload = wippersnapper_signal_DeviceToBroker_checkin_request_tag; MsgSignal.payload.checkin_request = *(wippersnapper_checkin_CheckinRequest *)payload; break; case wippersnapper_signal_DeviceToBroker_digitalio_event_tag: - WS_DEBUG_PRINTLN("DigitalIO Event"); + WS_DEBUG_PRINTLN("DigitalIOEvent"); MsgSignal.which_payload = wippersnapper_signal_DeviceToBroker_digitalio_event_tag; MsgSignal.payload.digitalio_event = *(wippersnapper_digitalio_DigitalIOEvent *)payload; break; case wippersnapper_signal_DeviceToBroker_analogio_event_tag: - WS_DEBUG_PRINTLN("AnalogIO Event"); + WS_DEBUG_PRINTLN("AnalogIOEvent"); MsgSignal.which_payload = wippersnapper_signal_DeviceToBroker_analogio_event_tag; MsgSignal.payload.analogio_event = *(wippersnapper_analogio_AnalogIOEvent *)payload; break; + case wippersnapper_signal_DeviceToBroker_ds18x20_added_tag: + WS_DEBUG_PRINTLN("DS18X20Added"); + MsgSignal.which_payload = + wippersnapper_signal_DeviceToBroker_ds18x20_added_tag; + MsgSignal.payload.ds18x20_added = + *(wippersnapper_ds18x20_Ds18x20Added *)payload; + break; + case wippersnapper_signal_DeviceToBroker_ds18x20_event_tag: + WS_DEBUG_PRINTLN("DS18X20Event"); + MsgSignal.which_payload = + wippersnapper_signal_DeviceToBroker_ds18x20_event_tag; + MsgSignal.payload.ds18x20_event = + *(wippersnapper_ds18x20_Ds18x20Event *)payload; + break; default: WS_DEBUG_PRINTLN("ERROR: Invalid signal payload type, bailing out!"); return false; @@ -910,20 +942,37 @@ bool Wippersnapper_V2::PublishSignal(pb_size_t which_payload, void *payload) { "ERROR: Unable to encode d2b signal message, bailing out!"); return false; } - WS_DEBUG_PRINTLN("Encoded!") + WS_DEBUG_PRINTLN("Encoded!"); - //. Check that we are still connected + // Check that we are still connected runNetFSMV2(); WsV2.feedWDTV2(); + #ifdef DEBUG_PROFILE + unsigned long publish_start_time = micros(); + #endif + // Attempt to publish the signal message to the broker WS_DEBUG_PRINT("Publishing signal message to broker..."); + #ifdef DEBUG_PROFILE + WS_DEBUG_PRINT("Message buffer size: "); + WS_DEBUG_PRINTLN(szMessageBuf); + #endif if (!WsV2._mqttV2->publish(WsV2._topicD2b, msgBuf, szMessageBuf, 1)) { WS_DEBUG_PRINTLN("ERROR: Failed to publish signal message to broker!"); return false; } WS_DEBUG_PRINTLN("Published!"); + #ifdef DEBUG_PROFILE + unsigned long publish_end_time = micros(); + WS_DEBUG_PRINT("Publishing time: "); + WS_DEBUG_PRINTLN(publish_end_time - publish_start_time); + unsigned long total_end_time = micros(); + WS_DEBUG_PRINT("Total PublishSignal() execution time: "); + WS_DEBUG_PRINTLN(total_end_time - total_start_time); + #endif + return true; } @@ -1167,6 +1216,9 @@ ws_status_t Wippersnapper_V2::runV2() { // Process all analog inputs WsV2.analogio_controller->update(); + // Process all DS18x20 sensor events + WsV2._ds18x20_controller->update(); + // TODO: Process I2C sensor events // TODO: Process DS18x20 sensor events diff --git a/src/Wippersnapper_V2.h b/src/Wippersnapper_V2.h index 7b2291f76..a65d34698 100644 --- a/src/Wippersnapper_V2.h +++ b/src/Wippersnapper_V2.h @@ -32,6 +32,7 @@ #include "protos/checkin.pb.h" #include "protos/digitalio.pb.h" #include "protos/signal.pb.h" +#include "protos/ds18x20.pb.h" // External libraries #include "Adafruit_MQTT.h" // MQTT Client @@ -48,15 +49,15 @@ #endif // Components (API v2) -#include "components/checkin/model.h" #include "components/sensor/model.h" +#include "components/checkin/model.h" #include "components/digitalIO/controller.h" #include "components/analogio/controller.h" +#include "components/ds18x20/controller.h" // Components (API v1) #include "components/analogIO/Wippersnapper_AnalogIO.h" -#include "components/ds18x20/ws_ds18x20.h" #include "components/i2c/WipperSnapper_I2C.h" #include "components/pixels/ws_pixels.h" #include "components/pwm/ws_pwm.h" @@ -71,8 +72,6 @@ #include "display/ws_display_ui_helper.h" #endif - - #include "provisioning/ConfigJson.h" #if defined(USE_TINYUSB) #include "provisioning/tinyusb/Wippersnapper_FS_V2.h" @@ -81,6 +80,10 @@ #include "provisioning/littlefs/WipperSnapper_LittleFS.h" #endif +// Debug print macros +// Uncomment the following to enable debug output for function profiling +// #DEBUG_PROFILE 1 + #define WS_VERSION \ "2.0.0-alpha.1" ///< WipperSnapper app. version (semver-formatted) @@ -90,9 +93,6 @@ #define WS_KEEPALIVE_INTERVAL_MS \ 5000 ///< Session keepalive interval time, in milliseconds -#define WS_MQTT_MAX_PAYLOAD_SIZE \ - 512 ///< MAXIMUM expected payload size, in bytes - // Forward declarations (API v1) class Wippersnapper_AnalogIO; class Wippersnapper_FS_V2; @@ -107,7 +107,6 @@ class ws_ledc; class WipperSnapper_Component_I2C; class ws_servo; class ws_pwm; -class ws_ds18x20; class ws_pixels; class ws_uart; @@ -116,6 +115,7 @@ class CheckinModel; class SensorModel; class DigitalIOController; class AnalogIOController; +class DS18X20Controller; /**************************************************************************/ /*! @@ -214,7 +214,6 @@ class Wippersnapper_V2 { ws_pixels *_ws_pixelsComponentV2; ///< ptr to instance of ws_pixels class ws_pwm *_pwmComponentV2; ///< Instance of pwm class ws_servo *_servoComponentV2; ///< Instance of servo class - ws_ds18x20 *_ds18x20ComponentV2; ///< Instance of DS18x20 class ws_uart *_uartComponentV2; ///< Instance of UART class // API v2 Components @@ -223,6 +222,7 @@ class Wippersnapper_V2 { DigitalIOController *digital_io_controller; ///< Instance of DigitalIO controller class AnalogIOController *analogio_controller; ///< Instance of AnalogIO controller + DS18X20Controller *_ds18x20_controller; ///< Instance of DS18X20 controller // TODO: does this really need to be global? uint8_t _macAddrV2[6]; /*!< Unique network iface identifier */ diff --git a/src/components/digitalIO/model.cpp b/src/components/digitalIO/model.cpp index 49ce105d7..6643b1c49 100644 --- a/src/components/digitalIO/model.cpp +++ b/src/components/digitalIO/model.cpp @@ -46,6 +46,8 @@ wippersnapper_digitalio_DigitalIOAdd *DigitalIOModel::GetDigitalIOAddMsg() { /***********************************************************************/ /*! @brief Parses a DigitalIORemove message. + @param stream + The nanopb input stream. @return DigitalIORemove message object. */ /***********************************************************************/ diff --git a/src/components/ds18x20/controller.cpp b/src/components/ds18x20/controller.cpp new file mode 100644 index 000000000..f24360695 --- /dev/null +++ b/src/components/ds18x20/controller.cpp @@ -0,0 +1,265 @@ +/*! + * @file controller.cpp + * + * Controller for the ds18x20.proto API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "controller.h" + +/***********************************************************************/ +/*! + @brief DS18X20Controller constructor +*/ +/***********************************************************************/ +DS18X20Controller::DS18X20Controller() { _DS18X20_model = new DS18X20Model(); } + +/***********************************************************************/ +/*! + @brief DS18X20Controller destructor +*/ +/***********************************************************************/ +DS18X20Controller::~DS18X20Controller() { delete _DS18X20_model; } + +/***********************************************************************/ +/*! + @brief Handles a Ds18x20Add message from the broker. Attempts to + initialize a OneWire bus on the requested pin, attempts to + initialize a DSTherm driver on the OneWire bus, adds the + OneWire bus to the controller, and publishes a Ds18x20Added + message to the broker indicating the result of this function. + @param stream + The nanopb input stream. + @return True if the sensor was successfully initialized, + added to the controller, and a response was succesfully + published to the broker. False otherwise. +*/ +/***********************************************************************/ +bool DS18X20Controller::Handle_Ds18x20Add(pb_istream_t *stream) { + // Attempt to decode the incoming message into a Ds18x20Add message + if (!_DS18X20_model->DecodeDS18x20Add(stream)) { + WS_DEBUG_PRINTLN("ERROR | DS18x20: Unable to decode Ds18x20Add message"); + return false; + } + + // If we receive no sensor types to configure, bail out + if (_DS18X20_model->GetDS18x20AddMsg()->sensor_types_count == 0) { + WS_DEBUG_PRINTLN("ERROR | DS18x20: No ds18x sensor types provided!"); + return false; + } + + // Extract the OneWire pin from the message + uint8_t pin_name = atoi(_DS18X20_model->GetDS18x20AddMsg()->onewire_pin + 1); + + // Initialize the DS18X20Hardware object + auto new_dsx_driver = std::make_unique(pin_name); + // Attempt to get the sensor's ID on the OneWire bus to show it's been init'd + bool is_initialized = new_dsx_driver->GetSensor(); + + if (is_initialized) { + WS_DEBUG_PRINTLN("Sensor found on OneWire bus and initialized"); + + // Set the sensor's pin name (non-logical name) + new_dsx_driver->setOneWirePinName( + _DS18X20_model->GetDS18x20AddMsg()->onewire_pin); + + // Set the sensor's resolution + new_dsx_driver->SetResolution( + _DS18X20_model->GetDS18x20AddMsg()->sensor_resolution); + + // Set the sensor's period + new_dsx_driver->SetPeriod(_DS18X20_model->GetDS18x20AddMsg()->period); + + // Configure the types of sensor reads to perform + for (int i = 0; i < _DS18X20_model->GetDS18x20AddMsg()->sensor_types_count; + i++) { + if (_DS18X20_model->GetDS18x20AddMsg()->sensor_types[i] == + wippersnapper_sensor_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE) { + new_dsx_driver->is_read_temp_c = true; + } else if ( + _DS18X20_model->GetDS18x20AddMsg()->sensor_types[i] == + wippersnapper_sensor_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE_FAHRENHEIT) { + new_dsx_driver->is_read_temp_f = true; + } else { + WS_DEBUG_PRINTLN( + "ERROR | DS18x20: Unknown SensorType, failed to add sensor!"); + is_initialized = false; + } + } + + // If the sensor was successfully initialized, add it to the controller + if (is_initialized == true) + _DS18X20_pins.push_back(std::move(new_dsx_driver)); + } else { + WS_DEBUG_PRINTLN("ERROR | DS18x20: Unable to get sensor ID!"); + is_initialized = false; + } + + // Encode and publish a Ds18x20Added message back to the broker + unsigned long encode_start_time = millis(); + if (!_DS18X20_model->EncodeDS18x20Added( + _DS18X20_model->GetDS18x20AddMsg()->onewire_pin, is_initialized)) { + WS_DEBUG_PRINTLN("ERROR | DS18x20: Unable to encode Ds18x20Added message!"); + return false; + } + + if (!WsV2.PublishSignal(wippersnapper_signal_DeviceToBroker_ds18x20_added_tag, + _DS18X20_model->GetDS18x20AddedMsg())) { + WS_DEBUG_PRINTLN( + "ERROR | DS18x20: Unable to publish Ds18x20Added message!"); + return false; + } + + return true; +} + +/***********************************************************************/ +/*! + @brief Handles a Ds18x20Remove message from the broker. Attempts to + remove a DS18X20Hardware object from the controller and + release the OneWire pin for other uses. + @param stream + The nanopb input stream. + @return True if the sensor was successfully removed from the + controller, False otherwise. +*/ +/***********************************************************************/ +bool DS18X20Controller::Handle_Ds18x20Remove(pb_istream_t *stream) { + WS_DEBUG_PRINT("Removing DS18X20 sensor..."); + // Attempt to decode the stream + if (!_DS18X20_model->DecodeDS18x20Remove(stream)) { + WS_DEBUG_PRINTLN("ERROR | DS18x20: Unable to decode Ds18x20Remove message"); + return false; + } + // Create a temp. instance of the Ds18x20Remove message + wippersnapper_ds18x20_Ds18x20Remove *msg_remove = + _DS18X20_model->GetDS18x20RemoveMsg(); + uint8_t pin_name = atoi(msg_remove->onewire_pin + 1); + + // Find the driver/bus in the vector and remove it + for (size_t i = 0; i < _DS18X20_pins.size(); ++i) { + if (_DS18X20_pins[i]->GetOneWirePin() == pin_name) { + _DS18X20_pins.erase(_DS18X20_pins.begin() + i); + return true; + } + } + WS_DEBUG_PRINTLN("Removed!"); + return true; +} + +/***********************************************************************/ +/*! + @brief Update/polling loop for the DS18X20 controller. +*/ +/***********************************************************************/ +void DS18X20Controller::update() { +#ifdef DEBUG_PROFILE + unsigned long total_start_time = millis(); +#endif + + // Bail out if there are no OneWire pins to poll + if (_DS18X20_pins.empty()) + return; + + // Iterate through the vector + for (uint8_t i = 0; i < _DS18X20_pins.size(); i++) { +#ifdef DEBUG_PROFILE + unsigned long sensor_start_time = millis(); +#endif + + // Create a reference to the DS18X20Hardware object + DS18X20Hardware &temp_dsx_driver = *(_DS18X20_pins[i]); + + // Check if the driver's timer has not expired yet + if (!temp_dsx_driver.IsTimerExpired()) { + continue; + } + +// Attempt to read the temperature in Celsius +#ifdef DEBUG_PROFILE + unsigned long temp_c_start_time = millis(); +#endif + + if (!temp_dsx_driver.ReadTemperatureC()) { + WS_DEBUG_PRINTLN( + "ERROR | DS18x20: Unable to read temperature in Celsius"); + continue; + } + +#ifdef DEBUG_PROFILE + unsigned long temp_c_end_time = millis(); + WS_DEBUG_PRINT("Read temperature Celsius time: "); + WS_DEBUG_PRINTLN(temp_c_end_time - temp_c_start_time); +#endif + + // We got a temperature value from the hardware, let's create a new + // sensor_event + _DS18X20_model->InitDS18x20EventMsg(temp_dsx_driver.getOneWirePinName()); + + // Are we reading the temperature in Celsius, Fahrenheit, or both? + if (temp_dsx_driver.is_read_temp_c) { + float temp_c = temp_dsx_driver.GetTemperatureC(); + _DS18X20_model->addSensorEvent( + wippersnapper_sensor_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE, + temp_c); + } + if (temp_dsx_driver.is_read_temp_f) { + float temp_f = temp_dsx_driver.GetTemperatureF(); + _DS18X20_model->addSensorEvent( + wippersnapper_sensor_SensorType_SENSOR_TYPE_OBJECT_TEMPERATURE_FAHRENHEIT, + temp_f); + } + + // Get the Ds18x20Event message + wippersnapper_ds18x20_Ds18x20Event *event_msg = + _DS18X20_model->GetDS18x20EventMsg(); + pb_size_t event_count = event_msg->sensor_events_count; + + // Print the message's content out for debugging + WS_DEBUG_PRINT("DS18x20: OneWire pin: "); + WS_DEBUG_PRINT(temp_dsx_driver.GetOneWirePin()); + WS_DEBUG_PRINT(" got value(s): "); + for (int i = 0; i < event_count; i++) { + WS_DEBUG_PRINT(event_msg->sensor_events[i].value.float_value); + } + + // Encode the Ds18x20Event message + if (!_DS18X20_model->EncodeDs18x20Event()) { + WS_DEBUG_PRINTLN( + "ERROR | DS18x20: Failed to encode Ds18x20Event message"); + continue; + } + + // Publish the Ds18x20Event message to the broker + WS_DEBUG_PRINT("DS18x20: Publishing event to broker..."); + if (!WsV2.PublishSignal( + wippersnapper_signal_DeviceToBroker_ds18x20_event_tag, + _DS18X20_model->GetDS18x20EventMsg())) { + WS_DEBUG_PRINTLN( + "ERROR | DS18x20: Failed to publish Ds18x20Event message"); + continue; + } + WS_DEBUG_PRINTLN("Published!"); + +#ifdef DEBUG_PROFILE + unsigned long sensor_end_time = millis(); + WS_DEBUG_PRINT("Total sensor processing time: "); + WS_DEBUG_PRINTLN(sensor_end_time - sensor_start_time); +#endif + } + +#ifdef DEBUG_PROFILE + unsigned long total_end_time = millis(); + if (total_end_time - total_start_time != 0) { + WS_DEBUG_PRINT("Total update() execution time: "); + WS_DEBUG_PRINTLN(total_end_time - total_start_time); + } +#endif +} diff --git a/src/components/ds18x20/controller.h b/src/components/ds18x20/controller.h new file mode 100644 index 000000000..a77182f47 --- /dev/null +++ b/src/components/ds18x20/controller.h @@ -0,0 +1,46 @@ +/*! + * @file controller.h + * + * Controller for the DS18X20 API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_DS18X20_CONTROLLER_H +#define WS_DS18X20_CONTROLLER_H +#include "Wippersnapper_V2.h" +#include "hardware.h" +#include "model.h" +#include + +class Wippersnapper_V2; ///< Forward declaration +class DS18X20Model; ///< Forward declaration +class DS18X20Hardware; ///< Forward declaration + +/**************************************************************************/ +/*! + @brief Routes messages between the ds18x20.proto API and the hardware. +*/ +/**************************************************************************/ +class DS18X20Controller { +public: + DS18X20Controller(); + ~DS18X20Controller(); + // Routing + bool Handle_Ds18x20Add(pb_istream_t *stream); + bool Handle_Ds18x20Remove(pb_istream_t *stream); + // Polling + void update(); + +private: + DS18X20Model *_DS18X20_model; ///< ds18x20 model + std::vector> _DS18X20_pins; +}; +extern Wippersnapper_V2 WsV2; ///< Wippersnapper V2 instance +#endif // WS_DS18X20_CONTROLLER_H \ No newline at end of file diff --git a/src/components/ds18x20/hardware.cpp b/src/components/ds18x20/hardware.cpp new file mode 100644 index 000000000..2ee871d5c --- /dev/null +++ b/src/components/ds18x20/hardware.cpp @@ -0,0 +1,197 @@ +/*! + * @file hardware.cpp + * + * Hardware interface for the ds18x20.proto API + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "hardware.h" + +/***********************************************************************/ +/*! + @brief DS18X20Hardware constructor + @param onewire_pin + The OneWire bus pin to use. +*/ +/***********************************************************************/ +DS18X20Hardware::DS18X20Hardware(uint8_t onewire_pin) : _drv_therm(_ow) { + is_read_temp_c = false; + is_read_temp_f = false; + // Initialize the OneWire bus object + _onewire_pin = onewire_pin; + new (&_ow) OneWireNg_CurrentPlatform(onewire_pin, false); +} + +/***********************************************************************/ +/*! + @brief DS18X20Hardware destructor +*/ +/***********************************************************************/ +DS18X20Hardware::~DS18X20Hardware() { + pinMode(_onewire_pin, + INPUT); // Set the pin to hi-z and release it for other uses +} + +/***********************************************************************/ +/*! + @brief Get the sensor's ID + @returns True if the sensor was successfully identified, False otherwise. +*/ +/***********************************************************************/ +bool DS18X20Hardware::GetSensor() { + OneWireNg::ErrorCode ec = _ow->readSingleId(_sensorId); + return ec == OneWireNg::EC_SUCCESS; +} + +/***********************************************************************/ +/*! + @brief Gets the pin used as a OneWire bus. + @returns The OneWire bus pin. +*/ +/***********************************************************************/ +uint8_t DS18X20Hardware::GetOneWirePin() { return _onewire_pin; } + +/***********************************************************************/ +/*! + @brief Sets the name of the OneWire bus pin. + @param prettyOWPinName + The name of the OneWire bus pin (non-logical pin name, + includes the "D" or "A" prefix). +*/ +/***********************************************************************/ +void DS18X20Hardware::setOneWirePinName(const char *prettyOWPinName) { + strncpy(_onewire_pin_name, prettyOWPinName, sizeof(_onewire_pin_name)); + _onewire_pin_name[sizeof(_onewire_pin_name) - 1] = '\0'; +} + +/***********************************************************************/ +/*! + @brief Gets the name of the OneWire bus pin. + @returns The name of the OneWire bus pin (non-logical pin name, + includes the "D" or "A" prefix). +*/ +/***********************************************************************/ +const char *DS18X20Hardware::getOneWirePinName() { return _onewire_pin_name; } + +/*************************************************************************/ +/*! + @brief Sets the DS18X20 sensor's resolution. + @param resolution + The desired resolution of the DS18X20 sensor, in bits (from + 9 to 12). +*/ +/*************************************************************************/ +void DS18X20Hardware::SetResolution(int resolution) { + // Set the resolution of the DS18X20 sensor driver + switch (resolution) { + case 9: + _resolution = DSTherm::Resolution::RES_9_BIT; + break; + case 10: + _resolution = DSTherm::Resolution::RES_10_BIT; + break; + case 11: + _resolution = DSTherm::Resolution::RES_11_BIT; + break; + case 12: + _resolution = DSTherm::Resolution::RES_12_BIT; + break; + default: + _resolution = + DSTherm::Resolution::RES_12_BIT; // Default to 12-bit resolution + break; + } + + // Set common resolution for all sensors. + // Th, Tl (high/low alarm triggers) are set to 0. + _drv_therm.writeScratchpadAll(0, 0, _resolution); + + // The configuration above is stored in volatile sensors scratchpad + // memory and will be lost after power unplug. Therefore store the + // configuration permanently in sensors EEPROM. + _drv_therm.copyScratchpadAll(false); +} + +/*************************************************************************/ +/*! + @brief Sets the timer to read from the sensor. + @param period + The desired period to read the sensor, in seconds. +*/ +/*************************************************************************/ +void DS18X20Hardware::SetPeriod(float period) { + _period = period * 1000; // Convert to milliseconds + _prv_period = 0; // Also reset the previous period whenever we set a + // new period +} + +/*************************************************************************/ +/*! + @brief Obtains the current time in milliseconds and compares it to + the last time the sensor was polled. + @returns True if the timer has expired, False otherwise. +*/ +/*************************************************************************/ +bool DS18X20Hardware::IsTimerExpired() { + return millis() - _prv_period > _period; +} + +/*************************************************************************/ +/*! + @brief Gets the temperature value last read by the sensor, in Celsius. + @returns The temperature in Celsius. +*/ +/*************************************************************************/ +float DS18X20Hardware::GetTemperatureC() { return _temp_c; } + +/*************************************************************************/ +/*! + @brief Gets the temperature value last read by the sensor, in Fahrenheit. + @returns The temperature in Fahrenheit. +*/ +/*************************************************************************/ +float DS18X20Hardware::GetTemperatureF() { + _temp_f = _temp_c * 9.0 / 5.0 + 32.0; + return _temp_f; +} + +/*************************************************************************/ +/*! + @brief Attempts to obtain the temperature from the sensor, in + degrees Celsius. + @returns True if the temperature was successfully read, False otherwise. +*/ +/*************************************************************************/ +bool DS18X20Hardware::ReadTemperatureC() { + // Start temperature conversion for the first identified sensor on the OneWire + // bus + OneWireNg::ErrorCode ec = + _drv_therm.convertTemp(_sensorId, DSTherm::MAX_CONV_TIME, false); + + if (ec != OneWireNg::EC_SUCCESS) + return false; + + // Scratchpad placeholder is static to allow reuse of the associated + // sensor id while reissuing readScratchpadSingle() calls. + // Note, due to its storage class the placeholder is zero initialized. + static Placeholder scrpd; + ec = _drv_therm.readScratchpad(_sensorId, scrpd); + if (ec != OneWireNg::EC_SUCCESS) + return false; + + // Read the temperature from the sensor + // NOTE: The temperature returned by getTemp2() is the temperature * 16 + long temp = scrpd->getTemp2(); + // Divide the temperature from the getTemp2() call by 16 and assign as a float. + _temp_c = temp / 16.0; + + _prv_period = millis(); // Update the last time the sensor was polled + return true; +} \ No newline at end of file diff --git a/src/components/ds18x20/hardware.h b/src/components/ds18x20/hardware.h new file mode 100644 index 000000000..0fcb8288c --- /dev/null +++ b/src/components/ds18x20/hardware.h @@ -0,0 +1,61 @@ +/*! + * @file model.h + * + * Hardware implementation for the ds18x20.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_DS18X20_HARDWARE_H +#define WS_DS18X20_HARDWARE_H +#include "Wippersnapper_V2.h" + +#include "OneWireNg_CurrentPlatform.h" +#include "drivers/DSTherm.h" +#include "utils/Placeholder.h" + +/**************************************************************************/ +/*! + @brief Interface for interacting with the's DallasTemp + and OneWire APIs. +*/ +/**************************************************************************/ +class DS18X20Hardware { +public: + DS18X20Hardware(uint8_t onewire_pin); + ~DS18X20Hardware(); + uint8_t GetOneWirePin(); + void SetResolution(int resolution); + void SetPeriod(float period); + void setOneWirePinName(const char *prettyOWPinName); + const char *getOneWirePinName(); + bool IsTimerExpired(); + bool GetSensor(); + bool ReadTemperatureC(); + float GetTemperatureC(); + float GetTemperatureF(); + bool is_read_temp_c; ///< Flag telling the controller to read the temperature + ///< in degrees Celsius + bool is_read_temp_f; ///< Flag telling the controller to read the temperature + ///< in degrees Fahrenheit +private: + Placeholder _ow; ///< OneWire bus object + OneWireNg::Id _sensorId; ///< Sensor ID + DSTherm _drv_therm; ///< DS18X20 driver object + DSTherm::Resolution _resolution; ///< Resolution of the DS18X20 sensor + float _temp_c; ///< Temperature in Celsius + float _temp_f; ///< Temperature in Fahrenheit + // From the PB model + uint8_t + _onewire_pin; ///< Pin utilized by the OneWire bus, used for addressing + char _onewire_pin_name[5]; ///< Name of the OneWire bus pin + float _period; ///< The desired period to read the sensor, in seconds + float _prv_period; ///< Last time the sensor was polled, in seconds +}; +#endif // WS_DS18X20_HARDWARE_H \ No newline at end of file diff --git a/src/components/ds18x20/model.cpp b/src/components/ds18x20/model.cpp new file mode 100644 index 000000000..17dd039c0 --- /dev/null +++ b/src/components/ds18x20/model.cpp @@ -0,0 +1,188 @@ +/*! + * @file model.cpp + * + * Model for the ds18x20.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#include "model.h" + +/***********************************************************************/ +/*! + @brief DS18X20Model constructor +*/ +/***********************************************************************/ +DS18X20Model::DS18X20Model() { + // Initialize the DS18X20 messages + _msg_DS18x20Add = wippersnapper_ds18x20_Ds18x20Add_init_zero; + _msg_DS18x20Added = wippersnapper_ds18x20_Ds18x20Added_init_zero; + _msg_DS18x20Remove = wippersnapper_ds18x20_Ds18x20Remove_init_zero; +} + +/***********************************************************************/ +/*! + @brief DS18X20Model destructor +*/ +/***********************************************************************/ +DS18X20Model::~DS18X20Model() {} + +/***********************************************************************/ +/*! + @brief Attempts to decode a Ds18x20Add message from the broker. + @param stream + The nanopb input stream. + @return True if the message was successfully decoded, False otherwise. +*/ +/***********************************************************************/ +bool DS18X20Model::DecodeDS18x20Add(pb_istream_t *stream) { + _msg_DS18x20Add = wippersnapper_ds18x20_Ds18x20Add_init_zero; + // Attempt to decode the stream into a Ds18x20Add message + return pb_decode(stream, wippersnapper_ds18x20_Ds18x20Add_fields, + &_msg_DS18x20Add); +} + +/***********************************************************************/ +/*! + @brief Gets a pointer to the Ds18x20Add message. + @return Pointer to the Ds18x20Add message. +*/ +/***********************************************************************/ +wippersnapper_ds18x20_Ds18x20Add *DS18X20Model::GetDS18x20AddMsg() { + return &_msg_DS18x20Add; +} + +/***********************************************************************/ +/*! + @brief Returns a pointer to the Ds18x20Added message. + @return Pointer to the Ds18x20Added message. +*/ +/***********************************************************************/ +wippersnapper_ds18x20_Ds18x20Added *DS18X20Model::GetDS18x20AddedMsg() { + return &_msg_DS18x20Added; +} + +/***********************************************************************/ +/*! + @brief Encodes a Ds18x20Added message. + @param onewire_pin + The OneWire bus pin to add. + @param is_init + True if the sensor was successfully initialized, + False otherwise. + @return True if the message was successfully encoded, + False otherwise. +*/ +/***********************************************************************/ +bool DS18X20Model::EncodeDS18x20Added(char *onewire_pin, bool is_init) { + // Fill the Ds18x20Added message + _msg_DS18x20Added = wippersnapper_ds18x20_Ds18x20Added_init_zero; + _msg_DS18x20Added.is_initialized = is_init; + strcpy(_msg_DS18x20Added.onewire_pin, onewire_pin); + + // Encode the Ds18x20Added message + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_ds18x20_Ds18x20Added_fields, + &_msg_DS18x20Added)) + return false; + + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_ds18x20_Ds18x20Added_fields, + &_msg_DS18x20Added); +} + +/*************************************************************************/ +/*! + @brief Attempts to decode a Ds18x20Remove message from the broker. + @param stream + The nanopb input stream. + @return True if the message was successfully decoded, False otherwise. +*/ +/*************************************************************************/ +bool DS18X20Model::DecodeDS18x20Remove(pb_istream_t *stream) { + _msg_DS18x20Remove = wippersnapper_ds18x20_Ds18x20Remove_init_zero; + return pb_decode(stream, wippersnapper_ds18x20_Ds18x20Remove_fields, + &_msg_DS18x20Remove); +} + +/*************************************************************************/ +/*! + @brief Gets a pointer to the Ds18x20Remove message. + @return Pointer to the Ds18x20Remove message. +*/ +/*************************************************************************/ +wippersnapper_ds18x20_Ds18x20Remove *DS18X20Model::GetDS18x20RemoveMsg() { + return &_msg_DS18x20Remove; +} + +/*************************************************************************/ +/*! + @brief Gets a pointer to the Ds18x20Event message. + @return Pointer to the Ds18x20Event message. +*/ +/*************************************************************************/ +wippersnapper_ds18x20_Ds18x20Event *DS18X20Model::GetDS18x20EventMsg() { + return &_msg_DS18x20Event; +} + +/*************************************************************************/ +/*! + @brief Encodes a Ds18x20Event message. + @return True if the message was successfully encoded, False otherwise. +*/ +/*************************************************************************/ +bool DS18X20Model::EncodeDs18x20Event() { + // take the filled _msg_DS18x20Event we built in the controller and encode it + size_t sz_msg; + if (!pb_get_encoded_size(&sz_msg, wippersnapper_ds18x20_Ds18x20Event_fields, + &_msg_DS18x20Event)) + return false; + + uint8_t buf[sz_msg]; + pb_ostream_t msg_stream = pb_ostream_from_buffer(buf, sizeof(buf)); + return pb_encode(&msg_stream, wippersnapper_ds18x20_Ds18x20Event_fields, + &_msg_DS18x20Event); +} + +/*************************************************************************/ +/*! + @brief Initializes the Ds18x20Event message. + @param ow_pin_name + The OneWire bus pin name. +*/ +/*************************************************************************/ +void DS18X20Model::InitDS18x20EventMsg(const char *ow_pin_name) { + _msg_DS18x20Event = wippersnapper_ds18x20_Ds18x20Event_init_zero; + _msg_DS18x20Event.sensor_events_count = 0; + strcpy(_msg_DS18x20Event.onewire_pin, ow_pin_name); +} + +/*************************************************************************/ +/*! + @brief Adds a "sensor event" to the Ds18x20Event message. + @param sensor_type + The event's SensorType. + @param sensor_value + The event's value. +*/ +/*************************************************************************/ +void DS18X20Model::addSensorEvent(wippersnapper_sensor_SensorType sensor_type, + float sensor_value) { + _msg_DS18x20Event.sensor_events[_msg_DS18x20Event.sensor_events_count].type = + sensor_type; + _msg_DS18x20Event.sensor_events[_msg_DS18x20Event.sensor_events_count] + .which_value = wippersnapper_sensor_SensorEvent_float_value_tag; + + // Convert the float to 2 decimal places + sensor_value = roundf(sensor_value * 100) / 100; + _msg_DS18x20Event.sensor_events[_msg_DS18x20Event.sensor_events_count] + .value.float_value = sensor_value; + _msg_DS18x20Event.sensor_events_count++; +} \ No newline at end of file diff --git a/src/components/ds18x20/model.h b/src/components/ds18x20/model.h new file mode 100644 index 000000000..0cae22782 --- /dev/null +++ b/src/components/ds18x20/model.h @@ -0,0 +1,54 @@ +/*! + * @file model.h + * + * Model interface for the DS18X20.proto message. + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * Copyright (c) Brent Rubell 2024 for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ +#ifndef WS_DS18X20_MODEL_H +#define WS_DS18X20_MODEL_H +#include "Wippersnapper_V2.h" + +/**************************************************************************/ +/*! + @brief Provides an interface for creating, encoding, and parsing + messages from DS18X20.proto. +*/ +/**************************************************************************/ +class DS18X20Model { +public: + DS18X20Model(); + ~DS18X20Model(); + // Ds18x20Add Message API + bool DecodeDS18x20Add(pb_istream_t *stream); + wippersnapper_ds18x20_Ds18x20Add *GetDS18x20AddMsg(); + // DS18x20Added Message API + bool EncodeDS18x20Added(char *onewire_pin, bool is_init); + wippersnapper_ds18x20_Ds18x20Added *GetDS18x20AddedMsg(); + // Ds18x20Remove Message API + bool DecodeDS18x20Remove(pb_istream_t *stream); + wippersnapper_ds18x20_Ds18x20Remove *GetDS18x20RemoveMsg(); + // Ds18x20Event Message API + bool EncodeDs18x20Event(); + wippersnapper_ds18x20_Ds18x20Event *GetDS18x20EventMsg(); + void InitDS18x20EventMsg(const char *ow_pin_name); + void addSensorEvent(wippersnapper_sensor_SensorType sensor_type, + float sensor_value); +private: + wippersnapper_ds18x20_Ds18x20Add + _msg_DS18x20Add; ///< wippersnapper_ds18x20_Ds18x20Add message + wippersnapper_ds18x20_Ds18x20Added + _msg_DS18x20Added; ///< wippersnapper_ds18x20_Ds18x20Added message + wippersnapper_ds18x20_Ds18x20Remove + _msg_DS18x20Remove; ///< wippersnapper_ds18x20_Ds18x20Remove message + wippersnapper_ds18x20_Ds18x20Event + _msg_DS18x20Event; ///< wippersnapper_ds18x20_Ds18x20Event message +}; +#endif // WS_DIGITALIO_MODEL_H \ No newline at end of file diff --git a/src/components/ds18x20/ws_ds18x20.cpp b/src/components/ds18x20/ws_ds18x20.cpp deleted file mode 100644 index 0cfa6e0c5..000000000 --- a/src/components/ds18x20/ws_ds18x20.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/*! - * @file ws_ds18x20.cpp - * - * This component implements 1-wire communication - * for the DS18X20-line of Maxim Temperature ICs. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2022-2023 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ - -#include "ws_ds18x20.h" - -/*************************************************************/ -/*! - @brief Creates a new WipperSnapper Ds18x20 component. -*/ -/*************************************************************/ -ws_ds18x20::ws_ds18x20() {} - -/*************************************************************/ -/*! - @brief Destructor for a WipperSnapper DS18X20 component. -*/ -/*************************************************************/ -ws_ds18x20::~ws_ds18x20() { - // delete DallasTemp sensors and release onewire buses - for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { - delete _ds18xDrivers[idx]->dallasTempObj; - delete _ds18xDrivers[idx]->oneWire; - } - // remove all elements - _ds18xDrivers.clear(); -} - -/********************************************************************/ -/*! - @brief Initializes a DS18x20 sensor using a - configuration sent by the broker and adds it to a - vector of ds18x20 sensor drivers. - @param msgDs18x20InitReq - Message containing configuration data for a - ds18x20 sensor. - @returns True if initialized successfully, False otherwise. -*/ -/********************************************************************/ -bool ws_ds18x20::addDS18x20( - wippersnapper_ds18x20_v1_Ds18x20InitRequest *msgDs18x20InitReq) { - bool is_success = false; - - // init. new ds18x20 object - ds18x20Obj *newObj = new ds18x20Obj(); - char *oneWirePin = msgDs18x20InitReq->onewire_pin + 1; - newObj->oneWire = new OneWire(atoi(oneWirePin)); - newObj->dallasTempObj = new DallasTemperature(newObj->oneWire); - newObj->dallasTempObj->begin(); - // attempt to obtain sensor address - if (newObj->dallasTempObj->getAddress(newObj->dallasTempAddr, 0)) { - // attempt to set sensor resolution - newObj->dallasTempObj->setResolution(msgDs18x20InitReq->sensor_resolution); - // copy the device's sensor properties - newObj->sensorPropertiesCount = - msgDs18x20InitReq->i2c_device_properties_count; - // TODO: Make sure this works, it's a new idea and untested :) - for (int i = 0; i < newObj->sensorPropertiesCount; i++) { - newObj->sensorProperties[i].sensor_type = - msgDs18x20InitReq->i2c_device_properties[i].sensor_type; - newObj->sensorProperties[i].sensor_period = - (long)msgDs18x20InitReq->i2c_device_properties[i].sensor_period * - 1000; - } - // set pin - strcpy(newObj->onewire_pin, msgDs18x20InitReq->onewire_pin); - // add the new ds18x20 driver to vec. - _ds18xDrivers.push_back(newObj); - is_success = true; - } else { - WS_DEBUG_PRINTLN("Failed to find DSx sensor on specified pin."); - } - - // fill and publish the initialization response back to the broker - size_t msgSz; // message's encoded size - wippersnapper_signal_v1_Ds18x20Response msgInitResp = - wippersnapper_signal_v1_Ds18x20Response_init_zero; - msgInitResp.which_payload = - wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_init_tag; - msgInitResp.payload.resp_ds18x20_init.is_initialized = is_success; - strcpy(msgInitResp.payload.resp_ds18x20_init.onewire_pin, - msgDs18x20InitReq->onewire_pin); - - WS_DEBUG_PRINT("Created OneWireBus on GPIO "); - WS_DEBUG_PRINT(msgDs18x20InitReq->onewire_pin); - WS_DEBUG_PRINTLN(" with DS18x20 attached!"); - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[DS18x] Attached DS18x20 sensor to pin %s\n", - msgDs18x20InitReq->onewire_pin); - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - // Encode and publish response back to broker - memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); - pb_ostream_t ostream = - pb_ostream_from_buffer(WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!ws_pb_encode(&ostream, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp)) { - WS_DEBUG_PRINTLN("ERROR: Unable to encode msg_init response message!"); - return false; - } - pb_get_encoded_size(&msgSz, wippersnapper_signal_v1_Ds18x20Response_fields, - &msgInitResp); - WS_DEBUG_PRINT("-> DS18x Init Response..."); - WS._mqtt->publish(WS._topic_signal_ds18_device, WS._buffer_outgoing, msgSz, - 1); - WS_DEBUG_PRINTLN("Published!"); - - return is_success; -} - -/********************************************************************/ -/*! - @brief De-initializes a DS18x20 sensor and releases its - pin and resources. - @param msgDS18x20DeinitReq - Message containing configuration data for a - ds18x20 sensor. -*/ -/********************************************************************/ -void ws_ds18x20::deleteDS18x20( - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest *msgDS18x20DeinitReq) { - // Loop thru vector of drivers to find the unique address - for (size_t idx = 0; idx < _ds18xDrivers.size(); idx++) { - if (strcmp(_ds18xDrivers[idx]->onewire_pin, - msgDS18x20DeinitReq->onewire_pin) == 0) { - WS_DEBUG_PRINT("Deleting OneWire instance on pin "); - WS_DEBUG_PRINTLN(msgDS18x20DeinitReq->onewire_pin); - delete _ds18xDrivers[idx] - ->dallasTempObj; // delete dallas temp instance on pin - delete _ds18xDrivers[idx] - ->oneWire; // delete OneWire instance on pin and release pin for reuse - _ds18xDrivers.erase(_ds18xDrivers.begin() + - idx); // erase vector and re-allocate - } - } - -#ifdef USE_DISPLAY - char buffer[100]; - snprintf(buffer, 100, "[DS18x] Deleted DS18x20 sensor on pin %s\n", - msgDS18x20DeinitReq->onewire_pin); - WS._ui_helper->add_text_to_terminal(buffer); -#endif -} - -/*************************************************************/ -/*! - @brief Iterates through each ds18x20 sensor and - reports data (if period expired) to Adafruit IO. -*/ -/*************************************************************/ -void ws_ds18x20::update() { - // return immediately if no drivers have been initialized - if (_ds18xDrivers.size() == 0) - return; - - long curTime; // used for holding the millis() value - std::vector::iterator iter, end; - for (iter = _ds18xDrivers.begin(), end = _ds18xDrivers.end(); iter != end; - ++iter) { - - // Create an empty DS18x20 event signal message and configure - wippersnapper_signal_v1_Ds18x20Response msgDS18x20Response = - wippersnapper_signal_v1_Ds18x20Response_init_zero; - msgDS18x20Response.which_payload = - wippersnapper_signal_v1_Ds18x20Response_resp_ds18x20_event_tag; - - // take the current time for the driver (*iter) - curTime = millis(); - // Poll each sensor type, if period has elapsed - for (int i = 0; i < (*iter)->sensorPropertiesCount; i++) { - // has sensor_period elapsed? - if (curTime - (*iter)->sensorPeriodPrv > - (long)(*iter)->sensorProperties[i].sensor_period) { - // issue global temperature request to all DS sensors - WS_DEBUG_PRINTLN("Requesting temperature.."); - (*iter)->dallasTempObj->requestTemperatures(); - // poll the DS sensor driver - float tempC = (*iter)->dallasTempObj->getTempC((*iter)->dallasTempAddr); - if (tempC == DEVICE_DISCONNECTED_C) { - WS_DEBUG_PRINTLN("ERROR: Could not read temperature data, is the " - "sensor disconnected?"); -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal( - "[DS18x ERROR] Unable to read temperature, is the sensor " - "disconnected?\n"); -#endif - break; - } - - // check and pack based on sensorType - char buffer[100]; - if ((*iter)->sensorProperties[i].sensor_type == - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE) { - - WS_DEBUG_PRINT("(OneWireBus GPIO: "); - WS_DEBUG_PRINT((*iter)->onewire_pin); - WS_DEBUG_PRINT(") DS18x20 Value: "); - WS_DEBUG_PRINT(tempC); - WS_DEBUG_PRINTLN("*C") - snprintf(buffer, 100, "[DS18x] Read %0.2f*C on GPIO %s\n", tempC, - (*iter)->onewire_pin); - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE; - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = - tempC; - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; - } - - if ((*iter)->sensorProperties[i].sensor_type == - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT) { - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].type = - wippersnapper_i2c_v1_SensorType_SENSOR_TYPE_AMBIENT_TEMPERATURE_FAHRENHEIT; - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i].value = - (*iter)->dallasTempObj->toFahrenheit(tempC); - WS_DEBUG_PRINT("(OneWireBus GPIO: "); - WS_DEBUG_PRINT((*iter)->onewire_pin); - WS_DEBUG_PRINT(") DS18x20 Value: "); - WS_DEBUG_PRINT( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value); - WS_DEBUG_PRINTLN("*F") - snprintf(buffer, 100, "[DS18x] Read %0.2f*F on GPIO %s\n", - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value, - (*iter)->onewire_pin); - - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count++; - } - - // did we obtain the expected amount of sensor events for the - // `resp_ds18x20_event` message? - if (msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count == - (*iter)->sensorPropertiesCount) { - - // prep sensor event data for sending to IO - // use onewire_pin as the "address" - strcpy(msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin, - (*iter)->onewire_pin); - // prep and encode buffer - memset(WS._buffer_outgoing, 0, sizeof(WS._buffer_outgoing)); - pb_ostream_t ostream = pb_ostream_from_buffer( - WS._buffer_outgoing, sizeof(WS._buffer_outgoing)); - if (!ws_pb_encode(&ostream, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response)) { - WS_DEBUG_PRINTLN( - "ERROR: Unable to encode DS18x20 event responsemessage!"); - snprintf(buffer, 100, - "[DS18x ERROR] Unable to encode event message!"); - return; - } - - WS_DEBUG_PRINTLN( - "DEBUG: msgDS18x20Response sensor_event message contents:"); - for (int i = 0; - i < - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event_count; - i++) { - WS_DEBUG_PRINT("sensor_event[#]: "); - WS_DEBUG_PRINTLN(i); - WS_DEBUG_PRINT("\tOneWire Bus: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.onewire_pin); - WS_DEBUG_PRINT("\tsensor_event type: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .type); - WS_DEBUG_PRINT("\tsensor_event value: "); - WS_DEBUG_PRINTLN( - msgDS18x20Response.payload.resp_ds18x20_event.sensor_event[i] - .value); - } - - // Publish I2CResponse msg - size_t msgSz; - pb_get_encoded_size(&msgSz, - wippersnapper_signal_v1_Ds18x20Response_fields, - &msgDS18x20Response); - WS_DEBUG_PRINT("PUBLISHING -> msgDS18x20Response Event Message..."); - if (!WS._mqtt->publish(WS._topic_signal_ds18_device, - WS._buffer_outgoing, msgSz, 1)) { - WS_DEBUG_PRINTLN("ERROR: Unable to publish DS18x20 event message - " - "MQTT Publish failed!"); - return; - }; - WS_DEBUG_PRINTLN("PUBLISHED!"); -#ifdef USE_DISPLAY - WS._ui_helper->add_text_to_terminal(buffer); -#endif - - (*iter)->sensorPeriodPrv = curTime; // set prv period - } - } - } - } -} \ No newline at end of file diff --git a/src/components/ds18x20/ws_ds18x20.h b/src/components/ds18x20/ws_ds18x20.h deleted file mode 100644 index 5398d4c4f..000000000 --- a/src/components/ds18x20/ws_ds18x20.h +++ /dev/null @@ -1,65 +0,0 @@ -/*! - * @file ws_ds18x20.h - * - * This component implements 1-wire communication - * for the DS18X20-line of Maxim Temperature ICs. - * - * Adafruit invests time and resources providing this open source code, - * please support Adafruit and open-source hardware by purchasing - * products from Adafruit! - * - * Copyright (c) Brent Rubell 2022 for Adafruit Industries. - * - * BSD license, all text here must be included in any redistribution. - * - */ -#ifndef WIPPERSNAPPER_DS18X20_H -#define WIPPERSNAPPER_DS18X20_H - -#include "Wippersnapper.h" - -#include -#include - -/** DS18x20 Object */ -struct ds18x20Obj { - OneWire * - oneWire; ///< Pointer to an OneWire bus used by a DallasTemperature object - char onewire_pin[5]; ///< Pin utilized by the OneWire bus, used for addressing - DallasTemperature - *dallasTempObj; ///< Pointer to a DallasTemperature sensor object - DeviceAddress dallasTempAddr; ///< Temperature sensor's address - int sensorPropertiesCount; ///< Tracks # of sensorProperties - wippersnapper_i2c_v1_I2CDeviceSensorProperties sensorProperties[2] = - wippersnapper_i2c_v1_I2CDeviceSensorProperties_init_zero; ///< DS sensor - ///< type(s) - long sensorPeriodPrv; ///< Last time the sensor was polled, in millis -}; - -// forward decl. -class Wippersnapper; - -/**************************************************************************/ -/*! - @brief Class that provides an interface with DS18X20-compatible - sensors. -*/ -/**************************************************************************/ -class ws_ds18x20 { -public: - ws_ds18x20(); - ~ws_ds18x20(); - - bool - addDS18x20(wippersnapper_ds18x20_v1_Ds18x20InitRequest *msgDs18x20InitReq); - void deleteDS18x20( - wippersnapper_ds18x20_v1_Ds18x20DeInitRequest *msgDS18x20DeinitReq); - void update(); - -private: - std::vector - _ds18xDrivers; ///< Vec. of ptrs. to ds18x driver objects -}; -extern Wippersnapper WS; - -#endif // WIPPERSNAPPER_DS18X20_H \ No newline at end of file diff --git a/src/components/sensor/model.cpp b/src/components/sensor/model.cpp index fd2343172..85f1cb3d3 100644 --- a/src/components/sensor/model.cpp +++ b/src/components/sensor/model.cpp @@ -20,7 +20,7 @@ */ /***********************************************************************/ SensorModel::SensorModel() { - _msg_sensor_event = wippersnapper_sensor_SensorEvent_init_default; + _msg_sensor_event = wippersnapper_sensor_SensorEvent_init_zero; } /***********************************************************************/ @@ -28,111 +28,7 @@ SensorModel::SensorModel() { @brief SensorModel destructor */ /***********************************************************************/ -SensorModel::~SensorModel() {} - -/***********************************************************************/ -/*! - @brief Decodes a SensorEvent message into a SensorModel object. - @param stream - The input stream - @returns True if the SensorEvent message was successfully decoded, - False otherwise. -*/ -/***********************************************************************/ -bool SensorModel::decodeSensorEvent(pb_istream_t *stream) { - // Decode the stream into theSensorEvent message - if (!pb_decode(stream, wippersnapper_sensor_SensorEvent_fields, - &_msg_sensor_event)) { - return false; - } - return true; -} - -/***********************************************************************/ -/*! - @brief Clears the SensorEvent message. -*/ -/***********************************************************************/ -void SensorModel::clearSensorEvent() { - _msg_sensor_event = wippersnapper_sensor_SensorEvent_init_default; -} - -/***********************************************************************/ -/*! - @brief Returns a SensorEvent message. - @returns The SensorEvent message. -*/ -/***********************************************************************/ -wippersnapper_sensor_SensorEvent *SensorModel::getSensorEvent() { - return &_msg_sensor_event; -} - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's type. - @returns The type of the SensorEvent message, as a SensorType. -*/ -/***********************************************************************/ -wippersnapper_sensor_SensorType SensorModel::getSensorType() { - return _msg_sensor_event.type; -} - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's which_value field. - @returns The which_value field of the SensorEvent message. -*/ -/***********************************************************************/ -pb_size_t SensorModel::getSensorEventWhichValue() { - return _msg_sensor_event.which_value; -} - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's float value field. - @returns The float value of the SensorEvent message. -*/ -/***********************************************************************/ -float SensorModel::getValueFloat() { - return _msg_sensor_event.value.float_value; -} - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's bool value field. - @returns The bool value of the SensorEvent message. -*/ -/***********************************************************************/ -bool SensorModel::getValueBool() { return _msg_sensor_event.value.bool_value; } - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's vector value field. - @returns The vector value of the SensorEvent message. -*/ -/***********************************************************************/ -wippersnapper_sensor_SensorEvent_SensorEvent3DVector -SensorModel::getValueVector() { - return _msg_sensor_event.value.vector_value; -} - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's orientation value field. - @returns The orientation value of the SensorEvent message. -*/ -/***********************************************************************/ -wippersnapper_sensor_SensorEvent_SensorEventOrientation -SensorModel::getValueOrientation() { - return _msg_sensor_event.value.orientation_value; -} - -/***********************************************************************/ -/*! - @brief Returns the the SensorEvent message's color value field. - @returns The color value of the SensorEvent message. -*/ -/***********************************************************************/ -wippersnapper_sensor_SensorEvent_SensorEventColor SensorModel::getValueColor() { - return _msg_sensor_event.value.color_value; +SensorModel::~SensorModel() { + // Zero-out the SensorEvent message + _msg_sensor_event = wippersnapper_sensor_SensorEvent_init_zero; } \ No newline at end of file diff --git a/src/components/sensor/model.h b/src/components/sensor/model.h index c6cf1adae..6be8584f8 100644 --- a/src/components/sensor/model.h +++ b/src/components/sensor/model.h @@ -27,19 +27,6 @@ class SensorModel { public: SensorModel(); ~SensorModel(); - bool decodeSensorEvent(pb_istream_t *stream); - void clearSensorEvent(); - - wippersnapper_sensor_SensorEvent *getSensorEvent(); - wippersnapper_sensor_SensorType getSensorType(); - pb_size_t getSensorEventWhichValue(); - - float getValueFloat(); - bool getValueBool(); - wippersnapper_sensor_SensorEvent_SensorEvent3DVector getValueVector(); - wippersnapper_sensor_SensorEvent_SensorEventOrientation getValueOrientation(); - wippersnapper_sensor_SensorEvent_SensorEventColor getValueColor(); - private: wippersnapper_sensor_SensorEvent _msg_sensor_event; ///< SensorEvent message };