If you want to build some fun Pi projects, but are still working on your Python skill, then mixing Node-Red with Python might be a good option for you.
Node-Red is a low-code application that is extremely powerful for the creation of Raspberry Pi robotic and Internet of Things (IoT) projects. Node-Red has a rich set of libraries to create simple drag-and-drop logic flows.
By default Node-Red custom scripting is done in Javascript, however Python can also be used. For Python users this offers them a platform to play and learn Python basics while taking advantage of the Node-Red’s low-code interface to manage scheduling and web dashboards.
There are many cases where Raspberry Pi features are only available in Python, so even die-hard Node-Red users could benefit with knowing how to integrate Python into their projects.
In this blog I’ll look at two examples that mix Python and Node-Red. The first will create web dashboard to drive a Raspberry Pi rover. The entire project will only use two Node-Red widgets, where one of the widgets uses Python script within the Node-Red environment. The second project will create an IoT page that shows temperature and humidity data from an BME280 sensor.
Getting Started
Depending on your Raspberry Pi image, Node-Red may already be installed. If not, see the Node-Red documentation or your Pi image for custom installation directions.
There are some excellent dashboard components that can be used to create some light-weight web interfaces. For this blog, I’ll be using the Button State flow. This widget can be used to create an array of buttons. To install this component go into the Manage Palette menu item, click on the install tab, and then search for ui-button.
The next important step is to add a Python enabled widget, there are a few choices. I chose the python-function-ps component because it’s been recently updated, however the other two choices worked on my test projects.
Being able to use Python instead of Javascript in Node-Red is an extremely useful features, however it’s not bulletproof and some care will be needed when you’re using some advanced Python libraries.
In the next section I’ll use these two widgets to control a Raspberry Pi rover.
Creating a Raspberry Rover
There are many approaches to creating a car or a rover with a Raspberry Pi. For this project I used:
- A 2-motor car chassis (~$15)
- Portable battery (5V, 3 Amp output ~$30)
- Raspberry Pi with a motor shield
- 4 alligator clips, and 4 jumper wires
- some elastic bands and duct tape
For a Raspberry Pi 3 and 4 the portable battery needs to output 3A. If you are using an other PI 1/2 you can use a standard 2.1A phone charger.
Due to the power draws it is not recommended that you connect motors directly to a Raspberry Pi, luckily there are quite a few good motor or automation shields available (~$25). If you’re feeling adventurous you can build your own motor shield with a L293D chip for ~$2. On this project, I used an older PiFace Digital module which is has good Python support, but weak Node-Red functionality.
The 2-motor car chassis usually come without any wiring on the motors. For a quick setup I used a combination of alligator clips and jumper wires to connect the motor terminals to the Pi motor shield. A couple of strips of duct tape is useful for holding the wires in place. Finally elastic bands can keep the portable battery and the Raspberry Pi attached to the chassis.
To test the hardware setup, I found it best to keep the car chassis raised up with the wheels off the ground. This step allows you to use a standard power plug without killing your battery before you’re ready to play. You may have to do some playing with the wiring to ensure the motors are both turning in the required direction.
The first software step is to install your motor’s Python library. (Note: this step will vary depending on your motor shield). For my hardware the PiFace library is installed by:
pip install pifaceio
At this point, it’s important to test the hardware directly from Python. Check your hardware documentation for some test code to turn on and off a motor.
To test a single motor with Python within Node-Red, three flows can be used: an inject, a python-function-ps, and a debug.
The inject flow is used to create a message payload with either a numeric 0 or 1, to stop or start a motor.
In the python-function-ps flow the incoming Node-Red message (msg) is accessed as a Python dictionary variable. Below are some Python examples to read and manipulate the Node-Red message.
# get the message payload
themsg = msg['payload']
# set the payload
msg['payload'] = "Good Status"
# create an item in the message structure
msg['temperature'] = 23.5
# clear the entire message
msg.clear()
For the PiFace library, my code needed to do a write_pin command to set a specific pin, and then a write command outputs the request states for all the pins.
pin = 0
# Pass the msg payload as the pin state
pf.write_pin(pin,msg["payload"])
pf.write()
A debug flow will show if there are any problems in the Python code.
Once the basic testing is complete, then next step is to define a Node-Red dashboard that creates an array of buttons to control the rover.
The Node-Red logic for this project only requires the two new widgets that were installed earlier.
The Button State widget is edited by double-clicking on it. Multiple buttons can be added with custom labels, payloads and colors.
A simple two character string is used for the button’s message payload, with the first character being the LEFT motor state, and the second being the RIGHT motor state. A FORWARD command will set both the LEFT and RIGHT motors to 1, with a payload of 11. It’s important to note, that to turn LEFT, the LEFT motor needs to be turned off and the RIGHT motor needs to run, and visa versa for turning RIGHT.
Inside the python-function-ps flow (see below), the Python pifaceio library is imported (line 4) and a pf object is created (line 5). Next the passed button payload is parsed to make two variables, the LEFT and the RIGHT requested motor state (lines 8-9). The final step is to write to the motor states (lines 11-12).
#
# Set PiFace Digital Pins
#
import pifaceio
pf = pifaceio.PiFace()
# Get the Left and Right requested state
LEFT = int(msg["payload"][0])
RIGHT = int(msg["payload"][1])
# Set the left and right pin motor values
# the left motor is on pins 0 and right on pin 1
pf.write_pin(0,LEFT)
pf.write_pin(1,RIGHT)
pf.write()
return msg
A debug flow isn’t required but it can be useful to verify that the Python code completed cleanly.
The image below shows Rover with PiFace Digital module mounted on a Pi 1 with the Node-Red dashboard.
The interesting thing about this first project, is that it shows how lean the logic can be if you mix Python with the right Node-Red components, (only 2 flows!).
If the motor shield is based on the L293D chip set, then buttons and pins could be added for going is backward directions.
Easing into Python
There is an excellent selection of Raspberry Pi Python starter projects that can be done. Communicating with I/O, motors or sensor is always a good place to start.
The second example will look at getting temperature and humidity data from a BME280 sensor (~$5). The gathering of the sensor data will be done in Python, then the real time scheduling and the web dashboard will be created in Node-Red.
The BME280 sensor wires to the Pi using I2C (Inter-Integrated Circuit) connections. The SDA (Serial Data) and SCL (Serial Clock) are on Rasp pins 3 and 5.
The first step in the project is to enable I2C communications, and then install a Python BME280 library:
# Enable I2C, 0 = enable, 1=disable
sudo raspi-config nonint do_i2c 0
# Install Python BME280 library
pip install RPI.BME280
BME280 sensors are typically on addresses 0x76 or 0x77. To verify the address the i2cdetect command line tool can be used:
# Show the feedback of i2cdetect with sample output
$ i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77
To ensure that things are working a quick Python program needs to be created.
# bme_test.py - Show values from a BME280 sensor
#
import smbus2
import bme280
# BME280 sensor address (default address could be: 0x76)
address = 0x77
# Initialize I2C bus
bus = smbus2.SMBus(1)
# Load calibration parameters
calibration_params = bme280.load_calibration_params(bus, address)
# Get sampled data
data = bme280.sample(bus, address, calibration_params)
print("Temperature: ", data.temperature)
print("Pressure: ", data.pressure)
print("Humidity: ", data.humidity)
If everything is hooked up and working correctly, some values should appear:
$ python3 bme_test.py
Temperature: 20.943249713495607
Pressure: 996.5068353240587
Humidity: 52.84257199879564
This Python code can be tested in Node-Red with inject and debug flows.
There is a slight modification to the code (lines 17-21) where instead of doing a print statement, the sensor results are passed to the dictionary msg variable. The debug flow is defined to show the complete message, so in the debug pane it is possible to see all the sensor results.
The next step is to show the results in a web pages, and this is done with the addition of two new widgets. The first new is an old style mercury thermometer widget (ui-widget-thermometer), and the second is a scheduler (bigtimer). Note: it is also possible to include the Node-Red BME280 component for a comparison check.
The final application uses the same Python code, but a bigtimer widget schedules its execution. The bigtimer has excellent scheduling functionality, but to keep things simple the middle output can be used to sends out a pulse every minute.
The thermometer widget shows the temperature value which is the payload message from the Python script. A chart widget also reads the temperature and is configured to a 2-hour line plot.
A change component is needed to move the humidity to the payload, and this allows a second chart to show the humidity in a bar chart.
Below is a picture of a Raspberry Pi with a BME280 sensor and the Node-Red Dashboard.
Calling External Python Scripts
I found a few cases where the python-function-ps widget crashed. For me this occurred with hardware specific libraries like pyusb.
The build-in Node-Red exec component can be used to run any external program.
Below is an example to pass a Javascript timestamp into a Python program which outputs the date/time as a string without milliseconds.
The exec block is configured to append the upstream msg.payload to the command.
The external Python script uses the sys library with the argv array to read in the passed payload. Python print statements become the top output pin (stdio) that is the msg.payload for downstream components.
Below is the test Python script used to read the Node-Red payload and output a date/time string.
#
# test.py - interface with Node-Red
# Convert Javascript timestamp to string
import sys
from datetime import datetime
# Print out the timestamp as a string
if len(sys.argv) > 1:
passedtime = int(sys.argv[1])
# Strip out milliseconds
timestamp = int(passedtime/1000)
print(datetime.fromtimestamp(timestamp))
Summary
Python scripting in Node-Red offers new user programmers a great way build some fun applications without getting too bogged down.