Using the micro:bit to detect electrical current

This blog is an ongoing record of using the micro:bit to detect electrical current.

Idea

The micro:bit has a magnetometer on the board. When a current flows through a wire, a magnetic field is produced. We can use the magnetometer to detect this.

This is part of an energy conservation project for classroom use.

Summary

The magnetometer on the micro:bit is used to detect the change in magnetic field produced in the mains cable to a hair dryer when it is turned on and off.

(max-min) magnetometer data for 1000ms windows, sampling at 15ms, hair dryer turned on and off

I tried two methods. The first uses the unprocessed magnetometer data from the micro:bit. The second method looks at the deflection of the compass direction reported by the micro:bit. The compass direction is calculated by the micro:bit using internally processed magnetometer data.

Both methods gave positive indications of when a hairdryer drawing 3A is turned on and off.

Theory

The magenetic field strength around a wire varies with the current through the wire and the distance from the wire as:

B = μ0 I/(2π r)

Where B is in Tesla, A is in Amps and r is in metres. There are, as always with Physics, quite a few assumptions with deriving this equation.

For our purposes, it is ‘good enough’.

μ0 is the ‘permeability of free air’ and is 1.2566 x 10-6 m kg s-2 A-2.

If we take 2mm as the distance from the middle of the power cable to the sensor, r=2x10-3m.

This gives:

B = 9.9997 x 10-5 A ≈ 10-4 A

So, with 1A of current, we get 10-4 T of magnetic field strength at 2mm from the centre of the wire core.

So, 0.1mT per Ampere of current. An Ampere is quite a lot of current in today’s world.

Turning a device on and off can cause an inductive spike, so the momentary current can be higher than the steady state current through the load.

Calcs

The micro:bit v1 has separate 3-axis accelerometer and magnetometer sensors. The 3-axis magnetometer is the MAG3110.

The micro:bit v2 has an integrated accelerometer and magnetometer, each 3-axis, the LSM303AGR.

I will assume micro:bit v1 is in use. The data sheet does not go into details on how the magnetometer works. It could well use the Hall effect.

The sensitivity of the MAG3110 is stated as 0.10μT with a range of ± 1000μT.

The maximum sample rate of the sensor is 80Hz. This becomes important when we start using it to measure AC devices (mains powered), as the frequency of mains current is 50Hz where I live.

One other piece of information: ‘Noise down to 0.25 μT rms’. I think that this means that if we put the magnetometer in a shielded box, so that the Earth’s magnetic field is absent, then the minimum magnetic field that the sensor could reliably measure is 0.25μT rms.

The Earth’s magnetic field intensity on the surface varies from 25 to 65μT.

Working with the results from the Theory section:

0.1 mT per Ampere is the same as 100μT per Ampere. So far, so good. We should be able to detect this. With one Amp of current. Which is a lot of current. How about 100mA? This gives 10μT of magnetic field strength in addition to the background magnetic field strength.

We should be able to detect this.

How about 10 mA? Should still be good.

How hard could it be? What could go wrong? The answer to this is that we need to consider:

Alternating Current

In the Real World, we are probably trying to monitor energy consumption from devices that run from alternating current (AC), not direct current (DC). There will be an alternating magnetic field from the wires that transfer the AC to and from our devices. But... there are two wires current carrying wires connected to the device, one for current going in to the device (the live wire), one for current out (the neutral wire). There may also be a third wire, the earth wire, which does not carry current unless a fault occurs in the device. These wires are twisted together in the power cable.

The currents flow in opposite directions in the live and neutral wires. The magnetic fields that are produced will be equal and opposite, so will cancel out to some degree.

Geomagnetic field

The reason that the micro:bit has a magnetometer is to use it as a compass. The magnetometer measures the Earth's magnetic field. This field is pretty stable for a given location, but does vary geographically. You can find out what the field is at your location at this website. Where I live, the magnetic field strength is quoted as 49588.2nT, which is 49.5882μT. So we should always measure some magnetic field on the magnetometer, unless you somehow shield it from the geomagnetic field.

AC Testing

The AC current to a device drawing current varies sinusoidally with a 50Hz cycle rate (where I live). I am looking for variation in the magnetic field as the current builds and decays during each cycle. There should be a difference in the max-min magnetic field. Ideally, when the device is off and no current is drawn, there is no variation between the max and min magnetic field strength.

There are, of course, several Real World limitations.

Real World Limitations

Noisy data

In the Real World, the magnetometer sensor data is noisy. There is a variation in the magnetometer readings from one sample to another even when the device it is monitoring is switched off. This is a combination of internal noise in the electronics and external noise from the environment. You could look at this experiment as adding another source of external noise. In our case, the noise is the data we want to measure. One person's noise is another person's data.

Sensor sample rate limit

The magnetometer has a maximum sampling rate of 80Hz according to the data sheet. I don't know what the max sample rate is when using the programming platform provided for the micro:bit. It clearly can't be more than 80Hz though, as the sensor cannot transmit at a higher rate. So I need to get data over a number of 50Hz mains cycles to have any chance of picking up the max and min magnetic field strength. Ideally, we would sample at least 10 times the frequency of the data set that we are trying to characterise. Instead, we sample over a number of cycles and hope to catch values that approximate the max and min for each cycle.

micro:bit v1 limitations

During testing, I regularly ran out of memory on the micro:bit v1. I am using Micropython for development so that the code can be easily re-used and improved by the target end users of this project. It may well be that the Micropython implementation is creating the memory limitation. I may move to C if Micropython is too limiting.

Ideally, we choose the hardware to fit the project. In this case, I am mandated to use the micro:bit. Which is fair enough. The end product is to be lesson plans that use the micro:bit to teach about energy conservation.

AC Testing Setup

The maximum 80Hz sample rate for the magnetometer equates to a maximum sample interval of 12.5ms. I set the sample interval to 15ms. Nobody likes being pushed to their theoretical limit.

The results presented below are for a hair dryer, which has a current of about 3A when on, according to my 'Plug-in Power & Energy Monitor'.

