⚡️Week 4: Electronics Production
Project Heros
An LED on a Raspberry Pi Pico being controlled by a button on the Xiao, all connected Via UART.
Group Assignment
Here is the group assignment for this week. This was a more "there was less value in the group assignment" week as both liam and I have extensive experience with them and have trawled through data sheets many times before. However it was a good opportunity to go a bit deeper and look at some of the features we otherwise wouldn't be searching for. This week I got caught up in the PIO instruction set for the RP2040 and actually wrote a test piece of code with it which Im very happy with because its an ability of the chip that I haven't much experience with. It was also a good opportunity to check out the features on the new Arduino R4. I have my quarrels with the board, but there are quite a few new features on it that are pretty cool like the dedicated LCD Controller, DAC and CanBus support.
Coding the Xiao
I have much experience in C++ and Python in a Microcontroller setting and have even done some minor work with assembly on an STM-32. After all of that experience I decided that for this assignment, my final project and fab academy I will be using MicroPython (and Python on the Pi 4). Both of the boards I am using have already have MicroPython installed from previous use, but I have previously made a video on how to do this with thonny (which is the IDE I will also be using).
With thonny open, if we open the correct com port that the xiao is using the bottom right we can run the code we have written in thonny. MicroPython is an enterpretted language meaning that we don't need to compile code and we can run code straight from our IDE via USB. This the test code from week 4 and we will be using it as a starting point.
Starting off we are going to change the button to use an external interrupt instead of polling it with a while true loop. When we check it with a .value() in the while true loop this is like checking your door over and over to see if your package has arrived - terribly inefficient. Instead we would wait for the delivery person to ring the door bell then we would do something - this is an external interrupt. In the following code we set up the xiao so that when the button is pressed, it will stop what ever it is doing and run the function "button_handler". And in here all we do is toggle the LED.
from machine import Pin
led = Pin(2, Pin.OUT)
button = Pin(4, Pin.IN, Pin.PULL_DOWN)
# define a function that we will call when the button is pressed
def button_handler(pin):
led.toggle()
# set up the interrupt on the button we made, assign the function to be called
button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)
Buttons aren't perfect and may press a few times when we press it, so lets add some debounce to it with ticks_ms. This is a MicroPython function we can call to get the time since the board has powered on. In the following code we add this button debounce using it.
from machine import Pin
import time
led = Pin(2, Pin.OUT)
button = Pin(4, Pin.IN, Pin.PULL_DOWN)
# this variable will keep track of the last time the button was pressed
last_button_press_time = 0
# define a function that we will call when the button is pressed
def button_handler(pin):
# tell mpython we want to use the global variable, not create a local one
global last_button_press_time
# get the current time
current_time = time.ticks_ms()
# only toggle the LED if 100 ms has passed since last toggle
if (time.ticks_ms()- last_button_press_time) > 100:
led.toggle()
last_button_press_time = current_time
# set up the interrupt on the button we made, assign the function to be called
button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)
Now lets get this to send a mesage over UART. To do this I am utilising some scaffolding that I have previously written in a video. We need to import UART, then set up the uart perhipheral, and then when the button is pressed, we will toggle the led as well as send the UART message "toggle". This should be all the code we need on the Xiao's side.
from machine import Pin, UART
import time
# intialise UART
uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
led = Pin(2, Pin.OUT)
button = Pin(4, Pin.IN, Pin.PULL_DOWN)
# this variable will keep track of the last time the button was pressed
last_button_press_time = 0
# define a function that we will call when the button is pressed
def button_handler(pin):
# tell mpython we want to use the global variable, not create a local one
global last_button_press_time
# get the current time
current_time = time.ticks_ms()
# only toggle the LED if 100 ms has passed since last toggle
if (time.ticks_ms()- last_button_press_time) > 100:
led.toggle()
uart.write("toggle")
last_button_press_time = current_time
# set up the interrupt on the button we made, assign the function to be called
button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)
Coding the Pico
On the Pico we are going to receive the Xiao's commands over UART an LED plugged into it. We will start by getting the LED going. You can open a second instance of thonny and connect to the Pico's com port to have 2 differnt boards being programmed and running at the same time. To start with this is the test code for the LED.
from machine import Pin
import time
led = Pin(16, Pin.OUT)
while True:
led.toggle()
time.sleep(1)
With it now succesfully flashing, lets add the recieving part of the UART code to get it to see that UART message. First we need to make the connections. For this we will connect the TX pin of the Xiao, to the Rx pin of the pico (and for formality, the Rx pin of the Xiao to the Tx pin of the pico). We connect TX to RX and NOT TX to TX because if one board transmits a message on its TX pin, it should be recieved on the RX pin of another. Also ensure that the 2 boards share a common gound (they have their ground pins connected). This is what this looks like.
Now we can add in the UART code which toggles the led connected to the Pico when it recieves the message "toggle" over UART. We will also print the recieved uart message to the shell for debuggin purposes.
from machine import UART, Pin
uart = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))
# set up onboard LED
led = Pin(16, Pin.OUT)
while True:
# Check if anything is available in buffer
if uart.any():
# recieve and store the message in a variable
message = uart.read().decode() # this .decode() removes the byte string format
print(message)
# control the led based on the string
if message == "toggle":
led.toggle()
And with that we now have it all working together.
Final code
Code for Xiao, TX is pin 0, RX is pin 1, LED in Pin 2 and Button in Pin 4.
from machine import Pin, UART
import time
# intialise UART
uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))
led = Pin(2, Pin.OUT)
button = Pin(4, Pin.IN, Pin.PULL_DOWN)
# this variable will keep track of the last time the button was pressed
last_button_press_time = 0
# define a function that we will call when the button is pressed
def button_handler(pin):
# tell mpython we want to use the global variable, not create a local one
global last_button_press_time
# get the current time
current_time = time.ticks_ms()
# only toggle the LED if 100 ms has passed since last toggle
if (time.ticks_ms()- last_button_press_time) > 100:
led.toggle()
uart.write("toggle")
last_button_press_time = current_time
# set up the interrupt on the button we made, assign the function to be called
button.irq(trigger=Pin.IRQ_RISING, handler=button_handler)
Code for Pico, LED and resistor plugged into Pin 16, RX is Pin 5 and TX is Pin 4.
from machine import UART, Pin
uart = UART(1, baudrate=9600, tx=Pin(4), rx=Pin(5))
# set up onboard LED
led = Pin(16, Pin.OUT)
while True:
# Check if anything is available in buffer
if uart.any():
# recieve and store the message in a variable
message = uart.read().decode() # this .decode() removes the byte string format
print(message)
# control the led based on the string
if message == "toggle":
led.toggle()