Collect and graph micro:bit data on a Raspberry Pi

Using a BBC micro:bit, an old Raspberry Pi and a surprisingly small amount of code, you can log sensor data in a simple CSV (comma separated values) text file and plot it on an interactive graph you can access from any device on your local network.

Of course, you can connect sensors direct to a Raspberry Pi, but using a micro:bit means it’s easy to code and it gives you access to all its built-in sensors: temperature, light, 3 dimensional accelerometer and magnetometer and sound on the micro:bit V2. Even if you don’t have access to an external temperature sensor, as I explain in the video above, the micro:bit’s on-board temperature sensor is still useful for measuring temperature changes over time.

The micro:bit also has a beautifully simple radio protocol, so you could collect data from micro:bits in other rooms and outside, collate the data on a micro:bit connected to a Raspberry Pi just using a USB cable, and plot it all on a single graph or dashboard.

Here’s how it works. I used a simple MakeCode program on the micro:bit to send sensor data in a string as serial data over USB to the Raspberry Pi. The two values are separated by a colon:

MakeCode blocks

Over on the Raspberry Pi, a Python program polls the USB port once a second. When it gets some data, it splits the numbers and logs them with a short timestamp to a CSV file.

An HTML file uses the Plotly JavaScript library to read the CSV file and turn it into an interactive graph:

Screenshot of temperature graph

The graph is then served up on a web page on your local network using a simple built-in Python web server.

You can test out a sample graph here: http://suppertime.co.uk/microbit/data/

And you can get all the code you need and more detailed build instructions over on my Github: https://github.com/blogmywiki/microbit-pi-data

All you need is a BBC micro:bit and an old Raspberry Pi!

Update – long term use

After around 4 days’ use, logging data every 10 minutes, this system failed. I think the SD card got corrupted. So, for long term use you’d need to find another solution. Perhaps use a RAM disk and send the CSV file somewhere by FTP. Or use external USB storage, although that’s not an option with this set-up with a very old Pi as both USB ports are in use, one for Wi-Fi and one for talking to the micro:bit. It would be possible to use serial communication between the micro:bit and the Pi, for example, but I like the simplicity of USB.

Also when trying to fix this, I could not get my USB Wi-Fi dongles working with Raspberry Pi OS Bullseye Lite. I had previously been using Buster.

micro:bit data logging feature

If don’t have a Raspberry Pi to hand, but you do have a micro:bit V2, you can use it on its own to log data that stays on the micro:bit when the power is disconnected, and which you can view direct from the micro:bit in an interactive graph and table. Find out more here: https://microbit.org/get-started/user-guide/data-logging/. You can also view a sample set of data collected on a micro:bit here – it’s the data set I used comparing readings from the micro:bit’s onboard temperature sensor with a more granular external temperature probe – click on ‘visual preview’ to see the interactive graph. It’s easy to copy and paste data into a spreadsheet and export the data as a CSV file.

micro:bit and Raspberry Pi

Posted in microbit, Raspberry Pi | Tagged , , , , | Leave a comment

Classic 68k Mac watch face

photo of the Mac watch face on a watch

Back in November 2021, I got a BangleJS2 hackable smart watch, having backed it on Kickstarter. It’s no Apple Watch, for sure, but it’s much cheaper and you can write your own apps for it really rather easily, in JavaScript. I particularly like the always-on display which is readable in direct light, especially in dark mode, and also has a backlight. It’s built on top of an inexpensive generic smart watch, the Shenzhen Smart Care Technology SMA Q3. You may think the BangleJS2 has a bit of hefty mark-up, but you’re getting a watch modified with new firmware making it easy to program, plus a community of support and a huge range of apps to download.

I said back in November that I wanted to get Susan Kare’s classic 1984 Macintosh font Chicago working on it. Well, I’ve now made a watch face inspired not just by the font, but by her whole design for the original Macintosh Finder desktop. I’m grateful to @peerdavid for helping me improve the code so that the time displayed was more accurate, widgets still run in the background and getting it to work when your watch is in dark mode, which normally looks much better.

Let me know if you have ideas for how to improve it. My ideas include showing seconds when you unlock the watch, some way of including widgets, text dates, and maybe showing some other data when you tap the window.

If you have a BangleJS2, you can download the app here. And if you haven’t got one of the watches yet, you can still run the clock in the emulator and have it on your desktop.

You can also buy a BangleJS right here: https://shop.espruino.com/banglejs2

Posted in computers | Tagged , , , | Leave a comment

A tiny screen for a Raspberry Pi

I have a few old Raspberry Pis lying around, including a Model B Rev 1 running Raspberry Pi OS Buster lite. I use it to serve up my simple MUD game. It has no wi-fi so it’s connected by ethernet to my router which also gives it power over USB. I really must write that up some time.