Please find the elegant and sophisticated experimental mounting arrangement presented below. The power cable is fixed over the top of the magnetometer, which is labelled as 'COMPASS' on the micro:bit.

micro:bit attached to the power cord of a hair dryer

I collected samples for the x, y and z magnetometer axis every 15ms for 1000ms time windows. I tried a longer window and had a memory allocation error. Then I calculate the max-min for each of the three windows. This max-min is called the 'delta'. This is plotted automagically using the mu editor. I am trying to stick to tools that are readily available for the class room. If I don't have success, then I'll log data to a file and hammer at it with data analysis software.

The power to the hair dryer is turned on and off using the power switch on a socket switch. Please find a photograph of this below, which also shows the 'Plug-in Power & Energy Monitor' used to measure the current. I use the switch on the socket strip to turn the power on and off to the hair dryer to avoid disturbing the power cable position. Moving the power cable causes noise on the magnetometer data.

Plug-in Power & Energy Monitor used to measure the current going to the hair dryer

Results

Please find a graph from the Plotter tool in Mu below. This graph automatically scales. I added the labels. This shows the max-min (delta) values for each of the x, y, z axis. The time window is 1000ms and the sensor sample rate is 15ms. So there are about 66 samples in each time window.

(max-min) magnetometer data for 1000ms windows, sampling at 15ms, hair dryer turned on and off

We can clearly see that the delta for two of the axis spike when the dryer is turned on and off, then remain higher while the dryer is on. The spikes are due to induction, which causes the current to spike high when the appliance is turned on or off. This leads to protection circuitry needing to be added to appliances to protect from damage from these spikes. But I digress.

Why do we see this marked effect on only two axis? My hypothesis is that as the magnetic field is circular around the wire, the magnetometer axis that is parallel to the wire will not see as much variation as the other two that are at right angles to the wire.

Direct current (DC) Testing

With DC, the magnetic field strength should not vary as the current does not fluctuate. So I intend to measure the absolute magnetic field strength and look for an increase when there is a DC current flowing through the wire to the device under test (DUT).

Setup

I used a USB powerbank connected to a mobile phone. I am not sure how much current it draws as my fancy USB power monitor is at the University and I am working from home due to the ongoing pandemic. I'll add this data when I have it. I attached the micro:bit to the USB cable in much the same manner as with the AC power cord. Sophisticated, advanced rubber band technology. I may have to patent this tech. The current along the cable will vary as the mobile phone battery charges up. This variation changes over a time scale of tens of seconds, compared with the 20ms cycle rate from AC 50Hz mains.

Results

I can measure a 20% increase in the average total magnetic field strength when the phone is connected, compared to when the phone is disconnected.

Historical method - compass deflection

After completing the above experiments, I read that in the early 1800s, current was measured by looking at the amount that a magnetic compass was deflected near to the wire carrying the current. The article about this is here.

The micro:bit uses the magnetometer to get compass readings. So I tested seeing if there is a deflection to the compass reading of the micro:bit when the hairdryer is turned on or off, using the same setup as in the previous experiment, with the hairdryer drawing 3A.

I used the makecode editor to quickly lash up a program to poll the compass. I trid to poll the compass at 4Hz, but I think it maxes out at about 1Hz. The reading is plotted using the Mu editor. Please see the results below. The higher, stable line, is the compass bearing when the hairdryer is off, the lower noisy readings are when it is on.

Clearly, the magnetic field from the wire deflects the compass reading. I now have a digital version of the galvanoscope, a mere 200 years after the invention of the original.

Compass reading from a micro:bit attached to a hairdryer cable as the hairdryer is turned on and off

Each time I reflash the micro:bit with code that uses the compass, the compass demands to be recalibrated. This means that the board has to be twisted around until all the LEDs fill on the screen. It takes maybe 20 seconds to do this. This slows down trying out new ideas with the compass.

Where's the code

I will put it up on a GitHub site.

Anticipated 'why didn't you...'

I tried to stick to tools that are easily available for classroom use, such as Micropython and the Mu editor.

I would like to put a space between numbers and units, as is recommended by journals. I couldn't figure out how to stop the units splitting from the numbers across lines in my markdown editor. I've looked for 'markdown non breaking spaces'. So far none of the suggested fixes work for me. Life is short.

micro:bit edge connector expansion board retainer clip

The problem

I use PCB expansion boards to plug my micro:bit into for a recent project. The micro:bit has connection pins printed along the edge of the board. This is called an 'edge connector'. Have a look here if you are not familiar with the micro:bit board. This edge connector plugs into an edge connector socket on the expansion board. The two components can be easily separated.

As I give the finished devices to other people to use, I need a way to stop the micro:bit from being easily removed from the expansion board. The micro:bit connector is not keyed, so it could be replaced upside down. Then Terrible Things will Happen.

The solution

I designed and 3D printed a clip that hooks over the back of the expansion board's edge connector socket and plugs into the 4mm holes near the micro:bit's edge connector. This clip prevents the micro:bit from being easily removed from the expansion board.

Please find a screenshot of the widget, taken from the 3D design software OpenSCAD used to design it, below.

micro:bit edge connector retainer, OpenSCAD screenshot.

The photo below shows the clip attached over the top of an expansion board edge connector socket, connecting the micro:bit board to it. You can see three of the five 4mm holes that are next to the micro:bit's edge connector half exposed, sticking out of the black edge connector socket of the expansion board. The 3D printed clip slots into the two outer 4mm holes.

Front view of micro:bit edge connector retainer.

A side view of the retainer is shown below:

Side view of micro:bit edge connector retainer.

The retainer shown in the photographs was printed at the 'low quality' setting of my printer as I am still refining the design. The expansion board that the micro:bit is plugged into shown in the photographs is the Sparkfun micro:bit breakout board.

Background

For my flex sensor project I need to add some functionality to the BBC micro:bit. To achieve this, I plug the micro:bit into a small expansion board. The expansion board is designed to allow a few components to be soldered onto it which connect with the micro:bit through the edge connector socket on the expansion board.

Design

