I used to hate on Arduino a bit, but have recently come around. I was having a conversation with a work colleague along the lines of how super easy it would be nowadays to build a temperature sensor with ethernet connectivity and a modbus interface. Remembering that I had an old Arduino board (with Ethernet) kicking around from some abandoned project years ago, I laid myself a little personal challenge: to see how quickly I could prototype such a device. The answer is: Really, surprisingly quickly!
Gluing together other people’s hard work together made this a trivial exercise, and it was kind of too easy. It felt a bit like cheating. And I think this kind of plug-n-play, lego block engineering has made me lazy – I know someone else has already done the legwork so all my time is spent evaluating the relative merit of one library against another, rather than engineering something tricky from first principles. I guess this is both the blessing and the curse of the Arduino phenomenon.
The original sketch to read q temperature measurement from the LM335 is based on the one found here: http://spacetinkerer.blogspot.com.
There are a handful of Modbus libraries but I chose to use https://github.com/andresarmento/modbus-arduino. I also used the runningaverage library found here, in an attempt to tame some of the noise and variability in measurements.
Of the whole exercise, what actually took the longest was debugging the poor performance of the LM335 sensor. In the end, I wisely took the datasheet’s advice and added a 10k potentiometer which just works as a resistive voltage divider into one of the pins of the LM335 which in turn gives a linear offset. The LM335 can then be easily calibrated against another thermometer.
To log, store and historise the data I of course used Mango Automation (and with the server located off-site, I just added a DST-NAT rule to forward TCP traffic on port 502 from my home internet to my Arduino).
The data source configuration in Mango looks like this:
1 2 3 4 5 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 36 37 38 |
{ "dataSources":[ { "xid":"DS_714957", "name":"Arduino Modbus Sensor", "enabled":true, "type":"MODBUS_IP", "alarmLevels":{ "POINT_WRITE_EXCEPTION":"DO_NOT_LOG", "POLL_ABORTED":"DO_NOT_LOG", "DATA_SOURCE_EXCEPTION":"DO_NOT_LOG", "POINT_READ_EXCEPTION":"DO_NOT_LOG" }, "purgeType":"YEARS", "updatePeriodType":"SECONDS", "transportType":"TCP", "encapsulated":false, "host":"arduino.host.address", "port":502, "contiguousBatches":false, "createSlaveMonitorPoints":false, "discardDataDelay":0, "ioLogFileSizeMBytes":1.0, "logIO":false, "maxHistoricalIOLogs":1, "maxReadBitCount":2000, "maxReadRegisterCount":125, "maxWriteRegisterCount":120, "multipleWritesOnly":false, "quantize":false, "retries":2, "timeout":500, "updatePeriods":10, "purgeOverride":false, "purgePeriod":1 } ] } |
The temperature value is read as a two-byte signed integer. The Mango data point configuration looks like this.
1 2 3 4 5 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
{ "dataPoints":[ { "xid":"DP_739238", "name":"Temperature Sensor 1", "enabled":true, "loggingType":"ALL", "intervalLoggingPeriodType":"MINUTES", "intervalLoggingType":"INSTANT", "purgeType":"YEARS", "pointLocator":{ "range":"INPUT_REGISTER", "modbusDataType":"TWO_BYTE_INT_SIGNED", "writeType":"NOT_SETTABLE", "additive":0.0, "bit":0, "charset":"ASCII", "multiplier":0.01, "offset":100, "registerCount":0, "slaveId":1, "slaveMonitor":false }, "eventDetectors":null, "plotType":"STEP", "unit":"?", "chartColour":"", "chartRenderer":null, "dataSourceXid":"DS_714957", "defaultCacheSize":1, "deviceName":"Arduino Modbus Temperature Sensor", "discardExtremeValues":true, "discardHighLimit":100.0, "discardLowLimit":0.0, "intervalLoggingPeriod":15, "intervalLoggingSampleWindowSize":0, "overrideIntervalLoggingSamples":false, "purgeOverride":false, "purgePeriod":1, "textRenderer":{ "type":"ANALOG", "useUnitAsSuffix":true, "unit":"?", "renderedUnit":"?", "format":"##.##" }, "tolerance":0.0 } ] } |
And here’s my actual Arduino sketch:
1 2 3 4 5 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
#include <SPI.h> #include <Ethernet.h> #include <Modbus.h> #include <ModbusIP.h> #include <RunningAverage.h> //Modbus Registers Offsets (0-9999) const int SENSOR_IREG = 100; //Used Pins const int sensorPin = A0; RunningAverage myRA(30); int samples = 0; //ModbusIP object ModbusIP mb; float temp_in_celsius = 0, temp_in_kelvin = 0; long ts; void setup() { // The media access control (ethernet hardware) address for the shield byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // The IP address for the shield byte ip[] = { 192, 168, 88, 18 }; //Config Modbus IP mb.config(mac, ip); // Add SENSOR_IREG register - Use addIreg() for analog Inputs mb.addIreg(SENSOR_IREG); ts = millis(); myRA.clear(); // explicitly clear the rolling average array Serial.begin(9600); //start serial port, 9600 baud } void loop() { //Call once inside loop() - all magic here mb.task(); //Reads the input and converts it to Kelvin degrees temp_in_kelvin = analogRead(0) * 0.004882812 * 100; //Print the temperature in Celsius to the serial port for monitoring //Serial.print("Celsius: "); //Serial.println(temp_in_celsius); //Serial.println(); //delay(1000); //Converts Kelvin to Celsius minus 2.5 degrees error temp_in_celsius = temp_in_kelvin - 2.5 - 273.15; myRA.addValue(temp_in_celsius * 100); //add the current temp to the myRA array //Read each two seconds if (millis() > ts + 2000) { ts = millis(); //Setting raw value (0-1024) mb.Ireg(SENSOR_IREG, myRA.getAverage()); } } |
LibreNMS network monitor with Slack integration and Oxidized config versioning
I used to use Observium for network monitoring. After evaluating a couple of options - Cacti, Zabbix, Nagios - I found Observium, and loved its beautiful, well organised and extremely usable interface. Observium has ... Read more
Raspberry Pi internet streaming radio station
My partner has been overseas for a few months and was desperately missing a local radio station. It doesn't have an online stream that she can listen to and so she asked me ... Read more