I am finishing a new book on Node-RED and the Raspberry Pi Pico W. This book is filled to the brim with projects combining Node-RED with Raspberry Pi Pico W (the Pico version with built-in WiFi). These projects span simple environment monitoring web Dashboards to more advanced ones using cloud APIs.
I’m sharing a couple of projects from this book in this post. The first explains how to use the “catch” node, equivalent to the “try” command you’ll find in many programming languages. The second is an example from the MicroPython primer (a mini-book within a book) that shows how to use the serial port on a microcontroller.
The “catch” node
The catch node in Node-RED is used for error handling. It captures runtime errors or exceptions in a flow, allowing you to manage them effectively. When an error occurs in any node that the catch node is configured to watch, the catch node triggers and sends a message object containing details about the error.
The catch node in Node-RED is equivalent to similar commands in languages like Python and Ruby. For example, you can use the try, except, finally blocks for exception handling in Python. Here’s a simple example:
try: result = 10 / 0 except ZeroDivisionError: print("Cannot divide by zero.") finally: print("Execution completed.")
In this example, an attempt is made to divide ten by zero within the try block. This produces a ZeroDivisionError, caught in the except block, and the message “Cannot divide by zero.” is printed. The “finally” block is executed regardless of whether an exception was raised, printing “Execution completed.”
In Ruby, exception handling can be done using begin, rescue, and ensure blocks. Here’s a straightforward example:
begin result = 10 / 0 rescue ZeroDivisionError puts "Cannot divide by zero." ensure puts "Execution completed." end
Like the Python example, this Ruby code attempts to divide ten by 0 in the beginning block. This raises a ZeroDivisionError, caught in the rescue block. The message “Cannot divide by zero.” is printed. The ensure block is always executed, regardless of an exception, and prints “Execution completed.”
Let’s look at how exception handling works in Node-RED with “catch”.
How the “Catch” Node Works
When you add a catch node to your flow, you can configure it to listen for errors from all nodes in the same tab or from specific nodes. Once an error is caught, the catch node generates a new message object that contains information about the error. This message object usually has properties like msg.payload for the error message, msg.topic for categorising the error, and msg.error containing the complete error object with extra details such as the error code and stack trace.
In the screenshot below, you can see the properties window for the ”catch” node. The configuration is simple and requires you to set the scope to “all nodes” or “selected nodes“.
If you choose “selected nodes”, all nodes in the current flow will be shown in a list from where you can select the ones you want to monitor for errors.
An Example Showing the Catch Node Operating Within a Flow
Let’s create a simple flow to demonstrate how the catch node works. The flow will consist of an inject node, a function node that deliberately produces an error, and a catch node to capture that error.
Here are the nodes that make up this example:
- Inject Node: Use this node to start the flow. You can set it to inject a timestamp or any other simple payload.
throw new Error("This is a deliberate error");
- Catch Node: Add and configure a catch node to catch errors from the function node.
- Debug Node: Connect the catch node to a debug node. This will display the error message in the debug sidebar when the flow runs and an error is caught.
Connect these nodes sequentially: Inject Node -> Function Node -> Catch Node -> Debug Node. You can see the flow below, as well as the debug node output (I have expanded the JSON object):
After deploying this flow, click the inject node’s button to initiate the sequence. The function node will throw an error, and the catch node will capture this error. The debug node will then display the error message, showing exactly what went wrong. Your flows can have multiple “catch” nodes. It can have one “catch” node listening for errors across all nodes in the flow or multiple “catch” nodes, each listening for errors in specific nodes.
This example demonstrates the core functionality of the catch node simply and understandably. Mastering the catch node can make your flows more robust and easier to debug.
Serial communications with the Raspberry Pi Pico
As virtually all microcontrollers, the Pico and Pico W also provide UART (Universal Asynchronous Receiver-Transmitter) channels that can be used to provide connectivity with nearby devices. The Pico and Pico W have two UART channels.
A UART channel allows bi-directional asynchronous serial communication and is one of the most common (and oldest) communications technologies.
UART (Universal Asynchronous Receiver/Transmitter) is a hardware protocol for asynchronous serial communication between devices. UART can interact with other microcontrollers, sensors, or a Raspberry Pi Pico computer terminal. This segment aims to provide a fundamental understanding of using UART communications on a Raspberry Pi Pico with MicroPython.
I decided to include this chapter in this book because UART, despite its age, is a beneficial communications technology. While Wi-Fi and Bluetooth are available in almost all modern microcontrollers, good-old UART is often a better option for your project. This may be because Bluetooth is unavailable on the other device, a WiFi hotspot might be unreliable or inoperative, or it is overkill considering the resources they require compared to UART. Therefore, knowing how to use UART in your project is helpful in your toolbox.
If you want to implement the instructions and code below, you will need two Raspberry Pi Picos. One will be the transmitter, and the other will be the receiver.
Setting Up UART with machine.UART
The UART class from the machine module is used to set up UART communication. You’ll need to specify the UART channel, baud rate, and, optionally, the pins for transmitting (TX) and receiving (RX).
Here is an example:
from machine import UART, Pin # Initialize UART1 uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))
This code first imports the UART and Pin classes from the machine module. We initialise a UART object named uart1 on UART channel 1 with a baud rate of 9600 bits per second. The transmitting (TX) is set to GPIO pin four, and the receiving (RX) is set to GPIO pin 5.
Sending data with write()
The write() method is used to send data over UART. Here is an example:
# Sending a string "Hello" uart1.write("Hello")
The uart1.write(“Hello”) command sends the string “Hello” through UART1.
Receiving data with read()
The read() method reads a specified number of bytes from the UART buffer. Here is an example:
# Reading 5 bytes from the UART buffer received_data = uart1.read(5)
The command uart1.read(5) reads 5 bytes of data from UART1 and stores it in the variable received_data.
Full UART communication example
Let’s consolidate the example code into a single program that establishes serial communications between two Picos. Here is the complete program:
from machine import UART, Pin import time # Initialize UART1 uart1 = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5)) # Wait for a second time.sleep(1) # Send data uart1.write("Hello") # Wait to receive data time.sleep(1) # Read received data received_data = uart1.read(5) print("Received:", received_data)
You can upload this code to both Picos. You should customise the message that each one sends to the other. For example, Pico 1 should send the message “Hello from Pico 1” and Pico 2 the message “Hello from Pico 2”. Using jumper wires to connect the TX pin of Pico 1 to the RX pin of Pico 2 and the RX pin of Pico 1 to the TX pin of Pico 2.
This code, UART1, is initialised with a baud rate of 9600, TX pin as GPIO 4, and RX pin as GPIO 5. A 1-second delay is introduced to ensure everything is set up. Then, the string “Hello” is sent over UART1. Another 1-second delay is added to wait for the incoming data. And finally, 5 bytes are read from UART1, and the received data is printed.
Understanding UART communication on the Raspberry Pi Pico opens doors to many exciting projects, especially those that require communications between a microcontroller and devices that only support UART, such as GPS receivers.