Category Archives: micro:bit

Project: A Micro:bit calculator

Using a 4×4 keypad and a 16×2 LCD screen, can you construct a simple calculator?

Step 1: Connect up the hardware. Review the notes on keypads and LCD screens, make sure you understand how they work, connect them up and run some test programs. Both should be able to connect to pins 19 and 20 (the I2C pins) and operate independently.

Step 2: Can you make what you type appear on the screen, one character at a time? What about using * to clear the screen?

Step 3: Start with a very simple calculator that will accept a single digit, use A-D for +,-,x and รท, then calculate on the second digit pressed. Show the input on the top line and the answer on the second. Tip, you will find the function int(string) useful for converting strings to integers.

Step 4: Make the calculator more advanced. Allow you to put in multiple digit numbers, then use the # key for an =. You will need to break down the string into variables for each part. There is a list of string functions at w3schools. What might be useful is the split() function. Remember you can make a note of which operator is used as it is pressed. This avoids having to search the string for operators, or you could use the find() function.

Micro:bit Keypads

Keypads make a convenient way to add lots of buttons to a Micro:bit and they come in a range of sizes. The type above are known as membrane keypads. They have a sticky back, are lightly waterproof and can be wiped clean.

4 x 1 Keypad

A 4 x 1 keypad is named as it is 4 buttons in 1 row. It comes with a ribbon connector cable, with 5 pins. There is one pin for each key and one for ground. The wiring for these can always differ, so it is worth checking. Often ground is on a strip of cable on it’s own. The connector usually had a small number on each end denoting the pin number. On the keypad pictured above, the pinout, going from left to right is:

PinFunctionMicro:bit
1GroundGnd
2Key ‘2’pin0
3Key ‘1’pin1
4Key ‘4’pin2
5Key ‘3’pin8

You can see from the above the wiring does not go 1, 2, 3, 4 as you may expect. Sometimes these keypads can have different symbols or be blank.

Have a quick read up on Micro:bit buttons to see how a basic push button works on a Micro:bit. If we use a variable for each button and assign it to a pin we can set an internal pull up resistor then use read_digital() to get the state. The following code will show on the display which button is being pressed:

# 4x1 keypad

from microbit import *

# Define our wiring. The 5th connection goes to ground
key1 = pin1
key2 = pin0
key3 = pin8
key4 = pin2

# Set internal pullup resistor
key1.set_pull(key1.PULL_UP)
key2.set_pull(key2.PULL_UP)
key3.set_pull(key3.PULL_UP)
key4.set_pull(key4.PULL_UP)

while True:
    if ( key1.read_digital() == 0 ):
        display.show("1")
        sleep(500)
        display.clear()
    if ( key2.read_digital() == 0 ):
        display.show("2")
        sleep(500)
        display.clear()
    if ( key3.read_digital() == 0 ):
        display.show("3")
        sleep(500)
        display.clear()
    if ( key4.read_digital() == 0 ):
        display.show("4")
        sleep(500)
        display.clear()        

There is a lot of repeated code there and the program does not scale well if we were to use a different sized keypad. We can take advantage of arrays in Python to avoid writing the same code over and over again. Read up on arrays at w3schools. Rather than use a straightforward array of single values, we use a two dimensional array. Each value in the array keypad is a small array itself, with two elements, the pin number and the value. Lists start at 0, so we can access the value of the second pin with keypad[1][1].

# 4x1 keypad

from microbit import *

# Create our pinout as a list. Use pairs of values
# of [ pin, value ]
keypad = [ [pin1, "1"],
           [pin0, "2"],
           [pin8, "3"],
           [pin2, "4"] ]


# Set internal pullup resistor, looping through the list
for k in keypad:
    k[0].set_pull(k[0].PULL_UP)

while True:
    # Loop through list checking for keypress
    for k in keypad:
        if ( k[0].read_digital() == 0 ):
            display.show(k[1])
            sleep(500)
            display.clear()

4×4 Keypads

We learned above that smaller keypads have one pin for each button, plus an additional pin for ground. A 2 button keypad will have 3 pins, a 4 button keypad has 5 pins. A 4×4 keypad has 16 keys, so it should have 17 pins, right? No, it only has 8 pins, so what is going on here?

The image below shows a circuit diagram of a 4×4 keypad:

The wiring forms a matrix. If you trace the circuit through, you can see if the ‘5’ key is pressed, a connection is formed between pin 7 and pin 3. If we can detect these connections then we can work out what button is being pressed.

Rather than use 8 IO pins on the Micro:bit, we can use a PCF8574 module to easily plug in the keypad and use some binary or hex to quickly work out what buttons are being pressed. Read up on the PCF8574, binary and hex here. Connect the keypad to the PCF8574 board making sure pin 1 on the keypad goes to p0 on the board. P0 is labelled on the underside.

The PCF8574 has four connections, connect Vcc to 3v and GND to GND. The remaining two connections are for the I2C bus. SDA goes to pin 20 and SCL goes to pin 19. It does not matter if you already have a device plugged into these two pins on the Micro:bit. As I2C is a bus technology, it can support multiple devices, each with it’s own address. The three small switches on the PCF8574 are used to change the address of the board, leave then all as ‘Off’.

On the PCF8574, if a pin set to be high is connected to a pin set to be low, then the high pin will be dragged low. If we set all the pins to high, except one row (for example pin 5), we can detect a button press on that row by a pin 1 to 4 also going low. We can write one value to set the pins then read it back, if there are no key presses on that row the value will be the same. If the value is different then something is being pressed. Cycle through pins 5 to 8 in turn setting each to be low then inspect to see if something is pressed and return what.

We can follow the circuit diagram and make a table of what the two low values should be for each key press. As this forms an 8 bit binary string, we can also shorten each of these to give a unique hex value for each pin.

KeyP8P7P6P5P4P3P2P1Hex
1011101110x77
2011110110x7B
3011111010x7D
4101101110xB7
5101110110xBB
6101111010xBD
7110101110xD7
8110110110xDB
9110111010xDD
A011111100x7E
B101111100xBE
C110111100xDE
D111011100xEE
*111001110xE7
0111010110xEB
#111011010xED

Similar to an array in Python, we can also use a data structure called a dictionary to store these hex values. An array uses an index number 0, 1, 2… etc for each entry, where as a dictionary can use anything else, even text. Read up here. If we make a dictionary with each of the above hex values as keys and the key symbol as the value, e.g. keys[0xBB] = "5", we can quickly look up what key is pressed from the value returned from the PCF8574 board.

