# An Internet of Things (IoT) Framework in PHP

## Overview

This project will demonstrate a distributed IoT application using one or more single-board computers (SBCs) and one or more microcontroller (MCU) development boards. It can be adapted to any combination of SBCs and MCUs. This demonstration will use a Raspberry Pi 3, two C.H.I.P.s, and an Arduino Nano. The SBCs mentioned all have onboard WiFi, allowing your projects to be mobile.

All PHP scripts and Arduino libraries and sketches used in this project are available for download. I recommend examining each file as it is mentioned in this article in order to gain a clear understanding of how each piece works. The demo project is a rather complex one, designed to fully demonstrate the power and versatility of the framework. You'll soon learn how it can serve as a foundation for all kinds of networked electronics projects, large and small, with a minimum of effort.

You can download the PHP IoT framework here. All required third-party files are linked to below.

The framework consists of a number of serial communication adapters and socket servers and clients combined in various ways. These will enable you to control multiple MCU-based hardware projects (herein referred to as "Droids") from the command line or from a vintage serial terminal or home computer. We will demonstrate the latter using a TRS-80 Model 100 laptop and a Color Computer 2 with the Deluxe RS-232 Pak. You can also automate your projects using the provided script engine. All of these things are accomplished using a simple, user-friendly command-and-response protocol.

This article assumes you're familiar with Linux and PHP programming and have some experience building electronic projects with Arduino. I cannot answer questions that have been covered elsewhere, so please review all of the material linked to below and spend some time experimenting with these concepts before you build.

## PHP Classes

As mentioned above, the PHP software consists of serial adapters and TCP socket servers and clients. The abstract `SocketServer` and `SocketClient` classes are extended to create the specific modules needed for network communications. Extending these base classes is largely a matter of implementing event handlers: methods that respond to connects, disconnects, errors, and incoming data. Several frequently-used concrete classes are provided:

- `SocketClient`
  - `Shell` — for issuing commands from the command line
  - `Terminal` — for issuing commands from a serial terminal
  - `Port` — for issuing commands from your own PHP scripts
- `SocketServer`
  - `Gateway` — routes any client on the network to any endpoint server on the network
  - `EchoServer` — demo endpoint server that simply echoes commands back to the client
  - `SignalIoServer` — endpoint server that controls our example project or Droid

The `Serial` and `Port` classes are used by other modules and scripts to create serial and TCP connections, respectively.

The `ScriptEngine` class reads a list of commands from a file and sends them out over the network one-by-one through a `Port` adapter. Responses to all of the commands in a script are returned in an array.

The `Droid` class encapsulates the operations of an MCU-based project. It enables sending commands to a MCU over a `Serial` adapter. The `SignalIoDroid` class extends `Droid` to implement all of the commands recognized by our Signal IO Droid project.

To better understand PHP socket programming, consult the Socket Functions section of the PHP Manual, paying attention to the user notes therein.

We'll also be using the Getopt class from Zend Framework. We don't need the whole Zend Framework, so just download the one class file and copy it to the IoT framework's `php/vnd` folder.

At this point, it should be apparent that any module can be hosted on any SBC on the network, allowing for tremendous architectural flexibility in your application.

## Arduino Libraries

A number of third-party Arduino libraries are required for the demo project. I recommend following the original authors' instructions to install these libraries in your Arduino IDE. It would be a good idea to read the tutorial accompanying each library to gain a clear understanding of how the various hardware devices and code work, as I won't spend too much time repeating what has already been written.

- Arduino Playground - Messenger (Ignore the message in red and use the download link further down the page.)
- Adafruit TCA9548A 1-to-8 I2C Multiplexer Breakout
- Etherkit Si5351Arduino
- Adafruit 4-Channel ADC Breakouts
- Adafruit MCP4725 12-Bit DAC Tutorial
- Adafruit 16-Channel Servo Driver with Arduino
- Arduino-AD9851-DDS-Direct-Digital-synthesizer-Signal-Generator

The first item, the Messenger library, is bundled with an application that we won't be using. Drill down to the subfolder `Messenger/messenger/firmware/Messenger` and copy it to your Arduino libraries folder. Then add the following lines to `Messenger.h` and `Messenger.cpp`. This will enable the Messenger class to read `unsigned long` numerical strings from the serial port.

Messenger.h, line 23:

unsigned long readUL();

Messenger.cpp, line 68 - 75:

