Ir al contenido

Tutorial Modbus: Cómo obtener la temperatura con un sensor Dallas

Sensor DS18B20, PLC Ardbox como esclavo Modbus RTU y un maestro en Python
30 de julio de 2021 por
Tutorial Modbus: Cómo obtener la temperatura con un sensor Dallas
Boot & Work Corp. S.L, Fernandez Queralt Martinez

Guía completa: Modbus RTU y TCP con Python y Node-RED

Para compartir la temperatura de un sensor Dallas DS18B20 por Modbus, el PLC que tiene el sensor lo lee con las librerías OneWire y DallasTemperature, la multiplica por 100 y la copia en un registro de entrada como valor de 16 bits con signo. Un maestro Modbus RTU, como un SCADA, un HMI u otro PLC, lee ese registro con el código de función 04 por RS-485 y lo divide por 100: 2150 son 21,50 °C y -550 son -5,50 °C.

En el post anterior conectamos un DS18B20 a un Ardbox Arduino PLC, un PLC basado en Arduino. Aquí el Ardbox Arduino PLC pasa a ser un esclavo Modbus RTU que publica la temperatura, y un programa corto en Python la lee como maestro.

¿Cómo llega la temperatura al maestro Modbus?

  1. Paso 1DS18B20Sensor digital en un bus 1-Wire, de -55 a 125 °C.
  2. Paso 2Ardbox Arduino PLCLee el sensor cada 2 s y guarda el valor x 100 en un registro de entrada.
  3. Paso 3RS-485Modbus RTU, 9600 bit/s, 8N1, dirección de esclavo 1.
  4. Paso 4MaestroLee los registros de entrada 0 y 1 con FC04 y divide la temperatura por 100.
Registro de entrada (FC04)ContenidoFormato
Dirección 0 (30001)Temperatura x 100Entero de 16 bits con signo (int16): 2150 = 21,50 °C, -550 = -5,50 °C
Dirección 1 (30002)Estado del sensor1 = lectura correcta, 0 = sensor no encontrado o lectura errónea

Los registros Modbus son palabras de 16 bits sin decimales, así que la temperatura viaja en centésimas de grado. Una temperatura negativa se convierte en un valor mayor que 32767 si el maestro la lee sin signo: configura el maestro para leer un entero con signo (INT16).

Modbus en dos minutos

Modbus es un protocolo de petición y respuesta que Modicon publicó en 1979 para sus PLC, y sigue siendo la forma más habitual de conectar equipos industriales. Un maestro (cliente) envía una petición con la dirección de un esclavo (servidor), y solo ese esclavo responde. Modbus RTU funciona sobre líneas serie como RS-485, y Modbus TCP sobre Ethernet. Cada trama lleva un CRC, así que el receptor puede descartar una trama dañada.

Esquema de una red Modbus: SCADA y un navegador web en Modbus TCP por Ethernet, y un panel de operador y un controlador de temperatura en Modbus RTU por RS-485
Modbus TCP por Ethernet entre SCADA, paneles de operador y controladores, y Modbus RTU por RS-485 hasta un panel de operador y un controlador de temperatura.

Un esclavo ofrece cuatro áreas de datos: coils y entradas discretas (bits), registros holding y registros de entrada (palabras de 16 bits). Una medida que el maestro solo lee, como esta temperatura, va en un registro de entrada.

Librerías para el IDE de Arduino

También necesitas las placas de Industrial Shields en el IDE de Arduino, para el puerto RS-485 y los pines del PLC. Sigue cómo instalar las placas de Industrial Shields en el IDE de Arduino.

Código Arduino del esclavo Modbus

// 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
}

Qué hace cada parte:

  • Conversión sin bloqueo. Un DS18B20 necesita hasta 750 ms para convertir con resolución de 12 bits. Con setWaitForConversion(false) el sketch inicia la conversión y lee el resultado 750 ms después, así que modbus.update() sigue respondiendo al maestro mientras tanto.
  • Valor con signo. La temperatura x 100 pasa por int16_t antes de guardarse en el registro, así que las temperaturas negativas llegan en complemento a dos y el maestro las puede leer como INT16.
  • Registro de estado. Si la librería devuelve DEVICE_DISCONNECTED_C (-127), el sketch mantiene el último valor y escribe 0 en el registro 1. El maestro sabe que la lectura no es válida.
  • Pin 2. La línea de datos del sensor va al pin 2 de Arduino, como en el post de conexión. En el Ardbox HF+ este pin también es la línea SDA del I2C, así que no uses el I2C a la vez.

En el IDE de Arduino, selecciona Tools > Board > Industrial Shields boards > Ardbox Arduino PLC family y, en Model, Ardbox Analog HF+ w/ HW RS-485. Después selecciona el puerto, sube el sketch y abre el monitor serie a 9600 para ver la temperatura.

