Skip to content
Snippets Groups Projects
Verified Commit 89b4ed72 authored by Zoe Michaela Dietmar Pfister's avatar Zoe Michaela Dietmar Pfister :gay_pride_flag:
Browse files

WIP: sentec sensor refactor

parent 30e6eb2e
No related branches found
No related tags found
2 merge requests!39Merge Develop into Main,!30Renamed Sentec Sensors, made RS485 serial connection a singleton shared...
Pipeline #102081 failed
...@@ -88,7 +88,7 @@ void setup() { ...@@ -88,7 +88,7 @@ void setup() {
// auto messages3 = dr26_channel3.buildMessages(); // auto messages3 = dr26_channel3.buildMessages();
// auto messages4 = battery_monitor.buildMessages(); // auto messages4 = battery_monitor.buildMessages();
ForteRS485::powerOnRS485Sensors(); ForteRS485::powerOnRS485Sensors();
auto soilMessage = rainGaugeSensor.buildMessages(); auto rainMessage = rainGaugeSensor.buildMessages();
ForteRS485::powerOffRS485Sensors(); ForteRS485::powerOffRS485Sensors();
// roughly takes 500ms, ~120ms for each adc channel, barely anything for battery monitor // roughly takes 500ms, ~120ms for each adc channel, barely anything for battery monitor
ESP_LOGD(TAG, "Reading data and building messages took %ld ms", millis() - ts); ESP_LOGD(TAG, "Reading data and building messages took %ld ms", millis() - ts);
...@@ -96,9 +96,9 @@ void setup() { ...@@ -96,9 +96,9 @@ void setup() {
// std::array<Message, 6> messages = // std::array<Message, 6> messages =
// {messages0.front(), messages1.front(), messages2.front(), // {messages0.front(), messages1.front(), messages2.front(),
// messages3.front(), messages4.front(), soilMessage.front()}; // messages3.front(), messages4.front(), rainMessage.front()};
std::array<Message, 6> messages = {Message::nullMessage(), Message::nullMessage(), Message::nullMessage(), std::array<Message, 6> messages = {Message::nullMessage(), Message::nullMessage(), Message::nullMessage(),
Message::nullMessage(), Message::nullMessage(), soilMessage.front()}; Message::nullMessage(), Message::nullMessage(), rainMessage.front()};
ts = millis(); ts = millis();
espnow_setup(); espnow_setup();
......
...@@ -6,18 +6,6 @@ ...@@ -6,18 +6,6 @@
static const char *TAG = "SEM404"; 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() { ErrorType SEM404::resetPrecipitation() {
// clears rainfall rata from the rain gauge // clears rainfall rata from the rain gauge
// delay resetting after the last register reading // delay resetting after the last register reading
......
...@@ -47,14 +47,5 @@ class SEM404 : public SentecSensorRS485, ForteSensor<out_data_rain_gauge> { ...@@ -47,14 +47,5 @@ class SEM404 : public SentecSensorRS485, ForteSensor<out_data_rain_gauge> {
out_data_rain_gauge readData() override; out_data_rain_gauge readData() override;
std::list<Message> buildMessages() override; std::list<Message> buildMessages() override;
[[nodiscard]] SensorInformation getSensorInformation() const override; [[nodiscard]] SensorInformation getSensorInformation() const override;
private:
/**
* @brief Check if the answer frame is equal to the query frame
* @param answerFrame The frame returned from the device
* @param queryFrame The frame sent to the device
* @return true if the answer frame is equal to the query frame
*/
bool isAnswerFrameEqualToQueryFrame(byte *answerFrame, byte *queryFrame);
}; };
#endif // CLIENT_CENTRAL_MAST_SENTECRAINGAUGESENSOR_H #endif // CLIENT_CENTRAL_MAST_SENTECRAINGAUGESENSOR_H
#include "NoDataAvailableException.hpp" #include "NoDataAvailableException.hpp"
#include "SentecRainGaugeSensor.h"
#include <SentecSensorsRS485.h> #include <SentecSensorsRS485.h>
/*************************************** /***************************************
...@@ -28,19 +29,11 @@ void SentecSensorRS485::write(byte queryFrame[], int length) { ...@@ -28,19 +29,11 @@ void SentecSensorRS485::write(byte queryFrame[], int length) {
} }
String SentecSensorRS485::getValueStr(float value) { String SentecSensorRS485::getValueStr(float value) {
if (valid) { return String(value, 1);
return String(value, 1);
} else {
return {"null"};
}
} }
String SentecSensorRS485::getValueStr(int value) { String SentecSensorRS485::getValueStr(int value) {
if (valid) { return String(value);
return String(value);
} else {
return {"null"};
}
} }
ErrorType SentecSensorRS485::queryAddress() { ErrorType SentecSensorRS485::queryAddress() {
...@@ -105,7 +98,11 @@ ErrorType SentecSensorRS485::setAddress(byte add) { ...@@ -105,7 +98,11 @@ ErrorType SentecSensorRS485::setAddress(byte add) {
WriteRegisterReturnType tmp = writeRegister(word(0x07, 0xD0), add); WriteRegisterReturnType tmp = writeRegister(word(0x07, 0xD0), add);
if (tmp.errorType == ErrorType::DATA_OK) if (tmp.errorType == ErrorType::DATA_OK)
address = add; address = add;
// TODO check response: matches the sent message exactly
if (!isAnswerFrameEqualToQueryFrame(answerFrame, tmp.writtenQuery)) {
return ErrorType::COULD_NOT_SET_ADDRESS;
}
return tmp.errorType; return tmp.errorType;
} }
...@@ -118,61 +115,34 @@ void SentecSensorRS485::resetAnswerFrame() { ...@@ -118,61 +115,34 @@ void SentecSensorRS485::resetAnswerFrame() {
ErrorType SentecSensorRS485::getResponse() { ErrorType SentecSensorRS485::getResponse() {
// reads the response of a sensor // reads the response of a sensor
ErrorType returnCode = ErrorType::DATA_OK; ErrorType returnCode = ErrorType::DATA_OK;
valid = true;
int idx = 0; int idx = 0;
int byteReceived;
// usual response length: changed in the while loop to match the response, // 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) // changed only when reading data (then it's 7 or 9 bytes, sensor dpendent)
int responseLength = 8; int responseLength = 8;
// reading an answer takes up to 39 milliseconds for 2 byte readRegister // reading an answer takes up to 39 milliseconds for 2 byte readRegister
const int timeout = 2000;
const int retries = 1; // #editet to q 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 // it doesn't seem to help to request multiple times if first time goes wrong
for (tries; tries <= retries; tries++) { for (size_t tries = 1; tries <= retries; tries++) {
// if we lose connection with the sensor, we get an array of zeros back // if we lose connection with the sensor, we get an array of zeros back
resetAnswerFrame(); resetAnswerFrame();
unsigned long time = millis(); readRS485(idx, responseLength);
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, "Response: 0x%s", formatBytes(answerFrame, responseLength).c_str());
// ESP_LOGD(TAG, "Tries: %d", tries);
// ESP_LOGD(TAG, "Bytes received: %d", idx); // TODO: Not optimal, has precedence
if (answerFrame[0] == 0) { auto connectionEndedPrematurelyReturnCode = checkConnectionEndedPrematurely(idx, responseLength);
ESP_LOGE(TAG, "Check sensor connection. First byte is 0x00."); auto sensorConnectedReturnCode = checkSensorConnected();
valid = false; auto crcReturnCode = checkCRC(responseLength);
returnCode = ErrorType::SENSOR_NOT_CONNECTED;
} else if (idx < responseLength) { if (sensorConnectedReturnCode != ErrorType::DATA_OK) {
ESP_LOGE(TAG, "Response too short: %d bytes < %d bytes. Unfinished transmission.", idx, responseLength); returnCode = sensorConnectedReturnCode;
valid = false; } else if (connectionEndedPrematurelyReturnCode != ErrorType::DATA_OK) {
returnCode = ErrorType::CONNECTION_ENDED_PREMATURELY; returnCode = connectionEndedPrematurelyReturnCode;
} } else if (crcReturnCode != ErrorType::DATA_OK) {
word crc_received = word(answerFrame[responseLength - 2], answerFrame[responseLength - 1]); returnCode = crcReturnCode;
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 // breaking after first successfull try
if (returnCode == ErrorType::DATA_OK) { if (returnCode == ErrorType::DATA_OK) {
break; break;
...@@ -182,6 +152,54 @@ ErrorType SentecSensorRS485::getResponse() { ...@@ -182,6 +152,54 @@ ErrorType SentecSensorRS485::getResponse() {
// ESP_LOGE(TAG, "Returncode: %d", returnCode); // ESP_LOGE(TAG, "Returncode: %d", returnCode);
return returnCode; return returnCode;
} }
ErrorType SentecSensorRS485::checkCRC(int responseLength) {
word crc_received = word(answerFrame[responseLength - 2], answerFrame[responseLength - 1]);
word crc = calculateCRC(answerFrame, responseLength - 2);
if (crc_received != word(crc)) {
ESP_LOGE(TAG, "CRC wrong: Expected 0x%s got 0x%s", formatBytes(crc).c_str(), formatBytes(crc_received).c_str());
return ErrorType::WRONG_CRC;
}
return ErrorType::DATA_OK;
}
ErrorType SentecSensorRS485::checkConnectionEndedPrematurely(int idx, int responseLength) const {
if (idx < responseLength) {
ESP_LOGE(TAG, "Response too short: %d bytes < %d bytes. Unfinished transmission.", idx, responseLength);
return ErrorType::CONNECTION_ENDED_PREMATURELY;
}
return ErrorType::DATA_OK;
}
ErrorType SentecSensorRS485::checkSensorConnected() const {
if (answerFrame[0] == 0) {
ESP_LOGE(TAG, "Check sensor connection. First byte is 0x00.");
return ErrorType::SENSOR_NOT_CONNECTED;
}
return ErrorType::DATA_OK;
}
void SentecSensorRS485::readRS485(int &idx, int &responseLength, const int timeout) {
int byteReceived = 0;
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++;
}
}
}
}
unsigned int SentecSensorRS485::calculateCRC(byte query[], int length) { unsigned int SentecSensorRS485::calculateCRC(byte query[], int length) {
// Change the last two bytes of the queryFrame to conform to a CRC check // Change the last two bytes of the queryFrame to conform to a CRC check
...@@ -246,3 +264,15 @@ std::string SentecSensorRS485::formatBytes(word data) { ...@@ -246,3 +264,15 @@ std::string SentecSensorRS485::formatBytes(word data) {
byte arr[2] = {static_cast<byte>(data >> 8), static_cast<byte>(data & 0xFF)}; byte arr[2] = {static_cast<byte>(data >> 8), static_cast<byte>(data & 0xFF)};
return formatBytes(arr, 2); return formatBytes(arr, 2);
} }
bool SentecSensorRS485::isAnswerFrameEqualToQueryFrame(byte returnedFrame[8], byte queryFrame[8]) {
for (int i = 0; i < 8; i++) {
if (returnedFrame[i] != queryFrame[i]) {
ESP_LOGD(TAG, "Response: 0x%s", formatBytes(returnedFrame, 8).c_str());
ESP_LOGD(TAG, "Response: 0x%s", formatBytes(queryFrame, 8).c_str());
ESP_LOGE("TEST", "Error: returnedFrame[%d] != ret.writtenQuery[%d]", i, i);
return false;
}
}
return true;
}
\ No newline at end of file
...@@ -19,8 +19,6 @@ class SentecSensorRS485 { ...@@ -19,8 +19,6 @@ class SentecSensorRS485 {
uint8_t serialCommunicationControlPin = 19; uint8_t serialCommunicationControlPin = 19;
std::shared_ptr<HardwareSerial> RS485 = RS485HardwareSerial::getInstance().getRS485Serial(); std::shared_ptr<HardwareSerial> RS485 = RS485HardwareSerial::getInstance().getRS485Serial();
byte answerFrame[10] = {0}; byte answerFrame[10] = {0};
// TODO use valid flag to log None
bool valid = false;
SentecSensorRS485(byte add); SentecSensorRS485(byte add);
...@@ -40,6 +38,12 @@ class SentecSensorRS485 { ...@@ -40,6 +38,12 @@ class SentecSensorRS485 {
WriteRegisterReturnType writeRegister(int registerAddress, int value); WriteRegisterReturnType writeRegister(int registerAddress, int value);
/**
* @brief Set the address of the sensor
* @param add The new address of the sensor
* @return ErrorType. If the address was not set correctly, the error type is set to
* ErrorType::COULD_NOT_SET_ADDRESS
*/
ErrorType setAddress(byte add); ErrorType setAddress(byte add);
void resetAnswerFrame(); void resetAnswerFrame();
...@@ -55,6 +59,27 @@ class SentecSensorRS485 { ...@@ -55,6 +59,27 @@ class SentecSensorRS485 {
std::string formatBytes(byte *data, int length); std::string formatBytes(byte *data, int length);
std::string formatBytes(word data); std::string formatBytes(word data);
protected:
/**
* @brief Check if the answer frame is equal to the query frame. ONLY CHECKS THE FIRST 8 BYTES
* @param returnedFrame The frame returned from the device
* @param queryFrame The frame sent to the device
* @return true if the answer frame is equal to the query frame
*/
bool isAnswerFrameEqualToQueryFrame(byte *returnedFrame, byte *queryFrame);
private:
/**
* @brief Read the response from the RS485 bus
* @param[in, out] idx The index of the response
* @param[in, out] responseLength The length of the response
* @param timeout The timeout in ms. Default is 2000ms
*/
void readRS485(int &idx, int &responseLength, const int timeout = 2000);
ErrorType checkSensorConnected() const;
ErrorType checkConnectionEndedPrematurely(int idx, int responseLength) const;
ErrorType checkCRC(int responseLength);
}; };
#endif #endif
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment