A Gentle Introduction to IoT

⏲ A 19 min read

The IoT revolution has started. But what is it exactly? Is it hard to take part to it? In this post I present you with all the details of a very simple and almost inexpensive Internet of Things project. Read through as we go from assembling the required hardware, to coding the software that will drive it, exploring some of the most modern free technologies that are on offer today. At the end we will be able to take control of some LEDs over the internet, from wherever we are. Consider this as a launch pad to more complex and exciting IoT projects.

Preamble

No, I'm afraid this post is not on how to build your army of toy soldiers. If the disappointment hasn't stopped you from reading any further, what I will discuss here is something simpler and more peaceful, involving general electronics single board computers, LEDs, Python, web servers and web applications. All in just one place!

I will try to introduce the fundamental concepts of IoT by example, and I will do so by sharing my recent hands-on experience with my first Raspberry Pi, and how this has led me to get to know of fascinating technologies of the modern era. If your main objective is still to build an army of toy soldiers, this is definitely a first step (however you should consider using technology for better purposes, really!).

I will try not to give much for granted, but of course I will have to draw a line at some point, or this post would have never been finished! The subject is software, but it is also hardware, for the former would make little sense without the latter.

The approach will be very practical. We will start with a concrete problem, and we shall see how to solve it, both from a hardware and a software perspective.

What's IoT?

Can you eat it? Well, although your are free of munching on a breadboard if you really want to, IoT, as you probably know already, stands for Internet of Things. This term is used to indicate the inter-networking of physical devices (the things) that are equipped with electronics, sensors, motors etc.. and that communicate information with each other, sometimes taking actions based on the received inputs. In this sense, every-day life devices, like the fridges or the washing machines in our homes, or vending machines, or cars even, become smart.

Even though the first example of smart things appeared in the 1982 (and you can surely consider the toy army of Toy Story as an example of IoT in early animation movies), it is around 2016 that the IoT has evolved greatly, and the key aspect is the almost ubiquitous availability of wireless networks that allow an increasing pool of diverse devices to communicate with one another.

The IoT example of this post is somewhat a classic, but that will hopefully give you a rough idea of what the IoT is also about, in case this is the first time that you came across it. It is also quite simple and with a contained cost, but nonetheless will spawn many interesting connections with some fascinating modern technologies.

The Project

So what is exactly the project described in this post about? The idea is to turn some LEDs on and off by sending commands to a Raspberry Pi through a web page. This might seem quite a trivial project, but it has given me the opportunity to refresh some rusty knowledge of electronics that dated back to my undergraduate years, as well as learn a great deal of new things from the software side too.

The Hardware

In this first part I will describe all the hardware that is necessary for the project. The main one is, I would say, a single-board computer like a Raspberry Pi. In my case, I'm using a Raspberry Pi 3 Model B running the Raspbian OS. To turn an LED on and off we will use one of the GPIO pins. For commodity, I will also use a T-cobbler to connect all the GPIO pins on a breadboard, where the rest of the circuit will be assembled. All that we still need are a couple of jumper wires, a 220 Ω resistor and an LED of your favourite colour. To summarise, here is the minimum required:

  • 1x Raspberry Pi
  • 1x breadboard
  • 1x T-cobbler and a bus cable (alternatively 2x male-to-female jumper wires)
  • 2x male-to-male jumper wires
  • 1x 220 Ω resistor
  • 1x LED (about 20 mA max current)

The GPIO Pins

Before looking at the circuit, it is probably best to mention a few facts about the Raspberry Pi. In particular, the key part here is the set of GPIO pins that it provides.

The General Purpose Input-Output pins represent, as the name itself suggests, a way to connect to external devices in order to send commands to and from the Raspberry Pi. Some of these can be set to work as input or output, and can be set either high (3.3 V or a logic 1) or low (0 V or a logic 0). Some pins can be used together for other purposes, like communicating with another device through a Serial Peripheral Interface (SPI), attaching LCD screen through the Display Parallel Interface (DPI) etc.... Good references for the Raspberry Pi GPIO pins are this website and the Broadcom BCM2835 ARM Peripherals manual.

From the software side, the pins can be conveniently configured and controlled by means of the RPi Python module. The thing to be mindful of is that there are a bunch of different naming conventions for the pins on the Raspberry Pi. The main ones, to use a terminology proper of the RPi module, is BOARD and BCM. The former is a pin numbering that reflects the physical location of the pins on the PCB. Pin number one is on the top-left corner and gives a 3.3 V fixed output. Pin 2 is the one to its right, Pin 3 is the one below it, and so forth. The latter is the numbering convention used in the Broadcom manual.

