Complete guide: Modbus RTU and TCP with Python and Node-RED
To share the temperature of a Dallas DS18B20 sensor over Modbus, the PLC that has the sensor reads it with the OneWire and DallasTemperature libraries, multiplies it by 100 and copies it into an input register as a signed 16-bit value. A Modbus RTU master, such as a SCADA, an HMI or another PLC, reads that register with function code 04 over RS-485 and divides it by 100: 2150 is 21.50 °C and -550 is -5.50 °C.
In the previous post we connected a DS18B20 to an Ardbox, an Arduino based PLC. Here the Ardbox becomes a Modbus RTU slave that publishes the temperature, and a short Python program reads it as the master.
How does the temperature reach the Modbus master?
- Step 1DS18B20Digital sensor on a 1-Wire bus, from -55 to 125 °C.
- Step 2Ardbox PLCReads the sensor every 2 s and saves the value x 100 in an input register.
- Step 3RS-485Modbus RTU, 9600 bit/s, 8N1, slave address 1.
- Step 4MasterReads input registers 0 and 1 with FC04 and divides the temperature by 100.
| Input register (FC04) | Content | Format |
|---|---|---|
| Address 0 (30001) | Temperature x 100 | Signed 16-bit (int16): 2150 = 21.50 °C, -550 = -5.50 °C |
| Address 1 (30002) | Sensor status | 1 = reading OK, 0 = sensor not found or bad reading |
Modbus registers are 16-bit words with no decimals, so the temperature travels as hundredths of a degree. A negative temperature becomes a value above 32767 if the master reads it as unsigned: configure the master to read a signed integer (INT16).
Modbus in two minutes
Modbus is a request and response protocol that Modicon published in 1979 for its PLCs, and it is still the most common way to connect industrial devices. A master (client) sends a request with the address of one slave (server), and only that slave answers. Modbus RTU runs on serial lines such as RS-485, and Modbus TCP runs on Ethernet. Every frame carries a CRC, so the receiver can discard a corrupted frame.

