Two-axis solar trackersRaspberry PLC 21TCP socketCommunication
Wiring a Python control loop to Node-RED over local TCP sockets
When the control logic lives in Python and the HMI lives in Node-RED on the same PLC, you need a clean bridge between the two processes. This example, from a real two-axis solar tracker deployment, uses two local TCP sockets on a Raspberry PLC 21: port 8181 streams encoder positions to the Node-RED dashboard once per second, while port 8182 receives start, stop and exit commands from the dashboard buttons — no broker, no files, no polling.
Two ports, two directions
Mixing telemetry and commands on one socket forces you to multiplex and parse both ways. Splitting them keeps each side trivial: the data server only writes, the command server only reads. Both bind to 127.0.0.1, so nothing is reachable from outside the PLC — the network exposure of this inter-process mechanism is exactly zero, with no firewall rules to maintain.
A one-character protocol
Each encoder sample is a line like e1234 or a567: a prefix for the axis (elevation or azimuth) followed by the raw position and a newline. In Node-RED, a switch node on the first character routes each stream to its gauge. Line-oriented text is debuggable with netcat from any terminal and immune to partial-read issues thanks to newline framing on both ends.
Threads and shared events
Each server runs in its own daemon thread, and the control loop reads two threading.Event flags: one for tracking active, one for shutdown. Commands arriving over TCP just set or clear these events, so the dashboard never touches the motor logic directly. Reconnection is handled by simply accepting again after a broken pipe, so the bridge survives Node-RED restarts and redeploys gracefully.
A snippet from the implementation
Straight from the example as deployed on the Raspberry PLC 21 — copy it freely:
def read_elevation_encoder():
"""Stub: on the real tracker, encoder.sdo['position_value'].raw."""
return int(time.time()) % 750 # simulation 0..749 (0..75.0 deg)
The full example is a complete program — wiring header, setup and main loop — ready to adapt to your application.
Frequently asked questions
Why TCP sockets instead of MQTT between Python and Node-RED?
Both processes run on the same machine, so a broker adds a dependency and a failure mode for no benefit. Localhost TCP is one import in Python and a built-in node in Node-RED, with latency in the microseconds.
How does Node-RED parse the e1234 messages?
A tcp-in node in stream mode with newline delimiter emits one message per line. A function or switch node checks the first character to route elevation versus azimuth, and the rest of the string is the numeric position.
What happens to the control loop if the dashboard disconnects?
Nothing. The data thread catches the broken pipe, closes the connection and waits for the next accept, while tracking continues. The dashboard is an observer and command source, never a dependency of the safety logic.