Zombie BBC Micro:bit serial ports created when using pyocd-gdbserver –persist

So I was happily using pyocd-gdbserver to program and enter debugging mode on a BBC Micro:bit attached to one of my laptop's USB port, as described here. Then I stopped being able to read data through the USB port... Long story short, multiple 'zombie' ports were created and my Python script was connecting to a zombie instead of the live one.

setserial -g /dev/ttyACM*


/dev/ttyACM0, UART: unknown, Port: 0x0000, IRQ: 0, Flags: low_latency
/dev/ttyACM1, UART: unknown, Port: 0x0000, IRQ: 0, Flags: low_latency

Sometimes for fun, I would also see a ttyACM2. Why would two ports have the same Port number? The answer is they don't. They are the same port. Connecting to /dev/ttyACM1 got me nothing. Connecting to /dev/ttyACM0 got me connected to the BBC Micro:bit. I had set the pyocd-gdb utility running using:

sudo ~/.local/bin/pyocd-gdbserver -t nrf51 -bh -r --persist

I think that the --persist flag does the damage. Run the script without this and I think we are good to go. I altered my serial port script to flag up when more than one Micro:bit is found. For good measure, I sort the ports into reverse order and connect to the first one with the PID and VID for the Micro:bit, which will be the lowest numbered ttyACM port. This is a work around when zombies appear.

Please find my Python 3 serial_port.py script for finding and returning a serial port connection to a BBC Micro:bit below.

import logging
import serial
import serial.tools.list_ports as list_ports
from time import sleep

BAUD = 115200

logging.basicConfig(level=logging.DEBUG, format='%(message)s')

class SerialPort():
    def __init__(self, pid=PID_MICROBIT, vid=VID_MICROBIT, baud=BAUD, timeout=TIMEOUT):
        self.serial_port = self.open_serial_port(pid, vid, baud, timeout)

    def count_same_ports(self, ports, pid, vid):
        ''' Count how many ports with pid and vid are in <ports>. '''
        return len([p for p in ports if p.pid==pid and p.vid==vid])

    def get_serial_data(self, serial_port):
        ''' get serial port data '''
        inWaiting = serial_port.inWaiting()
        read_bytes = serial_port.readline(inWaiting)
        if not read_bytes:
        return read_bytes.decode()

    def get_serial_port(self):
        ''' Return the serial port. '''
        return self.serial_port

    def open_serial_port(self, pid=PID_MICROBIT, vid=VID_MICROBIT, baud=BAUD, timeout=TIMEOUT):
        ''' open a serial connection '''
        print('looking for attached microbit on a serial port')
        # serial = find_comport(pid, vid, baud)
        serial_port = serial.Serial(timeout=timeout)
        serial_port.baudrate = baud
        ports = list(list_ports.comports())
        print('scanning ports')
        num_mb = self.count_same_ports(ports, pid, vid)
        logging.info('{} microbits found'.format(num_mb))
        if num_mb>1:
            logging.info('**** check for false connections ****')
        for p in ports:
            print('pid: {} vid: {}'.format(p.pid, p.vid))
            if (p.pid == pid) and (p.vid == vid):
                print('found target device pid: {} vid: {} port: {}'.format(
                    p.pid, p.vid, p.device))
                serial_port.port = str(p.device)
        if not serial:
            print('no serial port found')
            return None
            print('opened serial port: {}'.format(serial))
        # except (AttributeError, SerialException) as e:
        except Exception as e:
            print('cannot open serial port: {}'.format(e))
            return None
        # 100ms delay
        return serial_port

if __name__ == '__main__':
    print('instatiating SerialPort()')
    serial_port = SerialPort()

Sublime Text 3, adding a custom python 3 build

Typing 'python' at the command line of my Linux Mint 18 install gives me a python 2.7 prompt. So when I run a python script in Sublime Text, it was built using Python 2.7. But I want to use python 3! So I entered a custom python 3 build.

I use Linux Mint 18. The "shell_cmd" mentioned below will be different for Windows and maybe for Mac OS as well.

To create a build option in Sublime Text 3 for your favorite version of Python, create a file called:


Where sublime_install is the path to the directory where you have sublime installed.

