Build a Low-Cost Industrial Controller with the Raspberry Pi 3

Contributed By Digi-Key's North American Editors

Few small shop industrial operations require the combination of harsh operating environments, high I/O capacity, and complex functionality requirements for which programmable logic controllers (PLCs) were originally intended. While scaled down versions of PLCs are available, designers now also have the option to choose from an array of low-cost open-source hardware and software solutions for effective industrial monitoring and control.

An example of such a solution is the Raspberry Pi Foundation's tiny Raspberry Pi 3 and its associated add-on boards. This article will describe the main characteristics of the Raspberry Pi 3 before discussing how to use it for monitoring and control applications.

Why Raspberry Pi for small shop industrial control

For many smaller operations, the Raspberry Pi 3 platform offers a low-cost solution with substantial performance capabilities. Fully capable of handling dedicated industrial automation tasks, the Raspberry Pi 3 board combines a Broadcom ARM® Cortex®-A53-based processor, 1 Gbyte of RAM, digital interfaces, Wi-Fi, and Bluetooth connectivity. The processor itself is a high-performance system-on-chip (SoC) device that integrates a quad-core ARM Cortex-A53 CPU with 512 Kbytes of L2 cache, and 54 GPIOs arranged in three banks.

Each individual GPIO variously supports at least two and up to six alternative functions, including pulse-width modulators, clocks, and serial interfaces. Developers can use any unassigned GPIOs as interrupt lines, inputs, or outputs capable of supplying up to 16 milliamps (mA) (up to 50 mA total per GPIO bank).

As with other members of the Raspberry Pi family, the Raspberry Pi 3 is designed to make embedded development simple enough for beginners, yet powerful enough to meet the needs of experienced developers with more complex and powerful processing requirements.

To start, developers simply connect the board’s video ports to their displays and its USB ports to their keyboard and mouse. For software design, developers can build on a broad ecosystem supported by the Raspberry Pi Foundation’s free Linux-based Raspbian operating system loaded from a memory card through the board’s micro SD interface.

Adding industrial capabilities

Beyond its performance and ease of development, the Raspberry Pi’s simplified approach for extending its functionality make it well suited to the diverse requirements of industrial automation applications. To add hardware capabilities, developers only need to plug an add-on board called a HAT (Hardware Attached on Top) onto the Raspberry Pi 3 board. As with more complex industrial systems, the HAT provides a standard approach for identifying the HAT and automatically configuring the GPIOs and drivers as needed. As a result, developers can instantly upgrade their Raspberry Pi system for industrial applications simply by plugging in the Pimoroni PIM213 Automation HAT (Figure 1).

Image of Pimoroni Automation HAT add-on board

Figure 1: Developers can upgrade a base Raspberry Pi 3 board for industrial automation by attaching specialized add-on boards such as the Pimoroni Automation HAT. (Image source: Pimoroni)

Specifically designed for monitoring and control of automation systems, the Pimoroni Automation HAT combines multiple I/O channels, including analog and digital inputs, powered outputs, and relay controls. Along with their 24 volt (V) capability, the I/O channels provide substantial input and output buffering. For example, the relay outputs support up to 2 amps (A), sufficient to drive low-power 24 volt parts such as the Crouzet 81 546 001 solenoid valve.

For software development with the Automation Hat, Pimoroni provides an associated Python module that requires only a few lines of code to use the HAT’s hardware features. When imported into a Python program, the Pimoroni module creates software objects for analog input, digital input, digital output, relay output, and LED light control, each of which includes the appropriate lower level read/write functions (Listing 1).

class AnalogInput(object):

    type = 'Analog Input'


    def __init__(self, channel, max_voltage, led):

        self._en_auto_lights = True = channel

        self.value = 0

        self.max_voltage = float(max_voltage)

        self.light = SNLight(led)


    def auto_light(self, value):

        self._en_auto_lights = value

        return True


    def read(self):

        """Return the read voltage of the analog input"""

        return round(self.value * self.max_voltage, 2)


    def _update(self):

        self.value =


    def _auto_lights(self):

        if self._en_auto_lights:

            adc = self.value


Listing 1: Pimoroni’s Python module for the Automation HAT simplifies development by handling detailed transactions such as reading from the onboard analog-to-digital converter (ADC). (Image source: Pimoroni)

Each object identifies the corresponding channel and other related data. For example, on creation, the analog input object includes the maximum voltage for the associated pin (see init function in Listing 1). To perform an analog-to-digital converter (ADC) conversion, the ADC object calls the underlying ADC module ( in Listing 1). The ADC module, in turn, performs the low-level I2C transactions required to set up the ADC and perform a conversion before returning the value in a useful form (Listing 2).

