Beside working with a visual scripting tool like Node-RED, there is also the more common possibility of programming the IoT process using Python. To do this on the Raspberry Pi, it is now further described how to set up and use the web-browser editor JupyterHub as well as working with the Sense HAT and MQTT programmatically.

About JupyterHub

The “Project Jupyter” exists of different applications which are open-source. One of them is JupyterHub, a multi-user verion of Jupyter Notebook. It is used to write and work with Python code using a web GUI with the additional advantage of multiple user access.

This way, different users can access different files what can be used to give teachers access to solutions while students can only access prepared code.

The official website of the project is the http://jupyter.org/

Setting up and Working with Jupyter

The configuration of JupyterHub is already described in the setup of the Raspberry Pi in the “IoT Starter Kit ‘Raspberry Pi'” section. To use JupyterHub, the web interface must be started from the Raspberry Pi. If it is not done automatically, do the following:

  • Connect with the Raspberry Pi.
  • Execute the following command
    sudo jupytherhub
    Note: Starting without sudo causes only the starting user to gain access.
  • Connect your PC, laptop or other device with the same network as the Raspberry Pi is connected to.
  • Open a browser and type the IP of your Raspberry Pi.
  • Enter the password in the web interface.

Here are some advices for working with Jupyter:

  • The code is typically divided into blocks, so called “cells”.
  • To execute the code, select a cell and press the button “run cell, select below”.
  • After this step, a new cell will be added if there is no empty cell left.
  • You can stop an execution by pressing “interrupt kernel” or “restart the kernel” (beside the execute-button).
  • The project can be saved using the “save” button at the left top corner.
  • After you are done, you can also log out using the button at the top right corner.

Exercises

Exercise 1: Reading the Sensors and Using the Joy Stick

The first exercise is to read the sensors of the Sense HAT and trigger functions when the joy stick is used.

Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “01_ReadSensors” in the folder “Exercises” of the Jupyter interface.
  • You will see a template you can work with. The sense_hat module is already imported and will be extended later.
  • Also, an instance of Sense HAT is created which is needed for the whole exercise.

To read the sensors of the Sense HAT, do the following:

  • At the beginning, call the method clear() of the instance sense. This will clear the display if it was used before.
  • Afterwards, you can use the following SenseHAT methods to get environment parameters of the sensors: get_pressure(), get_temperature() and get_humidity().
  • Now you can print the values. To do that, please parse the result into a string literal. Additional information: The unit of the pressure is millibar, the one of temperature is degree Celsius and the humidity is measured in %%rH.

After the basic sensors are read, the information of the IMU can be used:

  • There is either the possibility of reading the values of the single components of the IMU or the combined result. In the following, we will do the latter. To do this, call the method sense.get_orientation and safe it in a variable, like orientation.
  • Now, get the single orientation values called “pitch”, “roll” and “yaw”. The single meanings in the coordinate system are shown e.g. here in the section “Learn about pitch, yaw, roll”: https://projects.raspberrypi.org/en/projects/getting-started-with-the-sense-hat/9
  • To get for example the “pitch” value, you can use the following construct: pitch = orientation["pitch"].
  • After repeating this step for the other two values, you can also print these results. Please remember to parse and possibly round the values before printing them.

Last of all, some basic work with the joy sticks will be shown:

  • To make the joy stick trigger a function, we need to program this function first. This function, for example called pushed_up, needs an event as parameter.
  • The only thing this method will do for the moment is to print the parameter event.
  • To connect the joy stick event with the method, the following line of code is needed:
    sense.stick.direction_up = pushed_up
  • If you execute the code and press the joy stick up now, you will notice that the method is called twice. This is because two events are fired: One when the action is pressed and one when the action is released.
  • To avoid that behaviour, import the constant ACTION_RELEASED from the module sense_hat.
  • Afterwards, before you print the event, check that the event action (event.action) does not equal ACTION_RELEASED.

Template for the students

from sense_hat import SenseHat
sense = SenseHat()

Solution