This gives us a process we can follow to detect key presses:

  • Create a variable with all the bits high except pin 5.
  • Send variable to the PCF8574 to set the pins.
  • Read data back from the PCF8574 and store in a variable.
  • If the value sent is different to the value received, a key on that row is being pressed
    • Lookup that value in the dictionary and return the key press
  • Skip to the next row and repeat if no press detected

This is done in the readKeypad function in the following code:

# 4x4 keypad
from microbit import *

PCF_ADDR = 0x20     # Use the base address of the PCF8574

# I2C send, function to simplify writing
def sendI2C(addr, value):
    # Convert sent value to a byte array, which is
    # required for I2C
    buf = bytearray(1)
    buf[0] = value
    # Send value
    i2c.write(addr, buf)
    # Short delay to send
    sleep(5)

def readI2C(addr):
    # Read in as a single byte and return
    v = i2c.read(addr, 1)[0]
    return v
    
def readKeypad():
    # Define array of keypress values
    # Curly brackets are a dictionary as we are not
    # using consecutive index values
    keys = {}
    keys[0x77] = "1"
    keys[0x7B] = "2"
    keys[0x7D] = "3"
    keys[0xB7] = "4"
    keys[0xBB] = "5"
    keys[0xBD] = "6"
    keys[0xD7] = "7"
    keys[0xDB] = "8"
    keys[0xDD] = "9"
    keys[0x7E] = "A"
    keys[0xBE] = "B"
    keys[0xDE] = "C"
    keys[0xEE] = "D"
    keys[0xE7] = "*"
    keys[0xEB] = "0"
    keys[0xED] = "#"
    
    # Set the default keypress to be an empty string
    # to mean nothing pressed
    kp = ""
    
    # Start with a zero on bit 5, all the rest 1
    setState = 0xEF
    
    for x in range(4):
        sendI2C(PCF_ADDR, setState)
        # Read value back
        r = readI2C(PCF_ADDR)
        
        # If the return state is different to the set state, a
        # key press has pulled one of the pins, 1-4 low
        if(setState != r):
            # print("setState=",hex(setState),", r=",hex(r))
            kp = keys[r]
            # Return key press
            return kp
            
        # Move the zero up to the next pin with a left shift.
        # We want to keep the value at 8 bits, so need to AND
        # with FF. Also, a zero will be moved in from the right.
        # OR with 0x01 to set this lower bit.
        # e.g. 1110 1111 << 1
        #   = 11101 1110 & 0xFF
        #   =  1101 1110 | 0x01
        #   =  1101 1111
        setState = ( ( setState << 1) & 0xFF) | 0x01
    
    # Return keypress value
    return kp

while True:
        # Read from keypad
        kp = readKeypad()
        # Clear if an empty string comes back
        if( kp == "" ):
            display.clear()
        else:
            # Otherwise show
            display.show(kp)
        sleep(200)
   

Using a dictionary this way also allows you to change the meaning of keys. For example if you were making a calculator you could change the dictionary to return ‘+’ instead of ‘A’. Remember you can use the bin() and hex() functions with print if you want to see what is going on for any stage of the above.

Micro:Bit – More IO pins (PCF8574), binary and hex

The Micro:bit comes with 20 IO (input/output) pins which can be used to connect a range of hardware devices, however if you look at a pinout diagram, you find that 8 of these are used by the LED display and the buttons. If you start using devices which require a lot of pins (such as keypads), then you can quickly run out of usable pins.

One solution is to use devices such as PCF8574 modules which connect to the I2C (19 and 20) pins and will provide 8 additional pins. As you can connect multiple devices to I2C, you can either add more pin expansion modules or other types of device. For example, with a keypad attached to a PCF8574 and an I2C LCD screen, you can attach both to the Micro:bit and only use pins 19 and 20.

When all onboard switches are set to off, PCF8574 modules usually use the I2C address 0x20. If we connect LEDs to the first six pins of a module (P0 to P5) with 220 or 330 ohm resistors to ground on the other side (as pictured below). Build this circuit but ignore the buttons for now. We can flash all six LEDs with the following code:

# Testing of a IO expanded - PCF8574
from microbit import *

PCF_ADDR = 0x20     # Use the base address of the PCF8574

# I2C send, function to simplify writing
def sendI2C(addr, value):
    # Convert sent value to a byte array, which is
    # required for I2C
    buf = bytearray(1)
    buf[0] = value
    # Send value
    i2c.write(addr, buf)
    # Short delay to send
    sleep(5)
    
while True:
    sendI2C(PCF_ADDR, 0xFF)
    sleep(1000)
    sendI2C(PCF_ADDR, 0x00)
    sleep(1000)

What is going on here? The PCF8574 has 8 IO pins labelled P0 through to P7. If we want to set the output to one of those pins to high, we need to send it a 1, or a zero to be low. Where as with the normal Micro:Bit IO pins, we set them individually, with the PCF8574, we set them all in one go by sending a single byte containing all the information we need. An I2C write to the modules address with the value 0xFF means send all 1s, or turn all the IO pins on. Sending 0x00 means turn all the IO pins off. The above code flashes all the LEDs.

To really understand what is happening and have greater control of the LEDs, we need to understand binary and hexadecimal.

Understanding binary

Binary is another way of counting. We are used to using the numbers 0 to 9 (known as decimal), but in binary we only use 0 and 1. There is a BBC Bitesize module which gives a lot more information into understanding binary. Here we have a very brief description.

In decimal counting, a single digit 0 to 9 is not a lot of use if we want to count higher than 9. To go higher, we use the same digits again, arranged into columns increasing in value as we go from right to left. We start with units, then tens, hundreds, thousands and so on. We think of the number 157 as:

1000100101
0157

We are saying 157 is no thousands add 1 x 100 add 5 x 10 add 1 x 7. You will also notice that the columns as you go from right to left increase by a multiple of 10 each time.

In binary, as we only have the two digits 0 and 1, the columns increase by a multiple of 2 each time we move one to the left, which means we can think of numbers as:

1286432168421
11111111

With all numbers set to 1, 1111 1111 in decimal is 128+64+32+16+8+4+2+1, which is 255. If we changed all the values to 0, this would be 0 x 128 + 0 x 64 + 0 x 32 etc. The result would be 0, so in binary and decimal, 0 is the same. (So is 1).

How does this relate to our LEDs? Each of the IO pins is controlled by a single binary digit, so we can think of them all as:

1286432168421
P7P6P5P4P3P2P1P0
11111111

