Ir al contenido

Modbus en Raspberry PLC con Python

Implementación para PLCs basados en Raspberry (Python)

Instalación

1. librpiplc implementa funciones útiles para utilizar los PLCs de Industrial Shields basados en Raspberry, en entornos industriales. Sigue los pasos de instalación desde aquí.

2. python3-librpiplc proporciona un wrapper en Python para la librería C librpiplc, permitiendo que las aplicaciones en Python interactúen con los GPIOs de los PLCs de Industrial Shields basados en Raspberry Pi. Sigue los pasos de instalación desde aquí.

3. pymodbus es una librería de Python que ofrece implementaciones de cliente y servidor para el protocolo Modbus. Consulta la documentación aquí. Instálala ejecutando el comando: pip install pymodbus.

Esclavo

Los siguientes ejemplos son implementaciones de esclavo Modbus, tanto con RTU como con TCP:


Esclavo Modbus RTU 


Esclavo Modbus TCP 

Guía de implementación

1. Device configuration (configuración del dispositivo): El código comienza definiendo constantes como la dirección del esclavo Modbus y la versión y modelo del PLC RPI. También incluye un diccionario de configuración, que especifica el número de salidas digitales, analógicas, entradas analógicas y digitales, para el modelo específico de PLC que se está utilizando. Proporcionando así, parámetros esenciales para la configuración e interacción del dispositivo. Este ejemplo usa la versión V4 del RPI PLC 21+. ¡Cambia estos parámetros según tu PLC!

# Define the slave address
SLAVE_ADDR = 1

# Define your version and model
VERSION = "RPIPLC_V4"
MODEL = "RPIPLC_21"

# Define the configuration for the PLC models (Define your PLC pinouts here)
PLC_CONFIG = {
    'RPIPLC_21': {'Q': 5, 'A': 3, 'AI': 6, 'I': 7}
}

2. Initializing the PLC (inicialización del PLC): Se define una función de inicialización, para configurar el hardware del PLC y establecer los modos de los pines, según el modelo especificado. Este paso asegura que el PLC esté correctamente configurado y listo para interactuar con el servidor Modbus, estableciendo una configuración básica, para las tareas de comunicación posteriores.

def init():
    # Initialize the RPI PLC
    rpiplc.init(VERSION, MODEL)
    
    # Set pin modes for digital and analog inputs and outputs
    for i in range(PLC_CONFIG[MODEL]['Q']):
        pin_name = "Q0.{}".format(i)
        rpiplc.pin_mode(pin_name, rpiplc.OUTPUT)

    for i in range(PLC_CONFIG[MODEL]['A']):
        pin_name = "A0.{}".format(i + 5)
        rpiplc.pin_mode(pin_name, rpiplc.OUTPUT)

    for i in range(PLC_CONFIG[MODEL]['I']):
        pin_name = "I0.{}".format(i)
        rpiplc.pin_mode(pin_name, rpiplc.INPUT)

    for i in range(PLC_CONFIG[MODEL]['AI']):
        pin_name = "I0.{}".format(i + 7)
        rpiplc.pin_mode(pin_name, rpiplc.INPUT)

3. Running the server (ejecución del servidor): El script configura un servidor Modbus, definiendo bloques de datos para entradas discretas, bobinas (coils), registros de retención (holding registers) y registros de entrada (input registers). Crea un contexto de esclavo Modbus con estos bloques de datos y arranca el servidor Modbus TCP en un hilo separado. Este paso permite que el servidor escuche solicitudes Modbus entrantes y responda de manera adecuada, facilitando la comunicación con clientes Modbus externos.

def run_server():
    # Define data blocks with 100 registers
    di = ModbusSequentialDataBlock(0, [0] * 100)
    co = ModbusSequentialDataBlock(0, [0] * 100)
    hr = ModbusSequentialDataBlock(0, [0] * 100)
    ir = ModbusSequentialDataBlock(0, [0] * 100)

    # Create a Modbus slave context
    store = ModbusSlaveContext(di=di, co=co, hr=hr, ir=ir)
    context = ModbusServerContext(slaves={SLAVE_ADDR: store}, single=False)

    # Start the Modbus TCP server in a separate thread
    server_thread = Thread(target=start_modbus_server, args=(context,), daemon=True)
    server_thread.start()

    # Main loop to update PLC data based on Modbus register values
    try:
        while True:
            update_plc_datastore(store)
            time.sleep(0.1)
    except KeyboardInterrupt:
        print("Server stopped")

