Minecraft + Lego

Let’s take a closer look at the Lego kits. It’s all there, from the small hut that will let you survive your first night, to the zombies that want to eat you, and the creeper who wants to blow you up. And, of course, a Minecraft kit wouldn’t be complete without torches. When building the kit, I noticed that the torch was hollow, and that the base was roughly 3mm in size, perfect for a small LED. Using a breadboard, a resistor, a white LED and a little bit of putty, I noticed that the light could shine through, adding a warming glow to the kit. However, torches aren’t stable light sources, they tend to flicker, so it was time to investigate.

To simulate a flickering candle, you normally need two red LEDs and a yellow or orange LED. Three LEDs don’t fit, and besides, the transparent bricks on the top already change the color, so I had to do with a single white LED. PWM came to mind. By modifying the frequency, I could get the LED to change brightness, and by repeating that often enough, it could look as if it flickered. The problem is, the BGM111 doesn’t have PWM output. It does, however, have an easily accessible I2C port, so I went looking for PWM expansion cards.

Adafruit to the rescue

The Adafruit 16-channel 12-bit PWM board (https://www.adafruit.com/product/815) was a perfect fit for my needs. It is small, but feature packed. It can handle up to 16 LEDs using a single I2C address, and the address is easily changed. Also of interest is the build in 220-ohm resistor for each output. This makes LED driving trivial; just plug in the LED, and you’re good to go. Oh, and it’s made by Adafruit, so you know it’s well designed, and has excellent source code available.

I ordered one, and it arrived the next day. Solder time! The board has 16 3-pin outputs; V+, GND and PWM. It doesn’t get much easier than this. On the left and right of the board are the power and data connectors, designed so that they can be chained together. Adafruit says that you can chain 62 board together, for a total of 992 PWM outputs. Adafruit, if you are listening, I’m interested in giving this a shot!

Before putting that soldering iron away, don’t forget to change the board’s I2C address. The temperature sensor on the WSTK uses address 0x40, so change the Adafruit address to something else. For this one, I used 0x41, and maybe I’ll get my hands on a few more later on.

Blinky things!

It’s time to connect. The SDA/SCL and power pins are clearly marked on the Adafruit board, and if you turn the SiLabs board over, you can see the pinout for the expansion header. The green LED on the Adafruit should light up, telling you the power is OK. Now it’s time to work on the software.

I wanted to get things up and running as fast as possible, so I used BGScript. It has everything needed to accept connections, talk to I2C peripherals, and use timers. Before working on the connection side of things, I wanted to get the I2C communications up and running. Adafruit supply a nicely written library for Arduino, which is a great start.

The PCA9685, the chip that powers the Adafruit board, can have PWM outputs that start and end at different times compared to the other outputs. It therefore requires two parameters; when to turn on, and when to turn off. If I needed an output to start slightly later than another one, this would be a great feature, but I don’t need it. The PCA9685 has a counter that starts at 0 and ends at 4095, within this period, you can tell the driver when to turn on, and when to turn off. To make things simple, I’ll be turning the LED at 0, and then randomly telling the LED when to turn off by creating a random number between 0 and 4095.

To send this information, you must send 5 bytes on I2C. The first one is the LED; which LED are we talking to? The base address is 0x06, and each LED has 4 bytes of data, so we’ll be using register 0x06 + (LED * 4). The next two bytes specify when we need to turn the LED on; since we’ll be turning it on immediately, we can leave these two at 0. Next, we tell the controller when to turn the LED off. This is done by creating a random byte using system_get_random_data(), and then multiplying that by a certain number to achieve a maximum of 4096; 16. The problem with this is that the LED can be anywhere between full brightness and completely off, which isn’t what a torch looks like. I simplified this by starting off at 2048, half of the maximum number, and then adding a random byte times 8. This gives me a value anywhere between full brightness and half brightness, which does look better. Then we have to send this to the controller, using shifting. This data will be put inside a buffer. The end code looks like this:

call system_get_random_data(1)(result, data_len, data_data) #Get one random byte
transdata(0:1) = ($06 + a*4) #Which LED are we talking to?
transdata(1:1) = $0 #ON, low
transdata(2:1) = $0 #ON, high
transdata(3:1) = data_data & 255 #OFF, low
transdata(4:1) = data_data >> 8 #OFF, high

Next, you have to send this on the I2C bus:

call hardware_write_i2c(0,MODULE_ADDR,5,transdata(0:5))
call hardware_stop_i2c(0)

Now that this is done, all we need to do is to create a for loop for each LED.

Allowing connections

I want this kit to light up, but only for the device that I want. Also, I don’t want to go through the pairing process every time I arrive at the office, that would be a waste of time. With Bluetooth, you can “bond”, that is to say pair and remember the pair, so that the next time, the two devices connect together automatically. Bonding is slightly complicated, but the BGM111 module handles all of that for you. All you need to do is to specify what mode you would like.

First of all, we need to make sure we can connect, so let’s add that command:

call le_gap_set_mode(0,2)

This will set up out device to be connectable, but not discoverable. If we have already connected to this device, then we should be able to connect automatically, otherwise we won’t be able to see it. To advertise our presence, we need to change the command slightly:

call le_gap_set_mode(1,2)

This puts the device into limited discoverability mode, meaning it will be visible to scans for just over a minute. Finally, to enable bonding, use this command:

call sm_set_bondable_mode(1)

With all that, all that is left to do is to look at the state of a pushbutton when starting up (or resetting). Let’s try this:

# Read GPIO status (Button PB1)
call hardware_read_gpio(5,$80)(r,data)
if(data)
# Pushbutton wasn’t pressed, normal mode
call le_gap_set_mode(0,2)
else
# Pushbutton was pressed, enable advertising and bonding
call le_gap_set_mode(1,2)
call sm_set_bondable_mode(1)
end if

Connecting and disconnecting

Here’s the important part! Connecting and disconnecting. Luckily, BGScript makes this easy. The module will handle everything, and just tell you when someone connects, using an event. When an authorized device connects, the event le_connection_opened is called, so let’s use that. When a connection is made, we want the LEDs to blink, so we need to do two things. First, we need to set up a timer, something that will be called every few milliseconds. Next, when this timer is called, we want to use the LED program we created earlier on. No problem! Let’s do it. To create a timer, you simply write this:

call hardware_set_soft_timer(819,TIMER_PWM,0)

This creates a timer, using the TIMER_PWM “channel” (created previously as a const). The timer is now set, and another event will occur when the timer “ticks”:

event hardware_soft_timer(handle)

Now, using an if statement, we can tell the board to blink:

event hardware_soft_timer(handle)
if handle=TIMER_PWM then
# Nice blinky things!
# Blink code goes here
end if
end

When a device disconnects, another event is generated, this time called le_connection_closed. Since only one device will be connected, we can safely shut down everything. First, let’s stop the timer. Just call the same timer code as before, but leave the timeout at zero:

call hardware_set_soft_timer(0,TIMER_PWM,0)

Next, we need to call the same blinky code, only this time, we need to set everything to zero to make it stop blinking:

transdata(0:1) = ($06 + a*4) #Which LED are we talking to?
transdata(1:1) = $0 #ON, low
transdata(2:1) = $0 #ON, high
transdata(3:1) = data_data & 255 #OFF, low
transdata(4:1) = data_data >> 8 #OFF, high

Finally, don’t forget to allow connections again! When a connection is made, the SiLabs module no longer accepts connections, and it is up to you to accept them or not. We didn’t want to, but now that no-one is connected, it is time to enable them again, so that my laptop can connect again when I arrive in the office (or, in my case, I turn it on again).

call le_gap_set_mode(0,2)

And that’s it! Time to flash the code to a SiLabs device, and to start it up! On first boot, you need to press down PB1 to set it to discoverable mode, but after that, it should be fully automatic. If you can’t press PB1 down when the board is powered, you can use the reset button; the event is called when the system starts, either by a cold boot, or warm boot. And that’s it! Now that my Lego torches are protecting me from creepers, zombies, bosses and furious kittens, I’m safe to work!