diff --git a/client/client/include/ForteSensor.hpp b/client/client/include/ForteSensor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..add5a01d566ef5977eef3e0286d908c4b49fcb0b
--- /dev/null
+++ b/client/client/include/ForteSensor.hpp
@@ -0,0 +1,17 @@
+#ifndef _FORTE_SENSOR
+#define _FORTE_SENSOR
+
+#include "Message.hpp"
+#include "Protocol.hpp"
+#include "SensorInformation.hpp"
+template <class T>
+class ForteSensor {
+  public:
+	virtual T readData() = 0;
+	virtual void setup() = 0;
+	virtual std::list<Message> buildMessages() = 0;
+	[[nodiscard]] virtual SensorInformation getSensorInformation() const = 0;
+	virtual ~ForteSensor() = default;
+};
+
+#endif
\ No newline at end of file
diff --git a/client/client/include/MeasurementData.hpp b/client/client/include/MeasurementData.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..36bd5db75fbdb714dad723d67b92affd4d4fd189
--- /dev/null
+++ b/client/client/include/MeasurementData.hpp
@@ -0,0 +1,39 @@
+//
+// 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/client/include/NoDataAvailableException.hpp b/client/client/include/NoDataAvailableException.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..83c56973868a76a1dca7f49487bcdb6fd33e86d8
--- /dev/null
+++ b/client/client/include/NoDataAvailableException.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <exception>
+#include <iostream>
+
+struct NoDataAvailableException : public std::exception {
+	const char *what() const noexcept override { return "Sensor could not read data"; }
+};
\ No newline at end of file
diff --git a/client/client/include/Pinout.hpp b/client/client/include/Pinout.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f277fd91b274caa1de127f0380ba75164dd9b707
--- /dev/null
+++ b/client/client/include/Pinout.hpp
@@ -0,0 +1,10 @@
+#ifndef _FORTE_PINOUT
+#define _FORTE_PINOUT
+
+// Pins for I2C
+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)
+
+#endif
diff --git a/client/client/include/Protocol.hpp b/client/client/include/Protocol.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..92270db81e4a54e525f2cd054a15a03e7ebade15
--- /dev/null
+++ b/client/client/include/Protocol.hpp
@@ -0,0 +1,15 @@
+//
+// Created by zoe on 10/5/22.
+//
+
+#ifndef CLIENT_PROTOCOL_HPP
+#define CLIENT_PROTOCOL_HPP
+
+#include <map>
+enum class Protocol { I2C, RS485, Analog };
+
+// protocol to string
+const static std::map<Protocol, const char *> protocolToString = {
+    {Protocol::I2C, "I2C"}, {Protocol::RS485, "RS485"}, {Protocol::Analog, "ANALOG"}};
+
+#endif // CLIENT_PROTOCOL_HPP
diff --git a/client/client/include/SensorInformation.hpp b/client/client/include/SensorInformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b8af5d357534be81670b503790960a2781bc282
--- /dev/null
+++ b/client/client/include/SensorInformation.hpp
@@ -0,0 +1,25 @@
+//
+// 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/client/include/forte_sensor.hpp b/client/client/include/forte_sensor.hpp
deleted file mode 100644
index 8e030b9403653c40c5eecfca88edab96e805631d..0000000000000000000000000000000000000000
--- a/client/client/include/forte_sensor.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _FORTE_SENSOR
-#define _FORTE_SENSOR
-
-class Forte_Sensor {
-    public:
-        virtual void* read_data() = 0;
-        virtual void setup() = 0;
-
-    private:
-
-};
-
-#endif
\ No newline at end of file
diff --git a/client/client/lib/caching/src/ram_caching.cpp b/client/client/lib/caching/src/ram_caching.cpp
index 9325e7c7909473c998d2a113779d94480fa3589c..b7df125c9f9885d5040b4a2c6cf38d14bdb69594 100644
--- a/client/client/lib/caching/src/ram_caching.cpp
+++ b/client/client/lib/caching/src/ram_caching.cpp
@@ -1,24 +1,28 @@
 #include "ram_caching.hpp"
 
-RTC_DATA_ATTR int cachedAmount = -1;
-RTC_DATA_ATTR ClientDataPackage backup[NUM_SENSORS];
+static const char* TAG = "CACHING";
+const int NUM_SENSORS = 10;
 
-ClientDataPackage ram_cache_pop()
-{
-	return backup[cachedAmount--];
-}
-
-void ram_cache_push(ClientDataPackage data)
-{
-	backup[++cachedAmount] = data;
-}
-
-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
diff --git a/client/client/lib/caching/src/ram_caching.hpp b/client/client/lib/caching/src/ram_caching.hpp
index 466668ba65578e0a9aa3f0af9bf20174889b0eb2..f4152ce799a7f746158ba80cc769ff879557f9ea 100644
--- a/client/client/lib/caching/src/ram_caching.hpp
+++ b/client/client/lib/caching/src/ram_caching.hpp
@@ -1,11 +1,12 @@
 #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
diff --git a/client/client/lib/dr26_analogue/dr26.cpp b/client/client/lib/dr26_analogue/dr26.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8d4555fff9470023788f92abfcd8af61d8aa905
--- /dev/null
+++ b/client/client/lib/dr26_analogue/dr26.cpp
@@ -0,0 +1,54 @@
+#include "dr26.hpp"
+
+void ForteDR26 ::setup()
+{
+	Wire.begin(I2C_SDA, I2C_SCL);
+	// ads.setGain(0);
+	if (ads.begin()) {
+		ESP_LOGI(sensorInformation.getSensorName().c_str(), "ADS initialized.");
+	} else {
+		ESP_LOGW(sensorInformation.getSensorName().c_str(), "ADS initialization failed.");
+	}
+	delay(100);
+}
+
+float ForteDR26 ::readData()
+{
+	float volts = 0;
+	for (int i = 0; i < 10; i++) {
+		int16_t adc = ads.readADC_SingleEnded(0);
+		float volt = ads.computeVolts(adc);
+		volts += volt;
+	}
+	volts /= 10;
+	return volts;
+}
+
+// The following functions change the ADC input range: be careful
+// to never to exceed VDD +0.3V max, or to exceed the upper and
+// lower limits if you adjust the input range.
+// SETTING THE GAIN VALUE INCORRECTLY MAY DESTROY THE ADC!
+// The maximum output of the dendrometer is 2.5V, and so a gain of
+// one (max 4.096V) or two (max 2.048V) is optimal. Changing the gain
+// changes the accuracy of the sensor: higher gain gives a higher
+// precision but a smaller range.
+//
+// GAIN_TWOTHIRDS  // 2/3x gain +/- 6.144V  1 bit = 0.1875mV (default)
+// GAIN_ONE        // 1x gain   +/- 4.096V  1 bit = 0.125mV
+// GAIN_TWO        // 2x gain   +/- 2.048V  1 bit = 0.0625mV
+// 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 ForteDR26 ::changeGain(adsGain_t gain)
+{
+	ads.setGain(gain);
+}
+
+std::list<Message> ForteDR26::buildMessages()
+{
+	throw "Not implemented";
+}
+SensorInformation ForteDR26::getSensorInformation() const
+{
+	return sensorInformation;
+}
diff --git a/client/client/lib/dr26_analogue/dr26.hpp b/client/client/lib/dr26_analogue/dr26.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..778f8fe112e1b4e2e85588e517811fc661a858ad
--- /dev/null
+++ b/client/client/lib/dr26_analogue/dr26.hpp
@@ -0,0 +1,24 @@
+#ifndef _DR26
+#define _DR26
+
+#include "Adafruit_ADS1X15.h"
+#include "ForteSensor.hpp"
+#include "Message.hpp"
+#include "Pinout.hpp"
+#include "esp_log.h"
+#include <Wire.h>
+
+class ForteDR26 : public ForteSensor<float> {
+  public:
+	void setup() override;
+	float readData() override;
+	void changeGain(adsGain_t gain);
+	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
diff --git a/client/client/lib/dr26_analogue/src/dr26.cpp b/client/client/lib/dr26_analogue/src/dr26.cpp
deleted file mode 100644
index e08a572956a2cabe23ece4ab80a71308105d4b5d..0000000000000000000000000000000000000000
--- a/client/client/lib/dr26_analogue/src/dr26.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-#include "dr26.hpp"
-
-
-Adafruit_ADS1115 ads;
-
-void Forte_DR26 :: setup(adsGain_t gain){
-    Wire.begin(I2C_SDA, I2C_SCL);
-    ads.setGain(gain);
-    ads.begin() ? 
-        Serial.println("ADS initialized") :
-         Serial.println("failed to initialize ADS");
-    delay(100);
-}
-
-float* Forte_DR26 :: read_data(){
-    float volts = 0;
-    for(int i=0; i<10; i++){
-        int16_t adc = ads.readADC_SingleEnded(0);
-        float volt = ads.computeVolts(adc);
-        volts += volt;
-    }
-    volts /= 10;
-    return &volts;
-}
-
-  // The following functions change the ADC input range: be careful
-  // to never to exceed VDD +0.3V max, or to exceed the upper and 
-  // lower limits if you adjust the input range.
-  // SETTING THE GAIN VALUE INCORRECTLY MAY DESTROY THE ADC!
-  // The maximum output of the dendrometer is 2.5V, and so a gain of
-  // one (max 4.096V) or two (max 2.048V) is optimal. Changing the gain
-  // changes the accuracy of the sensor: higher gain gives a higher
-  // precision but a smaller range.
-  //                                                                
-  // GAIN_TWOTHIRDS  // 2/3x gain +/- 6.144V  1 bit = 0.1875mV (default)
-  // GAIN_ONE        // 1x gain   +/- 4.096V  1 bit = 0.125mV
-  // GAIN_TWO        // 2x gain   +/- 2.048V  1 bit = 0.0625mV
-  // 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){
-    ads.setGain(gain);
-}
\ No newline at end of file
diff --git a/client/client/lib/dr26_analogue/src/dr26.hpp b/client/client/lib/dr26_analogue/src/dr26.hpp
deleted file mode 100644
index 0a7c53c59650ddcff26c208d05625defe5cc4b30..0000000000000000000000000000000000000000
--- a/client/client/lib/dr26_analogue/src/dr26.hpp
+++ /dev/null
@@ -1,20 +0,0 @@
-#ifndef _DR26
-#define _DR26
-
-#include "forte_sensor.hpp"
-#include "pinout.hpp"
-#include <Wire.h>
-#include <Adafruit_ADS1X15.h>
-
-class Forte_DR26 : public Forte_Sensor{
-    public:
-        void setup(adsGain_t gain);
-        float* read_data();
-        void change_Gain(adsGain_t gain);
-
-    private:
-
-
-};
-
-#endif
\ No newline at end of file
diff --git a/client/client/lib/drs26_digital/drs26.cpp b/client/client/lib/drs26_digital/drs26.cpp
index 4c3378ae05042fcd0bdc89bc905060be97dcd99e..5c21f3a98e01d665c38a879fb23f37217b39e8f4 100644
--- a/client/client/lib/drs26_digital/drs26.cpp
+++ b/client/client/lib/drs26_digital/drs26.cpp
@@ -1,42 +1,61 @@
 #include <drs26.hpp>
