Simple Raspberry Pi IIoT Server
2019-05-16 | By Maker.io Staff
License: See Original Project Wireless Raspberry Pi
In the previous project, we constructed a simple IIoT vibration sensor that can be attached to machinery to record vibrations. In this project, we will learn how to use a Raspberry Pi as a simple IIoT server for monitoring our vibration sensor remotely.
BOM
- Feather Huzzah - 1528-1901-ND
- Accelerometer - 1568-1420-ND
- Matrix Board - SBBTH1508-1-ND
- Raspberry Pi 3 - 1690-1000-ND
Scheme-It
The IIoT Sensor
The IIoT Sensor in this project consists of an Adafruit Huzzah Wi-Fi microcontroller and an MPU9250 sensor that incorporates magnetic and accelerometer sensors. While this allows for multiple different sensor readings, we’ll only look at the accelerometer to produce a single vibration number. The Adafruit Huzzah is based on an ESP8266 Wi-Fi SoC, which can be easily programmed via the Arduino IDE - making it a handy Wi-Fi prototyping platform.
When the IIoT sensor is turned on, the first step it performs is to configure the MPU9250 sensor and then connect to the local Wi-Fi. Once it has established a Wi-Fi connection, the system then attempts to connect to a local server in which all vibration data is streamed live. This data stream is tokenized and contains metadata including the device type (drill, band saw, press, etc.), the unique ID number of the IIoT sensor, and an END message that makes parsing data on the server side considerably easier. The exact inner workings of the IIoT sensor can be found in the previous project article.
A Raspberry Pi Monitoring Station
The monitoring station in this project is centered around a Raspberry Pi, and the server application is written using Python. In order to run the server application, matplotlib is required; this can be installed on the Raspberry Pi using the following command:
sudo pip3 install matplotlib
Once matplotlib has been installed, the file IIoT.py needs to be moved to the /home/pi/ directory. With the file transferred, the server application can be executed by using the following command from a terminal window:
sudo python3 IIoT.py
With the server launched, the IIoT sensor can be powered. After it connects, the graph on the server will begin to show data from the vibration sensor, looking something like this:
How the IIoT Server Works
The monitoring station in this example relies on the matplotlib library, which allows for the creation of graphical data. The first lines of code include configuring what style of graph and which port the server will use, binding the socket to a specified port, and initiating some variables.
One line of code that is particularly important is the s.setblocking(0). In our server program, s refers to the socket that the server communicates over; typically, these sockets are blocking. This means that when a socket should perform an action such as read or accept(), it halts the thread until that action is completed. For our server, this would be disastrous as it would freeze the live graph view and other important functions in our program. The instruction s.setblocking(0) essentially makes the sockets non-blocking, and if the socket operation does not execute immediately, an error is thrown. Therefore, all socket operations are put in a try catch block and ignore errors.
The first function in the program is mainServerThread() . This is a function that will be used by a separate thread so socket operations can occur independently to the graph and other functions. The first try catch block statement in this function attempts to accept incoming requests, while the second try catch block attempts to accept incoming data. Incoming data is appended to the end of an external text file called data.txt. Once new data is appended to the file is reopened, all the lines are counted, and then the first n elements are removed to ensure that there are no more than 500 lines in the data.txt file. The reason for this is to prevent too many plot points on the graph, which can throw errors as well as use up system resources.
The second function in the program is animate(i), which animates the graph and keeps the graph up to date with data found in the data.txt file. While this project keeps the system simple, the server could be expanded to parse the incoming data and plot different devices onto different graphs by looking at the device ID and device type. The first few lines in this function read the data from the data.txt file and then parse it line by line (using the split(‘\n’) function). When the lines have been separated, they are further split into their individual components via the use of the “:” delimiter. Remember that our IIoT sensors send packets in the following form:
DEVICE:ID:DATA:END
The data to be plotted is the 3rd element in the packet, which is element 2 in a zero-based environment. This data is plotted to the graph using the ys.append(float(y)) command, and when all plots have been plotted, the graph is cleared using the ax1.clear() function. All data points (found in lists xs and ys) are then plotted using the function ax1.plot(xs, ys).
The last two lines in the program create an animation function that calls the graph update function once every 50ms and show the matplotlib system.
Server Code
import socket
import threading
from tkinter import *
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
# Use the fivethirtyeight style
style.use('fivethirtyeight')
# Create a figure object and add a subplot
fig = plt.figure()
ax1 = fig.add_subplot(1,1,1)
# Our network details
TCP_IP = '192.168.1.104'
TCP_PORT = 1000
BUFFER_SIZE = 64
# Create a TCP socket and listen to it
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Dont allow the socket to block our program
s.setblocking(0)
s.bind((TCP_IP, TCP_PORT))
s.listen(1)
# Some variables
datalog = ""
dAsString = ""
# ***********************************************************************
# Main server listner thread
# Listens for connections then assigns them
def mainServerThread():
while(1):
# Try to accept incoming connections
try:
conn, addr = s.accept()
print("connection accepted")
except:
pass
# Try to accept incoming data
try:
# Get the data and store as a string
data = conn.recv(BUFFER_SIZE)
dAsString = data.decode("ASCII")
# Add this data to the end of the data file
f = open("data.txt","a+")
f.write(dAsString)
f.close()
# Get the total number of lines in our data file
f = open('data.txt','r')
infile = f.readlines()
lineCount = len(infile)
f.close()
# Remove the first elements until there are no more than 500 lines
f = open("data.txt","w+")
for i in range(lineCount):
if i > (lineCount - 500):
f.write(infile[i])
f.close()
except:
pass
# ***********************************************************************
# Server listener thread
threading.Thread(target=mainServerThread).start()
# ***********************************************************************
# Animate the graph (slide across whenever new data is added)
def animate(i):
try:
# Open the reveived data
graph_data = open('data.txt','r').read()
lines = graph_data.split('\n')
xs = []
ys = []
count = 0
# Plot each line of data from the IIoT Sensor
# The data is tokenised with ":" and so our plot data is element 2 (0 based)
for line in lines:
if len(line) > 1:
token = line.split(":")
x = count
y = token[2]
count = count + 1
xs.append(float(x))
ys.append(float(y))
ax1.clear()
ax1.plot(xs, ys)
except:
pass
# Animate our plot so that its a live output
ani = animation.FuncAnimation(fig, animate, interval=50)
plt.show()
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum