Wednesday, 16 June 2021

Micro:bit Sensory Extension aka The Directional Hat

I listened to a very interesting podcast the other day which was talking about sensory extensions for humans - basically, detecting something which is currently outside human sensory range and mapping it to one of our existing senses. For example, glasses which map UV frequencies to visible light, or - more possibly - magnetic fields e.g. a compass bearing, to little vibrators. The latter was the prime example on the podcast, with a university research project based on a belt with 16 vibrators connected to a microcontroller, that cooperated with one's smartphone to always indicate North, or to use GPS to buzz the motor in the direction one was to turn. 

Time for the sensory hat - an idea nicked from the podcaster, but hey, why not?

Compass - microbit!! It has a compass, and digital ports - what could possibly go wrong? Port maximum output 5mA, at 3V.

Vibrator - tiny 12000 rpm 3V DC motor, which draws about 60mA when running. Problem - motor can generate huge back EMF, which would wipe out the microbit port, so we need to bypass that with a diode.

Since the microbit ports can't handle the current draw of the motor, we need a switch of some sort. Let's use a transistor!! I haven't done this before, but conceptually it's pretty simple. That requires a resistor between the base of the transistor and the microbit port, so we don't exceed the microbit current limit.

Let's get the motor running, so to speak...

Test Motor connection

Circuit diagrams courtesy of DigiKey's excellent (for free!) web-based circuit diagrammer tool. I am very impressed.

from microbit import *
display.show(Image.HEART)
pin0.write_digital(0)
buzz = False

while True:

    if button_a.was_pressed():

        buzz = not buzz

    if buzz:

        display.show(Image.HEART_SMALL)

        pin0.write_digital(1)

        sleep(100)

        display.show(Image.HEART)

        pin0.write_digital(0)

        sleep(500)

That worked ok, although the ludicrously tiny and short wires of the motor are a total PitA. It is possible to modify the motor speed a bit using an analog output pin, but it's better to use short bursts of a digital pin, which are of course a long burst versions of the PWM analog pins!

However, I want to control lots, possibly 8 motors. This is tricky because the microbit only supports 6 "proper" i.e. unencumbered ports, viz. 0, 1, 2, 3, 8, 12 and 16. Now I could do 8 from 6 by multiplexing the pins' output, say 2 rows of 4 columns, with a pin driving each row/column. That requires something like two switches at each node of the 2x4 grid... like a transistor! Time for the AND gate...

Test hardware AND gate

This layout combined with the following code actually worked pretty much first time! The 1K resistors are required to limit the current from the microbit pins. 

from microbit import *


display.show(Image.HEART)

pin1.write_digital(0)

pin2.write_digital(0)

# Allow experimenter to see the "I'm working!" HEART

sleep(500)

display.clear()

a_on = False

b_on = False

while True:

    # Flip A&B states if buttons have been pressed

    if button_a.was_pressed():

        a_on = not a_on

    if button_b.was_pressed():

        b_on = not b_on

    # If a state is on, turn on the port and 

    # a helpful display pixel :-)

    # The AND gate does the summing in hardware

    if a_on:

        display.set_pixel(0, 0, 9)

        pin1.write_digital(1)

    else:

        display.set_pixel(0, 0, 0)

        pin1.write_digital(0)

    if b_on:

        display.set_pixel(4, 0, 9)

        pin2.write_digital(1)

    else:

        display.set_pixel(4, 0, 0)

        pin2.write_digital(0)

    # Let's give it a rest 

    sleep(500)


And now... the multiplexer!

Multiplexer circuit

This eventually worked on the breadboard - lots of problems just because I had to get some kind of sensible physical arrangement of all the wires, and I used an incorrect resistor value in one row location that caused the entire row to stay dark with insufficient base current. Cool though! I'm not sure how easily I'll be able to make this into a hat... and currently I've only got 6 motors!

from microbit import *


def setPin(pin_no: int, pinOn: bool):

    pin_no = pin_no % 8  # Make sure we haven't been slipped a dud

    col = pin_no % 4

    row = pin_no // 4

    cols[col].write_digital(1 if pinOn else 0)

    rows[row].write_digital(1 if pinOn else 0)

    display.set_pixel(col, row, 9 if pinOn else 0)


cols = [pin0, pin1, pin2, pin8]

rows = [pin12, pin16]

display.show(Image.HEART)

for i in range(0, len(cols)-1):

    cols[i].write_digital(0)

for j in range(0, len(rows)-1):

    rows[j].write_digital(0)


# Allow experimenter to see the "I'm working!" HEART

sleep(500)

display.clear()


a_on = False

b_on = False

count = -1

while True:

    # Flip A&B states if buttons have been pressed

    if button_a.was_pressed():

        a_on = not a_on

    if button_b.was_pressed():

        b_on = not b_on

    # If a state is on, turn on the port and

    # a helpful display pixel :-)

    # The AND gate does the summing in hardware

    display.clear()

    if a_on:

        count = count+1

        if count > 7:

            count = 0

        setPin(count, True)

        sleep(1000)

        setPin(count, False)

    # Let's give it a rest

    sleep(500)

And here's the current state of play...
Patch wire madness