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!