Friday, 5 August 2022

Starting a Code Club!

As discussed earlier, I'm expecting to be starting a new Code Club in September, at a secondary rather than primary school. This will be more challenging, I hope and expect, because of the age range and increasing teenager-ness. 

Budget?

The staff member leader says she has a small budget for this, but had thought of using Lego-based programmable stuff to provide a bridge from their possible learned experience for Year 7 students. I investigated this, and even the cheapest Lego automation kits for schools (no longer Mindstorms, but Lego Education SPIKE) run at £200-300 to support just two students. So that doesn't seem sensible. 

Meanwhile, over at Science Oxford, where I've been volunteering recently, the leader there has a huge wealth of good experience with stuff purchased from AliExpress etc., as raw materials or boards that are relatively easily, and experientially usefully, assembled into robots or other automated/controlled entities, for far less money. Excellent. 

It's worth looking at the Science Oxford GitHub repository for lots of projects that Sarah has run over the years. Of course, not all of these folders provide the recipes for components etc., but they're good for starting points. 

I certainly think it's worth looking at setting up a store of various controllable objects, like motors, servo motors, LEDs both raw and NeoPixel-like, and their associated control boards for microbits. Bought in bulk from AliExpress, they are pretty cheap. 

Topics?

I was thinking it would be best to start simple, by surveying the assembled multitude for their existing knowledge and possible ideas/enthusiasms, whilst giving them a starter project initially, probably on laptops only, or with microbits on their own, no added materiƩl. Get the right atmosphere, one of exploration and non-judgemental experiments, and they should flourish. Ideally, there would be multiple individual or small group projects running over a period, so that students are pursuing their interests with the enthusiasm that brings, rather than me excessively imposing my ideas. It would also allow for everyone to learn from everyone, as there would be examples of problems solved for all to see.

The Code Club website has bazillions of projects and learning paths, I've no doubt I'll be drawing on those. Letting kids find their own way so they can proceed at their pace, and get a blast from explaining to you what they're doing and why, whilst knowing that they can get help and explanations is a good result.

Let's see how it goes.

Washing Machines as a topic for CC exploration

I'm about to start a Code Club at a local secondary school, courtesy of a couple of days as part of the STEM Network. That involved working with Year 7 students on a "make a security device/container" project, using microbits. One of the staff involved asked me if I was interested in supporting a Code Club, what could I say?? Anyway, some ideas for entertainment may be required...

And in respect of that, how about designing/building a washing machine?? Not a fully functional one of course, but at least getting the kids to think about what the requirements for a washing machine are, how they could be met, and programming/building some of the more interesting aspects.