from sense_hat import SenseHat, ACTION_RELEASED
sense = SenseHat()
""" Read the Sensors for Pressure, Temperature and Humidity """
sense.clear()
pressure = sense.get_pressure()
print(round(pressure, 2), "Millibar")
sense.clear()
temperature = sense.get_temperature()
print(round(temperature, 2), "°C")
sense.clear()
humidity = sense.get_humidity()
print(round(humidity, 2), "%%rH")
""" Read the IMU Sensor (Gyroscope, Accelerometer, Magnetometer) """
sense.clear()
orientation = sense.get_orientation()
pitch = orientation["pitch"]
roll = orientation["roll"]
yaw = orientation["yaw"]
print("pitch", round(pitch, 2), "roll", round(roll, 2), "yaw", round(yaw, 2))
""" Using the Joy Stick """
def pushed_up(event):
  if event.action != ACTION_RELEASED:
 
print (event)
sense.stick.direction_up = pushed_up

Exercise 2: Showing Text on the Display

The second exercise is about using the display to show text or manipulate the single pixels. Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “02_UseDisplay” in the folder “Exercises” of the Jupyter interface.
  • The template is similar to the one of the last exercise. You can use it to extend it.
  • To show a message (sliding through the display), the method sense.show_message(string) can be used.

Beside that, there is also the possibility to display a color:

  • This time, use the sense.clear() method for displaying a color by passing the argument (tuple) (255, 0, 0). This is a color in RGB system and, by passing it to the function, the display will become red.
  • Afterwards, before calling the sense.clear() without arguments again, put the main thread asleep by the method time.sleep(1).

Additionally, it is also possible to set single pixels:

  • First, create two variables x and o which save two different RGB colors as tuple as it was done before.
  • Then, create a list with a pattern you want to show on the display later. The display consists of 8 x 8 pixels. Here is an example of how this pattern could look like:
    pattern = [
    o, o, o, o, o, o, o, o,
    o, o, o, x, x, o, o, o,
    o, o, x, o, o, x, o, o,
    o, o, o, o, o, x, o, o,
    o, o, o, o, x, o, o, o,
    o, o, o, x, o, o, o, o,
    o, o, o, o, o, o, o, o,
    o, o, o, x, o, o, o, o,]
  • To make the display show this pattern, call the Sense HAT method set_pixels(pattern) with the pattern that was just programmed.

Template for the students

from sense_hat import SenseHat
import time
sense = SenseHat()
sense.clear()

Solution

from sense_hat import SenseHat
import time sense = SenseHat()
sense.clear()
sense.show_message("Hello World")
time.sleep(1)
sense.clear((255, 0, 0))
time.sleep(1)
x = [255, 0, 0]
o = [255, 255, 255]
pattern = [
o, o, o, o, o, o, o, o,
o, o, o, x, x, o, o, o,
o, o, x, o, o, x, o, o,
o, o, o, o, o, x, o, o,
o, o, o, o, x, o, o, o,
o, o, o, x, o, o, o, o,
o, o, o, o, o, o, o, o,
o, o, o, x, o, o, o, o, ]
sense.set_pixels(pattern)

Exercise 3: Combining the Sensor Data and Display Output

The third exercise is a combined exercise for displaying text on the display, using the sensors and triggering functions using the joy stick. Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “03_CombinedDisplaySensors” in the folder “Exercises” of the Jupyter interface.
  • First of all, add a line of code so that the method pushed_right is triggered when the stick is pushed right.

Fill the single event methods by following these steps:

  • In both methods, replace the pass by an if-condition that checks that the event action does not equal ACTION_RELEASED.
  • Afterwards, extend the pushed_up function so that the current temperature is displayed on the Sense HAT when the joy stick is pushed up.
  • For pushing the joy stick right, read the humidity and display it together with an explaining text in pushed_right.

Template for the students

from sense_hat import SenseHat, ACTION_RELEASED
def pushed_up(event):
 
pass
def pushed_right(event):
 
pass
sense = SenseHat()
sense.clear()
sense.stick.direction_up = pushed_up

Solution

from sense_hat import SenseHat, ACTION_RELEASED
def pushed_up(event):
 
if event.action != ACTION_RELEASED:
 
  temperature.sense.get_temperature()
 
  sense.show_message(str(round(temperature, 2)) + " C")
def pushed_right(event):
 
if event.action != ACTION_RELEASED:
 
  humidity = sense.get_humidity()
 
  sense.show_message("Humidity: " + str(round(humidity, 2)))
sense = SenseHat()
sense.clear()
sense.stick.direction_up = pushed_up
sense.stick.direction_right = pushed_right

Excercise 4: Adding a MQTT Publisher for Temperature Data