So, looking at the above, we know that 1111 1111 is 255 in decimal, and 0000 0000 is 0, so what would happen if we changed our loop to:

while True:
    sendI2C(PCF_ADDR, 255)
    sleep(1000)
    sendI2C(PCF_ADDR, 0)
    sleep(1000)

Give it a try.

No change. We are setting pins 7 and 6 when we have nothing attached, so why bother? What if we wanted to stop sending to those and only flash the lights on alternate pins with P5 on? What value would we need to send instead of 255? Consider it as a table:

1286432168421
P7P6P5P4P3P2P1P0
00101010

We need to add all the columns where there is a 1, which is 32+8+2. The answer is 42. Try changing this in the main loop and see what happens? What other patterns can you produce?

In python, you can write binary numbers directly, using the prefix ‘0b’. Another way to send the above example would be ‘0b101010’. Give that a try.

Binary Operators

In binary, we have a number of binary operators available. These are functions in the same way we have plus and multiply in decimal. Using these, we can get other effects. You need to be aware that these operate on bytes. A byte is 8 binary digits, so 42 should always be considered as 0010 1010, not just 101010. Our PCF8574 requires a single byte send to cover all 8 IO pins.

Python notationFunctionDescription
~ aNOTThe inverse of the value of the variable a. All 1s are changed to 0 and all 0 to 1. 0010 1010 becomes 1101 0101
a & bANDApplied in a column by column basis. If there is a 1 in the 4th column of ‘a’ and a 1 in the 4th column of ‘b’ then the result will be 1.
0010 1010
AND 0110 1001 becomes
0010 1000
because there are only 1s in both the 4th and 6th column, counting right from left.
a | bORApplied in the same way as AND only there will be a 1 in the result if there is a 1 in ‘a’ or in ‘b’. e.g.
0010 1010
OR 0110 1001 becomes
0110 1011
a << bLeft shiftMove all the binary digits to the left ‘b’ number of places, padding the end with zeros. Imagine this as more zeros coming in from the right to shove the numbers up. If we try a left shift of 2 we get
0010 1010 << 2
= 1010 1000
a >> bRight shiftMove all the binary digits in a to the right ‘b’ number of places. Same as left shift only the other way round.
0010 1010 >> 2
= 0000 1010
The two digits on the right that were pushed out disappear – like pushing them into the bin!

We can have a bit of fun with these. What if we wanted to create a pattern and flash the opposite? Just assign our pattern to the variable ‘p’ and use the NOT function:

while True:
    p = 0b101010
    sendI2C(PCF_ADDR, p)
    sleep(1000)
    sendI2C(PCF_ADDR, ~ p)
    sleep(1000)

Try changing the pattern.

What if we wanted to flash each light in turn then return to the start? We can work out that to light only the right most light, we need to send a 1 or 0b1, then to only light the next we can send a 2 or 0b10, then 4 or 0b100 etc, but that gets a bit repetitive. All we are doing each time is moving the 1 to the left and we know we have 6 LEDs, so we can keep the code short and use the left shift:

while True:
    # Set the initial pattern, right light on
    p = 1

    # Loop 6 times
    for x in range(6):
        sendI2C(PCF_ADDR, p)
        sleep(500)
        # Shift the pattern 1 place to the left
        p = p << 1

Hexadecimal

So, what about hexadecimal?

Hexadecimal is another numbering system. Rather than use the digits 0-9 or just 1 and 0, hexadecimal has the numbers 0 to 15. Going into double digits gets confusing so we use the letters A to F. In Python, we use the 0x prefix to mean something is hexadecimal. It was this we were using in the original code snippet. There is a BBC Bitesize module on hexadecimal.

HexadecimalDecimalBinary
000000
110001
220010
330011
440100
550101
660110
770111
881000
991001
A101010
B111011
C121100
D131101
E141110
F151111

When counting in hexadecimal (or hex for short), the columns go up in multiples of 16. E.g.:

256161
02A

In the above, we have 2x 16 + 10 x 1, which is 42 again. We previously used the binary 0010 1010 to represent 42, but look at the table of hex to decimal to binary digits. A 2 in binary is 0010 and a A is 1010. If we put them together 2A = 0010 1010. Basically by looking at each hex digit we can quickly convert between hex and binary without worrying about how man 32s, how many 16s we have etc. This is why programmers often use hex. Computers operate using binary and hex gives a very short hand notation for writing binary.

In the very first example, we set all the outputs to 1 by sending 0xFF. Can you see why that works?

Reading inputs

What if we wanted to attach some buttons? Can we read inputs? Yes. Now connect up P6 and P7 to buttons as in the picture above. If we write a 1 to each of these pins to put them in a high state, connect then to one side of a button and the other side to ground, when you press the button, it will be pulled low and turn to zero.

The following code writes a 1 to P6 and P7, then reads it back and prints the value back as a decimal. If you convert the binary, 0b1100 0000 is 192. If no button is pressed it will return a 192. However if you press the left button, the left most bit will turn to zero. This is 128 less, so 64 will be reported on the REPL console. If you press the right button, 128 should be reported and both will set the returned state to zero.

# Testing of a IO expanded - PCF8574
from microbit import *

PCF_ADDR = 0x20     # Use the base address of the PCF8574

# I2C send, function to simplify writing
def sendI2C(addr, value):
    # Convert sent value to a byte array, which is
    # required for I2C
    buf = bytearray(1)
    buf[0] = value
    # Send value
    i2c.write(addr, buf)
    # Short delay to send
    sleep(5)

def readI2C(addr):
    # Read in as a single byte and return
    v = i2c.read(addr, 1)[0]
    return v
    
# Input test
# Set P6 and P7 to high
buttonSet = 0b11000000
sendI2C(PCF_ADDR, buttonSet)
while True:
    v = readI2C(PCF_ADDR)
    print ("Button input = ", v)
    sleep(1000)

If you want to look at the returned value as binary or hex, use bin(v) or hex(v) in the print statement. Note that it will always trim leading zeros off the binary string, so when you press the left button, the binary string shrinks by 1 bit.

Combining Input And Output

That is all very well, but now the LEDs are broken. If we send our LED pattern the buttons will stop working. If we send our pattern to set the button state, the LEDs will stop working. If you can set the button states and the LEDs together, when you read back the values, you will also read back the LED state, meaning our 192, 128 and 64 for the buttons no longer hold true. Their values will be higher depending on which LEDs are currently showing. How can we handle both?

