LearnSCADA

Part III · Chapter 10

Setting Up Your Microcomputer

Time to build the workbench: a small Linux board, the Python tools, and a practice Modbus device that lives entirely in software. That's enough to run every example in the next several chapters without owning any industrial hardware.

By the end of this chapter you can

  • List what the lab needs, and which chapters need which parts.
  • Describe the software stack: Linux, Python 3, pymodbus and pyserial.
  • Create and activate a virtual environment and install the pinned pymodbus version.
  • Run a practice Modbus server on localhost and use the two-terminal workflow.

The reference machine is a Raspberry Pi: cheap, common, and Linux-based. Any comparable single-board computer, or an ordinary Linux laptop, works just as well. The commands are the same; only the box changes.

What you need

The core of the lab is the board and a way to reach it. For the Ethernet examples in Chapters 11 and 12 that's all: the board can talk Modbus TCP to itself and to other machines on your network with no extra parts. For the serial work in Chapter 13 you add one inexpensive accessory, a USB-to-RS-485 adapter, which gives the board a serial port suited to a real Modbus RTU bus.

ItemNeeded forNotes
Raspberry Pi (or similar)EverythingAny recent model; Linux-based
Power supply + SD cardEverythingStandard Pi essentials
Network connectionModbus TCPWired or wireless; same network as your PC
USB-to-RS-485 adapterModbus RTUAdded in Chapter 13
A real Modbus deviceOptionalA meter or sensor, once you're ready

None of it is expensive, and the first several chapters need only the bare board.

The software stack

The tools sit in a short stack. At the bottom is Linux, the operating system the board boots into (Raspberry Pi OS on a Pi). On Linux runs Python 3, pre-installed on the Pi and the language of every example. Inside Python we add one main library, pymodbus, an open-source package that implements the whole protocol, so you speak Modbus in a few lines instead of building frames by hand. For serial work it uses a companion, pyserial, to reach the serial port. That's the whole stack.

The software stack builds up layer by layer: Linux at the bottom, Python 3 on top of it, then pymodbus and pyserial inside Python, and finally your own programs at the top, which can now speak Modbus. Linux Raspberry Pi OS · boots the board Python 3 pre-installed · 3.9 or newer pymodbus the whole protocol pyserial serial port access Your programs read_client.py · practice_server.py … and now Python speaks Modbus
The software stack. Linux boots the board, Python 3 runs on Linux, and pymodbus (with pyserial for serial work) gives Python the ability to speak Modbus.

Start by bringing the system up to date. Open a terminal on the board, update the operating system, and confirm Python 3 is present (it almost always is):

TERMINAL — update and check Python
sudo apt update && sudo apt full-upgrade -y
python3 --version

The second command prints something like Python 3.11.2. Any Python 3.9 or newer is fine. If Python 3 is somehow missing, sudo apt install python3 python3-pip python3-venv installs it along with the two helpers used next.

A virtual environment

Before installing pymodbus, create a virtual environment: a self-contained folder that holds this project's Python packages separately from the system's own. It's a small habit with a large payoff. The lab's libraries can't interfere with anything else on the machine, and you can wipe and rebuild the lab by deleting one folder. Create one named modbus-lab and activate it:

TERMINAL — create and activate a venv
python3 -m venv modbus-lab
source modbus-lab/bin/activate

After activation your prompt gains a (modbus-lab) prefix, a reminder that you're working inside the environment. Everything you install now lands in that folder and nowhere else. Type deactivate to leave; run the source command again to return. Make activating the environment the first thing you do in every work session.

A virtual environment. A pip install command runs in a prompt marked (modbus-lab). The pymodbus and pyserial packages drop into the modbus-lab folder, while the system Python folder next to it is left untouched. (modbus-lab) $ pip install "pymodbus==3.7.4" pyserial ONE MACHINE System Python /usr/lib/python3 the OS's own packages other tools unchanged isolated modbus-lab/ this project's venv pymodbus 3.7.4 pyserial installed here only
A virtual environment keeps this project's packages in their own folder, isolated from the system Python and from other projects, so installing pymodbus here changes nothing else on the machine.

Installing pymodbus

With the environment active, one command installs both libraries. Installing pyserial now means the serial examples will work later without a second trip:

TERMINAL — install the libraries
pip install "pymodbus==3.7.4" pyserial
Why this course pins pymodbus 3.7.4

The book's code targets the pymodbus 3.6/3.7 interface, and it runs unchanged on 3.7.4. A plain pip install pymodbus today pulls a much newer release where that code breaks: 3.8 and 3.9 removed the zero_mode argument; 3.10 and later renamed ModbusSlaveContext to ModbusDeviceContext, slaves= to devices=, and slave= to device_id=; and the current releases deprecate the datastore classes ahead of version 4. Pinning 3.7.4 keeps every example in Part III working exactly as printed. Keep the quotes around "pymodbus==3.7.4" so the shell passes it to pip intact.

