diff --git a/code-snippets/client/soil_sensor_sentec/main.cpp b/code-snippets/client/soil_sensor_sentec/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..aac6bac8b4d4084b3ead90eb19f85584560e25ce --- /dev/null +++ b/code-snippets/client/soil_sensor_sentec/main.cpp @@ -0,0 +1,172 @@ +#include "Arduino.h" +#include <SoftwareSerial.h> // RS485 setup with ESP32 + +// THIS SNIPPET APPLIES ONLY TO SOIL SENSORS!!! +// Other RS485 sensors will have to use other commands as well + +/* +TODO It would be better to have one class for ALL RS485 sensors since they +all send/receive data in the same way, and then specific classes for individual +sensors (soil moisture, rain gauge, radiation) with sensor-specific commands. +*/ + +/* TODO The address of the sensor (first byte of the query frame) is +hard-coded now - it should be passed as an argument instead since +we have three sensors with different addresses now. +The last two values of the queryFrame don't matter, they will be changed +later to confrom to the CRC check. +The length of the sent message will always be 8 bytes. +*/ +const byte bufferSize = 6; +byte queryFrame[bufferSize + 2] = {0x05, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00}; +// 0: sensor address (differs per sensor) +// 1: function code (03 = get data for all sensors) +// 2+3: register start (0x00 0x00 for all sensors) +// 4+5: register length (0x01 for radiation+rain, 0x02 for soil) +// 6+7: CRC check (calculated later per sensor and message) + +/* +TODO the length of the answer frame should NOT be hard-coded, it's going to be either +8 bytes (radiation, rain) or 9 bytes (soil) +*/ +byte answerFrame[9]; + + +#define RXPin 4 // Serial Receive pin +#define TXPin 5 // Serial Transmit pin + +// RS485 control +// this will change once we get different hardware +#define SERIAL_COMMUNICATION_CONTROL_PIN 6 // Transmission set pin +#define RS485_TX_PIN_VALUE HIGH +#define RS485_RX_PIN_VALUE LOW + +SoftwareSerial RS485Serial(RXPin, TXPin); // RX, TX + + +unsigned int calculateCRC() +// TODO pass the query/answer frame into this and return the changed frame +// to make things neater maybe? Apparently there are also libraries for this... + +// 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 < bufferSize; i++) + { + tmp1 = tmp1 ^ queryFrame[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; + queryFrame[bufferSize + 1] = tmp1; + queryFrame[bufferSize] = tmp1 >> 8; + + return tmp1; // the returned value is already swapped - CRC_L byte is first & CRC_H byte is last +} + + +void setup() { + // configure the pin to be output only + pinMode(SERIAL_COMMUNICATION_CONTROL_PIN, OUTPUT); + + // Set data rates: Serial baud rate has to be WAY HIGHER than RS485Serial! + Serial.begin(115200); + RS485Serial.begin(4800); + + // Calculate the check bytes: changes the last two bytes of queryFrame + calculateCRC(); + + // Print the message that's sent to the sensor + Serial.println("Query bytes:"); + for (int i = 0; i < sizeof(queryFrame); i++){ + Serial.print(queryFrame[i], HEX); + Serial.print(" "); + } + Serial.println(); + Serial.println(); +} + + +void loop() { + // Index for the readout while loop + int idx = 0; + int byteReceived; + + float vwc; // volumetric waetr content, i.e. soil moisture + float temp; // soil temperature + + + // Initialize the transmitter + Serial.println("Sending data"); + digitalWrite(SERIAL_COMMUNICATION_CONTROL_PIN, RS485_TX_PIN_VALUE); + // Send message: request a reading from the sensor + RS485Serial.write(queryFrame, sizeof(queryFrame)); + + // Initialize the receiver + digitalWrite(SERIAL_COMMUNICATION_CONTROL_PIN, RS485_RX_PIN_VALUE); + + // Read out data: this works ONLY when the Serial baud rate >> RS485Serial baud rate + // TODO add some logic for when there's no data and we get a timeout + while (idx < sizeof(answerFrame)) { + if(RS485Serial.available()) { + byteReceived = RS485Serial.read(); + // Serial.println(byteReceived, HEX); + answerFrame[idx] = byteReceived; + idx++; + } + } + + // Print out answer bytes + /* + Byte 0: sensor address + Byte 1: Function code + Byte 2: valid bytes (2 valid bytes per read variable) + Byte 3+4: 1st read variable + Byte 5+6: 2nd read variable + Byte n-1 + n: CRC checks + */ + Serial.println(" "); + Serial.println("Answer bytes:"); + for(int i = 0; i < sizeof(answerFrame); i++){ + Serial.print(answerFrame[i], HEX); + Serial.print(" "); + } + + Serial.println(); + Serial.println(); + + Serial.print("Sensor number: "); + Serial.println(answerFrame[0], HEX); + + Serial.print("Number of read variables: "); + Serial.println(answerFrame[2] / 2, DEC); + + vwc = word(answerFrame[3], answerFrame[4]); // word = 2 bytes, high byte first + Serial.print("vwc: "); + Serial.println(vwc / 10 , DEC); + + temp = word(answerFrame[5], answerFrame[6]); + Serial.print("soil T: "); + Serial.println(temp / 10, DEC); + + Serial.println(" "); + + // Get reading every 5 seconds + delay(5000); + + /* TODO add a CRC check for the received data, i.e. take n-2 bytes of the answer, + calculate CRC, and then compare if it fist with the CRC bytes that were received. + */ + +} diff --git a/code-snippets/client/soil_sensor_sentec/platformio.ini b/code-snippets/client/soil_sensor_sentec/platformio.ini new file mode 100644 index 0000000000000000000000000000000000000000..0b77e00dd063100148dec39a48144938f45d1373 --- /dev/null +++ b/code-snippets/client/soil_sensor_sentec/platformio.ini @@ -0,0 +1,19 @@ +; 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 = 115000 +lib_deps = EspSoftwareSerial + + +