This is where the bitwise operators come in very useful. An OR essentially combines two binary values together. If we have one value for our LED state and one value to set the buttons, then OR them together before sending, the two values are combined and we can have both. For example if we want the third LED from the right showing and to set our button states we can can see what happens with the following table:

ValueP7P6P5P4P3P2P1P0In Hex
LED set000001000x04
ORButton set110000000xC0
Combined110001000xC4

What about reading values back? The AND function is often described as a mask. If we only want to know the state of P7 then we can make a binary string where only the left most bit is set to 1. If we AND this with the returned value we ‘mask’ out the other bits and can only look at the state of the one bit. If the whole value is zero then the button is pressed and if the button is not pressed, it should be 128. However we do not really care what the value is, just if it is not equal to zero. In the following table, P7 is being pressed but P6 is not.

ValueP7P6P5P4P3P2P1P0In Hex
Returned value010001000x44
ANDP7 mask100000000x80
Result000000000x00

Can you see what the result would be if P7 were not being pressed?

Lets put this into our code. Reading the result on the REPL console is slow, so as we have made sure we can still use the LED display on the Micro:Bit, lets make use of it. If we say the left button on P7 has a value of 2 and P6 has a value of 1. We can say if no button is pressed show a 0, if P7 is pressed, show a 2, if P6 is pressed show a 1 and show a 3 if both are pressed. Meanwhile, show our marching LEDs from the previous example. Also use hax values as this is more common when coding. Change the main loop to:

# Input test
# Set P6 and P7 to high
buttonSet = 0xC0
while True:
    # Set the initial pattern, right light on
    p = 0x01

    # Loop 6 times
    for x in range(6):
        # Combine the two values with an OR and
        # send to the Micro:But. Use the variable
        # s for send
        s = buttonSet | p
        sendI2C(PCF_ADDR, s)
        
        # Shift the pattern 1 place to the left
        p = p << 1 
     
        # Check the button state, use the variable
        # bState. Initially set it to zero
        bState = 0
        
        # Read a value in from the PCF8574
        v = readI2C(PCF_ADDR)
        
        # If that value AND the binary 1000 000 is
        # zero then button on P7 is being pressed
        if( ( v & 0x80 ) == 0 ):
            # Button is pressed, add 2 to bState
            bState = bState + 2
        
        # Now do the same for P6
        if( ( v & 0x40 ) == 0 ):
            # Button is pressed, add 1 to bState
            bState = bState + 1
            
        # Show the button state on the display
        display.show(bState)

        sleep(500)

We can make the button check and the display set even more efficient with the following code. We could even combine everything to one line if we wanted. Can you describe what is happening here (I’ve deliberately left the comments out) and can you make this a one liner? Remember, you can use the REPL console for debugging and print values as binary to see what is going on, e.g. print ("v = ", bin(v)). A pencil and paper may help too.

# Input test
# Set P6 and P7 to high
buttonSet = 0xC0
while True:
    # Set the initial pattern, right light on
    p = 0x01

    # Loop 6 times
    for x in range(6):
        # Combine the two values with an OR and
        # send to the Micro:But. Use the variable
        # s for send
        s = buttonSet | p
        sendI2C(PCF_ADDR, s)
        
        # Shift the pattern 1 place to the left
        p = p << 1 
     
        # Check the button state, use the variable
        # bState. Initially set it to zero
        bState = 0
        
        # Read a value in from the PCF8574
        v = readI2C(PCF_ADDR)
        
        a = v >> 6 
        b = ~ a
        bState = b & 0x03

        # Show the button state on the display
        display.show(bState)

        sleep(500)

Micro:bit LCD Display Screen

16×2 LCD module with I2C adapter and external power supply

A LCD screen is a great way to give more feedback to a user, either for a text message or values back from a sensor. Known as a 1602 LCD, this common display gives two rows of 16 characters to work with, and can scroll text. However it comes with two issues for the Micro:bit. First is that it needs 5v input and the second is it uses a lot of pins.

The first problem can be overcome by using an external power supply. The Micro:bit can only supply 3.3v. If you try powering a LCD screen from this, it will light up but if you can see anything at all, it will be very faint. There are a number of ways to supply 5v, one of the easiest is using a ‘3.3v 5v breadboard power supply module’. One of these comes with the Elegoo 37 sensor kit, but searching for the above description will find a number of other suppliers. These are usually quite cheap.

Supply the board with anywhere between 6.5v and 12v. A 5v and 3.3v will be supplied by the power output pins.

The second issue was the amount of pins used by the LCD module. The LCD requires 16 pins. While some of these are for power, plugging directly into the Micro:bit will not leave many free pins for other hardware. The easiest solution is to buy a I2C LCD module, pictured above the screen in the title image. To buy one, search for “I2C 1602 LCD”. You will often find screens with these already fitted.

Wiring it up

If you screen or your module has female headers, you can plug the module directly into the back of the screen. In the picture above, both the screen and the I2C module had male headers. Plug these into breadboard making sure the left most pin on the LCD (often marked ‘1’) lines up with the pin on the left of the module when it has it’s four pins on the side pointing out to the left.

From the power supply module, connect a 5v pin to Vcc on the I2C module and connect Gnd to a ground strip on the breadboard. You must connect this to Gnd on the Micro:bit. If you wish, you can connect a 3.3v pin from the power supply board to the 3v pin on the Micro:bit to power it, or you can power it via a serial cable.

The LCD interface module uses I2C, which is a common protocol that can be used to reduce connecting various modules to two wires each. So long as the devices have different addresses (don’t worry about this for now), you can connect multiple devices to I2C. The Micro:bit has two I2C pins to support this, 19 and 20. Connect SDA on the LCD module to pin 20 and SCL to pin 19.

Displaying text in your code

At the time of writing, there does not appear to be a common I2C LCD library, however ‘shaoziyang‘ has produced on at github, which works quite nicely (Thank you!).

The following code displays a hello message (to our dog) and counts up the seconds the Micro:bit has been running. If this does not work first time, check your wiring but also try changing LCD_I2C_ADDR from 63 to 39. Some modules use a different address:

from microbit import *
import time

LCD_I2C_ADDR=63

