Introduction: A Micro:bit Directional Indicator for Bicycle Helmets

About: Scientist working in in-vitro diagnostics industry. Playing with all types of sensor as a spare time hobby. Aiming for simple and inexpensive tools and projects for STEM, with a bit of science and a bit of sil…

Updated Version 2018-May-12

Below you instructions how to build a simple micro:bit based directional indicator for bicycle helmets (or similar).
It uses the accelerometers build in the micro:bit as controls.

The provided micro python scripts are optimized for mu, a micro python editor with a specific "mode" for the micro:bit. In it's latest version it comes with a serial plotter and I initially just wanted to understand how to use it to display measured values (hint: send data as tuples: print((x, y, z)), using double brackets).

Four patterns are displayed on the micro:bit's 5x5 LED display:

  • In the resting state a nice, randomized pattern is displayed.
    Currently you find scripts for three different patterns, a 'firefly', a 'rain' and a 'falling star' pattern.
    Have a look and choose the one you like best.
    Feel free to adjust the parameters, to make them more or less dense or run faster or slower.
  • Then there are "turn right" or "turn left" indicators in form of moving arrows.
    They are activated by leaning your head left or right, by pressing the buttons on the micro:bit. In the external button version of the script, activate by pressing one of the external buttons connected to pins 0 and 1.
  • If you bend your head backwards, or both buttons on the micro:bit are activated at the same time an "alert" or "break" pattern is displayed.

This pattern-displaying micro:bit might be used as a directional indicator e.g. for biking, skating or skiing. Fix the micro:bit on your helmet and control it with your head's position. Or fix it on your bike, load the external button script and control it with two external switches attached to the micro:bit via some cables.

For those working with MakeCode, I added a block script in the last step, which can be copied directly to the micro:bit. Its less fancy but gives the basic functionality without the need to install mu.

Please keep in mind:

  • While this project might be helpful for your safety, please make sure that you always give clear indications where you want to drive using your hands and arms.
  • The concept has not been tested extensively on the road and was intended as a programming example only.
    Use it on your own risk.
  • Use the basic version only under dry weather conditions, as the micro:bit and the battery or LiPo packs are sensitive to humidity. There is a description how to build an encapsulated version further below.

Step 1: Materials Used

A micro:bit.
A computer with the mu editor installed.
Battery pack or LiPo pack for the micro:bit.
A bicycle helmet. I used one that used to have a LED backlight.
A piece of 3mm polypropylene cardboard, as distance piece between micro:bit and helmet.
Double-sided duct tape to fix the micro:bit to the distance piece and this to the helmet.
Duct tape, to fix the micro:bit and battery pack in the helmet.

For an encapsulated version:
a 59 x 59 x 30 mm clear plastic box, Modulor, Berlin: 0,70 Euro
Kitronic MI:power board, 5 GBP
double sided duct tape and a piece of the PP plates

For the external switches version (details not shown here):
Jumper cables and two switches, two white LEDs, a 10 kOhm resistor, a breadboard.
Crocodile clamps.
M3 brass screws (20 mm), M3 nylon nuts; four each, for pin 0, pin 1, 3V and Ground.
Place screws through the holes in the micro:bit's PCB and fix with the screws. They simplify to attach crocodile clamps.