Anyway, I have a whole bunch of cheap, tiny 1306 i2c OLED 128×64 pixel displays so I thought it might be fun to see if I could use one as a little shell display so I could do some basic admin on the headless Pi locally, just with a USB keyboard.

It took way longer than I expected, especially given I found someone had already done this, but it works. Here’s how I did it. Please bear in mind… my Pi is very old, I’m using Buster Lite as my OS, my OLED display’s address is 0x3C, it’s set to auto login to a command prompt, no GUI – your mileage may well vary!

It’s based on this project: https://github.com/satoshinm/oledterm – however, that was written in Python 2, uses a newer Pi, uses an spi not i2c interface, and because the OLED display libraries now only work in Python 3, I couldn’t get it to work. Issue #4 on that repo was the key to solving this, but I thought I’d summarise how I got this to work.

First, I connected the display. GND on the display to GND on the Pi, VCC to +3.3v on the Pi, SDA to Raspberry Pi pin 3, SCL to Pi pin 5 – remember this is an old Raspberry Pi original model B!

I installed git and downloaded oledterm:
git clone https://github.com/satoshinm/oledterm

This wouldn’t run for various reasons – not least because I needed to install luma.core to drive the OLED display, and I needed to install pip to install that:
sudo apt install python3-pip
sudo -H pip3 install --upgrade luma.oled

Then I copied the Python 3 version of oledterm from here and saved it as a file called oledterm3.py

I then edited /etc/rc.local to add this:
sudo python3 /home/pi/oledterm/oledterm3.py --display ssd1306 --interface i2c --i2c-port 0 &
exit 0

I also edited go.sh the same way. Let me explain the options in more detail. My display type is set to ssd1306, this is a very common kind of small OLED display. If my display’s i2c address were not 0x3c, I’d have needed to add an option to change that here. I specify the interface as i2c, rather than SPI as used in oledterm, and because I have a very old Pi I need to specify the i2c port as 0. With a newer Pi you could probably omit –i2c-port, or set it to 1.

I then unplugged the HDMI display, and rebooted – and lo! I could just about see a tiny shell and use my USB keyboard to type instructions! I could even edit text in nano – just about! Who needs more than 31 columns and 9 rows of text, anyway!?

If you like this, you may also like my adventures with using OLED displays in Arduino-based TinyBASIC computers, a micro:bit pulse oximeter, air quality sensor, or playing ArduBoy games on a BBC micro:bit.

Python 3 version of oledterm by Krizzel87

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# based on:
# Copyright (c) 2014-17 Richard Hull and contributors
# See LICENSE.rst for details.
# PYTHON_ARGCOMPLETE_OK

import os
import time
import sys
import subprocess
from luma.core import cmdline
from luma.core.virtual import terminal
from PIL import ImageFont

VIRTUAL_TERMINAL_DEVICE = "/dev/vcsa"
ROWS = 9
COLS = 31

# based on demo_opts.py
from luma.core import cmdline, error
def get_device(actual_args=None):
    """
    Create device from command-line arguments and return it.
    """
    if actual_args is None:
        actual_args = sys.argv[1:]
    parser = cmdline.create_parser(description='luma.examples arguments')
    args = parser.parse_args(actual_args)

    if args.config:
        # load config from file
        config = cmdline.load_config(args.config)
        args = parser.parse_args(config + actual_args)

    # create device
    try:
        device = cmdline.create_device(args)
    except error.Error as e:
        parser.error(e)

    #print(display_settings(args))

    return device

# based on luma.examples terminal
def make_font(name, size):
    font_path = os.path.abspath(os.path.join(
        os.path.dirname(__file__), 'fonts', name))
    return ImageFont.truetype(font_path, size)

def main():
    if not os.access(VIRTUAL_TERMINAL_DEVICE, os.R_OK):
       print(("Unable to access %s, try running as root?" % (VIRTUAL_TERMINAL_DEVICE,)))
       raise SystemExit

    fontname = "tiny.ttf"
    size = 6

    font = make_font(fontname, size) if fontname else None
    term = terminal(device, font, animate=False)

    term.clear()
    for i in range(0, ROWS):
        term.puts(str(i) * COLS)
    term.flush()
    #time.sleep(1)

    while True:
        # Get terminal text; despite man page, `screendump` differs from reading vcs dev
        #data = file(VIRTUAL_TERMINAL_DEVICE).read()
        data = subprocess.check_output(["screendump"])
	#print [data]
        # Clear, but don't flush to avoid flashing
        #term.clear()
        term._cx, term._cy = (0, 0)
        #term._canvas.rectangle(term._device.bounding_box, fill=term.bgcolor)
        term._canvas.rectangle(term._device.bounding_box, fill="black")

        # puts() flushes on newline(), so reimplement it ourselves
        #term.puts(data)

        for char in data:
            if '\r' in chr(char):
                term.carriage_return()
            elif chr(10) in chr(char):
                #term.newline()
                # no scroll, no flush
                term.carriage_return()
                x = 0
                term._cy += term._ch
            elif '\b' in chr(char):
                term.backspace()
                x =- 1
            elif '\t' in chr(char):
                term.tab()
            else:
                term.putch(chr(char))

        term.flush()
        time.sleep(0.01)
        #print "refresh"
        #print data