The file should contain this text:

    "shell_cmd": "/usr/bin/env python3 -u ${file}",
    "selector": "source.python",
    "file_regex": "^(...*?):([0-9]*):?([0-9]*)",
    "working_dir": "${file_path}",

You may need to change 'python3' to whichever command prompt fires up the version of python you want to run.

The option 'Python3' will now appear in your build menu on Sublime Text 3.

The -u option in the "shell_cmd" removes buffering. I missed this out initially, leading to some head scratching.  My scripts would run, but I wouldn't see any output for some time - until the output buffer had filled. Luckily  Stackoverflow came to my help: https://stackoverflow.com/questions/50296736/how-to-remove-output-buffering-when-running-python-in-sublime-text-3

Python 3, threading and references

Creating a thread

I used threading to enable real-time graphing of data from sensors. One thread collected data from the sensors. The main thread ran the real time graph. I had a few problems getting started. It came down to my incorrect use of brackets when creating the thread.

When we create a thread using the threading library, we need to pass the target to the thread without using brackets. e.g.

thread = threading.Thread(target=ThreadTest)


thread = threading.Thread(target=ThreadTest())

Otherwise the target is created in the main thread, which is what we are trying to avoid. Without the brackets, we pass a reference to the target. With the brackets, we have already created the object. I think that this is analogous to passing a pointer in C, but stand to be corrected.


In test1.py I call ThreadTest without using brackets. test_thread starts in the thread and allows test1.py to continue running.

In test2.py, I pass ThreadTest() as the target. In this case the thread does not allow test2.py to continue running.


import threading
from thread_test import ThreadTest

thread = threading.Thread(target=ThreadTest)
print('not blocked')


import threading
from thread_test import ThreadTest

thread = threading.Thread(target=ThreadTest())
print('not blocked')


from time import sleep

class ThreadTest():
    def __init__(self):
        print('thread_test started')
        while True:

output from test1.py:

thread_test started
not blocked

output from test2.py:

thread_test started

Installing the Eclipse Embedded Systems Register View plugin

I had some 'issues' getting the Eclipse Embedded Systems Register View plugin to install in Eclipse Oxygen under Linux Mint 18. When I tried to install the plugin from the Eclipse Marketplace, I repeatedly got an error:

Artifact not found...

Thanks to a Sourceforge issue post here I got the plugin installed. A solution to the problem is towards the bottom of this post. However, I needed to modify the solution slightly. So I'm putting up my work around here.
We need to download the plugin then tell Eclipse where the download is.

Downloading the plugin

We download the plugin using the 'wget' command

I had to slightly modify the script given in the Sourceforge post to get this to work. Please find the code below. Make yourself a directory to install the plugin to. Create an executable script with the code below and after you are happy that this is not a nefarious attempt at my making your system into my zombie, run it.


wget http://embsysregview.sourceforge.net/update/site.xml
mkdir -p features plugins
cd ./features

wget http://downloads.sourceforge.net/embsysregview/org.eclipse.cdt.embsysregview_feature_0.2.6.jar
wget http://downloads.sourceforge.net/embsysregview/org.eclipse.cdt.embsysregview.data_feature_0.2.6.r191.jar
cd ../plugins

wget http://downloads.sourceforge.net/embsysregview/org.eclipse.cdt.embsysregview_0.2.6.jar
wget http://downloads.sourceforge.net/embsysregview/org.eclipse.cdt.embsysregview.data_0.2.6.r191.jar

Your directory should now have two subdirectories called 'plugins' and 'features' each containing two .jar files. There will be a file called site.xml in the parent directory that you created.

Installing the plugin

In Eclipse, go to the 'Help' menu. Click on 'Install New Software'. Next to the 'Work with' window click on the button 'Add'. Next to the window called 'Name' click on the button called 'Local...' and navigate to the directory where you downloaded the plugin. I called this directory 'embsysregviewer. Please find a screenshot of the menus below.

Now you need to give the repository a name in the 'Add Repository' window. I called it 'embsysviewer local'. The name does not matter, so long as it makes sense to you.

Now you should see the Available Software screen fill with goodness:

Select the emsysregview plugin and click on Next. to complete the installation, you need to accept the licence and that you are installing 'unsigned content'. Eclipse will ask to be restarted. You should now have the Embedded Systems Register View plugin installed.