A slave exposes four data areas: coils and discrete inputs (bits), holding registers and input registers (16-bit words). A measurement that the master only reads, like this temperature, fits in an input register.
Libraries for the Arduino IDE
- OneWire: the 1-Wire bus of the DS18B20.
- DallasTemperature: commands and conversion of the DS18B20.
- Industrial Shields arduino-Modbus: the Modbus RTU slave.
You also need the Industrial Shields boards in the Arduino IDE, for the RS-485 port and the pins of the PLC. Follow how to install the Industrial Shields boards in the Arduino IDE.
Arduino code of the Modbus slave
// Ardbox Analog HF+ w/ HW RS-485 as Modbus RTU slave with a DS18B20 sensor
// Input register 0: temperature x 100, signed (2150 = 21.50 C, -550 = -5.50 C)
// Input register 1: sensor status (1 = reading OK, 0 = no sensor or bad reading)
#include <OneWire.h>
#include <DallasTemperature.h>
#include <RS485.h>
#include <ModbusRTUSlave.h>
#define ONE_WIRE_BUS 2 // DS18B20 data pin, wired as in the connection guide
#define RS485_RATE 9600UL // must match the master
#define SLAVE_ADDRESS 1 // Modbus address of this PLC (1 to 247)
#define READ_PERIOD_MS 2000UL // new temperature every 2 s
#define CONVERSION_MS 750UL // DS18B20 conversion time at 12-bit resolution
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensor(&oneWire);
uint16_t inputRegisters[2];
ModbusRTUSlave modbus(RS485, SLAVE_ADDRESS);
uint32_t requestTime;
bool resultPending = true; // a conversion was started and not read yet
void setup() {
Serial.begin(9600);
sensor.begin();
sensor.setWaitForConversion(false); // do not block the Modbus slave for 750 ms
sensor.requestTemperatures();
requestTime = millis();
RS485.begin(RS485_RATE, HALFDUPLEX, SERIAL_8N1);
modbus.begin(RS485_RATE);
modbus.setInputRegisters(inputRegisters, 2);
}
void loop() {
if (resultPending && millis() - requestTime >= CONVERSION_MS) {
float temp = sensor.getTempCByIndex(0);
if (temp == DEVICE_DISCONNECTED_C) {
inputRegisters[1] = 0; // keep the last value and flag the error
} else {
int16_t centi = (int16_t)(temp * 100.0f + (temp >= 0 ? 0.5f : -0.5f));
inputRegisters[0] = (uint16_t)centi; // two's complement for negative values
inputRegisters[1] = 1;
Serial.println(temp);
}
resultPending = false; // read each conversion only once
}
if (millis() - requestTime >= READ_PERIOD_MS) {
sensor.requestTemperatures(); // start the next conversion
requestTime = millis();
resultPending = true;
}
modbus.update(); // answer the master as often as possible
}
What each part does:
- Non-blocking conversion. A DS18B20 needs up to 750 ms to convert at 12-bit resolution. With
setWaitForConversion(false)the sketch starts the conversion and reads the result 750 ms later, somodbus.update()keeps answering the master in the meantime. - Signed value. The temperature x 100 goes through
int16_tbefore it is saved in the register, so negative temperatures arrive as two's complement and the master can read them as INT16. - Status register. If the library returns
DEVICE_DISCONNECTED_C(-127), the sketch keeps the last value and writes 0 in register 1. The master knows that the reading is not valid. - Pin 2. The data line of the sensor goes to Arduino pin 2, as in the wiring post. On the Ardbox HF+ this pin is also the SDA line of I2C, so do not use I2C at the same time.
In the Arduino IDE, select Tools > Board > Industrial Shields boards > Ardbox family and, in Model, Ardbox Analog HF+ w/ HW RS-485. Then select the port, upload the sketch and open the serial monitor at 9600 to see the temperature.
Changes from the first version of this post. The first sketch saved temp * 100 straight into an unsigned register, so a negative temperature had no defined value. It also blocked the Modbus slave during each 750 ms conversion, and it published -127 °C as a temperature when the sensor was missing. Its code blocks also had non-breaking spaces, and the compiler stops at them with "stray '\240' in program".
Read the temperature from a Python master
Any Modbus RTU master can read the Ardbox. This example uses pymodbus 3.16.1 on a Raspberry PLC, or on a PC with a USB to RS-485 adapter. Connect A+ to A+ and B- to B- of the two RS-485 ports. The installation of pymodbus and the RS-485 port names of each Raspberry PLC version are in the Modbus guide for Raspberry Pi PLCs.
from pymodbus.client import ModbusSerialClient
from pymodbus.exceptions import ModbusException
from pymodbus.framer import FramerType
PORT = "/dev/ttySC0" # RS-485 port of the master, check the name for your PLC version
DEVICE_ID = 1 # SLAVE_ADDRESS of the Ardbox sketch
client = ModbusSerialClient(port=PORT, framer=FramerType.RTU, baudrate=9600,
bytesize=8, parity="N", stopbits=1, timeout=1)
if not client.connect():
raise SystemExit(f"Cannot open {PORT}")
try:
rr = client.read_input_registers(0, count=2, device_id=DEVICE_ID) # FC04
if rr.isError():
print("Exception response:", rr)
elif rr.registers[1] == 0:
print("Sensor not found or bad reading")
else:
raw = rr.registers[0]
centi = raw - 65536 if raw > 32767 else raw # signed 16-bit
print(f"Temperature: {centi / 100:.2f} C (raw {raw})")
except ModbusException as exc:
print("No valid answer:", exc)
finally:
client.close()
Output with the slave values that we used in the test:
Temperature: 21.50 C (raw 2150)
Temperature: -5.50 C (raw 64986)
Sensor not found or bad reading
What we tested. The slave sketch compiles without errors for the Ardbox Analog HF+ w/ HW RS-485 with the Industrial Shields AVR boards, arduino-Modbus 1.1.0, OneWire 2.3.8 and DallasTemperature 4.0.6 (13,366 bytes of program memory, 40.8 %). The Python master ran with pymodbus 3.16.1 against a simulated Modbus RTU slave over a virtual serial link, with the values 2150, 64986 (-550) and a status 0. We did not run the sketch on an Ardbox with a real sensor for this update.
Common problems
| What you see | Usual cause | What to check |
|---|---|---|
| Status 0, or -127 in the serial monitor | The sensor does not answer on the 1-Wire bus | Wiring, sensor power and the 4.7 kΩ pull-up resistor between data and VDD |
| 85.00 °C | 85 °C is the power-on value of the DS18B20: the conversion did not finish | Sensor power, parasite power mode, and that the reading waits for the conversion |
| A value near 65000 | The master reads a negative temperature as unsigned | Read the register as INT16, or subtract 65536 above 32767 |
| Modbus timeout | No answer from the slave | Slave address, 9600 8N1 on both sides, A and B not swapped, RS-485 termination |
| Exception 02 (illegal data address) | The master reads a register that does not exist | Only input registers 0 and 1 exist in this sketch |
For the RS-485 side, follow the step-by-step RS-485 bus diagnosis.
Temperature over Modbus in a real project. In the fishing vessel case study, one Arduino based PLC per refrigerated hold measures temperature, humidity and air quality, and Modbus connects the PLCs with the screens of the control room.
Read the fishing vessel case studyChoose an industrial controller
FAQ about DS18B20 and Modbus
Can I connect a DS18B20 directly to a Modbus network?
No. The DS18B20 only speaks 1-Wire. A PLC or a converter reads it and publishes the value in a Modbus register.
Input register or holding register?
An input register, because the master only reads the measurement. Some masters only read holding registers (FC03): in that case use setHoldingRegisters in the sketch.
How do I send decimals over Modbus?
Scale the value: x 100 gives two decimals in one 16-bit register. For a wider range or more resolution, use two registers with a 32-bit integer or a float.
Can I read several DS18B20 on the same pin?
Yes. Each DS18B20 has its own 64-bit ROM code, so several sensors share the 1-Wire bus. Read them with getTempCByIndex(0), getTempCByIndex(1) and so on, and give each one its own input register.
Does the code work on other Industrial Shields PLCs?
The libraries are the same. Select your board in the Arduino IDE and check the pin of the 1-Wire bus and the RS-485 port in its user guide. We only compiled it for the Ardbox Analog HF+ w/ HW RS-485.
More about temperature and Modbus
- How to connect the Dallas DS18B20 temperature sensor to an Arduino PLC
- Node-RED and Touchberry: get the temperature of the Dallas sensor over Modbus RTU
- How to read the Dallas sensor and log the data on a µSD card
- DS18B20 temperature on a Raspberry PLC over 1-Wire
- Modbus RTU with Arduino and ESP32 based PLCs
- Modbus RTU frame, function codes and CRC
See the Ardbox Analog PLC.