Getting and Setting a Modbus Address

This example gets the Modbus address for a sensor and optionally sets it to a new address. This example can be used on sensors from any manufacturer, as long as you know the register for the device address.


PlatformIO Configuration

1; PlatformIO Project Configuration File
2;
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
7;
8; Please visit documentation for the other options and examples
9; http://docs.platformio.org/page/projectconf.html
10
11[platformio]
12description = Gettting and/or setting the Modbus address for a device
13src_dir = examples/getSetAddress
14
15[env:mayfly]
16monitor_speed = 57600
17board = mayfly
18platform = atmelavr
19framework = arduino
20lib_deps =
21 SensorModbusMaster

The Complete Code

1/** =========================================================================
2 * @example{lineno} getSetAddress.ino
3 * @author Anthony Aufdenkampe <aaufdenkampe@limno.com>
4 * @copyright Stroud Water Research Center
5 * @license This example is published under the BSD-3 license.
6 *
7 * @brief This example gets the Modbus address for a sensor and optionally
8 * sets it to a new address. This example can be used on sensors from
9 * any manufacturer, as long as you know the register for the device address.
10 *
11 * @m_examplenavigation{example_get_set_address,}
12 * @m_footernavigation
13 * ======================================================================= */
14
15// ---------------------------------------------------------------------------
16// Include the base required libraries
17// ---------------------------------------------------------------------------
18#include <Arduino.h>
19#include <SensorModbusMaster.h>
20
21// ==========================================================================
22// Sensor/Slave Settings
23// ==========================================================================
24
25// The sensor slave modbus address used by default to broadcast
26// commands to any or all slave devices.
27// 0x00 (0) used by many manufacturers
28// 0xFF (255) used by some manufacturers, such as YosemiTech
29byte modbusBroadcastAddress = 0xFF;
30
31// Your desired modbus address for your slave device.
32byte newModbusAddress = 0x05;
33
34
35// Set the registers for your device
36// NOTE: Registers are always two byte integers (int16_t) but are often
37// provided in Hexadecimal form in modbus register maps. C++ can auto-convert
38// from HEC to DEC, and an online "HEX to DEC Converter" can confirm values.
39
40int16_t addressRegister = 0x3000;
41// Common registers for storing device address, by manufacturer:
42// 0x3000 = 12288 for YosemiTech
43
44int16_t serialNumberRegister = 0x0900;
45// Common registers for storing device serial number, by manufacturer:
46// 0x0900 = 2304 for most YosemiTech sensors
47// 0x1400 = 5120 for the YosemiTech Y4000 Multi-parameter sonde
48
49// The Modbus baud rate the sensor uses
50int32_t modbusBaudRate = 9600; // The baud rate the sensor uses
51
52// Time in milliseconds after powering up for the slave device to respond
53#define WARM_UP_TIME 1500
54
55
56// ==========================================================================
57// Data Logger Options
58// ==========================================================================
59const int32_t serialBaud = 115200; // Baud rate for serial monitor
60
61// Define pin number variables
62const int sensorPwrPin = 10; // The pin sending power to the sensor
63const int adapterPwrPin = 22; // The pin sending power to the RS485 adapter
64const int DEREPin = -1; // The pin controlling Receive Enable and Driver Enable
65 // on the RS485 adapter, if applicable (else, -1)
66 // Setting HIGH enables the driver (arduino) to send text
67 // Setting LOW enables the receiver (sensor) to send text
68
69// Turn on debugging outputs (i.e. raw Modbus requests & responses)
70// by uncommenting next line (i.e. `#define DEBUG`)
71#define DEBUG
72
73// ==========================================================================
74// Create and Assign a Serial Port for Modbus
75// ==========================================================================
76// Hardware serial ports are preferred when available.
77// AltSoftSerial is the most stable alternative for modbus.
78// Select over alternatives with the define below.
79// #define BUILD_ALTSOFTSERIAL // Comment-out if you prefer alternatives
80
81#if defined(BUILD_ALTSOFTSERIAL)
82#include <AltSoftSerial.h>
83AltSoftSerial modbusSerial;
84
85#elif defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_FEATHER328P)
86// The Uno only has 1 hardware serial port, which is dedicated to communication with the
87// computer. If using an Uno, you will be restricted to using AltSofSerial or
88// SoftwareSerial
89#include <SoftwareSerial.h>
90const int SSRxPin = 10; // Receive pin for software serial (Rx on RS485 adapter)
91const int SSTxPin = 11; // Send pin for software serial (Tx on RS485 adapter)
92#pragma message("Using Software Serial for the Uno on pins 10 and 11")
93SoftwareSerial modbusSerial(SSRxPin, SSTxPin);
94
95#elif defined(ESP8266)
96#include <SoftwareSerial.h>
97#pragma message("Using Software Serial for the ESP8266")
98SoftwareSerial modbusSerial;
99
100#elif defined(NRF52832_FEATHER) || defined(ARDUINO_NRF52840_FEATHER)
101#pragma message("Using TinyUSB for the NRF52")
102#include <Adafruit_TinyUSB.h>
103HardwareSerial& modbusSerial = Serial1;
104
105#elif !defined(NO_GLOBAL_SERIAL1) && !defined(STM32_CORE_VERSION)
106// This is just a assigning another name to the same port, for convenience
107// Unless it is unavailable, always prefer hardware serial.
108#pragma message("Using HardwareSerial / Serial1")
109HardwareSerial& modbusSerial = Serial1;
110
111#else
112// This is just a assigning another name to the same port, for convenience
113// Unless it is unavailable, always prefer hardware serial.
114#pragma message("Using HardwareSerial / Serial")
115HardwareSerial& modbusSerial = Serial;
116#endif
117
118// Construct the modbus instance
119modbusMaster modbus;
120
121// Initialize success flag for set commands
122bool success;
123
124
125// ==========================================================================
126// Working Functions
127// ==========================================================================
128// A function for pretty-printing the Modbuss Address in Hexadecimal notation,
129// from ModularSensors `sensorLocation()`
130String prettyprintAddressHex(byte _modbusAddress) {
131 String addressHex = F("0x");
132 if (_modbusAddress < 0x10) { addressHex += "0"; }
133 addressHex += String(_modbusAddress, HEX);
134 return addressHex;
135}
136
137
138// ==========================================================================
139// Arduino Setup Function
140// ==========================================================================
141void setup() {
142 // Set various pins as needed
143 if (DEREPin >= 0) { pinMode(DEREPin, OUTPUT); }
144 if (sensorPwrPin >= 0) {
145 pinMode(sensorPwrPin, OUTPUT);
146 digitalWrite(sensorPwrPin, HIGH);
147 }
148 if (adapterPwrPin >= 0) {
149 pinMode(adapterPwrPin, OUTPUT);
150 digitalWrite(adapterPwrPin, HIGH);
151 }
152
153 // Turn on the "main" serial port for debugging via USB Serial Monitor
154 Serial.begin(serialBaud);
155
156 // Turn on your modbus serial port
157#if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_FEATHER328P) || \
158 defined(ARDUINO_SAM_DUE) || not defined(SERIAL_8O1)
159 modbusSerial.begin(modbusBaudRate);
160 // NOTE: The AVR implementation of SoftwareSerial only supports 8N1
161 // The hardware serial implementation of the Due also only supports 8N1
162#elif defined(ESP8266)
163 const int SSRxPin = 13; // Receive pin for software serial (Rx on RS485 adapter)
164 const int SSTxPin = 14; // Send pin for software serial (Tx on RS485 adapter)
165 modbusSerial.begin(modbusBaudRate, SWSERIAL_8O1, SSRxPin, SSTxPin, false);
166 // NOTE: See
167 // https://github.com/plerup/espsoftwareserial/blob/40038df/src/SoftwareSerial.h#L120-L160
168 // for a list of data/parity/stop bit configurations that apply to the ESP8266's
169 // implementation of SoftwareSerial
170#else
171 modbusSerial.begin(modbusBaudRate);
172 // Hardware Serial Options:
173 // Serial1.begin(modbusBaudRate, SERIAL_8O1);
174 // ^^ use this for 8 data bits - odd parity - 1 stop bit
175 // Serial1.begin(modbusBaudRate, SERIAL_8E1);
176 // ^^ use this for 8 data bits - even parity - 1 stop bit
177 // Serial1.begin(modbusBaudRate, SERIAL_8N2);
178 // ^^ use this for 8 data bits - no parity - 2 stop bits
179 // Serial1.begin(modbusBaudRate);
180 // ^^ use this for 8 data bits - no parity - 1 stop bits
181 // Despite being technically "non-compliant" with the modbus specifications
182 // 8N1 parity is very common.
183#endif
184
185 // Start the modbusMaster instance with the broadcast address
186 modbus.begin(modbusBroadcastAddress, modbusSerial, DEREPin);
187
188 // Turn on debugging
189#ifdef DEBUG
190 modbus.setDebugStream(&Serial);
191#endif
192
193 // Start up note
194 Serial.println(F("\nRunning the 'getSetAddress()' example sketch."));
195
196 // Allow the sensor and converter to warm up
197 Serial.println(F("\nWaiting for sensor and adapter to be ready."));
198 Serial.print(F(" Warm up time (ms): "));
199 Serial.println(WARM_UP_TIME);
200 delay(WARM_UP_TIME);
201
202 // Confirm Modbus Address
203 Serial.println(F("\nBroadcast modbus address:"));
204 Serial.print(F(" Decimal: "));
205 Serial.print(modbusBroadcastAddress, DEC);
206 Serial.print(F(", Hexidecimal: "));
207 Serial.println(prettyprintAddressHex(modbusBroadcastAddress));
208
209 // Get the current Modbus Address
210 Serial.println(F("Getting current modbus address..."));
211 // Read a byte from a single holding register
212 byte oldModbusAddress = modbus.byteFromRegister(0x03, addressRegister, 1);
213 Serial.print(F(" Decimal: "));
214 Serial.print(oldModbusAddress, DEC);
215 Serial.print(F(", Hexidecimal: "));
216 Serial.println(prettyprintAddressHex(oldModbusAddress));
217
218 // Scan method if broadcast address does not work
219 if (oldModbusAddress == 0) {
220 Serial.println(F("Modbus Address not found!"));
221 Serial.println(F("Will scan possible addresses (in future)..."));
222 // Add future scan loop function here
223 };
224
225 // Reset to current address
226 Serial.print(F("\nRestart the modbusMaster instance with the "));
227 Serial.println(F("current device address."));
228 modbus.begin(oldModbusAddress, modbusSerial, DEREPin);
229 delay(WARM_UP_TIME);
230
231 // Get the sensor serial number
232 Serial.println(F("\nGetting sensor serial number..."));
233 // Read a string from several holding registers
234 String SN = modbus.StringFromRegister(0x03, serialNumberRegister, 14);
235 Serial.print(F(" Serial Number: "));
236 Serial.println(SN);
237
238 // Set modbus address
239 Serial.print(F("\nSetting sensor modbus address to: "));
240 Serial.println(prettyprintAddressHex(newModbusAddress));
241 // Write to a holding register
242 success = modbus.byteToRegister(addressRegister, 1, newModbusAddress, true);
243 if (success)
244 Serial.println(F(" Success!"));
245 else
246 Serial.println(F(" Failed!"));
247 // Restart the modbusMaster instance to use new address
248 modbus.begin(newModbusAddress, modbusSerial, DEREPin);
249 delay(WARM_UP_TIME);
250
251 Serial.println(F("\nGetting sensor serial number using the new address."));
252 SN = modbus.StringFromRegister(0x03, serialNumberRegister, 14);
253 Serial.print(F(" Serial Number: "));
254 Serial.println(SN);
255}
256
257// ==========================================================================
258// Arduino Loop Function
259// ==========================================================================
260void loop() {
261 // This all runs from setup!
262}
263
264// cspell: words DERE SWSERIAL