Getting this to work sunk an hour or so, but it gave me an overview of how to download and install a plugin should I encounter a similar issue in the future.

Eclipse, yotta, C/C++ and the BBC Micro:bit

With the help of the excellent instructions at the link below I set up Eclipse with yotta to compile C code for the BBC micro:bit under Linux:
I get the debugger window to come up, but have not yet used this feature in anger.
The writer, achary, clearly knows more about Eclipse and embedded programming than I do. I got a little stuck at a couple of stages so created this page to pass on my solutions.

The offline C compiler for the BBC micro:bit is developed at Lancaster University. Installing the yotta compiler and downloading example files is explained here.

The rest of this article assumes you followed the instructions on this installation guide and have cloned the microbit-samples directory. I assume that you have Eclipse installed, either the C/C++ installation or you have installed the C/C++ development environment.

Installing yotta

Instructions for installing yotta can be found in the yotta documentation here. Note that yotta is designed for Python2.7 only. I used pip to install yotta to my user directory, using the --user flag. Then I started getting errors:

'module' object has no attribute 'X509_up_ref'

I faffed around upgrading my cryptography library as mentioned in the yotta documentation. Long story short, the recently installed 'yotta' and 'yt' commands in ~/.local/bin/yt and ~/.local/bin/yotta both referred to python 3. I probably did something sometime to cause this. To fix the error I changed ~/.local/bin/yt and ~/.local/bin/yotta from:


import yotta



import yotta

Get yotta to work in debug mode

Using yotta with the --debug-build flag allows for easier debugging. By default, the code is compiled in an optimised mode which makes debugging harder. I need all of the help that I can get, so would like to use this flag. The command to run yotta with the --debug-build flag  is:

yotta build --debug-build

However, this will throw an error and the last line of the build will give the error:

ninja: build stopped: subcommand failed.

I am not sure what a ninja is doing in my system. If I could see him, I would probably already be dead.

To fix this error, the '-fomit-frame' flag needs adding at two places in the file yotta_targets/mbed-gcc/CMake/Platform/mbedOS-GNU-C.cmake in your microbit-samples directory.

The two changes to mbedOS-GNU-C.cmake are:

line 21 from:
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -gdwarf-3")
set(CMAKE_C_FLAGS_DEBUG_INIT "-g -gdwarf-3 -fomit-frame-pointer")
line 28 from:
set(CMAKE_ASM_FLAGS_DEBUG_INIT "-g -gdwarf-3")
set(CMAKE_ASM_FLAGS_DEBUG_INIT "-g -gdwarf-3 -fomit-frame-pointer")

Then remove the build directory in the microbit-samples directory and rebuild using:

yotta build --debug-build

If you don't remove the old build directory directory, then the command will still fail. I know this.

Install pyOCD

We use the pyOCD tool to help debug and program the microbit. Details of this tool are on its github page.

'pyOCD is an Open Source python 2.7 based library for programming and debugging ARM Cortex-M microcontrollers using CMSIS-DAP. Linux, OSX and Windows are supported.'

Note the 'python 2.7' bit. Initially I pip installed it, which defaulted to a python 3 install. The install worked, but when I came to try running pyocd, I got a  bunch of assertion errors. So I read the instructions...

To ensure that pyocd is installed using python2.7, I used:

pip2 install --pre -U --user pyocd

--pre # 'Include pre-release and development versions. By default, pip only finds stable versions.' from https://pip.pypa.io/en/stable/reference/pip_install/#install-pre
-U # same as --upgrade 'Upgrade all specified packages to the newest available version.'
--user # libraries go to the user directory. In linux, this removes the need for sudo to install, which is a security issue.

Plugin your microbit.

Now we fire up a gdbserver using the newly installed pyocd. What is a gdbserver? This wikipedia page explains that 'gdbserver is a computer program that makes it possible to remotely debug other programs.'

sudo ~/.local/bin/pyocd-gdbserver -t nrf51 -bh -r

-t # target (nrf51 is the chipset used on the microbit).
-bh # replace software breakpoints with hardware breakpoints.
-r # halt the target when reset.

I get this output:

INFO:root:DAP SWD MODE initialised
INFO:root:ROM table #0 @ 0xf0000000 cidr=b105100d pidr=2007c4001
INFO:root:[0]&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;e00ff000: cidr=b105100d, pidr=4000bb471, class=1&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
INFO:root:ROM table #1 @ 0xe00ff000 cidr=b105100d pidr=4000bb471
INFO:root:[0]&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;e000e000:SCS-M0+ cidr=b105e00d, pidr=4000bb008, class=14&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
INFO:root:[1]&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;e0001000:DWT-M0+ cidr=b105e00d, pidr=4000bb00a, class=14&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
INFO:root:[2]&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;e0002000:BPU cidr=b105e00d, pidr=4000bb00b, class=14&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
INFO:root:[1]&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;f0002000: cidr=b105900d, pidr=4000bb9a3, class=9, devtype=13, devid=0&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;
INFO:root:CPU core is Cortex-M0
INFO:root:4 hardware breakpoints, 0 literal comparators
INFO:root:2 hardware watchpoints
INFO:root:Telnet: server started on port 4444
INFO:root:GDB server started at port:3333

There is a way to set up a file in udev to remove the need to use sudo to run pyocd. Please see this link on how to do this.

The guide I read recommended using the --persist flag with the pyocd-gdbserver command. I found my serial port communication with the micro:bit stopped working. Instead of there being a single serial port connection to the micro:bit, there were several. I suspect the --persist flag kept 'zombie' connections alive, causing my code to connect to a dead connection that only existed in the OS' imagination.

--persist # keep GDB server running even after remote has detached.

As awac explains, this shows that we have access to 4 hardware breakpoints. The server is started at port 3333. This port will be entered into the Eclipse debugger setup, which is explained below.

Set up Eclipse

http://flames-of-code.netlify.com/blog/microbit-cpp-3/ covers setting up a C/C++ Eclipse project with the microbit-samples code downloaded from the Lancaster University github. I am using Eclipse Oxygen at the time of writing this post.

A new project is set up by using File, New, Makefile Project with Existing Code.

Alter the default build command from 'make' to 'yotta build --debug-build'. Get to this by right clicking on the microbit-samples project and selecting 'Properties'.

Use control-B to build the code. I get a bunch of depreciation warnings that can be ignored.

dynamic exception specifications are deprecated in C++11 [-Wdeprecated]

Configuring the Eclipse C/C++ debugger for use with yotta

The pyOCD site mentions that the plugin 'Eclipse Embedded Systems Register View' should be installed. This took me a little while to figure out how to install, so I created a separate post on how to do this here. This plugin is not yet of use for the microbit. I hope to get CMSIS-SVD configuration files from Nordic for the microbit's microcontroller to be able to make use of the plugin.

I came a little unstuck when setting up the debug session as I could not find the 'GDB Hardware Debugging' option when editing my debug configuration. To get that option we need to install the GNU MCU Eclipse plugin from the Eclipse Marketplace. Go to the Help menu and click on 'Eclipse Marketplace'. Put 'gnu mcu' as the search term to find the plugin. This plugin adds the 'GDB Hardware Debugging' option to your run configurations which we use when setting up Eclipse.

This allows me set up for the GDB debugger as shown in the screen shots below. Go to Run, Debug configurations.

Double-clicking on the heading 'GDB Hardware Debugging' and set up the Main, Debugger and Startup tabs as shown below. The C/C++ application is found in the 'build' subdirectory, in the microbit-samples directory at:


The Debugger tab specifies the path to the GDB :


Enter port '3333', which we noted earlier when starting the gdb server.

In the Startup tab, click on 'Load image' and 'Use File'. Enter


Initially I had some errors:

Reset command not defined for device 'Generic TCP/IP'

Looking at this Stackoverflow question, I fixed this by also unchecking Reset & Delay and Halt options in the debugger configuration:

Running the Eclipse debugger

Run the command 'yt clean' from the command line in your microbit-samples directory to clean out the last build. Then 'control-b' in Eclipse to create a fresh build. I feel I should be able to click on the little bug icon on the menu bar to get to the debugger, but this gives me a 'launch failed. Binary not found.' window. So I right click on the project and select 'Debug As','Debug Configurations', 'microbit-samples Default' then click on the 'Debug' button on the bottom right. I get offered the chance to go to the debug screen, which is a result.