From bb49b9f867dff85052bb1c46efcef25fe77d7915 Mon Sep 17 00:00:00 2001
From: Zoe Pfister <zoe.pfister@uibk.ac.at>
Date: Fri, 10 Mar 2023 11:46:21 +0100
Subject: [PATCH] Renamed Sentec Sensors, made RS485 serial connection a
 singleton shared pointer, allow resetting of precipitation, added error type
 for non-ability to reset precipitation

---
 .../SentecSensors/SentecRainGaugeSensor.cpp   | 111 +++++---
 .../SentecSensors/SentecRainGaugeSensor.h     |  37 +--
 client/libs/SentecSensors/SentecSensors.cpp   | 266 ------------------
 client/libs/SentecSensors/SentecSensors.h     |  54 ----
 .../libs/SentecSensors/SentecSensorsRS485.cpp | 248 ++++++++++++++++
 .../libs/SentecSensors/SentecSensorsRS485.h   |  60 ++++
 .../SentecSolarRadiationSensor.cpp            |  38 ++-
 .../SentecSolarRadiationSensor.h              |  30 +-
 client/libs/rs485/RS485HardwareSerial.h       |  47 ++++
 client/libs/rs485/rs485.cpp                   |  84 +++---
 client/libs/rs485/rs485.hpp                   |   9 +-
 shared-libs/DataTransfer/ErrorTypes.cpp       |  66 ++---
 shared-libs/DataTransfer/ErrorTypes.h         |  31 +-
 13 files changed, 573 insertions(+), 508 deletions(-)
 delete mode 100644 client/libs/SentecSensors/SentecSensors.cpp
 delete mode 100644 client/libs/SentecSensors/SentecSensors.h
 create mode 100644 client/libs/SentecSensors/SentecSensorsRS485.cpp
 create mode 100644 client/libs/SentecSensors/SentecSensorsRS485.h
 create mode 100644 client/libs/rs485/RS485HardwareSerial.h

diff --git a/client/libs/SentecSensors/SentecRainGaugeSensor.cpp b/client/libs/SentecSensors/SentecRainGaugeSensor.cpp
index b785fd7..befb26d 100644
--- a/client/libs/SentecSensors/SentecRainGaugeSensor.cpp
+++ b/client/libs/SentecSensors/SentecRainGaugeSensor.cpp
@@ -4,51 +4,70 @@
 
 #include "SentecRainGaugeSensor.h"
 