unsigned long Messenger::readUL() { if (next()) { dumped = 1; return strtoul(current, NULL, 0); } return 0; }

The last item, the DDS function generator code, is not a library per se, but rather a set of functions that will automatically be included in the `SignalIO.ino` sketch when placed in the same folder as the latter.

In addition to the foregoing, read John Boxall's excellent tutorial on using the MCP23017 dual port expander IC with Arduino. (As a matter of fact, the Arduino novice wishing to expand his skills should read all of his tutorials.) The code to control the port expanders is included in our Signal IO sketch, so it doesn't require an additional library.

## Hardware

The Signal IO Droid consists of an Arduino Nano controlling several I2C chips and breakout boards, and a DDS waveform synthesizer breakout board. All of these components are available on Ebay or from an electronic hobby supplier such as Adafruit. The Ebay knockoffs tend to be cheaper, but you'll typically have to wait several weeks for them to arrive from overseas. Plan accordingly.

Not to shill for commercial vendors or anything, but they have invested the lion's share of time and money in creating the hardware and firmware used here. Please support open source hardware by purchasing at least a few of the components from a vendor such as Arduino, Adafruit, Sparkfun, or Freetronix.

You should have a good assortment of resistors, capacitors, linear voltage regulators, 22-gauge solid hookup wire, stripboard, and male and female headers and Dupont cables on hand to complete the project. (If you're attempting to build this project, you're most likely an experienced builder with a well-stocked lab, but I thought I'd mention it anyway.)

If you intend to use a serial terminal, you'll need a terminal (or home computer with an RS-232 adapter and terminal software), an FTDI cable, a null modem cable, and possibly a DB-25 to DE-9 adapter depending on which connectors your terminal and cables have. It's not necessary to use a terminal with the framework. You can run the shell client script from the Linux command line or an `ssh` client such as PuTTY. I included the terminal software because it's fun finding new uses for old computers and new `Serial` adapters.

### Layout

Main Board — Contains the Arduino Nano, four MCP23017 dual port expander ICs, an Si5351 three-channel clock generator, an AD9850 function generator, a socket for a PCA9685 16-channel 12-bit PWM/servo driver card, +5VDC and +/-15VDC power connections, and I2C bus/power connections.

DAC Board — Contains sixteen MCP4725 breakouts, multiplexed by two TCA9548A breakouts.

ADC Board — Contains four ADS1115 breakouts for a total of 16 analog-to-digital converter channels. Each channel is enhanced with input over-current and over-voltage protection, and a switchable unbiased positive clamp circuit to keep signals safely in the 0 to +5VDC range. ADCs are easy to fry. We're going the extra mile to make sure we don't do it.

## Rationale

As you may have guessed, our Signal IO Droid will provide a staggering number of digital and analog inputs and outputs. Here's what we get:

- Eight 8-bit IO ports, or 64 independent GPIO pins
- Sixteen analog-to-digital converters (ADCs)
- Sixteen digital-to-analog converters (DACs)
- Sixteen PWM servo controllers
- Three digital clock generators
- A direct digital synthesis (DDS) waveform generator

Some readers may wonder why anyone would build such a monstrosity. The answer is simple. Some combination of these signals is used to interface with practically every chip or device known to man, whether vintage, modern, electrical, electronic, or electromagnetic. Here is but a mere sample of the kinds of projects we can build around the Signal IO Droid:

- Robots (motor and relay control, sensor arrays, etc.)
- Old school voltage-controlled synthesizers
- Chiptune instruments (Commodore SID, Atari POKEY, etc.)

This one project can serve as the foundation for practically any kind of IoT project you can dream up. Instead of building out the infrastructure for each project separately, as many online tutorials demostrate, you can build a general-purpose infrastructure once and reuse it in all of your projects. Or you can use the Signal IO Droid to quickly test your designs before committing to an expensive and potentially faulty build. Or you can just experiment with devices on a breadboard without building an end product. As you can see, the slightly complicated initial build process really pays off. You'll have a Swiss army knife of signal interfacing at your command.

Another practical advantage of using the IoT framework is that you can pre-process MCU commands and constrain, scale, validate, or translate parameters before sending them to the MCU, thereby offloading some of the command interpreter work onto an SBC having more memory and processing power than the MCU. This gives the command protocol enough sophistication to be robust and user-friendly without sacrificing precious MCU resources. The included `SignalIoDroid` and `SignalIoServer` classes illustrate this concept.

Of course, you can use fewer or more of each component if desired, up to the limit specified in each device's documentation. The parts count used in our example implementation is good for most projects. Using more I2C devices could exceed the limits of the I2C bus. You could run out of IO connections if you use fewer. Find a trade-off that's right for you.

## Theory of Operation

A client (e.g., a `Shell`, `Terminal`, `Port`, or `ScriptEngine`) connects to the `Gateway` and issues commands to it and to endpoint Droid servers. The client may recognize a few special responses that are handled differently from the normal responses received from the `SignalIoDroid` or other Droid. These are:

- `#PROMPT promptText` — set the terminal or shell prompt to the specified text
- `#ERROR errorMessage` — indicates that the previous command caused an error

All clients connect only to the `Gateway` server, which serves as a home base for a network of Droids. It recognizes a few special navigation commands:

- `GO serverName` — routes the client connection to the specified endpoint server.
- `MENU` — prints a list of endpoint servers you can `GO` to via the gateway.
- `QUIT` — If the client is currently routed to an endpoint server, route it back to the gateway server. This is similar to navigating to the home screen or main menu in other applications. If the client is already "at home," disconnect the client from the network.

Responses from the `Gateway` and endpoint servers are routed back to the client. Here's a sample session:

$ php shell.php gateway> go echobot echobot> foo Received: foo echobot> bar Received: bar echobot> quit gateway> quit Disconnected. $

Droid servers translate human-friendly commands into `Droid` method calls. In that respect, a Droid server is rather like a controller in a model-view-controller (MVC) application. The Droid classes in turn generate very short command strings that an MCU, with its limited program memory, can parse and execute, and send those command strings to the MCU over a serial connection.

All of the clients and servers in your application can be hosted on one SBC, or spread out across multiple SBCs in any combination to achieve a desired load balance thusly:

- Set the IP and port number of the Gateway server in all client scripts
- Set the IPs and port numbers of all endpoint servers in the Gateway server script

## Tips for Builders

Here are a few choice tidbits I've picked up in the course of experimentation that will save you some time and frustration:

- Use female headers to make sockets on your circuit boards. Solder straight or right-angle pin headers onto breakout boards so you can plug them into the sockets on the main board(s).
  - For breakouts with one row of pins, use a right-angle header so the breakout will fit perpendicular to the main board, to free up board space for other components.
  - For breakouts with two or more rows of pins, you'll need to use the same number of female header rows for the socket, and mount the breakout parallel to the main board, as shown in the photos.
- For easy interconnections between boards, provide a header pin on your circuit boards for each signal input and output. Add some extra pins for distributing power from the main board to other boards.
- Use four adjacent traces on your stripboard to make an I2C connector bus running the length of the board. (These are for ground, 5VDC, SDA, and SCL).
- Place a 10µF electrolytic capacitor between the ground and reset pins on the Arduino. Unless you do this, the Arduino will reset each time a serial connection is made to it, resulting in up to a 10-second delay before the sketch responds to the first command — or no response at all. Very annoying!
- Don't try to create a WebSocket server by extending `SocketServer`. Implementing the WebSocket protocol would require extensive modification of `SocketServer` itself, which in turn might necessitate modifying all of its child classes. These classes are intended to help you do a few things very well and very easily. Keep it simple! You'd be better off using one of the popular PHP WebSocket server classes on the Web as a starting point. The techniques learned from studying this framework should help make it a fairly painless proposition to adapt one of them. I may include a WebSocket server in a future release, but it'll be a good while before I get to it. If you write one in the meantime, be sure to let me know!
- If possible, store all of your code on an external USB hard drive instead of relying on the SBC's flash media. If you're really paranoid about data loss, do a nightly `rsync` backup to a second drive. A good strategy would be to keep your code in a shared Samba folder for development and distribute it to your other SBCs from there.
- Use the `EchoServer` to troubleshoot networking issues. It's a standalone TCP server, so you don't need to build any hardware to use it.
- To test a Droid in isolation from the networking components, write test scripts that call the Droid's methods directly. This is also a good way to gain some extra speed in your programs, as it bypasses the Droid server's command pre-processing.
- Put a voltage regulator circuit on the main board, and use it to supply power from an external power source to all of the Droid components, including the Arduino. Use an external DC power supply that provides enough current for your circuit. Trying to run a project this big on USB power is not a good idea unless you have a really good powered hub. (Hint: Most hubs under $40 US aren't good enough.)

If you do need a good USB hub for whatever reason, I highly recommend the HooToo HT-UH010. It supplies ample current for practically any device, with no dropouts or disconnects except under extremely rare, punishing conditions. We won't come close to pushing its limits with any of our IoT projects.

## Build the Circuits

Having dutifully read all of the literature presented above and examined all the schematics, datasheets, and Fritzing layouts therein, you now know:

- how to write a simple command interpreter for Arduino using the Messenger library.
- how to connect the port expanders, ADCs, DACs, servo controller, and clock generator to the Arduino's I2C bus, and how to program them.
- the I2C bus address range allotted for each device, and, consequently, the maximum number of each device that can be placed on the bus.
- that we can't use sixteen of the DAC breakouts on the same I2C bus, necessitating the use of two I2C multiplexers to switch among them.
- that the waveform generator isn't an I2C device, and must be controlled with four of the Arduino's digital IO pins.

As we discussed earlier, it's up to you how many or how few of the IO features to include in your build. Here, I can only offer a few photos of my own build to provide ideas and inspiration. Click the photos for a larger view.

The Signal IO Droid's main board with (left to right) waveform generator, clock generator, Arduino, and four port expander chips mounted.

The servo controller breakout plugs into a socket above the waveform generator.

The Signal IO Droid's DAC board with sixteen DAC breakouts and two I2C multiplexer breakouts.

I haven't built the ADC board yet. I plan to put a clamp circuit on each of the sixteen inputs to keep everything in the 0 - 5V range.

## Setup

- Install PHP 5 on each SBC that will host application components.
- Install memcached on at least one host.
- Deploy the IoT framework on each of the PHP hosts. Simply unzip the package wherever you want to put it.
- Identify the filenames of your terminal and Droid serial adapters. They'll look something like `/dev/ttyUSB*` or `/dev/cu.usbmodem*`. Connecting the USB devices one at a time and noting what new filename appears in `/dev` will make this easier. The provided Droid scan function can help you find the filename associated with each Droid's ID once you figure out the naming convention.
- Configure the client, gateway, and server scripts:
  - Servers should listen on 127.0.0.1.
  - Clients should connect to the gateway's network IP address (starts with "192.168.2.") if they're not on the same host as the gateway.
  - Gateway routing ports should connect to the network IPs of their respective endpoint servers unless they're on the same host.
- To make the application accessible from outside your WiFi network, consult your router's documentation regarding port forwarding and:
  - Enter the gateway server's network IP (starts with "192.168.2.") and port number in the router's control panel.
  - Install the IoT framework on the remote computer.
  - Configure a client script on the remote computer to connect to your outside IP (the one assigned by your ISP) and the forwarded port number.
- Compile and upload the sketch `SignalIO.ino` to the Arduino. You can change the ID string to something that better suits your Droid's purpose. Make a backup copy before you change anything else.
- Insert the Arduino into the socket you made for it on your Droid board.
- Power up the Droid and connect it to a USB port on the SBC hosting the `SignalIoServer`.
- Critically, you'll want to calibrate the servo controller stop positions in the Droid firmware before use, to avoid pegging your servos and potentially damaging them.
- Start the programs in the following order: endpoint servers, then the gateway server, then the client scripts.

At this point, the system should be ready to accept commands. To take down the system, stop the programs in reverse order.

## Signal IO Droid Commands

When response data is expected from a command, it will be sent back to the client. Otherwise, one of these messages will be sent:

- `OK` — The command succeeded.
- `#ERROR errorMessage` — The command failed.

`ID?`

fetches the ID string stored in the Droid's firmware. This helps you use the Droid scanner to match Droid IDs with serial adapter filenames when it comes time to configure the Droid.

`PORT n SET PIN MODE p m`

where `n` is the port number 0 - 7, `p` is the pin number 0 - 7, and `m` is the pin mode `INPUT` or `OUTPUT`.

`PORT n GET PIN MODE p`

where `n` is the port number 0 - 7 and `p` is the pin number 0 - 7.

`PORT n SET PIN p l`

where `n` is the port number 0 - 7, `p` is the pin number 0 - 7, and `l` is the logic level `HIGH` or `LOW`. The pin mode must be set to `OUTPUT` before executing this command.

`PORT n GET PIN p`

where `n` is the port number 0 - 7 and `p` is the pin number 0 - 7. The pin mode must be set to `INPUT` before executing this command.

`PORT n STROBE PIN p l`

Strobe pin `p` of port `n` `HIGH` or `LOW`. The pin mode must be set to `OUTPUT`, and the pin level should have a resting value opposite of `l`, before executing this command, as it would make little sense to, e.g., strobe a pin `HIGH` if it's already `HIGH`.

`PORT n SET MODE m`

Set port `n` mode to `m`.

`PORT n GET MODE`

Get port `n` mode.

`PORT n READ`

Read a byte of data from port `n`. The port mode must be set to `INPUT` before executing this command.

`PORT n WRITE d`

Write a byte of data `d` (0 - 255) to port `n`. The port mode must be set to `OUTPUT` before executing this command.

`ADC n SET GAIN g`

Set amplifier gain of ADC channel `n` (0 - 15) to `g` (see ADC Amplifier Gain Values, below).

`ADC n READ`

Read the input voltage of ADC channel `n`.

`ADC n READ DIFF`

Read the difference in input voltages between an even-numbered ADC channel `n` and channel `n + 1`, or between an odd-numbered channel `n` and channel `n - 1`.

`DAC n WRITE v`

Set the output voltage of DAC `n` (0 - 15) to `v` (0 - 5).

`PWM SET FREQ f`

Set the frequency of all sixteen PWM/servo pulse generators to `f` (40 - 1000).

`PWM n SET DUTY CYCLE p`

Set the duty cycle of PWM/servo pulse generator `n` to `p`, expressed as the percentage (0 - 100) of the cycle where the level is `HIGH`.

`PWM n SET ANGLE a`

Set the angle of rotation of a servo connected to channel `n` to `a` degrees (0 - 180).

`CLOCK n SET FREQ f`

Set the frequency of clock generator `n` (0 - 2) to `f` (4000 - 150000000).

`WAVE SET FREQ f`

Set the frequency of function generator to `f` (0 - 30000000).

### ADC Amplifier Gain Values

There are six possible numeric values, each corresponding to a preset gain value:

- 0 — two thirds (default)
- 1 — one
- 2 — two
- 4 — four
- 8 — eight
- 16 — sixteen

### Sample Script

The `ScriptEngine` class can execute a list of Droid commands in a file:

GO signalio DAC 0 WRITE 5 DAC 1 WRITE 4.75 DAC 2 WRITE 4.5 DAC 3 WRITE 4.25 DAC 4 WRITE 4 DAC 5 WRITE 3.75 DAC 6 WRITE 3.5 DAC 7 WRITE 3.25 DAC 8 WRITE 3 DAC 9 WRITE 3.25 DAC 10 WRITE 3.5 DAC 11 WRITE 3.75 DAC 12 WRITE 4 DAC 13 WRITE 4.25 DAC 14 WRITE 4.5 DAC 15 WRITE 5 QUIT

## Example Application

As mentioned earlier, I'm using a Raspberry Pi 3, two C.H.I.P.s, and an Arduino Nano in my setup. The Nano is compatible with the ubiquitous Uno, and is small and cheap enough to permanently embed in an application. Thus, it's perfect for making Droids. You'd do well to keep a few on hand.

The SBCs are powered from a USB power strip. The Raspberry Pi has the aforementioned HooToo USB hub attached. Hard drives, terminal adapters, cameras, and Droids are attached to the hub. We'll start by running the whole application on the Raspberry Pi, then spread it out for a little WiFi fun.

[Work in progress]

## Conclusion

I wrote the first version of the IoT framework in the latter half of 2016, and had successfully tested the DAC board when the SD boot card in my Raspberry Pi crapped out during a DietPi upgrade, and I lost the only copy of all my code! That was what prompted me to set up a simple Samba-based NAS with nightly rsync backup, and to never store important stuff on an SD card again. I started over with a clean slate in summer of 2017, relearning all of the material I had forgotten over the preceding months, and started writing the current version. This version is much cleaner than the original. It does everything in a simpler, more straightforward way.

That's the long and short of it, if not all the nuts 'n' bolts. In future articles, we'll explore in depth how to create your own clients, servers, and Droids by extending the IoT framework's base classes. There will also be new Droids you can add to your network. Send me your questions, and I'll do a Q&A article as well.

For those of you who reached this page from my résumé and got sidetracked reading all of this: don't forget that you're planning to hire me.

Have fun, and happy hacking!

The permanent home of this article is http://php-iot.madamebutterface.com.

Copyright © 2017 James S. Elkins <jselkins@yahoo.com>