The Circuit

Here is the schematic of the circuit that we want to build.

The circuit schematics The schematic of the circuit, showing all the components used for this project.

As I have already mentioned, I prefer using a T-cobbler to connect all the GPIO pins to the breadboard. In case you are not using one, this is what your breadboard should look like this picture.

The physical components Another schematic representation of the circuit, showing how the components are physically connected with each other on the breadboard and the Raspberry Pi 3 Model B.

Where did the magic number 220 Ω come from? The explanation is very simple and essentially based on Ohm's law. Across a resistor \(R\) to which a voltage difference of \(V\) is applied, the current flowing through it is given by

$$I = \frac VR.$$

An LED is a diode, i.e. a p-n junction, that is capable of emitting light. In forward bias, an order-one approximation of a diode is given by a small resistor (order of 10 Ω) in series with a voltage generator (of about -0.67 V). The manufacturer of the LED usually provides the maximum current that the diode can withstand. In the case of common LEDs, this value is around 20 mA. Considering that, when a GPIO pin is on, it will provide 3.3 V to our circuit, in order not to burn our LED we need to use a resistor of resistance \(R\) given by the inequality

$$\frac{3.3\text{ V} - 0.67\text{ V}} R \leq 20\text{ mA},$$

which yields

$$R\geq 130\ \Omega.$$

For a better estimate, we can look at the V-I chart provided by the manufacturer, which would probably give us a minimum value closer to 200 Ω. Anything below and you might risk frying your LED. Using a way bigger resistor, however, would starve it of current and it would not turn on at all. But with 220 Ω we should be perfectly fine (and safe!).

The Software

Now that we have assembled the required hardware, it is time to see how to control it. What we have done so far is to connect the Raspberry Pi with a very simple one-wire device, i.e. an LED. The IoT is the possibility of controlling a device from the internet, anywhere in the world, with the actual device that we want to control possibly miles and miles away from us.

What we now need is then a simple interface, accessible from the internet, that allows us to control the LED. Let's see how to create such interface, step-by-step.

But before we go any further, let's have a look, like we did with the hardware part, at all that we will need.

  • Python (2.7 or later; note that the project has been tested with Python2.7 and it might have to be adapted slightly to work with Python3)
  • a web server e.g. Apache2
  • a text editor of your choice.

Yep, that's all.

The RPi Python Module

On Raspbian there is a pretty simple way to control the GPIO pin that comes already bundled with the OS. I'm talking about the RPi Python module. For the simple task that we want to achieve here, RPi exposes all the features that we need. However, keep in mind that more advanced tasks, like real-time applications, cannot be solved by this module. This is not just because it is a Python module, but a "limitation" of any OS based on the Linux kernel, which is multitasking in nature. This means that the kernel can decide on its own how to allocate resources for all the running processes, potentially giving rise to jitter in your applications.

Another important limitation that should push you towards other approaches, like wiringPi, is the lack of hardware PWM. PWM stands for Pulse-Width Modulation and is a technique used to encode a message in a pulsing signal. Some of the common uses are that of dimming an LED (recall that an LED response is exponential, even though we treated it as linear in our first-order approximation discussed above), or controlling a motor, but this is a topic that would take us away from the main focus of this post, and it might be the subject of a future one.

Returning to our project, let's have a look at how to turn our LED on. Recall from the schematic above that we are using the GPIO Pin 16 (according to the Broadcom convention), which is the 36th physical pin on the GPIO.

import RPi.GPIO as G     # Import the GPIO component from the RPi module

LED = 36                 # Define a constant with the pin number where the LED
                         # is connected

G.setmode(G.BOARD)       # Set the pin numbering mode to the physical number

G.setup(LED, G.OUTPUT)   # Set the LED pin to output mode

G.output(LED, 1)         # Set the pin to high (3.3 V)

We can type the above lines of Python code directly into the Python interpreter. To turn the LED off we can either set the value on the pin 36 back to 0

G.output(LED, 0)

or clean up the GPIO configuration with

G.cleanup()

If we'll ever want to use more than just one LED on our breadboard, we can encapsulate most of the above code inside a Python class so that it can be reused instead of having to type it every time with setup a new LED. A minimalist Python class that would represent a physical LED would be something like

# File: led.py
import RPi.GPIO as G

G.setmode(G.BOARD)

class LED(object):
  def __init__(self, ch):
    self._ch = ch
    G.setup(ch, G.OUT)

  def on(self):
    G.output(self._ch, 1)

  def off(self):
    G.output(self._ch, 0)

  @property
  def state(self):
    return bool(G.input(self._ch))

  @state.setter
  def state(self, value):
    G.output(self._ch, bool(value))

  def toggle(self):
    G.output(self._ch, not G.input(self._ch))

Our code for turning an LED on and off would then reduce to the following few lines

from led  import LED
from time import sleep

led_red = LED(36)

led_red.on()

sleep(1)

led_red.off()

The extra method toggle can be used, as the name suggests, to toggle the LED state from on to off and vice-versa. In the above example we could then replace both led_red.on and led_red.off() by led_red.toggle(). The property state is used to get the state of the LED as a Boolean value (True for on and False for off), and it can also be used to set it. For instance, something like

led_red.state = [1]

would turn the LED on, since [1] evaluates to True when converted to a Boolean. Analogously, the following line of code

led_red.state = {}

would turn the LED off, since bool({}) = False in Python.

Sweet! We now know how to control our LED with code and all that's left to do is build a nice web interface that will execute this code on demand.

The WSGI Specification

Given that we already have some code in Python to control our LED, it would be good if the web interface could use that code directly. Is this possible? The (short) answer is yes!

A more articulated answer to the above question leads us into the realm of the Web Server Gateway Interface specification, or WSGI for short. It is a universal specification that was born out of the necessity of putting some order among all the Python frameworks for developing web applications. Before the specification, each of said frameworks would be compatible with just a few web servers. Choosing one of them would then restrict your choice of a web server to go with it, and vice-versa. To overcome this limitation, the WSGI specification was proposed in the PEP 333, which dates back ton 2003. The technical details can be found in the linked page. Here we just limit ourself to the essential details of the specification that will allow us to write a simple web application to control the LED over the internet.

In very simple terms, a Python web application consists of a bootstrap callable object that is called by the web server. The python code contained in the callable is executed, and the web server expects a response consisting of a code (200 for OK, 403 for Forbidden, 404 for Not Found etc...) and a stream of bytes (usually the HTML code to be rendered by the browser). A callable can be any Python object that exposes a __call__ function like, e.g., classes and functions.

My favourite web server is Apache2 and it will be the one that I will discuss in this post. Its functionalities can be extended with modules, and the mod_wsgi project provides a WSGI-compliant module for Apache. The documentation is very detailed and covers all the aspects, from the installation to the configuration and debugging techniques.

Regarding the installation process, this can be carried out in two modes. Either on the Apache-side, or on the Python-side. If you are into the IoT, chances are you will have your web server running on a Raspberry Pi. For this reason, I will discuss how to install the mod_wsgi module for the Apache web server. On Raspbian, this can be done with

sudo apt install libapache2-mod-wsgi

After the installation, the module should already be enabled. If this isn't the case, you can enable it with

sudo a2enmod wsgi

Writing the Web Application

The next steps are to actually write our web application and configure Apache to run it when a request comes in. In the configuration process we will specify our bootstrap Python script. By default, Apache expects to find a callable object inside it with the name application. The simplest thing that we could do is then to create a Python script and define a function with such a name. The code of our application would then be contained in this function, or called by it, in case we decide for a more modular approach.

In case things would go wrong while we develop our web application, we might want to be able to have a look at the Python stack trace to see where the problems are. Normally we would have to look in the Apache error log (usually in /var/log/apache2/error.log). However, we can make use of a middleware from the paste Python package, which is specifically designed for WSGI applications. Our bootstrap script will then look like this.

# File: bootstrap.wsgi
import sys, os
sys.path.append(os.path.dirname(__file__))

from index import Index

def main(env, start_response):
    status = '200 OK'

    output = Index.main(env)

    response_headers = [
        ('Content-type'   , 'text/html'     )
       ,('Content-Length' , str(len(output)))
    ]
    start_response(status, response_headers)

    return [output]

from paste.exceptions.errormiddleware import ErrorMiddleware
application = ErrorMiddleware(main, debug = True)

The first two lines are necessary if we want to be able to import modules and packages that are in the same folder as the bootstrap script.

We then import the class Index from the index module. This is just a design choice. The bootstrap script contains the essential code to get us to the main page (by means of the Index class), and returns us the full stack trace in case of errors (by means of the ErrorMiddleware class from the paste package). To make sense of the rest of the code, have a look at the already referenced documentation of the mod_wsgi module for Apache2.

The core code of our application is contained in the Index class from the index module.

# File index.py
from util import templ, qs
from led  import LED

led = LED(15)

class Index(object):

    @staticmethod
    def main(env):
        params = qs(env)
        if "action" in params and 'toggle' in params['action']:
            led.toggle()
        return templ('index', state = "on" if led.state else "off")

In fact, this module looks more like a view rather than a controller, as the actual code for controlling the LED is buried in the led module that we have analysed previously. The util.py module contains some helper functions to conveniently deal with HTML templates and query strings. We refrain from showing the code in this post, but you can find it in the dedicated GitHub repository.

The HTML template contained in the index.html file is very simple and looks like this.

<!DOCTYPE html>
<html lang="en-us">
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="theme-color" content="#666666" />
    <link rel="stylesheet" type="text/css" href="led_app/led.css">
  </head>
  <body>
    <a href="?action=toggle"><div class="button {state}"></div></a>
  </body>
</html>

The body is essentially just a link that executes a request with the parameter action set to toggle, containing a placeholder div element. The look-and-feel of a 3D button is then provided by the classes contained in the linked stylesheet, which you can find in the GitHub repository. Note how we use the {state} placeholder in the class attribute of the div element. This allows us setting the button appearance according to the LED current state. In the stylesheet we have two classes, .on and .off, the former giving a bright red colour to the button, while the latter giving a darker shade. The value is passed by the line

        return templ('index', state = "on" if led.state else "off")

in the static method main of the Index class of index.py.

Configuring Apache

We are almost ready to start playing with our LED over the internet. The last step is to put our application up and running on the Apache web server. To this end there is a tiny bit of configuration that we need to do. We can start by making a copy of the default web site Apache2 comes with. On Raspbian, this is located in /etc/apache2/sites-available and is contained in the 000-default.conf configuration file. Make a copy of this file in, say, led.conf and modify it to look like the following one.

# File: led.conf
<VirtualHost *:80>
  ServerAdmin webmaster@localhost

  # Required by static data storage access (e.g. css files)
  DocumentRoot /home/pi/Projects/www

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

  # WSGI Configuration
  WSGIScriptAlias /led /home/pi/Projects/www/led_app/bootstrap.wsgi

  <Directory /home/pi/Projects/www/led_app>
    Require all granted
  </Directory>

</VirtualHost>

Even though we are implementing a WSGI application, the DocumentRoot /home/pi/Projects/www is needed because we are importing a css file in index.html. The above configuration file assumes that the web application resides in the /home/pi/Projects/www/led_app folder. This way, static files can be accessed with a relative path referring to the parent folder /home/pi/Projects/www. This explains why we are importing the led.css file as

    <link rel="stylesheet" type="text/css" href="led_app/led.css">

The section below the # WSGI Configuration comment is the WSGI part of the configuration. The first line of this section tells Apache which script to use to bootstrap the web application. This is the script that is assumed to contain the callable object named application. The rest of the WSGI configuration section is required to set the rights to read the bootstrap and the python scripts contained in the web application folder.

That's it! All we have to do now is deactivate any other website on port 80 with a2dissite (this is only required if the web sites are not named), and enable led.conf with a2ensite led.conf. Restart the Apache2 web server with

sudo service apache2 restart

and point the browser on any device that is on the same local network as the Pi to its local IP address, append '/led' to it to start the web application (e.g. http://192.168.0.203/led) and you should now be in the web application, with a red button that will now allow you to control the LED state over the LAN.

The final look of the web application The final look of the web application. The red button in the middle is used to toggle the LED state.

If you are behind a router, of course you will need to forward the port 80 to the Pi's local address before you could be able to access your web application from the internet, outside of your local network. In this case you will have to use the router's public IP instead of the local IP.

Conclusions

We have come to the end of this post on a simple IoT project. Its main purpose, like most of the other posts in this blog, is two-fold. On one hand, it is a way for me to take notes of new things that I have discovered and that I can later come back to if I need to. All the information gathered from the cited sources is gathered in a single place, which makes it more convenient than having to go through them separately. On the other hand, it is a way to share my experience with others, in the hope that it could be useful somehow. Even though the project in it self, as I have remarked many times now, is quite simple, the post includes references to many topics, e.g. electronics, programming, web servers and applications, and it shows you how all these different aspects can be organically combined together to create something.