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

move to c++17, change message building, refactored SensorInformation and MeasurementData

- messages are now built using the constructor
- messages now only contain a single measurement value, even if a sensor might include more than one measurement
- buildMessage has been changed to buildMessages and now returns a list of messages depending on how many measurements a sensor contains
- ClientDataPackage now builds from SensorInformation and MeasurementData
- minor naming refactors
parent 14619889
No related branches found
No related tags found
6 merge requests!39Merge Develop into Main,!19development into master,!17Inital Host, initial Client,!10merge serial comm and sd write into espnow,!8merge sensor_readout into develop,!7move to c++17, change message building, refactored SensorInformation and MeasurementData
Showing
with 243 additions and 165 deletions
......@@ -3,15 +3,15 @@
#include "Message.hpp"
#include "Protocol.hpp"
#include "SensorInformation.hpp"
template <class T>
class Forte_Sensor {
class ForteSensor {
public:
virtual T readData() = 0;
virtual void setup() = 0;
virtual Message buildMessage() = 0;
virtual Protocol getProtocol() = 0;
private:
virtual std::list<Message> buildMessages() = 0;
[[nodiscard]] virtual SensorInformation getSensorInformation() const = 0;
virtual ~ForteSensor() = default;
};
#endif
\ No newline at end of file
//
// 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
......@@ -4,5 +4,5 @@
#include <iostream>
struct NoDataAvailableException : public std::exception {
const char *what() const throw() { return "Sensor could not read data"; }
const char *what() const noexcept override { return "Sensor could not read data"; }
};
\ No newline at end of file
......@@ -2,8 +2,8 @@
#define _FORTE_PINOUT
// Pins for I2C
#define I2C_SDA 18
#define I2C_SCL 19
constexpr int I2C_SDA = 18;
constexpr int I2C_SCL = 19;
// TODO: IF THE BOARD CHANGES (I.E. ESPCAM MODULE), THESE HAVE TO BE CHANGED (EITHER COMPILE TIME FLAG OR IFDEF OR SMTH)
......
......@@ -6,9 +6,10 @@
#define CLIENT_PROTOCOL_HPP
#include <map>
enum Protocol { I2C, RS485, Analog };
enum class Protocol { I2C, RS485, Analog };
// protocol to string
static std::map<Protocol, const char *> protocolToString = {{I2C, "I2C"}, {RS485, "RS485"}, {Analog, "Analog"}};
const static std::map<Protocol, const char *> protocolToString = {
{Protocol::I2C, "I2C"}, {Protocol::RS485, "RS485"}, {Protocol::Analog, "ANALOG"}};
#endif // CLIENT_PROTOCOL_HPP
//
// 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
#include "dr26.hpp"
static const char* TAG = "DR26";
Adafruit_ADS1115 ads;
void Forte_DR26 ::setup()
void ForteDR26 ::setup()
{
Wire.begin(I2C_SDA, I2C_SCL);
// ads.setGain(0);
if(ads.begin()){
ESP_LOGI(TAG, "ADS initialized.");
}
else{
ESP_LOGW(TAG, "ADS initialization failed.");
if (ads.begin()) {
ESP_LOGI(sensorInformation.getSensorName().c_str(), "ADS initialized.");
} else {
ESP_LOGW(sensorInformation.getSensorName().c_str(), "ADS initialization failed.");
}
delay(100);
}
float Forte_DR26 ::readData()
float ForteDR26 ::readData()
{
float volts = 0;
for (int i = 0; i < 10; i++) {
......@@ -44,16 +39,16 @@ float Forte_DR26 ::readData()
// GAIN_FOUR // 4x gain +/- 1.024V 1 bit = 0.03125mV
// GAIN_EIGHT // 8x gain +/- 0.512V 1 bit = 0.015625mV
// GAIN_SIXTEEN // 16x gain +/- 0.256V 1 bit = 0.0078125mV
void Forte_DR26 ::changeGain(adsGain_t gain)
void ForteDR26 ::changeGain(adsGain_t gain)
{
ads.setGain(gain);
}
Message Forte_DR26::buildMessage()
std::list<Message> ForteDR26::buildMessages()
{
throw "Not implemented";
}
Protocol Forte_DR26::getProtocol()
SensorInformation ForteDR26::getSensorInformation() const
{
return Analog;
return sensorInformation;
}
#ifndef _DR26
#define _DR26
#include "Adafruit_ADS1X15.h"
#include "ForteSensor.hpp"
#include "Message.hpp"
#include "Pinout.hpp"
#include "esp_log.h"
#include "pinout.hpp"
#include <Adafruit_ADS1X15.h>
#include <Wire.h>
class Forte_DR26 : public Forte_Sensor<float> {
class ForteDR26 : public ForteSensor<float> {
public:
void setup() override;
float readData() override;
void changeGain(adsGain_t gain);
Message buildMessage() override;
Protocol getProtocol() override;
std::list<Message> buildMessages() override;
[[nodiscard]] SensorInformation getSensorInformation() const override;
private:
Adafruit_ADS1115 ads;
const SensorInformation sensorInformation{"DR26", Protocol::Analog};
};
#endif
\ No newline at end of file
#include <drs26.hpp>
static const std::string TAG = "DRS26";
/*
It happens for some reason that the sensor cant get reached every 2 time
Because the sensor use sdi12 protocoll we have to wait aproxemettly 1 secound between the commands
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 Forte_DRS26 ::setup()
void ForteDRS26 ::setup()
{
drs26.begin(4);
}
out_data_drs26 Forte_DRS26 ::readData()
out_data_drs26 ForteDRS26 ::readData()
{
String sdiResponse = "";
String measurement_command =
......@@ -42,14 +41,21 @@ out_data_drs26 Forte_DRS26 ::readData()
return data;
}
Message Forte_DRS26 ::buildMessage()
std::list<Message> ForteDRS26 ::buildMessages()
{
auto message = Message();
message.addData(12.12, measurementTypeToString[MeasurementType::TEMPERATURE], TAG, 4);
ESP_LOGE(TAG.c_str(), "test");
return message;
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;
}
Protocol Forte_DRS26::getProtocol()
SensorInformation ForteDRS26::getSensorInformation() const
{
return I2C;
return sensorInformation;
}
......@@ -3,9 +3,9 @@
#include "ForteSensor.hpp"
#include "Message.hpp"
#include "Pinout.hpp"
#include "Wire.h"
#include "esp_log.h"
#include "pinout.hpp"
#include <SDI12.h>
#include <map>
......@@ -15,16 +15,17 @@ struct out_data_drs26 {
float temperature;
};
class Forte_DRS26 : public Forte_Sensor<out_data_drs26> {
class ForteDRS26 : public ForteSensor<out_data_drs26> {
public:
void setup() override;
out_data_drs26 readData() override;
Message buildMessage() override;
Protocol getProtocol() 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 };
// enum to string
......
#pragma once
#include "ArduinoJson.h"
#include "MeasurementData.hpp"
#include "Protocol.hpp"
#include "SensorInformation.hpp"
#include <list>
#include <optional>
#include <string>
#include <utility>
struct MeasurementData {
double value;
int channel;
std::string measurementType; // TODO: consider using an enum
};
// 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 value;
std::string sensorName;
std::string protocol;
long timestamp; // maybe make this array
MeasurementData measurementData;
SensorInformation sensorInformation;
unsigned long timestamp; // maybe make this array
public:
ClientDataPackage(MeasurementData value, std::string sensorName, long timestamp, Protocol protocol)
: value(std::move(std::move(value))), sensorName(std::move(sensorName)), timestamp(timestamp),
protocol(protocolToString[protocol])
ClientDataPackage(MeasurementData value, SensorInformation sensorInformation, unsigned long timestamp)
: measurementData(std::move(value)), sensorInformation(std::move(sensorInformation)), timestamp(timestamp)
{
}
std::string getDataPackageAsMinifiedJsonString()
[[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"] = sensorName;
document["sensorName"] = sensorInformation.getSensorName();
document["timestamp"] = timestamp;
document["protocol"] = protocol;
document["value"] = value.value;
document["channel"] = value.channel;
document["measurementType"] = value.measurementType;
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);
......
#include "Message.hpp"
static const char *TAG = "MESSAGE";
void Message::addData(float value, int identifier)
{
StaticJsonDocument<100> document;
document["value"] = value;
document["identifier"] = identifier;
data["values"].add(document);
ESP_LOGD(TAG, "Added data: %s", getMessageAsMinifiedJsonString().c_str());
}
void Message::addData(float value, const std::string &measurementType, const std::string &sensorName)
{
StaticJsonDocument<100> document;
document["sensorName"] = sensorName;
document["measurementType"] = measurementType;
data["values"].add(document);
#include <utility>
ESP_LOGD(TAG, "Added data: %s", getMessageAsMinifiedJsonString().c_str());
}
/**
* Add data to a message that originates from an analog sensor with multiple channels
* @param value Value of the measurement
* @param measurementType Type of the measurement
* @param sensorName Name of the sensor
* @param channel Connected analog channel
*/
void Message::addData(float value, const std::string &measurementType, const std::string &sensorName, int channel)
{
StaticJsonDocument<100> document;
document["sensorName"] = sensorName;
document["channel"] = channel;
document["measurementType"] = measurementType;
document["value"] = value;
data["values"].add(document);
ESP_LOGD(TAG, "Added data: %s", getMessageAsMinifiedJsonString().c_str());
}
static const char *TAG = "MESSAGE";
esp_err_t Message::send()
esp_err_t Message::send() const
{
ESP_LOGI(TAG, "Sending message");
esp_err_t success;
auto messageData = getMessageAsMinifiedJsonString();
success = esp_now_send(recipient, (uint8_t *)&messageData, sizeof(data));
success = esp_now_send(recipient, (uint8_t *)&messageData, sizeof(clientDataPackage));
if (success != ESP_OK) {
// TODO REWRITE FOR JSON
// if (!ram_cache_is_full()) {
// // ram_cache_push(messageData);
// }
// if (!ram_cache_is_full()) {
// // ram_cache_push(messageData);
// }
}
ESP_LOGD(TAG, "Sent data: %s", messageData.c_str());
std::string timestampString = data["timestamp"];
ESP_LOGD(TAG, "time sent: %s", timestampString.c_str());
ESP_LOGD(TAG, "time sent: %l", clientDataPackage.getTimestamp());
ESP_LOGD(TAG, "send status: %d", success);
return success;
}
StaticJsonDocument<256> Message::getData()
std::string Message::getMessageAsMinifiedJsonString() const
{
return data;
return clientDataPackage.getDataPackageAsMinifiedJsonString();
}
Message ::Message()
Message::Message(ClientDataPackage data) : clientDataPackage(std::move(data))
{
// check for existing host mac address, use broadcast otherwise
get_host_mac(recipient);
data["values"] = JsonArray();
addTimestamp();
}
std::string Message::getMessageAsMinifiedJsonString()
Message::Message(MeasurementData const &data, const SensorInformation &information, unsigned long timestamp)
: clientDataPackage(data, information, timestamp)
{
std::string minimizedJson;
serializeJson(data, minimizedJson);
return minimizedJson;
}
void Message::addTimestamp()
{
// TODO: if we are not time synced (i.e. didn't reach the host for current time, use another value like number of
// reboots of the ESP)
data["timestamp"] = esptime::rtc.getMillis();
// check for existing host mac address, use broadcast otherwise
get_host_mac(recipient);
}
......@@ -13,19 +13,14 @@
// if more things are sent from the host the name might not be accurate anymore
class Message {
public:
void addData(float value, int identifier);
void addData(float value, const std::string &measurementType, const std::string &sensorName);
void addData(float value, const std::string &measurementType, const std::string &sensorName, int channel);
Message();
explicit Message(ClientDataPackage data);
esp_err_t send();
StaticJsonDocument<256> getData();
std::string getMessageAsMinifiedJsonString();
Message(MeasurementData const &data, const SensorInformation &information, unsigned long timestamp);
esp_err_t send() const;
[[nodiscard]] std::string getMessageAsMinifiedJsonString() const;
private:
StaticJsonDocument<256> data;
void addTimestamp();
// ClientDataPackage data;
ClientDataPackage clientDataPackage;
uint8_t recipient[6]{};
};
\ No newline at end of file
#include "ina219.hpp"
static const char* TAG = "INA219";
void Forte_INA219 ::setup()
void ForteINA219 ::setup()
{
Wire.begin(I2C_SDA, I2C_SCL);
if (!ina219.init()) {
// Sensor init went wrong
ESP_LOGW(TAG, "Initialization failed");
ESP_LOGW(sensorInformation.getSensorName().c_str(), "Initialization failed");
return;
}
}
out_data_ina219 Forte_INA219 ::readData()
out_data_ina219 ForteINA219 ::readData()
{
if (!ina219.getOverflow()) {
data.shuntVoltage_mV = ina219.getShuntVoltage_mV();
......@@ -27,11 +25,11 @@ out_data_ina219 Forte_INA219 ::readData()
return data;
}
Message Forte_INA219::buildMessage()
std::list<Message> ForteINA219::buildMessages()
{
throw "Not yet implemented";
}
Protocol Forte_INA219::getProtocol()
SensorInformation ForteINA219::getSensorInformation() const
{
return I2C;
return sensorInformation;
}
......@@ -3,9 +3,9 @@
#include "ForteSensor.hpp"
#include "Message.hpp"
#include "Pinout.hpp"
#include "Wire.h"
#include "esp_log.h"
#include "pinout.hpp"
#include <INA219_WE.h>
struct out_data_ina219 {
......@@ -17,16 +17,17 @@ struct out_data_ina219 {
bool ina219_overflow = false;
};
class Forte_INA219 : public Forte_Sensor<out_data_ina219> {
class ForteINA219 : public ForteSensor<out_data_ina219> {
public:
void setup() override;
out_data_ina219 readData() override;
Message buildMessage() override;
Protocol getProtocol() 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};
};
#endif
\ No newline at end of file
#include "scd30.hpp"
static const char* TAG = "SCD30";
void Forte_SCD30 ::setup()
void ForteSCD30 ::setup()
{
Wire.begin(I2C_SDA, I2C_SCL);
if (!airSensor.begin()) {
// Sensor init went wrong
ESP_LOGW(TAG, "Initialization failed.");
ESP_LOGW(sensorInformation.getSensorName().c_str(), "Initialization failed.");
return;
}
}
out_data_scd30 Forte_SCD30 ::readData()
out_data_scd30 ForteSCD30 ::readData()
{
if (airSensor.dataAvailable()) {
data.C02 = airSensor.getCO2();
......@@ -22,14 +20,13 @@ out_data_scd30 Forte_SCD30 ::readData()
return data;
}
throw NoDataAvailableException();
// return out_data_scd30{-1, -1, -1};
}
Message Forte_SCD30::buildMessage()
std::list<Message> ForteSCD30::buildMessages()
{
throw "Not yet implemented";
}
Protocol Forte_SCD30::getProtocol()
SensorInformation ForteSCD30::getSensorInformation() const
{
return I2C;
return sensorInformation;
}
......@@ -4,8 +4,8 @@
#include "ForteSensor.hpp"
#include "Message.hpp"
#include "NoDataAvailableException.hpp"
#include "Pinout.hpp"
#include "esp_log.h"
#include "pinout.hpp"
#include <SparkFun_SCD30_Arduino_Library.h>
#include <Wire.h>
......@@ -15,16 +15,17 @@ struct out_data_scd30 {
float Humidity;
};
class Forte_SCD30 : public Forte_Sensor<out_data_scd30> {
class ForteSCD30 : public ForteSensor<out_data_scd30> {
public:
void setup() override;
out_data_scd30 readData() override;
Message buildMessage() override;
Protocol getProtocol() override;
std::list<Message> buildMessages() override;
[[nodiscard]] SensorInformation getSensorInformation() const override;
private:
SCD30 airSensor;
out_data_scd30 data;
const SensorInformation sensorInformation{"SCD30", Protocol::I2C};
};
#endif
\ No newline at end of file
......@@ -13,10 +13,14 @@ platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
; 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
lib_deps =
-std=gnu++17
build_unflags = -std=gnu++11
lib_deps =
sparkfun/SparkFun SCD30 Arduino Library@^1.0.18
Wire
adafruit/Adafruit ADS1X15@^2.4.0
......
#include "../lib/dr26_analogue/dr26.hpp"
#include "NoDataAvailableException.hpp"
#include "esp_log.h"
#include <Arduino.h>
#include <dr26.hpp>
#include <drs26.hpp>
#include <ina219.hpp>
#include <scd30.hpp>
// #include "esp32-hal-log.h"
static const char *TAG = "MAIN";
static const std::string TAG = "MAIN";
Forte_DRS26 drs26;
ForteDRS26 drs26;
void setup()
{
......@@ -25,13 +25,17 @@ void loop()
try {
espnow_setup();
// data = drs26.readData();
auto message = drs26.buildMessage();
message.send();
auto messages = drs26.buildMessages();
for (const Message &message : messages) {
message.send();
}
} catch (const NoDataAvailableException &e) {
std::cerr << e.what() << '\n';
}
ESP_LOGE(TAG, "Sensor Circumference: ");
ESP_LOGE(TAG.c_str(), "Sensor Circumference: ");
// log_e("Temperature: ");
// log_e("Id: ");
......
......@@ -3,17 +3,44 @@
//
#include "TestClientDataPackage.hpp"
#include "MeasurementData.hpp"
#include "Protocol.hpp"
#include <vector>
void test_export_to_json()
{
ClientDataPackage dataPackage = ClientDataPackage(MeasurementData{1.1, 0, "TEMPERATURE"}, "DRS26", 0, Analog);
auto dataPackage =
ClientDataPackage(MeasurementData{1.1, 0, {}, "TEMPERATURE"}, SensorInformation{"DRS26", Protocol::Analog}, 0);
std::string json = dataPackage.getDataPackageAsMinifiedJsonString();
// expected
std::string expected =
R"({"sensorName":"DRS26","timestamp":0,"protocol":"Analog","value":1.1,"channel":0,"measurementType":"TEMPERATURE"})";
R"({"sensorName":"DRS26","timestamp":0,"protocol":"ANALOG","value":1.1,"channel":0,"measurementType":"TEMPERATURE"})";
TEST_ASSERT_EQUAL_STRING(expected.c_str(), json.c_str());
}
void test_export_to_json_no_analog()
{
auto dataPackage =
ClientDataPackage(MeasurementData{1.1, {}, {}, "TEMPERATURE"}, SensorInformation{"DRS26_DIGITAL", Protocol::I2C}, 0);
std::string json = dataPackage.getDataPackageAsMinifiedJsonString();
// expected
std::string expected =
R"({"sensorName":"DRS26_DIGITAL","timestamp":0,"protocol":"I2C","value":1.1,"measurementType":"TEMPERATURE"})";
TEST_ASSERT_EQUAL_STRING(expected.c_str(), json.c_str());
}
void test_export_to_json_no_channel_no_address()
{
auto dataPackage =
ClientDataPackage(MeasurementData{1.1,"TEMPERATURE"}, SensorInformation{"DRS26_DIGITAL", Protocol::I2C}, 0);
std::string json = dataPackage.getDataPackageAsMinifiedJsonString();
// expected
std::string expected =
R"({"sensorName":"DRS26_DIGITAL","timestamp":0,"protocol":"I2C","value":1.1,"measurementType":"TEMPERATURE"})";
TEST_ASSERT_EQUAL_STRING(expected.c_str(), json.c_str());
}
\ No newline at end of file
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