OpenPLC is an open-source Programmable Logic Controller (PLC) that serves as the control system in the CybICS testbed. It reads sensor data from the physical process (real or simulated) and controls actuators based on the programmed control logic. The OpenPLC provides a web-based interface for programming, monitoring, and debugging the control system.
In the CybICS testbed, OpenPLC serves several critical functions:
- Process Control: Implements the automatic control logic for the gas pressure system
- Manual Override: Allows operators to manually control valves and compressors
- Training Platform: Provides a realistic ICS environment for cybersecurity training
- Protocol Support: Supports multiple industrial protocols (Modbus TCP, DNP3, EtherNet/IP, S7)
- Vulnerability Target: Serves as a target for security testing and penetration exercises
OpenPLC in CybICS is configured to support multiple industrial protocols:
- Modbus TCP (Port 502): Primary communication protocol
- DNP3 (Port 20000): Distributed Network Protocol for SCADA
- EtherNet/IP (Port 44818): Industrial Ethernet protocol
- S7comm (Port 102): Siemens S7 protocol
- HTTP (Port 8080): Web interface for programming and monitoring
- URL:
http://<device-ip>:8080 - Default Credentials:
The CybICS OpenPLC comes with a pre-installed control program (cybICS.st) that implements:
- Automatic Control Mode: Maintains GST between 60-240 bar and HPT between 60-90 bar
- Manual Control Mode: Allows operator to manually control compressor and valves
- Safety Interlocks: Prevents compressor operation when GST is too low
- Heartbeat: Visual indicator that the PLC is running
- Emergency Stop: System stop functionality
| Address | Variable | Description | Range |
|---|---|---|---|
| %MW100 | gst | Gas Storage Tank pressure | 0-255 bar |
| %MW102 | hpt | High Pressure Tank pressure | 0-255 bar |
| %MW104 | stop | Emergency stop flag | 0=run, 1=stop |
| %MW106 | manual | Manual mode flag | 0=auto, 1=manual |
| %MW108 | systemSen | System sensor state | Boolean |
| %MW110 | boSen | Blowout sensor state | Boolean |
Modbus Register Mapping:
- Register 1124: GST pressure (written by hwio)
- Register 1126: HPT pressure (written by hwio)
| Address | Variable | Description | Control |
|---|---|---|---|
| %QX0.0 | heartbeat | System alive indicator | Auto (PLC) |
| %QX0.1 | compressor | Compressor control | Auto/Manual |
| %QX0.2 | systemValve | System valve control | Auto/Manual |
| %QX0.3 | gstSig | GST fill signal | Auto/Manual |
- Open a web browser
- Navigate to
http://<device-ip>:8080
- Click "Programs" in the navigation menu
- Click "Upload Program"
- Select your
.st(Structured Text) file - Click "Upload"
- Click "Compile" to build the program
- Click "Start PLC" to run
- Click "Monitoring" in the navigation menu
- Select the location to monitor (e.g., %MW100 for GST)
- View real-time values and graphs
Symptom: Browser shows "Connection refused" or timeout
Solutions:
- Verify OpenPLC container is running:
docker ps | grep openplc # or sudo systemctl status openplc
- Check if port 8080 is accessible:
curl http://localhost:8080
- Verify firewall allows port 8080:
sudo iptables -L -n | grep 8080 - Check OpenPLC logs:
docker logs openplc # or sudo journalctl -u openplc -n 50
Symptom: Compilation errors when uploading program
Solutions:
- Check syntax of Structured Text program
- Verify all variable declarations are correct
- Review compilation logs in the web interface
- Ensure no special characters in variable names
- Try uploading a simple test program to verify compiler works
Symptom: hwio cannot connect to OpenPLC via Modbus
Solutions:
- Verify Modbus TCP is enabled in Settings → Slave Devices
- Check port 502 is exposed:
netstat -tuln | grep 502 # or ss -tuln | grep 502
- Test Modbus connection:
from pymodbus.client import ModbusTcpClient c = ModbusTcpClient('localhost', 502) c.connect() print(c.connected) # Should be True
- Review OpenPLC runtime logs
- Restart OpenPLC service
Symptom: Input registers show 0 or don't update
Solutions:
- Verify hwio-raspberry or hwio-virtual is running and connected
- Check Modbus register mapping (1124 → %MW100, 1126 → %MW102)
- Use Modbus client to verify values are being written:
from pymodbus.client import ModbusTcpClient c = ModbusTcpClient('localhost', 502) c.connect() print(c.read_holding_registers(1124, 2)) # Should show GST and HPT
- Verify PLC is in RUN mode (not STOP)
- Check for program logic errors
Symptom: Physical LEDs or simulated process doesn't respond to PLC outputs
Solutions:
- Verify PLC outputs are changing in Monitoring view
- Check hwio service is reading from OpenPLC coils
- Verify Modbus coil addresses are correct (512, 513, 514, 515)
- Test coil reading:
from pymodbus.client import ModbusTcpClient c = ModbusTcpClient('localhost', 502) c.connect() print(c.read_coils(512, 4)) # Should show actuator states
- Review control logic in PLC program
Default: 100ms (10 Hz)
Modify in Settings → Hardware → Scan Cycle Time
For faster control: 50ms For less CPU usage: 200ms