How to use PyModbus with Raspberry Pi PLC

How to communicate through Modbus using a RPI PLC and the PyModbus library
September 25, 2023 by
How to use PyModbus with Raspberry Pi PLC
Boot & Work Corp. S.L., Ricard Franch Argullol

What is Modbus communication?

Modbus communication plays a crucial role in industrial automation, enabling devices to exchange data seamlessly. In this blog post, we'll explore how to implement Modbus communication using the pymodbus library on our Raspberry Pi PLCs. We'll cover both synchronous and asynchronous approaches to write alternating True/False values to a coil on a slave device and read the coil status. ooo

How to install pymodbus library?

Raspberry Pi PLC

PyModbus library

The PyModbus library can be installed with the command "pip install pymodbus". In addition, it might be beneficial to set up a virtual Python environment (venv).

Modbus Communication: modbus synchronous vs asynchronous

The objective of the "synchronous master" code is to demonstrate how to establish a Modbus communication link as a master using the pymodbus library. It focuses on writing alternating True/False values at the slave's first coil, and reads it to ensure that the value is alternating.

As for "the asynchronous slave" code is to demonstrate how to set up a Modbus slave server using the pymodbus library. It includes the creation of discrete inputs, coils, holding registers, and input registers, which can be accessed by the Modbus master.

What is the difference between asynchronous and synchronous approach?

When it comes to implementing Modbus communication using the pymodbus library on your Raspberry Pi PLCs, you'll encounter two different approaches to structure your program, synchronous and asynchronous. Each approach has its own set of advantages and considerations, and understanding the differences between them will empower you to make informed decisions based on your project requirements.

Synchronous Modbus Communication: Understanding Linear Execution Flow with pymodbus

Synchronous communication involves a linear and straightforward execution flow. Each task is completed before moving on to the next, making it easy to follow and comprehend. In the context of our Modbus communication example, synchronous code allows you to write and read coil values sequentially without the need to manage complex event loops or asynchronous contexts.

Advantages of synchronous communication:
  • Simplicity and Readability: Synchronous code tends to be easier to read and understand, making it an ideal starting point for those new at programming.
  • Predictable Timing: Synchronous communication offers more predictable timing and sequencing of operations, which can be advantageous for applications that require precise coordination.
  • Fewer Concurrency Challenges: Synchronous code simplifies concurrency management, reducing the potential for race conditions and related bugs.
  • Easier Debugging: Debugging synchronous code is often simpler due to its linear execution flow, aiding in identifying and resolving issues efficiently.

However, while the synchronous approach provides simplicity and ease of understanding, it's important to be aware of its performance limitations, particularly in scenarios with high-demand or time-sensitive applications. Synchronous clients are not inherently thread-safe, and using a single client from multiple threads is not recommended, due to conflicts that arise from the nature of the Modbus protocol. Although possible (with proper locking mechanisms) it's easier to implement complex programs following the asynchronous paradigm.


What is asynchronous communication?

Asynchronous communication, on the other hand, embraces parallelism and responsiveness, offering a powerful tool for scenarios where your program is heavily I/O bounded, such as in the case of Modbus communication. This approach enables tasks to execute concurrently, leading to a more efficient utilization of system resources and heightened responsiveness to external events. This level of efficiency can be particularly advantageous when working with I/O-intensive tasks like Modbus, as it ensures that system resources are maximally utilized, resulting in an optimized performance.

Advantages of Asynchronous Communication
  • Enhanced Responsiveness: Asynchronous communication enables your application to promptly respond to external events, making it suitable for real-time or interactive scenarios.
  • Resource Efficiency: Asynchronous code can utilize system resources more efficiently by allowing tasks to run concurrently, potentially improving overall system performance.
  • Concurrency: Asynchronous communication supports multiple tasks running concurrently, making it well-suited for applications with high levels of parallelism.
  • Scalability: Asynchronous programming can provide better scalability for applications that need to handle a large number of clients or tasks simultaneously.

How to Set Up a Synchronous modbus server with PyModbus on Raspberry Pi?

Let's take a quick look at a basic server (slave) program, which takes the synchronous approach,  using the PyModbus library. 

The script starts by importing necessary libraries. Then the server configuration and context setup takes place: data blocks (discrete inputs, coils, holding registers, input registers) as well as the Modbus slave context are created. This slave is assigned the address 0x01. Next, the server is properly configured and started. ModbusTCP (TCP) and ModbusRTU (Serial) can be used. Finally, in the main execution, the server is started using the run_sync_server function.

Keep in mind that this script provides a basic setup for a Modbus server, and you might need to modify it according to your specific requirements.

#!/usr/bin/env python3
import logging
logging.basicConfig()
_logger = logging.getLogger(__file__)
_logger.setLevel("INFO")
# Enable pymodbus information
from pymodbus import pymodbus_apply_logging_config
pymodbus_apply_logging_config("INFO")

# --------------------------------------------------------------------------- #
# import the various client implementations
# --------------------------------------------------------------------------- #
from pymodbus.server import (
StartSerialServer,
StartTcpServer,
)

from pymodbus.transaction import ModbusRtuFramer, ModbusSocketFramer
from pymodbus.datastore import ModbusSequentialDataBlock, ModbusSlaveContext, ModbusServerContext