The first exercise is to read the sensors of the Sense HAT and trigger functions when the joy stick is used. Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “04_MQTTPublish_Sensors” in the folder “Exercises” of the Jupyter interface.

Extend the template by connecting to the broker using MQTT:

  • In the template, the MQTT Client for Python is already imported. So, you can create a new instance of mqtt.Client() to work with.
  • The client can trigger a function when it is connected. To do this, assign the on_connect function to client.on_connect so that the function is called as callback. This function will then print a status message and code as soon as the client, so the current Pi, is connected to the broker.
  • To connect the Pi to the broker, call the method client.connect(ip, port, keepalive) whereas the IP is the one of the broker (e.g. “192.168.10.3”) and the port is also the one of the broker, default: 1883. The parameter “keepalive” determines the time in second between two communications with the broker. If there are no messages, a ping message will be sent after the keepalive time. You can set it to “60” here.
  • To complete the connection, call client.loop_start(). This starts a background thread which repeatedly calls loop() so that the main thread is not blocked.

Now that the connection is established, data can be published on the broker:

  • In an infinite loop, start with a sleep-command for two seconds.
  • Then, you can call the method client.publish(topic, payload). You can enter a topic of your choice, for example “temperature”. The payload is the actual message that is sent. In this case, set the payload to the current temperature using the Sense HAT sensors.
  • Additionally, you can add a debug message by printing an appropriate text after the publishing.

Template for the students

import paho.mqtt.client as mqtt
import time
from sense_hat import SenseHat, ACTION_RELEASED
sense = SenseHat()
def on_connect(client, userdata, flags, rc):
 
print("Connected with result code", str(rc))

Solution

import paho.mqtt.client as mqtt
import time
from sense_hat import SenseHat, ACTION_RELEASED
def on_connect(client, userdata, flags, rc):
 
print("Connected with result code", str(rc))
client = mqtt.Client()
client.on_connect = on_connect
client.connect("192.168.10.3", 1883, 60)
client.loop_start()
sense = SenseHat()
while True:
 
time.sleep(2)
 
client.publish("temperature", sense.get_temperature())
 
print("Temperature data published.")

Exercise 5: Adding a MQTT Subscriber for Temperature Data

After setting up a publisher, you can also subscribe to the data that is published on the broker. This will be done in this exercise. Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “05_MQTTSubscribe_Temperature” in the folder “Exercises” of the Jupyter interface.

At the beginning, set up the subscriber for the topic of the last exercise:

  • Again, instantiate a variable of type mqtt.Client and assign the on_connect function to the appropriate callback method of the client.
  • This time, also assign the appropriate method to the callback method client.on_message. The already given function on_message takes the payload of the topic, taken from the broker, and only prints it (at the moment).
  • Afterwards, call the connect method with the same parameters as before.
  • Then, call client.loop_forever(). This thread for connecting to the broker is blocking which is what we want as the subscriber should permanently listen to this topic.
  • At last, extend the on_connect function. Up to now, the client would not receive any message because it is not subscribing to any topic. By calling client.subscribe(topic) after calling print, the client can observe this topic and this observing will be renewed whenever the client reconnects. As topic, choose the same topic that the other client publishes to (in the example, the topic was “temperature”).
  • Now, at first, execute the code of the last exercise and, at the same time, your current code of this exercise. Then, the publisher will publish data to the broker and as soon as the data is published, the subscriber will be informed about it.
  • Take a look at the printed payload message. You will see that it contains the subscribed topic as well as the temperature that was published.

The functionality of the subscriber should now be extended. The temperature data should be saved in a list and when an event is triggered, this list of temperature data should be plotted. To implement this behaviour, do the following:

  • Add a variable called tempData in the global code and initialize it as empty list.
  • The on_message function is called whenever the subscriber receives a message from the broker (respectively when the publisher publishes data). So, add a line of code where this data, saved in msg.payload, is appended to the list of temperature data. Remember to parse the message.
  • Now, add a function which is triggered when the joystick is used, for example when the joy stick is pushed up. Using the method pyplot.plot(tempData) as well as pyplot.show(), this method should plot the temperature data that was already received.
  • Afterwards, connect the callback function with the joy stick method and test the application.

Template for the students

import paho.mqtt.client as mqtt
import matplotlib.pyplot as pyplot
from sense_hat import SenseHat, ACTION_RELEASED
def on_message(client, userdata, msg):
 
print(msg.topic, str(msg.payload))
def on_connect(client, userdata, flags, rc):
 
print("Connected with result code", str(rc))
sense = SenseHat()

sense.clear()

Solution

import paho.mqtt.client as mqtt
import matplotlib.pyplot as pyplot
from sense_hat import SenseHat, ACTION_RELEASED
sense = SenseHat()
sense.clear()
def pushed_up(event):

  if event.action != ACTION_RELEASED:
 
  pyplot.plot(tempData)
 
  pyplot.show()
def on_message(client, userdata, msg):
 
print(msg.topic, str(msg.payload))
 
tempData.append(round(float(msg.payload), 2))
def on_connect(client, userdata, flags, rc):
 
print("Connected with result code", str(rc))
 
client.subscribe("temperature")
sense.stick.direction_up = pushed_up
tempData = []
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.10.3", 1883, 60)
client.loop_forever()

Exercise 6: Adding a MQTT Subscriber for Light Switching

This script will be used later in companion with the MQTT Dash App for smartphones. To enable switching the Sense HAT’s display on and off, it will be started by implementing the corresponding subscriber which can only be tested in companion with the app later on. Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “06_MQTTSubscribe_LightSwitch” in the folder “Exercises” of the Jupyter interface.
  • You will see that the subscriber will observe the topic “item/switch”. You will need this later on for the app.

Now, implement two functions for switching the light on and off:

  • switch_on should set the display color to white.
  • switch_off should set the display color to black.

Afterwards, these functions should be called according to the incoming message:

  • To do this, there are different options, for example using if-else-conditions, a dictionary or similar.
  • Anyway, it is necessary to parse the payload into an integer and check whether its value is zero or one. If it equals zero, call the switch off method, and if it equals one, call the switch on method.

Template for the students

import paho.mqtt.client as mqtt
import matplotlib.pyplot as pyplot
from sense_hat import SenseHat, ACTION_RELEASED
sense = SenseHat()
sense.clear()
def on_message(client, userdata, msg):
 
print(msg.topic, str(msg.payload))
def on_connect(client, userdata, flags, rc):
 
print("Connected with result code", str(rc))
 
client.subscribe("item/switch")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.10.3", 1883, 60)
client.loop_forever()

Solution

import paho.mqtt.client as mqtt
import matplotlib.pyplot as pyplot
from sense_hat import SenseHat, ACTION_RELEASED
sense = SenseHat()
sense.clear()
def switch_on():
 
sense.clear((255, 255, 255))
def switch_off():
 
sense.clear()
def on_message(client, userdata, msg):
 
print(msg.topic, str(msg.payload))
 
options = {
 
  0: switch_off,
 
  1: switch_on
 
}
 
options[int(msg.payload)]()
def on_connect(client, userdata, flags, rc):
  print("Connected with result code", str(rc))
 
client.subscribe("item/switch")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.10.3", 1883, 60)
client.loop_forever()

Excercise 7: Adding a MQTT Subscriber for Displaying Text

This script will be used later in companion with the MQTT Dash App for smartphones. It will display the text that is published by the app, so it will not (easily) be possible now to test this code.Starting from the template for the students, you can follow these steps and explanations:

  • First of all, connect to the Jupyter interface using the IP 192.168.10.X with X corresponding to your Pi.
  • Then, open the notebook “07_MQTTSubscribe_Display” in the folder “Exercises” of the Jupyter interface.
  • This time, the subscriber will not be given by the template. So, please implement the client with an on_connect and on_message function, connect it with the broker and start the loop (forever). Subscribe to the topic “item/display”.
  • Additionally, in the on_message function, use the method of the Sense HAT to display the payload on the display.

Template for the students

import paho.mqtt.client as mqtt
from sense_hat import SenseHat
sense = SenseHat()
sense.clear()

Solution

import paho.mqtt.client as mqtt
from sense_hat import SenseHat
sense = SenseHat()
sense.clear()
def on_message(client, userdata, msg):
 
print(msg.topic, str(msg.payload))
 
sense.show_message(str(msg.payload))
def on_connect(client, userdata, flags, rc):
  print("Connected with result code", str(rc))
 
client.subscribe("item/display")
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("192.168.10.3", 1883, 60)
client.loop_forever()