Step 2: Setting Up the Device, Installation of the Script

  • Install the mu editor on your computer.
  • Connect the micro:bit to the computer.
  • Load the desired script.
  • Flash the script to the micro:bit.
  • In the case of the accelerometer (helmet) scripts, fix the micro:bit and the battery pack to your helmet.
    I used a small piece of plastic cardboard, a material you can find in your hardware store, as distance piece and double sided duct tape on both sides to fix the micro:bit to the helmet.
    Then fix the micro:bit and battery pack with duct tape on your helmet.
  • To make it weather prove, have a look on a later step.
  • If required, adjust the x and z threshold values according to your needs.
  • In the case of the button-driven script and if you like to use external buttons, connect the breadboard's power rails to Gnd and 3V ports of the micro:bit. Connect the buttons to Gnd and the Pin0 and Pin1 ports.

    Step 3: The Micro Python Scripts

    Attached you find the micro python scripts for mu and the micro:bit.

    There are four scripts: one that controls the display using the build-in and external buttons, three using the build-in accelerometers of the micro:bit. They have different random pattern generators for the resting state.

    There is a 'firefly' pattern, a 'rain' pattern and a 'falling star' (matrix-style) pattern. The firefly/accelerometer script is listed below. There is also a script that has all three patterns and runs them in a randomized order, with a new pick every time an indicator had been activated.

    The accelerometer values are sent to the computer and can be read via the serial monitor of the mu editor or displayed on the serial plotter.

    It is easy to change parameters to adjust the scripts for your requirements and preferences.

    <pre>'''<br>Angle/accelerometer or build-in buttons controlled version. 2018-May-07
    A simple script that produces 
    a "firefly" pattern in resting state,
    left or right moving arrows if 
       the m-bit is twisted in the coresponding direction,
       or buttons A or B are pressed 
       
    or a break indicator/alert pattern if both buttons are pressed 
        or the m-bit is bent backwards.
    Might be used for a bicycle helmet backlight or similar.
    Build for the mu micro python editor by Dr H.
    https://www.instructables.com/id/A-Microbit-Direction-Indicator-for-Biking-Helmets/
    '''
    from microbit import *
    import random
    random.seed(3433)   # enter your lucky number 
    de = 100   # sets display delay time in ms
    ff1 = 100    # sets firefly delay time 1 in ms
    ff2 = 50    # sets firefly delay time 2 in ms
    fn = 3     # sets number of firefly seed points
    thresh_z = 80   # threshold value for backwards
    thresh_x = 350  # threshold value for sidewards
    # define images
    image_l_1 = Image(   
        "00900:"
        "09000:"
        "97531:"
        "09000:"
        "00900")
                  
    image_l_2 = Image(   
        "09000:"
        "90000:"
        "75319:"
        "90000:"
        "09000")
                             
    image_l_3 = Image(   
        "90000:"
        "00009:"
        "53197:"
        "00009:"
        "90000")
                  
    image_l_4 = Image(   
        "00009:"
        "00090:"
        "31975:"
        "00090:"
        "00009")
        
    image_l_5 = Image(   
        "00090:"
        "00900:"
        "19753:"
        "00900:"
        "00090")
        
    image_r_1 = Image(   
        "00900:"
        "00090:"
        "13579:"
        "00090:"
        "00900")
                  
    image_r_2 = Image(   
        "00090:"
        "00009:"
        "91357:"
        "00009:"
        "00090") 
        
    image_r_3 = Image(   
        "00009:"
        "90000:"
        "79135:"
        "90000:"
        "00009")  
        
    image_r_4 = Image(   
        "90000:"
        "09000:"
        "57913:"
        "09000:"
        "90000")   
        
    image_r_5 = Image(   
        "09000:"
        "00900:"
        "35791:"
        "00900:"
        "09000")
        
    image_z_1 = Image(   
        "90009:"
        "00000:"
        "00900:"
        "00000:"
        "90009")
    image_z_2 = Image(   
        "09090:"
        "90009:"
        "00000:"
        "90009:"
        "09090")    
    # start the program    
    while True: 
        
        print((accelerometer.get_x(), accelerometer.get_y(), accelerometer.get_z()))
        # to be used with serial monitor or plotter for threshold value optimization; 
        # mute with '#' if not used
        
        if ((accelerometer.get_z() > thresh_z)      # head bent back, adjust if required
                or (button_a.is_pressed() and button_b.is_pressed())):   # for control purposes
               
            display.show(Image.DIAMOND_SMALL) 
            sleep(de) 
            display.show(Image.DIAMOND)
            sleep(de)
            display.show(image_z_2) 
            sleep(de)  
            display.show(image_z_1) 
            sleep(de)               
            display.clear()
            
        elif ((accelerometer.get_x() < (-1*thresh_x)) 
                # direction indicator left; bend head about 20 degrees left
                # adjust as needed
                or button_a.is_pressed()):    # for testing purposes 
                    
            display.show(image_l_1)
            sleep(de)
            display.show(image_l_2)
            sleep(de)
            display.show(image_l_3)
            sleep(de) 
            display.show(image_l_4)
            sleep(de)
            display.show(image_l_5)
            sleep(de)
            display.clear()
            
        elif ((accelerometer.get_x() > thresh_x)  
                # direction indicator right; to activate bend head about 20 degrees right
                or button_b.is_pressed()): 
            
            display.show(image_r_1)
            sleep(de)
            display.show(image_r_2)
            sleep(de)
            display.show(image_r_3)
            sleep(de) 
            display.show(image_r_4)
            sleep(de)
            display.show(image_r_5)
            sleep(de)  
            display.clear()
                 
        else:          # 'firefly' pattern generator
            for g in range(0, fn):           # seed a given number (fn) of pixels  
                x = random.randint(0, 4)     # picks a random position
                y = random.randint(0, 4)
                v = 9                        # seed brightness maximum
                # v = random.randint(0, 9)   # optional: randomized seed brightness
                display.set_pixel(x, y, v)
            # set firefly velocity
            sleep(ff1)                        # display for ff ms              
            
            # reduces intensity of all pixels by one step
            for j in range(0, 5):                # for every pixel of the LED array           
                for i in range(0, 5):
                    b = display.get_pixel(i, j)  # get current intensity
                    if (b > 0):
                        f = b - 1                # reduce brightness by one
                    else:
                        f = 0                    # sets 0 as lowest allowed value
                    display.set_pixel(i, j, f)
                    
            sleep(ff2)

    Step 4: An Encapsulated, Weather Proof Version

    As mentioned above, the basic version is not weatherproof. I therefore have build a encapsulated version.

    To power the micro:bit here I used a Kitronic MI:power board. It is powered by a 3V coin cell and can be fixed to the micro:bit with three bolts and nuts. It also has a build in power switch. Alternatively you may use a LiPo battery.

    As housing I am using a 59 x 59 x 30 mm clear plastic box. A piece of 3 mm plastic cardboard covered with double sided duct tape was used as a distance piece. It's required as the back of the MI:power is not even due to the nuts and holds the micro:bit in place.

    The box with the micro:bit is then fixed to the helmet by another piece of plastic cardboard covered with double sided tape.

    Step 5: A MakeCode Script

    For those who are not willing or able to install mu, I have added a MakeCode block script with a similar functionalities. By far not so fancy, but good enough to show the principle.

    You may just copy the file to your micro:bit and play.

    Microcontroller Contest

    Participated in the
    Microcontroller Contest