class LCD1620():
    def __init__(self):
        self.buf = bytearray(1)
        self.BK = 0x08
        self.RS = 0x00
        self.E = 0x04
        self.setcmd(0x33)
        sleep(5)
        self.send(0x30)
        sleep(5)
        self.send(0x20)
        sleep(5)
        self.setcmd(0x28)
        self.setcmd(0x0C)
        self.setcmd(0x06)
        self.setcmd(0x01)
        self.version='1.0'

    def setReg(self, dat):
        self.buf[0] = dat
        i2c.write(LCD_I2C_ADDR, self.buf)
        sleep(1)

    def send(self, dat):
        d=dat&0xF0
        d|=self.BK
        d|=self.RS
        self.setReg(d)
        self.setReg(d|0x04)
        self.setReg(d)

    def setcmd(self, cmd):
        self.RS=0
        self.send(cmd)
        self.send(cmd<<4)

    def setdat(self, dat):
        self.RS=1
        self.send(dat)
        self.send(dat<<4)

    def clear(self):
        self.setcmd(1)

    def backlight(self, on):
        if on:
            self.BK=0x08
        else:
            self.BK=0
        self.setdat(0)

    def on(self):
        self.setcmd(0x0C)

    def off(self):
        self.setcmd(0x08)

    def char(self, ch, x=-1, y=0):
        if x>=0:
            a=0x80
            if y>0:
                a=0xC0
            a+=x
            self.setcmd(a)
        self.setdat(ch)

    def puts(self, s, x=0, y=0):
        if len(s)>0:
            self.char(ord(s[0]),x,y)
            for i in range(1, len(s)):
                self.char(ord(s[i]))

lcd = LCD1620()
lcd.puts("Hello Benji!")
while True:
    lcd.puts("Running=" + str(running_time()/1000), 0, 1)
    sleep(500)

Functions in the library

FunctionUsage
clear()Clears the display
backlight(0 or 1)Setting the backlight to 0 turns the backlight off, 1 turns it back on again.
off()Turns the LCD off, thought the backlight stays on
on()Turns the LCD on
char(c,x,y)
char(65,x,y)
char(ord(‘@’),x,y)
Prints a single character at coordinates x,y
Prints the ASCII value 65 (capital A) at coordinates x,y
Using ord, prints @ at coordinates x,y
puts(s,x,y)
puts(“Hello World”, 0,1)
Writes a text string at coordinates x,y
Writes “Hello World” at the start of the second line.

Project: Rotary Encoder & Servo

Can we control a servo with a rotary encoder? Can we make it turn in the same direction as we turn the rotary encoder? What could we attach to the servo arm? Perhaps use this to control a crane or fork lift? Can it be attached to some lego to make something interesting?

Step 1: Set up the rotary encoder. Review Micro:bit Rotary Encoder and connect up the hardware. Run the test code to make sure the encoder is working.

Step 2: Can you turn this code into a function? Sit in the loop and call a function checkEncoder(), which will report back the direction (if any) it is being turned. Perhaps return 0 for stationary, 1 for turning clockwise and 2 for turning anti-clockwise. Consider returning two values to also report the state of the button.

Step 4: Connect the servo. Follow Micro:bit SG09 Servo and test the servo. Note, both examples use pin0 so you will have to make adjustments.

Step 5: Can you turn the servo in the same direction as the rotary encoder? You may need to write a function to tell the servo to move by X number of degrees. Try playing with different values for X and different sensitivities to control how fast the encoder turns the servo. Don’t forget to make sure you do not try to turn the servo beyond it’s limits.

Micro:bit Rotary Encoder

A rotary encoder, such as the common KY-040 above, is a knob that you can turn continuously either clockwise or anti-clockwise and detect which direction it is being turned. Types such as the KY-040 are known as incremental encoders. These will tell you the direction they are being turned, but can not report which direction they are pointing. The shaft can be pushed down to act like a push button. A common application for this is to navigate menu screens on devices such as 3d printers.

To connect a KY-040 to a Micro:bit, + and GND go to 3V and GND on the Micro:bit. The other three pins go to any digital input pins. In the following examples, SW goes to pin0, DT to pin1 and CLK to pin2.

For a detailed explanation of how rotary encoders work, see wikipedia. To use one, all you need to know is if CLK changes, then the encoder is being turned. If CLK is the same as DT (could be high or low), then it is being turned anti-clockwise. If CLK is different to DT it is being turned clockwise. Generally the sensitivity is changed with a sleep statement. The button on the top acts as a simple push button. The CLK and DT pins have their own internal resistors so we must disable the internal pull-up resistor to work. For a description about this, see Micro:bit Buttons. The one on the switch is still required.

This gives us a basic procedure to follow to read input from a rotary encoder:

  • Define the pins in use
  • Disable the internal pull-up resistors.
  • Read CLK and store this in a variable for the last known state of CLK.
  • Start a loop:
    • Read all three input pins
    • If CLK has changed since the last known state of CLK (lastCLK), the encoder is moving.
      • If CLK is not equal to DT it is moving clockwise
      • Otherwise it is moving anti-clockwise.
      • Wait, according to the sensitivity you want
      • Set lastCLK equal to CLK
    • Check for button press
    • Return to start of loop

In code form, this makes:

# Rotary encoder test
from microbit import *

RSW = pin0
RDT = pin1
RCLK = pin2

# KY-040 has its own pull up resistors.
# Disable the Micro:bit internal ones but
# ensure we use the one for the switch pin
RCLK.set_pull( RCLK.NO_PULL )
RDT.set_pull ( RDT.NO_PULL )
RSW.set_pull ( RSW.PULL_UP )

clkLast = RCLK.read_digital()

print ( "Ready to start" )
display.show ( Image.HAPPY )

while True:
    clk = RCLK.read_digital()
    dt = RDT.read_digital()
    sw = RSW.read_digital()

    if ( clk != clkLast ) :
        if ( clk != dt ) :
            print ( "Clockwise" )
            display.show(Image.ARROW_E)
            sleep(300)
        else:
            print ( "Anti-clockwise" )
            display.show(Image.ARROW_W)
            sleep(300)

        clkLast = clk
    else:
        # Has the switch been pressed?
        if (sw == 0) :
            display.show('X')
            sleep(500)
        else :
            display.clear()

There is a problem with this approach. If you try it and watch either the arrows or serial lines, you find while turning the encoder, you occasionally get the wrong direction reported. This is known as ‘switch bounce’ and is often caused by cheaper components in encoders such as the KY-040. There are many ways to look at solving this, either in hardware using capacitors or in code. In a few tests with capacitors, I did not find a significant difference. However I always found that the wrong direction was reported once in a row, never twice.

A ‘quick and dirty’ solution is to wrap up the detection into a function which takes a number of readings then reports back which direction was detected the most. This does mean it will take a little longer to react to a turn or change in direction, but only by a fraction of a second. In the code below, the function readEncoder returns a pair of values for the direction and the state of the switch. A 0 represents no movement, 1 for clockwise movement and -1 for anti-clockwise.