-void RainGaugeSensor::resetPrecipitation() {
-  // clears rainfall rata from the rain gauge
-  // delay resetting after the last register reading
-  delay(100);
-  Serial.println("Resetting precipitation sum");
-  writeRegister(0x00, 0x5A);
-  // TODO check response: matches the sent message exactly
-}
-void RainGaugeSensor::resetSensor() {
-  // no clue what the difference between this one and the previous one is...
-  //   and the manual is not helpful, of course
-  delay(100);
-  Serial.println("Resetting precipitation sum");
-  writeRegister(0x37, 0x03);
-  // TODO check response: matches the sent message exactly
-}
-out_data_rain_gauge RainGaugeSensor::getInstantaneousPrecipitation() {
-  // gets tips of scale since the last reset, i.e. total precipitation (I THINK)
-  //   manual says this is current precipitation - is it?
-  auto error = readRegister(0, 0x01);
-  precipitation = word(answerFrame[3], answerFrame[4]);
-
-  ESP_LOGI("RainGauge", "Precipitation: %.1f mm", precipitation / 10.0);
-  // resetPrecipitation();
-  return {static_cast<float>(precipitation), error};
-}
-String RainGaugeSensor::getPrecipitationStr() {
-  return getValueStr((float) (precipitation / 10.0));
-}
-
-SensorInformation RainGaugeSensor::getSensorInformation() const {
-  return SensorInformation(HardwareName::SEM404, SensorProtocol::RS485);
-}
-std::list<Message> RainGaugeSensor::buildMessages() {
-  auto messages = std::list<Message>();
-  auto data = getInstantaneousPrecipitation();
-  Measurement precipitationMeasurement{data.precipitation, address, NO_I2C_ADDRESS,
-                            MeasurementType::PRECIPITATION, data.precipitationError};
-  messages.emplace_back(Message{precipitationMeasurement, getSensorInformation(),
-                                Time::getInstance().getEpochSeconds()});
-  return messages;
-}
-out_data_rain_gauge RainGaugeSensor::readData() {
-  return getInstantaneousPrecipitation();
-}
-void RainGaugeSensor::setup() {
+static const char *TAG = "SEM404";
 
+bool SEM404::isAnswerFrameEqualToQueryFrame(byte answerFrame[8], byte queryFrame[8]) {
+    for (int i = 0; i < 8; i++) {
+        if (answerFrame[i] != queryFrame[i]) {
+            ESP_LOGD(TAG, "Response: 0x%s", formatBytes(answerFrame, 8).c_str());
+            ESP_LOGD(TAG, "Response: 0x%s", formatBytes(queryFrame, 8).c_str());
+            ESP_LOGE("TEST", "Error: answerFrame[%d] != ret.writtenQuery[%d]", i, i);
+            return false;
+        }
+    }
+    return true;
 }
+
+ErrorType SEM404::resetPrecipitation() {
+    // clears rainfall rata from the rain gauge
+    // delay resetting after the last register reading
+    delay(100);
+    Serial.println("Resetting precipitation sum");
+    auto writeRegisterReturn = writeRegister(0x00, 0x5A);
+
+    // other errors like not connected will be shared when reading out the sensor
+    if (!isAnswerFrameEqualToQueryFrame(answerFrame, writeRegisterReturn.writtenQuery)) {
+        ESP_LOGE(TAG, "Could not reset precipitation");
+        return ErrorType::SEM404_COULD_NOT_RESET_PRECIPITATION;
+    }
+
+    return writeRegisterReturn.errorType;
+}
+
+void SEM404::resetSensor() {
+    // no clue what the difference between this one and the previous one is...
+    //   and the manual is not helpful, of course
+    delay(100);
+    Serial.println("Resetting precipitation sum");
+    writeRegister(0x37, 0x03);
+    // TODO check response: matches the sent message exactly
+}
+out_data_rain_gauge SEM404::getInstantaneousPrecipitation() {
+    // gets tips of scale since the last reset, i.e. total precipitation (I THINK)
+    //   manual says this is current precipitation - is it?
+    auto error = readRegister(0, 0x01);
+    precipitation = word(answerFrame[3], answerFrame[4]);
+
+    ESP_LOGI("RainGauge", "Precipitation: %s mm", getPrecipitationStr().c_str());
+    // resetPrecipitation();
+    return {static_cast<float>(precipitation), error};
+}
+String SEM404::getPrecipitationStr() {
+    return getValueStr((float)(precipitation / 10.0));
+}
+
+SensorInformation SEM404::getSensorInformation() const {
+    return {HardwareName::SEM404, SensorProtocol::RS485};
+}
+std::list<Message> SEM404::buildMessages() {
+    auto messages = std::list<Message>();
+    auto data = getInstantaneousPrecipitation();
+    Measurement precipitationMeasurement{data.precipitation, address, NO_I2C_ADDRESS, MeasurementType::PRECIPITATION,
+                                         data.precipitationError};
+    messages.emplace_back(precipitationMeasurement, getSensorInformation(), Time::getInstance().getEpochSeconds());
+    return messages;
+}
+out_data_rain_gauge SEM404::readData() {
+    return getInstantaneousPrecipitation();
+}
+void SEM404::setup() {}
diff --git a/client/libs/SentecSensors/SentecRainGaugeSensor.h b/client/libs/SentecSensors/SentecRainGaugeSensor.h
index c329fb0..0425285 100644
--- a/client/libs/SentecSensors/SentecRainGaugeSensor.h
+++ b/client/libs/SentecSensors/SentecRainGaugeSensor.h
@@ -5,34 +5,35 @@
 #ifndef CLIENT_CENTRAL_MAST_SENTECRAINGAUGESENSOR_H
 #define CLIENT_CENTRAL_MAST_SENTECRAINGAUGESENSOR_H
 
-#include "SentecSensors.h"
+#include "SentecSensorsRS485.h"
 #include <ForteSensor.hpp>
 #include <Message.hpp>
 
 struct out_data_rain_gauge {
-  float precipitation;
-  ErrorType precipitationError;
+    float precipitation;
+    ErrorType precipitationError;
 };
 
-class RainGaugeSensor
-    : public SentecSensorRS485, ForteSensor<out_data_rain_gauge> {
- public:
-  using SentecSensorRS485::SentecSensorRS485;
-  // precipitation values [mm]
-  unsigned int precipitation = 0; // prcp since reset? (I THINK!!!)
+class SEM404 : public SentecSensorRS485, ForteSensor<out_data_rain_gauge> {
+  public:
+    using SentecSensorRS485::SentecSensorRS485;
+    SEM404() : SentecSensorRS485(0) {}
+    // precipitation values [mm]
+    unsigned int precipitation = 0; // prcp since reset? (I THINK!!!)
 
-  void resetPrecipitation();
+    ErrorType resetPrecipitation();
 
-  void resetSensor();
+    void resetSensor();
 
-  out_data_rain_gauge getInstantaneousPrecipitation();
+    out_data_rain_gauge getInstantaneousPrecipitation();
 
-  String getPrecipitationStr();
+    String getPrecipitationStr();
 
-  void setup() override;
-  out_data_rain_gauge readData() override;
-  std::list<Message> buildMessages() override;
-  [[nodiscard]] SensorInformation getSensorInformation() const override;
+    void setup() override;
+    out_data_rain_gauge readData() override;
+    std::list<Message> buildMessages() override;
+    [[nodiscard]] SensorInformation getSensorInformation() const override;
 
+    bool isAnswerFrameEqualToQueryFrame(byte *answerFrame, byte *queryFrame);
 };
-#endif //CLIENT_CENTRAL_MAST_SENTECRAINGAUGESENSOR_H
+#endif // CLIENT_CENTRAL_MAST_SENTECRAINGAUGESENSOR_H
diff --git a/client/libs/SentecSensors/SentecSensors.cpp b/client/libs/SentecSensors/SentecSensors.cpp
deleted file mode 100644
index 090a0d5..0000000
--- a/client/libs/SentecSensors/SentecSensors.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-#include "NoDataAvailableException.hpp"
-#include "SentecRainGaugeSensor.h"
-#include "SentecSolarRadiationSensor.h"
-#include <SentecSensors.h>
-
-/***************************************
- *          RS485 SENSOR READOUT
- ****************************************/
-static const char *TAG = "SENTEC";
-
-SentecSensorRS485::SentecSensorRS485(HardwareSerial *ser, byte add) {
-  address = add;
-  RS485 = ser;
-}
-
-SentecSensorRS485::SentecSensorRS485(HardwareSerial *ser,
-                                     byte add,
-                                     uint8_t serialControlPin) {
-  address = add;
-  RS485 = ser;
-  serialCommunicationControlPin = serialControlPin;
-}
-
-void SentecSensorRS485::write(byte queryFrame[], int length) {
-  // sends a message (bytes) to the sensor
-
-  // Initialize the transmitter
-  digitalWrite(serialCommunicationControlPin, HIGH);
-  // Send message: request a reading from the sensor
-  RS485->write(queryFrame, length);
-  RS485->flush();
-  // Initialize the receiver
-  digitalWrite(serialCommunicationControlPin, LOW);
-}
-
-String SentecSensorRS485::getValueStr(float value) {
-  if (valid) {
-    return String(value, 1);
-  } else {
-    return String("null");
-  }
-}
-
-String SentecSensorRS485::getValueStr(int value) {
-  if (valid) {
-    return String(value);
-  } else {
-    return String("null");
-  }
-}
-
-ErrorType SentecSensorRS485::queryAddress() {
-  // request the address of the sensor with ONLY ONE SENSOR ON THE BUS
-
-  byte tmp_addr = address; // store the address in a temporary byte
-  address = 0xFF;          // change the address to FF (0) for address check
-  ErrorType tmp = readRegister(word(0x07, 0xD0), 2);
-  address = tmp_addr; // set the original address back
-  return tmp;
-}
-
-ErrorType SentecSensorRS485::readRegister(int registerStartAddress) {
-  return readRegister(registerStartAddress, 1);
-}
-
-ErrorType
-SentecSensorRS485::readRegister(int registerStartAddress, int registerLength) {
-  // function code 0x03: get data measured by the sensor
-  byte query[8];
-  query[0] = address;
-  // function code
-  query[1] = 0x03;
-  // register start address
-  query[2] = registerStartAddress >> 8;
-  query[3] = registerStartAddress & 0xFF;
-  // register length
-  query[4] = registerLength >> 8;
-  query[5] = registerLength & 0xFF;
-  // calculate last two bytes (CRC check)
-  calculateCRC(query, sizeof(query) - 2);
-  // #   Serial.print("Query (get data): 0x");                    #Print bytes
-  // #   printBytes(query, 8);
-  //  write the data request to the modbus line
-  write(query, sizeof(query));
-  // get response from sensor
-  return getResponse();
-}
-
-ErrorType SentecSensorRS485::writeRegister(int registerAddress, int value) {
-  // function code 0x06: change sensor settings
-  // e.g. a new address, reset rainfal data...
-
-  byte query[8];
-  query[0] = address;
-  // function code
-  query[1] = 0x06;
-  // register start address
-  query[2] = registerAddress >> 8;
-  query[3] = registerAddress & 0xFF;
-  // register length
-  query[4] = value >> 8;
-  query[5] = value & 0xFF;
-  calculateCRC(query, sizeof(query) - 2);
-  ESP_LOGD(TAG, "Query (settings):    ");
-  printBytes(query, 8);
-  write(query, sizeof(query));
-  return getResponse();
-}
-
-ErrorType SentecSensorRS485::setAddress(byte add) {
-  // change the address of a sensor
-  ErrorType tmp = writeRegister(word(0x07, 0xD0), add);
-  if (tmp == ErrorType::DATA_OK)
-    address = add;
-  // TODO check response: matches the sent message exactly
-  return tmp;
-}
-
-void SentecSensorRS485::resetAnswerFrame() {
-  for (unsigned char &i: answerFrame) {
-    i = 0;
-  }
-}
-
-ErrorType SentecSensorRS485::getResponse() {
-  // reads the response of a sensor
-  ErrorType returnCode = ErrorType::DATA_OK;
-  valid = true;
-  int idx = 0;
-  int byteReceived;
-  // usual response length: changed in the while loop to match the response,
-  //   changed only when reading data (then it's 7 or 9  bytes, sensor dpendent)
-  int responseLength = 8;
-  // reading an answer takes up to 39 milliseconds for 2 byte readRegister
-  const int timeout = 200;
-  const int retries = 1; // #editet to q
-  size_t tries = 1;
-  // it doesn't seem to help to request multiple times if first time goes wrong
-  for (tries; tries <= retries; tries++) {
-    // if we lose connection with the sensor, we get an array of zeros back
-    resetAnswerFrame();
-
-    unsigned long time = millis();
-    while (idx < responseLength && (millis() - time) < timeout) {
-      if (RS485->available()) {
-        byteReceived = RS485->read();
-        // Serial.println(byteReceived, HEX);
-        // check for first byte. It has to be the device address unless for broadcasts with address = 0xFF
-        if (idx == 0 && address != 0xFF && byteReceived != address) {
-          ESP_LOGE(TAG,
-                   "Invalid byte. First byte needs to be address 0x%02X but got 0x%02Xinstead");
-        } else {
-          answerFrame[idx] = byteReceived;
-          // for reading register: third received byte is data length, read number of bytes accordingly
-          if (idx == 2 && answerFrame[1] == 0x03) {
-            // 5 bytes for address, function code, data length, CRC_H, CRC_L
-            responseLength = 5 + byteReceived;
-          }
-          idx++;
-        }
-      }
-    }
-    ESP_LOGD(TAG,
-             "-----------------------------------------------------------------------------------------");
-    ESP_LOGD(TAG,
-             "Response: 0x%s",
-             formatBytes(answerFrame, responseLength).c_str());
-    // ESP_LOGD(TAG, "Tries: %d", tries);
-    // ESP_LOGD(TAG, "Bytes received: %d", idx);
-    if (answerFrame[0] == 0) {
-      ESP_LOGE(TAG, "Check sensor connection. First byte is 0x00.");
-      valid = false;
-      returnCode = ErrorType::SENSOR_NOT_CONNECTED;
-    } else if (idx < responseLength) {
-      ESP_LOGE(TAG,
-               "Response too short: %d bytes < %d bytes. Unfinished transmission.",
-               idx,
-               responseLength);
-      valid = false;
-      returnCode = ErrorType::CONNECTION_ENDED_PREMATURELY;
-    }
-    word crc_received =
-        word(answerFrame[responseLength - 2], answerFrame[responseLength - 1]);
-    word crc = calculateCRC(answerFrame, responseLength - 2);
-    if (valid && crc_received != word(crc)) {
-      ESP_LOGE(TAG,
-               "CRC wrong: Expected 0x%s got 0x%s",
-               formatBytes(crc).c_str(),
-               formatBytes(crc_received).c_str());
-      valid = false;
-      returnCode = ErrorType::WRONG_CRC;
-    }
-    // breaking after first successfull try
-    if (returnCode == ErrorType::DATA_OK) {
-      break;
-    }
-  }
-
-  // ESP_LOGE(TAG, "Returncode: %d", returnCode);
-  return returnCode;
-}
-
-unsigned int SentecSensorRS485::calculateCRC(byte query[], int length) {
-  // Change the last two bytes of the queryFrame to conform to a CRC check
-  // Yes, this is necessary. No, I don't know exactly what it does.
-  unsigned int tmp1, tmp2, flag;
-  tmp1 = 0xFFFF;
-  for (unsigned char i = 0; i < length; i++) {
-    tmp1 = tmp1 ^ query[i];
-    for (unsigned char j = 1; j <= 8; j++) {
-      flag = tmp1 & 0x0001;
-      tmp1 >>= 1;
-      if (flag)
-        tmp1 ^= 0xA001;
-    }
-  }
-  // Reverse byte order.
-  tmp2 = tmp1 >> 8;
-  tmp1 = (tmp1 << 8) | tmp2;
-  tmp1 &= 0xFFFF;
-  // change last two query bytes
-  query[length + 1] = tmp1;
-  query[length] = tmp1 >> 8;
-
-  return tmp1; // the returned value is already swapped - CRC_L byte is first & CRC_H byte is last
-}
-
-void SentecSensorRS485::printBytes(byte *data, int length) {
-  // prints 8-bit data in hex with leading zeroes
-  char tmp[16];
-  for (int i = 0; i < length; i++) {
-    sprintf(tmp, "%.2X", data[i]);
-    // sprintf(tmp, "0x%.2X",data[i]);
-    Serial.print(tmp);
-    Serial.print(" ");
-  }
-  Serial.println();
-}
-
-void SentecSensorRS485::printBytes(word data) {
-  char tmp[10];
-  sprintf(tmp, "0x%.2X", data >> 8);
-  Serial.print(tmp);
-  sprintf(tmp, "%.2X", data & 0xFF);
-  // sprintf(tmp, "0x%.2X",data[i]);
-  Serial.print(tmp);
-}
-
-std::string SentecSensorRS485::formatBytes(byte *data, int length) {
-  // pls don't hate me for building strings like that.
-  // I also wish that I would be good at programming
-  std::string tmp;
-  // prints 8-bit data in hex with leading zeroes
-  char buff[4];
-  for (int i = 0; i < length; i++) {
-    snprintf(buff, sizeof(buff), "%02X ", data[i]);
-    tmp += buff;
-  }
-  return tmp;
-}
-
-std::string SentecSensorRS485::formatBytes(word data) {
-  byte arr[2] = {static_cast<byte>(data >> 8), static_cast<byte>(data & 0xFF)};
-  return formatBytes(arr, 2);
-}
-
diff --git a/client/libs/SentecSensors/SentecSensors.h b/client/libs/SentecSensors/SentecSensors.h
deleted file mode 100644
index fbef376..0000000
--- a/client/libs/SentecSensors/SentecSensors.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef SENTECSENSORS_H
-#define SENTECSENSORS_H
-
-#include <Arduino.h>
-#include <ErrorTypes.h>
-#include <HardwareNames.h>
-#include <SensorInformation.hpp>
-
-class SentecSensorRS485 {
- public:
-  byte address;
-  uint8_t serialCommunicationControlPin = 19;
-  HardwareSerial *RS485;
-  byte answerFrame[10];
-  // TODO use valid flag to log None
-  bool valid = false;
-
-  SentecSensorRS485(HardwareSerial *ser, byte add);
-
-  SentecSensorRS485(HardwareSerial *ser, byte add, uint8_t serialControlPin);
-
-  void write(byte queryFrame[], int length);
-
-  String getValueStr(float value);
-
-  String getValueStr(int value);
-
-  ErrorType queryAddress();
-
-  ErrorType readRegister(int registerStartAddress);
-
-  ErrorType readRegister(int registerStartAddress, int registerLength);
-
-  ErrorType writeRegister(int registerAddress, int value);
-
-  ErrorType setAddress(byte add);
-
-  void resetAnswerFrame();
-
-  ErrorType getResponse();
-
-  unsigned int calculateCRC(byte query[], int length);
-
-  void printBytes(byte *data, int length);
-
-  void printBytes(word data);
-
-  std::string formatBytes(byte *data, int length);
-
-  std::string formatBytes(word data);
-
-};
-
-#endif
diff --git a/client/libs/SentecSensors/SentecSensorsRS485.cpp b/client/libs/SentecSensors/SentecSensorsRS485.cpp
new file mode 100644
index 0000000..583c799
--- /dev/null
+++ b/client/libs/SentecSensors/SentecSensorsRS485.cpp
@@ -0,0 +1,248 @@
+#include "NoDataAvailableException.hpp"
+#include <SentecSensorsRS485.h>
+
+/***************************************
+ *          RS485 SENSOR READOUT
+ ****************************************/
+static const char *TAG = "SENTEC";
+
+SentecSensorRS485::SentecSensorRS485(byte add) {
+    address = add;
+}
+
+SentecSensorRS485::SentecSensorRS485(byte add, uint8_t serialControlPin) {
+    address = add;
+    serialCommunicationControlPin = serialControlPin;
+}
+
+void SentecSensorRS485::write(byte queryFrame[], int length) {
+    // sends a message (bytes) to the sensor
+
+    // Initialize the transmitter
+    digitalWrite(serialCommunicationControlPin, HIGH);
+    // Send message: request a reading from the sensor
+    RS485->write(queryFrame, length);
+    RS485->flush();
+    // Initialize the receiver
+    digitalWrite(serialCommunicationControlPin, LOW);
+}
+
+String SentecSensorRS485::getValueStr(float value) {
+    if (valid) {
+        return String(value, 1);
+    } else {
+        return {"null"};
+    }
+}
+
+String SentecSensorRS485::getValueStr(int value) {
+    if (valid) {
+        return String(value);
+    } else {
+        return {"null"};
+    }
+}
+
+ErrorType SentecSensorRS485::queryAddress() {
+    // request the address of the sensor with ONLY ONE SENSOR ON THE BUS
+
+    byte tmp_addr = address; // store the address in a temporary byte
+    address = 0xFF;          // change the address to FF (0) for address check
+    ErrorType tmp = readRegister(word(0x07, 0xD0), 2);
+    address = tmp_addr; // set the original address back
+    return tmp;
+}
+
+ErrorType SentecSensorRS485::readRegister(int registerStartAddress) {
+    return readRegister(registerStartAddress, 1);
+}
+
+ErrorType SentecSensorRS485::readRegister(int registerStartAddress, int registerLength) {
+    // function code 0x03: get data measured by the sensor
+    byte query[8];
+    query[0] = address;
+    // function code
+    query[1] = 0x03;
+    // register start address
+    query[2] = registerStartAddress >> 8;
+    query[3] = registerStartAddress & 0xFF;
+    // register length
+    query[4] = registerLength >> 8;
+    query[5] = registerLength & 0xFF;
+    // calculate last two bytes (CRC check)
+    calculateCRC(query, sizeof(query) - 2);
+    // #   Serial.print("Query (get data): 0x");                    #Print bytes
+    // #   printBytes(query, 8);
+    //  write the data request to the modbus line
+    write(query, sizeof(query));
+    // get response from sensor
+    return getResponse();
+}
+
+WriteRegisterReturnType SentecSensorRS485::writeRegister(int registerAddress, int value) {
+    // function code 0x06: change sensor settings
+    // e.g. a new address, reset rainfal data...
+
+    byte query[8];
+    query[0] = address;
+    // function code
+    query[1] = 0x06;
+    // register start address
+    query[2] = registerAddress >> 8;
+    query[3] = registerAddress & 0xFF;
+    // register length
+    query[4] = value >> 8;
+    query[5] = value & 0xFF;
+    calculateCRC(query, sizeof(query) - 2);
+    ESP_LOGD(TAG, "Query (settings):    ");
+    printBytes(query, 8);
+    write(query, sizeof(query));
+    return {getResponse(), {query[0], query[1], query[2], query[3], query[4], query[5], query[6], query[7]}};
+}
+
+ErrorType SentecSensorRS485::setAddress(byte add) {
+    // change the address of a sensor
+    WriteRegisterReturnType tmp = writeRegister(word(0x07, 0xD0), add);
+    if (tmp.errorType == ErrorType::DATA_OK)
+        address = add;
+    // TODO check response: matches the sent message exactly
+    return tmp.errorType;
+}
+
+void SentecSensorRS485::resetAnswerFrame() {
+    for (unsigned char &i : answerFrame) {
+        i = 0;
+    }
+}
+
+ErrorType SentecSensorRS485::getResponse() {
+    // reads the response of a sensor
+    ErrorType returnCode = ErrorType::DATA_OK;
+    valid = true;
+    int idx = 0;
+    int byteReceived;
+    // usual response length: changed in the while loop to match the response,
+    //   changed only when reading data (then it's 7 or 9  bytes, sensor dpendent)
+    int responseLength = 8;
+    // reading an answer takes up to 39 milliseconds for 2 byte readRegister
+    const int timeout = 2000;
+    const int retries = 1; // #editet to q
+    size_t tries = 1;
+    // it doesn't seem to help to request multiple times if first time goes wrong
+    for (tries; tries <= retries; tries++) {
+        // if we lose connection with the sensor, we get an array of zeros back
+        resetAnswerFrame();
+
+        unsigned long time = millis();
+        while (idx < responseLength && (millis() - time) < timeout) {
+            if (RS485->available()) {
+                byteReceived = RS485->read();
+                // Serial.println(byteReceived, HEX);
+                // check for first byte. It has to be the device address unless for broadcasts with address = 0xFF
+                if (idx == 0 && address != 0xFF && byteReceived != address) {
+                    ESP_LOGE(TAG, "Invalid byte. First byte needs to be address 0x%02X but got 0x%02Xinstead");
+                } else {
+                    answerFrame[idx] = byteReceived;
+                    // for reading register: third received byte is data length, read number of bytes accordingly
+                    if (idx == 2 && answerFrame[1] == 0x03) {
+                        // 5 bytes for address, function code, data length, CRC_H, CRC_L
+                        responseLength = 5 + byteReceived;
+                    }
+                    idx++;
+                }
+            }
+        }
+        ESP_LOGD(TAG, "-----------------------------------------------------------------------------------------");
+        ESP_LOGD(TAG, "Response: 0x%s", formatBytes(answerFrame, responseLength).c_str());
+        // ESP_LOGD(TAG, "Tries: %d", tries);
+        // ESP_LOGD(TAG, "Bytes received: %d", idx);
+        if (answerFrame[0] == 0) {
+            ESP_LOGE(TAG, "Check sensor connection. First byte is 0x00.");
+            valid = false;
+            returnCode = ErrorType::SENSOR_NOT_CONNECTED;
+        } else if (idx < responseLength) {
+            ESP_LOGE(TAG, "Response too short: %d bytes < %d bytes. Unfinished transmission.", idx, responseLength);
+            valid = false;
+            returnCode = ErrorType::CONNECTION_ENDED_PREMATURELY;
+        }
+        word crc_received = word(answerFrame[responseLength - 2], answerFrame[responseLength - 1]);
+        word crc = calculateCRC(answerFrame, responseLength - 2);
+        if (valid && crc_received != word(crc)) {
+            ESP_LOGE(TAG, "CRC wrong: Expected 0x%s got 0x%s", formatBytes(crc).c_str(),
+                     formatBytes(crc_received).c_str());
+            valid = false;
+            returnCode = ErrorType::WRONG_CRC;
+        }
+        // breaking after first successfull try
+        if (returnCode == ErrorType::DATA_OK) {
+            break;
+        }
+    }
+
+    // ESP_LOGE(TAG, "Returncode: %d", returnCode);
+    return returnCode;
+}
+
+unsigned int SentecSensorRS485::calculateCRC(byte query[], int length) {
+    // Change the last two bytes of the queryFrame to conform to a CRC check
+    // Yes, this is necessary. No, I don't know exactly what it does.
+    unsigned int tmp1, tmp2, flag;
+    tmp1 = 0xFFFF;
+    for (unsigned char i = 0; i < length; i++) {
+        tmp1 = tmp1 ^ query[i];
+        for (unsigned char j = 1; j <= 8; j++) {
+            flag = tmp1 & 0x0001;
+            tmp1 >>= 1;
+            if (flag)
+                tmp1 ^= 0xA001;
+        }
+    }
+    // Reverse byte order.
+    tmp2 = tmp1 >> 8;
+    tmp1 = (tmp1 << 8) | tmp2;
+    tmp1 &= 0xFFFF;
+    // change last two query bytes
+    query[length + 1] = tmp1;
+    query[length] = tmp1 >> 8;
+
+    return tmp1; // the returned value is already swapped - CRC_L byte is first & CRC_H byte is last
+}
+
+void SentecSensorRS485::printBytes(byte *data, int length) {
+    // prints 8-bit data in hex with leading zeroes
+    char tmp[16];
+    for (int i = 0; i < length; i++) {
+        sprintf(tmp, "%.2X", data[i]);
+        // sprintf(tmp, "0x%.2X",data[i]);
+        Serial.print(tmp);
+        Serial.print(" ");
+    }
+    Serial.println();
+}
+
+void SentecSensorRS485::printBytes(word data) {
+    char tmp[10];
+    sprintf(tmp, "0x%.2X", data >> 8);
+    Serial.print(tmp);
+    sprintf(tmp, "%.2X", data & 0xFF);
+    // sprintf(tmp, "0x%.2X",data[i]);
+    Serial.print(tmp);
+}
+
+std::string SentecSensorRS485::formatBytes(byte *data, int length) {
+    // pls don't hate me for building strings like that.
+    // I also wish that I would be good at programming
+    std::string tmp;
+    // prints 8-bit data in hex with leading zeroes
+    char buff[4];
+    for (int i = 0; i < length; i++) {
+        snprintf(buff, sizeof(buff), "%02X ", data[i]);
+        tmp += buff;
+    }
+    return tmp;
+}
+
+std::string SentecSensorRS485::formatBytes(word data) {
+    byte arr[2] = {static_cast<byte>(data >> 8), static_cast<byte>(data & 0xFF)};
+    return formatBytes(arr, 2);
+}
diff --git a/client/libs/SentecSensors/SentecSensorsRS485.h b/client/libs/SentecSensors/SentecSensorsRS485.h
new file mode 100644
index 0000000..bedb9b9
--- /dev/null
+++ b/client/libs/SentecSensors/SentecSensorsRS485.h
@@ -0,0 +1,60 @@
+#ifndef SENTECSENSORS_H
+#define SENTECSENSORS_H
+
+#include <Arduino.h>
+#include <ErrorTypes.h>
+#include <HardwareNames.h>
+#include <RS485HardwareSerial.h>
+#include <SensorInformation.hpp>
+#include <memory>
+
+struct WriteRegisterReturnType {
+    ErrorType errorType;
+    byte writtenQuery[8];
+};
+
+class SentecSensorRS485 {
+  public:
+    byte address;
+    uint8_t serialCommunicationControlPin = 19;
+    std::shared_ptr<HardwareSerial> RS485 = RS485HardwareSerial::getInstance().getRS485Serial();
+    byte answerFrame[10] = {0};
+    // TODO use valid flag to log None
+    bool valid = false;
+
+    SentecSensorRS485(byte add);
+
+    SentecSensorRS485(byte add, uint8_t serialControlPin);
+
+    void write(byte queryFrame[], int length);
+
+    String getValueStr(float value);
+
+    String getValueStr(int value);
+
+    ErrorType queryAddress();
+
+    ErrorType readRegister(int registerStartAddress);
+
+    ErrorType readRegister(int registerStartAddress, int registerLength);
+
+    WriteRegisterReturnType writeRegister(int registerAddress, int value);
+
+    ErrorType setAddress(byte add);
+
+    void resetAnswerFrame();
+
+    ErrorType getResponse();
+
+    unsigned int calculateCRC(byte query[], int length);
+
+    void printBytes(byte *data, int length);
+
+    void printBytes(word data);
+
+    std::string formatBytes(byte *data, int length);
+
+    std::string formatBytes(word data);
+};
+
+#endif
diff --git a/client/libs/SentecSensors/SentecSolarRadiationSensor.cpp b/client/libs/SentecSensors/SentecSolarRadiationSensor.cpp
index 68fd38c..7082a2c 100644
--- a/client/libs/SentecSensors/SentecSolarRadiationSensor.cpp
+++ b/client/libs/SentecSensors/SentecSolarRadiationSensor.cpp
@@ -4,31 +4,29 @@
 
 #include "SentecSolarRadiationSensor.h"
 
-
-String SolarRadiationSensor::getSolarRadiationStr() {
-  return getValueStr((int) glob);
+String SEM228A::getSolarRadiationStr() {
+    return getValueStr((int)glob);
 }
 
-void SolarRadiationSensor::setup() {
-  // TODO: check if rs485 serial is active
+void SEM228A::setup() {
+    // TODO: check if rs485 serial is active
 }
 
-out_data_solar_radiation SolarRadiationSensor::readData() {
-  auto error = readRegister(0, 1);
-  glob = word(answerFrame[3], answerFrame[4]);
-  ESP_LOGI("SolarRadiation", "Global solar radiation: %d W/m^2", glob);
-  return {static_cast<float>(glob), error};
+out_data_solar_radiation SEM228A::readData() {
+    auto error = readRegister(0, 1);
+    glob = word(answerFrame[3], answerFrame[4]);
+    ESP_LOGI("SolarRadiation", "Global solar radiation: %d W/m^2", glob);
+    return {static_cast<float>(glob), error};
 }
-std::list<Message> SolarRadiationSensor::buildMessages() {
-  auto messages = std::list<Message>();
-  auto data = readData();
-  Measurement solarRadiation{data.solarRadiation, address, NO_I2C_ADDRESS,
-                             MeasurementType::SOLAR_RADIATION, data.solarError};
-  messages.emplace_back(Message{solarRadiation, getSensorInformation(),
-                                Time::getInstance().getEpochSeconds()});
-  return messages;
+std::list<Message> SEM228A::buildMessages() {
+    auto messages = std::list<Message>();
+    auto data = readData();
+    Measurement solarRadiation{data.solarRadiation, address, NO_I2C_ADDRESS, MeasurementType::SOLAR_RADIATION,
+                               data.solarError};
+    messages.emplace_back(Message{solarRadiation, getSensorInformation(), Time::getInstance().getEpochSeconds()});
+    return messages;
 }
 
-SensorInformation SolarRadiationSensor::getSensorInformation() const {
-  return SensorInformation(HardwareName::SEM228A, SensorProtocol::RS485);
+SensorInformation SEM228A::getSensorInformation() const {
+    return SensorInformation(HardwareName::SEM228A, SensorProtocol::RS485);
 }
diff --git a/client/libs/SentecSensors/SentecSolarRadiationSensor.h b/client/libs/SentecSensors/SentecSolarRadiationSensor.h
index de4614d..3942914 100644
--- a/client/libs/SentecSensors/SentecSolarRadiationSensor.h
+++ b/client/libs/SentecSensors/SentecSolarRadiationSensor.h
@@ -5,28 +5,28 @@
 #ifndef CLIENT_CENTRAL_MAST_SENTECSOLARRADIATIONSENSOR_H
 #define CLIENT_CENTRAL_MAST_SENTECSOLARRADIATIONSENSOR_H
 
+#include "SentecSensorsRS485.h"
 #include <ForteSensor.hpp>
-#include "SentecSensors.h"
 
 struct out_data_solar_radiation {
-  float solarRadiation;
-  ErrorType solarError;
+    float solarRadiation;
+    ErrorType solarError;
 };
 
-class SolarRadiationSensor: public SentecSensorRS485, public ForteSensor<out_data_solar_radiation> {
- public:
-  using SentecSensorRS485::SentecSensorRS485;
+class SEM228A : public SentecSensorRS485, public ForteSensor<out_data_solar_radiation> {
+  public:
+    using SentecSensorRS485::SentecSensorRS485;
 
-  // global radiation [W/m^2]
-  word glob = 0;
+    SEM228A() : SentecSensorRS485(0) {}
+    // global radiation [W/m^2]
+    word glob = 0;
 
-  void setup() override;
-  out_data_solar_radiation readData() override;
-  std::list<Message> buildMessages() override;
-  [[nodiscard]] SensorInformation getSensorInformation() const override;
-
-  String getSolarRadiationStr();
+    void setup() override;
+    out_data_solar_radiation readData() override;
+    std::list<Message> buildMessages() override;
+    [[nodiscard]] SensorInformation getSensorInformation() const override;
 
+    String getSolarRadiationStr();
 };
 
-#endif //CLIENT_CENTRAL_MAST_SENTECSOLARRADIATIONSENSOR_H
+#endif // CLIENT_CENTRAL_MAST_SENTECSOLARRADIATIONSENSOR_H
diff --git a/client/libs/rs485/RS485HardwareSerial.h b/client/libs/rs485/RS485HardwareSerial.h
new file mode 100644
index 0000000..0b6fbd1
--- /dev/null
+++ b/client/libs/rs485/RS485HardwareSerial.h
@@ -0,0 +1,47 @@
+//
+// Created by zoe on 3/8/23.
+//
+
+#ifndef CLIENT_CENTRAL_MAST_RS485HARDWARESERIAL_H
+#define CLIENT_CENTRAL_MAST_RS485HARDWARESERIAL_H
+
+
+#include <HardwareSerial.h>
+#include <memory>
+class RS485HardwareSerial {
+ public:
+  static RS485HardwareSerial &getInstance()
+  {
+    static RS485HardwareSerial instance; // Guaranteed to be destroyed.
+    // Instantiated on first use.
+    return instance;
+  }
+
+    std::shared_ptr<HardwareSerial> getRS485Serial() {
+        return RS485Serial;
+    }
+
+ private:
+  RS485HardwareSerial() {} // Constructor? (the {} brackets) are needed here.
+
+  const std::shared_ptr<HardwareSerial> RS485Serial = std::make_shared<HardwareSerial>(Serial2);
+
+
+  // C++ 11
+  // =======
+  // We can use the better technique of deleting the methods
+  // we don't want.
+ public:
+  RS485HardwareSerial(RS485HardwareSerial const &) = delete;
+  void operator=(RS485HardwareSerial const &) = delete;
+
+  // Note: Scott Meyers mentions in his Effective Modern
+  //       C++ book, that deleted functions should generally
+  //       be public as it results in better error messages
+  //       due to the compilers behavior to check accessibility
+  //       before deleted status
+
+};
+
+
+#endif //CLIENT_CENTRAL_MAST_RS485HARDWARESERIAL_H
diff --git a/client/libs/rs485/rs485.cpp b/client/libs/rs485/rs485.cpp
index 20ec4fa..b2ec8b6 100644
--- a/client/libs/rs485/rs485.cpp
+++ b/client/libs/rs485/rs485.cpp
@@ -2,7 +2,7 @@
 #include "SentecRainGaugeSensor.h"
 #include "SentecSolarRadiationSensor.h"
 // RS485 control
-#define RS485Serial Serial2
+//#define RS485Serial Serial2
 #define RXPin 14 // Serial Receive pin
 #define TXPin 15 // Serial Transmit pin
 
@@ -14,59 +14,61 @@
 static const char *TAG = "RS485";
 
 // Configure sensors
-SolarRadiationSensor solarSensor(&RS485Serial, 1, RE_DE_PIN);
-RainGaugeSensor rainGauge = RainGaugeSensor(&RS485Serial,
-                                            2,
-                                            RE_DE_PIN);         // Give 2 Sensor Adress 2
+SEM228A solarSensor(1, RE_DE_PIN);
+SEM404 rainGauge = SEM404(2, RE_DE_PIN);
 
 void Forte_RS485::setup() {
-  // configure the pins to be output only
-  pinMode(RE_DE_PIN, OUTPUT);
-  pinMode(POWER_SWITCH_PIN_12V, OUTPUT);
-  pinMode(POWER_SWITCH_PIN_5V, OUTPUT);
-  RS485Serial.begin(4800, SERIAL_8N1, TXPin, RXPin);
+    RS485Serial = RS485HardwareSerial::getInstance().getRS485Serial();
+    // configure the pins to be output only
+    pinMode(RE_DE_PIN, OUTPUT);
+    pinMode(POWER_SWITCH_PIN_12V, OUTPUT);
+    pinMode(POWER_SWITCH_PIN_5V, OUTPUT);
+    RS485Serial->begin(4800, SERIAL_8N1, TXPin, RXPin);
+}
+
+void Forte_RS485::teardown() {
+    RS485Serial->end();
 }
 
 out_data_rs485 Forte_RS485::readData() {
-  // Power on sensor
-  digitalWrite(POWER_SWITCH_PIN_12V, HIGH);
-  digitalWrite(POWER_SWITCH_PIN_5V, HIGH);
-  // Wait for sensors to power up
-  // TODO minimize delay
-  delay(300);
-  out_data_rs485 output;
-  unsigned long ts = millis();
-  output.solar = solarSensor.readData();
-  output.precipitation = rainGauge.readData();
-  digitalWrite(POWER_SWITCH_PIN_12V, LOW);
-  digitalWrite(POWER_SWITCH_PIN_5V, LOW);
+    powerOnRS485Sensors();
 
-  gpio_hold_en((gpio_num_t) POWER_SWITCH_PIN_12V);
-  gpio_hold_en((gpio_num_t) POWER_SWITCH_PIN_5V);
-  return output;
+    out_data_rs485 output{};
+    output.solar = solarSensor.readData();
+    output.precipitation = rainGauge.readData();
+
+    powerOffRS485Sensors();
+    return output;
 }
+void Forte_RS485::powerOffRS485Sensors() {
+    digitalWrite(POWER_SWITCH_PIN_12V, LOW);
+    digitalWrite(POWER_SWITCH_PIN_5V, LOW);
 
-std::list<Message> Forte_RS485::buildMessages() {
-  std::list<Message> messages;
-  out_data_rs485 output = readData();
+    gpio_hold_en((gpio_num_t)POWER_SWITCH_PIN_12V);
+    gpio_hold_en((gpio_num_t)POWER_SWITCH_PIN_5V);
+}
+void Forte_RS485::powerOnRS485Sensors() { // Power on sensor
+    digitalWrite(POWER_SWITCH_PIN_12V, HIGH);
+    digitalWrite(POWER_SWITCH_PIN_5V, HIGH); // Wait for sensors to power up
+    // TODO minimize delay
+    delay(2000);
+}
 
-  Measurement solarRadiation{output.solar.solarRadiation, 1, NO_I2C_ADDRESS,
-                             MeasurementType::SOLAR_RADIATION,
-                             output.solar.solarError};
-  Measurement precipitation
-      {output.precipitation.precipitation, 2, NO_I2C_ADDRESS,
-       MeasurementType::PRECIPITATION, output.precipitation.precipitationError};
+std::list<Message> Forte_RS485::buildMessages() {
+    std::list<Message> messages;
+    out_data_rs485 output = readData();
 
+    Measurement solarRadiation{output.solar.solarRadiation, 1, NO_I2C_ADDRESS, MeasurementType::SOLAR_RADIATION,
+                               output.solar.solarError};
+    Measurement precipitation{output.precipitation.precipitation, 2, NO_I2C_ADDRESS, MeasurementType::PRECIPITATION,
+                              output.precipitation.precipitationError};
 
-  messages.emplace_back(solarRadiation, sensorInformation,
-                        Time::getInstance().getEpochSeconds());
-  messages.emplace_back(precipitation,
-                        sensorInformation,
-                        Time::getInstance().getEpochSeconds());
+    messages.emplace_back(solarRadiation, sensorInformation, Time::getInstance().getEpochSeconds());
+    messages.emplace_back(precipitation, sensorInformation, Time::getInstance().getEpochSeconds());
 
-  return messages;
+    return messages;
 }
 
 SensorInformation Forte_RS485::getSensorInformation() const {
-  return sensorInformation;
+    return sensorInformation;
 }
\ No newline at end of file
diff --git a/client/libs/rs485/rs485.hpp b/client/libs/rs485/rs485.hpp
index 01c39b4..00d4802 100644
--- a/client/libs/rs485/rs485.hpp
+++ b/client/libs/rs485/rs485.hpp
@@ -3,10 +3,11 @@
 
 #include <MeasurementTypes.h>
 #include <SentecSolarRadiationSensor.h>
+#include <RS485HardwareSerial.h>
 #include <SentecRainGaugeSensor.h>
 #include "Message.hpp"
 #include "ForteSensor.hpp"
-#include "SentecSensors.h"
+#include "SentecSensorsRS485.h"
 
 struct out_data_rs485 {
   out_data_solar_radiation solar;
@@ -19,8 +20,14 @@ class Forte_RS485: public ForteSensor<out_data_rs485> {
   out_data_rs485 readData() override;
   std::list<Message> buildMessages() override;
   [[nodiscard]] SensorInformation getSensorInformation() const override;
+  void teardown();
+  static void powerOnRS485Sensors();
+  static void powerOffRS485Sensors();
 
  private:
+
+  std::shared_ptr<HardwareSerial> RS485Serial;
+
   const SensorInformation
       sensorInformation{HardwareName::RS485, SensorProtocol::RS485};
 
diff --git a/shared-libs/DataTransfer/ErrorTypes.cpp b/shared-libs/DataTransfer/ErrorTypes.cpp
index 8526b71..726808c 100644
--- a/shared-libs/DataTransfer/ErrorTypes.cpp
+++ b/shared-libs/DataTransfer/ErrorTypes.cpp
@@ -5,37 +5,39 @@
 #include "ErrorTypes.h"
 
 namespace ErrorTypes {
-    // INFO: If you add a new error type, add it here and to the documentation at https://git.uibk.ac.at/informatik/qe/forte/sensor-system/-/wikis/Error-Types as well
-    std::string errorTypeToString(ErrorType errorType) {
-        switch (errorType) {
-            case ErrorType::SENSOR_NOT_FOUND:
-                return "SENSOR_NOT_FOUND";
-            case ErrorType::SENSOR_NOT_CONNECTED:
-                return "SENSOR_NOT_CONNECTED";
-            case ErrorType::NO_DATA:
-                return "NO_DATA";
-            case ErrorType::DATA_OK:
-                return "DATA_OK";
-            case ErrorType::NULL_MESSAGE:
-                return "NULL_MESSAGE";
-            case ErrorType::SENSOR_DOES_NOT_RETURN_DATA:
-                return "SENSOR_DOES_NOT_RETURN_DATA";
-            case ErrorType::BATTERY_VOLTAGE_TOO_LOW:
-                return "BATTERY_VOLTAGE_TOO_LOW";
-            case ErrorType::INVALID_VALUE:
-                return "INVALID_VALUE";
-            case ErrorType::SENSOR_INIT_FAILED:
-                return "SENSOR_INIT_FAILED";
-            case ErrorType::INA219_OVERFLOW:
-                return "INA219_OVERFLOW";
-            case ErrorType::CONNECTION_ENDED_PREMATURELY:
-                return "CONNECTION_ENDED_PREMATURELY";
-            case ErrorType::WRONG_CRC:
-                return "WRONG_CRC";
-            case ErrorType::UNKNOWN:
-            default:
-                return "UNKNOWN_ERROR_TYPE";
-        }
-    }
+// INFO: If you add a new error type, add it here and to the documentation at https://git.uibk.ac.at/informatik/qe/forte/sensor-system/-/wikis/Error-Types as well
+std::string errorTypeToString(ErrorType errorType) {
+  switch (errorType) {
+    case ErrorType::SENSOR_NOT_FOUND:
+      return "SENSOR_NOT_FOUND";
+    case ErrorType::SENSOR_NOT_CONNECTED:
+      return "SENSOR_NOT_CONNECTED";
+    case ErrorType::NO_DATA:
+      return "NO_DATA";
+    case ErrorType::DATA_OK:
+      return "DATA_OK";
+    case ErrorType::NULL_MESSAGE:
+      return "NULL_MESSAGE";
+    case ErrorType::SENSOR_DOES_NOT_RETURN_DATA:
+      return "SENSOR_DOES_NOT_RETURN_DATA";
+    case ErrorType::BATTERY_VOLTAGE_TOO_LOW:
+      return "BATTERY_VOLTAGE_TOO_LOW";
+    case ErrorType::INVALID_VALUE:
+      return "INVALID_VALUE";
+    case ErrorType::SENSOR_INIT_FAILED:
+      return "SENSOR_INIT_FAILED";
+    case ErrorType::INA219_OVERFLOW:
+      return "INA219_OVERFLOW";
+    case ErrorType::CONNECTION_ENDED_PREMATURELY:
+      return "CONNECTION_ENDED_PREMATURELY";
+    case ErrorType::WRONG_CRC:
+      return "WRONG_CRC";
+    case ErrorType::SEM404_COULD_NOT_RESET_PRECIPITATION:
+      return "SEM404_COULD_NOT_RESET_PRECIPITATION";
+    case ErrorType::UNKNOWN:
+    default:
+      return "UNKNOWN_ERROR_TYPE";
+  }
+}
 
 }
\ No newline at end of file
diff --git a/shared-libs/DataTransfer/ErrorTypes.h b/shared-libs/DataTransfer/ErrorTypes.h
index 3d12cbd..f24944a 100644
--- a/shared-libs/DataTransfer/ErrorTypes.h
+++ b/shared-libs/DataTransfer/ErrorTypes.h
@@ -7,24 +7,25 @@
 
 #include <string>
 
-enum class ErrorType : char {
-    SENSOR_NOT_FOUND,
-    SENSOR_INIT_FAILED,
-    SENSOR_NOT_CONNECTED,
-    NO_DATA,
-    DATA_OK,
-    NULL_MESSAGE, // message that is sent as padding, should be thrown away
-    SENSOR_DOES_NOT_RETURN_DATA,
-    BATTERY_VOLTAGE_TOO_LOW,
-    INVALID_VALUE,
-    INA219_OVERFLOW,
-    CONNECTION_ENDED_PREMATURELY, // connection ended prematurely
-    WRONG_CRC, // corrupted data package
-    UNKNOWN,
+enum class ErrorType: char {
+  SENSOR_NOT_FOUND,
+  SENSOR_INIT_FAILED,
+  SENSOR_NOT_CONNECTED,
+  SEM404_COULD_NOT_RESET_PRECIPITATION,
+  NO_DATA,
+  DATA_OK,
+  NULL_MESSAGE, // message that is sent as padding, should be thrown away
+  SENSOR_DOES_NOT_RETURN_DATA,
+  BATTERY_VOLTAGE_TOO_LOW,
+  INVALID_VALUE,
+  INA219_OVERFLOW,
+  CONNECTION_ENDED_PREMATURELY, // connection ended prematurely
+  WRONG_CRC, // corrupted data package
+  UNKNOWN,
 };
 
 namespace ErrorTypes {
-    std::string errorTypeToString(ErrorType errorType);
+std::string errorTypeToString(ErrorType errorType);
 }
 
 
-- 
GitLab