Projects

Make it Glow

Mini Coding Projects with the GlowBit and micro:bit

Liam Davies

Issue 45, April 2021

This article includes additional downloadable resources.
Please log in to access.

Log in

We show you how to create fun games and useful gadgets using the GlowBit from Core Electronics and a micro:bit. A great way to get into coding and electronics.

BUILD TIME: 1 HOUR PER BUILD
DIFFICULTY RATING: Intermediate

We recently got our hands on a few GlowBit modules from our friends at Core Electronics. The GlowBit’s are made right here in Newcastle, NSW (In Australia for our overseas readers), and feature 13 RGB LEDs that can be controlled with a variety of microcontrollers, including Arduino, the micro:bit and Adafruit’s Circuit Playground.

The LEDs are the newest version of the popular WS2812Bs, version 5. These little guys pack a punch with incredibly bright and vivid RGB LED colours accessible using just one data pin.

By themselves, these units aren’t particularly complex or smart. They’re simply 13 LEDs in a semicircle shape with 15 degrees of spacing. However, since these LEDs are addressable, it opens up the possibility of controlling both the brightness and the colour of individual lights.

Core Electronics has also included four small breakout header pins for daisy changing GlowBits. Two for power, and two for receiving and sending data to linked strips. These strips should work with just about any maker microcontroller, although differing libraries may be needed to get them working appropriately. We’re using the BBC micro:bit, so using the simple MicroPython programming language will help get our programs off the ground fast!

In terms of software, we’re using the popular Mu programming environment. It’s available from https://codewith.mu and requires virtually no setup to work with a micro:bit. If learning how to code in MicroPython sounds interesting, read our Intro to micro:bit series we featured in Issue 33 and 34. For these projects, we’re using some more advanced programming techniques to get some cool graphical effects running with the GlowBits.

If you’re using a micro:bit, the GlowBit can be connected to the board with three alligator clips with the included gold clipping holes. It just needs a wire for power (3.3V) data and Ground. We colour coded these red, white and black respectively. And that’s it! Alternatively, solder four male headers into the header slots and connect jumper wires to your microcontroller of choice.

To demonstrate, we’re going to be making some games and gadgets with these lights that should inspire some further ideas!

Project 1:

Rainbow Nightlight

For our first project, we’re making a nightlight that cycles through the colours of the rainbow, which gets brighter as the ambient lighting around the micro:bit gets dimmer! This is a great project to learn how to use the LEDs on the GlowBit and interface it with the sensors on the micro:bit.

In our Mu editor window, we first need to import libraries required for making our nightlight to work. The ‘microbit’ library provides an interface to communicate with sensors directly on the micro:bit, and the ‘neopixel’ library allows us to display information on connected NeoPixel strips, such as the WS2812Bs on our GlowBit. We also need to import ‘utime’, a library which will come in handy later. Finally, we initialise our NeoPixel object, which requires two arguments: a pin (Pin0 if you have the data cable connected to it on the micro:bit) and the number of LEDs on your strip (13).

from microbit import *
import neopixel
import utime
np = neopixel.NeoPixel(pin0, 13)

The looping code we used is shown on the next page. This will run constantly on our micro:bit, so it’s useful for providing continuous functionality as is required for the nightlight. We need to first define a threshold variables, which simply specifies how sensitive our nightlight is to turning on and off. Higher values requires brighter lights to turn it off. We left ours at 15, but feel free to play around with this until you find a value that works well.

In our while loop, we can now write our code that will run indefinitely. The microbit library includes support for using the inbuilt light sensors with the ‘read_light_level’ function, returning a value between 0 and 255. Then, we test whether the threshold value has been broken, and if so, set our value variable – set between 0 and 1 depending on how bright we would like our display. The darker the area the micro:bit is set up in, the brighter the LEDs should be!

Finally, we loop through each LED and create a rainbow colour with the brightness value “value”. The wheel function we’re using converts a position on the colour wheel to an RGB (Red, Green, Blue) colour. This isn’t included with any micro:bit libraries, we had to add in code provided from Adafruit. How this code works is out of the scope of the project, but check it out in the project files if you’re interested.