I use OpenSCAD, which allows the design to be entered through programming. There are many 3D CAD packages available. I use OpenSCAD as the method of programming in a design goes down my 'brain-hole' more easily than the different methods of design entry used by the other packages I have tried.

Use the design software that suits you

3D printing

I used my Creality CR10 mini printer which I first wrote about here to 3D print the clips. On the 'low quality' setting the print takes about 10 minutes. This print time is a little misleading though. You need to allow for the print bed and nozzle to warm up. Then it is best to leave the finished print to cool down so it comes off the print bed easily. Hacking away with the flat bladed paint remover tool to remove a still-warm print from the print bed risks damaging the print and marking the print bed.

How you can replicate this product

I put the OpenSCAD design file for the retainer in the project GitHub repository here.

The end

The retainer clip will be incorporated as part of the casing for the device. Currently, I have a 3D printed case over the expansion board and a silicone cover over the micro:bit. I will add the retainer clip to the case that goes over the expansion board.

Comments and improvement suggestions are welcome.

making assistive technology devices

Making assistive technology

This post presents a few of the lessons that I have learned from building assistive technology projects over the last few years. This is so that others may avoid some of the mistakes I made. Comments are welcome.

Summary

Work back from what you want the end-user's experience to be and choose the technology accordingly.

Define what success looks like

The currency of success in academic is publication. This brings closure to a project and passes on some of the lessons learned.

Building something to be used in the Real World, outside of academia, is a different level of Pain. Instead of a prototype fit for doing a few user studies, we need a fully working and dependable device which is safe to leave with the end users. We need to supply documentation and the other resources needed to get the device working and to maintain it. You don't receive much credit for this in academia.

Are you cherry-picking the technology?

Have a look at the Gartner Hype Cycle:

Gartner Hype Cycle

In academia, we work at the 'Peak of Inflated Expectations' with the latest ideas. I came across the idea of using the Gartner Hype Cycle as a model of academic research in this paper. The paper goes on to talk about how getting an idea into the real world is regarded as Somebody Else's Problem in academia.

Getting to the 'Plateau of Productivity' - the real world - entails crossing the 'Trough of Disillusionment'. I call this the 'Valley of Despair'.

For example, I tried to use a camera based game controller, the Microsoft Kinect as assistive technology by recognising hand motion. I tested my idea with several wheel chair users. The device would not build a skeleton model of somebody in a wheel chair due to the occlusion caused by communication devices mounted on the front of the wheel chairs. It didn't matter how good or how bad my code was, the underlying firmware/software was not able to cope with my user group.

Similarly, I tried to use a hand tracking device, the Leap Motion to identify hand motion. This would not reliably track the motion characteristics of the user group I was working with.

Academic papers flooded the Computer Human Interaction field based on user interactions with the Microsoft Kinect when this device first came out. It sat on the Peak of Inflated Expectations. I couldn't get to the Plateau of Productivity with the technology.

Instead of using tried and tested technologies, I went for the latest and greatest new technologies. Often it takes a few years for the limitations of a new technology to become widely known and the bugs in the supplied supporting software to be identified and fixed.

Ultimately I succeeded with the project by using simpler technology: handshake

Make a presentable device

I was advised at the start of my attempts to create assistive technology that nobody wants to wear an ugly kludge. Everybody wants to use something that looks good. This idea is expanded in the book Design Meets Disability.

I found an off-the-shelf arm band holder some of my micro:bit based projects. I did make one myself, but the store bought model looks better.

I made a real kludge of an ugly prototype for a flex-sensor based project. It was ugly! So I spent some happy hours with a 3D printer and openSCAD to make a case to put it all in. Far from perfect, but a lot better than it started out.

Test early, test ugly

This is in direct conflict with the previous paragraph! We need to identify as quickly as practical if an idea is worth pursuing. Spend time testing prototypes with the end user. It can be difficult to get time with the end users. It can be difficult to venture out of the comfortable bubble of the labs many of us work in. However, it is the only way to know if we are progressing.

I would have saved time with the Kinect based project if I had tested earlier. But I wanted to showcase a wonderfully interactive user experience I built using the latest and greatest Microsoft toolkit.

So how do we square these two contradictory requirements? If we know that the prototype is just that - only for testing - and we are sure that we will retain posession, then we can lean towards the ugly end of the spectrum. Especially if we are not testing with the intended end-users. I had one prototype taken away by a test subject as she was so pleased with the idea! Luckily, in this case, the device was a single micro:bit and I had put it in an off-the-shelf case for testing, so I was happy to see it used. This was for this project.

Documentation

Naively, I thought that if anybody showed an interest in my projects, I would then blaze into a frenzy of writing and produce detailed instructions to enable them to implement the project. Wrong. Nobody is going to beat a path to your door on the faint promise of something that looks good on a video or your website. We need detailed 'how-to' guides, preferably including videos to enable somebody who does not have a background with technology to painlessly implement our ideas. Documentation and videos take a lot of time to produce.

I use mkdocs for creating documentation and host the output on my GitHub site.

The software packages I use for creating videos are:

kdenlive for editing video.

audacity for recording and editing audio.

Many projects fail

A project definitely fails if you don't start it though. Knowing when to keep on plugging away and when to give up is a skill that I am still learning. This is where having good collaborators makes all the difference. Knowing 'when to hold and when to fold'.

micro:bit v1 with yotta – removing build warnings

problem

Running yotta build with the micro:bit v1 C tools produces screenfulls of depreceation warnings, such as:

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

This makes it hard to find the error messages with the reason why the build failed.

solution

Tell the C++ compiler not to include depreceation warnings.

instructions

Add the compiler flag -Wno-deprecated to the CMAKE_CXX_FLAGS_INIT options in the file toolchain.cmake.

File location:

<project>/yotta_targets/bbc-microbit-classic-gcc/CMake/toolchain.cmake>/yotta_targets/bbc-microbit-classic-gcc/CMake/toolchain.cmake

Line before:

set(CMAKE_CXX_FLAGS_INIT           "${CMAKE_CXX_FLAGS_INIT} ${_CPU_COMPILATION_OPTIONS} -std=c++11 -fwrapv")