When it finishes, confirm the install and check the version. The book's examples target pymodbus version 3, whose interface differs in small ways from the older version 2 you'll still find in online tutorials. Checking now saves confusion later:

TERMINAL — verify the install
python -c "import pymodbus; print(pymodbus.__version__)"
OUTPUT
3.7.4

If it prints 3.7.4, your toolchain is ready. If it prints anything else (a 2.x version, or a newer 3.x), run the pinned install command again inside the activated environment; pip replaces whatever version is there with 3.7.4.

A practice device in software

To read or write Modbus you need something on the other end. Rather than require hardware on day one, you'll run a software Modbus server on the same board: a small program that behaves like a device, holding registers a client can read and write. Your client reaches it over the network connection every computer has to itself, called localhost (address 127.0.0.1). One board plays both roles, a server pretending to be a device and a client talking to it: the perfect risk-free practice rig.

The practice loopback. Inside one Raspberry Pi, a client program sends a read request around a localhost loop to a server program on port 5020, and the server's reply travels back. The board's network port is shown unused: no external hardware is needed. RASPBERRY PI localhost · 127.0.0.1 Client read_client.py Server practice_server.py :5020 read 0–3 100 200 300 400 Ethernet not needed for practice
The practice loopback. A Modbus server and a Modbus client run on the same board and talk over localhost, so you can exercise the full protocol with no external hardware.

Here is the entire practice server. Don't worry about every line yet: Chapter 12 builds a server from scratch and explains all of it. Save this as practice_server.py. It creates a device with one hundred holding registers and one hundred input registers, seeds a few with recognizable values, and serves Modbus TCP on port 5020:

PYTHON — practice_server.py
from pymodbus.server import StartTcpServer
from pymodbus.datastore import (
    ModbusSlaveContext, ModbusServerContext,
    ModbusSequentialDataBlock)

hr_vals = [0] * 100
ir_vals = [0] * 100
hr_vals[0:4] = [100, 200, 300, 400]
ir_vals[0:2] = [235, 567]   # 23.5 C, 56.7%

hr = ModbusSequentialDataBlock(0, hr_vals)
ir = ModbusSequentialDataBlock(0, ir_vals)
dev = ModbusSlaveContext(hr=hr, ir=ir,
                         zero_mode=True)
ctx = ModbusServerContext(slaves=dev, single=True)

print("Practice server on port 5020 ...")
StartTcpServer(context=ctx,
               address=("0.0.0.0", 5020))

Why port 5020 and not 502? Modbus TCP's official port is 502, but on Linux any port below 1024 requires administrator privileges. Port 5020 doesn't, so you can run the server as an ordinary user. (Real devices still usually listen on 502.) Run it now, in its own terminal:

TERMINAL — run the practice server
python practice_server.py
OUTPUT
Practice server on port 5020 ...

The program prints its startup line and then sits quietly, waiting for connections. That's exactly right: a Modbus server does nothing until a client speaks to it, just as Part I described. Leave this terminal running. In the next chapter you'll open a second terminal, write a client, and watch it reach across localhost to read those seeded values back.

The two-terminal workflow. Terminal 1 runs practice_server.py, prints its startup line, and waits with a blinking cursor. In Terminal 2 the command python read_client.py is typed; a request travels to the server and back, and the client prints the holding registers, temperature, and humidity. Terminal 1 — server (modbus-lab) $ python practice_server.py Practice server on port 5020 ... waiting for a client (not frozen) client connected :5020 localhost Terminal 2 — client (modbus-lab) $ python read_client.py Holding 0-3: [100, 200, 300, 400] Temp: 23.5 C Humidity: 56.7 %
The two-terminal workflow used throughout Part III: the practice server runs in one terminal and waits, while you write and run client programs in a second terminal. (The client itself arrives in Chapter 11.)

Your bench is ready: a microcomputer, the Python tools, and a practice device waiting for a client. In Chapter 11 you'll write that client, a short program that connects to the practice server, reads its registers, and prints the results, and the protocol you've studied on paper will run in front of you for the first time.

Check your understanding

1. Why does the practice server listen on port 5020 instead of Modbus TCP's official port 502?

Low-numbered ports are privileged on Linux. Using 5020 lets you run the server as an ordinary user. Real devices still usually use 502.

2. Your prompt starts with (modbus-lab). What does that tell you?

The prefix appears after source modbus-lab/bin/activate and reminds you that pip installs land in that isolated folder.

3. Why does this course install "pymodbus==3.7.4" rather than the latest release?

Later releases removed zero_mode and renamed ModbusSlaveContext, slaves= and slave=. Pinning keeps every example working as printed.

4. You start practice_server.py. It prints one line and then nothing happens. What's going on?

A Modbus server only answers. Leave it running and talk to it from a second terminal.