The utime.ticks_ms() provides a counter in milliseconds, which we divide by 15 to slow down the colour changing. Increase this value if you would like to slow it down further.

threshold_value = 15
while True:
  light_level = display.read_light_level()
  value = 0
  if(light_level < threshold_value):
    value = 1 - (light_level / 255)
  for x in range(0, 13):
    np[x] = wheel(utime.ticks_ms()/15, value)
  np.show()

Testing

After hitting the “flash” button at the top of the Mu IDE, the micro:bit should kick into action and light up the GlowBit. When the room lights are turned on, if the threshold is set correctly, the LEDs should turn on and off. We found quite quickly that brightness isn’t a problem with the GlowBit. It’s capable of lighting up a reasonably sized room!

Later, we made some fixes to the nightlight so that it would “fade” in and out instead of suddenly turning on and off.

Check out the videos of it working on our social media!

Project 2:

Bubble Level

Next up, we’re going to make a digital bubble level that illuminates LEDs according to how straight the micro:bit is facing upwards. The micro:bit includes an accelerometer, which we can use to find its orientation.

To convert the orientation of the micro:bit’s accelerometer to lighting up the LEDs on the GlowBit, we need to map the accelerometer reading to correspond to one of the 13 LEDs on the GlowBit we’re using. The accelerometer of the micro:bit has a reading range of -1024 to 1024, representing negative and positive acceleration. If it is 0, we can read this as pointing directly upwards.

from microbit import *
import neopixel
import math
import time
np = neopixel.NeoPixel(pin0, 13)
while True:
  acc = int((-accelerometer.
get_x() + 1024) * (12/2048))
  for i in range(0,13):
    light_val = 0
    if acc == i:
  light_val = 255
    np[i] = (0, light_val, 0)
    np.show()

Here's the first version of our Bubble Level code. Within its looping code, we’re converting the accelerometer value of the X-axis to an integer between 0 and 12 (i.e. for each light on the GlowBit). Then, we loop through each of the LEDs, and if the value we read matches the current LED, we light it up green. This is a very simple approach to lighting up the LEDs depending on the angle. Feel free to play around with the numbers to produce different effects!

The next couple of steps for this project are completely optional and only contribute to the visual appeal of the Bubble Level. There are some more maths involved, so if you’re only starting out feel free to skip this section. We want to smooth out the LEDs on the GlowBit by fading between them depending on the angle. Currently, turning the micro:bit causes the LEDs to illuminate in steps, making it look rather jarring. To smooth it out, we’re going to use a mathematical function to generate a curve that is highest in the middle. So, when applied to the LEDs across the GlowBit, it will look like this:

We won’t go into the all the details, but here is the MicroPython function we wrote to make it work:

def smooth_function(x, steepness, offset):
  return int(255/math.pow
(1+math.pow(x-offset,2),steepness))

The ‘x’ parameter is which LED we are currently illuminating, the ‘steepness’ parameter is how steep the brightness curve is, and finally, ‘offset’ is the position of our accelerometer reading. Hence, it is the peak of our curve.

We aren’t done yet! We also added a running average that will help remove some of the jerky behaviour of the accelerometer readings. Since the micro:bit will respond to any small changes in movement, this will smooth out the displayed angle at the cost of response time.

Here is our final looping code with averaging inbuilt:

running_average = 0
averaging_samples = 5
while True:
  #Convert the raw readings to the 
range of our LEDs (0-12)
  acc = (-accelerometer.get_x() + 1024) * (12/2048)
  #Average our accelerometer readings 
for more smooth LEDs.
  running_average = (acc + running_average * 
(averaging_samples-1))/averaging_samples
  #Print readings to the plotter.
  print((acc,running_average))
  for i in range(0,13):
    #Get light value and apply 
to the green channel.
    light_val = smooth_function
