Skip to content
Snippets Groups Projects
Commit de64ec6e authored by Zoe Pfister's avatar Zoe Pfister :speech_balloon:
Browse files

WIP: Trying out message compression

parent ce2210f3
No related branches found
No related tags found
2 merge requests!39Merge Develop into Main,!27Move from json-string based transfer between client and host to C struct / class based transfer
Showing
with 289 additions and 124 deletions
......@@ -3,6 +3,8 @@
#include "NoDataAvailableException.hpp"
#include "f_deep_sleep.hpp"
#include <Arduino.h>
#include <CompressedDataPackage.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();
......@@ -73,28 +70,57 @@ void setup() {
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 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
CompressedDataPackage compresseddatapackage{};
ESP_LOGD(TAG, "Size of list of 4 CompressedDataPackage: %d", sizeof(std::list<CompressedDataPackage>) + (sizeof(CompressedDataPackage) * 4));
// sizeof compresseddatapackage
ESP_LOGD(TAG, "Size of CompressedDataPackage: %d", sizeof(CompressedDataPackage));
// sizeof list
ESP_LOGD(TAG, "Size of list: %d", sizeof(std::list<CompressedDataPackage>));
// sizeof vector
ESP_LOGD(TAG, "Size of vector: %d", sizeof(std::vector<CompressedDataPackage>));
// 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::list<Message> messages;
messages.insert(messages.end(), messages0.begin(), messages0.end());
messages.insert(messages.end(), messages1.begin(), messages1.end());
messages.insert(messages.end(), messages2.begin(), messages2.end());
messages.insert(messages.end(), messages3.begin(), messages3.end());
ts = millis();
// Message::sendMessages(messages);
send_msgs(messages0);
send_msgs(messages1);
send_msgs(messages2);
send_msgs(messages3);
// send_msgs(messages1);
// send_msgs(messages2);
// send_msgs(messages3);
// roughly takes 3s in ideal conditions
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);
}
......
......@@ -117,7 +117,7 @@ protected:
int16_t read16(uint8_t regAddress);
private:
const SensorInformation sensorInformation{"LC709203", Protocol::I2C};
const SensorInformation sensorInformation{"LC709203", SensorProtocol::I2C};
enum class MeasurementType {
BATTERY_VOLTAGE,
......
......@@ -22,7 +22,7 @@ class ForteDR26 : public ForteSensor<float> {
private:
Adafruit_ADS1115 ads;
const SensorInformation sensorInformation{"DR26", Protocol::Analog};
const SensorInformation sensorInformation{"DR26", SensorProtocol::Analog};
int channel;
};
......
......@@ -52,7 +52,7 @@ std::list<Message> ForteDRS26 ::buildMessages()
messages.emplace_back(Message{circumferenceIncrementMeasurementData, sensorInformation, 0});
messages.emplace_back(Message{temperatureMeasurementData, sensorInformation, 0});
ESP_LOGE(sensorInformation.getSensorName().c_str(), "test");
ESP_LOGE(sensorInformation.getHardwareName().c_str(), "test");
return messages;
}
SensorInformation ForteDRS26::getSensorInformation() const
......
......@@ -25,7 +25,7 @@ class ForteDRS26 : public ForteSensor<out_data_drs26> {
private:
SDI12 drs26;
out_data_drs26 data;
const SensorInformation sensorInformation{"DRS26", Protocol::I2C};
const SensorInformation sensorInformation{"DRS26", SensorProtocol::I2C};
enum class MeasurementType { TEMPERATURE, CIRCUMFERENCE_INCREMENT };
// enum to string
......
......@@ -2,8 +2,9 @@
#include <ArduinoJson.h>
#include "MeasurementData.hpp"
#include "Protocol.hpp"
#include "SensorProtocol.hpp"
#include "SensorInformation.hpp"
#include "CompressedDataPackage.hpp"
#include <list>
#include <optional>
#include <string>
......@@ -12,43 +13,62 @@
// 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;
}
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["hardwareName"] = sensorInformation.getHardwareName();
document["timestamp"] = timestamp;
document["sensorProtocol"] = 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;
}
[[nodiscard]] CompressedDataPackage getCompressedDataPackage() const {
CompressedDataPackage compressedDataPackage{};
compressedDataPackage.channel = measurementData.getChannel().value_or(-1);
compressedDataPackage.i2cAddress = measurementData.getI2CAddress().value_or(-1);
compressedDataPackage.value = measurementData.getValue();
compressedDataPackage.timestamp = timestamp;
compressedDataPackage.errorType = ErrorTypes::DATA_OK;
strcpy(compressedDataPackage.hardwareName, sensorInformation.getHardwareName().c_str());
strcpy(compressedDataPackage.sensorProtocol,
protocolToString.at(sensorInformation.getProtocol()));
strcpy(compressedDataPackage.measurementType,
measurementData.getMeasurementType().c_str());
return compressedDataPackage;
}
};
//
// Created by cynthya on 1/27/23.
//
#ifndef CLIENT_MOCK_COMPRESSEDDATAPACKAGE_HPP
#define CLIENT_MOCK_COMPRESSEDDATAPACKAGE_HPP
#include <ArduinoJson.h>
#include <list>
#include <string>
#include <utility>
enum ErrorTypes : short {
SENSOR_NOT_FOUND,
SENSOR_NOT_CONNECTED,
NO_DATA,
DATA_OK,
};
struct CompressedDataPackage {
int channel;
int i2cAddress;
double value;
unsigned long timestamp;
ErrorTypes errorType;
char hardwareName[6];
char sensorProtocol[6];
char measurementType[18];
// tostring
[[nodiscard]] std::string toString() const {
StaticJsonDocument<250> document; // 250 byte is the max send size of espnow
document["hardwareName"] = hardwareName;
document["timestamp"] = timestamp;
document["sensorProtocol"] = sensorProtocol;
document["value"] = value;
if (channel != -1) {
document["channel"] = channel;
}
if (i2cAddress != -1) {
document["i2cAddress"] = i2cAddress;
}
document["measurementType"] = measurementType;
std::string jsonString;
serializeJson(document, jsonString);
return jsonString;
}
};
#endif // CLIENT_MOCK_COMPRESSEDDATAPACKAGE_HPP
#include "Message.hpp"
#include "CompressedDataPackage.hpp"
static const char *TAG = "MESSAGE";
esp_err_t Message::send() const
{
ESP_LOGD(TAG, "Sending message");
esp_err_t success;
auto messageData = getMessageAsMinifiedJsonString();
esp_err_t Message::send() const {
ESP_LOGD(TAG, "Sending message");
esp_err_t success;
auto messageData = getCompressedDataPackage();
// 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());
// 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, sizeof(CompressedDataPackage));
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);
ESP_LOGD(TAG, "Timestamp sent: %ld", clientDataPackage.getTimestamp());
ESP_LOGD(TAG, "send status: %d", success);
return success;
return success;
}
std::string Message::getMessageAsMinifiedJsonString() const
{
return clientDataPackage.getDataPackageAsMinifiedJsonString();
esp_err_t Message::sendMessages(const std::list<Message> &messages) {
// recipient
uint8_t rec[6]{};
get_host_mac(rec);
ESP_LOGD(TAG, "Sending messages");
esp_err_t success;
// list of compressed data
std::list<CompressedDataPackage> compressedDataPackages;
// max 4 messages
int i = 0;
for (const auto &message: messages) {
i++;
compressedDataPackages.push_back(message.getCompressedDataPackage());
if (i == 4) {
break;
}
}
// 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 * ) & compressedDataPackages,
sizeof(std::list<CompressedDataPackage>) +
4 * sizeof(CompressedDataPackage));
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;
}
Message::Message(ClientDataPackage data) : clientDataPackage(std::move(data))
{
// check for existing host mac address, use broadcast otherwise
get_host_mac(recipient);
std::string Message::getMessageAsMinifiedJsonString() const {
return clientDataPackage.getDataPackageAsMinifiedJsonString();
}
Message::Message(MeasurementData 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);
CompressedDataPackage Message::getCompressedDataPackage() const {
return clientDataPackage.getCompressedDataPackage();
}
Message::Message(ClientDataPackage data) : clientDataPackage(std::move(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
get_host_mac(recipient);
}
......@@ -4,6 +4,7 @@
#include "ESPNow.hpp"
#include "Time.hpp"
#include "esp_log.h"
#include "CompressedDataPackage.hpp"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP32Time.h>
......@@ -20,7 +21,12 @@ class Message {
esp_err_t send() const;
[[nodiscard]] std::string getMessageAsMinifiedJsonString() const;
private:
ClientDataPackage clientDataPackage;
static esp_err_t sendMessages(const std::list<Message>& messages);
private:
ClientDataPackage clientDataPackage;
uint8_t recipient[6]{};
CompressedDataPackage getCompressedDataPackage() const;
};
......@@ -5,7 +5,7 @@ void ForteINA219 ::setup()
Wire.begin(I2C_SDA, I2C_SCL);
if (!ina219.init()) {
// Sensor init went wrong
ESP_LOGW(sensorInformation.getSensorName().c_str(), "Initialization failed");
ESP_LOGW(sensorInformation.getHardwareName().c_str(), "Initialization failed");
return;
}
}
......
......@@ -27,7 +27,7 @@ class ForteINA219 : public ForteSensor<out_data_ina219> {
private:
INA219_WE ina219;
out_data_ina219 data;
const SensorInformation sensorInformation{"INA219", Protocol::I2C};
const SensorInformation sensorInformation{"INA219", SensorProtocol::I2C};
enum class MeasurementType {SHUNT_VOLTAGE, BUS_VOLTAGE, CURRENT_mA, POWER_mA, LOAD_VOLTAGE_V, INA219_OVERFLOW};
std::map<MeasurementType, const char*> measurementTypeToString = {
......
......@@ -2,7 +2,7 @@
#define _FORTE_SENSOR
#include "Message.hpp"
#include "Protocol.hpp"
#include "SensorProtocol.hpp"
#include "SensorInformation.hpp"
template <class T>
class ForteSensor {
......
......@@ -6,34 +6,39 @@
#define CLIENT_MEASUREMENTDATA_HPP
#include "ArduinoJson.h"
#include "Protocol.hpp"
#include "SensorProtocol.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;
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
// TODO: is it possible for a sensor to have both a channel and an i2cAddress?
std::optional<int> channel;
std::optional<int> i2cAddress;
};
#endif // CLIENT_MEASUREMENTDATA_HPP
......@@ -5,21 +5,21 @@
#ifndef CLIENT_SENSORINFORMATION_HPP
#define CLIENT_SENSORINFORMATION_HPP
#include "Protocol.hpp"
#include "SensorProtocol.hpp"
#include <string>
class SensorInformation {
public:
SensorInformation(std::string sensorName, Protocol protocol) : sensorName(std::move(sensorName)), protocol(protocol)
SensorInformation(std::string hardwareName, SensorProtocol sensorProtocol) : hardwareName(std::move(hardwareName)), sensorProtocol(sensorProtocol)
{
}
[[nodiscard]] const std::string &getSensorName() const { return sensorName; }
[[nodiscard]] Protocol getProtocol() const { return protocol; }
[[nodiscard]] const std::string &getHardwareName() const { return hardwareName; }
[[nodiscard]] SensorProtocol getProtocol() const { return sensorProtocol; }
private:
std::string sensorName;
Protocol protocol;
std::string hardwareName;
SensorProtocol sensorProtocol;
};
#endif // CLIENT_SENSORINFORMATION_HPP
......@@ -6,10 +6,10 @@
#define CLIENT_PROTOCOL_HPP
#include <map>
enum class Protocol { I2C, RS485, Analog, Mock };
enum class SensorProtocol { 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"}};
// sensorProtocol to string
const static std::map<SensorProtocol, const char *> protocolToString = {
{SensorProtocol::I2C, "I2C"}, {SensorProtocol::RS485, "RS485"}, {SensorProtocol::Analog, "ANALOG"}, {SensorProtocol::Mock, "MOCK"}};
#endif // CLIENT_PROTOCOL_HPP
......@@ -17,7 +17,7 @@ class MockSensor : public ForteSensor<float> {
[[nodiscard]] SensorInformation getSensorInformation() const override;
private:
const SensorInformation sensorInformation{"MOCK", Protocol::Mock};
const SensorInformation sensorInformation{"MOCK", SensorProtocol::Mock};
int channel;
};
......
......@@ -24,7 +24,7 @@ class Forte_RS485 : public ForteSensor <out_data_rs485> {
[[nodiscard]] SensorInformation getSensorInformation() const override;
private:
const SensorInformation sensorInformation{"RS485", Protocol::RS485};
const SensorInformation sensorInformation{"RS485", SensorProtocol::RS485};
enum class MeasurementType {
SOLAR_RADIATION,
......
......@@ -5,7 +5,7 @@ void ForteSCD30 ::setup()
Wire.begin(I2C_SDA, I2C_SCL);
if (!airSensor.begin()) {
// Sensor init went wrong
ESP_LOGW(sensorInformation.getSensorName().c_str(), "Initialization failed.");
ESP_LOGW(sensorInformation.getHardwareName().c_str(), "Initialization failed.");
return;
}
}
......
......@@ -25,7 +25,7 @@ class ForteSCD30 : public ForteSensor<out_data_scd30> {
private:
SCD30 airSensor;
out_data_scd30 data;
const SensorInformation sensorInformation{"SCD30", Protocol::I2C};
const SensorInformation sensorInformation{"SCD30", SensorProtocol::I2C};
};
#endif
\ No newline at end of file
......@@ -37,7 +37,7 @@ class Sht85 : public ForteSensor<out_data_sht85> {
private:
SHTSensor sht; // I2C address: 0x44
out_data_sht85 data;
const SensorInformation sensorInformation{"SHT85", Protocol::I2C};
const SensorInformation sensorInformation{"SHT85", SensorProtocol::I2C};
void readSHT();
enum class MeasurementType { TEMPERATURE, HUMIDITY };
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment