RP2040 MicroPython: Debugging

5 minute read

Where I describe various methods of debugging programs using MicroPython on the RP2040 (Pi Pico and Pi Pico W).

Introduction

Programming is a science, debugging is an art. When I chat with students learning to program, I’ve found it isn’t “programming” which causes the most problems, its “what to do when the programming doesn’t work.

On any new microcontroller, there is a new tool chain and a new process, which affects how you debug. For example, with the AVR family of microcontrollers, you have gdb, bloom as a hardware debugger (which is fantastic!), make and gcc. Or you may use the Arduino framework. Both approaches are well-defined, well-understood and work well.

Micropython is a bit more of a mystery. You may use typical Python tools such as Thonny to create the code and load the code, however, Thonny isn’t a hardware debugger, meaning it is unable to show variables in memory, set breakpoints and generally, dive into the internals of your program. MicroPython doesn’t lend itself well to line by line hardware debugging. Leaving you to determine the best method of solving “my program isn’t working.

In other words, MicroPython’s greatest strength, ease of code generation, can be a problem when attempting to debug your program. The abstraction which the language provides encourages rapid code development, hinders rapid code debugging. That said, the REPL can be your friend, much more so than anything in the C language and we’ll lean on it heavily. Let’s begin.

One last point…there is no one way to do it. There are multiple methods of determining why your program is failing and each problem might require one or more methods. I’ll describe several methods below, and I encourage you to use them as appropriate.

Remember blink? It was the first program many of us attempted and it simply blinked our built-in LED. That’s it. Blink or indicating where our program last worked can be a powerful tool. For example, I use a slow blink to indicate my Pico W is attempting to connect with the wireless network. The LED stays off if the Pico has succeeded, and it blinks at a faster rate, if it fails. This provides me a ready indicator of what is happening in the early stages of my programming.

I strongly recommend your incorporate something similar into your coding early on. For example, here is my code which I use:

from machine import Pin, Timer

# provide an indication as to why the connection failed
# Use built-in LED to show status
wireless = Pin("LED", Pin.OUT)

# simple timer function to blink builtin LED showing wireless status
# slow blink - attempting to connect
attempting = 4
# fast blink - connection failed
failure = 20
# off - connection success
def tick(timer):
    global wireless
    wireless.toggle()


# initiate a timer to indicate a success (or failure)
    blink = Timer()

# slow blink to indicate attempting to connect
    blink.init(freq=attempting, mode=Timer.PERIODIC, callback=tick)

# fast blink to indicate failure to connect
    blink.init(freq=failure, mode=Timer.PERIODIC, callback=tick)

This approach coupled with a reset button, provides me with my first indication something “has gone wrong”. Every I press reset I watch the LED. If it blinks slowly then goes off, I know I have connected to the network and may begin to debug the rest of my program. If it begins to blink fast, I have yet to connect and need to investigate startup issues.

Serial Monitor and REPL

NOTE: Serial Monitors

On macOS, I use Serial as it is outstanding. If you have a Mac and plan on doing a lot of Serial work, it is a must have. On Linux, moserial is decent and CoolTerm works on Windows, however, watch for malware masquerading as CoolTerm. Only download CoolTerm from the Roger Meier’s Freeware page, https://freeware.the-meiers.org. I provide the link visually so you get to the correct page!

One more recommendation is to use Python’s pyserial miniterm. Install via pypi then use it like this:

python3 -m serial.tools.miniterm /dev/cu.usbmodem14101 115200

REPL

The REPL is the classic method of debugging Python. If you are not sure what a command does, “try it out in the REPL”. This continues to be a great way to debug MicroPython. A simple sequence such as:

# connect to Pico via a serial program, CTRL-C to enter REPL
from machine import Pin
Pin("LED", Pin.OUT, value=1)

Is the fastest method of determining if you have a connection to your Pico, and it is working. (Just having a response from the REPL, also shows this, however, nothing like making the Pico do something to know for sure!)

In your serial program, hit the up arrow, change the “1” to a “0”, hit return and the LED is off. We’re good!

We still have that issue of the fast blink (or no blink) at startup, how do we solve that? In my programs, I’ll have everything be a function and the “main program” will be the following:

if __name__ == '__main__':
    web_server()

This allows me to do the following:

# connect to Pico via a serial program, CTRL-C to enter REPL
MicroPython v1.20.0 on 2023-04-26; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
>>> from main import *
>>> web_server()
Connection failed: LINK_BADAUTH
wireless connection failed

I clearly have an issue with my secrets.py file! The error message LINK_BADAUTH indicates I wasn’t able to connect due to “bad authorization credentials”. Yes, I can see, I dropped a character in my password. Change it, copy back over and we’re good to go!

Anther issue I have frequently, is I will add “.py” to a local import, which results in the following:

# connect to Pico via a serial program, CTRL-C to enter REPL
MicroPython v1.20.0 on 2023-04-26; Raspberry Pi Pico W with RP2040
Type "help()" for more information.
>>> from main import *
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "main.py", line 6, in <module>
ImportError: no module named 'pins.py'

Arrrrgggh! Delete the .py, save and copy then restart. Everything works!

The point of this section is to remember, if something isn’t working on startup:

  1. Connect via serial monitor
  2. Ctrl-C to engage REPL
  3. Start the program manually and look for errors

Comments powered by Talkyard.