class ads1015:

    def __init__(self, i2c_bus=None, addr=ADDR):

        self._over_voltage = [False] * 4


        self.i2c_bus = i2c_bus

        if not hasattr(i2c_bus, "write_i2c_block_data") or not hasattr(i2c_bus, "read_i2c_block_data"):

            raise TypeError("Object given for i2c_bus must implement write_i2c_block_data and read_i2c_block_data")


        self.addr = addr


    def read(self, channel=0, programmable_gain=PGA_4_096V, samples_per_second=1600):

        # sane defaults

        config = 0x0003 | 0x0100


        config |= SAMPLES_PER_SECOND_MAP[samples_per_second]

        config |= CHANNEL_MAP[channel]

        config |= PROGRAMMABLE_GAIN_MAP[programmable_gain]


        # set "single shot" mode

        config |= 0x8000


        # write single conversion flag

        self.i2c_bus.write_i2c_block_data(self.addr, REG_CFG, [(config >> 8) & 0xFF, config & 0xFF])


        delay = (1.0 / samples_per_second) + 0.0001



        data = self.i2c_bus.read_i2c_block_data(self.addr, REG_CONV)


        value = ((data[0] << 4) | (data[1] >> 4))


        if value & 0x800:

            value -= 1 << 12


        value /= 2047.0 # Divide down to percentage of FS

        value *= float(programmable_gain)

        value /= 3300.0 # Divide by VCC


        return value


Listing 2: A higher level function call for an ADC conversation invokes a read routine, which performs an I2C bus write to start the conversion, sleeps long enough for the conversion to complete, and performs an I2C bus read to collect the result. (Image source: Pimoroni)

For the developer, however, reading an analog value simply requires executing the high-level read function (.read()) on the specified analog input (.one) for the analog object:

          value =

The library supports this simple model for other HAT features, so turning a relay on or off is a simple call:

          automationhat.relay.write(1) # 1 = ON, 0 = OFF

Flexible options

The Pimoroni Automation HAT offers the basic IO functionality required for a small industrial automation application, but developers can choose from a rich set of available HATs for all manner of features required for specialized applications such as industrial automation. For example, the Adafruit 3013 RTC HAT provides real-time clock (RTC) functionality, which is not a standard feature of the board itself. Raspberry Pi designers expect that developers will keep the board attached to the Internet, where it can use the standard Network Time Protocol (NTP) to maintain clock time. Consequently, an external RTC such as the Adafruit RTC HAT is required for designs that may lose connection to the Internet by design or by accident.

In adding functionality such as an RTC, developers do not need to limit themselves to a single HAT in an industrial automation design. Developers can stack multiple HATs on a Raspberry Pi board. Although most HATS are designed for stacking, developers may need to add stacking headers such as Adafruit’s 2223 to complete the assembly, or M2.5 standoffs to eliminate the chance that HATs might touch each other or the base board.

Using stacking headers and standoffs, developers could easily stack a HAT such as the Adafruit 2348 motor HAT to add motor drivers needed in many industrial automation applications. Each 2348 motor HAT can drive two stepper motors or four DC motors. In fact, developers can stack as many as 32 of these add-on boards to support up to 64 stepper motors or 128 DC motors (Figure 2).

Image of multiple Adafruit 2348 motor HATs

Figure 2: Developers can stack multiple Adafruit 2348 motor HATs to support up to 64 stepper motors or 128 DC motors in a design. (Image source: Adafruit)

As with the Pimoroni Automation HAT, the Adafruit 2348 motor HAT can be programmed using a few simple Python commands. Adafruit’s sample software for the motor HAT even demonstrates basic design patterns for using the Python threading module to run multiple motors in parallel (Listing 3).

from Adafruit_MotorHAT import Adafruit_MotorHAT, Adafruit_DCMotor, Adafruit_StepperMotor

import threading


# create a default object, no changes to I2C address or frequency

mh = Adafruit_MotorHAT()


# create empty threads (these will hold the stepper 1 and 2 threads)

st1 = threading.Thread()

st2 = threading.Thread()


. . .


myStepper1 = mh.getStepper(200, 1)      # 200 steps/rev, motor port #1

myStepper2 = mh.getStepper(200, 2)      # 200 steps/rev, motor port #1

myStepper1.setSpeed(60)          # 30 RPM

myStepper2.setSpeed(60)          # 30 RPM


stepstyles = [Adafruit_MotorHAT.SINGLE, Adafruit_MotorHAT.DOUBLE, Adafruit_MotorHAT.INTERLEAVE, Adafruit_MotorHAT.MICROSTEP]


