This example writes a setting value to a holding register, reads it to confirm the value has changed, and then reads several data values from holding registers.
PlatformIO Configuration
1; PlatformIO Project Configuration File
3; Build options: build flags, source filter
4; Upload options: custom upload port, speed and extra flags
5; Library options: dependencies, extra library storages
6; Advanced options: extra scripting
8; Please visit documentation for the other options and examples
9; http://docs.platformio.org/page/projectconf.html
12description = Changing a setting and reading data from a modbus sensor
The Complete Code
1/** =========================================================================
2 * @example{lineno} readWriteRegister.ino
3 * @author Sara Geleskie Damiano <sdamiano@stroudcenter.org>
4 * @copyright Stroud Water Research Center
5 * @license This example is published under the BSD-3 license.
7 * @brief This example writes a setting value to a holding register, reads it to confirm
8 * the value has changed, and then reads several data values from holding registers.
10 * The register numbers in this example happen to be for an S::CAN oxy::lyser.
12 * @m_examplenavigation{example_read_write_register,}
14 * ======================================================================= */
16// ---------------------------------------------------------------------------
17// Include the base required libraries
18// ---------------------------------------------------------------------------
20#include <SensorModbusMaster.h>
22// ==========================================================================
24// ==========================================================================
26// Define the sensor's modbus address
27byte modbusAddress = 0x01; // The sensor's modbus address, or SlaveID
29// The Modbus baud rate the sensor uses
30int32_t modbusBaudRate = 9600; // The baud rate the sensor uses
33// Edit these to explore
34#define WARM_UP_TIME 1500 // milliseconds for sensor to respond to commands.
37// ==========================================================================
39// ==========================================================================
40const int32_t serialBaud = 115200; // Baud rate for serial monitor
42// Define pin number variables
43const int sensorPwrPin = 10; // The pin sending power to the sensor
44const int adapterPwrPin = 22; // The pin sending power to the RS485 adapter
45const int DEREPin = -1; // The pin controlling Receive Enable and Driver Enable
46 // on the RS485 adapter, if applicable (else, -1)
47 // Setting HIGH enables the driver (arduino) to send text
48 // Setting LOW enables the receiver (sensor) to send text
50// Turn on debugging outputs (i.e. raw Modbus requests & responses)
51// by uncommenting next line (i.e. `#define DEBUG`)
54// ==========================================================================
55// Create and Assign a Serial Port for Modbus
56// ==========================================================================
57// Hardware serial ports are preferred when available.
58// AltSoftSerial is the most stable alternative for modbus.
59// Select over alternatives with the define below.
60// #define BUILD_ALTSOFTSERIAL // Comment-out if you prefer alternatives
62#if defined(BUILD_ALTSOFTSERIAL)
63#include <AltSoftSerial.h>
64AltSoftSerial modbusSerial;
66#elif defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_FEATHER328P)
67// The Uno only has 1 hardware serial port, which is dedicated to communication with the
68// computer. If using an Uno, you will be restricted to using AltSofSerial or
70#include <SoftwareSerial.h>
71const int SSRxPin = 10; // Receive pin for software serial (Rx on RS485 adapter)
72const int SSTxPin = 11; // Send pin for software serial (Tx on RS485 adapter)
73#pragma message("Using Software Serial for the Uno on pins 10 and 11")
74SoftwareSerial modbusSerial(SSRxPin, SSTxPin);
77#include <SoftwareSerial.h>
78#pragma message("Using Software Serial for the ESP8266")
79SoftwareSerial modbusSerial;
81#elif defined(NRF52832_FEATHER) || defined(ARDUINO_NRF52840_FEATHER)
82#pragma message("Using TinyUSB for the NRF52")
83#include <Adafruit_TinyUSB.h>
84HardwareSerial& modbusSerial = Serial1;
86#elif !defined(NO_GLOBAL_SERIAL1) && !defined(STM32_CORE_VERSION)
87// This is just a assigning another name to the same port, for convenience
88// Unless it is unavailable, always prefer hardware serial.
89#pragma message("Using HardwareSerial / Serial1")
90HardwareSerial& modbusSerial = Serial1;
93// This is just a assigning another name to the same port, for convenience
94// Unless it is unavailable, always prefer hardware serial.
95#pragma message("Using HardwareSerial / Serial")
96HardwareSerial& modbusSerial = Serial;
99// Construct the modbus instance
103// ==========================================================================
105// ==========================================================================
106// A function for pretty-printing the Modbuss Address in Hexadecimal notation,
107// from ModularSensors `sensorLocation()`
108String prettyprintAddressHex(byte _modbusAddress) {
109 String addressHex = F("0x");
110 if (_modbusAddress < 0x10) { addressHex += "0"; }
111 addressHex += String(_modbusAddress, HEX);
116// ==========================================================================
117// Main setup function
118// ==========================================================================
120 // Set various pins as needed
121 if (DEREPin >= 0) { pinMode(DEREPin, OUTPUT); }
122 if (sensorPwrPin >= 0) {
123 pinMode(sensorPwrPin, OUTPUT);
124 digitalWrite(sensorPwrPin, HIGH);
126 if (adapterPwrPin >= 0) {
127 pinMode(adapterPwrPin, OUTPUT);
128 digitalWrite(adapterPwrPin, HIGH);
131 // Turn on the "main" serial port for debugging via USB Serial Monitor
132 Serial.begin(serialBaud);
134 // Turn on your modbus serial port
135#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_FEATHER328P) || \
136 defined(ARDUINO_SAM_DUE) || not defined(SERIAL_8O1)
137 modbusSerial.begin(modbusBaudRate);
138 // NOTE: The AVR implementation of SoftwareSerial only supports 8N1
139 // The hardware serial implementation of the Due also only supports 8N1
140#elif defined(ESP8266)
141 const int SSRxPin = 13; // Receive pin for software serial (Rx on RS485 adapter)
142 const int SSTxPin = 14; // Send pin for software serial (Tx on RS485 adapter)
143 modbusSerial.begin(modbusBaudRate, SWSERIAL_8O1, SSRxPin, SSTxPin, false);
145 // https://github.com/plerup/espsoftwareserial/blob/40038df/src/SoftwareSerial.h#L120-L160
146 // for a list of data/parity/stop bit configurations that apply to the ESP8266's
147 // implementation of SoftwareSerial
149 modbusSerial.begin(modbusBaudRate);
150 // Hardware Serial Options:
151 // modbusSerial.begin(modbusBaudRate, SERIAL_8O1);
152 // ^^ use this for 8 data bits - odd parity - 1 stop bit
153 // Serial1.begin(modbusBaudRate, SERIAL_8E1);
154 // ^^ use this for 8 data bits - even parity - 1 stop bit
155 // Serial1.begin(modbusBaudRate, SERIAL_8N2);
156 // ^^ use this for 8 data bits - no parity - 2 stop bits
157 // Serial1.begin(modbusBaudRate);
158 // ^^ use this for 8 data bits - no parity - 1 stop bits
159 // Despite being technically "non-compliant" with the modbus specifications
160 // 8N1 parity is very common.
163 // Start the modbusMaster instance
164 modbus.begin(modbusAddress, modbusSerial, DEREPin);
168 modbus.setDebugStream(&Serial);
172 Serial.print(F("\nreadWriteRegister() Example "));
174 // Allow the sensor and converter to warm up
175 Serial.println(F("\nWaiting for sensor and adapter to be ready."));
176 Serial.print(F(" Warm up time (ms): "));
177 Serial.println(WARM_UP_TIME);
180 // Confirm Modbus Address
181 Serial.println(F("\nSelected modbus address:"));
182 Serial.print(F(" integer: "));
183 Serial.print(modbusAddress, DEC);
184 Serial.print(F(", hexidecimal: "));
185 Serial.println(prettyprintAddressHex(modbusAddress));
187 // Write to a holding register
188 // In this case, we are changing the output units of a dissolved oxygen sensor
189 Serial.println("Setting DO units to ppm");
190 modbus.int16ToRegister(0x01, 1, bigEndian);
191 // Verify that the register changed
192 // 0x03 = holding register
193 // only holding registers are writeable
194 int16_t doUnitMode = modbus.int16FromRegister(0x03, 0x01, bigEndian);
195 Serial.print("Current unit mode is ");
196 Serial.println(doUnitMode);
199// ==========================================================================
201// ==========================================================================
203 // Get data values from read-only input registers (0x04)
204 // Just for show, we will do the exact same thing 2 ways
205 // All values will be read as bigEndian
207 // Some variables to hold results
208 uint16_t deviceStatus = 0;
210 uint16_t temperature = 0;
213 // Get three values one at a time from 3 different registers.
214 // This code is easier to follow, but it requires more back-and-forth between
215 // the Arduino and the sensor so it is a little "slower".
216 deviceStatus = modbus.uint16FromRegister(0x04, 0x00, bigEndian);
217 doPPM = modbus.int16FromRegister(0x04, 0x01, bigEndian);
218 temperature = modbus.uint16FromRegister(0x04, 0x02, bigEndian);
221 Serial.print("Device Status:");
222 Serial.println(deviceStatus);
223 Serial.print("Dissolved Oxygen in ppm:");
224 Serial.println(doPPM);
225 Serial.print("Temperature in °C:");
226 Serial.println(temperature);
230 // Read all three registers at once and parse the values from the response.
231 // This is faster, especially when getting many readings, but it's trickier to
232 // write and understand the code.
233 bool success = modbus.getRegisters(0x04, 0x00, 3);
234 // ^ This gets the values and stores them in an internal "frame" with the hex values
237 deviceStatus = modbus.uint16FromFrame(bigEndian, 3);
238 // ^ The first data value is at position 3 in the modbus response frame
239 // 0 = modbus address, 1 = modbus method, 2 = # registers returned, 3 = 1st
241 doPPM = modbus.int16FromFrame(bigEndian, 5);
242 // ^ The next data value is at position 5 since each register occupies 2 places
243 temperature = modbus.uint16FromFrame(bigEndian, 7);
247 Serial.print("Device Status:");
248 Serial.println(deviceStatus);
249 Serial.print("Dissolved Oxygen in ppm:");
250 Serial.println(doPPM);
251 Serial.print("Temperature in °C:");
252 Serial.println(temperature);
256// cspell: words lyser DERE SWSERIAL