(i,3.5,running_average)
    np[i] = (0, light_val, 0)
    np.show(

Testing

It’s ready to go! Hit the “Flash” button at the top of the Mu editor to upload the light code to the micro:bit, and voila, our new digital bubble level works! Tilting the micro:bit one way or another updates the LEDs across the GlowBit.

We also checked out the plotter on the Mu editor to verify our averaging system works.

Green is Raw Accelerometer Data, Blue is Averaged Data (5 samples)

Notice that, while the blue line is much smoother than the green line (the raw readings), it is slower to respond to any changes in micro:bit movement. If we take too many samples for the data, tilting the micro:bit will take longer to update the display on the GlowBit. Feel free to mess about with the variables to change the behaviour of our bubble level, including it's colour and responsiveness.

Project 3:

Glow Pong

Let’s make a multiplayer game of Pong! This is just a 1D-version of the classic game, with two paddles and a ball bouncing between them. We’ll use the micro:bit’s inbuilt pushbuttons to act as the bouncing trigger, one for each player. We’ll make LEDs represent the balls and paddles - If the player presses their button when the ball isn’t over the paddle, they lose and a point is given to the opposing player.

First, we need to set up our global variables. These will control how hard our game is, what colours the players are and the current state of the game.

Note: While we’ve included the majority of our code here, there are some smaller functions that we’ve omitted for sake of simplicity. You can check out the rest in the project files.

from microbit import *
import neopixel
np = neopixel.NeoPixel(pin0, 13)
paddle_size = 2
paddle_colour_a = (100, 0, 0)
paddle_colour_b = (0, 0, 100)
goal_color = (255, 120, 0)
ball_colour = (0, 100, 0)
paddle_pos = 6
cur_ball_speed = 0
ball_speed = 200
travelling_right = True
player_a_score = 0
player_b_score = 0
harder_increase_level = 0.9

Most of the variables are configurable however you’d like the game to work. We found that the maximum brightness of the GlowBit’s WS2812B LEDs are far too bright to look at directly, so we dialled it down from 255 to 100 instead. The other parameters can be left as is, however, if you wish to change the difficulty of the game, play around with the ball_speed and the harder_increase_level variable. These control how much faster the ball increases speed as a round continues.

The following code essentially handles what state our game is in and how to illuminate the LEDs accordingly. When the game is won by a player, or a new round is required, we call the below functions to reset the variables and let the micro:bit continue running. Our game_win function also flashes the entire GlowBit strip to the winning player’s colour.

For readers who are still learning Python/MicroPython, you may be wondering what the ‘global’ keyword is used for at the beginning of the game_win function. This is intended so that the global variables we defined at the beginning of the program are accessible inside the function, otherwise they would be treated as local (i.e. only accessible inside the function) and would throw up all sorts of errors.

def new_round():
  global cur_ball_speed, ball_speed, paddle_pos
  cur_ball_speed = ball_speed
  paddle_pos = 6
new_round()
def game_win(player_a):
  global paddle_colour_a, paddle_colour_b, player_a_score, player_b_score
  for a in range(0, 3):
    for i in range(0, 13):
      if player_a:
        np[i] = paddle_colour_a
      else:
        np[i] = paddle_colour_b
    np.show()
    sleep(500)
    for i in range(0, 13):
      np[i] = (0, 0, 0)
    np.show()
    sleep(500)
  player_a_score = 0
  player_b_score = 0
  new_round()

The following code is the “bread and butter” of our Rainbow Pong game. This code handles the core game logic, including deciding when a player wins a round and when to bounce the ball back the other direction. When a player bounces the ball back to the opposition, we increase the ball’s speed so someone eventually wins. There are a lot of IF conditionals here, which is for detecting when the ball has gone offscreen, or a player has accidentally pressed their button when the ball isn’t over their paddle.

while True:
  while True:
  if(button_a.was_pressed()):
    travelling_right = True
    cur_ball_speed *= harder_increase_level
    if paddle_pos > 1:
      round_win(False)
  if(button_b.was_pressed()):
    travelling_right = False
    cur_ball_speed *= harder_increase_level
    if paddle_pos < 11:
      round_win(True)
  if(travelling_right):
    paddle_pos += 1
  else:
    paddle_pos -= 1
  if(paddle_pos > 12):
    round_win(True)
  if(paddle_pos < 0):
    round_win(False)
  fill_paddles()
  for i in range(0, 13):
    if(i == paddle_pos):
      np[i] = ball_colour
    elif (i >= paddle_size and 
i <= 12 - paddle_size):
      np[i] = (0, 0, 0)
  np.show()
  sleep(cur_ball_speed)

We’re nearly there! We now just need to write the code for when a player wins a round (i.e. the opponent presses their button at the wrong time). We wanted to design a cool bar display where each player’s LEDs ‘climb’ towards a golden LED at the top of the GlowBit when they score. As a goal is scored, the score screen comes up and displays each player’s score at the left and right sides of the GlowBit. Any new score point flashes its corresponding LED to highlight who won that round. As usual, all parameters can be modified in the global variables to change the behaviour.

def round_win(player_a):
  global paddle_colour_a, paddle_colour_b, player_a_score, player_b_score
  np.clear()
  new_round()
  np[6] = goal_color
  for i in range(0, 6):
    if i < player_a_score:
      np[i] = paddle_colour_a
    if i < player_b_score:
      np[12-i] = paddle_colour_b
  blinking_index = 0
  color = (0, 0, 0)
  if player_a:
    blinking_index = player_a_score
    color = paddle_colour_a
  else:
    blinking_index = 12-player_b_score
    color = paddle_colour_b
  for a in range(0, 3):
    np[blinking_index] = color
    np.show()
    sleep(500)
    np[blinking_index] = (0, 0, 0)
    np.show()
    sleep(500)
    
  if player_a:
    player_a_score += 1
  else:
    player_b_score += 1
  
  if player_a_score > 6:
    game_win(True)
  elif player_b_score > 6:
    game_win(False)

Testing

Once this was uploaded, we gave it a try with some friends and found it worked really well for a simple project with effectively two buttons and a couple of LEDs. It’s an easy way to develop a fun game to show off the capabilities of the GlowBit system.

Red winning, 3 to 2.
Red wins the game.

Troubleshooting

While these GlowBits and micro:bits are pretty bulletproof, there are a couple of things to keep in mind when using them with your projects so everything works as expected.

Always triple-check your three connections to your GlowBits, as incorrectly connecting them will at best prevent them from illuminating and at worst destroy the WS2812B LEDs.

We suggest that you don’t daisy chain any more of the GlowBits onto a single micro:bit, as it’s internal regulators will likely not be able to supply the current required by these additional LEDs.

If you’ve checked all of your hardware, the code may be at fault. Check you are uploading to the correct COM port (although Mu should generally pick this up) and the micro:bit’s yellow ‘upload’ LED rapidly blinks.

When there are errors with your code, your micro:bit will scroll errors across the 5x5 LED display as text. This can be a pain to read, so we suggest opening up the REPL console at the top of the Mu editor window and check the errors the MicroPython console throws out. Since MicroPython is not a compiled language, unlike Arduino, you will find that some errors don't show up until the code responsible is executed.Check the global parameters and variables in the program to make sure the code is running as expected.

WHERE TO FROM HERE?

We had a blast experimenting with these GlowBits. They’re a great way to mess around with addressable LEDs without messing about with soldering bendable strips. They can even be added into your own bigger 3D-printed electronics projects since Core-Electronics has included mounting holes in each GlowBit.

If you’ve got a spare micro:bit or Arduino lying around, they’re a good way of displaying bright vivid LED colours fully controllable by code.

For more project ideas, you can visit the GlowBit page on the Core Electronics website: https://core-electronics.com.au/glowbit-rainbow.html. Some of these include an electronic dice, and electronic compass.

If you are the proud owner of a Raspberry Pi Pico, you can also check out the Core Electronics video tutorial: https://core-electronics.com.au/videos/how-to-use-ws2812b-rgb-leds-with-raspberry-pi-pico

Shopping List:

GlowBit rainbow and micro:bit are available from Core Electronics: https://core-electronics.com.au

Liam Davies

Liam Davies

DIYODE Staff Writer