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

WIP: replace pack solution of Message transfer to JSON solution using ArduinoJson.

parent cf45fde7
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 266 additions and 111 deletions
......@@ -2,12 +2,14 @@
#define _FORTE_SENSOR
#include "Message.hpp"
#include "Protocol.hpp"
template <class T>
class Forte_Sensor {
public:
virtual T read_data() = 0;
virtual T readData() = 0;
virtual void setup() = 0;
virtual Message build_message() = 0;
virtual Message buildMessage() = 0;
virtual Protocol getProtocol() = 0;
private:
};
......
//
// Created by zoe on 10/5/22.
//
#ifndef CLIENT_PROTOCOL_HPP
#define CLIENT_PROTOCOL_HPP
#include <map>
enum Protocol { I2C, RS485, Analog };
// protocol to string
static std::map<Protocol, const char *> protocolToString = {{I2C, "I2C"}, {RS485, "RS485"}, {Analog, "Analog"}};
#endif // CLIENT_PROTOCOL_HPP
#include "ram_caching.hpp"
static const char* TAG = "CACHING";
const int NUM_SENSORS = 10;
RTC_DATA_ATTR int cachedAmount = -1;
RTC_DATA_ATTR ClientDataPackage backup[NUM_SENSORS];
ClientDataPackage ram_cache_pop()
{
return backup[cachedAmount--];
}
void ram_cache_push(ClientDataPackage data)
{
backup[++cachedAmount] = data;
ESP_LOGI(TAG, "ClientDataPackage saved");
}
bool ram_cache_is_empty()
{
return cachedAmount == -1;
}
bool ram_cache_is_full()
{
return cachedAmount == 9;
}
\ No newline at end of file
//RTC_DATA_ATTR int cachedAmount = -1;
//RTC_DATA_ATTR ClientDataPackage backup[NUM_SENSORS];
//
//ClientDataPackage ram_cache_pop()
//{
// return backup[cachedAmount--];
//}
//
//void ram_cache_push(ClientDataPackage data)
//{
// backup[++cachedAmount] = data;
// ESP_LOGI(TAG, "ClientDataPackage saved");
//}
//
//bool ram_cache_is_empty()
//{
// return cachedAmount == -1;
//}
//
//bool ram_cache_is_full()
//{
// return cachedAmount == 9;
//}
\ No newline at end of file
#ifndef _RAM_CACHE
#define _RAM_CACHE
#include "ClientDataPackage.hpp"
//#include "ClientDataPackage.hpp"
#include "esp_log.h"
#include <ESP32Time.h>
bool ram_cache_is_empty();
bool ram_cache_is_full();
void ram_cache_push(ClientDataPackage data);
ClientDataPackage ram_cache_pop();
//void ram_cache_push(ClientDataPackage data);
//ClientDataPackage ram_cache_pop();
#endif
\ No newline at end of file
......@@ -17,7 +17,7 @@ void Forte_DR26 ::setup()
delay(100);
}
float Forte_DR26 ::read_data()
float Forte_DR26 ::readData()
{
float volts = 0;
for (int i = 0; i < 10; i++) {
......@@ -44,12 +44,16 @@ float Forte_DR26 ::read_data()
// 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 ::change_Gain(adsGain_t gain)
void Forte_DR26 ::changeGain(adsGain_t gain)
{
ads.setGain(gain);
}
Message Forte_DR26::build_message()
Message Forte_DR26::buildMessage()
{
throw "Not implemented";
}
\ No newline at end of file
}
Protocol Forte_DR26::getProtocol()
{
return Analog;
}
......@@ -11,9 +11,10 @@
class Forte_DR26 : public Forte_Sensor<float> {
public:
void setup() override;
float read_data() override;
void change_Gain(adsGain_t gain);
Message build_message() override;
float readData() override;
void changeGain(adsGain_t gain);
Message buildMessage() override;
Protocol getProtocol() override;
private:
};
......
#include <drs26.hpp>
static const char* TAG = "DRS26";
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
......@@ -12,7 +12,7 @@ void Forte_DRS26 ::setup()
drs26.begin(4);
}
out_data_drs26 Forte_DRS26 ::read_data()
out_data_drs26 Forte_DRS26 ::readData()
{
String sdiResponse = "";
String measurement_command =
......@@ -36,16 +36,20 @@ out_data_drs26 Forte_DRS26 ::read_data()
if (sdiResponse.length() > 1) {
data.id = sdiResponse.substring(0, 8).toInt();
data.circumference = sdiResponse.substring(9, 15).toFloat();
data.temperatur = sdiResponse.substring(16, 22).toFloat();
data.circumferenceIncrement = sdiResponse.substring(9, 15).toFloat();
data.temperature = sdiResponse.substring(16, 22).toFloat();
}
return data;
}
Message Forte_DRS26 ::build_message()
Message Forte_DRS26 ::buildMessage()
{
auto message = Message();
message.add_data(12.12, 1);
ESP_LOGE(TAG, "test");
return message;
}
\ No newline at end of file
auto message = Message();
message.addData(12.12, measurementTypeToString[MeasurementType::TEMPERATURE], TAG, 4);
ESP_LOGE(TAG.c_str(), "test");
return message;
}
Protocol Forte_DRS26::getProtocol()
{
return I2C;
}
......@@ -7,22 +7,30 @@
#include "esp_log.h"
#include "pinout.hpp"
#include <SDI12.h>
#include <map>
struct out_data_drs26 {
int id;
float circumference;
float temperatur;
float circumferenceIncrement;
float temperature;
};
class Forte_DRS26 : public Forte_Sensor<out_data_drs26> {
public:
void setup() override;
out_data_drs26 read_data() override;
Message build_message() override;
out_data_drs26 readData() override;
Message buildMessage() override;
Protocol getProtocol() override;
private:
SDI12 drs26;
out_data_drs26 data;
enum class MeasurementType { TEMPERATURE, CIRCUMFERENCE_INCREMENT };
// enum to string
std::map<MeasurementType, const char *> measurementTypeToString = {
{MeasurementType::TEMPERATURE, "TEMPERATURE"},
{MeasurementType::CIRCUMFERENCE_INCREMENT, "CIRCUMFERENCE_INCREMENT"}};
};
#endif
\ No newline at end of file
# basic usage
To send data using espnow, create a new Message object,
then use the add_data(value, identifier) method for every value
then use the addData(value, identifier) method for every value
to fill the message.
when every value is added, use the send() method to send the data
to the host (fipy). If the esp client has never recieved a config
......
#pragma once
#define NUM_SENSORS 10
// packing the struct without padding, makes reading it on the fipy easier
#pragma pack(1)
#include "ArduinoJson.h"
#include "Protocol.hpp"
#include <list>
#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
struct ClientDataPackage {
int identifiers[NUM_SENSORS];
float values[NUM_SENSORS];
int amountData;
class ClientDataPackage {
private:
MeasurementData value;
std::string sensorName;
std::string protocol;
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])
{
}
std::string getDataPackageAsMinifiedJsonString()
{
StaticJsonDocument<250> document; // 250 byte is the max send size of espnow
document["sensorName"] = sensorName;
document["timestamp"] = timestamp;
document["protocol"] = protocol;
document["value"] = value.value;
document["channel"] = value.channel;
document["measurementType"] = value.measurementType;
std::string jsonString;
serializeJson(document, jsonString);
return jsonString;
}
};
......@@ -2,51 +2,89 @@
static const char *TAG = "MESSAGE";
void Message::add_data(float value, int identifier)
void Message::addData(float value, int identifier)
{
if (data.amountData < NUM_SENSORS) {
data.values[data.amountData] = value;
data.identifiers[data.amountData] = identifier;
data.amountData++;
}
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);
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());
}
esp_err_t Message::send()
{
ESP_LOGI(TAG, "Sending message");
esp_err_t success;
success = esp_now_send(recipient, (uint8_t *)&data, sizeof(data));
// if(success != ESP_OK){
// if(!ram_cache_is_full()){
// ram_cache_push(*data);
// }
// }
for (int i = 0; i < data.amountData; i++) {
ESP_LOGD(TAG, "Sent data: %i", data.values[i]);
auto messageData = getMessageAsMinifiedJsonString();
success = esp_now_send(recipient, (uint8_t *)&messageData, sizeof(data));
if (success != ESP_OK) {
// TODO REWRITE FOR JSON
// if (!ram_cache_is_full()) {
// // ram_cache_push(messageData);
// }
}
ESP_LOGD(TAG, "Sent data: %s", messageData.c_str());
ESP_LOGD(TAG, "time sent: %l", data.timestamp);
std::string timestampString = data["timestamp"];
ESP_LOGD(TAG, "time sent: %s", timestampString.c_str());
ESP_LOGD(TAG, "send status: %d", success);
return success;
}
StaticJsonDocument<256> Message::getData()
{
return data;
}
Message ::Message()
{
// check for existing host mac address, use broadcast otherwise
get_host_mac(recipient);
data.amountData = 0;
data.timestamp = esptime::rtc.getMillis(); // I am assuming we are not sending data from Unix Epoch
data["values"] = JsonArray();
addTimestamp();
}
Message ::Message(ClientDataPackage old_data)
std::string Message::getMessageAsMinifiedJsonString()
{
data = old_data;
// memcpy(&data, &old_data, sizeof(data));
get_host_mac(recipient);
std::string minimizedJson;
serializeJson(data, minimizedJson);
return minimizedJson;
}
ClientDataPackage Message::getData()
void Message::addTimestamp()
{
return data;
// 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();
}
......@@ -5,6 +5,7 @@
#include "Time.hpp"
#include "esp_log.h"
#include <Arduino.h>
#include <ArduinoJson.h>
#include <ESP32Time.h>
#include <esp_now.h>
......@@ -12,13 +13,19 @@
// 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 old_data);
void add_data(float value, int identifier);
esp_err_t send();
ClientDataPackage getData();
StaticJsonDocument<256> getData();
std::string getMessageAsMinifiedJsonString();
private:
ClientDataPackage data;
uint8_t recipient[6];
StaticJsonDocument<256> data;
void addTimestamp();
// ClientDataPackage data;
uint8_t recipient[6]{};
};
\ No newline at end of file
......@@ -12,7 +12,7 @@ void Forte_INA219 ::setup()
}
}
out_data_ina219 Forte_INA219 ::read_data()
out_data_ina219 Forte_INA219 ::readData()
{
if (!ina219.getOverflow()) {
data.shuntVoltage_mV = ina219.getShuntVoltage_mV();
......@@ -27,7 +27,11 @@ out_data_ina219 Forte_INA219 ::read_data()
return data;
}
Message Forte_INA219::build_message()
Message Forte_INA219::buildMessage()
{
throw "Not yet implemented";
}
\ No newline at end of file
}
Protocol Forte_INA219::getProtocol()
{
return I2C;
}
......@@ -20,8 +20,9 @@ struct out_data_ina219 {
class Forte_INA219 : public Forte_Sensor<out_data_ina219> {
public:
void setup() override;
out_data_ina219 read_data() override;
Message build_message() override;
out_data_ina219 readData() override;
Message buildMessage() override;
Protocol getProtocol() override;
private:
INA219_WE ina219;
......
......@@ -12,7 +12,7 @@ void Forte_SCD30 ::setup()
}
}
out_data_scd30 Forte_SCD30 ::read_data()
out_data_scd30 Forte_SCD30 ::readData()
{
if (airSensor.dataAvailable()) {
data.C02 = airSensor.getCO2();
......@@ -25,7 +25,11 @@ out_data_scd30 Forte_SCD30 ::read_data()
// return out_data_scd30{-1, -1, -1};
}
Message Forte_SCD30::build_message()
Message Forte_SCD30::buildMessage()
{
throw "Not yet implemented";
}
\ No newline at end of file
}
Protocol Forte_SCD30::getProtocol()
{
return I2C;
}
......@@ -18,8 +18,9 @@ struct out_data_scd30 {
class Forte_SCD30 : public Forte_Sensor<out_data_scd30> {
public:
void setup() override;
out_data_scd30 read_data() override;
Message build_message() override;
out_data_scd30 readData() override;
Message buildMessage() override;
Protocol getProtocol() override;
private:
SCD30 airSensor;
......
......@@ -13,16 +13,17 @@ platform = espressif32
board = esp32-c3-devkitm-1
framework = arduino
monitor_speed = 115200
build_flags =
-I include
-DCORE_DEBUG_LEVEL=5
lib_deps =
sparkfun/SparkFun SCD30 Arduino Library@^1.0.18
Wire
adafruit/Adafruit ADS1X15@^2.4.0
wollewald/INA219_WE@^1.3.1
adafruit/Adafruit BusIO@^1.13.2
Adafruit_I2CDevice
SPI
envirodiy/SDI-12@^2.1.4
fbiego/ESP32Time@^2.0.0
build_flags =
-I include
-DCORE_DEBUG_LEVEL=5
lib_deps =
sparkfun/SparkFun SCD30 Arduino Library@^1.0.18
Wire
adafruit/Adafruit ADS1X15@^2.4.0
wollewald/INA219_WE@^1.3.1
adafruit/Adafruit BusIO@^1.13.2
Adafruit_I2CDevice
SPI
envirodiy/SDI-12@^2.1.4
fbiego/ESP32Time@^2.0.0
bblanchon/ArduinoJson@^6.19.4
......@@ -24,8 +24,8 @@ void loop()
try {
espnow_setup();
// data = drs26.read_data();
auto message = drs26.build_message();
// data = drs26.readData();
auto message = drs26.buildMessage();
message.send();
} catch (const NoDataAvailableException &e) {
std::cerr << e.what() << '\n';
......
//
// Created by zoe on 10/6/22.
//
#include "TestClientDataPackage.hpp"
#include "Protocol.hpp"
#include <vector>
void test_export_to_json()
{
ClientDataPackage dataPackage = ClientDataPackage(MeasurementData{1.1, 0, "TEMPERATURE"}, "DRS26", 0, Analog);
std::string json = dataPackage.getDataPackageAsMinifiedJsonString();
// expected
std::string expected =
R"({"sensorName":"DRS26","timestamp":0,"protocol":"Analog","value":1.1,"channel":0,"measurementType":"TEMPERATURE"})";
TEST_ASSERT_EQUAL_STRING(expected.c_str(), json.c_str());
}
\ No newline at end of file
//
// Created by zoe on 10/6/22.
//
#ifndef CLIENT_TESTCLIENTDATAPACKAGE_HPP
#define CLIENT_TESTCLIENTDATAPACKAGE_HPP
#include <unity.h>
#include <Arduino.h>
#include <ClientDataPackage.hpp>
void test_export_to_json();
#endif // CLIENT_TESTCLIENTDATAPACKAGE_HPP
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