In python, you can build lists of values in brackets with a comma, e.g. a=(2, 4, 8). To access the first element of the list, use square brackets, a[0]. For the third value, a[2]. This allows us to return the direction as the first value and the switch state as the second. The function in the below code takes a few attempts to read the encoder direction and returns a pair of values, the first for the direction and the second for the switch state.

# Rotary encoder test
from microbit import *

RSW = pin0
RDT = pin1
RCLK = pin2

# KY-040 has its own pull up resistors.
# Disable the Micro:bit internal ones but
# ensure we use the one for the switch pin
RCLK.set_pull(RCLK.NO_PULL)
RDT.set_pull(RDT.NO_PULL)
RSW.set_pull(RSW.PULL_UP)

def readEncoder():
    # Return a pair of values direction and switch state
    #   0 = no movement
    #   1 = clockwise
    #   2 = anti-clockwise
    
    # Set sensitivity - how long we wait between readings in ms
    sens = 50
    # Accuracy is how many checks we perform. This should at least
    # be 3. Note that the more checks, the longer it will take and
    # the greater the chance of the direction changing while we checks
    acc = 5
    
    # Record the last known state of the CLK line
    clkLast = RCLK.read_digital()
    
    # Set direction counters, clockwise and anti-clockwise
    cw = 0
    ac = 0
    
    # Checking loop
    for i in range(0, acc):
        # Read data from the encoder
        clk = RCLK.read_digital()
        dt = RDT.read_digital()
        sw = RSW.read_digital()
        
        if(clk != clkLast):
            # print ("clkLast = "+str(clkLast)+" - clk = "+str(clk)+" - dt = "+str(dt))
            if(clkLast != dt):
                # Clockwise, add 1 to clockwise count
                cw = cw+1
            else:
                # Anti-clockwise, add 1 to anti-clockwise count
                ac = ac+1

            # Record the last known state of the clock
            clkLast = clk
            # Wait according to sensitivity setting
            sleep(sens)
            
    # Initially assume the direction is 0
    d = 0
    # Check the clockwise and anti-clockwise counters
    # If they are the same, no change to d is made. We assume
    # the encoder is not moving
    if(cw > ac):
        # More clockwise movement than anti-clockwise
        d = 1
    elif(ac > cw):
        # More anti-clockwise movement than clockwise
        d = -1

    # Return data as a pair
    # We can return the value of the switch directly
    return (d, sw)
# End of readEncoder

print("Ready to start")
display.show(Image.HAPPY)
sleep(1000)

# Main loop
while True:
    r = readEncoder()
    
    # Check the direction and report. The screen
    # will show the last recorded direction
    if(r[0] == 1):
        # Clockwise movement
        print("Clockwise")
        display.show(Image.ARROW_E)
    elif(r[0] == -1):
        # Anti-clockwise movement
        print("Anti-clockwise")
        display.show(Image.ARROW_W)
     
    # Check for a switch press
    if(r[1] == 0):
        display.show('X')
        sleep(500)
        display.clear()

Micro:bit Buttons

About Buttons

Microcontrollers such as the Micro:bit all do one similar thing, accept an input (some sort of ‘message’ from the outside world), process (run some code) and output (show something to the outside world). The most common type of input everyone uses is a button.

What is a button? Essentially a button is a couple of bits of wire or metal plate that touch together when pressed. This allows electricity to flow between the connectors at either side, making the connection. There are a few different types of button each with different properties. Some stay on or stay off after being pressed (or toggled), like a light switch. Some only make a contact when the button or lever is being moved and some you push to break the circuit. The types we are looking at in this tutorial are momentary push buttons, which only make a connection while they are being pressed. The standard circuit symbols below shows a little about how they work.

There is a connection at either side, when the buttons are pressed, the two connections are joined and electricity flows.

Micro:bit Onboard Buttons

The easiest two buttons to use are the two build onto the Micro:bit board itself, labelled A and B. There is a lot of information on the Micropython tutorial page about using these. When using the microbit library, two objects are defined, button_a and button_b. You can check if these are pressed with the is_pressed() function. The following code loops checking for a button press then points an arrow to the last pressed button:

from microbit import *

while True:
    if ( button_a.is_pressed() ) :
        display.show(Image.ARROW_W)
    if ( button_b.is_pressed() ) :
        display.show(Image.ARROW_E)

While quick and easy to use, this does not help us add other buttons. By wiring in a button, it will not automatically create a button object. External buttons are connected to pins, but consider the Micro:bit pinout diagram. When you look at this, the buttons A and B are connected to pins 5 and 11. For us, this means two things. First is that when connecting hardware, if we want to use buttons A and B, we should not use pins 5 and 11 for anything else. But it also means we can read the buttons as if they were digital pins. When we read digital pins, the result can be a 1 or a 0. The following will show on the display the value of the A pin, pin 5:

from microbit import *

while True:
    display.show( pin5.read_digital() )

Try it. What does the value change to when you press the button? We often think of 1 as being on and 0 as being off, but find this works the other way round. There is a reason for this which will be revealed below. For now, as long as we are happy that 0 means a button is being pressed, if we want to rewrite the first program to use pin numbers, we can write it as:

from microbit import *

while True:
    if ( pin5.read_digital() == 0 ) :
        display.show(Image.ARROW_W)
    if ( pin11.read_digital() == 0 ) :
        display.show(Image.ARROW_E)

External Buttons

We can add external buttons to the Micro:bit to give a larger number of buttons, or to use different types of buttons. One end can connect to a data pin, but what do we connect the other end to? Consider the circuit diagram below:

pin0 is said to be ‘floating’. It is neither connected to ground (0v) or the positive 3v. If we read the value of this, it could be anything. Putting an open button on it would leave it in the same state. Imagine a kite free to fly in the wind. If we pin it to the ground, then we know it is on the ground. If it were possible to pin it into the air, again we would know where it was. Anything else, it is at the mercy of the wind and can keep flying and crashing all day long.

Electronic inputs are the same. Unless we decide to ‘pin’ them to something, we can not predict what state they would be in. In practice, I have found an unconnected Mirco:bit input always seems to be zero. The same is not true of other micro controllers, such as a Raspberry Pi or an Arduino, so it is good practice to make sure you know what state your inputs are in. This is done with the use of pull-up or pull-down resistors.

