micro:bit Morse code transmitter

Here’s a pretty simple, and I think fun, way to introduce some quite sophisticated computing concepts with a few lines of Python, 2 BBC micro:bits and a crocodile clip lead or two (as you’d find in a Makey Makey box).

2021 update
I’ve since written new wired and wireless Morse projects here: https://github.com/blogmywiki/Morse


With a few trivial lines of code you can teach:

  • drawing an image on the LED screen
  • variables
  • if/else conditional branching
  • infinite loops
  • controlling GPIO pins
  • physical networking and protocols

This takes the excellent Morse code program included with the micro:bit Python documentation and makes a simpler transmitter that is compatible with it. My simple code only works as a transmitter but:

  • it’s easier for children to type in
  • it’s easier for them to understand
  • it makes it physically easier to send the Morse code by standardising the duration of dots and dashes, using the A button to send a dot, the B button to send a dash.

Here’s what you might do in a lesson:

Get the class into pairs. Using Mu or the online Python editor, Child 1 (who will be the receiver) copies and pastes the original Morse code program onto her micro:bit and tests it – or uses my simpler receiver code at the foot of this blog post. Child 2 (the transmitter) types in my transmitter code below and flashes it onto her micro:bit.

Unplug them both and carefully connect pin 1 on the transmitter to pin 2 on the receiver using wire with crocodile clips. If both microbits are powered off the same computer’s USB sockets, you can probably stop there. If, however, you are using battery packs attach another wire joining the two microbits’ GND pins together to complete the circuit:

Children will have to be very careful not to short pins when they do this, so it might be wise to physically inspect them before they plug them back in.

Now power them back up. Child 2 should be able to send a Morse code message to child 1 using combinations of A and B button presses – A for dot, B for dash. It takes a bit of practice but my hunch is this is easier than holding down a button for the right length of time. The sender has to be pretty snappy and pay attention to what she’s doing, sending the next dot or dash as quickly as she can otherwise the decoder will think it’s the end of a letter.

There’s no sound, alas – perhaps someone can suggest a way round this. Making a beep of the appropriate duration slows the code down too much. I don’t think this is too much of a drawback as I think wiring up headphones or speakers is a step too far in a lesson anyway.

There’s lots of scope to pick apart my script – and the much more sophisticated original which encodes AND decodes, makes sound AND stores messages. And you could see how far you can send a message this way – perhaps join loads of croc clip leads together. Mmm, I may have to try that later…

See if you can send and decode this message:

.  -    .--.  ....  ---  -. .   ....  ---  -- .

Here’s the Python transmitter code…

# @blogmywiki's simplest microbit Morse code transmitter
# Press A for .       Press B for -

from microbit import *

DOT = Image("00000:"
            "00000:"
            "00900:"
            "00000:"
            "00000:")

DASH = Image("00000:"
             "00000:"
             "09990:"
             "00000:"
             "00000:")

# durations made a bit shorter than 250ms for a dot
# durations made a bit shorter than 500ms for a dot
DOT_DURATION = 230
DASH_DURATION = 470

while True:
    if button_a.is_pressed():
        display.show(DOT)
        pin1.write_digital(1)
        sleep(DOT_DURATION)
        pin1.write_digital(0)
        sleep(50) # little sleep added to debounce
    elif button_b.is_pressed():
        display.show(DASH)
        pin1.write_digital(1)
        sleep(DASH_DURATION)
        pin1.write_digital(0)
        sleep(50) # little sleep added to debounce
    else:
        display.show(Image.ASLEEP)

…and here’s my simplified receiver code:

# simple microbit Morse receiver based on
# http://microbit-micropython.readthedocs.io/en/latest/tutorials/network.html

from microbit import *

# A lookup table of morse codes and associated characters.
MORSE_CODE_LOOKUP = {
    ".-": "A",
    "-...": "B",
    "-.-.": "C",
    "-..": "D",
    ".": "E",
    "..-.": "F",
    "--.": "G",
    "....": "H",
    "..": "I",
    ".---": "J",
    "-.-": "K",
    ".-..": "L",
    "--": "M",
    "-.": "N",
    "---": "O",
    ".--.": "P",
    "--.-": "Q",
    ".-.": "R",
    "...": "S",
    "-": "T",
    "..-": "U",
    "...-": "V",
    ".--": "W",
    "-..-": "X",
    "-.--": "Y",
    "--..": "Z",
    ".----": "1",
    "..---": "2",
    "...--": "3",
    "....-": "4",
    ".....": "5",
    "-....": "6",
    "--...": "7",
    "---..": "8",
    "----.": "9",
    "-----": "0"
}

DOT = Image("00000:"
            "00000:"
            "00900:"
            "00000:"
            "00000:")

DASH = Image("00000:"
             "00000:"
             "09990:"
             "00000:"
             "00000:")             

def decode(buffer):
    # Attempts to get the buffer of Morse code data from the lookup table. If
    # it's not there, just return a question mark.
    return MORSE_CODE_LOOKUP.get(buffer, '?')

# detetct a DOT if incoming signal is less than 250ms.
DOT_THRESHOLD = 250
# detect a DASH if incoming signal is less than 500ms.
DASH_THRESHOLD = 500

# Holds the incoming Morse signals.
buffer = ''
# Holds the translated Morse as characters.
message = ''
# The time from which the device has been waiting for the next event.
started_to_wait = running_time()

while True:
    # Work out how long the device has been waiting for a signal.
    waiting = running_time() - started_to_wait
    # Reset the timestamp for the key_down_time.
    key_down_time = None
    # If pin2 (input) is getting a signal, start timing
    while pin2.read_digital():
        if not key_down_time:
            key_down_time = running_time()
    # Get the current time and call it key_up_time.
    key_up_time = running_time()
    # If there's a key_down_time (created when signal detected)
    if key_down_time:
        # ... then work out for how long it was pressed.
        duration = key_up_time - key_down_time
        # If the duration is less than the max length for a "dot" press...
        if duration < DOT_THRESHOLD:
            # ... then add a dot to the buffer containing incoming Morse codes
            # and display a dot on the display.
            buffer += '.'
            display.show(DOT)
        # Else, if the duration is less than the max length for a "dash"
        # (but longer than that for a DOT ~ handled above)
        elif duration < DASH_THRESHOLD:
            # ... then add a dash to the buffer and display a dash.
            buffer += '-'
            display.show(DASH)
        # Otherwise, any other sort of keypress duration is ignored (this isn't
        # needed, but added for "understandability").
        else:
            pass
        # The button press has been handled, so reset the time from which the
        # device is starting to wait for a signal.
        started_to_wait = running_time()
    # check there's not been a pause to indicate an end of the
    # incoming Morse code character. The pause must be longer than a DASH
    # code's duration.
    elif len(buffer) > 0 and waiting > DASH_THRESHOLD:
        # There is a buffer and it's reached the end of a code so...
        # Decode the incoming buffer.
        character = decode(buffer)
        # Reset the buffer to empty.
        buffer = ''
        # Show the decoded character.
        display.show(character)
        # Add the character to the message.
        message += character
    # Finally, if button_b was pressed while all the above was going on...
    if button_b.was_pressed():
        # ... display the message,
        display.scroll(message)
        # then reset it to empty (ready for a new message).
        message = ''
This entry was posted in computers, education, ICT and tagged , , , . Bookmark the permalink.

Leave a Reply