diff --git a/firmware/weather_station_MK2_sensor_temperature/weather_station_MK2_sensor_temperature.ino b/firmware/weather_station_MK2_sensor_temperature/weather_station_MK2_sensor_temperature.ino new file mode 100644 index 0000000..62421ad --- /dev/null +++ b/firmware/weather_station_MK2_sensor_temperature/weather_station_MK2_sensor_temperature.ino @@ -0,0 +1,238 @@ +/********************************************************************************* + * + * A temperature sensor which can be connected to an RS-485 ModBus interface. + * + * LED on Arduino gives status: + * + * ON : Booting + * BLINK : I2C ERROR + * FLASH : Heartbeat + * + * Copyright (C) 2025 M.T. Konstapel https://meezenest.nl/mees + * + * This file is part of weather_station_MK2_sensor_temperature + * + * weather_station_MK2_sensor_temperature is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * weather_station_MK2_sensor_temperature is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with weather_station_MK2_sensor_temperature. If not, see . + * + * See CHANGELOG.md + * + **********************************************************************************/ + +// When set to 1 debug messages are send to the serial port, set it to 0 for production code! +#define _DEBUG_ 0 + +#include +#include "SparkFun_Weather_Meter_Kit_Arduino_Library.h" + +/**************************/ +/* Configurable variables */ +/**************************/ +// RS485 driver +#define RS485_RE 11 // Tight to RS485_DE and must be configured as an input to prevent a short circuit +#define RS485_DE 12 +// Used Pins +const uint16_t TxenPin = RS485_DE; // -1 disables the feature, change that if you are using an RS485 driver, this pin would be connected to the DE and /RE pins of the driver. +// ModBus address +const uint8_t SlaveId = 14; +/* Modbus Input Registers + * + * 0-3 : DefineSensorSerial (unique value): 0x4D45 followed by index. The index starts at 0x0 0x0 0x0 and is incremented for every unique device. + * 4 : Sensor Type (specifies sensor type): 0x01 for this temperature sensor + * 5 : Sensor Version (specifies sensor version) + * 6-30 : Human readable description of sensor: Every register holds two ASCII values, one in the MSB and the other in the LSB for a total of 60 characters. "Temparature sensor with two sensors" + * 31 : Number of available input registers + * + * 40 : Temperature reading sensor 1 + * 41 : Temperature reading sensor 2 (if available) + * 42 : Minimum temperature reading sensor 1 since last reset + * 43 : Minimum temperature reading sensor 2 since last reset (if available) + * 44 : Maximum temperature reading sensor 1 since last reset + * 45 : Maximum temperature reading sensor 2 since last reset (if available) + */ +// Pre-defined register values +const uint16_t DefineSensorSerial[4] = {0x4D45,0x00,0x00,0x01}; +const uint16_t DefineSensorType = 0x01; +const uint16_t DefineSensorVersion = 0x01; +const uint8_t DefineSensorTypeString[] = "Dual temperature sensor"; // Make sure the string is not longer than 60 charaters +const uint16_t DefineNumberOfInputRegisters = 6; +const uint16_t DefineNumberOfHoldingRegisters = 0; +const uint16_t DefineNumberOfCoilRegisters = 0; +const uint16_t DefineNumberOfContactRegisters = 0; + +// Registers +const uint16_t SensorID[4] = {0,1,2,3}; +const uint16_t SensorType = 4; +const uint16_t SensorVersion = 5; +const uint16_t SensorTypeString[30] = {6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35}; +const uint16_t NumberOfInputRegisters = 36; +const uint16_t NumberOfHoldingRegisters = 37; +const uint16_t NumberOfCoilRegisters = 38; +const uint16_t NumberOfContactRegisters = 39; + +const uint16_t SensorTemperature[2] = {40,41}; +const uint16_t SensorTemperatureMin[2] = {42,43}; +const uint16_t SensorTemperatureMax[2] = {44,45}; + +/* Modbus Coil Registers + * 0: ResetMinMax (Reset Min/Max values) + */ +const uint16_t ResetMinMax = 0; + +// RS-485 serial port +#define MySerial Serial // define serial port used, Serial most of the time, or Serial1, Serial2 ... if available +const uint32_t Baudrate = 9600; +/******************************/ +/* END Configurable variables */ +/******************************/ + +// ModbusSerial object +ModbusSerial mb (MySerial, SlaveId, TxenPin); + +uint32_t ts; + +struct MeasuredData { + float Temperature01; + float Temperature02; + + float MinTemperature01; + float MinTemperature02; + + float MaxTemperature01; + float MaxTemperature02; + +} MeasuredData; + +void setup() { + + uint16_t RegisterIndex; + + MySerial.begin (Baudrate); // works on all boards but the configuration is 8N1 which is incompatible with the MODBUS standard + // prefer the line below instead if possible + // MySerial.begin (Baudrate, MB_PARITY_EVEN); + + // initialize digital pin LED_BUILTIN as an output and turn it on. + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + + //Setup control lines for RS485 driver + pinMode(RS485_RE,INPUT); // In hardware connected to RS485_DE. Should be input to prevent a short circuit! + pinMode(RS485_DE,OUTPUT); + digitalWrite(RS485_DE,LOW); + + mb.config (Baudrate); + mb.setAdditionalServerData ("TEMP_SENSOR"); // for Report Server ID function (0x11) + + // Add SensorID to ModBus + for (RegisterIndex = 0; RegisterIndex < 4; RegisterIndex++) { + mb.addIreg (SensorID[RegisterIndex]); + mb.Ireg (SensorID[RegisterIndex], DefineSensorSerial[RegisterIndex]); + } + + // Add SensorType to ModBus + mb.addIreg (SensorType); + + mb.addIreg (SensorTemperature[0]); + mb.addIreg (SensorTemperature[1]); + mb.addIreg (SensorTemperatureMin[0]); + mb.addIreg (SensorTemperatureMin[1]); + mb.addIreg (SensorTemperatureMax[0]); + mb.addIreg (SensorTemperatureMax[1]); + // Add HeaterCoil register + mb.addCoil (ResetMinMax); + + // Set Weather station ID (ModBus) + mb.Ireg (SensorType, DefineSensorType); + + // Fill SensorTypeString (ModBus) + uint16_t SensorTypeCharacter; + uint16_t LastRegister; + + // Get length of pre defined description string + LastRegister = strlen(DefineSensorTypeString); + + // Add SensorTypeString to ModBus + for (RegisterIndex = 0; RegisterIndex < 30; RegisterIndex++) { + + mb.addIreg (SensorTypeString[RegisterIndex]); + + // Fill MSB of SensorTypeString with character from pre defined string until we reach the end of the description string + if (2*RegisterIndex < LastRegister) { + SensorTypeCharacter = DefineSensorTypeString[2*RegisterIndex] << 8; + // ...otherwise fill with a space + } else { + SensorTypeCharacter = ' ' << 8; + } + // Fill LSB of SensorTypeString with character from pre defined string until we reach the end of the description string + if ((2*RegisterIndex)+1 < LastRegister) { + SensorTypeCharacter += DefineSensorTypeString[(2*RegisterIndex)+1]; + // ...otherwise fill with a space + } else { + SensorTypeCharacter += ' '; + } + mb.Ireg (SensorTypeString[RegisterIndex], SensorTypeCharacter); + + } + + mb.addIreg (NumberOfInputRegisters); + mb.Ireg (NumberOfInputRegisters, DefineNumberOfInputRegisters); + + mb.addIreg (NumberOfHoldingRegisters); + mb.Ireg (NumberOfHoldingRegisters, DefineNumberOfHoldingRegisters); + + mb.addIreg (NumberOfCoilRegisters); + mb.Ireg (NumberOfCoilRegisters, DefineNumberOfCoilRegisters); + + mb.addIreg (NumberOfContactRegisters); + mb.Ireg (NumberOfContactRegisters, DefineNumberOfContactRegisters); + + Serial.println(F("weather_station_MK2_sensor_temperature v0.1")); + Serial.println(F("(C) 2025 M.T. Konstapel")); + Serial.println(F("This project is free and open source (GPL-3.0)")); + Serial.println(F("More details: https://meezenest.nl/mees/")); + + ts = millis(); +} + +void loop() { + + // Call once inside loop() - all magic here + mb.task(); + + // Read each two seconds + if ( ( millis() - ts) >= 2000) { + + ts = millis(); + + digitalWrite(LED_BUILTIN, HIGH); // LED as heartbeat + + // Fill input registers + mb.Ireg (SensorTemperature[0], 20); + mb.Ireg (SensorTemperature[1], 21); + mb.Ireg (SensorTemperatureMin[0], 22); + mb.Ireg (SensorTemperatureMax[0], 23); + mb.Ireg (SensorTemperatureMin[1], 24); + mb.Ireg (SensorTemperatureMax[1], 25); + + // reset min/max temperatures + if (mb.Coil (ResetMinMax)) { + // reset min/max temperatures + } else { + // do something + } + + digitalWrite(LED_BUILTIN, LOW); // LED as heartbeat + + + } +} \ No newline at end of file