Skip to content

Commit

Permalink
Documentation (#58)
Browse files Browse the repository at this point in the history
* - Restructuring of the documentation
- Adding getting started informations

* Updating OpenPLC_v3 to newer commit

* Fixing connection bug on readI2Cpi for raspberry pi hardware abstraction

* Improved installation script

---------

Co-authored-by: Matthias Niedermaier <matthias.niedermaier@googlemail.com>
  • Loading branch information
mniedermaier and Matthias Niedermaier authored Sep 26, 2024
1 parent 5b49706 commit 4e3abc5
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 35 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<p align="center">
<img alt="CybICS Logo" src="doc/CybICS_logo.png" height="120" />
<img alt="CybICS Logo" src="doc/pics/CybICS_logo.png" height="120" />
<p align="center">Understanding industrial Cybersecurity.</p>
</p>

Expand Down Expand Up @@ -28,14 +28,15 @@ First, read through this page to get a better understanding of the testbed setup
- [Physical Process](#physical)
- [Hardware](#hardware)
- [Software](#software)
- [Getting Started](doc/README.md)
- [Training](#training)
- [Abbreviations](#abbreviations)

## CybICS <a id="cybics"></a>
The complete CybICS testbed

<table align="center"><tr><td align="center" width="9999">
<img src="doc/cybics.png" width=99%></img>
<img src="doc/pics/cybics.png" width=99%></img>
</td></tr></table>

## Physical Process <a id="physical"></a>
Expand Down Expand Up @@ -112,8 +113,8 @@ This is an open source PLC, with an web interfce for programming and configurati
FUXA is used as an HMI and historian, with an web interface for the operator.

<table align="center"><tr><td align="center" width="9999">
<img src="doc/openplc.png" width=49%></img>
<img src="doc/fuxa.png" width=49%></img>
<img src="doc/pics/openplc.png" width=49%></img>
<img src="doc/pics/fuxa.png" width=49%></img>
</td></tr></table>

## Training <a id="training"></a>
Expand Down
32 changes: 32 additions & 0 deletions doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Getting Started
There are two ways to interact with the CybICS learning platform. The first and preferred method is through the physical hardware.
Alternatively, you can run the platform in a virtual environment using Docker Compose.

## Using the Hardware (preferred method)
For this setup, the hardware must be fully configured and operational.
See [Hardware](../hardware/README.md) and [Software](../software/README.md).

### The PCB
The PCB (Printed Circuit Board) for the CybICS platform serves as the essential backbone, orchestrating the integration of all components necessary for the platform’s operation.
More than just a hardware foundation, the PCB acts as a dynamic interface that mimics the behavior of physical processes, while also simulating both sensors and actuators.

The hardware is powered through a USB-C connector, ensuring efficient and reliable power delivery.
A dedicated reset button allows for easy resetting of the physical process (microcontroller).
Connectivity to the testbed is established via Wi-Fi, with a button that toggles between station and access point (AP) mode.
Additionally, a separate button enables switching between different display modes.

<table align="center"><tr><td align="center" width="9999">
<img src="pics/cybics_description.png" width=99%></img>
</td></tr></table>

### Connecting to the Testbed
To connect to the testbed, use the built-in Wi-Fi functionality.
After powering on, the device takes approximately one minute to boot and initialize the process.

In AP mode, the CybICS platform creates a network named "cybics-XXXXXX," secured with the password 1234567890.
Alternatively, in station mode, the device attempts to join an existing network named "cybics" using the same password.

### Access the Testbed
First **connect in AP mode** to access the different applications running on the system.


Binary file removed doc/cybics.png
Binary file not shown.
Binary file added doc/pics/CybICS.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
Binary file added doc/pics/cybics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/pics/cybics_description.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
2 changes: 1 addition & 1 deletion software/FUXA/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ RUN apt-get update && apt-get install -y \
curl \
unixodbc-dev \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g --unsafe-perm @frangoteam/fuxa
RUN npm install -g --unsafe-perm @frangoteam/fuxa@1.2.0

WORKDIR /CybICS
COPY ./ ./
Expand Down
2 changes: 1 addition & 1 deletion software/OpenPLC/OpenPLC_v3
Submodule OpenPLC_v3 updated 138 files
58 changes: 39 additions & 19 deletions software/hwio-raspberry/readI2Cpi.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from pymodbus.client import ModbusTcpClient
import nmcli
import RPi.GPIO as GPIO
import logging

GPIO.setmode(GPIO.BCM)
GPIO.setup(8, GPIO.OUT) # compressor
Expand All @@ -37,10 +38,24 @@

gst = 0 # variable for the gas storage tank (GST)
hpt = 0 # variable for the high pressure tank (HPT)

countCheckIP = 0 # Variable to check IP every 100 runs
# Connect to OpenPLC
client = ModbusTcpClient(host="openplc",port=502) # Create client object
client.connect() # connect to device, reconnect automatically

attempts = 0

# Try "10" times to connect to the OpenPLC
logging.info("Trying to connect to OpenPLC")
while attempts < 10:
try:
client.connect() # connect to device, reconnect automatically
logging.info("Connected to OpenPLC within " + str(attempts) + " attempts")
break
except:
attempts += 1
time.sleep(10)
logging.error("Connection to OpenPLC failed retrying... " + str(attempts) + "/" + "10")


current_connection = ""
nmcli.disable_use_sudo()
Expand All @@ -50,22 +65,27 @@
break

current_ssid = nmcli.connection.show('cybics')["802-11-wireless.ssid"]
print (f"Current connection: {current_connection}, ap ssid: {current_ssid}")
logging.info(f"Current connection: {current_connection}, ap ssid: {current_ssid}")

# Entering while true loop
while True:
# Get IP address of wlan0
ip = nmcli.device.show('wlan0').get('IP4.ADDRESS[1]', "unknown")
ip = ip.split('/')[0] # remove the network CIDR suffix
listIp = list(ip)
# print(listIp)

# Format the IP and send it via i2c to the RPI
listIp = ['I', 'P',':'] + listIp
for row in range(len(listIp)):
listIp[row] = ord(listIp[row])
# print(listIp)
bus.write_i2c_block_data(address, 0x00, listIp)
if countCheckIP == 0:
# Get IP address of wlan0
ip = nmcli.device.show('wlan0').get('IP4.ADDRESS[1]', "unknown")
ip = ip.split('/')[0] # remove the network CIDR suffix
listIp = list(ip)
# print(listIp)

# Format the IP and send it via i2c to the RPI
listIp = ['I', 'P',':'] + listIp
for row in range(len(listIp)):
listIp[row] = ord(listIp[row])
# print(listIp)
bus.write_i2c_block_data(address, 0x00, listIp)
elif countCheckIP > 100:
countCheckIP=0

countCheckIP = countCheckIP + 1

# Read the values for GST and HPT
data = bus.read_i2c_block_data(address, 0x00, 20)
Expand Down Expand Up @@ -95,14 +115,14 @@
GPIO.output(7, plcCoils.bits[2]) # systemValve
GPIO.output(20, plcCoils.bits[3]) # gstSig
except Exception as e:
print("Read from OpenPLC failed - " + str(e))
logging.error("Read from OpenPLC failed - " + str(e))

# write input register to OpenPLC
try:
client.write_registers(1132,GPIO.input(1)) # System sensor
client.write_registers(1134,GPIO.input(12)) # BO sensor
except Exception as e:
print("Write to OpenPLC failed - " + str(e))
logging.error("Write to OpenPLC failed - " + str(e))

# Read STM32 ID Code
data = bus.read_i2c_block_data(address, 0x01, 13)
Expand All @@ -116,13 +136,13 @@
if data[12] in ['0', '1']:
ssid = f"cybics-{id}"
if current_ssid != ssid:
print (f"Configure ssid {ssid}")
logging.info(f"Configure ssid {ssid}")
nmcli.connection.modify('cybics', {'wifi.ssid': ssid})
current_ssid = ssid

connection = 'cybics' if data[12] == '1' else 'preconfigured'
if current_connection != connection:
print (f"Enable connection {connection}")
logging.info(f"Enable connection {connection}")
nmcli.connection.up(connection)
current_connection = connection

Expand Down
71 changes: 61 additions & 10 deletions software/installRPI.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ ENDCOLOR="\e[0m"
GIT_ROOT=$(realpath "$(dirname "${BASH_SOURCE[0]}")/..")

# start time for calculation of the execution time
START=$(date +%s.%N)
START=$(date +%s)

echo -ne "${MAGENTA}"
echo " "
Expand Down Expand Up @@ -44,6 +44,16 @@ echo " \`---' "
echo -ne "${ENDCOLOR}"
sleep 1

###
### Check if raspberry is up
###
if ping -c 1 -W 5 "$DEVICE_IP" > /dev/null 2>&1; then
echo -ne "${GREEN}# IP $DEVICE_IP is reachable.\n${ENDCOLOR}"
else
echo -ne "${RED}#IP $DEVICE_IP is not reachable. Exiting.\n${ENDCOLOR}"
exit 1
fi

###
### Remove IP from known_hosts and copy ssh key
###
Expand All @@ -67,21 +77,36 @@ ssh "$DEVICE_USER"@"$DEVICE_IP" /bin/bash <<EOF
sudo locale-gen en_US.UTF-8
EOF

###
### Stopping containers
###
echo -ne "${GREEN}# Stopping containers ... \n${ENDCOLOR}"
ssh -t "$DEVICE_USER"@"$DEVICE_IP" sudo docker compose -f /home/pi/CybICS/docker-compose.yaml down || true

###
### Increasing swap size
###
echo -ne "${GREEN}# Increasing swap file ... \n${ENDCOLOR}"
ssh "$DEVICE_USER"@"$DEVICE_IP" /bin/bash <<EOF
set -e
if grep 1024 /etc/dphys-swapfile; then
if grep "CONF_SWAPSIZE=2048" /etc/dphys-swapfile; then
exit 0
fi
sudo dphys-swapfile swapoff
sudo sed -i s/CONF_SWAPSIZE=.*/CONF_SWAPSIZE=1024/g /etc/dphys-swapfile
sudo sed -i s/CONF_SWAPSIZE=.*/CONF_SWAPSIZE=2048/g /etc/dphys-swapfile
sudo dphys-swapfile setup
sudo dphys-swapfile swapon
EOF

###
### Update and upgrade
###
echo -ne "${GREEN}# Update and upgrade ... \n${ENDCOLOR}"
ssh "$DEVICE_USER"@"$DEVICE_IP" /bin/bash <<EOF
set -e
sudo apt-get update && sudo apt-get upgrade -y
EOF

###
### Install tools
###
Expand All @@ -93,15 +118,19 @@ ssh "$DEVICE_USER"@"$DEVICE_IP" /bin/bash <<EOF
fi
if ! which tcpdump; then
sudo apt-get update && sudo apt-get install tcpdump -y
sudo apt-get install tcpdump -y
fi
if ! which btop; then
sudo apt-get update && sudo apt-get install btop -y
sudo apt-get install btop -y
fi
if ! which socat; then
sudo apt-get update && sudo apt-get install socat -y
sudo apt-get install socat -y
fi
if ! which lsof; then
sudo apt-get install lsof -y
fi
EOF

Expand Down Expand Up @@ -148,21 +177,43 @@ ssh "$DEVICE_USER"@"$DEVICE_IP" /bin/bash <<EOF
EOF

###
### Build container local and install on rasperry pi
### Build container locally
###
echo -ne "${GREEN}# Build containers ... \n${ENDCOLOR}"
"$GIT_ROOT"/software/build.sh

echo -ne "${GREEN}# Install container ... \n${ENDCOLOR}"
###
### Install containers on the raspberry
###
echo -ne "${GREEN}# Install containers on the raspberry ... \n${ENDCOLOR}"
ssh "$DEVICE_USER"@"$DEVICE_IP" mkdir -p /home/pi/CybICS
scp "$GIT_ROOT"/software/docker-compose.yaml "$DEVICE_USER"@"$DEVICE_IP":/home/pi/CybICS/docker-compose.yaml
ssh -R 5000:localhost:5000 -t "$DEVICE_USER"@"$DEVICE_IP" sudo docker compose -f /home/pi/CybICS/docker-compose.yaml pull

###
### Starting containers
###
echo -ne "${GREEN}# Starting containers ... \n${ENDCOLOR}"
ssh -t "$DEVICE_USER"@"$DEVICE_IP" sudo docker compose -f /home/pi/CybICS/docker-compose.yaml up -d --remove-orphans

###
### all done
###
END=$(date +%s.%N)
END=$(date +%s)
DIFF=$(echo "$END - $START" | bc)
echo -ne "${GREEN}# Total execution time $DIFF \n${ENDCOLOR}"
echo -ne "${GREEN}# Total execution time $((DIFF/60)):$((DIFF%60)) \n${ENDCOLOR}"
echo -ne "${GREEN}# All done, ready to rumble ... \n${ENDCOLOR}"

# Ask the user if they want to restart
read -p "Do you want to restart the system? (yes/no): " user_input

# Convert the user input to lowercase for easier matching
user_input=$(echo "$user_input" | tr '[:upper:]' '[:lower:]')

# Check the user input
if [[ "$user_input" == "yes" || "$user_input" == "y" ]]; then
echo -ne "${RED}# Restarting the raspberry pi ... \n${ENDCOLOR}"
ssh -t "$DEVICE_USER"@"$DEVICE_IP" sudo reboot || true
else
echo -ne "${GREEN}# done ... \n${ENDCOLOR}"
fi

0 comments on commit 4e3abc5

Please sign in to comment.