+
 /*
 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 
+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(SDI_DATA);
+	drs26.begin(4);
 }
 
-out_data_drs26 *Forte_DRS26 ::read_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);
-
-    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.circumference = sdiResponse.substring(9, 15).toFloat();
-        data.temperatur = sdiResponse.substring(16, 22).toFloat();
-        return &data;
-    }
-
-    return 0;
-}
\ No newline at end of file
+	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;
+}
+SensorInformation ForteDRS26::getSensorInformation() const
+{
+	return sensorInformation;
+}
diff --git a/client/client/lib/drs26_digital/drs26.hpp b/client/client/lib/drs26_digital/drs26.hpp
index 443838716a6187707f85cc24131aed3311a910e0..0d713ca66260895f63cdc1e2137ec4027294e8bd 100644
--- a/client/client/lib/drs26_digital/drs26.hpp
+++ b/client/client/lib/drs26_digital/drs26.hpp
@@ -1,27 +1,37 @@
 #ifndef _DRS26
 #define _DRS26
 
-#include "forte_sensor.hpp"
-#include "pinout.hpp"
-#include <SDI12.h>
+#include "ForteSensor.hpp"
+#include "Message.hpp"
+#include "Pinout.hpp"
 #include "Wire.h"
+#include "esp_log.h"
+#include <SDI12.h>
+#include <map>
 
-     
 struct out_data_drs26 {
-        int id;
-        float circumference;
-        float temperatur;
-    };
+	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;
 
+  private:
+	SDI12 drs26;
+	out_data_drs26 data;
+	const SensorInformation sensorInformation{"DRS26", Protocol::I2C};
+	enum class MeasurementType { TEMPERATURE, CIRCUMFERENCE_INCREMENT };
 
-class Forte_DRS26 : public Forte_Sensor{
-    public:
-        void setup();
-        out_data_drs26* read_data();
- 
-    private:
-        SDI12 drs26;
-        out_data_drs26 data; 
+	// 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/client/lib/espnow/README b/client/client/lib/espnow/README
index 55f89c0b2141283b3dd2d94c882cdaccbe2064fe..bbf1db0c44bf4b375af8aefcd9f6649864d64677 100644
--- a/client/client/lib/espnow/README
+++ b/client/client/lib/espnow/README
@@ -1,7 +1,7 @@
 # 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
diff --git a/client/client/lib/espnow/src/ClientDataPackage.hpp b/client/client/lib/espnow/src/ClientDataPackage.hpp
index 52f2be6032a190e6346bef71457cb78c983b0038..7cdd66fdc3b9175a4870933654a82c31bcf392d1 100644
--- a/client/client/lib/espnow/src/ClientDataPackage.hpp
+++ b/client/client/lib/espnow/src/ClientDataPackage.hpp
@@ -1,14 +1,54 @@
 #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 "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
-struct ClientDataPackage {
-	int identifiers[NUM_SENSORS];
-	float values[NUM_SENSORS];
-	int amountData;
-	long timestamp; // maybe make this array
+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/client/lib/espnow/src/ESPNow.cpp b/client/client/lib/espnow/src/ESPNow.cpp
index 359b43c50b855b7f90d955995befe915a3846e3d..890392ad22e6b2c7a596c12c5d7030085a76bcab 100644
--- a/client/client/lib/espnow/src/ESPNow.cpp
+++ b/client/client/lib/espnow/src/ESPNow.cpp
@@ -1,5 +1,7 @@
 #include "ESPNow.hpp"
 
+static const char *TAG = "ESPNOW";
+
 uint8_t BROADCAST_MAC[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 esp_now_peer_info_t hostInfo;
 Preferences preferences;
@@ -7,11 +9,11 @@ Preferences preferences;
 void get_host_mac(uint8_t *destination)
 {
 	preferences.begin("config", true);
-	if (!preferences.isKey("host")) {
+	if (preferences.isKey("host")) {
 		preferences.getBytes("host", destination, sizeof(uint8_t) * 6);
 	} else {
 		memcpy(destination, BROADCAST_MAC, sizeof(BROADCAST_MAC));
-		Serial.println("backup mac used");
+		ESP_LOGW(TAG, "Backup MAC address used");
 	}
 	preferences.end();
 }
@@ -22,7 +24,7 @@ void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status)
 
 void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len)
 {
-	Serial.println("message recieved");
+	ESP_LOGI(TAG, "Message recieved");
 	config new_config;
 	memcpy(&new_config, incomingData, sizeof(new_config)); // TODO: check for valid mac
 
@@ -30,16 +32,12 @@ void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len)
 	preferences.begin("config", false);
 	if (!preferences.isKey("host")) {
 		preferences.putBytes("host", new_config.host, sizeof(new_config.host));
-		Serial.println("host mac saved to flash");
-	} else{
-		Serial.println("host mac already exists");
-	}// host change shouldn't be an issue
+		ESP_LOGI(TAG, "host MAC address saved to flash");
+	} // host change shouldn't be an issue
 	preferences.end();
 	// sync time
-	Time::getInstance().setTime(
+	esptime::rtc.setTime(
 	    new_config.time_millis); // see https://www.esp32.com/viewtopic.php?t=9965, maybe this needs an offset
-	Serial.println("Saved Time: " + (String) new_config.time_millis);
-	Serial.flush();
 }
 
 esp_err_t espnow_setup()
@@ -55,7 +53,10 @@ esp_err_t espnow_setup()
 	get_host_mac(hostInfo.peer_addr); // check if there is a host saved in flash mem, broadcast otherwise
 
 	hostInfo.channel = 0;
-	hostInfo.encrypt = 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);
 
 	esp_now_register_recv_cb(on_data_recv);
@@ -63,29 +64,3 @@ esp_err_t espnow_setup()
 
 	return ESP_OK;
 }
-
-esp_err_t espnow_send_message(const Message& message){
-	Serial.println("sending Message");
-	esp_err_t success;
-	ClientDataPackage dataP = message.get_client_data_package();
-	uint8_t recipient;
-	get_host_mac(&recipient);
-
-	success = esp_now_send(&recipient, (uint8_t *) &dataP, sizeof(ClientDataPackage));
-	// if(success != ESP_OK){
-	//     if(!ram_cache_is_full()){
-	//         ram_cache_push(*data);
-	//     }
-	// }
-
-	for (int i = 0; i < dataP.amountData; i++) {
-		Serial.println(dataP.values[i]);
-	}
-	
-	Serial.println((String) "time sent: " + dataP.timestamp);
-	Serial.println((String) "Send status: " + success);
-	Serial.println();
-	Serial.println("done");
-	Serial.flush();
-	return success;
-}
diff --git a/client/client/lib/espnow/src/ESPNow.hpp b/client/client/lib/espnow/src/ESPNow.hpp
index e4ac43b351c4431e8a1bd1b15740ae5fe085e2fe..797d1f3aef01bcb7726f0304f85f9c08c34aa333 100644
--- a/client/client/lib/espnow/src/ESPNow.hpp
+++ b/client/client/lib/espnow/src/ESPNow.hpp
@@ -3,6 +3,7 @@
 
 #include "Message.hpp"
 #include "Time.hpp"
+#include "esp_log.h"
 #include "ram_caching.hpp"
 #include <ClientDataPackage.hpp>
 #include <ESP32Time.h>
diff --git a/client/client/lib/espnow/src/Message.cpp b/client/client/lib/espnow/src/Message.cpp
index 6975ab6cd228ab218df9759abddc498a082c288c..7de1fc9d38ed4ea322994c52bb6a608ff74d332a 100644
--- a/client/client/lib/espnow/src/Message.cpp
+++ b/client/client/lib/espnow/src/Message.cpp
@@ -1,29 +1,45 @@
 #include "Message.hpp"
 
-void Message::add_data(float value, int identifier)
+#include <utility>
+
+static const char *TAG = "MESSAGE";
+
+esp_err_t Message::send() const
 {
-	if (data.amountData < NUM_SENSORS) {
-		data.values[data.amountData] = value;
-		data.identifiers[data.amountData] = identifier;
-		data.amountData++;
+	ESP_LOGI(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");
+		// 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", clientDataPackage.getTimestamp());
+	ESP_LOGD(TAG, "send status: %d", success);
+
+	return success;
 }
 
-ClientDataPackage Message ::get_client_data_package() const
+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
-
-	data.amountData = 0;
-	data.timestamp = Time::getInstance().getMillis(); // I am assuming we are not sending data from Unix Epoch
+	get_host_mac(recipient);
 }
-
-Message ::Message(ClientDataPackage old_data)
+Message::Message(MeasurementData const &data, const SensorInformation &information, unsigned long timestamp)
+    : clientDataPackage(data, information, timestamp)
 {
-	data = old_data;
-	// memcpy(&data, &old_data, sizeof(data));
-}
\ No newline at end of file
+	// check for existing host mac address, use broadcast otherwise
+	get_host_mac(recipient);
+}
diff --git a/client/client/lib/espnow/src/Message.hpp b/client/client/lib/espnow/src/Message.hpp
index 397ca6f707105be90fb76add8200b8edef93c8ed..3711df244474bb1f7e38bb50bdbac029e73681e0 100644
--- a/client/client/lib/espnow/src/Message.hpp
+++ b/client/client/lib/espnow/src/Message.hpp
@@ -1,8 +1,11 @@
 #pragma once
 
 #include "ClientDataPackage.hpp"
+#include "ESPNow.hpp"
 #include "Time.hpp"
+#include "esp_log.h"
 #include <Arduino.h>
+#include <ArduinoJson.h>
 #include <ESP32Time.h>
 #include <esp_now.h>
 
@@ -10,11 +13,14 @@
 // if more things are sent from the host the name might not be accurate anymore
 class Message {
   public:
-	Message();
-	Message(ClientDataPackage old_data);
-	void add_data(float value, int identifier);
-	ClientDataPackage get_client_data_package() const;
+
+	explicit Message(ClientDataPackage data);
+
+	Message(MeasurementData const &data, const SensorInformation &information, unsigned long timestamp);
+	esp_err_t send() const;
+	[[nodiscard]] std::string getMessageAsMinifiedJsonString() const;
 
   private:
-	ClientDataPackage data;
+	ClientDataPackage clientDataPackage;
+	uint8_t recipient[6]{};
 };
\ No newline at end of file
diff --git a/client/client/lib/ina219/ina219.cpp b/client/client/lib/ina219/ina219.cpp
index 7f4beb3527965f301f0f9c50679189958275220c..def6a9d73acefe00e1a160a1c3fc2ba64ab2099d 100644
--- a/client/client/lib/ina219/ina219.cpp
+++ b/client/client/lib/ina219/ina219.cpp
@@ -1,26 +1,35 @@
 #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;
+	}
+}
+
+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();
 
-void Forte_INA219 :: setup(){
-    Wire.begin(I2C_SDA, I2C_SCL);
-     if(!ina219.init()){
-        // Sensor init went wrong
-        return;
-    } 
+		return data;
+	} else
+		return data;
 }
 
-out_data_ina219* Forte_INA219  :: read_data(){
-    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 0;
-}
\ No newline at end of file
+std::list<Message> ForteINA219::buildMessages()
+{
+	throw "Not yet implemented";
+}
+SensorInformation ForteINA219::getSensorInformation() const
+{
+	return sensorInformation;
+}
diff --git a/client/client/lib/ina219/ina219.hpp b/client/client/lib/ina219/ina219.hpp
index 67217e193308b3145460c357f8480116f0b17c46..4a34d9591a325f9232f90ef61b4a74077fe1cf51 100644
--- a/client/client/lib/ina219/ina219.hpp
+++ b/client/client/lib/ina219/ina219.hpp
@@ -1,31 +1,33 @@
 #ifndef _INA219
 #define _INA219
 
-#include "forte_sensor.hpp"
-#include "pinout.hpp"
-#include <INA219_WE.h>
+#include "ForteSensor.hpp"
+#include "Message.hpp"
+#include "Pinout.hpp"
 #include "Wire.h"
+#include "esp_log.h"
+#include <INA219_WE.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;  
-    };
+	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;
+};
 
+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 Forte_INA219 : public Forte_Sensor{
-    public:
-        void setup();
-        out_data_ina219* read_data();
- 
-    private:
-        INA219_WE ina219;
-        out_data_ina219 data; 
+  private:
+	INA219_WE ina219;
+	out_data_ina219 data;
+	const SensorInformation sensorInformation{"INA219", Protocol::I2C};
 };
 
 #endif
\ No newline at end of file
diff --git a/client/client/lib/scd30/scd30.cpp b/client/client/lib/scd30/scd30.cpp
index c7c2084754bfc94aef7b1ca25947ede9c4c03fc1..507d815ea7c401a4872aec3f7a4e2567409d3ddb 100644
--- a/client/client/lib/scd30/scd30.cpp
+++ b/client/client/lib/scd30/scd30.cpp
@@ -1,23 +1,32 @@
 #include "scd30.hpp"
 
-
-void Forte_SCD30 :: setup(){
-    Wire.begin(I2C_SDA, I2C_SCL);
-    if(!airSensor.begin()){
-        // Sensor init went wrong
-        return;
-    }
+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;
+	}
 }
 
-out_data_scd30* Forte_SCD30 :: read_data(){
-    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 = airSensor.getCO2();
+		data.Temperature = airSensor.getTemperature();
+		data.Humidity = airSensor.getHumidity();
+
+		return data;
+	}
+	throw NoDataAvailableException();
+}
 
-        return   &data;
-    }
-    else
-        return 0;
-}
\ No newline at end of file
+std::list<Message> ForteSCD30::buildMessages()
+{
+	throw "Not yet implemented";
+}
+SensorInformation ForteSCD30::getSensorInformation() const
+{
+	return sensorInformation;
+}
diff --git a/client/client/lib/scd30/scd30.hpp b/client/client/lib/scd30/scd30.hpp
index 82a10cd23df90187cd025251f0d2a73279b8d8c8..db3e85500924db80ac3c0b5cd3698314564f6fd6 100644
--- a/client/client/lib/scd30/scd30.hpp
+++ b/client/client/lib/scd30/scd30.hpp
@@ -1,27 +1,31 @@
 #ifndef _SCD30
 #define _SCD30
 
-#include "forte_sensor.hpp"
-#include "pinout.hpp"
-#include <Wire.h>
+#include "ForteSensor.hpp"
+#include "Message.hpp"
+#include "NoDataAvailableException.hpp"
+#include "Pinout.hpp"
+#include "esp_log.h"
 #include <SparkFun_SCD30_Arduino_Library.h>
+#include <Wire.h>
 
-struct out_data_scd30
-{
-    float C02;
-    float Temperature;
-    float Humidity;
+struct out_data_scd30 {
+	float C02;
+	float Temperature;
+	float 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;
 
-class Forte_SCD30 : public Forte_Sensor{
-    public:
-        void setup();
-        out_data_scd30* read_data();
-
-    private:
-        SCD30 airSensor;
-        out_data_scd30 data;
+  private:
+	SCD30 airSensor;
+	out_data_scd30 data;
+	const SensorInformation sensorInformation{"SCD30", Protocol::I2C};
 };
 
 #endif
\ No newline at end of file
diff --git a/client/client/platformio.ini b/client/client/platformio.ini
index 08a880b1fed6366c59e978a41906e6b1b9864da3..435ae69baa774f3494c60228192f3fbfaec44f94 100644
--- a/client/client/platformio.ini
+++ b/client/client/platformio.ini
@@ -12,10 +12,18 @@
 platform = espressif32
 board = esp32-c3-devkitm-1
 framework = arduino
-monitor_speed = 9600
+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
-lib_deps = 
+	-DCORE_DEBUG_LEVEL=5
+	-std=gnu++17
+build_unflags = -std=gnu++11
+; serial port
+monitor_port = /dev/ttyUSB1
+upload_port = /dev/ttyUSB1
+lib_deps =
 	sparkfun/SparkFun SCD30 Arduino Library@^1.0.18
 	Wire
 	adafruit/Adafruit ADS1X15@^2.4.0
@@ -24,3 +32,5 @@ lib_deps =
 	Adafruit_I2CDevice
 	SPI
 	envirodiy/SDI-12@^2.1.4
+	fbiego/ESP32Time@^2.0.0
+	bblanchon/ArduinoJson@^6.19.4
diff --git a/client/client/src/main.cpp b/client/client/src/main.cpp
index 1c549ba722451f99037e2fb54643cdb5dfc91636..bf964c0c5c0280b41dc8e0a514f76408276314d0 100644
--- a/client/client/src/main.cpp
+++ b/client/client/src/main.cpp
@@ -1,44 +1,44 @@
-#include "ESPNow.hpp"
-#include "ram_caching.hpp"
+#include "../lib/dr26_analogue/dr26.hpp"
+#include "NoDataAvailableException.hpp"
+#include "esp_log.h"
 #include <Arduino.h>
-#include <ArduinoJson.h>
-#include "Time.hpp"
-#include "f_deep_sleep.hpp"
+#include <drs26.hpp>
+#include <ina219.hpp>
+#include <scd30.hpp>
+// #include "esp32-hal-log.h"
+static const std::string TAG = "MAIN";
 
+ForteDRS26 drs26;
 
 void setup()
 {
-
-	// put your setup code here, to run once:
 	Serial.begin(115200);
-	while (!Serial)
-		;
-	Serial.flush();
+	drs26.setup();
+	espnow_setup();
 
-	esp_err_t result = espnow_setup();
+	//	log_e("Setup complete.");
 }
 
-int counter = 0;
-
 void loop()
 {
-	// put your main code here, to run repeatedly:
-	Message new_data = Message{};
-	new_data.add_data(++counter * 1.1, 0);
-	new_data.add_data(counter * 1.2, 1);
-	new_data.add_data(counter * 1.3, 2);
-	espnow_send_message(new_data);
-
-	// if(!ram_cache_is_empty()){
-	//   ClientDataPackage old_data = ram_cache_pop();
-	//   Message* resend_message = new Message(old_data);
-	//   resend_message->send();
-	//   delete resend_message;u
-	// }
-	Serial.println("Saved Time Loop: " + Time::getInstance().getTime("%c"));
-
-	Serial.println("delaying...");
-	Serial.flush();
-	delay(5000); // 5 second receive window
-	// deep_sleep(900); // go to sleep for 15 mins (900s)
-}
\ No newline at end of file
+
+	out_data_drs26 data{};
+
+	try {
+		//			data = drs26.readData();
+		auto messages = drs26.buildMessages();
+
+		for (const Message &message : messages) {
+			message.send();
+		}
+
+	} catch (const NoDataAvailableException &e) {
+		std::cerr << e.what() << '\n';
+	}
+
+	ESP_LOGE(TAG.c_str(), "Sensor Circumference: ");
+	//	log_e("Temperature: ");
+	//	log_e("Id: ");
+
+	delay(5000);
+}
diff --git a/client/client/test/TestClientDataPackage.cpp b/client/client/test/TestClientDataPackage.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eea536cfbc63f7c6dc9797d8288a5887c384aa0c
--- /dev/null
+++ b/client/client/test/TestClientDataPackage.cpp
@@ -0,0 +1,46 @@
+//
+// Created by zoe on 10/6/22.
+//
+
+#include "TestClientDataPackage.hpp"
+#include "MeasurementData.hpp"
+#include "Protocol.hpp"
+#include <vector>
+
+void test_export_to_json()
+{
+	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"})";
+
+	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
diff --git a/client/client/test/TestClientDataPackage.hpp b/client/client/test/TestClientDataPackage.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa5f20396c423714daff914dfd16b24c8cd6cff5
--- /dev/null
+++ b/client/client/test/TestClientDataPackage.hpp
@@ -0,0 +1,16 @@
+//
+// 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();
+void test_export_to_json_no_analog();
+void test_export_to_json_no_channel_no_address();
+
+#endif // CLIENT_TESTCLIENTDATAPACKAGE_HPP
diff --git a/client/client/test/main.cpp b/client/client/test/main.cpp
index 82d68817015b01f036c346b18fa79601a5e842aa..5ad5f3f7a0c3626689b5093cfbec886b6afa4e96 100644
--- a/client/client/test/main.cpp
+++ b/client/client/test/main.cpp
@@ -1,7 +1,7 @@
 #include "TestESPNow.hpp"
-#include "TestMessage.hpp"
 #include <Arduino.h>
 #include <unity.h>
+#include "TestClientDataPackage.hpp"
 
 void setup()
 {
@@ -9,8 +9,9 @@ void setup()
 
 	UNITY_BEGIN();
 	RUN_TEST(test_on_data_recv_valid_config);
-	RUN_TEST(test_on_data_recv);
-	RUN_TEST(test_new_message_filled);
+	RUN_TEST(test_export_to_json);
+	RUN_TEST(test_export_to_json_no_analog);
+	RUN_TEST(test_export_to_json_no_channel_no_address);
 	UNITY_END();
 }
 
diff --git a/code-snippets/client/ESPcam/.gitignore b/code-snippets/client/ESPcam/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..89cc49cbd652508924b868ea609fa8f6b758ec56
--- /dev/null
+++ b/code-snippets/client/ESPcam/.gitignore
@@ -0,0 +1,5 @@
+.pio
+.vscode/.browse.c_cpp.db*
+.vscode/c_cpp_properties.json
+.vscode/launch.json
+.vscode/ipch
diff --git a/code-snippets/client/ESPcam/.vscode/extensions.json b/code-snippets/client/ESPcam/.vscode/extensions.json
new file mode 100644
index 0000000000000000000000000000000000000000..080e70d08b9811fa743afe5094658dba0ed6b7c2
--- /dev/null
+++ b/code-snippets/client/ESPcam/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        "platformio.platformio-ide"
+    ],
+    "unwantedRecommendations": [
+        "ms-vscode.cpptools-extension-pack"
+    ]
+}
diff --git a/code-snippets/client/ESPcam/include/README b/code-snippets/client/ESPcam/include/README
new file mode 100644
index 0000000000000000000000000000000000000000..194dcd43252dcbeb2044ee38510415041a0e7b47
--- /dev/null
+++ b/code-snippets/client/ESPcam/include/README
@@ -0,0 +1,39 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the usual convention is to give header files names that end with `.h'.
+It is most portable to use only letters, digits, dashes, and underscores in
+header file names, and at most one dot.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/code-snippets/client/ESPcam/platformio.ini b/code-snippets/client/ESPcam/platformio.ini
new file mode 100644
index 0000000000000000000000000000000000000000..51be1376266f8dbbf7633667bf84b283f5403a6f
--- /dev/null
+++ b/code-snippets/client/ESPcam/platformio.ini
@@ -0,0 +1,17 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[env:esp32cam]
+platform = espressif32
+board = esp32cam
+framework = arduino
+monitor_speed = 115200
+lib_deps = sensirion/arduino-sht@^1.2.2
+           adafruit/RTClib @^2.1.1
\ No newline at end of file
diff --git a/code-snippets/client/ESPcam/src/main.cpp b/code-snippets/client/ESPcam/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac20e29084b8801963f6875cd65dda2241634643
--- /dev/null
+++ b/code-snippets/client/ESPcam/src/main.cpp
@@ -0,0 +1,321 @@
+/*
+Snippet content:
+Takes pictures with ESP32-CAM, saves images to MicroSD Card.
+Measures air temperature and relative humidity from SHT85.
+
+We use the ESP-CAM for the SHT85 readout because of the
+limited wire length that can be used for I2C sensors - the
+camera sits at the top of the mast next to the SHT85 sensor.
+
+ESP-CAM code partially copied from
+https://dronebotworkshop.com/esp32-cam-microsd/
+
+NOTE: To flash the ESP-CAM, pin IO1 has to be connected to GND!
+TODO: because of this limitation, it would be nice to implement
+over-the-air updates for the camera at one point.
+*/
+
+// Include Required Libraries
+// I2C: SHT85, RTC
+#include <Arduino.h>
+#include <Wire.h>
+#include "SHTSensor.h"    // sensirion/arduino-sht@^1.2.2
+#include "RTClib.h"       // adafruit/RTClib @^2.1.1
+#include "SPI.h"
+
+// Camera libraries
+#include "esp_camera.h"
+#include "soc/soc.h"
+#include "soc/rtc_cntl_reg.h"
+#include "driver/rtc_io.h"
+
+// MicroSD Libraries
+#include "FS.h"
+#include "SD_MMC.h"
+
+// Pin definitions for CAMERA_MODEL_AI_THINKER
+#define PWDN_GPIO_NUM     32
+#define RESET_GPIO_NUM    -1
+#define XCLK_GPIO_NUM      0
+#define SIOD_GPIO_NUM     26
+#define SIOC_GPIO_NUM     27
+#define Y9_GPIO_NUM       35
+#define Y8_GPIO_NUM       34
+#define Y7_GPIO_NUM       39
+#define Y6_GPIO_NUM       36
+#define Y5_GPIO_NUM       21
+#define Y4_GPIO_NUM       19
+#define Y3_GPIO_NUM       18
+#define Y2_GPIO_NUM        5
+#define VSYNC_GPIO_NUM    25
+#define HREF_GPIO_NUM     23
+#define PCLK_GPIO_NUM     22
+
+// Pin definitions for I2C (SHT85, RTC)
+//  This is different from the pins on the ESP32-C3-DevKit boards!
+#define SDA         12
+#define SCL         13
+
+// LED control
+#define LEDpin 4
+
+// string for saving the time
+char time_string[20];
+
+// number of images to take: the last one is saved
+//   this is done because the first few images have a green tint otherwise
+int img_number = 3;
+
+SHTSensor sht(SHTSensor::SHT85);    // I2C address: 0x44
+RTC_DS3231 rtc;                     // I2C address: 0x68
+camera_config_t config;             // camera configuration parameters
+
+
+
+void configESPCamera() { 
+  // Configure Camera parameters 
+
+  config.ledc_channel = LEDC_CHANNEL_0;
+  config.ledc_timer = LEDC_TIMER_0;
+  config.pin_d0 = Y2_GPIO_NUM;
+  config.pin_d1 = Y3_GPIO_NUM;
+  config.pin_d2 = Y4_GPIO_NUM;
+  config.pin_d3 = Y5_GPIO_NUM;
+  config.pin_d4 = Y6_GPIO_NUM;
+  config.pin_d5 = Y7_GPIO_NUM;
+  config.pin_d6 = Y8_GPIO_NUM;
+  config.pin_d7 = Y9_GPIO_NUM;
+  config.pin_xclk = XCLK_GPIO_NUM;
+  config.pin_pclk = PCLK_GPIO_NUM;
+  config.pin_vsync = VSYNC_GPIO_NUM;
+  config.pin_href = HREF_GPIO_NUM;
+  config.pin_sscb_sda = SIOD_GPIO_NUM;
+  config.pin_sscb_scl = SIOC_GPIO_NUM;
+  config.pin_pwdn = PWDN_GPIO_NUM;
+  config.pin_reset = RESET_GPIO_NUM;
+  config.xclk_freq_hz = 20000000;
+  config.pixel_format = PIXFORMAT_JPEG; // Choices are YUV422, GRAYSCALE, RGB565, JPEG
+
+  // Select lower framesize if the camera doesn't support PSRAM
+  if (psramFound()) {
+    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
+    config.jpeg_quality = 10; //10-63 lower number means higher quality
+    config.fb_count = 2;
+  } else {
+    config.frame_size = FRAMESIZE_SVGA;
+    config.jpeg_quality = 12;
+    config.fb_count = 1;
+  }
+
+  // Initialize the Camera
+  esp_err_t err = esp_camera_init(&config);
+  
+  if (err != ESP_OK) {
+    Serial.printf("Camera init failed with error 0x%x", err);
+    return;
+  }
+
+  // Camera quality adjustments
+  sensor_t * s = esp_camera_sensor_get();
+
+  // TODO: this is copied from an online tutorial, it's possible that we don't need this at all!
+  // BRIGHTNESS (-2 to 2)
+  s->set_brightness(s, 0);
+  // CONTRAST (-2 to 2)
+  s->set_contrast(s, 0);
+  // SATURATION (-2 to 2)
+  s->set_saturation(s, 0);
+  // SPECIAL EFFECTS (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
+  s->set_special_effect(s, 0);
+  // WHITE BALANCE (0 = Disable , 1 = Enable)
+  s->set_whitebal(s, 1);
+  // AWB GAIN (0 = Disable , 1 = Enable)
+  s->set_awb_gain(s, 1);
+  // WB MODES (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
+  s->set_wb_mode(s, 0);
+  // EXPOSURE CONTROLS (0 = Disable , 1 = Enable)
+  s->set_exposure_ctrl(s, 1);
+  // AEC2 (0 = Disable , 1 = Enable)
+  s->set_aec2(s, 0);
+  // AE LEVELS (-2 to 2)
+  s->set_ae_level(s, 0);
+  // AEC VALUES (0 to 1200)
+  s->set_aec_value(s, 300);
+  // GAIN CONTROLS (0 = Disable , 1 = Enable)
+  s->set_gain_ctrl(s, 1);
+  // AGC GAIN (0 to 30)
+  s->set_agc_gain(s, 0);
+  // GAIN CEILING (0 to 6)
+  s->set_gainceiling(s, (gainceiling_t)0);
+  // BPC (0 = Disable , 1 = Enable)
+  s->set_bpc(s, 0);
+  // WPC (0 = Disable , 1 = Enable)
+  s->set_wpc(s, 1);
+  // RAW GMA (0 = Disable , 1 = Enable)
+  s->set_raw_gma(s, 1);
+  // LENC (0 = Disable , 1 = Enable)
+  s->set_lenc(s, 1);
+  // HORIZ MIRROR (0 = Disable , 1 = Enable)
+  s->set_hmirror(s, 0);
+  // VERT FLIP (0 = Disable , 1 = Enable)
+  s->set_vflip(s, 0);
+  // DCW (0 = Disable , 1 = Enable)
+  s->set_dcw(s, 1);
+  // COLOR BAR PATTERN (0 = Disable , 1 = Enable)
+  s->set_colorbar(s, 0);
+}
+
+void initMicroSDCard() {
+  // Start the MicroSD card
+  Serial.println("Mounting MicroSD Card");
+  if (!SD_MMC.begin("/sdcard", true)) {  // the arguments disable the LED when using the SD card - leave them there!
+    Serial.println("MicroSD Card Mount Failed");
+    return;
+  }
+  uint8_t cardType = SD_MMC.cardType();
+  if (cardType == CARD_NONE) {
+    Serial.println("No MicroSD Card found");
+    return;
+  }
+}
+
+void takeNewPhoto(String path) {
+  // Take picture with the camera and save it
+
+  // Take multiple pictures with a short break in between
+  //   necessary for the camera to auto-adjust settings
+  camera_fb_t * fb;
+  for (int i = 1; i <= img_number; i++) {
+    // Setup frame buffer
+    fb = esp_camera_fb_get();
+    // digitalWrite(LEDpin, LOW);  // disable flash LED if needed
+  
+    if (!fb) {
+      Serial.println("Camera capture failed");
+      return;
+    } else {
+      Serial.println("Camera capture successful");
+    }
+    // TODO check if this delay is necessary once the SD-card todo below
+    //   is resolved. It's possible that it can be at least shortened.
+    if (i == img_number) {
+      delay(2000);
+    }
+
+    esp_camera_fb_return(fb);
+    // Without this delay the image is corrupt
+    // TODO this delay can possibly also be optimized.
+    delay(1500);
+
+    /* TODO: 
+    The SD card throws the following error when the capture fails:
+    E (877528) sdmmc_cmd: sdmmc_write_sectors_dma: sdmmc_send_cmd returned 0x109
+    E (877528) diskio_sdmmc: sdmmc_write_blocks failed (265)
+    This happens infrequently, say every 10th image or so.
+    If this happens, one more new image should be taken and saved
+    */
+  }  
+
+  // Save picture to microSD card
+  fs::FS &fs = SD_MMC;
+  File file = fs.open(path.c_str(), FILE_WRITE);
+  if (!file) {
+    Serial.println("Failed to open file in write mode");
+  }
+  else {
+    file.write(fb->buf, fb->len); // payload (image), payload length
+    Serial.printf("Saved file to path: %s\n", path.c_str());
+  }
+  // Close the file
+  file.close();
+
+  // Return the frame buffer back to the driver for reuse
+  esp_camera_fb_return(fb);
+}
+
+
+void 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 readRTC(){
+  // This is used for a picture timestamp (name of the image)
+  rtc.begin();
+  DateTime now = rtc.now();
+  sprintf(time_string, "%04d-%02d-%02dT%02d-%02d-%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());
+  Serial.println(time_string);
+}
+
+void readI2C(){
+  // Start the I2C bus
+  Wire.begin(SDA, SCL);
+  delay(100);     // let serial console settle
+
+  // initiate the SHT85 sensor
+  Serial.println("");
+  if (sht.init()) {
+      Serial.println("SHT initialization successful");
+  } else {
+      Serial.println("SHT initialization failed");
+  }
+
+  // T + RH reading
+  readSHT();
+  // real-time clock reading
+  readRTC();
+  delay(100);
+
+  // end I2C to free the pins to be used by the SD card
+  Wire.end();
+}
+
+
+void setup() {
+  // control of the LED pin
+  pinMode(LEDpin, OUTPUT);
+  // Disable brownout detector
+  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
+
+  // Start Serial
+  Serial.begin(115200); 
+
+  // Initialize the camera
+  Serial.print("Initializing the camera module...");
+  configESPCamera();
+  Serial.println("Camera OK!");
+
+}
+
+void loop() {
+  // initiate I2C, read SHT+RTC, end I2C
+  readI2C();
+
+  // Initialize the MicroSD
+  Serial.print("Initializing the MicroSD card module... ");
+  initMicroSDCard();
+
+  // Path where new image will be saved in MicroSD card
+  String path = "/" + String(time_string) + ".jpg";
+  Serial.printf("Picture file name: %s\n", path.c_str());
+
+  // Take and save a picture
+  takeNewPhoto(path);
+
+  // Unmount SD card to free the pins for I2C use
+  SD_MMC.end();
+
+  // Delay for specified period
+  delay(1000);
+}
+
+
+
diff --git a/code-snippets/client/ESPcam/test/README b/code-snippets/client/ESPcam/test/README
new file mode 100644
index 0000000000000000000000000000000000000000..9b1e87bc67c90e7f09a92a3e855444b085c655a6
--- /dev/null
+++ b/code-snippets/client/ESPcam/test/README
@@ -0,0 +1,11 @@
+
+This directory is intended for PlatformIO Test Runner and project tests.
+
+Unit Testing is a software testing method by which individual units of
+source code, sets of one or more MCU program modules together with associated
+control data, usage procedures, and operating procedures, are tested to
+determine whether they are fit for use. Unit testing finds problems early
+in the development cycle.
+
+More information about PlatformIO Unit Testing:
+- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
diff --git a/code-snippets/client/sensor_station_Analog/client/.gitignore b/code-snippets/client/sensor_station_Analog/client/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..89cc49cbd652508924b868ea609fa8f6b758ec56
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/.gitignore
@@ -0,0 +1,5 @@
+.pio
+.vscode/.browse.c_cpp.db*
+.vscode/c_cpp_properties.json
+.vscode/launch.json
+.vscode/ipch
diff --git a/code-snippets/client/sensor_station_Analog/client/.gitkeep b/code-snippets/client/sensor_station_Analog/client/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/code-snippets/client/sensor_station_Analog/client/.vscode/extensions.json b/code-snippets/client/sensor_station_Analog/client/.vscode/extensions.json
new file mode 100644
index 0000000000000000000000000000000000000000..080e70d08b9811fa743afe5094658dba0ed6b7c2
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/.vscode/extensions.json
@@ -0,0 +1,10 @@
+{
+    // See http://go.microsoft.com/fwlink/?LinkId=827846
+    // for the documentation about the extensions.json format
+    "recommendations": [
+        "platformio.platformio-ide"
+    ],
+    "unwantedRecommendations": [
+        "ms-vscode.cpptools-extension-pack"
+    ]
+}
diff --git a/code-snippets/client/sensor_station_Analog/client/.vscode/settings.json b/code-snippets/client/sensor_station_Analog/client/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..67863418a31be3d92fc16a4546f09287d55dd01a
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/.vscode/settings.json
@@ -0,0 +1,53 @@
+{
+    "files.associations": {
+        "array": "cpp",
+        "atomic": "cpp",
+        "*.tcc": "cpp",
+        "cctype": "cpp",
+        "clocale": "cpp",
+        "cmath": "cpp",
+        "cstdarg": "cpp",
+        "cstddef": "cpp",
+        "cstdint": "cpp",
+        "cstdio": "cpp",
+        "cstdlib": "cpp",
+        "cstring": "cpp",
+        "ctime": "cpp",
+        "cwchar": "cpp",
+        "cwctype": "cpp",
+        "deque": "cpp",
+        "unordered_map": "cpp",
+        "unordered_set": "cpp",
+        "vector": "cpp",
+        "exception": "cpp",
+        "algorithm": "cpp",
+        "functional": "cpp",
+        "iterator": "cpp",
+        "map": "cpp",
+        "memory": "cpp",
+        "memory_resource": "cpp",
+        "numeric": "cpp",
+        "optional": "cpp",
+        "random": "cpp",
+        "string": "cpp",
+        "string_view": "cpp",
+        "system_error": "cpp",
+        "tuple": "cpp",
+        "type_traits": "cpp",
+        "utility": "cpp",
+        "fstream": "cpp",
+        "initializer_list": "cpp",
+        "iomanip": "cpp",
+        "iosfwd": "cpp",
+        "iostream": "cpp",
+        "istream": "cpp",
+        "limits": "cpp",
+        "new": "cpp",
+        "ostream": "cpp",
+        "sstream": "cpp",
+        "stdexcept": "cpp",
+        "streambuf": "cpp",
+        "cinttypes": "cpp",
+        "typeinfo": "cpp"
+    }
+}
\ No newline at end of file
diff --git a/code-snippets/client/sensor_station_Analog/client/include/Adafruit_ADS1X15_Forte/Adafruit_ADS1X15_Forte.cpp b/code-snippets/client/sensor_station_Analog/client/include/Adafruit_ADS1X15_Forte/Adafruit_ADS1X15_Forte.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d78b4d7bd52142293bcd1fd95a73082c4f2fe05
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/include/Adafruit_ADS1X15_Forte/Adafruit_ADS1X15_Forte.cpp
@@ -0,0 +1,403 @@
+/**************************************************************************/
+/*!
+    @file     Adafruit_ADS1X15.cpp
+    @author   K.Townsend (Adafruit Industries)
+
+    @mainpage Adafruit ADS1X15 ADC Breakout Driver
+
+    @section intro_sec Introduction
+
+    This is a library for the Adafruit ADS1X15 ADC breakout boards.
+
+    Adafruit invests time and resources providing this open source code,
+    please support Adafruit and open-source hardware by purchasing
+    products from Adafruit!
+
+    @section author Author
+
+    Written by Kevin "KTOWN" Townsend for Adafruit Industries.
+
+    @section  HISTORY
+
+    v1.0  - First release
+    v1.1  - Added ADS1115 support - W. Earl
+    v2.0  - Refactor - C. Nelson
+
+    @section license License
+
+    BSD license, all text here must be included in any redistribution
+*/
+/**************************************************************************/
+#include "Adafruit_ADS1X15_Forte.h"
+
+/**************************************************************************/
+/*!
+    @brief  Instantiates a new ADS1015 class w/appropriate properties
+*/
+/**************************************************************************/
+Adafruit_ADS1015::Adafruit_ADS1015() {
+  m_bitShift = 4;
+  m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
+  m_dataRate = RATE_ADS1015_1600SPS;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Instantiates a new ADS1115 class w/appropriate properties
+*/
+/**************************************************************************/
+Adafruit_ADS1115::Adafruit_ADS1115() {
+  m_bitShift = 0;
+  m_gain = GAIN_TWOTHIRDS; /* +/- 6.144V range (limited to VDD +0.3V max!) */
+  m_dataRate = RATE_ADS1115_128SPS;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Sets up the HW (reads coefficients values, etc.)
+
+    @param i2c_addr I2C address of device
+    @param wire I2C bus
+
+    @return true if successful, otherwise false
+*/
+/**************************************************************************/
+bool Adafruit_ADS1X15::begin(uint8_t i2c_addr, TwoWire *wire) {
+  m_i2c_dev = new Adafruit_I2CDevice(i2c_addr, wire);
+  return m_i2c_dev->begin();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Sets the gain and input voltage range
+
+    @param gain gain setting to use
+*/
+/**************************************************************************/
+void Adafruit_ADS1X15::setGain(adsGain_t gain) { m_gain = gain; }
+
+/**************************************************************************/
+/*!
+    @brief  Gets a gain and input voltage range
+
+    @return the gain setting
+*/
+/**************************************************************************/
+adsGain_t Adafruit_ADS1X15::getGain() { return m_gain; }
+
+/**************************************************************************/
+/*!
+    @brief  Sets the data rate
+
+    @param rate the data rate to use
+*/
+/**************************************************************************/
+void Adafruit_ADS1X15::setDataRate(uint16_t rate) { m_dataRate = rate; }
+
+/**************************************************************************/
+/*!
+    @brief  Gets the current data rate
+
+    @return the data rate
+*/
+/**************************************************************************/
+uint16_t Adafruit_ADS1X15::getDataRate() { return m_dataRate; }
+
+/**************************************************************************/
+/*!
+    @brief  Gets a single-ended ADC reading from the specified channel
+
+    @param channel ADC channel to read
+
+    @return the ADC reading
+*/
+/**************************************************************************/
+int16_t Adafruit_ADS1X15::readADC_SingleEnded(uint8_t channel) {
+  if (channel > 3) {
+    return 0;
+  }
+
+  startADCReading(MUX_BY_CHANNEL[channel], /*continuous=*/false);
+
+  // Wait for the conversion to complete
+  while (!conversionComplete())
+    ;
+
+  // Read the conversion results
+  return getLastConversionResults();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Reads the conversion results, measuring the voltage
+            difference between the P (AIN0) and N (AIN1) input.  Generates
+            a signed value since the difference can be either
+            positive or negative.
+
+    @return the ADC reading
+*/
+/**************************************************************************/
+int16_t Adafruit_ADS1X15::readADC_Differential_0_1() {
+  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_1, /*continuous=*/false);
+
+  // Wait for the conversion to complete
+  while (!conversionComplete())
+    ;
+
+  // Read the conversion results
+  return getLastConversionResults();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Reads the conversion results, measuring the voltage
+            difference between the P (AIN0) and N (AIN3) input.  Generates
+            a signed value since the difference can be either
+            positive or negative.
+    @return the ADC reading
+*/
+/**************************************************************************/
+int16_t Adafruit_ADS1X15::readADC_Differential_0_3() {
+  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_0_3, /*continuous=*/false);
+
+  // Wait for the conversion to complete
+  while (!conversionComplete())
+    ;
+
+  // Read the conversion results
+  return getLastConversionResults();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Reads the conversion results, measuring the voltage
+            difference between the P (AIN1) and N (AIN3) input.  Generates
+            a signed value since the difference can be either
+            positive or negative.
+    @return the ADC reading
+*/
+/**************************************************************************/
+int16_t Adafruit_ADS1X15::readADC_Differential_1_3() {
+  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_1_3, /*continuous=*/false);
+
+  // Wait for the conversion to complete
+  while (!conversionComplete())
+    ;
+
+  // Read the conversion results
+  return getLastConversionResults();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Reads the conversion results, measuring the voltage
+            difference between the P (AIN2) and N (AIN3) input.  Generates
+            a signed value since the difference can be either
+            positive or negative.
+
+    @return the ADC reading
+*/
+/**************************************************************************/
+int16_t Adafruit_ADS1X15::readADC_Differential_2_3() {
+  startADCReading(ADS1X15_REG_CONFIG_MUX_DIFF_2_3, /*continuous=*/false);
+
+  // Wait for the conversion to complete
+  while (!conversionComplete())
+    ;
+
+  // Read the conversion results
+  return getLastConversionResults();
+}
+
+/**************************************************************************/
+/*!
+    @brief  Sets up the comparator to operate in basic mode, causing the
+            ALERT/RDY pin to assert (go from high to low) when the ADC
+            value exceeds the specified threshold.
+
+            This will also set the ADC in continuous conversion mode.
+
+    @param channel ADC channel to use
+    @param threshold comparator threshold
+*/
+/**************************************************************************/
+void Adafruit_ADS1X15::startComparator_SingleEnded(uint8_t channel,
+                                                   int16_t threshold) {
+  // Start with default values
+  uint16_t config =
+      ADS1X15_REG_CONFIG_CQUE_1CONV |   // Comparator enabled and asserts on 1
+                                        // match
+      ADS1X15_REG_CONFIG_CLAT_LATCH |   // Latching mode
+      ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
+      ADS1X15_REG_CONFIG_CMODE_TRAD |   // Traditional comparator (default val)
+      ADS1X15_REG_CONFIG_MODE_CONTIN |  // Continuous conversion mode
+      ADS1X15_REG_CONFIG_MODE_CONTIN;   // Continuous conversion mode
+
+  // Set PGA/voltage range
+  config |= m_gain;
+
+  // Set data rate
+  config |= m_dataRate;
+
+  config |= MUX_BY_CHANNEL[channel];
+
+  // Set the high threshold register
+  // Shift 12-bit results left 4 bits for the ADS1015
+  writeRegister(ADS1X15_REG_POINTER_HITHRESH, threshold << m_bitShift);
+
+  // Write config register to the ADC
+  writeRegister(ADS1X15_REG_POINTER_CONFIG, config);
+}
+
+/**************************************************************************/
+/*!
+    @brief  In order to clear the comparator, we need to read the
+            conversion results.  This function reads the last conversion
+            results without changing the config value.
+
+    @return the last ADC reading
+*/
+/**************************************************************************/
+int16_t Adafruit_ADS1X15::getLastConversionResults() {
+  // Read the conversion results
+  uint16_t res = readRegister(ADS1X15_REG_POINTER_CONVERT) >> m_bitShift;
+  if (m_bitShift == 0) {
+    return (int16_t)res;
+  } else {
+    // Shift 12-bit results right 4 bits for the ADS1015,
+    // making sure we keep the sign bit intact
+    if (res > 0x07FF) {
+      // negative number - extend the sign to 16th bit
+      res |= 0xF000;
+    }
+    return (int16_t)res;
+  }
+}
+
+/**************************************************************************/
+/*!
+    @brief  Returns true if conversion is complete, false otherwise.
+
+    @param counts the ADC reading in raw counts
+
+    @return the ADC reading in volts
+*/
+/**************************************************************************/
+float Adafruit_ADS1X15::computeVolts(int16_t counts) {
+  // see data sheet Table 3
+  float fsRange;
+  switch (m_gain) {
+  case GAIN_TWOTHIRDS:
+    fsRange = 6.144f;
+    break;
+  case GAIN_ONE:
+    fsRange = 4.096f;
+    break;
+  case GAIN_TWO:
+    fsRange = 2.048f;
+    break;
+  case GAIN_FOUR:
+    fsRange = 1.024f;
+    break;
+  case GAIN_EIGHT:
+    fsRange = 0.512f;
+    break;
+  case GAIN_SIXTEEN:
+    fsRange = 0.256f;
+    break;
+  default:
+    fsRange = 0.0f;
+  }
+  return counts * (fsRange / (32768 >> m_bitShift));
+}
+
+/**************************************************************************/
+/*!
+    @brief  Non-blocking start conversion function
+
+    Call getLastConversionResults() once conversionComplete() returns true.
+    In continuous mode, getLastConversionResults() will always return the
+    latest result.
+    ALERT/RDY pin is set to RDY mode, and a 8us pulse is generated every
+    time new data is ready.
+
+    @param mux mux field value
+    @param continuous continuous if set, otherwise single shot
+*/
+/**************************************************************************/
+void Adafruit_ADS1X15::startADCReading(uint16_t mux, bool continuous) {
+  // Start with default values
+  uint16_t config =
+      ADS1X15_REG_CONFIG_CQUE_1CONV |   // Set CQUE to any value other than
+                                        // None so we can use it in RDY mode
+      ADS1X15_REG_CONFIG_CLAT_NONLAT |  // Non-latching (default val)
+      ADS1X15_REG_CONFIG_CPOL_ACTVLOW | // Alert/Rdy active low   (default val)
+      ADS1X15_REG_CONFIG_CMODE_TRAD;    // Traditional comparator (default val)
+
+  if (continuous) {
+    config |= ADS1X15_REG_CONFIG_MODE_CONTIN;
+  } else {
+    config |= ADS1X15_REG_CONFIG_MODE_SINGLE;
+  }
+
+  // Set PGA/voltage range
+  config |= m_gain;
+
+  // Set data rate
+  config |= m_dataRate;
+
+  // Set channels
+  config |= mux;
+
+  // Set 'start single-conversion' bit
+  config |= ADS1X15_REG_CONFIG_OS_SINGLE;
+
+  // Write config register to the ADC
+  writeRegister(ADS1X15_REG_POINTER_CONFIG, config);
+
+  // Set ALERT/RDY to RDY mode.
+  writeRegister(ADS1X15_REG_POINTER_HITHRESH, 0x8000);
+  writeRegister(ADS1X15_REG_POINTER_LOWTHRESH, 0x0000);
+}
+
+/**************************************************************************/
+/*!
+    @brief  Returns true if conversion is complete, false otherwise.
+
+    @return True if conversion is complete, false otherwise.
+*/
+/**************************************************************************/
+bool Adafruit_ADS1X15::conversionComplete() {
+  return (readRegister(ADS1X15_REG_POINTER_CONFIG) & 0x8000) != 0;
+}
+
+/**************************************************************************/
+/*!
+    @brief  Writes 16-bits to the specified destination register
+
+    @param reg register address to write to
+    @param value value to write to register
+*/
+/**************************************************************************/
+void Adafruit_ADS1X15::writeRegister(uint8_t reg, uint16_t value) {
+  buffer[0] = reg;
+  buffer[1] = value >> 8;
+  buffer[2] = value & 0xFF;
+  m_i2c_dev->write(buffer, 3);
+}
+
+/**************************************************************************/
+/*!
+    @brief  Read 16-bits from the specified destination register
+
+    @param reg register address to read from
+
+    @return 16 bit register value read
+*/
+/**************************************************************************/
+uint16_t Adafruit_ADS1X15::readRegister(uint8_t reg) {
+  buffer[0] = reg;
+  m_i2c_dev->write(buffer, 1);
+  m_i2c_dev->read(buffer, 2);
+  return ((buffer[0] << 8) | buffer[1]);
+}
diff --git a/code-snippets/client/sensor_station_Analog/client/include/Adafruit_ADS1X15_Forte/Adafruit_ADS1X15_Forte.h b/code-snippets/client/sensor_station_Analog/client/include/Adafruit_ADS1X15_Forte/Adafruit_ADS1X15_Forte.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1c1adf4f789653e7962ef07e3a6ab0685306737
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/include/Adafruit_ADS1X15_Forte/Adafruit_ADS1X15_Forte.h
@@ -0,0 +1,201 @@
+/**************************************************************************/
+/*!
+    @file     Adafruit_ADS1X15.h
+
+    This is a library for the Adafruit ADS1X15 ADC breakout boards.
+
+    Adafruit invests time and resources providing this open source code,
+    please support Adafruit and open-source hardware by purchasing
+    products from Adafruit!
+
+    Written by Kevin "KTOWN" Townsend for Adafruit Industries.
+
+    BSD license, all text here must be included in any redistribution
+*/
+/**************************************************************************/
+#ifndef __ADS1X15_Forte_H__
+#define __ADS1X15_Forte_H__
+
+#include <Adafruit_I2CDevice.h>
+#include <Arduino.h>
+#include <Wire.h>
+
+/*=========================================================================
+    I2C ADDRESS/BITS
+    -----------------------------------------------------------------------*/
+#define ADS1X15_ADDRESS (0x48) ///< 1001 000 (ADDR = GND)
+/*=========================================================================*/
+
+/*=========================================================================
+    POINTER REGISTER
+    -----------------------------------------------------------------------*/
+#define ADS1X15_REG_POINTER_MASK (0x03)      ///< Point mask
+#define ADS1X15_REG_POINTER_CONVERT (0x00)   ///< Conversion
+#define ADS1X15_REG_POINTER_CONFIG (0x01)    ///< Configuration
+#define ADS1X15_REG_POINTER_LOWTHRESH (0x02) ///< Low threshold
+#define ADS1X15_REG_POINTER_HITHRESH (0x03)  ///< High threshold
+/*=========================================================================*/
+
+/*=========================================================================
+    CONFIG REGISTER
+    -----------------------------------------------------------------------*/
+#define ADS1X15_REG_CONFIG_OS_MASK (0x8000) ///< OS Mask
+#define ADS1X15_REG_CONFIG_OS_SINGLE                                           \
+  (0x8000) ///< Write: Set to start a single-conversion
+#define ADS1X15_REG_CONFIG_OS_BUSY                                             \
+  (0x0000) ///< Read: Bit = 0 when conversion is in progress
+#define ADS1X15_REG_CONFIG_OS_NOTBUSY                                          \
+  (0x8000) ///< Read: Bit = 1 when device is not performing a conversion
+
+#define ADS1X15_REG_CONFIG_MUX_MASK (0x7000) ///< Mux Mask
+#define ADS1X15_REG_CONFIG_MUX_DIFF_0_1                                        \
+  (0x0000) ///< Differential P = AIN0, N = AIN1 (default)
+#define ADS1X15_REG_CONFIG_MUX_DIFF_0_3                                        \
+  (0x1000) ///< Differential P = AIN0, N = AIN3
+#define ADS1X15_REG_CONFIG_MUX_DIFF_1_3                                        \
+  (0x2000) ///< Differential P = AIN1, N = AIN3
+#define ADS1X15_REG_CONFIG_MUX_DIFF_2_3                                        \
+  (0x3000) ///< Differential P = AIN2, N = AIN3
+#define ADS1X15_REG_CONFIG_MUX_SINGLE_0 (0x4000) ///< Single-ended AIN0
+#define ADS1X15_REG_CONFIG_MUX_SINGLE_1 (0x5000) ///< Single-ended AIN1
+#define ADS1X15_REG_CONFIG_MUX_SINGLE_2 (0x6000) ///< Single-ended AIN2
+#define ADS1X15_REG_CONFIG_MUX_SINGLE_3 (0x7000) ///< Single-ended AIN3
+
+constexpr uint16_t MUX_BY_CHANNEL[] = {
+    ADS1X15_REG_CONFIG_MUX_SINGLE_0, ///< Single-ended AIN0
+    ADS1X15_REG_CONFIG_MUX_SINGLE_1, ///< Single-ended AIN1
+    ADS1X15_REG_CONFIG_MUX_SINGLE_2, ///< Single-ended AIN2
+    ADS1X15_REG_CONFIG_MUX_SINGLE_3  ///< Single-ended AIN3
+};                                   ///< MUX config by channel
+
+#define ADS1X15_REG_CONFIG_PGA_MASK (0x0E00)   ///< PGA Mask
+#define ADS1X15_REG_CONFIG_PGA_6_144V (0x0000) ///< +/-6.144V range = Gain 2/3
+#define ADS1X15_REG_CONFIG_PGA_4_096V (0x0200) ///< +/-4.096V range = Gain 1
+#define ADS1X15_REG_CONFIG_PGA_2_048V                                          \
+  (0x0400) ///< +/-2.048V range = Gain 2 (default)
+#define ADS1X15_REG_CONFIG_PGA_1_024V (0x0600) ///< +/-1.024V range = Gain 4
+#define ADS1X15_REG_CONFIG_PGA_0_512V (0x0800) ///< +/-0.512V range = Gain 8
+#define ADS1X15_REG_CONFIG_PGA_0_256V (0x0A00) ///< +/-0.256V range = Gain 16
+
+#define ADS1X15_REG_CONFIG_MODE_MASK (0x0100)   ///< Mode Mask
+#define ADS1X15_REG_CONFIG_MODE_CONTIN (0x0000) ///< Continuous conversion mode
+#define ADS1X15_REG_CONFIG_MODE_SINGLE                                         \
+  (0x0100) ///< Power-down single-shot mode (default)
+
+#define ADS1X15_REG_CONFIG_RATE_MASK (0x00E0) ///< Data Rate Mask
+
+#define ADS1X15_REG_CONFIG_CMODE_MASK (0x0010) ///< CMode Mask
+#define ADS1X15_REG_CONFIG_CMODE_TRAD                                          \
+  (0x0000) ///< Traditional comparator with hysteresis (default)
+#define ADS1X15_REG_CONFIG_CMODE_WINDOW (0x0010) ///< Window comparator
+
+#define ADS1X15_REG_CONFIG_CPOL_MASK (0x0008) ///< CPol Mask
+#define ADS1X15_REG_CONFIG_CPOL_ACTVLOW                                        \
+  (0x0000) ///< ALERT/RDY pin is low when active (default)
+#define ADS1X15_REG_CONFIG_CPOL_ACTVHI                                         \
+  (0x0008) ///< ALERT/RDY pin is high when active
+
+#define ADS1X15_REG_CONFIG_CLAT_MASK                                           \
+  (0x0004) ///< Determines if ALERT/RDY pin latches once asserted
+#define ADS1X15_REG_CONFIG_CLAT_NONLAT                                         \
+  (0x0000) ///< Non-latching comparator (default)
+#define ADS1X15_REG_CONFIG_CLAT_LATCH (0x0004) ///< Latching comparator
+
+#define ADS1X15_REG_CONFIG_CQUE_MASK (0x0003) ///< CQue Mask
+#define ADS1X15_REG_CONFIG_CQUE_1CONV                                          \
+  (0x0000) ///< Assert ALERT/RDY after one conversions
+#define ADS1X15_REG_CONFIG_CQUE_2CONV                                          \
+  (0x0001) ///< Assert ALERT/RDY after two conversions
+#define ADS1X15_REG_CONFIG_CQUE_4CONV                                          \
+  (0x0002) ///< Assert ALERT/RDY after four conversions
+#define ADS1X15_REG_CONFIG_CQUE_NONE                                           \
+  (0x0003) ///< Disable the comparator and put ALERT/RDY in high state (default)
+/*=========================================================================*/
+
+/** Gain settings */
+typedef enum {
+  GAIN_TWOTHIRDS = ADS1X15_REG_CONFIG_PGA_6_144V,
+  GAIN_ONE = ADS1X15_REG_CONFIG_PGA_4_096V,
+  GAIN_TWO = ADS1X15_REG_CONFIG_PGA_2_048V,
+  GAIN_FOUR = ADS1X15_REG_CONFIG_PGA_1_024V,
+  GAIN_EIGHT = ADS1X15_REG_CONFIG_PGA_0_512V,
+  GAIN_SIXTEEN = ADS1X15_REG_CONFIG_PGA_0_256V
+} adsGain_t;
+
+/** Data rates */
+#define RATE_ADS1015_128SPS (0x0000)  ///< 128 samples per second
+#define RATE_ADS1015_250SPS (0x0020)  ///< 250 samples per second
+#define RATE_ADS1015_490SPS (0x0040)  ///< 490 samples per second
+#define RATE_ADS1015_920SPS (0x0060)  ///< 920 samples per second
+#define RATE_ADS1015_1600SPS (0x0080) ///< 1600 samples per second (default)
+#define RATE_ADS1015_2400SPS (0x00A0) ///< 2400 samples per second
+#define RATE_ADS1015_3300SPS (0x00C0) ///< 3300 samples per second
+
+#define RATE_ADS1115_8SPS (0x0000)   ///< 8 samples per second
+#define RATE_ADS1115_16SPS (0x0020)  ///< 16 samples per second
+#define RATE_ADS1115_32SPS (0x0040)  ///< 32 samples per second
+#define RATE_ADS1115_64SPS (0x0060)  ///< 64 samples per second
+#define RATE_ADS1115_128SPS (0x0080) ///< 128 samples per second (default)
+#define RATE_ADS1115_250SPS (0x00A0) ///< 250 samples per second
+#define RATE_ADS1115_475SPS (0x00C0) ///< 475 samples per second
+#define RATE_ADS1115_860SPS (0x00E0) ///< 860 samples per second
+
+/**************************************************************************/
+/*!
+    @brief  Sensor driver for the Adafruit ADS1X15 ADC breakouts.
+*/
+/**************************************************************************/
+class Adafruit_ADS1X15 {
+protected:
+  // Instance-specific properties
+  Adafruit_I2CDevice *m_i2c_dev; ///< I2C bus device
+  uint8_t m_bitShift;            ///< bit shift amount
+  adsGain_t m_gain;              ///< ADC gain
+  uint16_t m_dataRate;           ///< Data rate
+
+public:
+  bool begin(uint8_t i2c_addr = ADS1X15_ADDRESS, TwoWire *wire = &Wire);
+  int16_t readADC_SingleEnded(uint8_t channel);
+  int16_t readADC_Differential_0_1();
+  int16_t readADC_Differential_0_3();
+  int16_t readADC_Differential_1_3();
+  int16_t readADC_Differential_2_3();
+  void startComparator_SingleEnded(uint8_t channel, int16_t threshold);
+  int16_t getLastConversionResults();
+  float computeVolts(int16_t counts);
+  void setGain(adsGain_t gain);
+  adsGain_t getGain();
+  void setDataRate(uint16_t rate);
+  uint16_t getDataRate();
+
+  void startADCReading(uint16_t mux, bool continuous);
+
+  bool conversionComplete();
+
+private:
+  void writeRegister(uint8_t reg, uint16_t value);
+  uint16_t readRegister(uint8_t reg);
+  uint8_t buffer[3];
+};
+
+/**************************************************************************/
+/*!
+    @brief  Sensor driver for the Adafruit ADS1015 ADC breakout.
+*/
+/**************************************************************************/
+class Adafruit_ADS1015 : public Adafruit_ADS1X15 {
+public:
+  Adafruit_ADS1015();
+};
+
+/**************************************************************************/
+/*!
+    @brief  Sensor driver for the Adafruit ADS1115 ADC breakout.
+*/
+/**************************************************************************/
+class Adafruit_ADS1115 : public Adafruit_ADS1X15 {
+public:
+  Adafruit_ADS1115();
+};
+
+#endif
diff --git a/code-snippets/client/sensor_station_Analog/client/include/NoDataAvailableException.hpp b/code-snippets/client/sensor_station_Analog/client/include/NoDataAvailableException.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a5de09a1410326f7f6352f4e27a455b3f2b0061f
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/include/NoDataAvailableException.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <exception>
+#include <iostream>
+
+struct NoDataAvailableException : public std::exception {
+	const char *what() const throw() { return "Sensor could not read data"; }
+};
\ No newline at end of file
diff --git a/code-snippets/client/sensor_station_Analog/client/include/README b/code-snippets/client/sensor_station_Analog/client/include/README
new file mode 100644
index 0000000000000000000000000000000000000000..194dcd43252dcbeb2044ee38510415041a0e7b47
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/include/README
@@ -0,0 +1,39 @@
+
+This directory is intended for project header files.
+
+A header file is a file containing C declarations and macro definitions
+to be shared between several project source files. You request the use of a
+header file in your project source file (C, C++, etc) located in `src` folder
+by including it, with the C preprocessing directive `#include'.
+
+```src/main.c
+
+#include "header.h"
+
+int main (void)
+{
+ ...
+}
+```
+
+Including a header file produces the same results as copying the header file
+into each source file that needs it. Such copying would be time-consuming
+and error-prone. With a header file, the related declarations appear
+in only one place. If they need to be changed, they can be changed in one
+place, and programs that include the header file will automatically use the
+new version when next recompiled. The header file eliminates the labor of
+finding and changing all the copies as well as the risk that a failure to
+find one copy will result in inconsistencies within a program.
+
+In C, the usual convention is to give header files names that end with `.h'.
+It is most portable to use only letters, digits, dashes, and underscores in
+header file names, and at most one dot.
+
+Read more about using header files in official GCC documentation:
+
+* Include Syntax
+* Include Operation
+* Once-Only Headers
+* Computed Includes
+
+https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
diff --git a/code-snippets/client/sensor_station_Analog/client/include/forte_sensor.hpp b/code-snippets/client/sensor_station_Analog/client/include/forte_sensor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ca8e39602ae03062a79a2acb8c25e634521f8b8
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/include/forte_sensor.hpp
@@ -0,0 +1,15 @@
+#ifndef _FORTE_SENSOR
+#define _FORTE_SENSOR
+
+// #include "Message.hpp"
+template <class T>
+class Forte_Sensor {
+  public:
+	virtual T read_data() = 0;
+	virtual void setup() = 0;
+	//virtual Message build_message() = 0;
+
+  private:
+};
+
+#endif
\ No newline at end of file
diff --git a/client/client/include/pinout.hpp b/code-snippets/client/sensor_station_Analog/client/include/pinout.hpp
similarity index 64%
rename from client/client/include/pinout.hpp
rename to code-snippets/client/sensor_station_Analog/client/include/pinout.hpp
index 9a9ad92494f6fda373d184ef94f91d17934e01ba..f59c23778660216a8d632661933da581c7de2a4a 100644
--- a/client/client/include/pinout.hpp
+++ b/code-snippets/client/sensor_station_Analog/client/include/pinout.hpp
@@ -2,7 +2,7 @@
 #define _FORTE_PINOUT
 
 // Pins for I2C
-#define I2C_SDA 18
-#define I2C_SCL 19
+#define I2C_SDA 6
+#define I2C_SCL 7
 
 #endif
\ No newline at end of file
diff --git a/code-snippets/client/sensor_station_Analog/client/platformio.ini b/code-snippets/client/sensor_station_Analog/client/platformio.ini
new file mode 100644
index 0000000000000000000000000000000000000000..d91781822e43930b87bdfd6cea9c76ef8529e360
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/platformio.ini
@@ -0,0 +1,27 @@
+; PlatformIO Project Configuration File
+;
+;   Build options: build flags, source filter
+;   Upload options: custom upload port, speed and extra flags
+;   Library options: dependencies, extra library storages
+;   Advanced options: extra scripting
+;
+; Please visit documentation for the other options and examples
+; https://docs.platformio.org/page/projectconf.html
+
+[env:esp32-c3-devkitm-1]
+platform = espressif32
+board = esp32-c3-devkitm-1
+framework = arduino
+monitor_speed = 9600
+build_flags = 
+	-I include
+lib_deps = 
+	sparkfun/SparkFun SCD30 Arduino Library@^1.0.18
+	Wire
+	adafruit/Adafruit BusIO@^1.13.2
+	Adafruit_I2CDevice
+	SPI
+	envirodiy/SDI-12@^2.1.4
+	fbiego/ESP32Time@^2.0.0
+	Wifi
+	adafruit/Adafruit INA219@^1.2.0
diff --git a/code-snippets/client/sensor_station_Analog/client/src/main.cpp b/code-snippets/client/sensor_station_Analog/client/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9376c9906b479b213600b54ea1abecb42558b2ff
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/src/main.cpp
@@ -0,0 +1,59 @@
+#include <Arduino.h>
+#include <Wire.h>
+#include <dr26.hpp>
+#include <ina219.hpp>
+#include "esp_debug_helpers.h"
+
+Adafruit_ADS1115 adss;
+
+Forte_DR26 dr26;
+Forte_INA219 ina219;
+
+void setup()
+{
+	Serial.begin(9600);
+   Wire.begin(6, 7);
+    adss.setGain(GAIN_ONE);
+    adss.begin() ?  Serial.println("ADS initialized") : Serial.println("failed to initialize ADS");
+    delay(100);
+	dr26.setup();
+	ina219.setup();
+	
+}
+int x =0;
+
+void loop()
+{
+   Serial.println("******************************DR26-Analog********************Tree-Size**********************************");
+   float data_drs26=-1;
+   try
+   {
+      data_drs26=dr26.read_data();
+      Serial.print("dr26: ");
+	   Serial.println(data_drs26);
+   }
+   catch(NoDataAvailableException& e){
+	Serial.println("No Sensor aviable");
+   }
+   
+	out_data_ina219 data_ina219;
+	Serial.println("******************************INA219********************Power--Consumption**********************************");
+   try{
+		   data_ina219=ina219.read_data();
+   		Serial.print("Shunt Voltage [mV]: "); Serial.println(data_ina219.shuntVoltage_mV);
+        Serial.print("Bus Voltage [V]: "); Serial.println(data_ina219.busVoltage_V);
+        Serial.print("Load Voltage [V]: "); Serial.println(data_ina219.loadVoltage_V);
+        Serial.print("Current[mA]: "); Serial.println(data_ina219.current_mA);
+        Serial.print("Bus Power [mW]: "); Serial.println(data_ina219.power_mW);
+        data_ina219.ina219_overflow== false ? Serial.println("Values OK - no overflow") : Serial.println("Overflow! Choose higher PGAIN");
+        Serial.println();
+   }
+
+   catch(NoDataAvailableException& e){
+	   Serial.println("No Sensor aviable");
+   }
+ 
+  // Serial.println("Free Heap:"); Keep trach of the free stack -> momery leak can cause stack overflow 
+  // Serial.println(esp_get_free_heap_size());
+	delay(4000);
+}
diff --git a/code-snippets/client/sensor_station_Analog/client/test/README b/code-snippets/client/sensor_station_Analog/client/test/README
new file mode 100644
index 0000000000000000000000000000000000000000..9b1e87bc67c90e7f09a92a3e855444b085c655a6
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/test/README
@@ -0,0 +1,11 @@
+
+This directory is intended for PlatformIO Test Runner and project tests.
+
+Unit Testing is a software testing method by which individual units of
+source code, sets of one or more MCU program modules together with associated
+control data, usage procedures, and operating procedures, are tested to
+determine whether they are fit for use. Unit testing finds problems early
+in the development cycle.
+
+More information about PlatformIO Unit Testing:
+- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html
diff --git a/code-snippets/client/sensor_station_Analog/client/test/TestESPNow.cpp b/code-snippets/client/sensor_station_Analog/client/test/TestESPNow.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a4c149bb8742e61c0a235691e7e0b8d9a3eda76
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/test/TestESPNow.cpp
@@ -0,0 +1,12 @@
+#include "TestESPNow.hpp"
+
+void test_on_data_recv_valid_config()
+{
+	uint8_t mac_addr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+	int len = 0;
+	config conf = {host : {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, time_millis : 0};
+
+	// preferences / hostinfo would need to be global for this to work
+
+	TEST_FAIL();
+}
diff --git a/code-snippets/client/sensor_station_Analog/client/test/TestESPNow.hpp b/code-snippets/client/sensor_station_Analog/client/test/TestESPNow.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a2512b2ffd4ca48031415456d658302d4bae6b91
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/test/TestESPNow.hpp
@@ -0,0 +1,6 @@
+#pragma once
+#include <Arduino.h>
+#include <ESPNow.hpp>
+#include <unity.h>
+
+void test_on_data_recv_valid_config();
\ No newline at end of file
diff --git a/client/client/test/TestMessage.cpp b/code-snippets/client/sensor_station_Analog/client/test/TestMessage.cpp
similarity index 100%
rename from client/client/test/TestMessage.cpp
rename to code-snippets/client/sensor_station_Analog/client/test/TestMessage.cpp
diff --git a/client/client/test/TestMessage.hpp b/code-snippets/client/sensor_station_Analog/client/test/TestMessage.hpp
similarity index 100%
rename from client/client/test/TestMessage.hpp
rename to code-snippets/client/sensor_station_Analog/client/test/TestMessage.hpp
diff --git a/code-snippets/client/sensor_station_Analog/client/test/main.cpp b/code-snippets/client/sensor_station_Analog/client/test/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82d68817015b01f036c346b18fa79601a5e842aa
--- /dev/null
+++ b/code-snippets/client/sensor_station_Analog/client/test/main.cpp
@@ -0,0 +1,17 @@
+#include "TestESPNow.hpp"
+#include "TestMessage.hpp"
+#include <Arduino.h>
+#include <unity.h>
+
+void setup()
+{
+	delay(2000); // service delay
+
+	UNITY_BEGIN();
+	RUN_TEST(test_on_data_recv_valid_config);
+	RUN_TEST(test_on_data_recv);
+	RUN_TEST(test_new_message_filled);
+	UNITY_END();
+}
+
+void loop() {}
\ No newline at end of file
diff --git a/host/esp32/src/main.cpp b/host/esp32/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..132224b96d1c3240b9c6ef17df736f0d1f16c01c
--- /dev/null
+++ b/host/esp32/src/main.cpp
@@ -0,0 +1,42 @@
+#include <Arduino.h>
+#include <WiFi.h>
+#include <esp_now.h>
+
+void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status)
+{
+	// go to sleep
+}
+
+void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len)
+{
+	// print mac
+	Serial.println("Message recieved");
+	for (int i = 0; i < 6; i++) {
+		Serial.print(mac[i], HEX);
+		Serial.print(":");
+	}
+	Serial.println();
+
+	char data[len];
+	memcpy(data, incomingData, len);
+	Serial.println(data);
+}
+
+void setup()
+{
+	Serial.begin(115200);
+	WiFi.mode(WIFI_STA);
+	Serial.println("ESPNow init");
+	if (esp_now_init() != ESP_OK) {
+		// initialization failed
+		Serial.println("ESPNow init failed");
+		return; // not sure about this
+	}
+	Serial.println("ESPNow init success");
+
+	esp_now_register_recv_cb(on_data_recv);
+	//  write your initialization code here
+}
+
+void loop() {
+}
\ No newline at end of file
diff --git a/host/fipy/main.py b/host/fipy/main.py
index 020d027e0345c701725c418eade7e55179d05551..9e65d65ab025701fe5dd71b5f2f25b2a839d741b 100644
--- a/host/fipy/main.py
+++ b/host/fipy/main.py
@@ -1,19 +1,58 @@
 # main.py -- put your code here!
-# from network import ESPNOW
+from network import WLAN
+from network import ESPNOW
+import binascii
+import struct
 from time import sleep
 from lib.server_transfer import DataTransferWiFi
-from lib.espnow import Forte_ESPNOW
 from lib.rtc_time import RTCTime
 
-# data = DataTransferWiFi()
-# data.connect("Z", "AbsoluteSandwich")
-# data.send(
-#     "airSensors,sensor_id=TLM0201 temperature=73.97038159354763,humidity=35.23103248356096,co=0.4844531056779361 1661175680\nairSensors,sensor_id=TLM0202 temperature=75.30007505999716,humidity=35.65192991869171,co=0.5141876544505826 1661175680\nairSensors,sensor_id=TLM0202 temperature=75.30007505999756,humidity=35.65192991869171,co=0.5141876544505826 1661175680"
-# )
+
+def bytes_to_data(msg):
+    # turn bytes from message into 22-tuple of integers(identifiers), floats(values), integer(amount) and long(timestamp)
+    data = struct.unpack("<10i10fil", bytes(msg))
+    amountData = data[20]
+    timestamp = data[21]
+    identifiers = data[0:amountData]
+    values = data[10 : (10 + amountData)]
+    return {
+        "amountData": amountData,
+        "timestamp": timestamp,
+        "data": dict(zip(identifiers, values)),
+    }
+
+
+def espnow_recv(result):
+    mac, peer, msg = result
+
+    data = bytes_to_data(msg)
+    try:
+        print(data["amountData"])
+        print(data["timestamp"])
+        print(data["data"])
+        print(str(binascii.hexlify(mac), "ascii"))
+
+    except Exception as error:
+        print(error)
+
+
+data = DataTransferWiFi()
+data.connect("Z", "AbsoluteSandwich")
+data.send(
+    "airSensors,sensor_id=TLM0201 temperature=73.97038159354763,humidity=35.23103248356096,co=0.4844531056779361 1661175680\nairSensors,sensor_id=TLM0202 temperature=75.30007505999716,humidity=35.65192991869171,co=0.5141876544505826 1661175680\nairSensors,sensor_id=TLM0202 temperature=75.30007505999756,humidity=35.65192991869171,co=0.5141876544505826 1661175680"
+)
 # data.disconnect()
-#
+
 rtc_time = RTCTime((2014, 5, 1, 4, 13, 0, 0, 0))
-espnow = Forte_ESPNOW()
+# w = WLAN()
+
+# ESPNOW.init()
+# p = ESPNOW.add_peer("58cf79043c84")
+
+# ESPNOW.on_recv(espnow_recv)
+
 while True:
+    print("...")
     sleep(5)
+    print(rtc_time.get_time())
     pass