4. Start Modbus Server (iniciar servidor Modbus): La función start_modbus_server() es responsable de inicializar y arrancar el servidor Modbus. Dependiendo del protocolo (TCP o RTU), esta función configura el servidor con los parámetros de comunicación adecuados y lo inicia. Permitiendo así,  que gestione las solicitudes Modbus de los clientes. El parámetro context representa el contexto del servidor Modbus, que contiene los bloques de datos para entradas discretas, bobinas (coils), registros de retención (holding registers) y registros de entrada (input registers). Esta función gestiona y responde a las solicitudes Modbus.

  • Para Modbus TCP, el servidor se configura con la dirección, que incluye la dirección IP y el puerto utilizado:
# Start Modbus Server TCP
def start_modbus_server(context):
    StartTcpServer(context=context, address=("10.10.10.20", 502))
  • Para Modbus RTU, el servidor se configura con los parámetros de la comunicación serie: puerto serie, velocidad en baudios, bits de datos, paridad y bits de parada:
# Start Modbus Server RTU
def start_modbus_server(context):
    StartSerialServer(context=context,
                      port='/dev/ttySC0',
                      baudrate=38400,
                      bytesize=8,
                      parity='E',
                      stopbits=1,
                      )

5. Updating PLC datastore (actualización del almacén de datos del PLC): Se implementa una función de actualización que sincroniza el estado del PLC con los valores de los registros Modbus. Esta función lee los valores de las bobinas (coils) y de los registros de retención (holding registers) del almacén Modbus y los escribe en las salidas digitales y analógicas correspondientes del PLC, respectivamente. También lee los valores de las entradas digitales y analógicas del PLC y actualiza el almacén Modbus en consecuencia, de modo que la comunicación entre el PLC y los clientes Modbus es bidireccional.

def update_plc_datastore(store):
    # Update digital outputs
    coils = store.getValues(1, 0, PLC_CONFIG[MODEL]['Q'])
    for i in range(PLC_CONFIG[MODEL]['Q']):
        pin_name = "Q0.{}".format(i)
        rpiplc.digital_write(pin_name, coils[i])

    # Update analog outputs
    registers = store.getValues(3, 0, PLC_CONFIG[MODEL]['A'])
    for i in range(PLC_CONFIG[MODEL]['A']):
        pin_name = "A0.{}".format(i + 5)
        rpiplc.analog_write(pin_name, registers[i])

    # Update digital inputs
    di_values = []
    for i in range(PLC_CONFIG[MODEL]['I']):
        pin_name = "I0.{}".format(i)
        value = rpiplc.digital_read(pin_name)
        di_values.append(value)

    store.setValues(2, 0, di_values)

    # Update analog inputs
    ai_values = []
    for i in range(PLC_CONFIG[MODEL]['AI']):
        pin_name = "I0.{}".format(i + 7)
        value = rpiplc.analog_read(pin_name)
        ai_values.append(value)

    store.setValues(4, 0, ai_values)

Maestro

Los siguientes módulos de código están escritos en Python y usan la librería pymodbus para ofrecer funciones para dispositivos maestro Modbus RTU y TCP:


Maestro Modbus RTU


Maestro Modbus TCP

Referencia de la API

Este módulo encapsula la funcionalidad necesaria para conectarse a un servidor Modbus y realizar operaciones de lectura y escritura en bobinas y registros. A continuación se describen en detalle los métodos de la clase, con su sintaxis, sus parámetros y códigos de ejemplo.

Para usar estas funciones, inicializa primero el cliente Modbus indicando el método de conexión, TCP o RTU, y los parámetros correspondientes.

  • Para TCP: modbus_client = ModbusTCP(host='10.10.10.20', port=502)
  • Para RTU: modbus_client = ModbusRTU(port='/dev/ttySC0', baudrate=38400, bytesize=8, parity='E', stopbits=1)