Line after adding flag:

set(CMAKE_CXX_FLAGS_INIT           "${CMAKE_CXX_FLAGS_INIT} ${_CPU_COMPILATION_OPTIONS} -std=c++11 -fwrapv -Wno-deprecated")

extra flags to suppress other distracting warning

While we're on a roll, we can also suppress distracting warnings for array-bounds and misleading indentation by adding:

-Wno-array-bounds -Wno-misleading-indentation

to the same line.

endnote

There are layers of build tools used to transform your C code into a hex file that can be copied onto the micro:bit. I tried to understand and explain the toolchain in these blogs:

c-toolchain-make-ninja-cmake-explained

c-toolchain-explained-part-2-yotta-file-locations

Getting started with C++ on the micro:bit v2 in Linux

Aims

This post is about getting started with programming the micro:bit v2 using C++. I use the example code from the Lancaster University GitHub for this.

We will:

  • Download the GitHub repository with the sample code.
  • Build and load the default HelloWorld example to the micro:bit, which scrolls 'HELLO WORLD!' on the LEDs.
  • Modify and build one of the other examples to use one of the micro:bit pins as an analog input. The LEDs display the voltage on this pin.

I tried to load one of the MICROBIT.hex files onto a v1 of the micro:bit with no success. This post is aimed at v2 of the micro:bit only.

Download the samples

Download the GitHub repository with the sample files produced by Lancaster University from their GitHub site here.

There are a couple of ways to do this. You can click on the green 'Code' button to download a zip file and unzip it, or you can use the 'git clone' command if you have git installed:
git clone https://github.com/lancaster-university/microbit-v2-samples

Setting up for coding

The README.md file in the repository is pretty good. There are several packages that need to be installed on your Linux installation to be able to build the example code with. The maintainers of the repository use Ubuntu, so the package installation commands use the 'apt' package manager:

sudo apt install gcc
sudo apt install git
sudo apt install cmake
sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi

gcc is the Gnu C compiler. This is used to compile the example C++ code with.

cmake is used for creating files that tell the system how to build the project.

gcc-arm-none-eabi is a version of gcc designed to run with the target hardware we are using on the micro:bit.

binutils-arm-none-eabi contains tools that build the .hex file that goes onto the micro:bit - the assembler and linker amongst other things.

If you have a different Linux distro, you may have a different command to install these packages with. I use Debian with the 'aptitude' package manager.

On the GitHub site and in the README.md file, there is some guidance on how to use a Dockerfile for installing the dependencies. I didn't use this, so can't comment.

You also need Python 3 installed, but odds on you already have this set up.

Directory structure layout

Like all GitHub repositories, the file layout seems really obvious to whoever created it, but might not be as clear to the rest of us.

In the top level directory, we have a file called build.py. This is the file that builds (clue is in the name) the MICROBIT.hex file that we will copy to our micro:bit. We will come back to this file in a moment.

In the source directory you find the file main.cpp. This is the file that we will work with. The samples directory contains - you guessed it! - the sample files that we will work with.

Hello World

The main.cpp file that comes with the GitHub repository is all set up with the classic Hello World example. All we have to do to get this running on our micro:bit is:

  • Build the code to create a MICROBIT.hex file.
  • Copy the MICROBIT.hex file to an attached micro:bit v2.

Building the code

Go to the top directory in the repository and type the command:

python build.py

After some seconds and a wall of text, the MICROBIT.hex file is created in the same directory as our build.py file.

This is the file that needs to be copied to our micro:bit.

Copying the MICROBIT.hex file to the micro:bit

One simple way to deploy the newly created MICROBIT.hex file to the micro:bit is to use a file browser, such as Nautilus. Connect the micro:bit to your PC and it should appear as a drive called MICROBIT. Double click on this to connect the micro:bit to your PC. Then drag and drop the MICROBIT.hex file onto this folder. The LED on the back of the micro:bit should flash for a few seconds and then the code will run. You will see 'HELLO WORLD' scroll across the LEDs on the front of the micro:bit.

There are slicker ways to transfer the hex file across though. Each time that the micro:bit has new code flashed to it, the connection to the PC is lost. This means that you need to double click on the MICROBIT drive each time you want to load a new hex file to connect the board, then drag over the newly created hex file. Wouldn't it be nice to automate this, so that each time you create a new hex file, it is automagically loaded to the micro:bit?

Automating loading hex files to the micro:bit

I wrote a blog post about a script I created to find and mount a micro:bit after it is connected to my laptop's USB port. Please find this post here. I aliased the script needed to find and mount an attached micro:bit in my .bashrc file as 'mm'. In LInux, we can use the 'inotifywait' command to watch the MICROBIT.hex file we create when we build the C++ project for an update. When the hex file is updated, we can tell the system to mount the attached micro:bit and write the new hex file to the micro:bit. Here's the command that I use:

while inotifywait -e close_write MICROBIT.hex ; do mm && cp ./MICROBIT.hex /media/myusername/MICROBIT ; done

You need to change 'myusername' to your user-name. You could, of course, put this into your .bashrc file with a suitable alias.

Analog input test example

The next test I did was to edit one of the sample files supplied in the 'source/samples' directory to display the voltage applied to pin 1 of the micro:bit. I am looking to interface a sensor with the micro:bit, so this is the first step in the project.

If we look in the samples directory, there are a plethora of example files. The one that we will work with is called GPIOTest.cpp. If you look through the example files, you may notice something. None of them have a main function. The main function is needed for a c++ file to execute - this is the first function that runs. Why is this?

When we run python build.py from the root directory of the repository, it looks for a main function throughout the source directory. If there is more than one, the build crashes. I found this out the hard way. So we need to add a main function to our example file. Here's what I did:

Move the sample files to a new directory under the root directory of the repository.
mv ./source/samples samples_all

I moved the file ./source/main.cpp to the samples_all directory and renamed it HelloWorld.cpp
mv ./source/main.cpp ./samples_all/HelloWorld.cpp