if __name__ == "__main__":
    os.system("stty --file=/dev/console rows %d" % (ROWS,))
    os.system("stty --file=/dev/console cols %d" % (COLS,))
    try:
        device = get_device()
        main()
    except KeyboardInterrupt:
        pass
Posted in computers, Raspberry Pi, Raspbian | Tagged , , , | Leave a comment

Streaming internet radio on FM

An FM radio showing RDS radio station name 'fip'Years ago I remember thinking how amazing it was that the Raspberry Pi could be turned into an FM radio transmitter with nothing more than a short piece of wire and some code. Back then you could only transmit audio files in a very particular format and it very much seemed like a novelty.

Fast forward to 2022 and it seems things have moved on a bit. Reading a blog post about reviving old radio pagers, I finally caught up with rptix. This suite allows you to turn an old Raspberry Pi into a little radio transmitter that can broadcast in a stunning range of formats and frequencies: FM, AM and SSB radio, slow-scan TV, pager data and many, many more.

Now you probably won’t be doing this as it’s probably illegal to broadcast on most frequencies where you live, and if you find a legal frequency you’d need to add some filtering before attaching any kind of physical antenna, or indeed a wire, to pin 4. The only other things you’d need are an old Raspberry Pi, an internet connection and an old FM radio (with RDS if you have one).

Years ago in Brighton, I’m told, someone used to illegally rebroadcast my favourite radio station, the French music station fip, on FM. I was curious to know if a Raspberry Pi could do the same trick, albeit on a much smaller scale, within a couple of rooms.

I didn’t do this, of course, but if you were so minded, it looks like this is, hypothetically, possible. You could even broadcast RDS station name and text too. This is what you might, hypothetically, do:

Install rpitx per the instructions atΒ https://github.com/F5OEO/rpitx

Install sox:

sudo apt-get install sox

Install lame and some associated tools for handling mp3 streams:

sudo apt-get install lame
sudo apt-get install libsox-fmt-mp3
sudo apt install libsox-fmt-all

At the command line, navigate to the rpitx directory, and type this to pipe the output of the fip internet stream to rpitx:

sox -t mp3 http://icecast.radiofrance.fr/fip-midfi.mp3 -t wav
 - | sudo ./pifmrds -freq 107.5 -ps fip -rt 'en direct a Paris' -audio -

This would stream fip radio and broadcast it on FM on 107.5 MHz FM, with the RDS station name ‘fip’ and the scrolling text ‘en direct a Paris’ (You could try an accented Γ  but it doesn’t seem to support unicode!).

All very, very hypothetically of course.

 

 

Posted in computers, hardware, radio, Raspberry Pi | Tagged , , | Leave a comment

Emojify your Python

Screen shot of Emojifier

I’ve been thinking of ways of sharing Python programs on Twitter and I’ve come up with something a bit crazy, but which might have other uses.

The micro:bit Python emojifier encodes and condenses Python programs using emojis. It also decodes them too, and turns strings of emojis back into full Python programs.

Many keywords are tokenised, in effect, into a single character. This means the programs take up less space, and so you could, for example, share longer programs in a tweet. I used carefully-chosen emojis so even the encoded programs can be read by humans. Our old friend the teleporting duck:

from microbit import *
import radio
radio.config(group=23)
radio.on()

while True:
    message = radio.receive()
    if message:
        display.show(Image.DUCK)
    if accelerometer.was_gesture('shake'):
        display.clear()
        radio.send('duck')

becomes:

πŸ”
πŸ“²πŸ“»
πŸ“»βš™(πŸ‘₯=23)
πŸ“»πŸ”›β†©
πŸ”
γ€°message = πŸ“».πŸ“₯
〰❓message:
γ€°γ€°πŸ“ΊπŸ’(πŸ–ΌπŸ¦†)
γ€°β“πŸ“³.was_🀟('πŸ₯€'):
γ€°γ€°πŸ“ΊπŸšΏ
γ€°γ€°πŸ“».πŸ“€('duck')

I think this might have other uses.

It may also encourage reluctant students to engage with coding concepts by presenting them in a new, but familiar visual language. The emojis are, in effect, a visual abstraction of programming concepts.

Programs written pictorially could appeal to those who struggle with reading or find large blocks of text hard to process, in a way not even block coding can manage,

Maybe students could even write Python programs using emojis. It might make Python more accessible to those who only have mobile devices.

This is very much optimised for Python on the BBC micro:bit, but perhaps it could be applied to other languages.

What do you think? Let me know!

Posted in computers, microbit | Tagged , , , | Leave a comment