def stepper_worker(stepper, numsteps, direction, style):


    stepper.step(numsteps, direction, style)



while (True):

    if not st1.isAlive():

        randomdir = random.randint(0, 1)

        if (randomdir == 0):

            dir = Adafruit_MotorHAT.FORWARD


            dir = Adafruit_MotorHAT.BACKWARD

        randomsteps = random.randint(10,50)

        st1 = threading.Thread(target=stepper_worker, args=(myStepper1, randomsteps, dir, stepstyles[random.randint(0,3)],))



    if not st2.isAlive():

        randomdir = random.randint(0, 1)

        if (randomdir == 0):

            dir = Adafruit_MotorHAT.FORWARD


            dir = Adafruit_MotorHAT.BACKWARD


        randomsteps = random.randint(10,50)

        print("%d steps" % randomsteps)


        st2 = threading.Thread(target=stepper_worker, args=(myStepper2, randomsteps, dir, stepstyles[random.randint(0,3)],))


Listing 3: Adafruit’s motor HAT Python module includes sample software such as this snippet demonstrating use of simple control commands and use of the Python threading module to control a pair of stepper motors. (Image source: Adafruit)

For functionality not offered in available HATs, developers do not need to limit themselves to the HAT format. The DFRobot DFR0327 Arduino shield, Seeed Technology GrovePi+ starter kit, and MikroElektronika MIKROE-2756 click shield give developers access to the broad array of available Arduino shields, Grove devices, and MikroBUS click boards, respectively.

Using these boards, developers can quickly add support for standard CAN interfaces by attaching the MikroElektronika MIKROE-988 CAN click board, and for 4-20 mA current loops with the MikroElektronika MIKROE-1296 4-20 mA current loop click board.

Rounding out the small shop design

Even after rapidly configuring the Raspberry Pi-based design with the required add-on functionality, developers often lose time in building an appropriate user interface. With Raspberry Pi 3, developers can connect the design to Adafruit’s IO cloud service to provide users with graphical feedback and control of the automation processes. The cloud service allows developers to create simple feeds of data and process information (Listing 4) and build dashboards that allow users to monitor and control projects from any web browser on desktop, smartphone, or other mobile device (Figure 3).

# Import library and create instance of REST client.

from Adafruit_IO import Client

aio = Client('YOUR ADAFRUIT IO KEY')


# Send the value 100 to a feed called 'Foo'.

aio.send('Foo', 100)

Listing 4: Developers can easily stream data from their industrial automation application to the Adafruit IO cloud for display and control. (Image source: Adafruit)

Image of Adafruit IO dashboard

Figure 3: Developers can display information from their Raspberry Pi 3-based industrial automation applications and provide control using the Adafruit IO dashboard. (Image source: Adafruit)

The combination of simple software development, diverse add-on boards, and the high-performance Raspberry Pi provides a suitable solution for small-scale industrial automation applications. In some of these applications, however, developers may need tighter timing control than that provided in configurations using an RTC, such as the Adafruit 3013 RTC HAT.

The 3013 RTC HAT is based on the Maxim Integrated DS3231 RTC IC, which provides two programmable alarms and a square wave output signal. Developers can use the alarms to generate an interrupt at a specified number of days, hours, minutes, and seconds, or use the square wave to generate an interrupt at its 1 Hertz (Hz) rate. For applications that require periodic interrupts faster than 1 Hz, developers will need to develop custom software functions using the processor’s system timer, or build custom hardware counters able to generate an interrupt at the desired rate.

In applications that require faster timing resolution, an equally important requirement becomes deterministic response latency. At high rates, variability in response latency in the standard Raspbian OS could compromise accuracy. Although the standard system will likely provide a sufficiently deterministic response with millisecond resolution, developers might need to turn to approaches using the Linux PREEMPT_RT patch to meet tighter and more deterministic resolution requirements.


Conventional PLCs offer capabilities typically beyond the requirements and budgets of most smaller industrial operations in small scale manufacturing, machining, and prototyping shops. For these applications, operators typically face more modest requirements that lie well within the capability of the Raspberry Pi 3.

Using Raspberry Pi 3 and appropriate add-on boards, developers can rapidly implement dedicated industrial automation systems capable of meeting requirements for a wide range of small shop operations.

Disclaimer: The opinions, beliefs, and viewpoints expressed by the various authors and/or forum participants on this website do not necessarily reflect the opinions, beliefs, and viewpoints of Digi-Key Electronics or official policies of Digi-Key Electronics.

About this publisher

Digi-Key's North American Editors