diff --git a/client/ESPcam/.idea/misc.xml b/client/ESPcam/.idea/misc.xml index 79b3c94830bab93d40d0770f2765540fe24ed423..6e1c9f37b93d78b6c09407d34dc9e55175665cd7 100644 --- a/client/ESPcam/.idea/misc.xml +++ b/client/ESPcam/.idea/misc.xml @@ -1,4 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" /> + <component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$"> + <contentRoot DIR="$PROJECT_DIR$/.." /> + </component> </project> \ No newline at end of file diff --git a/client/ESPcam/CMakeListsPrivate.txt b/client/ESPcam/CMakeListsPrivate.txt index 1031c7cce53c3431dd7caa12d100c935a5bd749b..2c27268f2f0e34564668c2926a94775303a907e8 100644 --- a/client/ESPcam/CMakeListsPrivate.txt +++ b/client/ESPcam/CMakeListsPrivate.txt @@ -21,7 +21,7 @@ SET(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 17) if (CMAKE_BUILD_TYPE MATCHES "esp32cam") - add_definitions(-DPLATFORMIO=60104) + add_definitions(-DPLATFORMIO=60105) add_definitions(-DARDUINO_ESP32_DEV) add_definitions(-DBOARD_HAS_PSRAM) add_definitions(-DCORE_DEBUG_LEVEL=5) @@ -247,10 +247,14 @@ if (CMAKE_BUILD_TYPE MATCHES "esp32cam") include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif32/tools/sdk/esp32/dio_qspi/include") include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif32/cores/esp32") include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif32/variants/esp32") + include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/.idea") + include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/LC7090203F") + include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/SentecSensors") include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/deep_sleep") include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/dr26_analogue") include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/drs26_digital") include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/ina219") + include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/mock_sensor") include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/rs485") include_directories("$ENV{HOME}/GitRepos/sensor-system/client/libs/scd30") include_directories("$ENV{HOME}/.platformio/packages/framework-arduinoespressif32/libraries/ArduinoOTA/src") diff --git a/client/ESPcam/platformio.ini b/client/ESPcam/platformio.ini index 0573bea475a110f93b65a2554d555f39a127b16c..835183a1a921563ea8720791c13a22e3cdafd9dd 100644 --- a/client/ESPcam/platformio.ini +++ b/client/ESPcam/platformio.ini @@ -15,11 +15,12 @@ framework = arduino monitor_speed = 115200 lib_ldf_mode = deep lib_extra_dirs = - ../libs + ../libs + ../../shared-libs build_flags = - -I include - -DCORE_DEBUG_LEVEL=5 - -std=gnu++17 + -I include + -DCORE_DEBUG_LEVEL=5 + -std=gnu++17 build_unflags = -std=gnu++11 lib_deps = sensirion/arduino-sht@^1.2.2 adafruit/RTClib @^2.1.1 diff --git a/client/client_central_mast/platformio.ini b/client/client_central_mast/platformio.ini index 0f778714c620418858a06af712025c04d4221c05..a75dccb356b65c764d38b5ff2a0c795fb0d42045 100644 --- a/client/client_central_mast/platformio.ini +++ b/client/client_central_mast/platformio.ini @@ -15,28 +15,29 @@ framework = arduino monitor_speed = 115200 lib_ldf_mode = deep lib_extra_dirs = - ../libs + ../libs + ../../shared-libs ; C++17 https://community.platformio.org/t/esp32-c-17-toolchain-missing-std-optional/25850/6 ; we use c++17 features (i.e. optionals in ClientDataPackage.hpp) build_flags = - -I include - -DCORE_DEBUG_LEVEL=5 - -std=gnu++17 + -I include + -DCORE_DEBUG_LEVEL=5 + -std=gnu++17 build_unflags = -std=gnu++11 monitor_port = /dev/ttyUSB0 upload_port = /dev/ttyUSB0 lib_deps = - Wire - adafruit/Adafruit ADS1X15@^2.4.0 - wollewald/INA219_WE@^1.3.1 - adafruit/Adafruit BusIO@^1.13.2 - Adafruit_I2CDevice - SPI - fbiego/ESP32Time@^2.0.0 - bblanchon/ArduinoJson@^6.19.4 - 4-20ma/ModbusMaster@^2.0.1 - adafruit/RTClib@^2.1.1 - sensirion/arduino-sht@^1.2.2 - robtillaart/SHT85@^0.3.2 - fbiego/ESP32Time@^2.0.0 - bblanchon/ArduinoJson@^6.19.4 + Wire + adafruit/Adafruit ADS1X15@^2.4.0 + wollewald/INA219_WE@^1.3.1 + adafruit/Adafruit BusIO@^1.13.2 + Adafruit_I2CDevice + SPI + fbiego/ESP32Time@^2.0.0 + bblanchon/ArduinoJson@^6.19.4 + 4-20ma/ModbusMaster@^2.0.1 + adafruit/RTClib@^2.1.1 + sensirion/arduino-sht@^1.2.2 + robtillaart/SHT85@^0.3.2 + fbiego/ESP32Time@^2.0.0 + bblanchon/ArduinoJson@^6.19.4 diff --git a/client/client_central_mast/test/TestClientDataPackage.cpp b/client/client_central_mast/test/TestClientDataPackage.cpp index eea536cfbc63f7c6dc9797d8288a5887c384aa0c..694eec4dc26fab1041fa9c083963b694ee83cfe7 100644 --- a/client/client_central_mast/test/TestClientDataPackage.cpp +++ b/client/client_central_mast/test/TestClientDataPackage.cpp @@ -3,7 +3,7 @@ // #include "TestClientDataPackage.hpp" -#include "MeasurementData.hpp" +#include "Measurement.hpp" #include "Protocol.hpp" #include <vector> diff --git a/client/client_mock/platformio.ini b/client/client_mock/platformio.ini index b7c388a5d2b4fd93dd5ddb27969ebd259d807847..817104fcf3a217a02f4eb028b7387da79fd71548 100644 --- a/client/client_mock/platformio.ini +++ b/client/client_mock/platformio.ini @@ -4,8 +4,10 @@ board = esp32dev framework = arduino monitor_speed = 115200 lib_ldf_mode = deep +upload_port = /dev/ttyUSB0 lib_extra_dirs = ../libs + ../../shared-libs ; C++17 https://community.platformio.org/t/esp32-c-17-toolchain-missing-std-optional/25850/6 ; we use c++17 features (i.e. optionals in ClientDataPackage.hpp) build_flags = diff --git a/client/client_mock/src/main.cpp b/client/client_mock/src/main.cpp index 4e322c5fd2aa7baf9d260b3d8a8f760327b17038..bf5af15f103b2311f1f352fd526302cc24610a26 100644 --- a/client/client_mock/src/main.cpp +++ b/client/client_mock/src/main.cpp @@ -3,6 +3,8 @@ #include "NoDataAvailableException.hpp" #include "f_deep_sleep.hpp" #include <Arduino.h> +#include <ClientDataPackage.hpp> +#include <soc/rtc_cntl_reg.h> static const char *TAG = "MAIN"; @@ -34,20 +36,15 @@ void send_msgs(const std::__cxx11::list<Message> msgs) { // one loop takes ~2200 ms void setup() { + // disable brownout +// WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); unsigned long ts = millis(); Serial.begin(115200); - // Set the GPIO which conrtols the step up to OUTPUT - gpio_set_direction(GPIO_NUM_32, GPIO_MODE_OUTPUT); - - // blinking led for debug - // gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT); - // gpio_set_level(GPIO_NUM_17, 1); DeepSleep::print_wakeup_reason(); DeepSleep::bootCount++; ESP_LOGD(TAG, "Boot number: %d", DeepSleep::bootCount); - gpio_set_level(GPIO_NUM_32, 1); // delay(100); mock_channel0.setup(); mock_channel1.setup(); @@ -59,42 +56,86 @@ void setup() { mock_channel2.setChannel(2); mock_channel3.setChannel(3); - ESP_LOGD(TAG, "Setup took %ld ms", millis() - ts); + // disable led + gpio_set_direction(GPIO_NUM_32, GPIO_MODE_OUTPUT); - try { - // FIXME: put me into seperate trys? No data will be sent when 1 exception occurs + ESP_LOGD(TAG, "Setup took %ld ms", millis() - ts); + try { + // FIXME: put me into seperate trys? No data will be sent when 1 exception occurs ts = millis(); auto messages0 = mock_channel0.buildMessages(); auto messages1 = mock_channel1.buildMessages(); auto messages2 = mock_channel2.buildMessages(); auto messages3 = mock_channel3.buildMessages(); + // roughly takes 500ms, ~120ms for each adc channel, barely anything for battery monitor - ESP_LOGD(TAG, "Reading data and building messages took %ld ms", millis() - ts); + ESP_LOGD(TAG, + "Reading data and building messages took %ld ms", + millis() - ts); gpio_set_level(GPIO_NUM_32, 0); + ESP_LOGD(TAG, "Size of message to be sent: %d", sizeof(messages0.front())); + ESP_LOGD(TAG, "Size of Message class: %d", sizeof(Message)); + ESP_LOGD(TAG, + "Size of ClientDataPackage class: %d", + sizeof(ClientDataPackage)); + // sizeof string + ESP_LOGD(TAG, "Size of string: %d", sizeof(std::string)); + // sizeof string with 5 char + ESP_LOGD(TAG, "Size of string with 5 char: %d", sizeof(char[5])); + // sizeof optional int + ESP_LOGD(TAG, "Size of optional int: %d", sizeof(std::optional<int>)); + // sizeof int + ESP_LOGD(TAG, "Size of int: %d", sizeof(int)); + // sizeof short + ESP_LOGD(TAG, "Size of short: %d", sizeof(short)); + // sizeof double + ESP_LOGD(TAG, "Size of double: %d", sizeof(double)); + // sizeof float + ESP_LOGD(TAG, "Size of float: %d", sizeof(float)); + // list of 5 compresseddatapackage + ClientDataPackage compresseddatapackage{}; + ESP_LOGD(TAG, + "Size of list of 4 ClientDataPackage: %d", + sizeof(std::list<ClientDataPackage>) + + (sizeof(ClientDataPackage) * 4)); + // sizeof compresseddatapackage + ESP_LOGD(TAG, + "Size of ClientDataPackage: %d", + sizeof(ClientDataPackage)); + // sizeof list + ESP_LOGD(TAG, "Size of list: %d", sizeof(std::list<ClientDataPackage>)); + // sizeof vector + ESP_LOGD(TAG, + "Size of vector: %d", + sizeof(std::vector<ClientDataPackage>)); + // FIXME: put this outside the try loop? ts = millis(); espnow_setup(); ESP_LOGD(TAG, "EPSNow setup took %ld ms", millis() - ts); + // make a list of messages + std::array<Message, 6> messages = + {messages0.front(), messages1.front(), messages2.front(), + Message::nullMessage(), Message::nullMessage(), + Message::nullMessage()}; + + // print messages + for (const Message &msg: messages) { + ESP_LOGD(TAG, "Message: %s", msg.getMessageAsMinifiedJsonString().c_str()); + } + ts = millis(); - send_msgs(messages0); - send_msgs(messages1); - send_msgs(messages2); - send_msgs(messages3); - // roughly takes 3s in ideal conditions + Message::sendMessages(messages); ESP_LOGD(TAG, "Sending messages took %ld ms", millis() - ts); } catch (const NoDataAvailableException &e) { std::cerr << e.what() << '\n'; } - // just to be safe in case exception happens above - gpio_set_level(GPIO_NUM_32, 0); - // keep it in deep sleep - gpio_hold_en((gpio_num_t) GPIO_NUM_32); // battery protection: go to deep sleep for unlimited time when voltage less than 3.2V - DeepSleep::deep_sleep(20); + DeepSleep::deep_sleep(5); } diff --git a/client/client_satellite/platformio.ini b/client/client_satellite/platformio.ini index b7c388a5d2b4fd93dd5ddb27969ebd259d807847..1a9a18984b3f34f3cba2b6d992ddf9eb49f61e74 100644 --- a/client/client_satellite/platformio.ini +++ b/client/client_satellite/platformio.ini @@ -6,6 +6,7 @@ monitor_speed = 115200 lib_ldf_mode = deep lib_extra_dirs = ../libs + ../../shared-libs ; C++17 https://community.platformio.org/t/esp32-c-17-toolchain-missing-std-optional/25850/6 ; we use c++17 features (i.e. optionals in ClientDataPackage.hpp) build_flags = diff --git a/client/client_satellite/test/TestClientDataPackage.cpp b/client/client_satellite/test/TestClientDataPackage.cpp index eea536cfbc63f7c6dc9797d8288a5887c384aa0c..694eec4dc26fab1041fa9c083963b694ee83cfe7 100644 --- a/client/client_satellite/test/TestClientDataPackage.cpp +++ b/client/client_satellite/test/TestClientDataPackage.cpp @@ -3,7 +3,7 @@ // #include "TestClientDataPackage.hpp" -#include "MeasurementData.hpp" +#include "Measurement.hpp" #include "Protocol.hpp" #include <vector> diff --git a/client/libs/LC7090203F/LC709203F.cpp b/client/libs/LC7090203F/LC709203F.cpp index 037130060b547c019b1ba30e8769f9d52c3f51e0..b182f1378cc7fecc15369c716322d3c732093d23 100644 --- a/client/libs/LC7090203F/LC709203F.cpp +++ b/client/libs/LC7090203F/LC709203F.cpp @@ -39,13 +39,13 @@ uint8_t i2c_address = LC709203F_I2C_ADDR; * @return True if initialization was successful, otherwise false. */ bool LC709203F::begin(void) { - Wire.begin(); - setPowerMode(LC709203F_POWER_OPERATE); - setCellCapacity(LC709203F_APA_1000MAH); - setTemperatureMode(LC709203F_TEMPERATURE_I2C); - setCellProfile(LC709203_NOM3p7_Charge4p2); + Wire.begin(); + setPowerMode(LC709203F_POWER_OPERATE); + setCellCapacity(LC709203F_APA_1000MAH); + setTemperatureMode(LC709203F_TEMPERATURE_I2C); + setCellProfile(LC709203_NOM3p7_Charge4p2); - return true; + return true; } @@ -54,9 +54,9 @@ bool LC709203F::begin(void) { * @return 16-bit value read from LC709203F_RO_ICVERSION registers */ uint16_t LC709203F::getICversion(void) { - uint16_t vers = 0; - vers = read16(LC709203F_RO_ICVERSION); - return vers; + uint16_t vers = 0; + vers = read16(LC709203F_RO_ICVERSION); + return vers; } @@ -65,7 +65,7 @@ uint16_t LC709203F::getICversion(void) { * @return */ void LC709203F::initRSOC(void) { - write16(LC709203F_WO_INITRSOC, 0xAA55); + write16(LC709203F_WO_INITRSOC, 0xAA55); } @@ -74,9 +74,9 @@ void LC709203F::initRSOC(void) { * @return Cell voltage in milliVolt */ uint16_t LC709203F::cellVoltage_mV(void) { - uint16_t mV = 0; - mV = read16(LC709203F_RO_CELLVOLTAGE); - return 1000 * (mV / 1000.0); + uint16_t mV = 0; + mV = read16(LC709203F_RO_CELLVOLTAGE); + return 1000 * (mV / 1000.0); } @@ -85,9 +85,9 @@ uint16_t LC709203F::cellVoltage_mV(void) { * @return point value from 0 to 1000 */ uint16_t LC709203F::cellRemainingPercent10(void) { - uint16_t percent = 0; - percent = read16(LC709203F_RO_ITE); - return percent; + uint16_t percent = 0; + percent = read16(LC709203F_RO_ITE); + return percent; } /*! @@ -95,9 +95,9 @@ uint16_t LC709203F::cellRemainingPercent10(void) { * @return point value from 0 to 100 */ uint16_t LC709203F::cellStateOfCharge(void) { - uint16_t percent = 0; - percent = read16(LC709203F_RW_RSOC); - return percent; + uint16_t percent = 0; + percent = read16(LC709203F_RW_RSOC); + return percent; } @@ -106,9 +106,9 @@ uint16_t LC709203F::cellStateOfCharge(void) { * @return value from -20 to 60 *C // CdB Needs testing, no thermistor on ESP32_Bat_R2 board */ uint16_t LC709203F::getCellTemperature(void) { - uint16_t temp = 0; - temp = read16(LC709203F_RW_CELLTEMPERATURE); - return temp; + uint16_t temp = 0; + temp = read16(LC709203F_RW_CELLTEMPERATURE); + return temp; } @@ -118,7 +118,7 @@ uint16_t LC709203F::getCellTemperature(void) { * LC709203F_TEMPERATURE_THERMISTOR */ void LC709203F::setTemperatureMode(lc709203_tempmode_t t) { - return write16(LC709203F_RW_STATUSBIT, (uint16_t) t); + return write16(LC709203F_RW_STATUSBIT, (uint16_t) t); } @@ -127,7 +127,7 @@ void LC709203F::setTemperatureMode(lc709203_tempmode_t t) { * @param apa The lc709203_adjustment_t enumerated approximate cell capacity */ void LC709203F::setCellCapacity(lc709203_adjustment_t apa) { - write16(LC709203F_RW_APA, (uint16_t) apa); + write16(LC709203F_RW_APA, (uint16_t) apa); } @@ -136,7 +136,7 @@ void LC709203F::setCellCapacity(lc709203_adjustment_t apa) { * @param percent The threshold value, set to 0 to disable alarm */ void LC709203F::setAlarmRSOC(uint8_t percent) { - write16(LC709203F_RW_ALARMRSOC, percent); + write16(LC709203F_RW_ALARMRSOC, percent); } @@ -145,7 +145,7 @@ void LC709203F::setAlarmRSOC(uint8_t percent) { * @param voltage The threshold value, set to 0 to disable alarm */ void LC709203F::setAlarmVoltage(float voltage) { - write16(LC709203F_RW_ALARMVOLT, voltage * 1000); + write16(LC709203F_RW_ALARMVOLT, voltage * 1000); } @@ -156,7 +156,7 @@ void LC709203F::setAlarmVoltage(float voltage) { * @return */ void LC709203F::setPowerMode(lc709203_powermode_t t) { - write16(LC709203F_RW_POWERMODE, (uint16_t) t); + write16(LC709203F_RW_POWERMODE, (uint16_t) t); } /*! @@ -165,7 +165,7 @@ void LC709203F::setPowerMode(lc709203_powermode_t t) { * @return */ void LC709203F::setCellProfile(lc709203_cell_profile_t t) { - write16(LC709203F_RW_PROFILE, (uint16_t) t); + write16(LC709203F_RW_PROFILE, (uint16_t) t); } /*! @@ -173,9 +173,9 @@ void LC709203F::setCellProfile(lc709203_cell_profile_t t) { * @return The uint16_t Beta value */ uint16_t LC709203F::getThermistorBeta(void) { - uint16_t val = 0; - val = read16(LC709203F_RW_THERMISTORB); - return val; + uint16_t val = 0; + val = read16(LC709203F_RW_THERMISTORB); + return val; } @@ -185,17 +185,19 @@ uint16_t LC709203F::getThermistorBeta(void) { * @return */ void LC709203F::setThermistorB(uint16_t beta) { - write16(LC709203F_RW_THERMISTORB, beta); + write16(LC709203F_RW_THERMISTORB, beta); } std::list<Message> LC709203F::buildMessages() { - std::list<Message> messages; - float data = cellVoltage_mV() / 1000.0; - MeasurementData IncrementData{data, 0, {}, - measurementTypeToString.at(MeasurementType::BATTERY_VOLTAGE)}; - messages.emplace_back(IncrementData, sensorInformation, Time::getInstance().getEpochSeconds()); - return messages; + std::list<Message> messages; + float data = cellVoltage_mV() / 1000.0; + Measurement IncrementData{data, 0, NO_I2C_ADDRESS, MeasurementType::BATTERY_VOLTAGE, + ErrorType::DATA_OK}; + messages.emplace_back(IncrementData, + sensorInformation, + Time::getInstance().getEpochSeconds()); + return messages; } @@ -216,52 +218,52 @@ std::list<Message> LC709203F::buildMessages() { * @return The computed CRC8 value. */ static uint8_t crc8(uint8_t *data, int len) { - const uint8_t POLYNOMIAL(0x07); - uint8_t crc(0x00); + const uint8_t POLYNOMIAL(0x07); + uint8_t crc(0x00); - for (int j = len; j; --j) { - crc ^= *data++; + for (int j = len; j; --j) { + crc ^= *data++; - for (int i = 8; i; --i) { - crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1); - } + for (int i = 8; i; --i) { + crc = (crc & 0x80) ? (crc << 1) ^ POLYNOMIAL : (crc << 1); } - return crc; + } + return crc; } // writes a 16-bit word (d) to register pointer regAddress // when selecting a register pointer to read from, data = 0 void LC709203F::write16(uint8_t regAddress, uint16_t data) { - // Setup array to hold bytes to send including CRC-8 - uint8_t crcArray[5]; - crcArray[0] = 0x16; - crcArray[1] = regAddress; - crcArray[2] = lowByte(data); - crcArray[3] = highByte(data); - // Calculate crc of preceding four bytes and place in crcArray[4] - crcArray[4] = crc8(crcArray, 4); - // Device address - Wire.beginTransmission(i2c_address); - // Register address - Wire.write(regAddress); - // low byte - Wire.write(crcArray[2]); - // high byte - Wire.write(crcArray[3]); - // Send crc8 - Wire.write(crcArray[4]); - Wire.endTransmission(); + // Setup array to hold bytes to send including CRC-8 + uint8_t crcArray[5]; + crcArray[0] = 0x16; + crcArray[1] = regAddress; + crcArray[2] = lowByte(data); + crcArray[3] = highByte(data); + // Calculate crc of preceding four bytes and place in crcArray[4] + crcArray[4] = crc8(crcArray, 4); + // Device address + Wire.beginTransmission(i2c_address); + // Register address + Wire.write(regAddress); + // low byte + Wire.write(crcArray[2]); + // high byte + Wire.write(crcArray[3]); + // Send crc8 + Wire.write(crcArray[4]); + Wire.endTransmission(); } int16_t LC709203F::read16(uint8_t regAddress) { - int16_t data = 0; - Wire.beginTransmission(i2c_address); - Wire.write(regAddress); - Wire.endTransmission(false); - Wire.requestFrom(i2c_address, static_cast<uint8_t>(2)); - uint8_t lowByteData = Wire.read(); - uint8_t highByteData = Wire.read(); - data = word(highByteData, lowByteData); - return (data); + int16_t data = 0; + Wire.beginTransmission(i2c_address); + Wire.write(regAddress); + Wire.endTransmission(false); + Wire.requestFrom(i2c_address, static_cast<uint8_t>(2)); + uint8_t lowByteData = Wire.read(); + uint8_t highByteData = Wire.read(); + data = word(highByteData, lowByteData); + return (data); } diff --git a/client/libs/LC7090203F/LC709203F.h b/client/libs/LC7090203F/LC709203F.h index 75ed26c319f43810ea13e331a9b28854d3611081..d2624df882e6d55bbd50ac9543232ff6af54883c 100644 --- a/client/libs/LC7090203F/LC709203F.h +++ b/client/libs/LC7090203F/LC709203F.h @@ -40,33 +40,33 @@ static uint8_t crc8(uint8_t *data, int len); /*! Approx cell capacity Table 7 */ typedef enum { - LC709203F_APA_100MAH = 0x08, - LC709203F_APA_200MAH = 0x0B, - LC709203F_APA_500MAH = 0x10, - LC709203F_APA_1000MAH = 0x19, - LC709203F_APA_2000MAH = 0x2D, - LC709203F_APA_3000MAH = 0x36, + LC709203F_APA_100MAH = 0x08, + LC709203F_APA_200MAH = 0x0B, + LC709203F_APA_500MAH = 0x10, + LC709203F_APA_1000MAH = 0x19, + LC709203F_APA_2000MAH = 0x2D, + LC709203F_APA_3000MAH = 0x36, } lc709203_adjustment_t; /*! Cell profile */ typedef enum { - LC709203_NOM3p7_Charge4p2 = 1, - LC709203_NOM3p8_Charge4p35 = 3, - LC709203_NOM3p8_Charge4p35_Less500mAh = 6, - LC709203_ICR18650_SAMSUNG = 5, - LC709203_ICR18650_PANASONIC = 4 + LC709203_NOM3p7_Charge4p2 = 1, + LC709203_NOM3p8_Charge4p35 = 3, + LC709203_NOM3p8_Charge4p35_Less500mAh = 6, + LC709203_ICR18650_SAMSUNG = 5, + LC709203_ICR18650_PANASONIC = 4 } lc709203_cell_profile_t; /*! Cell temperature source */ typedef enum { - LC709203F_TEMPERATURE_I2C = 0x0000, - LC709203F_TEMPERATURE_THERMISTOR = 0x0001, + LC709203F_TEMPERATURE_I2C = 0x0000, + LC709203F_TEMPERATURE_THERMISTOR = 0x0001, } lc709203_tempmode_t; /*! Chip power state */ typedef enum { - LC709203F_POWER_OPERATE = 0x0001, - LC709203F_POWER_SLEEP = 0x0002, + LC709203F_POWER_OPERATE = 0x0001, + LC709203F_POWER_SLEEP = 0x0002, } lc709203_powermode_t; /*! @@ -74,57 +74,55 @@ typedef enum { * the LC709203F I2C LiPo monitor */ class LC709203F { -public: - LC709203F(); + // FIXME: This does not conform to FORTE's interface - ~LC709203F(); + public: + LC709203F(); - bool begin(void); + ~LC709203F(); - void initRSOC(void); + bool begin(void); - void setPowerMode(lc709203_powermode_t t); + void initRSOC(void); - void setCellCapacity(lc709203_adjustment_t apa); + void setPowerMode(lc709203_powermode_t t); - void setCellProfile(lc709203_cell_profile_t t); + void setCellCapacity(lc709203_adjustment_t apa); - uint16_t getICversion(void); + void setCellProfile(lc709203_cell_profile_t t); - uint16_t cellVoltage_mV(void); + uint16_t getICversion(void); - uint16_t cellRemainingPercent10(void); // Remaining capacity in increments of 0.1% as integer - uint16_t cellStateOfCharge(void); // In increments of 1% as integer + uint16_t cellVoltage_mV(void); + uint16_t + cellRemainingPercent10(void); // Remaining capacity in increments of 0.1% as integer + uint16_t + cellStateOfCharge(void); // In increments of 1% as integer - uint16_t getThermistorBeta(void); - void setThermistorB(uint16_t beta); + uint16_t getThermistorBeta(void); - void setTemperatureMode(lc709203_tempmode_t t); + void setThermistorB(uint16_t beta); - uint16_t getCellTemperature(void); + void setTemperatureMode(lc709203_tempmode_t t); - void setAlarmRSOC(uint8_t percent); + uint16_t getCellTemperature(void); - void setAlarmVoltage(float voltage); + void setAlarmRSOC(uint8_t percent); - std::list<Message> buildMessages(); + void setAlarmVoltage(float voltage); -protected: - void write16(uint8_t regAddress, uint16_t data); + std::list<Message> buildMessages(); - int16_t read16(uint8_t regAddress); + protected: + void write16(uint8_t regAddress, uint16_t data); -private: - const SensorInformation sensorInformation{"LC709203", Protocol::I2C}; + int16_t read16(uint8_t regAddress); - enum class MeasurementType { - BATTERY_VOLTAGE, - }; - - std::map<MeasurementType, const char *> measurementTypeToString = { - {MeasurementType::BATTERY_VOLTAGE, "BATTERY_VOLTAGE"}}; + private: + const SensorInformation + sensorInformation{HardwareName::LC709203, SensorProtocol::I2C}; }; #endif diff --git a/client/libs/SentecSensors/SentecSensors.cpp b/client/libs/SentecSensors/SentecSensors.cpp index 91c60c148c88cdaa612857e426ef9d611e8ebcc0..4029f3dcd049c916a852f92f2cc6ca279d015e59 100644 --- a/client/libs/SentecSensors/SentecSensors.cpp +++ b/client/libs/SentecSensors/SentecSensors.cpp @@ -1,345 +1,324 @@ #include "NoDataAvailableException.hpp" #include <SentecSensors.h> + /*************************************** * RS485 SENSOR READOUT ****************************************/ static const char *TAG = "SENTEC"; -SentecSensorRS485::SentecSensorRS485(HardwareSerial *ser, byte add) -{ - address = add; - RS485 = ser; + +SentecSensorRS485::SentecSensorRS485(HardwareSerial *ser, byte add) { + address = add; + RS485 = ser; } -SentecSensorRS485::SentecSensorRS485(HardwareSerial *ser, byte add, uint8_t serialControlPin) -{ - address = add; - RS485 = ser; - serialCommunicationControlPin = serialControlPin; + +SentecSensorRS485::SentecSensorRS485(HardwareSerial *ser, byte add, uint8_t serialControlPin) { + address = add; + RS485 = ser; + serialCommunicationControlPin = serialControlPin; } -void SentecSensorRS485::write(byte queryFrame[], int length) -{ - // sends a message (bytes) to the sensor - - // Initialize the transmitter - digitalWrite(serialCommunicationControlPin, HIGH); - // Send message: request a reading from the sensor - RS485->write(queryFrame, length); - RS485->flush(); - // Initialize the receiver - digitalWrite(serialCommunicationControlPin, LOW); +void SentecSensorRS485::write(byte queryFrame[], int length) { + // sends a message (bytes) to the sensor + + // Initialize the transmitter + digitalWrite(serialCommunicationControlPin, HIGH); + // Send message: request a reading from the sensor + RS485->write(queryFrame, length); + RS485->flush(); + // Initialize the receiver + digitalWrite(serialCommunicationControlPin, LOW); } -String SentecSensorRS485::getValueStr(float value) -{ - if (valid) { - return String(value, 1); - } else { - return String("null"); - } +String SentecSensorRS485::getValueStr(float value) { + if (valid) { + return String(value, 1); + } else { + return String("null"); + } } -String SentecSensorRS485::getValueStr(int value) -{ - if (valid) { - return String(value); - } else { - return String("null"); - } +String SentecSensorRS485::getValueStr(int value) { + if (valid) { + return String(value); + } else { + return String("null"); + } } -SentecSensorRS485::ReturnCode SentecSensorRS485::queryAddress() -{ - // request the address of the sensor with ONLY ONE SENSOR ON THE BUS +ErrorType SentecSensorRS485::queryAddress() { + // request the address of the sensor with ONLY ONE SENSOR ON THE BUS - byte tmp_addr = address; // store the address in a temporary byte - address = 0xFF; // change the address to FF (0) for address check - SentecSensorRS485::ReturnCode tmp = readRegister(word(0x07, 0xD0), 2); - address = tmp_addr; // set the original address back - return tmp; + byte tmp_addr = address; // store the address in a temporary byte + address = 0xFF; // change the address to FF (0) for address check + ErrorType tmp = readRegister(word(0x07, 0xD0), 2); + address = tmp_addr; // set the original address back + return tmp; } -SentecSensorRS485::ReturnCode SentecSensorRS485::readRegister(int registerStartAddress) -{ - return readRegister(registerStartAddress, 1); +ErrorType SentecSensorRS485::readRegister(int registerStartAddress) { + return readRegister(registerStartAddress, 1); } -SentecSensorRS485::ReturnCode SentecSensorRS485::readRegister(int registerStartAddress, int registerLength) -{ - // function code 0x03: get data measured by the sensor - byte query[8]; - query[0] = address; - // function code - query[1] = 0x03; - // register start address - query[2] = registerStartAddress >> 8; - query[3] = registerStartAddress & 0xFF; - // register length - query[4] = registerLength >> 8; - query[5] = registerLength & 0xFF; - // calculate last two bytes (CRC check) - calculateCRC(query, sizeof(query) - 2); - // # Serial.print("Query (get data): 0x"); #Print bytes - // # printBytes(query, 8); - // write the data request to the modbus line - write(query, sizeof(query)); - // get response from sensor - return getResponse(); +ErrorType SentecSensorRS485::readRegister(int registerStartAddress, int registerLength) { + // function code 0x03: get data measured by the sensor + byte query[8]; + query[0] = address; + // function code + query[1] = 0x03; + // register start address + query[2] = registerStartAddress >> 8; + query[3] = registerStartAddress & 0xFF; + // register length + query[4] = registerLength >> 8; + query[5] = registerLength & 0xFF; + // calculate last two bytes (CRC check) + calculateCRC(query, sizeof(query) - 2); + // # Serial.print("Query (get data): 0x"); #Print bytes + // # printBytes(query, 8); + // write the data request to the modbus line + write(query, sizeof(query)); + // get response from sensor + return getResponse(); } -SentecSensorRS485::ReturnCode SentecSensorRS485::writeRegister(int registerAddress, int value) -{ - // function code 0x06: change sensor settings - // e.g. a new address, reset rainfal data... - - byte query[8]; - query[0] = address; - // function code - query[1] = 0x06; - // register start address - query[2] = registerAddress >> 8; - query[3] = registerAddress & 0xFF; - // register length - query[4] = value >> 8; - query[5] = value & 0xFF; - calculateCRC(query, sizeof(query) - 2); - ESP_LOGD(TAG, "Query (settings): "); - printBytes(query, 8); - write(query, sizeof(query)); - return getResponse(); +ErrorType SentecSensorRS485::writeRegister(int registerAddress, int value) { + // function code 0x06: change sensor settings + // e.g. a new address, reset rainfal data... + + byte query[8]; + query[0] = address; + // function code + query[1] = 0x06; + // register start address + query[2] = registerAddress >> 8; + query[3] = registerAddress & 0xFF; + // register length + query[4] = value >> 8; + query[5] = value & 0xFF; + calculateCRC(query, sizeof(query) - 2); + ESP_LOGD(TAG, "Query (settings): "); + printBytes(query, 8); + write(query, sizeof(query)); + return getResponse(); } -SentecSensorRS485::ReturnCode SentecSensorRS485::setAddress(byte add) -{ - // change the address of a sensor - SentecSensorRS485::ReturnCode tmp = writeRegister(word(0x07, 0xD0), add); - if (tmp == SentecSensorRS485::ReturnCode::OK) - address = add; - // TODO check response: matches the sent message exactly - return tmp; +ErrorType SentecSensorRS485::setAddress(byte add) { + // change the address of a sensor + ErrorType tmp = writeRegister(word(0x07, 0xD0), add); + if (tmp == ErrorType::DATA_OK) + address = add; + // TODO check response: matches the sent message exactly + return tmp; } -void SentecSensorRS485::resetAnswerFrame() -{ - for (int i = 0; i < 10; i++) { - answerFrame[i] = 0; - } +void SentecSensorRS485::resetAnswerFrame() { + for (int i = 0; i < 10; i++) { + answerFrame[i] = 0; + } } -SentecSensorRS485::ReturnCode SentecSensorRS485::getResponse() -{ - // reads the response of a sensor - SentecSensorRS485::ReturnCode returnCode = SentecSensorRS485::ReturnCode::OK; - valid = true; - int idx = 0; - int byteReceived; - // usual response length: changed in the while loop to match the response, - // changed only when reading data (then it's 7 or 9 bytes, sensor dpendent) - int responseLength = 8; - // reading an answer takes up to 39 milliseconds for 2 byte readRegister - const int timeout = 200; - const int retries = 1; // #editet to q - size_t tries = 1; - // it doesn't seem to help to request multiple times if first time goes wrong - for (tries; tries <= retries; tries++) { - // if we lose connection with the sensor, we get an array of zeros back - resetAnswerFrame(); - - unsigned long time = millis(); - while (idx < responseLength && (millis() - time) < timeout) { - if (RS485->available()) { - byteReceived = RS485->read(); - // Serial.println(byteReceived, HEX); - // check for first byte. It has to be the device address unless for broadcasts with address = 0xFF - if (idx == 0 && address != 0xFF && byteReceived != address) { - ESP_LOGE(TAG, "Invalid byte. First byte needs to be address 0x%02X but got 0x%02Xinstead"); - } else { - answerFrame[idx] = byteReceived; - // for reading register: third received byte is data length, read number of bytes accordingly - if (idx == 2 && answerFrame[1] == 0x03) { - // 5 bytes for address, function code, data length, CRC_H, CRC_L - responseLength = 5 + byteReceived; - } - idx++; - } - } - } - ESP_LOGD(TAG, "-----------------------------------------------------------------------------------------"); - ESP_LOGD(TAG, "Response: 0x%s", formatBytes(answerFrame, responseLength).c_str()); - // ESP_LOGD(TAG, "Tries: %d", tries); - // ESP_LOGD(TAG, "Bytes received: %d", idx); - if (answerFrame[0] == 0) { - ESP_LOGE(TAG, "Check sensor connection. First byte is 0x00."); - valid = false; - returnCode = SentecSensorRS485::ReturnCode::NO_CONNECTION; - } else if (idx < responseLength) { - ESP_LOGE(TAG, "Response too short: %d bytes < %d bytes. Unfinished transmission.", idx, responseLength); - valid = false; - returnCode = SentecSensorRS485::ReturnCode::SHORT_RESPONSE; - } - word crc_received = word(answerFrame[responseLength - 2], answerFrame[responseLength - 1]); - word crc = calculateCRC(answerFrame, responseLength - 2); - if (valid && crc_received != word(crc)) { - ESP_LOGE(TAG, "CRC wrong: Expected 0x%s got 0x%s", formatBytes(crc).c_str(), - formatBytes(crc_received).c_str()); - valid = false; - resetAnswerFrame(); - returnCode = SentecSensorRS485::ReturnCode::WRONG_CRC; - } - // breaking after first successfull try - if (returnCode == SentecSensorRS485::ReturnCode::OK) { - break; - } - } - - // ESP_LOGE(TAG, "Returncode: %d", returnCode); - return returnCode; +ErrorType SentecSensorRS485::getResponse() { + // reads the response of a sensor + ErrorType returnCode = ErrorType::DATA_OK; + valid = true; + int idx = 0; + int byteReceived; + // usual response length: changed in the while loop to match the response, + // changed only when reading data (then it's 7 or 9 bytes, sensor dpendent) + int responseLength = 8; + // reading an answer takes up to 39 milliseconds for 2 byte readRegister + const int timeout = 200; + const int retries = 1; // #editet to q + size_t tries = 1; + // it doesn't seem to help to request multiple times if first time goes wrong + for (tries; tries <= retries; tries++) { + // if we lose connection with the sensor, we get an array of zeros back + resetAnswerFrame(); + + unsigned long time = millis(); + while (idx < responseLength && (millis() - time) < timeout) { + if (RS485->available()) { + byteReceived = RS485->read(); + // Serial.println(byteReceived, HEX); + // check for first byte. It has to be the device address unless for broadcasts with address = 0xFF + if (idx == 0 && address != 0xFF && byteReceived != address) { + ESP_LOGE(TAG, + "Invalid byte. First byte needs to be address 0x%02X but got 0x%02Xinstead"); + } else { + answerFrame[idx] = byteReceived; + // for reading register: third received byte is data length, read number of bytes accordingly + if (idx == 2 && answerFrame[1] == 0x03) { + // 5 bytes for address, function code, data length, CRC_H, CRC_L + responseLength = 5 + byteReceived; + } + idx++; + } + } + } + ESP_LOGD(TAG, + "-----------------------------------------------------------------------------------------"); + ESP_LOGD(TAG, "Response: 0x%s", formatBytes(answerFrame, responseLength).c_str()); + // ESP_LOGD(TAG, "Tries: %d", tries); + // ESP_LOGD(TAG, "Bytes received: %d", idx); + if (answerFrame[0] == 0) { + ESP_LOGE(TAG, "Check sensor connection. First byte is 0x00."); + valid = false; + returnCode = ErrorType::SENSOR_NOT_CONNECTED; + } else if (idx < responseLength) { + ESP_LOGE(TAG, "Response too short: %d bytes < %d bytes. Unfinished transmission.", idx, + responseLength); + valid = false; + returnCode = ErrorType::CONNECTION_ENDED_PREMATURELY; + } + word crc_received = word(answerFrame[responseLength - 2], answerFrame[responseLength - 1]); + word crc = calculateCRC(answerFrame, responseLength - 2); + if (valid && crc_received != word(crc)) { + ESP_LOGE(TAG, "CRC wrong: Expected 0x%s got 0x%s", formatBytes(crc).c_str(), + formatBytes(crc_received).c_str()); + valid = false; + resetAnswerFrame(); + returnCode = ErrorType::WRONG_CRC; + } + // breaking after first successfull try + if (returnCode == ErrorType::DATA_OK) { + break; + } + } + + // ESP_LOGE(TAG, "Returncode: %d", returnCode); + return returnCode; } -unsigned int SentecSensorRS485::calculateCRC(byte query[], int length) -{ - // Change the last two bytes of the queryFrame to conform to a CRC check - // Yes, this is necessary. No, I don't know exactly what it does. - unsigned int tmp1, tmp2, flag; - tmp1 = 0xFFFF; - for (unsigned char i = 0; i < length; i++) { - tmp1 = tmp1 ^ query[i]; - for (unsigned char j = 1; j <= 8; j++) { - flag = tmp1 & 0x0001; - tmp1 >>= 1; - if (flag) - tmp1 ^= 0xA001; - } - } - // Reverse byte order. - tmp2 = tmp1 >> 8; - tmp1 = (tmp1 << 8) | tmp2; - tmp1 &= 0xFFFF; - // change last two query bytes - query[length + 1] = tmp1; - query[length] = tmp1 >> 8; - - return tmp1; // the returned value is already swapped - CRC_L byte is first & CRC_H byte is last +unsigned int SentecSensorRS485::calculateCRC(byte query[], int length) { + // Change the last two bytes of the queryFrame to conform to a CRC check + // Yes, this is necessary. No, I don't know exactly what it does. + unsigned int tmp1, tmp2, flag; + tmp1 = 0xFFFF; + for (unsigned char i = 0; i < length; i++) { + tmp1 = tmp1 ^ query[i]; + for (unsigned char j = 1; j <= 8; j++) { + flag = tmp1 & 0x0001; + tmp1 >>= 1; + if (flag) + tmp1 ^= 0xA001; + } + } + // Reverse byte order. + tmp2 = tmp1 >> 8; + tmp1 = (tmp1 << 8) | tmp2; + tmp1 &= 0xFFFF; + // change last two query bytes + query[length + 1] = tmp1; + query[length] = tmp1 >> 8; + + return tmp1; // the returned value is already swapped - CRC_L byte is first & CRC_H byte is last } -void SentecSensorRS485::printBytes(byte *data, int length) -{ - // prints 8-bit data in hex with leading zeroes - char tmp[16]; - for (int i = 0; i < length; i++) { - sprintf(tmp, "%.2X", data[i]); - // sprintf(tmp, "0x%.2X",data[i]); - Serial.print(tmp); - Serial.print(" "); - } - Serial.println(); +void SentecSensorRS485::printBytes(byte *data, int length) { + // prints 8-bit data in hex with leading zeroes + char tmp[16]; + for (int i = 0; i < length; i++) { + sprintf(tmp, "%.2X", data[i]); + // sprintf(tmp, "0x%.2X",data[i]); + Serial.print(tmp); + Serial.print(" "); + } + Serial.println(); } -void SentecSensorRS485::printBytes(word data) -{ - char tmp[10]; - sprintf(tmp, "0x%.2X", data >> 8); - Serial.print(tmp); - sprintf(tmp, "%.2X", data & 0xFF); - // sprintf(tmp, "0x%.2X",data[i]); - Serial.print(tmp); +void SentecSensorRS485::printBytes(word data) { + char tmp[10]; + sprintf(tmp, "0x%.2X", data >> 8); + Serial.print(tmp); + sprintf(tmp, "%.2X", data & 0xFF); + // sprintf(tmp, "0x%.2X",data[i]); + Serial.print(tmp); } -std::string SentecSensorRS485::formatBytes(byte *data, int length) -{ - // pls don't hate me for building strings like that. - // I also wish that I would be good at programming - std::string tmp; - // prints 8-bit data in hex with leading zeroes - char buff[4]; - for (int i = 0; i < length; i++) { - snprintf(buff, sizeof(buff), "%02X ", data[i]); - tmp += buff; - } - return tmp; +std::string SentecSensorRS485::formatBytes(byte *data, int length) { + // pls don't hate me for building strings like that. + // I also wish that I would be good at programming + std::string tmp; + // prints 8-bit data in hex with leading zeroes + char buff[4]; + for (int i = 0; i < length; i++) { + snprintf(buff, sizeof(buff), "%02X ", data[i]); + tmp += buff; + } + return tmp; } -std::string SentecSensorRS485::formatBytes(word data) -{ - byte arr[2] = {data >> 8, data & 0xFF}; - return formatBytes(arr, 2); +std::string SentecSensorRS485::formatBytes(word data) { + byte arr[2] = {data >> 8, data & 0xFF}; + return formatBytes(arr, 2); } -word SolarRadiationSensor::getSolarRadiation() -{ - readRegister(0, 1); - glob = word(answerFrame[3], answerFrame[4]); - ESP_LOGI(TAG, "Global solar radiation: %d W/m^2", glob); - return glob; +word SolarRadiationSensor::getSolarRadiation() { + readRegister(0, 1); + glob = word(answerFrame[3], answerFrame[4]); + ESP_LOGI(TAG, "Global solar radiation: %d W/m^2", glob); + return glob; } -String SolarRadiationSensor::getSolarRadiationStr() -{ - return getValueStr((int)glob); +String SolarRadiationSensor::getSolarRadiationStr() { + return getValueStr((int) glob); } -void RainGaugeSensor::resetPrecipitation() -{ - // clears rainfall rata from the rain gauge - // delay resetting after the last register reading - delay(100); - Serial.println("Resetting precipitation sum"); - writeRegister(0x00, 0x5A); - // TODO check response: matches the sent message exactly +void RainGaugeSensor::resetPrecipitation() { + // clears rainfall rata from the rain gauge + // delay resetting after the last register reading + delay(100); + Serial.println("Resetting precipitation sum"); + writeRegister(0x00, 0x5A); + // TODO check response: matches the sent message exactly } -void RainGaugeSensor::resetSensor() -{ - // no clue what the difference between this one and the previous one is... - // and the manual is not helpful, of course - delay(100); - Serial.println("Resetting precipitation sum"); - writeRegister(0x37, 0x03); - // TODO check response: matches the sent message exactly +void RainGaugeSensor::resetSensor() { + // no clue what the difference between this one and the previous one is... + // and the manual is not helpful, of course + delay(100); + Serial.println("Resetting precipitation sum"); + writeRegister(0x37, 0x03); + // TODO check response: matches the sent message exactly } -word RainGaugeSensor::getInstantaneousPrecipitation() -{ - // gets tips of scale since the last reset, i.e. total precipitation (I THINK) - // manual says this is current precipitation - is it? - readRegister(0, 0x01); - precipitation = word(answerFrame[3], answerFrame[4]); +word RainGaugeSensor::getInstantaneousPrecipitation() { + // gets tips of scale since the last reset, i.e. total precipitation (I THINK) + // manual says this is current precipitation - is it? + readRegister(0, 0x01); + precipitation = word(answerFrame[3], answerFrame[4]); - ESP_LOGI(TAG, "Precipitation: %.1f mm", precipitation / 10.0); - // resetPrecipitation(); - return precipitation; + ESP_LOGI(TAG, "Precipitation: %.1f mm", precipitation / 10.0); + // resetPrecipitation(); + return precipitation; } -String RainGaugeSensor::getPrecipitationStr() -{ - return getValueStr((float)(precipitation / 10.0)); +String RainGaugeSensor::getPrecipitationStr() { + return getValueStr((float) (precipitation / 10.0)); } -float SoilMoistureSensor::getMoistureTemp() -{ - readRegister(0, 2); // start register at 0, read 2 variables (vwc, soil temp) - moistureRaw = (answerFrame[3] << 8) + answerFrame[4]; - // TODO: neg. temp check - if (answerFrame[5] < 0x80) { - temperatureRaw = (answerFrame[5] << 8) + answerFrame[6]; - } else { - temperatureRaw = (answerFrame[5] << 8) + answerFrame[6] - 65536; - } - ESP_LOGI(TAG, "Soil moisture: %.1f %", (moistureRaw - moistureOffset) / 10.0); - ESP_LOGI(TAG, "Soil temperature: %.1f °C", (temperatureRaw - temperatureOffset) / 10.0); - return (temperatureRaw - temperatureOffset) / 10.0; +float SoilMoistureSensor::getMoistureTemp() { + readRegister(0, 2); // start register at 0, read 2 variables (vwc, soil temp) + moistureRaw = (answerFrame[3] << 8) + answerFrame[4]; + // TODO: neg. temp check + if (answerFrame[5] < 0x80) { + temperatureRaw = (answerFrame[5] << 8) + answerFrame[6]; + } else { + temperatureRaw = (answerFrame[5] << 8) + answerFrame[6] - 65536; + } + ESP_LOGI(TAG, "Soil moisture: %.1f %", (moistureRaw - moistureOffset) / 10.0); + ESP_LOGI(TAG, "Soil temperature: %.1f °C", (temperatureRaw - temperatureOffset) / 10.0); + return (temperatureRaw - temperatureOffset) / 10.0; } -float SoilMoistureSensor::getMoisture() -{ - return (moistureRaw - moistureOffset) / 10.0; +float SoilMoistureSensor::getMoisture() { + return (moistureRaw - moistureOffset) / 10.0; } -String SoilMoistureSensor::getMoistureStr() -{ - return getValueStr((float)((moistureRaw - moistureOffset) / 10.0)); +String SoilMoistureSensor::getMoistureStr() { + return getValueStr((float) ((moistureRaw - moistureOffset) / 10.0)); } -String SoilMoistureSensor::getTemperatureStr() -{ - return getValueStr((float)((temperatureRaw - temperatureOffset) / 10.0)); +String SoilMoistureSensor::getTemperatureStr() { + return getValueStr((float) ((temperatureRaw - temperatureOffset) / 10.0)); } diff --git a/client/libs/SentecSensors/SentecSensors.h b/client/libs/SentecSensors/SentecSensors.h index 336011134eb736a5265f5aa2b0ae86919b41d1c0..55fb834cdc770fd43edfff579a1f3f5ce08d5af1 100644 --- a/client/libs/SentecSensors/SentecSensors.h +++ b/client/libs/SentecSensors/SentecSensors.h @@ -2,79 +2,96 @@ #define SENTECSENSORS_H #include <Arduino.h> +#include <ErrorTypes.h> class SentecSensorRS485 { - public: - byte address; - uint8_t serialCommunicationControlPin = 19; - HardwareSerial *RS485; - byte answerFrame[10]; - // TODO use valid flag to log None - bool valid = false; - SentecSensorRS485(HardwareSerial *ser, byte add); - SentecSensorRS485(HardwareSerial *ser, byte add, uint8_t serialControlPin); - - enum class ReturnCode { - OK, - NO_CONNECTION, - SHORT_RESPONSE, // connection ended prematurely - WRONG_CRC // corrupted data package - }; - - void write(byte queryFrame[], int length); - String getValueStr(float value); - String getValueStr(int value); - SentecSensorRS485::ReturnCode queryAddress(); - SentecSensorRS485::ReturnCode readRegister(int registerStartAddress); - SentecSensorRS485::ReturnCode readRegister(int registerStartAddress, int registerLength); - SentecSensorRS485::ReturnCode writeRegister(int registerAddress, int value); - SentecSensorRS485::ReturnCode setAddress(byte add); - void resetAnswerFrame(); - SentecSensorRS485::ReturnCode getResponse(); - unsigned int calculateCRC(byte query[], int length); - void printBytes(byte *data, int length); - void printBytes(word data); - std::string formatBytes(byte *data, int length); - std::string formatBytes(word data); +public: + byte address; + uint8_t serialCommunicationControlPin = 19; + HardwareSerial *RS485; + byte answerFrame[10]; + // TODO use valid flag to log None + bool valid = false; + + SentecSensorRS485(HardwareSerial *ser, byte add); + + SentecSensorRS485(HardwareSerial *ser, byte add, uint8_t serialControlPin); + + void write(byte queryFrame[], int length); + + String getValueStr(float value); + + String getValueStr(int value); + + ErrorType queryAddress(); + + ErrorType readRegister(int registerStartAddress); + + ErrorType readRegister(int registerStartAddress, int registerLength); + + ErrorType writeRegister(int registerAddress, int value); + + ErrorType setAddress(byte add); + + void resetAnswerFrame(); + + ErrorType getResponse(); + + unsigned int calculateCRC(byte query[], int length); + + void printBytes(byte *data, int length); + + void printBytes(word data); + + std::string formatBytes(byte *data, int length); + + std::string formatBytes(word data); }; class SolarRadiationSensor : public SentecSensorRS485 { - public: - using SentecSensorRS485::SentecSensorRS485; - // global radiation [W/m^2] - word glob = 0; +public: + using SentecSensorRS485::SentecSensorRS485; + // global radiation [W/m^2] + word glob = 0; - word getSolarRadiation(); - String getSolarRadiationStr(); + word getSolarRadiation(); + + String getSolarRadiationStr(); }; class RainGaugeSensor : public SentecSensorRS485 { - public: - using SentecSensorRS485::SentecSensorRS485; - // precipitation values [mm] - word precipitation = 0; // prcp since reset? (I THINK!!!) - - void resetPrecipitation(); - void resetSensor(); - word getInstantaneousPrecipitation(); - String getPrecipitationStr(); +public: + using SentecSensorRS485::SentecSensorRS485; + // precipitation values [mm] + word precipitation = 0; // prcp since reset? (I THINK!!!) + + void resetPrecipitation(); + + void resetSensor(); + + word getInstantaneousPrecipitation(); + + String getPrecipitationStr(); }; class SoilMoistureSensor : public SentecSensorRS485 { - public: - using SentecSensorRS485::SentecSensorRS485; - // vwc: volumetric water content [%] - uint16_t moistureRaw = 0; - int moistureOffset = 0; - // soil temperature [deg C] - int temperatureRaw = 0; - int temperatureOffset = 0; - - float getMoistureTemp(); - float getMoisture(); - String getMoistureStr(); - String getTemperatureStr(); +public: + using SentecSensorRS485::SentecSensorRS485; + // vwc: volumetric water content [%] + uint16_t moistureRaw = 0; + int moistureOffset = 0; + // soil temperature [deg C] + int temperatureRaw = 0; + int temperatureOffset = 0; + + float getMoistureTemp(); + + float getMoisture(); + + String getMoistureStr(); + + String getTemperatureStr(); }; #endif diff --git a/client/libs/dr26_analogue/dr26.cpp b/client/libs/dr26_analogue/dr26.cpp index 1f01f0820e05d25db819606361626869352e3851..c0e7d0ee4959ea87d04043672a35fc82d59e4c2b 100644 --- a/client/libs/dr26_analogue/dr26.cpp +++ b/client/libs/dr26_analogue/dr26.cpp @@ -1,46 +1,47 @@ #include "dr26.hpp" static const char *TAG = "DR26"; + void ForteDR26::setup() { Wire.begin(); - ads1.setGain(GAIN_ONE); - ads1.setDataRate(RATE_ADS1115_8SPS); - ads1.begin() ? ESP_LOGD(TAG, "ADS initialized") : ESP_LOGE(TAG, "failed to initialize ADS"); + ads.setGain(GAIN_ONE); + ads.setDataRate(RATE_ADS1115_8SPS); + if (ads.begin()) { + ESP_LOGD(TAG, "ADS initialized"); + sensorConnected = true; + } else { + ESP_LOGE(TAG, "failed to initialize ADS"); + sensorConnected = false; + } + delay(100); channel = 0; } -float ForteDR26::readData() { +Measurement ForteDR26::readData() { float volts = 0; - - // adjust data rate instead of yourself. ADC averages on its on with low data rate - // FIXME: Test which datarate gets most stable result. If it still is not good enough consider adding a small capacitor or averaging again - // for (int i = 0; i < 10; i++) { - // int16_t adc = 0; - // float volt = 0; - // try { - // adc = ads1.readADC_SingleEnded(channel); - // volt = ads1.computeVolts(adc); - // } - // catch (NoDataAvailableException &e) { - // throw NoDataAvailableException(); //propagate exception - // } - // volts += volt; - // } - - // volts /= 10; - - int16_t adc = 0; + try { - adc = ads1.readADC_SingleEnded(channel); - volts = ads1.computeVolts(adc); + // TODO: This function might never return if conversionComplete is never true. Check if this is the case + // FIXME: How to notice from the dr if a value is incorrect? + adc = ads.readADC_SingleEnded(channel); + volts = ads.computeVolts(adc); } catch (NoDataAvailableException &e) { - throw NoDataAvailableException(); //propagate exception + // FIXME: This catch block will never be called since we aint throwing any exceptions + return {ERROR_VALUE, channel, NO_I2C_ADDRESS, MeasurementType::CIRCUMFERENCE_INCREMENT, + ErrorType::NO_DATA}; } - return volts; + + if (!sensorConnected) { + return {ERROR_VALUE, channel, NO_I2C_ADDRESS, MeasurementType::CIRCUMFERENCE_INCREMENT, + ErrorType::SENSOR_INIT_FAILED}; + } + + return {volts, channel, NO_I2C_ADDRESS, MeasurementType::CIRCUMFERENCE_INCREMENT, + ErrorType::DATA_OK}; } // The following functions change the ADC input range: be careful @@ -59,7 +60,7 @@ float ForteDR26::readData() { // GAIN_EIGHT // 8x gain +/- 0.512V 1 bit = 0.015625mV // GAIN_SIXTEEN // 16x gain +/- 0.256V 1 bit = 0.0078125mV void ForteDR26::changeGain(adsGain_t gain) { - ads1.setGain(gain); + ads.setGain(gain); } void ForteDR26::setChannel(int c) { @@ -68,18 +69,10 @@ void ForteDR26::setChannel(int c) { std::list<Message> ForteDR26::buildMessages() { std::list<Message> messages; - float data = readData(); - MeasurementData IncrementData{data, channel, {}, "CIRCUMFERENCE_INCREMENT"}; - messages.emplace_back(IncrementData, sensorInformation, Time::getInstance().getEpochSeconds()); - return messages; -} - -// FIXME: passing the measurementType as a string is not a good idea, we should use an enum like we do for other sensors -std::list<Message> ForteDR26::buildMessages(std::string measurementType) { - std::list<Message> messages; - float data = readData(); - MeasurementData IncrementData{data, channel, {}, measurementType}; - messages.emplace_back(IncrementData, sensorInformation, Time::getInstance().getEpochSeconds()); + auto data = readData(); + messages.emplace_back(data, + sensorInformation, + Time::getInstance().getEpochSeconds()); return messages; } @@ -88,4 +81,6 @@ SensorInformation ForteDR26::getSensorInformation() const { } -Adafruit_ADS1115 ForteDR26::ads1 = ads1; // TODO: What is this? \ No newline at end of file +// TODO: What is this? +Adafruit_ADS1115 ForteDR26::ads = ads; + diff --git a/client/libs/dr26_analogue/dr26.hpp b/client/libs/dr26_analogue/dr26.hpp index e1bc6b3e6c1857716a4e0fc6a0870ffd48774fbb..ea209f597917e3aee4a0dd0f2e89394e4833e499 100644 --- a/client/libs/dr26_analogue/dr26.hpp +++ b/client/libs/dr26_analogue/dr26.hpp @@ -9,21 +9,21 @@ #include <Wire.h> #include "NoDataAvailableException.hpp" -class ForteDR26 : public ForteSensor<float> { - public: - void setup() override; - float readData() override; - void changeGain(adsGain_t gain); - void setChannel(int channel); - std::list<Message> buildMessages() override; - std::list<Message> buildMessages(std::string mesurment_type); - [[nodiscard]] SensorInformation getSensorInformation() const override; - static Adafruit_ADS1115 ads1; +class ForteDR26: public ForteSensor<Measurement> { + public: + void setup() override; + Measurement readData() override; + void changeGain(adsGain_t gain); + void setChannel(int channel); + std::list<Message> buildMessages() override; + [[nodiscard]] SensorInformation getSensorInformation() const override; + static Adafruit_ADS1115 ads; - private: - Adafruit_ADS1115 ads; - const SensorInformation sensorInformation{"DR26", Protocol::Analog}; - int channel; + private: + const SensorInformation + sensorInformation{HardwareName::DRS26, SensorProtocol::Analog}; + int channel; + bool sensorConnected = true; }; #endif \ No newline at end of file diff --git a/client/libs/drs26_digital/drs26.cpp b/client/libs/drs26_digital/drs26.cpp index 5c21f3a98e01d665c38a879fb23f37217b39e8f4..a674964785a78476fa9783b0968bca161605e6b4 100644 --- a/client/libs/drs26_digital/drs26.cpp +++ b/client/libs/drs26_digital/drs26.cpp @@ -6,56 +6,56 @@ Because the sensor use sdi12 protocoll we have to wait aproxemettly 1 secound be It is not known how lond the response takes so we use a while loop which can be a risk wehre the programm can get stuck */ -void ForteDRS26 ::setup() -{ - drs26.begin(4); +void ForteDRS26::setup() { + drs26.begin(4); } -out_data_drs26 ForteDRS26 ::readData() -{ - String sdiResponse = ""; - String measurement_command = - "1M!"; // The drs26 sensor uses the sdi12 protocoll , in the sdi12 protocoll is the measurement command is - // specified as 1M!=Sebsir measurement request at adress 1 - String data_command = "1D0!"; // and the followed data command 1D0! = Sensor data request at adress 1 - - drs26.sendCommand(measurement_command); - delay(1000); - drs26.sendCommand(data_command); - - data = {-1, -1, -1}; - - while (drs26.available()) { - char next_character = drs26.read(); - if ((next_character != '\n') && (next_character != '\r')) { - sdiResponse += next_character; - delay(10); // 1 character ~ 7.5ms - } - } - - if (sdiResponse.length() > 1) { - data.id = sdiResponse.substring(0, 8).toInt(); - data.circumferenceIncrement = sdiResponse.substring(9, 15).toFloat(); - data.temperature = sdiResponse.substring(16, 22).toFloat(); - } - return data; +out_data_drs26 ForteDRS26::readData() { + String sdiResponse = ""; + String measurement_command = + "1M!"; // The drs26 sensor uses the sdi12 protocoll , in the sdi12 protocoll is the measurement command is + // specified as 1M!=Sebsir measurement request at adress 1 + String data_command = + "1D0!"; // and the followed data command 1D0! = Sensor data request at adress 1 + + drs26.sendCommand(measurement_command); + delay(1000); + drs26.sendCommand(data_command); + + data = {-1, -1, -1}; + + while (drs26.available()) { + char next_character = drs26.read(); + if ((next_character != '\n') && (next_character != '\r')) { + sdiResponse += next_character; + delay(10); // 1 character ~ 7.5ms + } + } + + if (sdiResponse.length() > 1) { + data.id = sdiResponse.substring(0, 8).toInt(); + data.circumferenceIncrement = sdiResponse.substring(9, 15).toFloat(); + data.temperature = sdiResponse.substring(16, 22).toFloat(); + } + return data; } -std::list<Message> ForteDRS26 ::buildMessages() -{ - std::list<Message> messages; - MeasurementData circumferenceIncrementMeasurementData{ - data.circumferenceIncrement, 0, {}, measurementTypeToString.at(MeasurementType::CIRCUMFERENCE_INCREMENT)}; - MeasurementData temperatureMeasurementData{ - data.temperature, 0, {}, measurementTypeToString.at(MeasurementType::TEMPERATURE)}; - - messages.emplace_back(Message{circumferenceIncrementMeasurementData, sensorInformation, 0}); - messages.emplace_back(Message{temperatureMeasurementData, sensorInformation, 0}); - - ESP_LOGE(sensorInformation.getSensorName().c_str(), "test"); - return messages; +std::list<Message> ForteDRS26::buildMessages() { + std::list<Message> messages; + Measurement circumferenceIncrementMeasurementData{data.circumferenceIncrement, 0, + NO_I2C_ADDRESS, + MeasurementType::CIRCUMFERENCE_INCREMENT, + ErrorType::DATA_OK}; + Measurement temperatureMeasurementData{data.temperature, 0, NO_I2C_ADDRESS, + MeasurementType::TEMPERATURE, ErrorType::DATA_OK}; + + messages.emplace_back(circumferenceIncrementMeasurementData, + sensorInformation, + 0); + messages.emplace_back(temperatureMeasurementData, sensorInformation, 0); + + return messages; } -SensorInformation ForteDRS26::getSensorInformation() const -{ - return sensorInformation; +SensorInformation ForteDRS26::getSensorInformation() const { + return sensorInformation; } diff --git a/client/libs/drs26_digital/drs26.hpp b/client/libs/drs26_digital/drs26.hpp index 0d713ca66260895f63cdc1e2137ec4027294e8bd..c02f0d23ebd4ae8e6e89dac952015f6f146c1415 100644 --- a/client/libs/drs26_digital/drs26.hpp +++ b/client/libs/drs26_digital/drs26.hpp @@ -10,28 +10,24 @@ #include <map> struct out_data_drs26 { - int id; - float circumferenceIncrement; - float temperature; + int id; + float circumferenceIncrement; + float temperature; }; -class ForteDRS26 : public ForteSensor<out_data_drs26> { - public: - void setup() override; - out_data_drs26 readData() override; - std::list<Message> buildMessages() override; - [[nodiscard]] SensorInformation getSensorInformation() const override; +class ForteDRS26: public ForteSensor<out_data_drs26> { + public: + void setup() override; + out_data_drs26 readData() override; + std::list<Message> buildMessages() override; + [[nodiscard]] SensorInformation getSensorInformation() const override; - private: - SDI12 drs26; - out_data_drs26 data; - const SensorInformation sensorInformation{"DRS26", Protocol::I2C}; - enum class MeasurementType { TEMPERATURE, CIRCUMFERENCE_INCREMENT }; + private: + SDI12 drs26; + out_data_drs26 data; + const SensorInformation + sensorInformation{HardwareName::DRS26, SensorProtocol::I2C}; - // enum to string - std::map<MeasurementType, const char *> measurementTypeToString = { - {MeasurementType::TEMPERATURE, "TEMPERATURE"}, - {MeasurementType::CIRCUMFERENCE_INCREMENT, "CIRCUMFERENCE_INCREMENT"}}; }; #endif \ No newline at end of file diff --git a/client/libs/espnow/src/ClientDataPackage.hpp b/client/libs/espnow/src/ClientDataPackage.hpp deleted file mode 100644 index f08e6bba4ea43308e606f06f7870735d7f4820bc..0000000000000000000000000000000000000000 --- a/client/libs/espnow/src/ClientDataPackage.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include <ArduinoJson.h> -#include "MeasurementData.hpp" -#include "Protocol.hpp" -#include "SensorInformation.hpp" -#include <list> -#include <optional> -#include <string> -#include <utility> - -// having the data be a struct of basic types makes sending easier, -// otherwise we would have to serialize the data before sending -class ClientDataPackage { - private: - MeasurementData measurementData; - SensorInformation sensorInformation; - unsigned long timestamp; // maybe make this array - - public: - ClientDataPackage(MeasurementData value, SensorInformation sensorInformation, unsigned long timestamp) - : measurementData(std::move(value)), sensorInformation(std::move(sensorInformation)), timestamp(timestamp) - { - } - - [[nodiscard]] const MeasurementData &getMeasurementData() const { return measurementData; } - [[nodiscard]] const SensorInformation &getSensorInformation() const { return sensorInformation; } - [[nodiscard]] unsigned long getTimestamp() const { return timestamp; } - - [[nodiscard]] std::string getDataPackageAsMinifiedJsonString() const - { - StaticJsonDocument<250> document; // 250 byte is the max send size of espnow - - document["sensorName"] = sensorInformation.getSensorName(); - document["timestamp"] = timestamp; - document["protocol"] = protocolToString.at(sensorInformation.getProtocol()); - document["value"] = measurementData.getValue(); - - if (measurementData.getChannel().has_value()) { - document["channel"] = measurementData.getChannel().value(); - } - - if (measurementData.getI2CAddress().has_value()) { - document["i2cAddress"] = measurementData.getI2CAddress().value(); - } - - document["measurementType"] = measurementData.getMeasurementType(); - - std::string jsonString; - serializeJson(document, jsonString); - return jsonString; - } - -}; diff --git a/client/libs/espnow/src/ESPNow.cpp b/client/libs/espnow/src/ESPNow.cpp index b5627716f2101f11bb91e24d65b08baedae090b2..6ef9054a64321ec0318602b66b738fd7ccc4f3f9 100644 --- a/client/libs/espnow/src/ESPNow.cpp +++ b/client/libs/espnow/src/ESPNow.cpp @@ -7,98 +7,141 @@ esp_now_peer_info_t hostInfo; Preferences preferences; bool msg_recv = false; -bool was_msg_received(){ - if(msg_recv){ - msg_recv = false; - return true; - } - return false; +bool was_msg_received() { + if (msg_recv) { + msg_recv = false; + return true; + } + return false; } -esp_err_t add_host_to_peers(config received){ - esp_now_peer_info_t host; - memset(&host, 0, sizeof(host)); - memcpy(host.peer_addr, received.host, sizeof(received.host)); - host.encrypt = false; - host.channel = 0; - return esp_now_add_peer(&host); +void get_host_mac(uint8_t *destination) { + preferences.begin("config", true); + if (preferences.isKey("host")) { + preferences.getBytes("host", destination, sizeof(uint8_t) * 6); + } else { + memcpy(destination, BROADCAST_MAC, sizeof(BROADCAST_MAC)); + ESP_LOGI(TAG, "Backup MAC address used"); + } + preferences.end(); } -void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) -{ - ESP_LOGE(TAG, "Message sent to %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], - mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); +esp_err_t add_host_to_peers(config received) { + esp_now_peer_info_t host; + memset(&host, 0, sizeof(host)); + memcpy(host.peer_addr, received.host, sizeof(received.host)); + host.encrypt = false; + host.channel = 0; + return esp_now_add_peer(&host); } -void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) -{ - // is msg host -> yes -> set bool - // assume host change not happening, rare event - // => on host change, broadcast - ESP_LOGD(TAG, "Message received"); - config received_msg; - memcpy(&received_msg, incomingData, sizeof(received_msg)); // TODO: check for valid mac - // all the esp32 macs so far use the same first 3(?) bytes so maybe use that - // First three bytes of MACs are always vendor specific. But vendors have a range of them. - // all Espressif registered MAC starting bytes: https://maclookup.app/vendors/espressif-inc - // you can also set your own MAC https://randomnerdtutorials.com/get-change-esp32-esp8266-mac-address-arduino/ - switch (received_msg.type){ - case hostChange:{ - msg_recv = true; - ESP_LOGI(TAG, "hostChange received"); - Time::getInstance().setTime(received_msg.epoch_seconds); - - // delete old host - uint8_t old[6]; - RtcMemory::getInstance().get_host_mac(old); - if(memcmp(received_msg.host, old, 6) == 0){ - ESP_LOGI(TAG, "removing old host"); - esp_now_del_peer(old); - } - // add new host - RtcMemory::getInstance().store_mac_address(received_msg.host); - add_host_to_peers(received_msg); - } - case dataAck:{ - msg_recv = true; - ESP_LOGI(TAG, "dataAck received."); - Time::getInstance().setTime( - received_msg.epoch_seconds); // see https://www.esp32.com/viewtopic.php?t=9965, maybe this needs an offset - ESP_LOGI(TAG, "Timestamp received: %ld", Time::getInstance().getEpochSeconds()); - } - // delay(50); - - default:{ - break; - } - } +void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { + ESP_LOGE(TAG, "Message sent to %02X:%02X:%02X:%02X:%02X:%02X", mac_addr[0], + mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); } +void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) { + // is msg host -> yes -> set bool + // assume host change not happening, rare event + // => on host change, broadcast +// ESP_LOGD(TAG, "Message received"); +// time this + auto start = millis(); + config received_msg; + memcpy(&received_msg, + incomingData, + sizeof(received_msg)); // TODO: check for valid mac + // all the esp32 macs so far use the same first 3(?) bytes so maybe use that + // First three bytes of MACs are always vendor specific. But vendors have a range of them. + // all Espressif registered MAC starting bytes: https://maclookup.app/vendors/espressif-inc + // you can also set your own MAC https://randomnerdtutorials.com/get-change-esp32-esp8266-mac-address-arduino/ + switch (received_msg.type) { + case hostChange: { + ESP_LOGI(TAG, "hostChange received"); + Time::getInstance().setTime(received_msg.epoch_seconds); + // delete old host + preferences.begin("config", false); + if (preferences.isKey("host")) { + ESP_LOGI(TAG, "removing old host"); + uint8_t old[6]; + preferences.end(); + get_host_mac(old); // maybe problem here, re-opening preferences + esp_now_del_peer(old); + } + // add new host + preferences.begin("config", false); + if (preferences.putBytes("host", + received_msg.host, + sizeof(received_msg.host)) > 0) { + ESP_LOGI(TAG, + "Host MAC address saved to flash %02X:%02X:%02X:%02X:%02X:%02X", + received_msg.host[0], + received_msg.host[1], + received_msg.host[2], + received_msg.host[3], + received_msg.host[4], + received_msg.host[5]); + } else { + ESP_LOGI(TAG, "Couldn't save Host Mac to flash"); + } + preferences.end(); + add_host_to_peers(received_msg); + } + case dataAck: { +// ESP_LOGI(TAG, "dataAck received."); + Time::getInstance().setTime( + received_msg.epoch_seconds); // see https://www.esp32.com/viewtopic.php?t=9965, maybe this needs an offset -esp_err_t espnow_setup() -{ - esp_err_t result; - WiFi.mode(WIFI_STA); - result = esp_now_init(); - if (result != ESP_OK) { - // initialization failed - ESP_LOGE(TAG, "ESPNow setup failed"); - return result; // not sure about this - } +// ESP_LOGI(TAG, "Timestamp received: %ld", Time::getInstance().getEpochSeconds()); + preferences.begin("config", false); + if (!preferences.isKey("host")) { + if (preferences.putBytes("host", + received_msg.host, + sizeof(received_msg.host)) > 0) { +// ESP_LOGI(TAG, "host MAC address saved to flash %02X:%02X:%02X:%02X:%02X:%02X", received_msg.host[0], +// received_msg.host[1],received_msg.host[2],received_msg.host[3],received_msg.host[4],received_msg.host[5]); +// } - RtcMemory::getInstance().get_host_mac(hostInfo.peer_addr); // check if there is a host saved in flash mem, broadcast otherwise + // add host to peers + add_host_to_peers(received_msg); + } + } + preferences.end(); + // delay(50); + msg_recv = true; + } + default: { + break; + } + } + auto end = millis(); - hostInfo.channel = 0; +} + + +esp_err_t espnow_setup() { + esp_err_t result; + WiFi.mode(WIFI_STA); + result = esp_now_init(); + if (result != ESP_OK) { + // initialization failed + ESP_LOGE(TAG, "ESPNow setup failed"); + return result; // not sure about this + } + + get_host_mac(hostInfo.peer_addr); // check if there is a host saved in flash mem, broadcast otherwise + + hostInfo.channel = 0; - // TODO: PMK is used to encrypt LMK with the AES-128 algorithm. Call esp_now_set_pmk() to set PMK. If PMK is not - // set, a default PMK will be used. - // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html - hostInfo.encrypt = false; - esp_now_add_peer(&hostInfo); + // TODO: PMK is used to encrypt LMK with the AES-128 algorithm. Call esp_now_set_pmk() to set PMK. If PMK is not + // set, a default PMK will be used. + // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html + hostInfo.encrypt = false; + esp_now_add_peer(&hostInfo); - esp_now_register_recv_cb(on_data_recv); - esp_now_register_send_cb(on_data_sent); + esp_now_register_recv_cb(on_data_recv); + esp_now_register_send_cb(on_data_sent); - ESP_LOGI(TAG, "ESPNow started. MAC: %s", WiFi.macAddress().c_str()); - return ESP_OK; + ESP_LOGI(TAG, "ESPNow started. MAC: %s", WiFi.macAddress().c_str()); + return ESP_OK; } diff --git a/client/libs/espnow/src/Message.cpp b/client/libs/espnow/src/Message.cpp index fb5960e7ce182691e0da83daeb774e65320b592f..0df90ac3056149208dd7038e08f3ed52641822d3 100644 --- a/client/libs/espnow/src/Message.cpp +++ b/client/libs/espnow/src/Message.cpp @@ -2,39 +2,88 @@ static const char *TAG = "MESSAGE"; -esp_err_t Message::send() const -{ - ESP_LOGD(TAG, "Sending message"); - esp_err_t success; - auto messageData = getMessageAsMinifiedJsonString(); - - // conversion from std::string to c_str adds null terminator, which is why we add 1 to message length - success = esp_now_send(recipient, (uint8_t *)messageData.c_str(), (messageData.length() + 1) * sizeof(char)); - if (success != ESP_OK) { - ESP_LOGE(TAG, "Error sending the data"); - // Removed caching from here, better do this in main - } - ESP_LOGD(TAG, "Sent data: %s", messageData.c_str()); - - ESP_LOGD(TAG, "Timestamp sent: %ld", clientDataPackage.getTimestamp()); - ESP_LOGD(TAG, "send status: %d", success); - - return success; +esp_err_t Message::send() const { + ESP_LOGD(TAG, "Sending message"); + esp_err_t success; + + // conversion from std::string to c_str adds null terminator, which is why we add 1 to message length + success = esp_now_send(recipient, + (uint8_t *) &clientDataPackage, + sizeof(ClientDataPackage)); + if (success != ESP_OK) { + ESP_LOGE(TAG, "Error sending the data"); + // Removed caching from here, better do this in main + } +// ESP_LOGD(TAG, "Sent data: %s", messageData.c_str()); + + ESP_LOGD(TAG, "Timestamp sent: %ld", clientDataPackage.getTimestamp()); + ESP_LOGD(TAG, "send status: %d", success); + + return success; +} + +esp_err_t Message::sendMessages(const std::array<Message, 6> &messages) { + // recipient + uint8_t rec[6]{}; + get_host_mac(rec); + ESP_LOGD(TAG, "Sending messages"); + esp_err_t success; + // list of compressed data. Sending 6 at a time + std::array<ClientDataPackage, 6> clientDataPackages{}; + // max 6 messages + int i = 0; + for (const auto &message: messages) { + if (i >= 6) { + ESP_LOGE(TAG, "Too many messages to send"); + break; + } + clientDataPackages[i] = message.getClientDataPackage(); + + i++; + } + + // conversion from std::string to c_str adds null terminator, which is why we add 1 to message length + success = esp_now_send(rec, (uint8_t *) &clientDataPackages, + sizeof(std::array<ClientDataPackage, 6>)); + if (success != ESP_OK) { + ESP_LOGE(TAG, "Error sending the data"); + // Removed caching from here, better do this in main + } + unsigned long ts = millis(); + // it takes ~110ms for receiving an acknowledgement by the host in perfect conditions + uint16_t message_timeout = 2000; + while (!was_msg_received()) { + if ((millis() - ts) > message_timeout) { + ESP_LOGE(TAG, "Timeout: Host not available\n"); + break; + } + } + ESP_LOGD(TAG, "Time until acknowledgement: %ld", millis() - ts); +// ESP_LOGD(TAG, "Sent data: %s", messageData.c_str()); + +// ESP_LOGD(TAG, "Timestamp sent: %ld", clientDataPackage.getTimestamp()); + ESP_LOGD(TAG, "send status: %d", success); + + return success; } -std::string Message::getMessageAsMinifiedJsonString() const -{ - return clientDataPackage.getDataPackageAsMinifiedJsonString(); +std::string Message::getMessageAsMinifiedJsonString() const { + return clientDataPackage.getDataPackageAsMinifiedJsonString(); } -Message::Message(ClientDataPackage data) : clientDataPackage(std::move(data)) -{ - // check for existing host mac address, use broadcast otherwise - RtcMemory::getInstance().get_host_mac(recipient); +Message::Message(ClientDataPackage data) : clientDataPackage(data) { + // check for existing host mac address, use broadcast otherwise + get_host_mac(recipient); } -Message::Message(MeasurementData const &data, const SensorInformation &information, unsigned long timestamp) - : clientDataPackage(data, information, timestamp) -{ - // check for existing host mac address, use broadcast otherwise - RtcMemory::getInstance().get_host_mac(recipient); + +Message::Message(Measurement const &data, + const SensorInformation &information, + unsigned long timestamp) + : clientDataPackage(data, information, timestamp) { + // check for existing host mac address, use broadcast otherwise + get_host_mac(recipient); +} +const ClientDataPackage &Message::getClientDataPackage() const { + return clientDataPackage; } + diff --git a/client/libs/espnow/src/Message.hpp b/client/libs/espnow/src/Message.hpp index ba2724deaf3e01ffcc56d146ca75909d20c69446..3dc83bf24274e19424d43d22187287651e927253 100644 --- a/client/libs/espnow/src/Message.hpp +++ b/client/libs/espnow/src/Message.hpp @@ -4,6 +4,7 @@ #include "ESPNow.hpp" #include "Time.hpp" #include "esp_log.h" +#include "GlobalDefinitions.hpp" #include <Arduino.h> #include <ArduinoJson.h> #include <ESP32Time.h> @@ -13,14 +14,34 @@ // Format of the message sent from host to client_satellite // if more things are sent from the host the name might not be accurate anymore class Message { - public: - explicit Message(ClientDataPackage data); +public: + explicit Message(ClientDataPackage data); - Message(MeasurementData const &data, const SensorInformation &information, unsigned long timestamp); - esp_err_t send() const; - [[nodiscard]] std::string getMessageAsMinifiedJsonString() const; + Message(Measurement const &data, + const SensorInformation &information, + unsigned long timestamp); + + esp_err_t send() const; + + [[nodiscard]] std::string getMessageAsMinifiedJsonString() const; + + static Message nullMessage() { + return Message{Measurement(ERROR_VALUE, + NO_CHANNEL, + NO_I2C_ADDRESS, + MeasurementType::NULL_MEASUREMENT, ErrorType::NULL_MESSAGE), + SensorInformation(HardwareName::NONE, + SensorProtocol::NULL_PROTOCOL), + NULL_TIMESTAMP}; + } + + static esp_err_t sendMessages(const std::array<Message, 6> &messages); + + [[nodiscard]] const ClientDataPackage &getClientDataPackage() const; + +private: + ClientDataPackage clientDataPackage; + + uint8_t recipient[6]{}; - private: - ClientDataPackage clientDataPackage; - uint8_t recipient[6]{}; }; diff --git a/client/libs/ina219/ina219.cpp b/client/libs/ina219/ina219.cpp index 930885f2d99e6ccd1571b778441dfd39668c2108..6253c034074687a7919f0b7f66afde1415bf37b5 100644 --- a/client/libs/ina219/ina219.cpp +++ b/client/libs/ina219/ina219.cpp @@ -1,63 +1,61 @@ #include "ina219.hpp" -void ForteINA219 ::setup() -{ - Wire.begin(I2C_SDA, I2C_SCL); - if (!ina219.init()) { - // Sensor init went wrong - ESP_LOGW(sensorInformation.getSensorName().c_str(), "Initialization failed"); - return; - } +void ForteINA219::setup() { + Wire.begin(I2C_SDA, I2C_SCL); + if (!ina219.init()) { + // Sensor init went wrong + ESP_LOGW(sensorInformation.getHardwareNameString().c_str(), + "Initialization failed"); + return; + } } -out_data_ina219 ForteINA219 ::readData() -{ - if (!ina219.getOverflow()) { - data.shuntVoltage_mV = ina219.getShuntVoltage_mV(); - data.busVoltage_V = ina219.getBusVoltage_V(); - data.current_mA = ina219.getCurrent_mA(); - data.power_mW = ina219.getBusPower(); - data.loadVoltage_V = data.busVoltage_V + (data.shuntVoltage_mV / 1000); - data.ina219_overflow = ina219.getOverflow(); - - return data; - } else - return data; +out_data_ina219 ForteINA219::readData() { + data.shuntVoltage_mV = Measurement{ina219.getShuntVoltage_mV(), NO_CHANNEL, + INA219_BASE_ADDRESS, + MeasurementType::SHUNT_VOLTAGE, ErrorType::DATA_OK}; + data.busVoltage_V = Measurement{ina219.getBusVoltage_V(), NO_CHANNEL, INA219_BASE_ADDRESS, + MeasurementType::BUS_VOLTAGE, ErrorType::DATA_OK}; + data.current_mA = Measurement{ina219.getCurrent_mA(), NO_CHANNEL, INA219_BASE_ADDRESS, + MeasurementType::CURRENT_mA, ErrorType::DATA_OK}; + data.power_mW = Measurement{ina219.getBusPower(), NO_CHANNEL, INA219_BASE_ADDRESS, + MeasurementType::POWER_mW, ErrorType::DATA_OK}; + auto loadVoltage_V_value = + data.busVoltage_V.getValue() + (data.shuntVoltage_mV.getValue() / 1000); + data.loadVoltage_V = Measurement{ + loadVoltage_V_value, NO_CHANNEL, + INA219_BASE_ADDRESS, MeasurementType::LOAD_VOLTAGE_V, + ErrorType::DATA_OK}; + + data.ina219_overflow = ina219.getOverflow(); + if (data.ina219_overflow) { + // set all measurements to overflow + data.shuntVoltage_mV.setErrorType(ErrorType::INA219_OVERFLOW); + data.busVoltage_V.setErrorType(ErrorType::INA219_OVERFLOW); + data.current_mA.setErrorType(ErrorType::INA219_OVERFLOW); + data.power_mW.setErrorType(ErrorType::INA219_OVERFLOW); + data.loadVoltage_V.setErrorType(ErrorType::INA219_OVERFLOW); + return data; + } else { + return data; + } } -std::list<Message> ForteINA219::buildMessages() -{ - std::list<Message> messages; - out_data_ina219 data = readData(); - MeasurementData shuntVoltageData{ - data.shuntVoltage_mV, 0, {}, measurementTypeToString.at(MeasurementType::SHUNT_VOLTAGE) - }; - MeasurementData busVoltageData{ - data.busVoltage_V, 0, {}, measurementTypeToString.at(MeasurementType::BUS_VOLTAGE) - }; - MeasurementData currentMilliAmpData{ - data.current_mA, 0, {}, measurementTypeToString.at(MeasurementType::CURRENT_mA) - }; - MeasurementData powerMilliWattData{ - data.power_mW, 0, {}, measurementTypeToString.at(MeasurementType::POWER_mA) - }; - MeasurementData loadVoltageData{ - data.loadVoltage_V, 0, {}, measurementTypeToString.at(MeasurementType::LOAD_VOLTAGE_V) - }; - MeasurementData ina219OverflowData{ - data.ina219_overflow, 0, {}, measurementTypeToString.at(MeasurementType::INA219_OVERFLOW) - }; - - messages.emplace_back(Message(shuntVoltageData, sensorInformation, 0)); - messages.emplace_back(Message(busVoltageData, sensorInformation,0)); - messages.emplace_back(Message(currentMilliAmpData, sensorInformation, 0)); - messages.emplace_back(Message(powerMilliWattData, sensorInformation, 0)); - messages.emplace_back(Message(loadVoltageData, sensorInformation, 0)); - messages.emplace_back(Message(ina219OverflowData, sensorInformation, 0)); - - return messages; +std::list<Message> ForteINA219::buildMessages() { + std::list<Message> messages; + out_data_ina219 measurements = readData(); + + + messages.emplace_back(measurements.shuntVoltage_mV, sensorInformation, 0); + messages.emplace_back(measurements.busVoltage_V, sensorInformation, 0); + messages.emplace_back(measurements.current_mA, sensorInformation, 0); + messages.emplace_back(measurements.power_mW, sensorInformation, 0); + messages.emplace_back(measurements.loadVoltage_V, sensorInformation, 0); +// messages.emplace_back(ina219OverflowData, sensorInformation, 0); TODO: Do we need this as an extra message? + + return messages; } -SensorInformation ForteINA219::getSensorInformation() const -{ - return sensorInformation; + +SensorInformation ForteINA219::getSensorInformation() const { + return sensorInformation; } diff --git a/client/libs/ina219/ina219.hpp b/client/libs/ina219/ina219.hpp index 1985b996820f696290be13bc776a5f238fcd466a..a360948d0d98ab0b5030bb5aa8b8c4c38419cec8 100644 --- a/client/libs/ina219/ina219.hpp +++ b/client/libs/ina219/ina219.hpp @@ -7,36 +7,32 @@ #include "Wire.h" #include "esp_log.h" #include <INA219_WE.h> +#include <MeasurementTypes.h> + struct out_data_ina219 { - float shuntVoltage_mV = 0.0; - float loadVoltage_V = 0.0; - float busVoltage_V = 0.0; - float current_mA = 0.0; - float power_mW = 0.0; - bool ina219_overflow = false; + Measurement shuntVoltage_mV; + Measurement loadVoltage_V; + Measurement busVoltage_V; + Measurement current_mA; + Measurement power_mW; + bool ina219_overflow = false; }; -class ForteINA219 : public ForteSensor<out_data_ina219> { - public: - void setup() override; - out_data_ina219 readData() override; - std::list<Message> buildMessages() override; - [[nodiscard]] SensorInformation getSensorInformation() const override; +class ForteINA219: public ForteSensor<out_data_ina219> { + public: + void setup() override; + out_data_ina219 readData() override; + std::list<Message> buildMessages() override; + [[nodiscard]] SensorInformation getSensorInformation() const override; - private: - INA219_WE ina219; - out_data_ina219 data; - const SensorInformation sensorInformation{"INA219", Protocol::I2C}; - enum class MeasurementType {SHUNT_VOLTAGE, BUS_VOLTAGE, CURRENT_mA, POWER_mA, LOAD_VOLTAGE_V, INA219_OVERFLOW}; + private: + INA219_WE ina219; + out_data_ina219 data; + const SensorInformation + sensorInformation{HardwareName::INA219, SensorProtocol::I2C}; + const uint8_t INA219_BASE_ADDRESS = 0x40; - std::map<MeasurementType, const char*> measurementTypeToString = { - {MeasurementType::SHUNT_VOLTAGE, "SHUNT_VOLTAGE"}, - {MeasurementType::BUS_VOLTAGE, "BUS_VOLTAGE"}, - {MeasurementType::CURRENT_mA, "CURRENT_mA"}, - {MeasurementType::POWER_mA, "POWER_mA"}, - {MeasurementType::LOAD_VOLTAGE_V, "LOAD_VOLTAGE_V"}, - {MeasurementType::INA219_OVERFLOW, "INA219_OVERFLOW"}}; }; #endif \ No newline at end of file diff --git a/client/libs/includes/ForteSensor.hpp b/client/libs/includes/ForteSensor.hpp index add5a01d566ef5977eef3e0286d908c4b49fcb0b..1da7c49716cdf7c0bfc133babfc83cd834fc1216 100644 --- a/client/libs/includes/ForteSensor.hpp +++ b/client/libs/includes/ForteSensor.hpp @@ -2,7 +2,7 @@ #define _FORTE_SENSOR #include "Message.hpp" -#include "Protocol.hpp" +#include "SensorProtocol.hpp" #include "SensorInformation.hpp" template <class T> class ForteSensor { diff --git a/client/libs/includes/MeasurementData.hpp b/client/libs/includes/MeasurementData.hpp deleted file mode 100644 index 36bd5db75fbdb714dad723d67b92affd4d4fd189..0000000000000000000000000000000000000000 --- a/client/libs/includes/MeasurementData.hpp +++ /dev/null @@ -1,39 +0,0 @@ -// -// Created by cynthya on 10/6/22. -// - -#ifndef CLIENT_MEASUREMENTDATA_HPP -#define CLIENT_MEASUREMENTDATA_HPP - -#include "ArduinoJson.h" -#include "Protocol.hpp" -#include "SensorInformation.hpp" -#include <list> -#include <optional> -#include <string> -#include <utility> -class MeasurementData { - public: - MeasurementData(double value, std::optional<int> channel, std::optional<int> i2cAddress, - std::string measurementType) - : value(value), measurementType(std::move(measurementType)), channel(channel), i2cAddress(i2cAddress) - { - } - - MeasurementData(double value, std::string measurementType) - : value(value), measurementType(std::move(measurementType)) - { - } - - [[nodiscard]] double getValue() const { return value; } - [[nodiscard]] const std::string &getMeasurementType() const { return measurementType; } - [[nodiscard]] const std::optional<int> &getChannel() const { return channel; } - [[nodiscard]] const std::optional<int> &getI2CAddress() const { return i2cAddress; } - - private: - double value; - std::string measurementType; // TODO: consider using an enum - std::optional<int> channel; - std::optional<int> i2cAddress; -}; -#endif // CLIENT_MEASUREMENTDATA_HPP diff --git a/client/libs/includes/Protocol.hpp b/client/libs/includes/Protocol.hpp deleted file mode 100644 index f9a124978a42626fd128e579adc497bd23257678..0000000000000000000000000000000000000000 --- a/client/libs/includes/Protocol.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by zoe on 10/5/22. -// - -#ifndef CLIENT_PROTOCOL_HPP -#define CLIENT_PROTOCOL_HPP - -#include <map> -enum class Protocol { I2C, RS485, Analog, Mock }; - -// protocol to string -const static std::map<Protocol, const char *> protocolToString = { - {Protocol::I2C, "I2C"}, {Protocol::RS485, "RS485"}, {Protocol::Analog, "ANALOG"}, {Protocol::Mock, "MOCK"}}; - -#endif // CLIENT_PROTOCOL_HPP diff --git a/client/libs/includes/SensorInformation.hpp b/client/libs/includes/SensorInformation.hpp deleted file mode 100644 index 4b8af5d357534be81670b503790960a2781bc282..0000000000000000000000000000000000000000 --- a/client/libs/includes/SensorInformation.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by cynthya on 10/6/22. -// - -#ifndef CLIENT_SENSORINFORMATION_HPP -#define CLIENT_SENSORINFORMATION_HPP - -#include "Protocol.hpp" -#include <string> - -class SensorInformation { - public: - SensorInformation(std::string sensorName, Protocol protocol) : sensorName(std::move(sensorName)), protocol(protocol) - { - } - - [[nodiscard]] const std::string &getSensorName() const { return sensorName; } - [[nodiscard]] Protocol getProtocol() const { return protocol; } - - private: - std::string sensorName; - Protocol protocol; -}; - -#endif // CLIENT_SENSORINFORMATION_HPP diff --git a/client/libs/mock_sensor/MockSensor.cpp b/client/libs/mock_sensor/MockSensor.cpp index 4833773e9b7e26da8ff00842e1fcee47fb613635..bc1deaf1a62b868fd91ec3755732b1eb3f115068 100644 --- a/client/libs/mock_sensor/MockSensor.cpp +++ b/client/libs/mock_sensor/MockSensor.cpp @@ -1,34 +1,40 @@ #include "MockSensor.hpp" +#include "MeasurementTypes.h" static const char *TAG = "MOCK"; void MockSensor::setup() { - ESP_LOGD(TAG, "MOCK Sensor initialized"); - delay(100); - channel = 0; + ESP_LOGD(TAG, "MOCK Sensor initialized"); + delay(100); + channel = 0; } -float MockSensor::readData() { - // generate a random float value between 0 and 100 - float randomValue = (float)rand() / (float)RAND_MAX * 100.0; - ESP_LOGD(TAG, "MOCK Sensor read value: %f", randomValue); - return randomValue; +Measurement MockSensor::readData() { + // generate a random float value between 0 and 100 + float randomValue = DeepSleep::bootCount * 10 + channel; + ESP_LOGD(TAG, "MOCK Sensor read value: %f", randomValue); + return {randomValue, channel, NO_I2C_ADDRESS, MeasurementType::MOCK, + ErrorType::DATA_OK}; + } void MockSensor::setChannel(int c) { - channel = c; + channel = c; } std::list<Message> MockSensor::buildMessages() { - std::list<Message> messages; - float data = readData(); - MeasurementData MockData{data, channel, {}, "MOCK"}; - messages.emplace_back(MockData, sensorInformation, Time::getInstance().getEpochSeconds()); - return messages; + std::list<Message> messages; + auto measurement = readData(); +// Measurement +// MockData{data, channel, NO_I2C_ADDRESS, MeasurementType::MOCK, ErrorType::DATA_OK}; + messages.emplace_back(measurement, + sensorInformation, + Time::getInstance().getEpochSeconds()); + return messages; } SensorInformation MockSensor::getSensorInformation() const { - return sensorInformation; + return sensorInformation; } diff --git a/client/libs/mock_sensor/MockSensor.hpp b/client/libs/mock_sensor/MockSensor.hpp index 20df4d1d2c66b954949a5fbce9a7e074293d28b2..aa39578f58b2ae0bb8bb651340bf02fa82c01426 100644 --- a/client/libs/mock_sensor/MockSensor.hpp +++ b/client/libs/mock_sensor/MockSensor.hpp @@ -7,18 +7,20 @@ #include "Pinout.hpp" #include "esp_log.h" #include <Wire.h> +#include <f_deep_sleep.hpp> -class MockSensor : public ForteSensor<float> { - public: - void setup() override; - float readData() override; - void setChannel(int channel); - std::list<Message> buildMessages() override; - [[nodiscard]] SensorInformation getSensorInformation() const override; - private: - const SensorInformation sensorInformation{"MOCK", Protocol::Mock}; - int channel; +class MockSensor: public ForteSensor<Measurement> { + public: + void setup() override; + Measurement readData() override; + void setChannel(int channel); + std::list<Message> buildMessages() override; + [[nodiscard]] SensorInformation getSensorInformation() const override; + + private: + const SensorInformation sensorInformation{HardwareName::MOCK, SensorProtocol::Mock}; + int channel; }; #endif \ No newline at end of file diff --git a/client/libs/rs485/rs485.cpp b/client/libs/rs485/rs485.cpp index 269c7f37a0c6cb685adda7dc599edbea185d5f8e..55f18b6ba21b55729ba6e36f677770f7298135ce 100644 --- a/client/libs/rs485/rs485.cpp +++ b/client/libs/rs485/rs485.cpp @@ -13,72 +13,88 @@ static const char *TAG = "RS485"; // Configure sensors SolarRadiationSensor solarSensor(&RS485Serial, 1, RE_DE_PIN); -RainGaugeSensor rainGauge = RainGaugeSensor(&RS485Serial, 2, RE_DE_PIN); // Give 2 Sensor Adress 2 -SoilMoistureSensor soilSensor3 = SoilMoistureSensor(&RS485Serial, 3, RE_DE_PIN); //..... +RainGaugeSensor rainGauge = RainGaugeSensor(&RS485Serial, + 2, + RE_DE_PIN); // Give 2 Sensor Adress 2 +SoilMoistureSensor + soilSensor3 = SoilMoistureSensor(&RS485Serial, 3, RE_DE_PIN); //..... SoilMoistureSensor soilSensor4 = SoilMoistureSensor(&RS485Serial, 4, RE_DE_PIN); SoilMoistureSensor soilSensor5 = SoilMoistureSensor(&RS485Serial, 5, RE_DE_PIN); -void Forte_RS485::setup() -{ - // configure the pins to be output only - pinMode(RE_DE_PIN, OUTPUT); - pinMode(POWER_SWITCH_PIN_12V, OUTPUT); - pinMode(POWER_SWITCH_PIN_5V, OUTPUT); - RS485Serial.begin(4800, SERIAL_8N1, TXPin, RXPin); +void Forte_RS485::setup() { + // configure the pins to be output only + pinMode(RE_DE_PIN, OUTPUT); + pinMode(POWER_SWITCH_PIN_12V, OUTPUT); + pinMode(POWER_SWITCH_PIN_5V, OUTPUT); + RS485Serial.begin(4800, SERIAL_8N1, TXPin, RXPin); } -out_data_rs485 Forte_RS485::readData() -{ - // Power on sensor - digitalWrite(POWER_SWITCH_PIN_12V, HIGH); - digitalWrite(POWER_SWITCH_PIN_5V, HIGH); - // Wait for sensors to power up - // TODO minimize delay - delay(300); - out_data_rs485 output; - unsigned long ts = millis(); - output.solarRadiation = solarSensor.getSolarRadiation(); - output.soilTemperature3 = soilSensor3.getMoistureTemp(); - output.soilTemperature4 = soilSensor4.getMoistureTemp(); - output.soilTemperature5 = soilSensor5.getMoistureTemp(); - output.soilMoisture3 = soilSensor3.getMoisture(); - output.soilMoisture4 = soilSensor4.getMoisture(); - output.soilMoisture5 = soilSensor5.getMoisture(); - output.precipitation = rainGauge.getInstantaneousPrecipitation(); - digitalWrite(POWER_SWITCH_PIN_12V, LOW); - digitalWrite(POWER_SWITCH_PIN_5V, LOW); +out_data_rs485 Forte_RS485::readData() { + // Power on sensor + digitalWrite(POWER_SWITCH_PIN_12V, HIGH); + digitalWrite(POWER_SWITCH_PIN_5V, HIGH); + // Wait for sensors to power up + // TODO minimize delay + delay(300); + out_data_rs485 output; + unsigned long ts = millis(); + output.solarRadiation = solarSensor.getSolarRadiation(); + output.soilTemperature3 = soilSensor3.getMoistureTemp(); + output.soilTemperature4 = soilSensor4.getMoistureTemp(); + output.soilTemperature5 = soilSensor5.getMoistureTemp(); + output.soilMoisture3 = soilSensor3.getMoisture(); + output.soilMoisture4 = soilSensor4.getMoisture(); + output.soilMoisture5 = soilSensor5.getMoisture(); + output.precipitation = rainGauge.getInstantaneousPrecipitation(); + digitalWrite(POWER_SWITCH_PIN_12V, LOW); + digitalWrite(POWER_SWITCH_PIN_5V, LOW); - gpio_hold_en((gpio_num_t)POWER_SWITCH_PIN_12V); - gpio_hold_en((gpio_num_t)POWER_SWITCH_PIN_5V); - return output; + gpio_hold_en((gpio_num_t) POWER_SWITCH_PIN_12V); + gpio_hold_en((gpio_num_t) POWER_SWITCH_PIN_5V); + return output; } -std::list<Message> Forte_RS485::buildMessages() -{ - std::list<Message> messages; - out_data_rs485 output = readData(); - MeasurementData solarRadiation {output.solarRadiation, measurementTypeToString.at(MeasurementType::SOLAR_RADIATION)}; - MeasurementData soilTemp3{output.soilTemperature3, measurementTypeToString.at(MeasurementType::SOIL_TEMPERATURE_3)}; - MeasurementData soilTemp4 {output.soilTemperature4, measurementTypeToString.at(MeasurementType::SOIL_TEMPERATURE_4)}; - MeasurementData soilTemp5 {output.soilTemperature5, measurementTypeToString.at(MeasurementType::SOIL_TEMPERATURE_5)}; - MeasurementData soilMoisture3 {output.soilMoisture3, measurementTypeToString.at(MeasurementType::SOIL_MOISTURE_3)}; - MeasurementData soilMoisture4 {output.soilMoisture4, measurementTypeToString.at(MeasurementType::SOIL_MOISTURE_4)}; - MeasurementData soilMoisture5 {output.soilMoisture5, measurementTypeToString.at(MeasurementType::SOIL_MOISTURE_5)}; - MeasurementData precipitation {output.precipitation, measurementTypeToString.at(MeasurementType::PRECIPITATION)}; +std::list<Message> Forte_RS485::buildMessages() { + std::list<Message> messages; + out_data_rs485 output = readData(); + Measurement + solarRadiation{output.solarRadiation, MeasurementType::SOLAR_RADIATION}; + Measurement + soilTemp3{output.soilTemperature3, MeasurementType::SOIL_TEMPERATURE_3}; + Measurement + soilTemp4{output.soilTemperature4, MeasurementType::SOIL_TEMPERATURE_4}; + Measurement + soilTemp5{output.soilTemperature5, MeasurementType::SOIL_TEMPERATURE_5}; + Measurement + soilMoisture3{output.soilMoisture3, MeasurementType::SOIL_MOISTURE_3}; + Measurement + soilMoisture4{output.soilMoisture4, MeasurementType::SOIL_MOISTURE_4}; + Measurement + soilMoisture5{output.soilMoisture5, MeasurementType::SOIL_MOISTURE_5}; + Measurement + precipitation{output.precipitation, MeasurementType::PRECIPITATION}; - messages.emplace_back(Message{solarRadiation,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{soilTemp3,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{soilTemp4,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{soilTemp5,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{soilMoisture3,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{soilMoisture4,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{soilMoisture5,sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message(precipitation, sensorInformation, Time::getInstance().getEpochSeconds())); + messages.emplace_back(solarRadiation, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(soilTemp3, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(soilTemp4, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(soilTemp5, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(soilMoisture3, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(soilMoisture4, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(soilMoisture5, sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(precipitation, + sensorInformation, + Time::getInstance().getEpochSeconds()); - return messages; + return messages; } -SensorInformation Forte_RS485::getSensorInformation() const -{ - return sensorInformation; +SensorInformation Forte_RS485::getSensorInformation() const { + return sensorInformation; } \ No newline at end of file diff --git a/client/libs/rs485/rs485.hpp b/client/libs/rs485/rs485.hpp index 4fc4f5e3d5b9e49f2748bc72406f1091625b7aa4..7ae7c9287f12f4af6370398495bfa083e9a88a5a 100644 --- a/client/libs/rs485/rs485.hpp +++ b/client/libs/rs485/rs485.hpp @@ -1,51 +1,32 @@ #ifndef _RS485 #define _RS485 +#include <MeasurementTypes.h> #include "Message.hpp" #include "ForteSensor.hpp" #include "SentecSensors.h" -struct out_data_rs485{ - float solarRadiation; - float soilMoisture3; - float soilTemperature3; - float soilMoisture4; - float soilTemperature4; - float soilMoisture5; - float soilTemperature5; - float precipitation; +struct out_data_rs485 { + float solarRadiation; + float soilMoisture3; + float soilTemperature3; + float soilMoisture4; + float soilTemperature4; + float soilMoisture5; + float soilTemperature5; + float precipitation; }; -class Forte_RS485 : public ForteSensor <out_data_rs485> { - public: - void setup() override; - out_data_rs485 readData() override; - std::list<Message> buildMessages() override; - [[nodiscard]] SensorInformation getSensorInformation() const override; +class Forte_RS485: public ForteSensor<out_data_rs485> { + public: + void setup() override; + out_data_rs485 readData() override; + std::list<Message> buildMessages() override; + [[nodiscard]] SensorInformation getSensorInformation() const override; - private: - const SensorInformation sensorInformation{"RS485", Protocol::RS485}; + private: + const SensorInformation + sensorInformation{HardwareName::RS485, SensorProtocol::RS485}; - enum class MeasurementType { - SOLAR_RADIATION, - SOIL_MOISTURE_3, - SOIL_TEMPERATURE_3, - SOIL_MOISTURE_4, - SOIL_TEMPERATURE_4, - SOIL_MOISTURE_5, - SOIL_TEMPERATURE_5, - PRECIPITATION - }; - - std::map<MeasurementType, const char *> measurementTypeToString = { - {MeasurementType::SOLAR_RADIATION, "SOLAR_RADIATION"}, - {MeasurementType::SOIL_MOISTURE_3, "SOIL_MOISTURE_3"}, - {MeasurementType::SOIL_TEMPERATURE_3, "SOIL_TEMPERATURE_3"}, - {MeasurementType::SOIL_MOISTURE_4, "SOIL_MOISTURE_4"}, - {MeasurementType::SOIL_TEMPERATURE_4, "SOIL_TEMPERATURE_4"}, - {MeasurementType::SOIL_MOISTURE_5, "SOIL_MOISTURE_5"}, - {MeasurementType::SOIL_TEMPERATURE_5, "SOIL_TEMPERATURE_5"}, - {MeasurementType::PRECIPITATION, "PRECIPATION"} - }; }; #endif \ No newline at end of file diff --git a/client/libs/scd30/scd30.cpp b/client/libs/scd30/scd30.cpp index 7cff381af4ac4034d955366bf571a89b7e551ddf..6d830203bf58d3c14f03e5cc4a5c5d31c725b5a4 100644 --- a/client/libs/scd30/scd30.cpp +++ b/client/libs/scd30/scd30.cpp @@ -1,44 +1,63 @@ #include "scd30.hpp" -void ForteSCD30 ::setup() -{ - Wire.begin(I2C_SDA, I2C_SCL); - if (!airSensor.begin()) { - // Sensor init went wrong - ESP_LOGW(sensorInformation.getSensorName().c_str(), "Initialization failed."); - return; - } +void ForteSCD30::setup() { + Wire.begin(I2C_SDA, I2C_SCL); + if (!airSensor.begin()) { + // Sensor init went wrong + ESP_LOGW(sensorInformation.getHardwareNameString().c_str(), + "Initialization failed."); + sensorInit = false; + } else + sensorInit = true; } -out_data_scd30 ForteSCD30 ::readData() -{ - if (airSensor.dataAvailable()) { - data.C02 = airSensor.getCO2(); - data.Temperature = airSensor.getTemperature(); - data.Humidity = airSensor.getHumidity(); +out_data_scd30 ForteSCD30::readData() { + if (airSensor.dataAvailable()) { + data.C02 = Measurement{static_cast<double>(airSensor.getCO2()), NO_CHANNEL, + SCD30_BASE_ADDRESS, + MeasurementType::CO2, ErrorType::DATA_OK}; - return data; - } - throw NoDataAvailableException(); + data.Temperature = Measurement{airSensor.getTemperature(), NO_CHANNEL, SCD30_BASE_ADDRESS, + MeasurementType::TEMPERATURE, ErrorType::DATA_OK}; + data.Humidity = Measurement{airSensor.getHumidity(), NO_CHANNEL, SCD30_BASE_ADDRESS, + MeasurementType::HUMIDITY, ErrorType::DATA_OK}; + } else { + data.C02 = Measurement{ERROR_VALUE, NO_CHANNEL, SCD30_BASE_ADDRESS, + MeasurementType::CO2, ErrorType::NO_DATA}; + data.Temperature = Measurement{ERROR_VALUE, NO_CHANNEL, SCD30_BASE_ADDRESS, + MeasurementType::TEMPERATURE, ErrorType::NO_DATA}; + data.Humidity = Measurement{ERROR_VALUE, NO_CHANNEL, SCD30_BASE_ADDRESS, + MeasurementType::HUMIDITY, ErrorType::NO_DATA}; + } + + if (!sensorInit) { + data.C02.setErrorType(ErrorType::SENSOR_INIT_FAILED); + data.Temperature.setErrorType(ErrorType::SENSOR_INIT_FAILED); + data.Humidity.setErrorType(ErrorType::SENSOR_INIT_FAILED); + } + + return data; } -std::list<Message> ForteSCD30::buildMessages() -{ - std::list<Message> messages; +std::list<Message> ForteSCD30::buildMessages() { + std::list<Message> messages; + + auto measurements = readData(); + - out_data_scd30 data = readData(); - MeasurementData CO2Data{data.C02, 0, {}, "CO2"}; - MeasurementData TempData{data.Temperature, 0, {}, "Temperature"}; - MeasurementData HumidData{data.Humidity, 0, {}, "Humidity"}; + messages.emplace_back(measurements.C02, + sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(measurements.Temperature, + sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(measurements.Humidity, + sensorInformation, + Time::getInstance().getEpochSeconds()); - messages.emplace_back(Message(CO2Data, sensorInformation, Time::getInstance().getEpochSeconds())); - messages.emplace_back(Message(TempData, sensorInformation, Time::getInstance().getEpochSeconds())); - messages.emplace_back(Message(HumidData, sensorInformation, Time::getInstance().getEpochSeconds())); - - return messages; + return messages; } -SensorInformation ForteSCD30::getSensorInformation() const -{ - return sensorInformation; +SensorInformation ForteSCD30::getSensorInformation() const { + return sensorInformation; } diff --git a/client/libs/scd30/scd30.hpp b/client/libs/scd30/scd30.hpp index db3e85500924db80ac3c0b5cd3698314564f6fd6..00ea13198e336b3afadde75e3ed265afbcec6fdf 100644 --- a/client/libs/scd30/scd30.hpp +++ b/client/libs/scd30/scd30.hpp @@ -10,22 +10,28 @@ #include <Wire.h> struct out_data_scd30 { - float C02; - float Temperature; - float Humidity; + Measurement C02; + Measurement Temperature; + Measurement Humidity; }; class ForteSCD30 : public ForteSensor<out_data_scd30> { - public: - void setup() override; - out_data_scd30 readData() override; - std::list<Message> buildMessages() override; - [[nodiscard]] SensorInformation getSensorInformation() const override; - - private: - SCD30 airSensor; - out_data_scd30 data; - const SensorInformation sensorInformation{"SCD30", Protocol::I2C}; +public: + void setup() override; + + out_data_scd30 readData() override; + + std::list<Message> buildMessages() override; + + [[nodiscard]] SensorInformation getSensorInformation() const override; + +private: + SCD30 airSensor; + out_data_scd30 data; + const SensorInformation + sensorInformation{HardwareName::SCD30, SensorProtocol::I2C}; + const uint8_t SCD30_BASE_ADDRESS = 0x61; + bool sensorInit = true; }; #endif \ No newline at end of file diff --git a/client/libs/sht85/Sht85.cpp b/client/libs/sht85/Sht85.cpp index f9c1ee0e1089b6c2cc63abe9430cc0ec7ca002d9..f645944fdd4d1247bdfbdf7c587e2a623d6ec4fa 100644 --- a/client/libs/sht85/Sht85.cpp +++ b/client/libs/sht85/Sht85.cpp @@ -3,70 +3,68 @@ // #include "Sht85.hpp" -void Sht85::setup() -{ - - this->sht = SHTSensor(SHTSensor::SHT85); - readData(); +void Sht85::setup() { + this->sht = SHTSensor(SHTSensor::SHT85); + readData(); } -void Sht85::readSHT() -{ - // Read data from the initialized SHT85 - // TODO send via esp now instead of printing - if (sht.readSample()) { - Serial.print("T: "); - Serial.println(sht.getTemperature(), 2); - Serial.print("RH: "); - Serial.println(sht.getHumidity(), 2); - } else { - Serial.print("Error in readSample()\n"); - } +void Sht85::readSHT() { + // Read data from the initialized SHT85 + // TODO send via esp now instead of printing + if (sht.readSample()) { + Serial.print("T: "); + Serial.println(sht.getTemperature(), 2); + Serial.print("RH: "); + Serial.println(sht.getHumidity(), 2); + } else { + Serial.print("Error in readSample()\n"); + } } -out_data_sht85 Sht85::readData() -{ - // Start the I2C bus - Wire.begin(SDA, SCL); - delay(100); // let serial console settle +out_data_sht85 Sht85::readData() { + // Start the I2C bus + Wire.begin(SDA, SCL); + delay(100); // let serial console settle - // initiate the SHT85 sensor - Serial.println(""); - if (sht.init(Wire)) { - Serial.println("SHT initialization successful"); - } else { - Serial.println("SHT initialization failed"); - } + // initiate the SHT85 sensor + Serial.println(""); + if (sht.init(Wire)) { + Serial.println("SHT initialization successful"); + } else { + Serial.println("SHT initialization failed"); + } - // T + RH reading - readSHT(); + // T + RH reading + readSHT(); - delay(100); + delay(100); - // end I2C to free the pins to be used by the SD card - Wire.end(); + // end I2C to free the pins to be used by the SD card + Wire.end(); - out_data_sht85 sht85Data; - sht85Data.temperature = sht.getTemperature(); - sht85Data.humidity = sht.getHumidity(); - return sht85Data; + out_data_sht85 sht85Data; + sht85Data.temperature = sht.getTemperature(); + sht85Data.humidity = sht.getHumidity(); + return sht85Data; } -std::list<Message> Sht85::buildMessages() -{ - std::list<Message> messages; - auto data = readData(); - MeasurementData temperatureMeasurementData{ - data.temperature, {}, 0x44, measurementTypeToString.at(MeasurementType::TEMPERATURE)}; - MeasurementData humidityMeasurementData{ - data.humidity, {}, 0x44, measurementTypeToString.at(MeasurementType::HUMIDITY)}; +std::list<Message> Sht85::buildMessages() { + std::list<Message> messages; + auto data = readData(); + Measurement temperatureMeasurementData{data.temperature, NO_CHANNEL, 0x44, + MeasurementType::TEMPERATURE, ErrorType::DATA_OK}; + Measurement humidityMeasurementData{data.humidity, NO_CHANNEL, 0x44, + MeasurementType::HUMIDITY, ErrorType::DATA_OK}; - messages.emplace_back( - Message{temperatureMeasurementData, sensorInformation, Time::getInstance().getEpochSeconds()}); - messages.emplace_back(Message{humidityMeasurementData, sensorInformation, Time::getInstance().getEpochSeconds()}); + messages.emplace_back( + temperatureMeasurementData, + sensorInformation, + Time::getInstance().getEpochSeconds()); + messages.emplace_back(humidityMeasurementData, + sensorInformation, + Time::getInstance().getEpochSeconds()); - return messages; + return messages; } -SensorInformation Sht85::getSensorInformation() const -{ - return sensorInformation; +SensorInformation Sht85::getSensorInformation() const { + return sensorInformation; } diff --git a/client/libs/sht85/Sht85.hpp b/client/libs/sht85/Sht85.hpp index 00c7114f27a7ab0382bc09e765d2728fd7d1b219..c9e86383ef5d60261fd993653c348f0b48c08261 100644 --- a/client/libs/sht85/Sht85.hpp +++ b/client/libs/sht85/Sht85.hpp @@ -6,7 +6,7 @@ #define ESPCAM_SHT85_HPP #include "ForteSensor.hpp" -#include "MeasurementData.hpp" +#include "Measurement.hpp" #include "Message.hpp" #include "Pinout.hpp" #include "RTClib.h" // adafruit/RTClib @^2.1.1 @@ -23,28 +23,24 @@ #define SCL 12 struct out_data_sht85 { - float temperature = 0.0; - float humidity = 0.0; + float temperature = 0.0; + float humidity = 0.0; }; -class Sht85 : public ForteSensor<out_data_sht85> { - public: - void setup() override; - out_data_sht85 readData() override; - std::list<Message> buildMessages() override; - [[nodiscard]] SensorInformation getSensorInformation() const override; +class Sht85: public ForteSensor<out_data_sht85> { + public: + void setup() override; + out_data_sht85 readData() override; + std::list<Message> buildMessages() override; + [[nodiscard]] SensorInformation getSensorInformation() const override; - private: - SHTSensor sht; // I2C address: 0x44 - out_data_sht85 data; - const SensorInformation sensorInformation{"SHT85", Protocol::I2C}; - void readSHT(); + private: + SHTSensor sht; // I2C address: 0x44 + out_data_sht85 data; + const SensorInformation + sensorInformation{HardwareName::SHT85, SensorProtocol::I2C}; + void readSHT(); - enum class MeasurementType { TEMPERATURE, HUMIDITY }; - - // enum to string - std::map<MeasurementType, const char *> measurementTypeToString = {{MeasurementType::TEMPERATURE, "TEMPERATURE"}, - {MeasurementType::HUMIDITY, "HUMIDITY"}}; }; #endif // ESPCAM_SHT85_HPP diff --git a/code-snippets/espnow_satellite/src/ClientDataPackage.hpp b/code-snippets/espnow_satellite/src/ClientDataPackage.hpp index f08e6bba4ea43308e606f06f7870735d7f4820bc..2f80c5647490a42597f9f36aa1bba8fff8b26941 100644 --- a/code-snippets/espnow_satellite/src/ClientDataPackage.hpp +++ b/code-snippets/espnow_satellite/src/ClientDataPackage.hpp @@ -1,7 +1,7 @@ #pragma once #include <ArduinoJson.h> -#include "MeasurementData.hpp" +#include "Measurement.hpp" #include "Protocol.hpp" #include "SensorInformation.hpp" #include <list> diff --git a/host/host_central_mast/include/QueueStruct.h b/host/host_central_mast/include/QueueStruct.h new file mode 100644 index 0000000000000000000000000000000000000000..15607ea353f5b0fc52b5f4385132deb2fa761595 --- /dev/null +++ b/host/host_central_mast/include/QueueStruct.h @@ -0,0 +1,13 @@ +// +// Created by zoe on 2/7/23. +// + +#ifndef HOST_CENTRAL_MAST_QUEUESTRUCT_H +#define HOST_CENTRAL_MAST_QUEUESTRUCT_H + +struct QueueStruct { + std::array<ClientDataPackage, 6> data; + uint8_t mac[6]{}; +}; + +#endif // HOST_CENTRAL_MAST_QUEUESTRUCT_H diff --git a/host/host_central_mast/lib/Utilities/SDCardLogger.cpp b/host/host_central_mast/lib/Utilities/SDCardLogger.cpp index b7fe7c7dcd2ef8b075cd67faae9ae187f01922b0..e35196503a0b10e858b8f32916ae4b2acaef24a3 100644 --- a/host/host_central_mast/lib/Utilities/SDCardLogger.cpp +++ b/host/host_central_mast/lib/Utilities/SDCardLogger.cpp @@ -15,10 +15,14 @@ namespace SDCardLogger { int vprintf_into_sd(const char *szFormat, va_list args) { String logstring = "[" + rtc.getDateTime() + "] "; logstring += szFormat; - + // print current core + logstring += "Core: "; + logstring += xPortGetCoreID(); + logstring += ". "; if (!SDUtilities::isSDAvailable()) { if (printToSerial) { logstring += " (SD card not available)\n"; + vprintf(logstring.c_str(), args); } return 1; diff --git a/host/host_central_mast/lib/Utilities/Utilities.cpp b/host/host_central_mast/lib/Utilities/Utilities.cpp index 316d33c95d27260f7b3c0ca299ed049db6305bce..50e18e0adbb892bd01ceeac66dde1a77eb57f5f1 100644 --- a/host/host_central_mast/lib/Utilities/Utilities.cpp +++ b/host/host_central_mast/lib/Utilities/Utilities.cpp @@ -237,6 +237,40 @@ namespace SDUtilities { } } // namespace SDUtilities +namespace ESPUtilities { + + String getMacAddressAsString(const uint8_t *mac) { + String macAddress; + for (int i = 0; i < 6; i++) { + macAddress += String(mac[i], HEX); + } + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "MAC: %s\n", macAddress.c_str()); + return macAddress; + } + + void checkPeerExistence(const uint8_t *mac) { + if (!esp_now_is_peer_exist(mac)) { + esp_now_peer_info_t client = {}; + memcpy(client.peer_addr, mac, sizeof(uint8_t) * 6); + client.encrypt = false; + client.channel = 0; + + esp_err_t status = esp_now_add_peer(&client); + if (status != ESP_OK) { + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Failed to add new Peer: %d", status); + } + } + } + + void sendResponse(const uint8_t *mac) { + response response = {}; + response.type = dataAck; + esp_read_mac(response.mac, ESP_MAC_WIFI_STA); + response.time = rtc.getEpoch(); + esp_err_t success = esp_now_send(mac, (uint8_t *)&response, sizeof(response)); + } +} // namespace ESPUtilities + // I don't think this does anything. Copied from the example void turnOffLEDs() { // Set LED OFF pinMode(LED_PIN, OUTPUT); @@ -248,15 +282,6 @@ void turnOffLEDs() { // Set LED OFF digitalWrite(PWR_PIN, LOW); } -String getMacAddressAsString(const uint8_t *mac) { - String macAddress; - for (int i = 0; i < 6; i++) { - macAddress += String(mac[i], HEX); - } - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "MAC: %s\n", macAddress.c_str()); - return macAddress; -} - String documentToLineProtocolString(const DynamicJsonDocument &doc) { String measurementType = doc["measurementType"].as<String>(); String sensorName = doc["sensorName"].as<String>(); @@ -288,16 +313,42 @@ String documentToServerReadableString(const DynamicJsonDocument &doc) { serverDoc["host"] = hostMacAddressString; serverDoc["client"] = doc["clientMac"].as<String>(); - serverDoc["sensorProtocol"] = doc["protocol"].as<String>(); + serverDoc["sensorProtocol"] = doc["sensorProtocol"].as<String>(); serverDoc["protocolAddress"] = doc["channel"].as<String>(); - serverDoc["hardwareName"] = doc["sensorName"].as<String>(); + serverDoc["i2cAddress"] = doc["i2cAddress"].as<String>(); + serverDoc["hardwareName"] = doc["hardwareName"].as<String>(); // each value is a element in the readigs array JsonArray readings = serverDoc.createNestedArray("readings"); JsonObject reading = readings.createNestedObject(); reading["name"] = doc["measurementType"].as<String>(); - reading["value"] = doc["value"].as<float>(); + reading["value"] = doc["value"].as<double>(); serverDoc["time"] = doc["timestamp"].as<uint32_t>(); + String serverString; + serializeJson(serverDoc, serverString); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Server readable data: %s\n", serverString.c_str()); + return serverString; +} + +String dataPackageToServerReadableString(ClientDataPackage clientDataPackage, const String &clientMacAddress) { + StaticJsonDocument<300> serverDoc; + String hostMacAddressString = WiFi.macAddress(); + hostMacAddressString.replace(":", ""); + hostMacAddressString.toLowerCase(); + + serverDoc["host"] = hostMacAddressString; + serverDoc["client"] = clientMacAddress; + serverDoc["sensorProtocol"] = clientDataPackage.getSensorInformation().getProtocolString(); + serverDoc["protocolAddress"] = clientDataPackage.getMeasurementData().getChannel(); + serverDoc["i2cAddress"] = clientDataPackage.getMeasurementData().getI2CAddress(); + serverDoc["hardwareName"] = clientDataPackage.getSensorInformation().getHardwareNameString(); + // each value is a element in the readigs array + JsonArray readings = serverDoc.createNestedArray("readings"); + JsonObject reading = readings.createNestedObject(); + reading["name"] = clientDataPackage.getMeasurementData().getMeasurementTypeString(); + reading["value"] = clientDataPackage.getMeasurementData().getValue(); + serverDoc["time"] = clientDataPackage.getTimestamp(); + String serverString; serializeJson(serverDoc, serverString); esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Server readable data: %s\n", serverString.c_str()); diff --git a/host/host_central_mast/lib/Utilities/Utilities.h b/host/host_central_mast/lib/Utilities/Utilities.h index dc18e3459e0fabe83c64f2e09a4a9ae3bf8c45f6..a196ea8c18fbf854aa482b5b84f673bec7d260e9 100644 --- a/host/host_central_mast/lib/Utilities/Utilities.h +++ b/host/host_central_mast/lib/Utilities/Utilities.h @@ -6,13 +6,16 @@ #define HOST_CENTRAL_MAST_UTILITIES_H #include "ArduinoJson.h" +#include "MessageType.h" #include "SD.h" #include "SDCardException.h" #include "SDSetupException.h" #include "WiFi.h" #include <Arduino.h> +#include <ClientDataPackage.hpp> #include <Definitions.h> #include <WString.h> +#include <esp_now.h> #include <list> namespace SDUtilities { @@ -29,10 +32,18 @@ namespace SDUtilities { bool isSDAvailable(); } // namespace SDUtilities +namespace ESPUtilities { + + String getMacAddressAsString(const uint8_t *mac); + void checkPeerExistence(const uint8_t *mac); + void sendResponse(const uint8_t *mac); +} // namespace ESPUtilities + void turnOffLEDs(); String getMacAddressAsString(const uint8_t *mac); String documentToLineProtocolString(const DynamicJsonDocument &doc); DynamicJsonDocument parseReceivedJsonData(char *data); String documentToServerReadableString(const DynamicJsonDocument &doc); +String dataPackageToServerReadableString(ClientDataPackage clientDataPackage, const String &clientMacAddress); #endif // HOST_CENTRAL_MAST_UTILITIES_H diff --git a/host/host_central_mast/platformio.ini b/host/host_central_mast/platformio.ini index f935fa80544f7810f4d3d5e7d9712f08d8551bde..5cd5785ab457376b7c5a95f86002031764cad269 100644 --- a/host/host_central_mast/platformio.ini +++ b/host/host_central_mast/platformio.ini @@ -14,8 +14,10 @@ board = esp-wrover-kit framework = arduino monitor_speed = 115200 lib_ldf_mode = deep -monitor_port = /dev/ttyACM0 -upload_port = /dev/ttyACM0 +monitor_port = /dev/ttyACM1 +upload_port = /dev/ttyACM1 +lib_extra_dirs = + ../../shared-libs build_flags = -I include -DCORE_DEBUG_LEVEL=5 diff --git a/host/host_central_mast/src/main.cpp b/host/host_central_mast/src/main.cpp index af25396fb4e9f4d82cd8169b914373894ec541f9..68b7710938fde3cf3f3d74f7c4d25001394a7fe8 100644 --- a/host/host_central_mast/src/main.cpp +++ b/host/host_central_mast/src/main.cpp @@ -5,8 +5,10 @@ #define TINY_GSM_MODEM_SIM7000 +#include "ClientDataPackage.hpp" #include "ConnectionManager.h" #include "MessageType.h" +#include "QueueStruct.h" #include "SDCardLogger.h" #include "SPI.h" #include "Utilities.h" @@ -14,13 +16,12 @@ #include <Definitions.h> #include <ESP32Time.h> #include <NTPManager.h> -#include <ResendManager.h> #include <TinyGsmClient.h> #include <WiFi.h> #include <esp_log.h> #include <esp_now.h> -#include <map> #include <queue> +#include <soc/rtc_cntl_reg.h> #include <sys/unistd.h> ESP32Time rtc; @@ -51,68 +52,26 @@ SemaphoreHandle_t xMutex; TaskHandle_t ESPNOWTask; -static std::queue<String> queue; - -ResendManager resendManager; +void handleDataReceive(const uint8_t *mac, const uint8_t *incomingData); +static std::queue<QueueStruct> queue; void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { // go to sleep } +/** + * @brief ESPNOW callback function that is called when data is received + * @param mac + * @param incomingData + * @param len length of the incoming data in bytes + */ void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) { + ESPUtilities::checkPeerExistence(mac); - response response = {}; - // send a host change msg if client is new, simplifies client - response.type = esp_now_is_peer_exist(mac) ? dataAck : hostChange; - esp_read_mac(response.mac, ESP_MAC_WIFI_STA); - response.time = rtc.getEpoch(); - - // this block needs to happen before we send the message, as it's not possible to - // send a message to an unregistered peer (i.e. new client) - if (!esp_now_is_peer_exist(mac)) { - esp_now_peer_info_t client = {}; - memcpy(client.peer_addr, mac, sizeof(uint8_t) * 6); - client.encrypt = false; - client.channel = 0; - - esp_err_t status = esp_now_add_peer(&client); - if (status != ESP_OK) { - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Failed to add new Peer: %d", status); - } - } - - esp_err_t success = esp_now_send(mac, (uint8_t *)&response, sizeof(response)); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, (success == ESP_OK) ? "Response sent\n" : "Failed to respond\n"); - - char data[len]; - memcpy(data, incomingData, len); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Raw received Data: %s\n", data); - - DynamicJsonDocument doc = parseReceivedJsonData(data); + ESPUtilities::sendResponse(mac); - String macAddress = getMacAddressAsString(mac); - - // add timestamp and mac address - // doc["timestamp"] = rtc.getEpoch(); - doc["clientMac"] = macAddress; - - // serialize json document again - std::string dataString{}; - serializeJson(doc, dataString); - - try { - SDUtilities::saveStringToSDCard(dataString); - } catch (const std::exception &e) { - esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW, "Failed to save data to SD card: %s", e.what()); - } - - String serverData = documentToServerReadableString(doc); - - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Data to be sent: %s\n", serverData.c_str()); - - xSemaphoreTake(xMutex, portMAX_DELAY); - queue.push(serverData); - xSemaphoreGive(xMutex); + // requires queue and I am too lazy to extern declare the queue + handleDataReceive(mac, incomingData); } [[noreturn]] void esp_loop() { @@ -121,7 +80,7 @@ void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) { } [[noreturn]] void ESPNOWReceiveTask(void *parameter) { - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "ESPNOWReceiveTask started on core %d\n", xPortGetCoreID()); + esp_log_write(ESP_LOG_INFO, TAG_ESPNOW, "ESPNOWReceiveTask started on core %d\n", xPortGetCoreID()); WiFi.mode(WIFI_STA); @@ -160,6 +119,7 @@ void setup() { // Set console baud rate Serial.begin(115200); delay(10); + WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable detector try { SDUtilities::setupSDCard(SD_MISO, SD_MOSI, SD_SCLK, SD_CS); } catch (const SDSetupException &e) { @@ -167,14 +127,13 @@ void setup() { esp_log_write(ESP_LOG_ERROR, TAG_MAIN, "SD Card setup failed: %s\n", e.what()); } - SDCardLogger::printDebugToSerial(PRINT_TO_SERIAL); + SDCardLogger::printDebugToSerial(true); // https://stackoverflow.com/questions/60442350/arduinos-esp-log-set-vprintf-does-not-work-on-esp32 esp_log_set_vprintf(&SDCardLogger::vprintf_into_sd); esp_log_level_set("*", ESP_LOG_VERBOSE); esp_log_write(ESP_LOG_DEBUG, TAG_MAIN, "%s", WiFi.macAddress().c_str()); - resendManager.init(); turnOffLEDs(); xMutex = xSemaphoreCreateMutex(); @@ -192,10 +151,57 @@ void setup() { 0); /* Core where the task should run */ SerialAT.begin(UART_BAUD, SERIAL_8N1, PIN_RX, PIN_TX); + + // Restart takes quite some time + // To skip it, call init() instead of restart() +} + +bool isQueueEmpty() { + bool emptyQueue; + xSemaphoreTake(xMutex, portMAX_DELAY); + emptyQueue = queue.empty(); + xSemaphoreGive(xMutex); + return emptyQueue; } void loop() { + // exit after 10 retries + int earlyExitCounter = 0; + + // do while loop that takes one element of the queue each until the queue is empty + while (!isQueueEmpty()) { + xSemaphoreTake(xMutex, portMAX_DELAY); + auto data = queue.front(); + queue.pop(); + xSemaphoreGive(xMutex); + + // get mac address as string + String macAddress = ESPUtilities::getMacAddressAsString(data.mac); + + for (const auto &dataPackage : data.data) { + // ignore padding messages + if (dataPackage.getMeasurementData().getErrorType() == ErrorType::NULL_MESSAGE) { + continue; + } + + auto serverReadableString = dataPackageToServerReadableString(dataPackage, macAddress); + + try { + SDUtilities::saveStringToSDCard(serverReadableString.c_str()); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW, "Failed to save data to SD card: %s", e.what()); + } + + // TODO: Try send or add to queue + } + + if (earlyExitCounter > 10) { + break; + } + earlyExitCounter++; + } + connectionManager.modemPowerOn(); delay(5000L); @@ -253,29 +259,9 @@ void loop() { esp_log_write(ESP_LOG_ERROR, TAG_GSM, "Error writing time to rtc: %s\n", e.what()); } - if (connectionManager.isGprsConnected()) { - esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "GPRS connected\n"); - // make list of map of headers - std::map<String, String> headers; - headers["Content-Type"] = "application/json"; - headers["Accept"] = "application/json"; - - xSemaphoreTake(xMutex, portMAX_DELAY); - String data = queue.front(); - queue.pop(); - xSemaphoreGive(xMutex); - - RequestInformation requestInformation{POST, "influxdb.qe-forte.uibk.ac.at", - 80, "/api/v2/write?org=QE&bucket=esp32test&precision=s", - data, headers}; - - try { - connectionManager.connect(requestInformation); - } catch (const std::exception &e) { - esp_log_write(ESP_LOG_ERROR, TAG_GSM, "Error sending data: %s\n", e.what()); - } - esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Data sent: %s\n", data.c_str()); - } + // RequestInformation requestInformation("GET", "vsh.pp.ua", "/TinyGSM/logo.txt", ""); + // auto s = connectionManager.connect(80, requestInformation); + // esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Response: %s\n", s.c_str()); connectionManager.gprsDisconnect(); delay(5000L); @@ -290,3 +276,10 @@ void loop() { delay(10000); } +void handleDataReceive(const uint8_t *mac, const uint8_t *incomingData) { + std::array<ClientDataPackage, 6> compressedDataPackage{}; + memcpy(&compressedDataPackage, incomingData, sizeof(ClientDataPackage) * 6); + xSemaphoreTake(xMutex, portMAX_DELAY); + queue.emplace(QueueStruct{compressedDataPackage, {mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]}}); + xSemaphoreGive(xMutex); +} \ No newline at end of file diff --git a/shared-libs/DataTransfer/ClientDataPackage.hpp b/shared-libs/DataTransfer/ClientDataPackage.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3f1413cc21e9e13c6428973166eaf817650dbcfe --- /dev/null +++ b/shared-libs/DataTransfer/ClientDataPackage.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include <ArduinoJson.h> +#include "Measurement.hpp" +#include "SensorProtocol.hpp" +#include "SensorInformation.hpp" +#include <list> +#include <optional> +#include <string> +#include <utility> +#include <ErrorTypes.h> + +// having the data be a struct of basic types makes sending easier, +// otherwise we would have to serialize the data before sending +class ClientDataPackage { +private: + Measurement measurementData; + SensorInformation sensorInformation; + unsigned long timestamp; // maybe make this array + +public: + ClientDataPackage(Measurement value, + SensorInformation sensorInformation, + + unsigned long timestamp) + : measurementData(value), + sensorInformation(sensorInformation), + timestamp(timestamp) { + } + + + [[nodiscard]] const Measurement & + getMeasurementData() const { return measurementData; } + + [[nodiscard]] const SensorInformation & + getSensorInformation() const { return sensorInformation; } + + [[nodiscard]] unsigned long getTimestamp() const { return timestamp; } + + [[nodiscard]] std::string getDataPackageAsMinifiedJsonString() const { + StaticJsonDocument<250> document; // 250 byte is the max send size of espnow + + document["hardwareName"] = sensorInformation.getHardwareNameString(); + document["timestamp"] = timestamp; + document["sensorProtocol"] = sensorInformation.getProtocolString(); + document["value"] = measurementData.getValue(); + + + document["channel"] = measurementData.getChannel(); + + + document["i2cAddress"] = measurementData.getI2CAddress(); + + document["measurementType"] = measurementData.getMeasurementTypeString(); + document["errorType"] = measurementData.getErrorTypeString(); + + std::string jsonString; + serializeJson(document, jsonString); + return jsonString; + } + + ClientDataPackage() : measurementData(Measurement(ERROR_VALUE, NO_CHANNEL, NO_I2C_ADDRESS, + MeasurementType::TEMPERATURE, + ErrorType::NULL_MESSAGE)), + sensorInformation(SensorInformation(HardwareName::NONE, + SensorProtocol::NULL_PROTOCOL)), + timestamp(0) {} +}; diff --git a/shared-libs/DataTransfer/ErrorTypes.cpp b/shared-libs/DataTransfer/ErrorTypes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2babe9db1275a12c7b1a61e74ca16011cadca18f --- /dev/null +++ b/shared-libs/DataTransfer/ErrorTypes.cpp @@ -0,0 +1,41 @@ +// +// Created by zoe on 2/2/23. +// + +#include "ErrorTypes.h" + +namespace ErrorTypes { + + std::string errorTypeToString(ErrorType errorType) { + switch (errorType) { + case ErrorType::SENSOR_NOT_FOUND: + return "SENSOR_NOT_FOUND"; + case ErrorType::SENSOR_NOT_CONNECTED: + return "SENSOR_NOT_CONNECTED"; + case ErrorType::NO_DATA: + return "NO_DATA"; + case ErrorType::DATA_OK: + return "DATA_OK"; + case ErrorType::NULL_MESSAGE: + return "NULL_MESSAGE"; + case ErrorType::SENSOR_DOES_NOT_RETURN_DATA: + return "SENSOR_DOES_NOT_RETURN_DATA"; + case ErrorType::BATTERY_VOLTAGE_TOO_LOW: + return "BATTERY_VOLTAGE_TOO_LOW"; + case ErrorType::INVALID_VALUE: + return "INVALID_VALUE"; + case ErrorType::SENSOR_INIT_FAILED: + return "SENSOR_INIT_FAILED"; + case ErrorType::INA219_OVERFLOW: + return "INA219_OVERFLOW"; + case ErrorType::CONNECTION_ENDED_PREMATURELY: + return "CONNECTION_ENDED_PREMATURELY"; + case ErrorType::WRONG_CRC: + return "WRONG_CRC"; + case ErrorType::UNKNOWN: + default: + return "UNKNOWN_ERROR_TYPE"; + } + } + +} \ No newline at end of file diff --git a/shared-libs/DataTransfer/ErrorTypes.h b/shared-libs/DataTransfer/ErrorTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..3d12cbd8bbdc923154322960ed984ef9e734334a --- /dev/null +++ b/shared-libs/DataTransfer/ErrorTypes.h @@ -0,0 +1,31 @@ +// +// Created by zoe on 2/2/23. +// + +#ifndef CLIENT_MOCK_ERRORTYPES_H +#define CLIENT_MOCK_ERRORTYPES_H + +#include <string> + +enum class ErrorType : char { + SENSOR_NOT_FOUND, + SENSOR_INIT_FAILED, + SENSOR_NOT_CONNECTED, + NO_DATA, + DATA_OK, + NULL_MESSAGE, // message that is sent as padding, should be thrown away + SENSOR_DOES_NOT_RETURN_DATA, + BATTERY_VOLTAGE_TOO_LOW, + INVALID_VALUE, + INA219_OVERFLOW, + CONNECTION_ENDED_PREMATURELY, // connection ended prematurely + WRONG_CRC, // corrupted data package + UNKNOWN, +}; + +namespace ErrorTypes { + std::string errorTypeToString(ErrorType errorType); +} + + +#endif //CLIENT_MOCK_ERRORTYPES_H diff --git a/shared-libs/DataTransfer/GlobalDefinitions.hpp b/shared-libs/DataTransfer/GlobalDefinitions.hpp new file mode 100644 index 0000000000000000000000000000000000000000..b4863dcd8950c5cf430eaf30c236cb56bce58345 --- /dev/null +++ b/shared-libs/DataTransfer/GlobalDefinitions.hpp @@ -0,0 +1,14 @@ +// +// Created by zoe on 2/2/23. +// + +#ifndef CLIENT_MOCK_GLOBALDEFINITIONS_HPP +#define CLIENT_MOCK_GLOBALDEFINITIONS_HPP + + +constexpr unsigned long NULL_TIMESTAMP = 0; +constexpr int NO_I2C_ADDRESS = -1; +constexpr int NO_CHANNEL = -1; +constexpr double ERROR_VALUE = -999.99; + +#endif //CLIENT_MOCK_GLOBALDEFINITIONS_HPP diff --git a/shared-libs/DataTransfer/HardwareNames.cpp b/shared-libs/DataTransfer/HardwareNames.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0150aaf61a6286ff03a93844d7124058c2aac741 --- /dev/null +++ b/shared-libs/DataTransfer/HardwareNames.cpp @@ -0,0 +1,39 @@ +// +// Created by zoe on 2/2/23. +// + +#include "HardwareNames.h" +namespace HardwareNames { +std::string hardwareNameToString(HardwareName hardwareName) { + // switch + switch (hardwareName) { + case HardwareName::RS485: + return "RS485"; + case HardwareName::INA219: + return "INA219"; + case HardwareName::SCD30: + return "SCD30"; + case HardwareName::RAIN_GAUGE: + return "RAIN_GAUGE"; + case HardwareName::SOIL_MOISTURE_SENSOR: + return "SOIL_MOISTURE_SENSOR"; + case HardwareName::SOIL_TEMPERATURE_SENSOR: + return "SOIL_TEMPERATURE_SENSOR"; + case HardwareName::SOLAR_RADIATION_SENSOR: + return "SOLAR_RADIATION_SENSOR"; + case HardwareName::SHT85: + return "SHT85"; + case HardwareName::DRS26: + return "DRS26"; + case HardwareName::DR26: + return "DR26"; + case HardwareName::MOCK: + return "MOCK"; + case HardwareName::NONE: + return "NONE"; + case HardwareName::LC709203: + break; + } + return "UNKNOWN HARDWARE NAME"; +} +} \ No newline at end of file diff --git a/shared-libs/DataTransfer/HardwareNames.h b/shared-libs/DataTransfer/HardwareNames.h new file mode 100644 index 0000000000000000000000000000000000000000..5bf6930379ab6069940cd6763eb022db8d511287 --- /dev/null +++ b/shared-libs/DataTransfer/HardwareNames.h @@ -0,0 +1,34 @@ +// +// Created by zoe on 2/2/23. +// + +#ifndef CLIENT_MOCK_HARDWARENAMES_H +#define CLIENT_MOCK_HARDWARENAMES_H + +#include <map> + +// 32,767 possible values +enum class HardwareName : short { + RS485, // TODO: THIS IS THE PROTOCOL NAME NOT THE HARDWARE NAME + INA219, + SCD30, + RAIN_GAUGE, + SOIL_MOISTURE_SENSOR, + SOIL_TEMPERATURE_SENSOR, + SOLAR_RADIATION_SENSOR, + SHT85, + DRS26, + DR26, + MOCK, + NONE, + LC709203, +}; + +// hardware name to string function + +namespace HardwareNames { +std::string hardwareNameToString(HardwareName hardwareName); +} + + +#endif //CLIENT_MOCK_HARDWARENAMES_H diff --git a/shared-libs/DataTransfer/Measurement.hpp b/shared-libs/DataTransfer/Measurement.hpp new file mode 100644 index 0000000000000000000000000000000000000000..49bcd4956e151bc1f4ec910970fceba51cd80df2 --- /dev/null +++ b/shared-libs/DataTransfer/Measurement.hpp @@ -0,0 +1,68 @@ +// +// Created by cynthya on 10/6/22. +// + +#ifndef CLIENT_MEASUREMENTDATA_HPP +#define CLIENT_MEASUREMENTDATA_HPP + +#include "ArduinoJson.h" +#include "SensorProtocol.hpp" +#include "SensorInformation.hpp" +#include <list> +#include <optional> +#include <string> +#include <utility> +#include <MeasurementTypes.h> +#include <GlobalDefinitions.hpp> +#include "ErrorTypes.h" + +class Measurement { +public: + Measurement(double value, int channel, int i2cAddress, MeasurementType measurementType, + ErrorType errorType) + : value(value), measurementType(measurementType), channel(channel), + i2cAddress(i2cAddress), errorType(errorType) { + } + + Measurement(double value, MeasurementType measurementType) + : value(value), measurementType(measurementType) { + channel = NO_CHANNEL; + i2cAddress = NO_I2C_ADDRESS; + errorType = ErrorType::DATA_OK; + } + + [[nodiscard]] double getValue() const { return value; } + + [[nodiscard]] const MeasurementType & + getMeasurementType() const { return measurementType; } + + [[nodiscard]] std::string getMeasurementTypeString() const { + return MeasurementTypes::measurementTypeToString(measurementType); + } + + [[nodiscard]] const int &getChannel() const { return channel; } + + [[nodiscard]] const ErrorType &getErrorType() const { return errorType; } + + [[nodiscard]] std::string getErrorTypeString() const { + return ErrorTypes::errorTypeToString(errorType); + } + + void setErrorType(ErrorType type) { + errorType = type; + } + + [[nodiscard]] const int & + getI2CAddress() const { return i2cAddress; } + + +private: + double value; + MeasurementType measurementType; + ErrorType errorType; + // TODO: is it possible for a sensor to have both a channel and an i2cAddress? + int channel = NO_CHANNEL; + int i2cAddress = NO_I2C_ADDRESS; +}; + +#endif // CLIENT_MEASUREMENTDATA_HPP diff --git a/shared-libs/DataTransfer/MeasurementTypes.cpp b/shared-libs/DataTransfer/MeasurementTypes.cpp new file mode 100644 index 0000000000000000000000000000000000000000..220da6e1879f570b4faacd47372aea5974bdee7b --- /dev/null +++ b/shared-libs/DataTransfer/MeasurementTypes.cpp @@ -0,0 +1,54 @@ +// +// Created by zoe on 2/2/23. +// +# include "MeasurementTypes.h" +namespace MeasurementTypes { +std::string measurementTypeToString(MeasurementType measurementType) { + switch (measurementType) { + case MeasurementType::CIRCUMFERENCE_INCREMENT: + return "CIRCUMFERENCE_INCREMENT"; + case MeasurementType::TEMPERATURE: + return "TEMPERATURE"; + case MeasurementType::HUMIDITY: + return "HUMIDITY"; + case MeasurementType::SHUNT_VOLTAGE: + return "SHUNT_VOLTAGE"; + case MeasurementType::BUS_VOLTAGE: + return "BUS_VOLTAGE"; + case MeasurementType::CURRENT_mA: + return "CURRENT_mA"; + case MeasurementType::POWER_mW: + return "POWER_mW"; + case MeasurementType::LOAD_VOLTAGE_V: + return "LOAD_VOLTAGE_V"; + case MeasurementType::INA219_OVERFLOW: + return "INA219_OVERFLOW"; + case MeasurementType::BATTERY_VOLTAGE: + return "BATTERY_VOLTAGE"; + case MeasurementType::MOCK: + return "MOCK"; + case MeasurementType::SOLAR_RADIATION: + return "SOLAR_RADIATION"; + case MeasurementType::SOIL_MOISTURE_3: + return "SOIL_MOISTURE_3"; + case MeasurementType::SOIL_TEMPERATURE_3: + return "SOIL_TEMPERATURE_3"; + case MeasurementType::SOIL_MOISTURE_4: + return "SOIL_MOISTURE_4"; + case MeasurementType::SOIL_TEMPERATURE_4: + return "SOIL_TEMPERATURE_4"; + case MeasurementType::SOIL_MOISTURE_5: + return "SOIL_MOISTURE_5"; + case MeasurementType::SOIL_TEMPERATURE_5: + return "SOIL_TEMPERATURE_5"; + case MeasurementType::PRECIPITATION: + return "PRECIPITATION"; + case MeasurementType::CO2: + return "CO2"; + case MeasurementType::NULL_MEASUREMENT: + return "NULL_MEASUREMENT"; + } + return "UNKNOWN"; +} + +} \ No newline at end of file diff --git a/shared-libs/DataTransfer/MeasurementTypes.h b/shared-libs/DataTransfer/MeasurementTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..0818bba5453a7223c9f84025bd9608343a6a0672 --- /dev/null +++ b/shared-libs/DataTransfer/MeasurementTypes.h @@ -0,0 +1,40 @@ +// +// Created by zoe on 2/2/23. +// + +#ifndef CLIENT_MOCK_MEASUREMENTTYPES_H +#define CLIENT_MOCK_MEASUREMENTTYPES_H + +#include <map> + +enum class MeasurementType { + CIRCUMFERENCE_INCREMENT, + TEMPERATURE, + HUMIDITY, + SHUNT_VOLTAGE, + BUS_VOLTAGE, + CURRENT_mA, + POWER_mW, + LOAD_VOLTAGE_V, + INA219_OVERFLOW, + BATTERY_VOLTAGE, + MOCK, + SOLAR_RADIATION, + SOIL_MOISTURE_3, + SOIL_TEMPERATURE_3, + SOIL_MOISTURE_4, + SOIL_TEMPERATURE_4, + SOIL_MOISTURE_5, + SOIL_TEMPERATURE_5, + PRECIPITATION, + CO2, + NULL_MEASUREMENT, +}; + +// convert below to a function +// measurement type to string function +namespace MeasurementTypes { +std::string measurementTypeToString(MeasurementType measurementType); +} + +#endif //CLIENT_MOCK_MEASUREMENTTYPES_H diff --git a/shared-libs/DataTransfer/SensorInformation.hpp b/shared-libs/DataTransfer/SensorInformation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c95d99107fb54b138c77990a92ad2dba1ad37021 --- /dev/null +++ b/shared-libs/DataTransfer/SensorInformation.hpp @@ -0,0 +1,33 @@ +// +// Created by cynthya on 10/6/22. +// + +#ifndef CLIENT_SENSORINFORMATION_HPP +#define CLIENT_SENSORINFORMATION_HPP + +#include "SensorProtocol.hpp" +#include <string> +#include <HardwareNames.h> + +class SensorInformation { + public: + SensorInformation(HardwareName hardwareName, SensorProtocol sensorProtocol) + : hardwareName(hardwareName), sensorProtocol(sensorProtocol) { + } + + [[nodiscard]] const HardwareName & + getHardwareName() const { return hardwareName; } + [[nodiscard]] std::string getHardwareNameString() const { + return HardwareNames::hardwareNameToString(hardwareName); + } + [[nodiscard]] SensorProtocol getProtocol() const { return sensorProtocol; } + [[nodiscard]] std::string getProtocolString() const { + return SensorProtocols::sensorProtocolToString(sensorProtocol); + } + + private: + HardwareName hardwareName; + SensorProtocol sensorProtocol; +}; + +#endif // CLIENT_SENSORINFORMATION_HPP diff --git a/shared-libs/DataTransfer/SensorProtocol.cpp b/shared-libs/DataTransfer/SensorProtocol.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2aa02da070bf93f3cdee9ca2682b792a6ee62cf --- /dev/null +++ b/shared-libs/DataTransfer/SensorProtocol.cpp @@ -0,0 +1,22 @@ +// +// Created by zoe on 2/2/23. +// + +#include "SensorProtocol.hpp" +namespace SensorProtocols { +std::string sensorProtocolToString(SensorProtocol sensorProtocol) { + switch (sensorProtocol) { + case SensorProtocol::I2C: + return "I2C"; + case SensorProtocol::RS485: + return "RS485"; + case SensorProtocol::Analog: + return "Analog"; + case SensorProtocol::Mock: + return "Mock"; + case SensorProtocol::NULL_PROTOCOL: + return "NULL_PROTOCOL"; + } + return "UNKNOWN"; +} +} \ No newline at end of file diff --git a/shared-libs/DataTransfer/SensorProtocol.hpp b/shared-libs/DataTransfer/SensorProtocol.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fb4d0b0f114cb2e10decb6a263044bc05ade7c3d --- /dev/null +++ b/shared-libs/DataTransfer/SensorProtocol.hpp @@ -0,0 +1,15 @@ +// +// Created by zoe on 10/5/22. +// + +#ifndef CLIENT_PROTOCOL_HPP +#define CLIENT_PROTOCOL_HPP + +#include <map> +enum class SensorProtocol : short { I2C, RS485, Analog, Mock, NULL_PROTOCOL }; + +namespace SensorProtocols { +std::string sensorProtocolToString(SensorProtocol sensorProtocol); +} + +#endif // CLIENT_PROTOCOL_HPP