Supervisory control and data acquisition (SCADA ) software has been in use for many years, having been implemented on mainframe computers to manage remote installations such as hydroelectric plants, water treatment facilities, and sewage treatment works. Advances in electronics technology that reduced mainframe computer systems to laptop dimensions, object-oriented programming software advances, and the “unit operations” concepts of chemical engineering now allow the individual experimentalist to create very small, dedicated SCADA systems to interface with equipment on desk- or benchtops, between laboratories, or even between laboratories and mobile, remote, “in-field” experimental setups.
This chapter is designed to introduce the general concept of using open source systems for experimental monitoring and control. As noted in the introduction to this book, investigators will need sufficient familiarity with the concepts of programming and electronics to be able to adapt the code sequences and circuit diagrams depicted here into workable systems on the hardware–software combinations being used in their investigative work.
Open source, freely available software such as Python, with its built-in Tkinter graphical user interface (GUI), when combined with inexpensive hardware such as the Arduino and Raspberry Pi systems, allows the experimenter to assemble customized GUI control systems for their experimental setups at very low cost, measured in dollars and tens of dollars rather than in thousands. Internet capability allows investigators to work in both local and remote locations.
A significant concern with open source systems is being current or “up to date.” Many minds are developing new and better methods for both hardware and software data acquisition with ever increasing computational capabilities. The experimentalist choosing to use an open source system must keep up to date with the current stable version of the system in use. Open source software and hardware systems change with time and at the time of writing, Python is up to version 3.6, Python’s Tkinter is at version 8.5, and Arduino is at version 1.8.13, while the Raspberry Pi board advanced from its first implementation to its fourth revision.
The following six Python program examples have been developed throughout the implementation of two Raspberry Pi (RPi) releases. These programs are working examples of Python–Tkinter–Arduino systems that are able to execute the desired functions of digital data display, component activation, component selection, component adjustment, and realtime analog data recording from a GUI resident on a Model B Raspberry Pi.
Prior to embarking on developing an experiment or process-control system with the Raspberry Pi, the investigator must be aware that the Pi is a Linux system operating on a very inexpensive computing facility with no graphics chips, math co-processor, or large amount of memory to vastly boost system performance.
The very simple USB cable serial interfacing of an Arduino to the RPi greatly increases the control capabilities and range of measurement available to the experimenter. Pulse-width modulation, analog-to-digital conversion (ADC), and substantial computing power from the Arduino’s potent C language–based integrated development environment (IDE) programming aid are readily available from the microcontroller. Although GUI displays place a large demand on available computing resources and require simpler, more utilitarian interfaces, substantial digital signal processing such as time averaging can be performed by the high-speed microcontroller before sending the processed data to the RPi for display.
Because the concept and initial development of the core of this book pre-dates the Raspberry Pi, Arduino, and Python, projects in this chapter will also be behind the current technology when reviewed by experimental investigators. However, the initial code and hardware specified in the following pages is reproducible. The code was developed on different computing platforms, but the final revisions all have been tested and debugged on the RPi Model B with the NOOBs 2.1.0 operating system resident on an 8 GB SD card. The supporting microcontroller was the Arduino Uno or revision 3 boards.
To provide some assistance to those developing their own SCADA system, a series of six programs spanning the primary unit operations required to invoke a working experiment control system are presented. The RPi programs are written in Python, and an Arduino microcontroller provides a signal for display. The microcontroller programs are presented in as simple a form as possible and do not include any significant signal-noise reduction, which can be achieved by more programming of the microcontroller.
As noted earlier, prior to any use of the Python language, RPi, or Arduino components, obtain the latest updates and upgrades for the RPi operating system. The Python language is open source and extensively documented in numerous online tutorials and reference books. Python programs should be developed on the computing system being used to control the experiments at hand. Several Python–Tkinter–Arduino programs developed on Microsoft or Apple systems have been found to be incompatible with RPi systems . The RPi operating system has been designed to facilitate the download of the required supporting software packages, such as the numerical calculation package Numpy; the plotting library Matplotlib; the scientific computing package SciPy; and other specialized libraries, such as the serial port communications library called serial, and the experimenter is advised to use them. Many of the Python–Tkinter GUI screens developed for the RPi do not run on Microsoft- or Apple-based operating systems.
The basic Tkinter display frame
An auto-updating, numerical-input screen display
Screen-controlled On/Off buttons
A mouse-activated screen slider for power control or fine adjustment
Radio button exclusive selection of one option from a group
Graphical data displays: a realtime strip-chart recorder
Program 1: Display Frame
Creating an Empty Basic GUI Display Frame with Python
The output from Listing 2-1 is depicted in Figure 2-1. On screen, the size of the display can be varied by dragging the corners of the image frame out or in. The Minimize, Maximize, and Delete buttons all are functional, while the image itself can be “dragged” to a convenient screen location by the usual technique of placing the cursor on the frame’s empty space to the right of the Arduino Advanced Techniques heading and depressing the left mouse button (all Tkinter exercises have been developed on the RPi Pixel OS).
In the simple Tkinter-based window seen in Figure 2-1, the basic outer frame and functional resizable display area are all created by the first six lines of code. Three lines of code define a label, its size, background color, text content, and display font, and paint it into the display window.
Program 2: Display of Changing Data in Realtime
In the example in Figure 2-1, a simple label displays the static text coded into the text= option of the Tkinter label method. By substituting special Tkinter variables (see Tkinter documentation) for the “Data to Display!” text message, the label can display the actual value of the variable. By setting the value of the variable to that of an input data stream arriving at the serial port, the label can provide a “realtime” display of incoming data. Analog sensor outputs being monitored by an Arduino board can then be streamed out to the serial port for collection and display on the RPi monitor. With the aid of a Python program, the data arriving at the serial port can be processed into a format compatible with a realtime display in a Tkinter-based GUI.
Monitoring an output signal from a remote sensor with a USB serial connection to a host computer running a Python, Tkinter, GUI display window requires forming a coordinated process for data transfer and display. In terms of relative speeds, the GUI display has a large software overhead and is thus relatively slow to respond to changing experimental data. An easily implemented coordination of the one-way data-flow system is to adjust the rate of delivery from the source of the data stream to the GUI display so that the visual display operates at its normal rate and the delivered data displays correctly. The data delivery rate must be slow enough that the large resource consumption by the GUI neither fails to display nor distorts the incoming data. In the visual digital display of Figure 2-2 the display response is fast enough that a manual turning of the potentiometer shaft is seen virtually immediately in the values observed in the screen display. Timing on Arduino–RPi host displays is often best adjusted experimentally.
Python Tkinter GUI Code for Realtime Data Display
Arduino Code to Read Data and Write Values to the Serial Port
The small windows displaying the data are termed widgets, and for simplicity the default properties of the individual label windows have been used in order to minimize the complexity of the GUI creation code. There are a large number of label options available to alter the appearance of the widget display, and as noted the minimize, maximize, and close widget functions are all active, along with the widget cursor-dragging size-adjustment functions.
In these two figures, the smaller Tkinter windows on the right contain a text label naming the data being displayed, and beneath the static text is a character string of values. This character string is generated by the Arduino program that is streaming the integer ADC counts from the microprocessor and the actual calculated floating-point wiper voltage in a continuous stream to the serial port. The Python code receives the stream as a character string that can vary from six to nine units in length as the potentiometer is rotated to either extreme. The console output recordings in Figures 2-3 and 2-4 have captured the voltage transit from the 1023 ADC counts and the 5.000V level to the most common mid-range three-digit values then to the transition to the 0 ADC counts and 0.000V at the low-end extreme of the potentiometer setting. Examination of the Python code shows that in this simple demonstration the string-to-integer conversion is fixed in code at ten digits; that is far larger than the midrange three-digit value most likely to occur during potentiometer-shaft rotation. Shaft positions within the limits of 100 and 999 ADC counts trigger the slicing-logic code that picks out only the three-digit ADC values and converts the string characters into integer numbers that are displayed on the console.
To aid the programmer in setting up the slice function, the actual character stream to parse is displayed along with the len() function value that indicates how the Python code is counting characters. Note that the string reportedly arriving at the port as depicted in Figure 2-2 is 4872.380, which the Python code reports as b'4872.380 ', which in turn has a counted character length of ten. When creating the slice function, the programmer can see that the Python len() function counts the b' and ' as single characters.
A point of caution is noted here, and will be repeated later, that involves the nature of the data being streamed out by the Arduino’s analog-to-digital converter (ADC). At either extreme of the potentiometer rotation, the data stream can vary from a single digit to four, and hence the Python code receiving the stream must be able to accommodate the variation in the size of the serial data stream with multiple slice functions to cover the expected width range.
In spite of the large overhead created by the Python software, the speed of the RPi enables the graphic display to keep up with the experimentalist turning the shaft of the potentiometer without any noticeable delay.
Program 3: Activation of Experimental Devices from Button Displays
To create a functioning unit operation such as activating an LED connected to an Arduino microcontroller board, a more complex process must be followed.
The creation of a display-screen button that when pressed by a mouse click or a keyboard entry initiates a desired remote action requires implementation of “event-driven programming.” Event-driven systems require two programs: the first creates an RPi screen displaying a button that is monitored for activation, and the second, resident on the remote device, monitors the serial communications stream for instructions from the button-display monitor program. In this simple demonstration, our screen button will activate an LED already mounted on the remote Arduino microcontroller board.
Tkinter event-driven “objects,” such as buttons and checkboxes, use an internal loop to scan over the possible actions associated with the object in the screen display. “Pressing” the button image with a mouse-button click is caught by the scanning loop software code of the image-object, and an action is initiated that can consist of sending a serial command to the Arduino, which in turn switches on the power to an LED. In the current exercise, event-driven programming is initiated by the user’s activating a screen image that in turn invokes the USB serial communication system between the host computer and the Arduino microcontroller board. Since both the display software and the Arduino microcontroller are activated by looping software code, our event-driven system needs to be coordinated.
Python Tkinter GUI for Button Control of Remote Arduino LED
Arduino Sketch for Response to Python GUI Button Activation
This relatively simple on-screen GUI does not have an indicator to confirm activation of the LED illumination, as the exercise assumes that the Arduino board is within the field of view of the user. If, however, the device being controlled is remote and out of sight, the researcher may want to have the remote unit send back conformation that the LED or experimental operation is actually powered up by having the code of the remote device send a message back to the initiating GUI to display some form of message label or background-color change to indicate power application to the LED. Alternately, a photoelectric signal derived from light output from the remote device that confirms both power delivery and light-source functionality could be returned to the local GUI.
Program 4: A Sliding-Scale Implementation
Listings 2-6 and 2-7 are the pair required to implement Program 4, which illustrates the use of a sliding-scale selection capability for a screen component that can be used to simulate an analog-type control (Figure 2-6). Rough control of the output signal can be achieved by dragging the scale position-indicator rectangle along the “trough” of the slider image. Fine control of the movement can be achieved by placing the mouse cursor over the trough above or below the rectangular index position indicator and clicking on the mouse button. When the mouse cursor is over the trough between the end of the scale and the indicator, each mouse-button click will move the index indicator one unit toward the scale end at hand.
Python Tkinter GUI Code for Implementation of the Analog Slider Control
Arduino Sketch Code for Controlling LED Brightness in Response to Python Tkinter Slider GUI Position
Program 5: Radio Button Controls
GUI radio buttons function in the same manner as the push buttons used in numerous consumer entertainment electronics and electrical appliances. A single button depression exclusively selects a single option from the number of choices available. Program 5 is implemented with Listings 2-8 and 2-9.
Python Tkinter GUI for a Radio Button Selection Panel Display
Our radio-button example uses Tkinter variable objects that cannot be passed as normal integer values on the serial bus. (See Tkinter documentation.) Conversion of the object to a string that is compatible with both the serial bus transmission and Arduino C code is required to implement the interfacing of the two system components. The author has left in place one of the “commented out” print statements used in the initial development of the code. Printing variables is a technique that can be used to see what the code, in an experimental passage, is actually producing. Leaving or “commenting out” the invisible statement in place can be an aid in future maintenance or code-modification operations.
Arduino Sketch for Implementing the Radio Button Selection from the Python Tkinter GUI Panel
Program 6: Graphical Data Display—A Realtime Strip-Chart Recording
Many experimental investigations use graphical data displays that may be divided into two basic types: those that use previously recorded or theoretically generated data to create the display and those that monitor and display the present and recent past values of a data stream. Static files of data, collected over years or microseconds and maintained in stored format, can be used to create static or animated images for visual analysis with two- or three-dimensional plotting software, as provided by the Matplotlib facilities of Python. Matplotlib also has the ability to generate continuous graphical displays of streamed, varying data with minimal time delays between the change in data-stream values and the alteration of the visual display. A visual display that tracks a varying-value data stream is commonly referred to as a “realtime” display. Realtime displays are often required in setting up experimental apparatus or following slowly changing signal measurements such as weather conditions, animal movement, or titrations in chemical analysis determinations.
Oscilloscopes electronically record and display relatively high-speed electrical signals over very short time scales, while strip-chart recorders monitor longer time bases for displaying varying signals. Strip-chart recorders are electro-mechanical devices in which a pen draws a trace on a paper strip chart, moving at a constant velocity beneath the potentiometrically balanced pen drive mechanism.
An audio frequency range oscilloscope display can be created from a monitor and a PC sound card with software programs obtained commercially or from internet download. Xy recording and audio-frequency measurements are accessible for more advanced investigations through these various freeware and moderate-cost options.
Python’s plotting library, known as Matplotlib, contains a generic strip-chart recorder program in its list of animation program examples. There are other plotting facilities available in commercial, free, and open source formats obtainable from the internet. (See DAQFactory Express, PL Plot, etc.)
Python’s strip-chart recorder is based upon the animations capability found in the Matplotlib module, and the example program provides its own internal series of “random spiking” events to record for demonstration purposes.
Program 6 is implemented with the Arduino and Python–Matplotlib pair of Listings 2-10 and 2-11.
Arduino Sketch for Digitization and Serial Transmission Streaming of TMP36 Temperature Data
Python Matplotlib Strip-Chart Recording Code for Streamed TMP36 Data
The plotting code originally posted by B. Welt in 2013 and modified by the author only accepts two-digit temperature values but works well as a demonstration of realtime recording.
In the examples section of the Matplotlib documentation an actual strip-chart recorder (SCR) program is presented in which a random-number generator is used to provide data for the program display. A modification of the SCR program to record the streamed output of an Arduino microcontroller monitoring the ambient room light near a window on a cloudy day is presented here. The Arduino and RPi–Python code pair of Figure 2-12 and Listing 2-12 are presented together with a screen display from the RPi in Figure 2-10 and a detail of the actual plotter output in Figure 2-11.
Python Matplotlib SCR Display Code
When Listing 2-12 is run on the RPi, it generates the dual screens of Figure 2-10, with Python on the left and the graphic display trace depicted on the right and in close up in Figure 2-11. The SCR display is divided into forty-minute increments, and data display is initiated and timed from the loading and running of the Python code. After the accumulation and display of forty minutes of data, the program advances to the next forty-minute window, as is visible on the time axis in Figures 2-10 and 2-11. The SCR program code does not have any provision for storing the data from a previous window time increment other than the ability to save the current graphical display provided by the Save button on the right-hand end of the lower left panel of buttons. The area of the plot window can be dragged to any convenient size by the usual mouse window-edge manipulation techniques.
All of the programs and code presented in this chapter are very simple, have no signal-to-noise reduction code, and are uncalibrated. Most of the code has been commented, and some contains print statements for diagnosing malfunctioning code that have been “commented out” for actual use. Where applicable, cautions have been added with respect to processing the incoming data-stream variations that may occur. The analog-to-digital converter on an Arduino can output values from 0 to 1023. In other words, the incoming stream can vary from a single-digit value to a four-digit value together with the control characters required to uniquely define the transmission of the data stream. There are numerous serial data-transmission protocols used in various sensors, and the experimenter must accommodate these protocols when modifying the Python codes presented here for data display.
The programs work and can be used as a known starting point for the development of more-complex, very low-cost, small-scale, Linux–Python–based SCADA systems.
Summary
A series of six basic “unit operations” that can be used to assemble a SCADA system are developed.
Data displays and analog-and-digital control functions are developed in Python–Tkinter–based GUI screen displays resident on the RPi, and serial data or operational commands are streamed back and forth with an Arduino microcontroller functioning as an intelligent peripheral or interface.
A Python–Matplotlib continuous strip-chart recorder data display is demonstrated by providing a continuous recording of Arduino data streams following ambient temperature and lighting conditions.
In Chapter 3, the care required when generating and working with high heats and high temperatures in experimental investigations is explored.