In the diagram on the left, we are using a pull-down resistor. (10k ohm is a common value for this.) When the button is not pressed, the resistor connects pin0 to ground, meaning a read_digital function will always return 0. However when the button is pressed, it connects pin0 to +3v, which the Micro:bit reads as 1. The resistor limits how much of the current can flow between +3v and GND, leaving a higher current to be detected on pin0.

In the diagram on the right, we do the opposite. pin0 is pulled high (read as 1), unless the button is pressed, at which point it is pulled low (read as 0). This means in our code, we detect a button press as a 0 and no button press as a 1.

The Micro:bit has a built in pull-up and pull-down resistors that you can turn on in the code, using the command set_pull. If you want to use your own external resistors with buttons, then you must disable these:

pin0.set_pull(pin0.NO_PULL)

However, as they are there it is best to make use of them. If you remember, reading the internal buttons give a 1 when not pressed and a 0 when pressed, meaning they must be using the internal pull-up resistor. If we use external buttons on pins 0 and 1, we can use the same code again.

from microbit import *

# Set the pull up state for the internal resistors
pin0.set_pull(pin0.PULL_UP)
pin1.set_pull(pin1.PULL_UP)

while True:
    if ( pin0.read_digital() == 0 ) :
        display.show(Image.ARROW_W)
    if ( pin1.read_digital() == 0 ) :
        display.show(Image.ARROW_E)

Button Modules

You can also use button modules such as the one in the middle of the picture above. Going from left to right, pin S connects to one of the IO pins of the Micro:bit, the middle to 3v and the right pin to GND. If you look closely, you can see a small black rectangle labelled 103 connecting S and 3v. This is a small pull-up resistor. As this has its own resistor, we need to make sure we disable the internal resistor. We can then check the button state after connecting it to pin2.

from microbit import *

# Disable the internal pull up
pin2.set_pull(pin2.NO_PULL)

while True:
    display.show ( pin2.read_digital() ) 

Button modules may come on their own or be included as part of a joystick module, game controller package, etc. If the instructions do not say if a pull-up or pull-down resistor is used, you should be able to check with the the above code.

Micro:bit SG09 Servo

SG09 servo

Servos are small motors with gearing that can add a high amount of torque (turning strength), and reliably turn to a precise angle. Servos such as the SG09 are small, cheap, run on low power and can rotate around 180 degrees, or a half circle. This makes them different to a normal motor which will spin round. You can commonly use this for things that need to turn by small amounts, such as a joint in a robotic arm, to lift the arm of a barrier or to steer a radio controlled car.

The SG09 only requires 3 connections two for power and one for data/control, but it does require 4.8v to operate. As the Micro:bit can only produce low current and 3v, an external battery pack is required. In the above wiring, brown is ground, red is +4.8v and orange is control. Connect a 3xAA battery pack to the two power pins and control to one of the Micro:bit pins, as shown below. Note a 4xAA battery pack is shown on the diagram.

There are a number of pages on controlling the SG09 with a Micro:bit, however none of these worked when I tried this. The servo operates by receiving pulses of 50Hz (50 pulses per second). If we divide one second by 50 pulses, we get a time of 20ms between pulses. We start our code by defining this. Next, if we write the analog value 1 to the servo, it will turn all the way to it’s clockwise limit. (If you send a 0, it does nothing.) Sending a high enough value will turn the servo the other way.

If you try a value of 150 or higher, you will feel the servo straining beyond it’s limits and you could damage it. Start with a low value for maxLimit in the following code (say 80) and gradually increase it until the motor just reaches it’s anti-clockwise limit. I found 122 to be a figure which does not strain the servo but reaches the other side.

from microbit import *

servo = pin0

# Motor requires a 50 Hz signal. 1 second / 50 gives 
# a pulse every 20 ms. Set this as the analog period
servo.set_analog_period(20)

maxLimit = 122


while True:
    servo.write_analog(0)      # Set the servo to 90 degrees - the middle
    sleep(500)
    for i in range(10,maxLimit):
        servo.write_analog(i)    # Turn the servo to the clockwise limits, 0 degrees
        sleep(50)
 

As the servo moves between 0 and 180 degrees, and values between 1 and 122 causes this movement, we can work out what value to analog write to achieve by dividing the angle we want by 180 then multiplying this by 122. We can wrap this up in a function and then call it to move the servo to a desired angle. The below code moves the servo to its limits and back again, like a windscreen wiper.

from microbit import *

servo = pin0


def servoAngle(s, a) :
    maxLimit = 122

    # Set the values into a sensible range
    # 1 is the smallest value
    if a < 1 :
        a = 1
    # Max limit is the highest value
    if a > 180 :
        a = 180
        
    v = (a/180) * maxLimit
    s.write_analog(v)

# Motor requires a 50 Hz signal. 1 second / 50 gives 
# a pulse every 20 ms. Set this as the analog period   
servo.set_analog_period(20)

while True:
    servo.write_analog(1)
    sleep(1000)
    #servo.write_analog(122)
    servoAngle(servo, 180)
    sleep(1000)

    

Note that this angle is an approximation and not exact. The design specs of the servo detail exactly what sort of pulses to send for precise control. This gives a very quick and simple way of achieving something close to what we require for a lot of applications.

Project: Neopixel game

Given what we have learned about controlling the joystick and neopixel hardware in Micro:bit Joystick Module and Micro:bit Neopixels, can we construct a simple game? We need to consider the hardware, animation, random position generation and collision detection.

Step 1: Construct the hardware. Can you connect both the Neopixel grid and the joystick to the micro:bit? Is there any testing we can do to make sure they work?

Step 2: Start the game by positioning the ‘player’ in the top left corner. Pick a colour for the player and light up that LED. Be sure to use variables to describe the player position as we are going to move this soon.

Step 3: Can we move the player around with the joystick? In a loop detect joystick movement then adjust the coordinates of the players position accordingly. Be sure to put a delay in the loop, otherwise the player will move very fast! What happens when the player reaches the edge of the display? Can we stop the player leaving the screen?

Step 4: Can you make something happen to the player when you press the joystick down? Perhaps flash?

Step 5: Using a random number generator, can you put some ‘food’ at a random point on the screen? How can we check this food does not land on top of the player? Do we need to store the food coordinates for when we redraw the screen?

Step 6: Now lets look at collision detection. Can you detect when the player lands on the food? Can it ‘eat’ the food and then new food appears at another random location? Can you keep a score of food eaten on the micro:bit LEDs? End the game when the you reach 10 bits of food.