Then I copied and renamed the GPIOTest.cpp file to the source directory:
cp ./samples_all/GPIOTest.cpp ./source/main.cpp

Now we need to add a main function to the main.cpp file that runs the relevant analog testing function in the file. Looking through the file listing, there is a function called 'analog_test()' starting at line 61 which looks a good bet. Line 69 is:

int px = analogPins[0]->getAnalogValue() / 40;

Looks like analogPins[0] is the pin that is set up to read an analog input. So which pin is analogPins[0]?

Line 16 is:

static Pin *analogPins[] = {&uBit.io.P1};

So analogPins[0] is uBit.io.P1, which looks a lot like pin 1 on the edge connector. Worth a shot. I dug through the firmware files on the relevant repository to confirm this.

Add the following few lines to the end of your new main.cpp file:

int main() {
    uBit.init();
    analog_test();
}

This runs the analog_test() function.

Now we have to build the project to generate a new MICROBIT.hex file. Go to the root directory and type python build.py.

We get an error!

This is because line 2 of the file is:

#include "Tests.h"

We need to copy this file back from our samples_all directory to the source directory, alongside our main.cpp file. The other #include ... files are found in the libraries folder in the repository.

After a wall of text, we should have a new MICROBIT.hex file, ready to load on to the micro:bit. After copying this file over, the LED on the back of the micro:bit will flash for a few seconds, then we should see about half of the LEDs on. This shows that the analog input is 'floating' at about half of the voltage of the power supply.

So, how do we test the analog input? We can test the 0 and maximum input by connecting the ground pin to pin 1. I used a pair of tweezers to do this. Probably more sensible to use some alligator clips connected by a wire. With the ground connected to pin1, no LEDs are on. With the 3V connected to pin 1, all of the LEDs are on. So far so good.

I then tested the input using a 1/2Hz 3V sinusoidal input signal.I have an Analog Discovery 2 board made by Digilent. I programmed this to create the sinusoid and also to power the board. I connected this to the micro:bit using a Kitronik edge connector. If you are powering the micro:bit through the edge connector pins, be sure to only apply 3V. The micro:bit is only designed to take 5V through the USB connector. The little board starts to get quite hot quite quickly if you inadvertantly put 5V into the 3V pin on the edge connector!

To see a short YouTube video showing testing the analog input with the sinusoidal input please click on the picture below:

Setting up a micropython editor with the BBC micro:bit v2 on Linux

This post follows on from my blog post that shows how to set up the mu-editor in Linux for v1 of the micro:bit. This blog post is here:

https://mattoppenheim.com/2021/01/05/setting-up-mu-editor-with-the-bbc-microbit-on-linux/

I couldn't get the mu-editor to work with micro:bit v2. This was frustrating as I wanted to check some existing code ran on v2 of the micro:bit as well as start to use some of the new feautures on board.

I solved this by following a tip from a comment on my first post. This recommended using a different editor and following some setup instructions for that editor.

I will call this editor the PythonEditor as this is the name of the project's GitHub site:

https://github.com/bbcmicrobit/PythonEditor

The PythonEditor can be used online at:

https://python.microbit.org/v/2

Python Editor can also run offline. Initially, the editor would not recognise any micro:bits that were attached to my laptop. The information that I needed to follow to get PythonEditor working is here:

https://support.microbit.org/support/solutions/articles/19000105428-webusb-troubleshooting

In case this site is removed, I replicate this information below and add one or two tips of my own.

I use <user> to represent whatever user name you are using. I use Debian 10 on a Lenovo X260 and X230.

Create a udev rule to get the micro:bit to be recognised

Create the file:

/etc/udev/rules.d/50-microbit.rules

with this content:

SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", MODE="0664", GROUP="plugdev"

To create this file you need to be root or use the sudo command. I created and edited the file using vim:

sudo vim /etc/udev/rules.d/50-microbit.rules

Add <user> to plugdev group

To add your username to the plugdev group:

sudo usermod -aG plugdev <user>

To have this change recognised by the system, we need to restart the udev rules:

sudo udevadm control --reload-rules 

The webpage I followed to get this far says to log out and back in. I did not do this and things worked out.

Using PythonEditor online.

The online PythonEditor only works in Chrome for me. The editor now connected with micro:bit v1 and v2 and would program them. The first time that I flashed code to the micro:bit v2 took a while, with a message saying that the initial flash could take a while, but subsequent flashes would be faster. This is the case. I suspected that the first flash copied a micropython hex file to the micro:bit.

Using PythonEditor offline

The PythonEditor source code can be downloaded from GitHub and run offline. The instructions on how to do this are on the GitHub site, linked at the start of this post. This is how I run the PythonEditor. If we look in our downloaded GitHub repository, in the folder:

PythonEditor/micropython

we find two .hex files:

microbit-micropython-v1.hex 
microbit-micropython-v2.hex

I suspected that copying the v2.hex file to the micro:bit v2 was necessary before it would work with either of the editors.

So I took my spare micro:bit v2, connected it and tried to look at it with the serial connection on the PythonEditor. Nothing - a blank screen. I dragged and dropped the microbit_micropython-v2.hex file onto it using the Nautilus file explorer. After the requisite 10 seconds or so of flashing on the micro:bit as it loaded the hex, I tried again with the serial connection on PythonEditor and saw this:

PythonEditor serial connection with micro:bit v2

I see something similar when I click the REPL button on the mu-editor. I also get a REPL when I use the cu command I talked about in my earlier blog:

cu -l /dev/ttyACM0 -s 115200

So copying over the .hex file is a necessary step. PythonEditor does this for us if we haven't done it already.

So now I have some tools to program micropython on the micro:bit v2.

mu-editor with micro:bit v2

I can get this working with v2 of the micro:bit after getting PythonEditor up and running. But it is not stable for me. Sometimes it works, sometimes it doesn't. If I can figure out a reliable way of making this run, I'll edit this post.

There is some cryptic information in this post, which says that the UART buffer needs to be cleared to get the mu-editor to work.

https://github.com/mu-editor/mu/issues/1162