Si no se especifican los parámetros, se usan los valores por defecto:

  • Para TCP, los valores por defecto son host='10.10.10.20' y port=502.
  • Para RTU, los valores por defecto son port='/dev/ttySC0', baudrate=38400, bytesize=8, parity='E' y stopbits=1.

Descripción:

Lee el estado de las bobinas a partir de una dirección determinada (FC=01).

Sintaxis:

read_coil(address, quantity, slave_id)

Parámetros:

  • address: la dirección inicial de las bobinas.
  • quantity: el número de bobinas que se leen.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Read coils from the Modbus server
if modbus_client.connect():
    modbus_client.read_coil(address=1, quantity=10, slave_id=1)
    modbus_client.close()

Descripción:

Lee el estado de las entradas discretas a partir de una dirección determinada (FC=02).

Sintaxis:

read_discrete_input(address, quantity, slave_id)

Parámetros:

  • slave_id: la dirección del dispositivo esclavo.
  • address: la dirección inicial de las entradas discretas.
  • quantity: el número de entradas discretas que se leen.

Código de ejemplo:

# Read discrete inputs from the Modbus server
if modbus_client.connect():
    modbus_client.read_discrete_input(address=1, quantity=10, slave_id=1)
    modbus_client.close()

Descripción:

Lee los valores de los registros de retención a partir de una dirección determinada (FC=03).

Sintaxis:

read_holding_register(address, quantity, slave_id)

Parámetros:

  • address: la dirección inicial de los registros de retención.
  • quantity: el número de registros de retención que se leen.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Read holding registers from the Modbus server
if modbus_client.connect():
    modbus_client.read_holding_register(address=1, quantity=10, slave_id=1)
    modbus_client.close()

Descripción:

Lee los valores de los registros de entrada a partir de una dirección determinada (FC=04).

Sintaxis:

read_input_register(address, quantity, slave_id)

Parámetros:

  • address: la dirección inicial de los registros de entrada.
  • quantity: el número de registros de entrada que se leen.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Read input registers from the Modbus server
if modbus_client.connect():
    modbus_client.read_input_register(address=1, quantity=10, slave_id=1)
    modbus_client.close()

Descripción:

Escribe un único valor binario en la dirección de una bobina determinada (FC=05).

Sintaxis:

write_coil(address, value, slave_id)

Parámetros:

  • address: la dirección de la bobina.
  • value: el valor que se escribe en la bobina.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Write a single coil to the Modbus server
if modbus_client.connect():
    modbus_client.write_coil(address=1, value=True, slave_id=1)
    modbus_client.close()

Descripción:

Escribe un único valor en la dirección de un registro de retención determinado (FC=06).

Sintaxis:

write_register(address, value, slave_id)

Parámetros:

  • address: la dirección del registro de retención.
  • value: el valor que se escribe en el registro de retención.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Write a single register to the Modbus server
if modbus_client.connect():
    modbus_client.write_register(address=1, value=123, slave_id=1)
    modbus_client.close()

Descripción:

Escribe varios valores binarios en direcciones de bobina consecutivas (FC=15).

Sintaxis:

write_multiple_coils(address, value, slave_id)

Parámetros:

  • address: la dirección inicial de las bobinas.
  • value: los valores que se escriben en las bobinas.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Write multiple coils to the Modbus server
if modbus_client.connect():
    modbus_client.write_multiple_coils(address=1, value=[True, False, True], slave_id=1)
    modbus_client.close()

Descripción:

Escribe varios valores en direcciones consecutivas de registros de retención (FC=16).

Sintaxis:

write_multiple_registers(address, value, slave_id)

Parámetros:

  • address: la dirección inicial de los registros de retención.
  • value: los valores que se escriben en los registros de retención.
  • slave_id: la dirección del dispositivo esclavo.

Código de ejemplo:

# Write multiple registers to the Modbus server
if modbus_client.connect():
    modbus_client.write_multiple_registers(address=1, value=[123, 456, 789], slave_id=1)
    modbus_client.close()


Calificación
0 0

No hay comentarios por ahora.

para ser el primero en comentar.