Step 7: Can you make the game more interesting or use what you have learned to make a different game? What if there were two players taking it in turn to move? What if the food made the player grow like a worm? Could painting the screen a different colour be part of the game?

Micro:bit Neopixels

NeoPixels are Adafruit’s brand of individually addressable RGB LEDs. However the name has become common use to describe most kinds of WS2812 (and similar) LED arrays. Each LED can have it’s colour set individually using a RGB value. This can lead to interesting light and animation displays. The above image is not an Adafruit branded matrix, it was cheaper and has one ‘stuck’ LED. Adafruit’s offerings are generally considered to be higher quality. For this article, all similar arrays will be referred to as Neopixels.

Neopixels can come in a large range of shapes and sizes, rings, strips, arrays, long strings or as individuals. They come with 3 connections, Vcc (3v), GND and Data/IN. Data connects to any pin which supports PWM. It is not clear from the docs if all pins support PWM, though pins 0-3 certainly do. Most units also have output connections for power and data, allowing you to daisy chain units together for large LED displays. Chaining more units together may require the use of external power supplies.

Micropython has a Neopixel library, which has 2-3 simple steps:

  • Clear the display (optional)
  • Configure colour values for each pixel
  • ‘Show’ the display.

The following code clears the display, then sets the first three pixels to be red, green and blue. Like most things in computing, counting starts at zero not one:

# NeoPixel Demo
from microbit import *
import neopixel

# Init a 64 Neopixel display on Pin0
np = neopixel.NeoPixel(pin0, 64)

# Clear the display
np.clear()

# Set the first pixel to red
np[0] = (255, 0, 0)
# Set the second pixel to green
np[1] = (0, 255, 0)
# Set the third pixel to blue
np[2] = (0, 0, 255)

# Show new display settings
np.show()

This version of the neopixel library does not allow the adjustment of brightness. The only supported method is to lower the RGB values. Adafruit does have a library which does allow brightness lowering.

Pixel numbering

Different Neopixel shapes have different numbering schemes to determine how your pixels are laid out, the following code will flash each pixel in turn:

# NeoPixel Demo
from microbit import *
import neopixel

# Init a 64 Neopixel display on Pin0
np = neopixel.NeoPixel(pin0, 64)

for i in range(64):
    # Clear the display
    np.clear()

    # Set the pixel to red
    np[i] = (64, 0, 0)

    # Show new display settings
    np.show()
    sleep(300)

From this, I could determine the layout on the left of the below image for my 8×8 matrix.

It would be more useful if we could address the pixels using coordinates as demonstrated on the right, especially if we want to do an animation or move a cursor. The red pixel has the coordinates (4,3), pixel number 28. The green pixel is (1,6), pixel 49. The formula p=8*y + x can be used to calculate the pixel number. If we wrap this up in a function, we can address our pixels using coordinates. The following code displays the red and green pixel in the right image.

# NeoPixel Demo
from microbit import *
import neopixel

# Init a 64 Neopixel display on Pin0
np = neopixel.NeoPixel(pin0, 64)

# Function neoCoord
def neoCoord(n, x, y, col):
    # Accept a neopixel object, x and y values then set the pixel to be that colour
    # Calculate pixel number
    p = y*8 + x
    
    # Set the pixel
    n[p]=col
    
# Set red and green pixels by coordinate
# Clear the display
np.clear()

neoCoord(np, 4, 3, (128,0,0))
neoCoord(np, 1, 6, (0,128,0))

# Show new display settings
np.show()

You may need to write your own formula in neoCoord depending on your display. It may be rectangular or you could join two 4×4 displays together to make a 8×4 display.

Displaying Images

The onboard LEDs can be sent an image to display, either using a pre-defined image or defining your own. See the tutorial here. Wouldn’t it be nice if we could do the same? With large displays we could use images smaller than the overall display. We tell it where we want the image to appear, what colour and what image. If we define this is a binary bitmap (using 0 and 1 only) and code correctly, we need not worry if the image goes off the ‘screen’. We can use this to produce nice animation effects and call the function multiple times with different colours to produce multi-colour images.

neoImage takes a Image object and prints it in the colour col, at the coordinates specified. It is happy if that prints part of the image (including the starting point) off the screen, as this allows images to scroll on and off the screen. Use negative coordinates to go off the top or left of the screen. The following code includes the image displaying function neoImage along with a short bit of code to scroll a TIE fighter across the screen twice.

# Neopixel Images
from microbit import *
import neopixel

# Init a 64 Neopixel display on Pin0
np = neopixel.NeoPixel(pin0, 64)

# Set the display width and height. We need this for correctly
# displaying images
dwidth = 8
dheight = 8

tie = Image("10001:"
            "10101:"
            "11111:"
            "10101:"
            "10001")
            
# Function neoCoord
def neoCoord(n, x, y, col):
    # Accept a neopixel object, x and y values then set the pixel to be that colour
    # Calculate pixel number
    p = y*8 + x
    
    # Set the pixel
    n[p]=col

# Display an image on a neoPixel matrix
def neoImage(n, px, py, col, img):
    # n=NeoPixel object, px & py=top left positional coordinates,
    # col=colour and img is a binary image object
    # The position may be negative to allow an image to scroll onto
    # the screen

    # Find dimensions
    w = img.width()
    h = img.height()
    
    # Set coordinates to start plotting (x and y)
    x = px
    y = py
    
    # Run through each row of the image as iy (image y)
    for iy in range(h):
        # Run through each column in the image as ix (image x)
        # Reset x to start of row
        x = px
        for ix in range(w):
            # Find the value of that pixel
            v = img.get_pixel(ix, iy)
            if(v > 0):
                # Check the pixel is in the range of the screen
                # If not, ignore
                if(x >= 0 and y >= 0 and x < dwidth and y < dheight):
                    # Set that pixel
                    neoCoord(n, x, y, col)
                
            # Increase x
            x = x + 1
        
        # Move to the next row
        y = y + 1

    
# Scroll the image onto the top left and across the screen
for i in range(-5,9):
    # Clear the display
    np.clear()
    
    # Display the image
    neoImage(np, i, 0, (0,0,80), tie)

    # Show new display settings
    np.show()
    
    # Pause
    sleep(200)
    
# Scroll back on the from the other side at the bottom
for i in range(9,-6,-1):
    # Clear the display
    np.clear()
    
    # Display the image
    neoImage(np, i, 3, (0,0,80), tie)

    # Show new display settings
    np.show()
    
    # Pause
    sleep(200)