I like the mu-editor and hope that I can get it up and running. Right now, I am relying on a single tool to program the micro:bit v2.

Summary of how to get micro:bit v2 working with a micropython editor

  • Create the udev rule.
  • Add <user> to plugdev group.
  • Add .hex file to the micro:bit

To do

I use the uflash or pyboard scripts with v1 of the micro:bit to enable me to use e.g. vscode to edit micropython and have a script automagically load updated code to the micro:bit. So far I have not gotten anything like this set up for the micro:bit v2. I would like this. I've gotten used to the idea that setting up programming environments requires tenacity.

Setting up mu-editor with the BBC micro:bit v1 on Linux

I recently set up an installation of Debian 10 and set up a tool chain for programming the BBC micro:bit with micropython. I'd forgotten some of the stumbling blocks I had the last time I did this, so am recording them here. Hopefully, this post will help you avoid them.

I use <user> to represent the user name that I log in with.

The instructions in this blog get you set up to work with the micro:bit v1 using mu-editor.

Part 2 shows how to set up a different editor that works with both v1 and v2 of the micro:bit under Debian. The link for part 2 is at the end of this post. I tried and failed to get mu-editor to work reliably with the micro:bit v2.

Install mu-editor

Use your package manager! In my case:

sudo aptitude install mu-editor

Using the package manager installs a bushel of python3 libraries, which you may not like. I tried the clean method of setting up a virtual environment and downloading the mu repository from GitHub. I ended up trying to install various dependencies that have other dependencies... In the end I gave up and used the package manager. One line. Installed.

Many people use apt-get instead of aptitude. I think both commands use the same libraries under the hood.

Finding the micro:bit

Which port does the micro:bit mount under? In my case /dev/ttyACM0. One way to find the port is with this command:

sudo dmesg | grep tty
[  135.410510] cdc_acm 1-6:1.1: ttyACM0: USB ACM device

The last line of the output gives the game away. /dev/ttyACM0 it is.

Setup permissions

We need to add our user name to the dialout group in /etc/group

When I first setup Debain and then tried to connect to a micro:bit on port /dev/ttyACM0 using Debian 10, I could only do this as root. Why?

ls -al /dev/ttyACM0

displays:

crw-rw-rw- 1 root dialout 166, 0 Jan 5 16:13 /dev/ttyACM0

This indicates why we can access the port using sudo - the port is owned by root. It also shows why adding the non-root user to the dialout group allows access without being root. I added my user account to the dialout group in /etc/group using:

sudo usermod -a -G dialout <user>

Enabling the micro:bit to mount under /media/<user>

I still could not connect with the micro:bit using a serial port monitor without being root.

The micro:bit is mounted under /media/<user>/MICROBIT.

ls -al /media/<user>
drwxr-x---+  2 root  root  4096 Jan  5 12:44 <user>

This shows that the directory that the micro:bit is mounted on is owned by root. I needed to change the owner and group of this directory to <user>.

sudo chowner /media/<user>
sudo chgrp /media/<user>

Now I can mount the micro:bit under /media/<user>/MICROBIT. I found I had to get this set up for mu-editor to allow me to program the micro:bit and use the REPL.

I wrote a blog post a few years ago presenting a bash script to find and mount or dismount a micro:bit here.

Terminal monitors

I came across the cu terminal monitor. To install and use with the micro:bit:

sudo aptitude install cu
cu -l /dev/ttyACM0 -s 115200

Hit ctrl-c and you get a REPL. The way to exit it is using the cryptic characters:

~.

The tilda character '~' stands for <escape> in this instance.

The nice people at micropython produced a script called pyboard.py which finds a connected micro: bit and opens a terminal to display the output from the micro:bit. This tool is detailed at: http://docs.micropython.org/en/latest/reference/pyboard.py.html.

gtkterm is another useful serial terminal monitor which runs in its own GUI. Install using:

sudo aptitude install gtkterm

micro:bit v2 issues

The above all works for micro:bit v1. When I tried to flash some code to the micro:bit v2 using the mu-editor, I get this pop-up:

I can now run the latest mu-editor from the mu GitHub repository at: https://github.com/mu-editor/mu. However, I still got the same error when trying to use it with a micro:bit v2.

To run the latest version, download the GitHub repository, then type:

python run.py

in the downloaded mu directory. I guess that installing mu-editor using aptitude also installed all of the necessary dependencies to enable the GitHub version to run. The GitHub version is 1.1.0.alpha.3. The version installed using aptitude is 1.0.2.

I tried programming the micro:bit v2 using pyboard.py. This also failed.

After posting this blog with these issues, Mark left the comment at the end of this post that helped me get a different editor working with v2 of the micro:bit. The comment came the same day that I posted the blog! I was well chuffed as I didn't realise that anybody reads my blog posts, let alone the same day as I posted them. I guess a lot of people are searching for how to get micro:bit v2 running right now.

So I wrote a new post detailing how to setup and run a micropython editor with v2 of the micro:bit:

https://mattoppenheim.com/2021/01/07/setting-up-a-micropython-editor-with-the-bbc-microbit-v2-on-linux/

micro:bit c toolchain explained, part 2. yotta, file locations

December 2020: I wrote this post when only v1 of the micro:bit existed. I haven't yet checked to see if the contents are still valid for the toolchain used to program v2 of the micro:bit.

This article follows on from part 1 of my attempt to explain the chain of tools used to build your C code into something that can run on the BBC micro:bit. In this part, I will cover the yotta build tool and how the make build tool is used as the top level component.

Here's the final tool chain. Admire the beauty.

Offline tool chain for building an executable from C code for the micro:bit

So what is yotta and why do companies choose names like 'yotta' and 'ninja' for their products when this will make it hard for us to find them on the web?

yotta

Yotta is another build tool. Yotta takes the build file module.json and the configuration files target.json and config.json. Yotta produces the build file for cmake called CMakeLists.txt. If you read part 1 of this explanation of the micro:bit C tool chain, you will be getting used to the idea that one build tool creates the build file for the build tool underneath it. It took me a while to get my head around this. The reason for using these layers of build tools is that there are many other files than the ones that we write to pull into the project to enable our C code to be built to run on the micro:bit hardware.