Cambios respecto a la primera versión de este post. El primer sketch guardaba temp * 100 directamente en un registro sin signo, así que una temperatura negativa no tenía un valor definido. También bloqueaba el esclavo Modbus durante cada conversión de 750 ms, y publicaba -127 °C como temperatura cuando faltaba el sensor. Además, sus bloques de código tenían espacios de no separación, y el compilador se detiene en ellos con "stray '\240' in program".

Lee la temperatura desde un maestro en Python

Cualquier maestro Modbus RTU puede leer el Ardbox Arduino PLC. Este ejemplo usa pymodbus 3.16.1 en un Raspberry PLC, o en un PC con un adaptador USB a RS-485. Conecta A+ con A+ y B- con B- de los dos puertos RS-485. La instalación de pymodbus y los nombres del puerto RS-485 de cada versión de Raspberry PLC están en la guía de Modbus para PLC con Raspberry Pi.

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()

Salida con los valores de esclavo que usamos en la prueba:

Temperature: 21.50 C (raw 2150)
Temperature: -5.50 C (raw 64986)
Sensor not found or bad reading

Qué hemos probado. El sketch del esclavo compila sin errores para el Ardbox Analog HF+ w/ HW RS-485 con las placas AVR de Industrial Shields, arduino-Modbus 1.1.0, OneWire 2.3.8 y DallasTemperature 4.0.6 (13.366 bytes de memoria de programa, 40,8 %). El maestro en Python se ejecutó con pymodbus 3.16.1 contra un esclavo Modbus RTU simulado en un enlace serie virtual, con los valores 2150, 64986 (-550) y un estado 0. No hemos ejecutado el sketch en un Ardbox Arduino PLC con un sensor real para esta actualización.

Problemas habituales

Qué vesCausa habitualQué revisar
Estado 0, o -127 en el monitor serieEl sensor no responde en el bus 1-WireEl cableado, la alimentación del sensor y la resistencia pull-up de 4,7 kΩ entre datos y VDD
85,00 °C85 °C es el valor de arranque del DS18B20: la conversión no ha terminadoLa alimentación del sensor, el modo de alimentación parásita, y que la lectura espere a la conversión
Un valor cercano a 65000El maestro lee una temperatura negativa sin signoLee el registro como INT16, o resta 65536 por encima de 32767
Timeout de ModbusEl esclavo no respondeDirección del esclavo, 9600 8N1 en los dos lados, A y B sin cruzar, terminación RS-485
Excepción 02 (dirección de datos no válida)El maestro lee un registro que no existeEn este sketch solo existen los registros de entrada 0 y 1

Para la parte RS-485, sigue el diagnóstico paso a paso del bus RS-485.

Temperatura por Modbus en un proyecto real. En el caso de estudio del barco pesquero, un PLC basado en Arduino en cada bodega refrigerada mide la temperatura, la humedad y la calidad del aire, y Modbus conecta los PLC con las pantallas de la sala de control.

Lee el caso del barco pesqueroElige un controlador industrial

Preguntas frecuentes sobre DS18B20 y Modbus

¿Puedo conectar un DS18B20 directamente a una red Modbus?

No. El DS18B20 solo habla 1-Wire. Un PLC o un convertidor lo lee y publica el valor en un registro Modbus.

¿Registro de entrada o registro holding?

Un registro de entrada, porque el maestro solo lee la medida. Algunos maestros solo leen registros holding (FC03): en ese caso usa setHoldingRegisters en el sketch.

¿Cómo envío decimales por Modbus?

Escala el valor: x 100 da dos decimales en un registro de 16 bits. Para más rango o más resolución, usa dos registros con un entero de 32 bits o un float.

¿Puedo leer varios DS18B20 en el mismo pin?

Sí. Cada DS18B20 tiene su propio código ROM de 64 bits, así que varios sensores comparten el bus 1-Wire. Léelos con getTempCByIndex(0), getTempCByIndex(1), etc., y dale a cada uno su propio registro de entrada.

¿El código funciona en otros PLC de Industrial Shields?

Las librerías son las mismas. Selecciona tu placa en el IDE de Arduino y comprueba el pin del bus 1-Wire y el puerto RS-485 en su guía de usuario. Nosotros solo lo hemos compilado para el Ardbox Analog HF+ w/ HW RS-485.

Buscar en nuestro blog

Tutorial Modbus: Cómo obtener la temperatura con un sensor Dallas
Boot & Work Corp. S.L, Fernandez Queralt Martinez 30 de julio de 2021
Compartir esta publicación

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.

Comparativa de PLCs