Ideas include:

  • Water Temperature (variable)
  • Heating control
  • Washing powder/softener/pre-wash release
  • Water valve controls, water height/volume
  • Motor speed, intermittent vs spin cycle
  • Wash Programmes (separate value settings vs. validity, or "packaged" programmes?)
  • All the interlocks (doors, water height, stop/start/reset etc)
  • Timers of all sorts
  • Front panel display and controls (content, complexity, UI aspects, warning sounds/signals etc)
  • The future of washing machines?? Online interface? Security concerns? Phone control app (using 
All of this could be coded on microbits, with physical "simulation" of some kind or other. It could be a multi-team/person project, with each group working on a different aspect, and having to coordinate.

More on this later...

Tuesday, 13 July 2021

Microsoft MakeCode Arcade 3D effects and Kitronik Arcade

 I wanted to create a simple game for a granddaughter to play on the Kitronik Arcade which involved hiding a sprite behind various objects using the direction keys. However, I had no idea how to get one sprite to go behind another!!

The Kitronik Arcade is a pretty neat piece of kit which runs the downloaded code from Arcade, and includes a "proper" set of GameBoy-like control buttons with a very neat colour screen, a piezo speaker and a vibrator motor for haptic effects. It's great to run your own code on a real handheld device instead of an emulator.

It turns out 3D is pretty simple. Here's my code, stolen largely from an excellent post in the MakeCode Forum, with the addition of jumping on button B, and "mooing" on button A! The main thing is to set the "z" parameter on all the sprites, using a suitable algorithm or choice of values. So the example sets the z value of the two physical object sprites to their "bottom" value, and the moving sprite z value to its "bottom". Then the moving sprite "disappears" behind the object when it overlaps it, because its lowest point is either greater than (in front of) or less than (behind) the object. There's some more code for making sure it walks around the object by removing ambiguous values. 

This was so much fun I decided to make a spaceship orbiting a planet... Here's the code. This uses the formula for an ellipse to move the moving sprite around the planet sprite. The spaceship Z is set to its current Y location, and that of the planet to its horizontal centreline. The cunning bit is using A and B buttons to accelerate/decelerate the ship and change the orbit - faster is lower, slower is higher. The really hard bit was getting an image of the Earth into the Arcade environment - I followed the instructions in the manual, which results in a .ts file that you copy into your JavaScript in Arcade. Yeah, JavaScript, sadly Blocks/PXT doesn't seem to support imported images. The video is of the emulator on my computer, not the Kitronik Arcade.





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



Saturday, 6 February 2021

Micro:bit Message Scroller (or hat) Part 2

 I couldn't resist fiddling about. 

V4

I've 

  • Put an array ("list") of messages in the scroller code
  • Press A+B to select the next message, or back to the start of the message list if already at the last
  • Set a "changeMsg" flag in the A+B pressed  and the "receive name value" blocks
  • Put the message switching code into a function, called switchMsg
  • Check the "changeMsg" flag in the main forever loop; if it's set, then invoke the message switch function
  • Any microbit can be used to switch message
  • Enabled MBs to switch message before they are configured with an ID - this required an additional "configured" flag, distinct from the "readyToGo" display control flag
I think the use of a very small "receive name value" block to set the "changeMsg" flag has made it a lot more responsive. In addition, putting the switchMsg code in the main processing line has made the switch more responsive, there's no lag where the main processing is still running while the message is changing.
Initialisation

Main loop - only really works for ID = 0

Code for ID > 0 - character index code needs tidying!

Button and change message signal processing

switchMsg function

In addition, I've created a MessageSwitcher, which independently sends a new message index, using A and B buttons to increment/decrement the index to send. Hurray!


Micro:bit Scrolling Message Board (or Hat!)

It's been impossible to run Code Clubs and so on in these COVID-19-y times. However, I've just volunteered for a series of online Code Clubs with the Oxford Science Centre, and I thought I'd do something to introduce myself. What better than a scrolling message hat??

A simple idea - an array of microbits that scroll a message.

V1

Set up a displayString with the message to display. Assign each microbit an ID (1 -> n-1, string of length n), then using "radio send number", have the first (0) microbit send the index of the letter it's displaying to the others. They then subtract their ID to get the index of the letter they should be displaying, and display it. 

  • MB 0 - Forever; For index = 0 to displayString.length -1; radio send number index; show displayString[index]; end For; end Forever
  • MB >0 - On radio receive number; If number - ID >=0 show displayString[number - ID]
OK... but it doesn't let the message tail off the display on the repeat, which is uncool, because the received number goes below the ID again!

Important point: I had to make sure I did "radio send index" before starting to display the current character, otherwise the later microbits in the chain didn't get the message until the first microbit had moved on :-/.

V2

Added some code to check if anything has been displayed yet, and if not, use number-ID to check for displayability; if previously displayed something, then use (number+displayString.length-ID) mod displayString.length to determine what character to display.

V3

It was really boring having N versions of the code to get a different ID into each microbit. So I used "press button A N times" to set the ID, and "press button B" to indicate I'd finished configuring the ID. The main loop and on radio receive number code were prevented from executing until the ID was configured by using a "readyToGo" boolean that was set to TRUE by the "press button B" code. Nice. Easy to have 10 microbits now!

V4

Having the same message all the time is really boring. I modified the scroller code to include "on radio received string" which would receive a new string, reset things, and start again, thereby changing the message. 

I also created a "send new message" microbit, with an array of messages, selected by pressing button A repeatedly, and then pressing buttons A+B to send the radio message with the new string. 

This seemed to be cutting the string short, and I eventually discovered that the maximum string length for radio send string is 19 characters, or I think 18+terminator. So keep those messages short!


The latest scroller code

The current message setting code

Possible enhancements

  • Have all the messages in the scroller code, and have the ID 0 microbit do the triggering to all the others, so they use the identified message in the list; could be a timer or a number of repeats before changing; this avoids the radio send message length limitation
  • The above, with a simple trigger microbit to tell them all to switch to the next or specific one. Nice for a stupid Stephen Hawking style message hat (simple words like Yes, No, Maybe, What?).
  • With, say, 10 MBs, you could display individual words statically, or if too long, scroll them. Use an array of words rather than entire messages. That would be easier to read!

Tuesday, 10 November 2020

Micro:bit Bluetooth communications with RaspberryPi

I have acquired a number of micro:bits for a primary school Code Club - currently in abeyance because of COVID-19 constraints - and I've been thinking about useful experiments and instrumentation.

One of the basic models is collect data over a period of time and then look for patterns, make associations and draw conclusions. A microbit doesn't really have a means to store data over an arbitrary length of time, let alone process it in any meaningful way. For that you need a "proper" computer. So how do you get machine-readable data off a microbit?

The first way is to write the data down a serial link over a USB connection to a PC. Then since that requires a terminal program to "catch" the serial data, the terminal program has to write it to a file. Then what if you want to use multiple microbits? That means multiple USB connections, wires etc., multiple terminal program instances and so on. Of course, you could write a Python program to do all that...

It seemed more interesting to use Bluetooth if possible. No wires!! So I've been investigating python-bluezero, which provides a handy Python interface to the Linux BlueZ Bluetooth software. It also includes a microbit interface, which is extremely handy. In fact, the software includes 3 different levels of interaction with BlueZ, all based on the Linux d-Bus IPC mechanism, requiring greater or lesser (in my case!) knowledge of the underlying mechanism. 

Well, dear reader, it works. Certainly the simple, polling-style interfaces seem to work just fine. You can scan the microbit for temperature, button state, LED state etc., and also set LED state and send max. 20 character text strings to display. Pretty handy! I've got it working with a single microbit, and will have a go at interfacing to 2+ microbits simultaneously. Python allows for multiple instances of the Microbit class, so I can presumably handle each one separately in a single Python process. That would allow for, for example, differential temperature measurements across a room, or inside/outside, taken simultaneously. Nice.

One strange behaviour I've noticed is that the callback for Button presses doesn't seem to work. This is in microbit.py:

def _decode_btn_a(self, *button_values):
    """Decode button A state and pass on to user callback."""
    if 'Value' in button_values[1]:
        self.user_btn_a_callback(int(button_values[1]['Value'][0]))

def subscribe_button_a(self, user_callback):
    """
    Execute user_callback on Button A being pressed on micro:bit
    :param user_callback: User callback method receiving the button state
    :return:
    """
    self.user_btn_a_callback = user_callback
    self._btn_a_state.add_characteristic_cb(self._decode_btn_a)
    self._btn_a_state.start_notify()

Basically, you call subscribe_button_a with your routine to be called when Button A is detected to have changed state e.g. 

def buttonA_pressed (action):
    print('Button A: ', action)

This is effected by

  • Saving the user callback in the Microbit class
  • Installing _decode_btn_a as the callback from the dBus messaging service
  • When that is called, decoding the notification and Button action value from the dBus message
  • Invoking the saved user callback with a single parameter, the action value
So in principle, this should all work. It doesn't seem to. In fact, it's like the code actually installed the user callback as the dBus-level callback, because the parameter count and types don't match. I've ended up replicating the code in the _decode_btn_a function in order to make it work. Poo. Weird.

Update

I contacted the author on GitHub, and the short version is that I hadn't correctly installed the latest version into python3. I had downloaded the old version (event_service), as indicated by a blog I found, which actually did install the user callback into the dBus slot! So it worked exactly as coded.

Learning Points
Always 
  • Go to GitHub and get the code that corresponds to what you think you are supposed to be running
  • Do sudo python3 -m pip install <installable_thing> - that way it gets installed in the right environment