In your yotta examples directory, which I represent with <yotta_examples> below, have a look in the file <yotta_examples>/yotta_targets/bbc-microbit-classic-gcc/target.json. Almost at the end of the target.json file, you will see where yotta tool is configured to produce a build file for CMake:

"toolchain": "CMake/toolchain.cmake",
  "scripts": {
    "debug": [
      "valinor",
      "--target",
      "NRF51822",
      "$program"

Here ends the description of build tools used to create the hex file that goes onto your micro:bit from the source C code.

make

This section is a bit of an extension/digression. The 'make' tool is not used in the programming stack for micro:bit. I use it to automate building and loading a hex file to a micro:bit.

Make is the Grandad of build tools. I often use this tool for building C projects outside of micro:bit programming. ninja may be faster for large C projects, but make is fast enough for my projects and has proven reliability.

The build file for make tool is called makefile, or sometimes Makefile. Linux does not distinguish between the two names, but Windows will. Typing 'make' at the command line will cause the contents of makefile to be processed by the make tool. You will be used to the idea of a build file being processed by a build tool by now.

I use make to automate building the binary and loading it onto my micro:bit. Here's a listing of a typical makefile for one of my projects:

build_microbit:
	yt build
	# script to mount a microbit
	~/data/infolab2/progs/scripts/microbit_mount.sh mount
	cp ./build/bbc-microbit-classic-gcc/source/microbit-c-combined.hex /media/bill/MICROBIT/

yt build obviously builds the project. The line after the comment checks to see if the micro:bit is mounted and if not, mount it. The final line copies the newly generated hex file to the mount point for the micro:bit, which is specified in the script microbit_mount.sh.

I use a script to mount the micro:bit, as this is unmounted after flashing the new code. I use Linux; this may not be necessary with Windows. Details on the script I use can be found on my blog post. The micro:bit is mounted at /media/bill/MICROBIT. You need to change the mount point in the makefile to the location that you choose to mount your micro:bit.

Accessing Smartbox Grid 3 using Python and win32gui

Summary

Smartbox's Grid 3 communication software creates two windows containing the words 'Grid 3' in their titles, even though you can only see one. If you are trying to interact with this software using your own program, you need to make sure to access the window that you intend to.

Problem

I wrote some Python code to detect the use of Grid 3 or Tobii's Communicator software for this project, to visually show when somebody who uses eyegaze technology interacts with the software.

This post concentrates on the issue I had with finding the correct window that Grid 3 runs in. Grid 3 runs under Windows.

I use the pywin32 library to access the win32gui library. This library allows me to find which window is running the software that I want to monitor. However, after using this library to find the 'grid 3' window, my code kept on telling me that nothing was changing in the window, when I could clearly see something was. To make matters more confusing, the code seemed to run fine on one machine and not another.

Solution

Please find the the parts of the Python script needed to explain my solution below. All of the script is on my GitHub site.

import logging
import win32gui

logging.basicConfig(
    format='%(asctime)s.%(msecs)03d %(message)s',
    level=logging.INFO,
    datefmt='%H:%M:%S')

COM_SOFTWARE = ['grid', 'communicator']
IGNORE = ['grid 3.exe', 'users']

def find_window_handle(com_software=COM_SOFTWARE, ignore=IGNORE):
    ''' Find the window for communication software. '''
    toplist, winlist = [], []

    def _enum_cb(window_handle, results):
        winlist.append((window_handle, win32gui.GetWindowText(window_handle)))

    win32gui.EnumWindows(_enum_cb, toplist)
    for sware in com_software:
        # winlist is a list of tuples (window_id, window title)
        logging.debug('items in ignore: {}'.format([item.lower() for item in ignore]))
        for window_handle, title in winlist:
            #logging.debug('window_handle: {}, title: {}'.format(window_handle, title))
            if sware in title.lower() and not any (x in title.lower() for x in ignore):
                logging.info('found title: {}'.format(title))
                return window_handle
    logging.info('no communications software found for {}'.format(com_software))
    time.sleep(0.5)

The critical debugging line is the commented out line 24:

logging.debug('window_handle: {}, title: {}'.format(window_handle, title))

When uncommented, and running the logging in debug mode, this listed out two windows that contained 'Grid 3' as part of their title, even though only a single Grid 3 window was visible. Even with just the 'Users' screen up, before launching a grid communication window, the logging.debug line returned two windows containing the name 'Grid 3' in their title:

grid: [(66532, 'GDI+ Window (Grid 3.exe)'), (197532, 'Grid 3 - Users')]

When running one of the Grids (for testing I used the Super Core grid), the software still tells me there are two windows with 'grid' in the title:

grid: [(66532, 'GDI+ Window (Grid 3.exe)'), (263256, 'Grid 3 - Super Core - .CORE')]

For this example, I could take the second window found and be done. However, to be robust, I created an IGNORE list, containing strings that are in the window titles that I do not want to use.

In the code example above, line 25 looks for the correct string to be in the window title and also checks that none of the strings in the IGNORE list are in the title:

if sware in title.lower() and not any (x in title.lower() for x in ignore):

This only passes the title for the window that I am interested in - the one containing the communication grid.

Testing

I use a Windows 10 virtual machine running in VirtualBox, with Debian Linux as the host. I also test on a separate Windows 10 only PC. I use a virtual machine for Windows for development as I run Linux on my laptop. The virtual machine allows me to create a static and controlled testing environment with only the software that I am working on in it. I double test on a stand alone Windows 10 machine in case the virtual environment somehow effects the software.

In this case, my script seemed to run well on one system and not another. I now suspect that sometimes the window that I was interested in was the only one generated by Grid 3 and at other times, the extra spurious Grid 3 window was generated as well. This spurious window was then selected by the software.

Using a microbit to control a servo motor

Subtitle: Making things move a microbit at a time

In this post I explain how I set up and operated a small servo motor that runs directly from the same 2xAAA battery pack that powers my microbit. I then explain how to operate a servo motor that requires a 5V power supply from the same microbit, using a transistor as a voltage level converter for the control signal.

Introduction

I set up a small servo motor for a project to make an automated guinea pig feeder. Details on how to use this are here. Naturally, mistakes were made. I will detail the hardware setup, explain the control signals for the servo and show where the software can be found.

Hardware: SG51R servo motor

I used a Tower Pro SG51R servo motor, which runs from the same 2xAAA battery pack as the microbit. The battery pack, microbit and servo motor can be seen in the photo below. I slotted the microbit into a Kitronik edge connector.

2xAAA battery pack, microbit in a Kitronik edge connector and a SG51R servo motor

I got my SG51R servo motor from the Pi Hut here.

Why use this servo motor?

The SG51R works using a 2xAAA battery pack as the power supply, while many other servo motors require a higher voltage than 2xAAA batteries produce. This means it can run direct from the same battery pack used to power your microbit.

The manufacturer's website says that this servo motor requires 4.8V to operate. However, it works fine with the 2.4V or so that we get from the 2xAAA rechargeable battery supply I use to power the microbit. This is great, as it means I don't need to use a different voltage supply to power the servo motors with.

The servo has three wires for power and control. The black wire is for ground, the red wire for the voltage supply and the orange wire is the control signal.

The wires from the Kitronik edge connector to the servo motor connector can be seen in the photo below. I used D0 as the pin to output the servo motor control pulses on. The labels on the microbit are guides as how to use the microbit for the guinea pig feeder project.

The photo below shows how the wires from the microbit connect to the servo motor plug. Black is ground, red is positive voltage, yellow is the signal, from pin D0 on the microbit.

Wires from the microbit on the left, servo motor connector on the right

A servo typically responds to a control signal of a pulse between 1 ms and 2 ms long within a 20ms period. 180° needs a 1 ms pulse.

So what does that look like? The software I wrote to drive the servo motor is at the end of this article. I put an oscilloscope onto the signal pin. Here's what it looks like:

Servo motor controller pulses

We see a positive pulse every 20 ms. Remember that these pulses go from ground to the battery voltage (about 2.4 V in this plot). We will revisit this polarity when we talk about driving a 5 V servo motor later in this post. The width of this pulse sets the angle of the servo motor. The pulse must occur within the same 20 ms window, just how much of this window it takes up changes.

From the data sheet for the SG51R:

This servo is pulse controlled. This means that supplying a pulse will move it to a particular angle. In this case 0° is a 1.5 ms pulse, 90° a 2 ms pulse and -90° is a 1ms pulse. This may not be set by your controller by default or give you the full range of movement as such you may wish to try up to 2.25 ms and as low as 0.75ms. Be aware that if you go to far you can break the servo.

Using my 'scope, I could see that the servo motor responds to pulses with a width from 0.57 ms to 2.58 ms. Remember that the window that these pulses occur in remains as 20 ms wide. Just the width of the control pulse changes to take up more or less of this 20 ms.

Software

Initially I carefully crafted beautifully structured micropython code in classes. Then ran out of memory, so ripped out all of the classes and bludgeoned the code to fit into the memory of the microbit.

I did, of course, find example software elsewhere on the 'net to get started and apologise that I can't remember which site I found this on to give proper credit.

The code can be found in the file called feeder.py on my GitHub site at: https://github.com/hardwaremonkey/microbit_projects

5V servo motor MG90D

Maybe you need a more powerful servo motor for your application. In this case, you will probably need one that requires a higher voltage than we get from the battery pack for the microbit. This section explains how to control a servo motor that is running from a separate power supply using a battery-powered microbit.

The mg90d is a servo motor that requires a higher voltage than the microbit can supply to operate. I ran it at 5V from a power supply. So how do we set up our microbit to control this?

I used a 2n2222 transistor to interface the microbit with the servo motor control wire. The control signal from the microbit goes to the base of the transistor. There is a 1K Ohm current limiting resistor in between this signal from the microbit and the base of the transistor. The signal going to the base of the transistor switches the transistor on and off.

The collector of the transistor connects to the 5V power supply through a 100 Ohm resistor.

Remember: We are only using the transistor to change the voltage level of the control signal. The voltage for the servo motor comes directly from the +5V power supply.

When the control signal from the microbit goes high, the transistor turns on. When the control signal goes low, the transistor turns off.

Find the wiring diagram below. The images come from Fritzing, though I made the diagram in Inkscape.

Circuit layout showing how to power and control a 5V servo using a microbit and a 2N222 npn transistor

There is one catch with this circuit - the control signals become inverted. You might ask - why not tap the signal from the emitter of the transistor, not the collector, then the signal would not be inverted. However, doing this produces an output signal going to the servo motor of lower voltage than the signal coming into the base of the transistor. Which defeats the object. There is about a 0.6V drop between the base and emitter of a typical bipolar transistor.

The simple answer is to invert the control signal coming from the microbit. I did this. See the oscilloscope grab below, showing the inverted control signal from the microbit in yellow and the non-inverted control signal going to the servo motor in blue. Note the different voltage levels. The signal from the microbit is around 2.4 V in amplitude and the signal going to the servo is about 5 V in amplitude.

Inverted controller signal from microbit is yellow, signal from transistor emitter to servo is blue

I put the micropython code needed to generate both a non-inverted and an inverted pulse below so you can see the simple trick needed to invert the pulse.

   def set_ms_pulse(self, ms_on):
        # pin.write_analog(1023) is constant on pulse
        # (1023 * ms_on / 20) gives a pulse of length ms_on
        self.pin.write_analog(1023 * ms_on / 20)

    def set_ms_pulse_invert(self, ms_on):
        ''' Gives inverted logic servo control pulse. '''
        self.pin.write_analog(1023 - 1023 * (ms_on / 20))

This worked to operate the 5V servo motor. The transistor allows us to drive the servo motor using the output of a battery powered microbit.

Another component that allows us to drive a device requiring a voltage that is not compatible the part that generates the control signal is an opto isolator. This is a story for another day.

As always, comments are welcome.