def run_sync_server(server_type="tcp", ip="127.0.0.1", port=5020):
# Create 10 discrete inputs, 10 coils, 10 holding registers and 10 input registers at slave address 0x01
di = ModbusSequentialDataBlock(0, [0]*10)
co = ModbusSequentialDataBlock(0, [0]*10)
hr = ModbusSequentialDataBlock(0, [0]*10)
ir = ModbusSequentialDataBlock(0, [0]*10)
context = { 0x01: ModbusSlaveContext(di=di, co=co, hr=hr, ir=ir) }
context = ModbusServerContext(slaves=context, single=False)
try:
if server_type == "tcp":
_logger.info(f"Starting synchronous server, listening on {ip} - {port}")
StartTcpServer(
context = context, # Data storage
# identity = identity, # server identify
address = (ip, port), # listen address
# custom_functions=[], # allow custom handling
framer = ModbusSocketFramer, # The framer strategy to use
# broadcast_enable = True, # treat slave_id 0 as broadcast address,
# timeout=1, # waiting time for request to complete
)
elif server_type == "serial":
_logger.info( f"Starting synchronous server, using serial {port}")
StartSerialServer(
context = context,
# identity = identity, # server identify
framer = ModbusRtuFramer,
port = port,
baudrate = 38400,
bytesize = 8,
parity = 'N',
stopbits = 1,
timeout = 10,
)
_logger.info("Stopped server")
else:
_logger.error(f"Invalid server type selected ({server_type}), cannot start pymodbus server")
return
except KeyboardInterrupt:
print()

if __name__ == "__main__":
run_sync_server("tcp", "localhost", 5020)
# run_sync_server("serial", port="/dev/ttySC0")

How to create asynchronous Modbus client? 

As for the client (master) program, let's see one example taking the asynchronous approach. In this case, in addition to PyModbus, the asyncio library is used to write the concurrent code. 

As the previous example, the script starts by importing the required libraries. Then, the run_async_client function, which supports both TCP and RTU, is defined. Inside this function an asynchronous Modbus client is created. the client is then connected to the specified server and assert statements check if the connection was successful. Afterwards, the script enters a loop that toggles the value of a Modbus coil (coils are binary outputs) every 5 seconds and then reads the its value to verify the write operation, printing the read value. Finally, a main function which serves as the entry point for the script is defined and called using using asyncio.run to execute the asynchronous code.

This script demonstrates how to create a basic asynchronous Modbus client, therefore it might need some modifications to adapt it to certain requirements.

#!/usr/bin/env python3
import logging
logging.basicConfig()
_logger = logging.getLogger(__file__)
_logger.setLevel("INFO")
# Enable pymodbus information
from pymodbus import pymodbus_apply_logging_config
pymodbus_apply_logging_config("INFO")

import asyncio
from pymodbus.client import AsyncModbusTcpClient, AsyncModbusSerialClient
from pymodbus.transaction import ModbusSocketFramer, ModbusRtuFramer
from pymodbus.exceptions import ModbusException

async def run_async_client(client_type="tcp", host="127.0.0.1", port=5020):
global client
if client_type == "tcp":
client = AsyncModbusTcpClient(
host = host,
port = port,
framer = ModbusSocketFramer,
)
elif client_type == "serial":
client = AsyncModbusSerialClient(
port = port,
framer = ModbusRtuFramer,
baudrate = 38400,
bytesize = 8,
parity = 'N',
stopbits = 1,
# timeout = 1, # waiting time for request to complete
)
else:
_logger.error(f"Invalid client type selected ({client_type}), cannot start pymodbus client")
return -1

_logger.info("Connecting to the server...")
await client.connect()
assert client.connected
_logger.info("Connected!")

return_code = 0
a = False
_logger.info("Starting with coil[0] = False")
while True:
a = not a
await asyncio.sleep(5)
try:
await client.write_coil(0, a, 0x01)
response = await client.read_coils(0, 1, 0x01)
assert not response.isError()
assert len(response.bits) == 8
_logger.info(f"coil[0] has value {response.bits[0]}")
except ModbusException as exc:
_logger.error(f"Received ModbusException({exc}) from library")
return_code = -1
break
return return_code

async def main():
global client
status = 0
try:
status = await run_async_client("tcp", "localhost", 5020)
# status = await run_async_client("serial", port="/dev/ttySC0")
except KeyboardInterrupt:
print()
except Exception as e:
status = -1
_logger.error(f"Unknown exception ocurred: {e}")
finally:
client.close()
return status

if __name__ == "__main__":
exit(asyncio.run(main(), debug=True))


Unlocking the Potential of Raspberry Pi PLCs with Powerful Modbus Communication

Harnessing the power of Modbus communication with a Raspberry Pi PLC can significantly streamline the process of data exchange in industrial automation settings. As demonstrated in this post, the pymodbus library provides a robust and efficient means to integrate Modbus communication, whether you opt for a synchronous or asynchronous approach.


Each method has its unique advantages—while the synchronous approach offers simplicity and predictability, the asynchronous method excels in efficiency and scalability, particularly in I/O-intensive tasks. The choice between the two would largely depend on your project's specific demands and your level of familiarity with programming paradigms.


The example scripts provided serve as a foundation and may require modifications tailored to your needs. By understanding the core concepts and the structure of these scripts, you're well on your way to building powerful and reliable Modbus communication systems for your Raspberry Pi PLCs.


Always remember, whether you're dealing with a virtual keyboard or establishing complex Modbus communications, the Raspberry Pi PLC offers immense versatility. Stay curious, keep experimenting, and leverage the potential of your devices to the fullest!

​Search in our Blog

How to use PyModbus with Raspberry Pi PLC
Boot & Work Corp. S.L., Ricard Franch Argullol September 25, 2023
Share this post

Looking for your ideal Programmable Logic Controller?

Take a look at this product comparison with other industrial controllers Arduino-based. 

We are comparing inputs, outputs, communications and other features with the ones of the relevant brands.


Industrial PLC comparison >>>