Y bueno se va la 2da patita de 3, hoy les comentare la forma mas básica creo de implementar el Modbus RTU en una tarjeta Raspberrie Pico (RP de aqui en adelante), en la entrega anterior les comente de mi sistema de desarrollo incluso mostré una foto de este, en esta ocasión la idea es mostrara un esclavo muy básico que responda a la funcion3 del Modbus que es Leer los Holding Register del RP, lo primero que implemente es un parser en este para analizar que esta llegando por la port serial asociado al RS485 y después la 2 parte despachar la respuesta al comando o ModbusRTU.py / Tx/Rx => PIN 1 y PIN 2 en raspberry pico Modbus en esta parte se complica la cosa porque la respuesta debe contar al final con un CRC16 para que el Modbus Poll pueda verificar la integridad de la trama enviada y la 3 parte y final es enviar la trama completa al Modbus Poll.
Bueno a continuación le muestro el código que cree usando micropyhon en Thonny, tiene bastantes comentarios para que lo puedan implementar si así lo desean.
# ModbusRTU.py / Tx/Rx => PIN 1 y PIN 2 en raspberry pico
# 17-01-2020 funcionando el Modo HR en modo RTU para el modbus se pueden leer 10 registros .
import os
from machine import UART
import machine
from time import sleep
# crea el CRC y lo agrega al en las ultimas 2 posiciones del arreglo
def calc(data : bytearray, n):
crc_table= [0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040]
crc_hi=0xFF
crc_lo=0xFF
for i in range(n+1):
a=data[i]
index=crc_lo ^ a
crc_val=crc_table[index]
crc_temp=crc_val//256
crc_val_low=crc_val-(crc_temp*256)
crc_lo=crc_val_low ^ crc_hi
crc_hi=crc_temp
crc=crc_hi*256 +crc_lo
data[n+1]=crc_lo
data[n+2]=crc_hi
return(data)
# Parser analiza lo que llega por la port serial para ver si es una trama Modbus Valida
def lee_trama_mb(addr_mb):
paso = 1
while paso < 10 :
if uart.any():
b = uart.read()
if paso == 1 : # paso 1 leemos la direccion
addr = b[0]
paso = paso + 1
if paso == 2: # leemos la funcion
funcion = b[1]
paso = paso + 1
if paso == 3: # leemos la direccion de origen msb
addr_msb = b[2]
paso = paso + 1
if paso == 4: # leemos la direccion de origen Lsb
addr_lsb = b[3]
paso = paso + 1
if paso == 5: # leemos nro de registros msb
nreg_msb = b[4]
paso = paso + 1
if paso == 6: # leemos nro de registros Lsb
nreg_lsb = b[5]
paso = paso + 1
if paso == 7: # leemos codigo de chequeo CRC lo
crc_lo = b[6]
paso = paso + 1
if paso == 8: # leemos codigo de chequeo CRC hi
crc_hi = b[7]
paso = paso + 1
if paso == 9: # trama recivida generar respuesta dependieendo de la funcion
addr_reg = (addr_msb << 8) + addr_lsb
nreg = (nreg_msb << 8) + nreg_lsb
trama = bytearray(30)
if funcion == 0x03: # Corresponde a Holding Register y parten con 4000
trama[0] = addr # direccion del esclavo que responde
trama[1] = funcion # funcion a la que responde
trama[2] = nreg*2 # numero de bytes de datos a transmitir
for i in range(nreg * 2):
trama[3+i] = hr_tabla[i]
# calcular el CRC
trama = calc(trama,3+i)
uart.write(trama)
paso = 10
return(funcion, addr, nreg )
# Punto de partida
print('Modbus RTU V0.1')
uart = machine.UART(0, 9600)
print(uart)
contador = 0
hr_tabla=[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]
# loop Principal
while True:
print(contador)
lee_trama_mb(1)
hr_tabla[0]=contador
contador = contador + 1
Bueno y esta fue la 2da patita el código esta mostrado les comento que funciona como les dije lo probé con Modbus Poll y Kepserver V6.0.
En la próxima entrega a ampliare un poco mas el parser para soportar la función 6 de Modbus
Si desean mayor conocimiento del Protocolo Modbus en el Modbus Poll en la sección de ayuda existe una descripción muy completa incluso con tramas de ejemplo.
Y nos veremos pronto en la Tercera patita y Ultima Gracias.
Comentarios
Publicar un comentario