MakeCode coding tutorial - bitty mood lighting

micro:bit code

Your micro:bit needs the right code on it to be able to respond to buttons being pressed and it being tilted to change the current colour on your tablet's screen.

As with all our apps you can simply download and install a hex file which we've prepared for you or you can code it yourself, following the Bitty Software coding tutorial below. Naturally, we recommend you code it yourself!


goals

From the user's point of view, and without worrying yet how this will be accomplished, here's what we want:

  1. Button B - The user must be able to unlock the currently displayed colour, so that they can change it, by pressing micro:bit button B. Pressing button B while the colour is unlocked must return it to a locked state so that it can no longer be changed. When locking, the system must also save the newly selected colour.
  2. Button A - All colours on the screen are a mixture of 0 - 255 parts of red, green and blue. When the system has been unlocked by pressing button B, the user must be able to increase or decrease the red, green or blue part of the current colour. Pressing button A will switch the current colour part from red to green to blue and back to red again. The micro:bit display should indicate the current colour part which is selected by displaying 'R', 'G' or 'B'.
  3. Button A+B together - When buttons A and B are both pressed together, the system must go into "standby mode' and display a black screen. If it is already in standby mode when A+B are pressed, it should come out of standby mode and enter 'active mode', displaying the last colour to have been selected.
  4. Left and right tilting - with the colour unlocked (pressing button B), tilting the micro:bit to the right (with the display facing up) should increase the selected colour part of red, green or blue while tilting to the left should reduce it. Tilting to a greater angle from the horizontal should make the rate of change proceed faster. Tilting less should make the change proceed more slowly to allow fine tuning.

choices

There are at least a couple of different ways to create a system which responds to the micro:bit buttons and to movement or tilting.

  1. The micro:bit's Bluetooth "profile" includes "services" (like components or modules) called the Button Service and the Accelerometer Service. The Button Service can send messages over Bluetooth to a connected device whenever a button is pressed, held down for more than 2 seconds or released. The Accelerometer Service can send a stream of X,Y and Z values to describe the raw motion of the micro:bit. We could use these services and have the smartphone code do all the hard work to process that data and decide how to respond to it. The micro:bit would need very little code for this approach to be used, other than to ensure these two Bluetooth services are enabled.
  2. We could alternatively send information over Bluetooth in the form of special messages known as "events" from the micro:bit to the smartphone. The code on the micro:bit would indicate actions it wants the smartphone to take instead of sending it raw data about button presses or movement. To do this, the micro:bit code would need to process button and motion data and make decisions about what this should mean to the connected phone or tablet. The micro:bit does more of the work and the smartphone a bit less in this approach.
  3. Alternative system designs like this, where we have to decide which part of our system will be responsible for which aspect of the logic, are known as the System Architecture.
  4. There are pros and cons in each of these two approaches but option 2 lets you learn more about micro:bit coding. So we'll proceed with option 2!

coding requirements

Our micro:bit code needs to:

  1. Keep track of whether or not something is connected to the micro:bit over Bluetooth
  2. Keep track of its own state, whether "locked" or "unlocked" and to change this state when button B is pressed.
  3. Cycle through the selected colour part of red/green/blue when button A is pressed and in the unlocked state (because button B was pressed).
  4. Send the connected smartphone data values which indicate which colour part should be changed and by how much, when in an unlocked state and the micro:bit is tilted more than, say +/- 5% from the horizontal
  5. Send the smartphone data which means "go into standby mode" or "go into active mode" when buttons A and B are pressed together.

before you start

You should install the bitty mood lighting application on your phone so you can use it for testing whilst you develop the micro:bit code. Check the bitty mood lighting page for details of where to find the app for your type of phone or tablet.


step 1 - MakeCode

Go to the MakeCode web site in your web browser and start a new project by clicking Projects and then selecting New Project.


step 2 - make sure your project includes the right packages

Packages

MakeCode groups function blocks together in 'packages' and these are listed in a column. A screenshot of a list of MakeCode packages is shown above. By default it includes a package called 'Radio' which allows you to use a very simple wireless capability of the micro:bit. 'Radio' is good if you want to broadcast a short string of characters to any / all micro:bits around. It's not Bluetooth though and is not what we need for the bitty audio prank application. Importantly, a MakeCode project cannot include both the Radio package and the Bluetooth package at the same time. Add the Bluetooth package to your project using the Settings menu (cog icon on the right) and when prompted to, choose to remove the radio package.

When prompted, opt to remove the radio package


step 3 - create some variables

We're going to use a number of different variables, for the following purposes:

  1. to tell us whether or not something has connected over Bluetooth
  2. to indicate whether the ability to change the colour is locked or unlocked
  3. to indicate whether the system is in the standby state or not
  4. to indicate what the current "colour part" (red, green or blue) it is that we're changing

and so on. You'll get a clearer idea of what our variables are used for as we progress through the steps in this tutorial. For now, we'll create a series of variables so that we can use them when we need them.

From within the "Variables" package, click on the "Make a Variable" block and use it to create a series of variables, named connected, colour_unlocked, standby_mode, roll, roll_event_value and colour. Then add a series of the "Set Item to 0" blocks to your "on start" block and set the variable names and values as shown in the next figure:


step 4 - display a start-up message

Next, we'll add a start-up message to be displayed on the micro:bit when it starts.

In the Basic tray, click on the "show string" block to add it to your project inside "on start". Change the text to "ML" (for Mood Lighting!) and drag the block to snap to the variable blocks above it.


step 5 - Bluetooth connection tracking

Now we'll use some of the blocks in the Bluetooth package to add connection tracking to our project. What we'll do is set the "connection" variable to 1 whenever a connection is established and display "C" on the micro:bit display or we'll set the variable to 0 and display "D" if a connection gets dropped. We'll also reset some of our other variables to initial values whenever we accept a new connection.

From the Bluetooth tray, click on the "on bluetooth connected" block and then the "on bluetooth disconnected" block to add them to your project. Then add "set variable" blocks and "show string" blocks as shown, inside each of the bluetooth connection event blocks.


step 6 - button A

When button A is pressed, we want to change our "colour" variable so that it represents the next colour part of red, green or blue, in that order, with 1 meaning red, 2 meaning green and 3 meaning blue. If we currently have blue selected, pressing A again must cause red to be the selected colour part and the "colour" variable will have a value of 1 once again. We only want to do this if there is a Bluetooth connection and if button B has been pressed, putting the system into the "unlocked" state, meaning we can now change the colour by tilting the micro:bit.

From the Input tray, click on the "on button A pressed" block to add it to your project. From the Logic tray, grab an If/Then block and snap it inside your new button handler block. Proceed to build the complete set of code blocks for button A until it matches the image above. Note that you can adjust your if block by clicking on its settings (cog) icon and dragging Else If or Else blocks in to extend the structure of the block.

Start by adding a logical "and" block to your if condition. Then add "0 = 0" logic blocks to each operand of the logical "and" and finally, set the variables and values in each of the two conditions. You should now have an if condition that looks the way it needs to. Continue with the other conditions, nested if blocks and the actions to take, using "set variable" and "show string" blocks.


step 7 - button B - state change and Bluetooth event

When button B is pressed, if the system is in the "locked" state, we want to put it into the "unlocked" state, meaning we can now change the colour by tilting the micro:bit. Alternatively, if it's already unlocked, we want to switch it back into the locked state. In either case, we want to inform the bitty mood lighting app that this change has happened by sending it an 'event" over Bluetooth. There are other things we want to do when we unlock the system, but let"s build this part of our code one small step at a time.

Start by placing a new "on button A presses" block but change A to B. Add an if block to it and complete as shown in the graphic. You'll find the "raise event" block in the Controls package under Advanced.


step 8 - button B - get ready / reset

Now, if the system has been unlocked, we need to do a couple of things before we start handling the micro:bit being tilted. We need to set the colour variable to 1 so that red is the first colour part we start to change and we need to display "R" on the LED display so that the user knows red is the current colour. If the system was just locked, we just need to clear the screen.

Add another if/else block to the button B handler, underneath the "raise event" block as shown in the graphic below and use other blocks until you have exactly the same as is shown here.


step 9 - button B - measure micro:bit tilt in background

With the system unlocked, we now need to monitor the amount that the micro:bit is being tilted by, measured in degrees from the horizontal. We need to do this quite frequently so that the changes which take place on the screen of the tablet seem nice and smooth but we also need to do it in such a way that we don't block other processing which might happen at the same time, like the buttons being pressed.

To accomplish this, we'll add a while loop which will contain the tilt monitoring code but it will pause for 100ms after each measurement is taken and, most importantly, it will run in background. This means that other processing can take place at the same time, with little interference between our tilt measurement processing and any other processing. This is an example of quite an advanced topic known as "concurrency".

From the Control tray, add a "Raise Event" block to the most recently added If block and give it a from value (its ID) of 9007 and value of 1, as shown in the graphic below. Than add an "on event" block with from=2007 and value=MICROBIT_EVT_ANY, which means it doesn't matter what the value part of an event is for this code to be executed.

Raising an event, will cause the code in the associated "on event" block to be executed in the background, while the remainder of our code continues. Inside the "on event" block, add a While true loop block from the Loops tray and set its condition part so that the loop will keep running as long as we still have a Bluetooth connection and the colour_unlocked variable indicates that the system is unlocked so that the current colour can be changed. Remember that this is indicated by the colour_unlocked variable having a value of 1.

Inside the While loop, add code which will measure the amount the micro:bit is being tilted to the left or right. In PXT, this quantity is called "roll". Use the "rotation" block from the Input tray and assign it"s value to your "roll" variable. Make sure you click on the rotation block's drop down menu to change the rotation value being measured from "pitch" to "roll".

From the Basic tray, add a "pause (ms)" block and set it to pause for 100ms.

Your button B handler and the associated while loop in our event handler should now look like this:


step 10 - button B - calculate and report the amount of colour change required

To finish the code required to handle button B, we need to perform a calculation whose result will indicate which direction of colour change is being requested (less red or more red for example) and how large a change. We then need to send this information, together with a number which indicates which particular colour part is to be changed (red/green/blue) to the bitty mood lighting application running on a Bluetooth connected phone or tablet as an "event".

That said, if the micro:bit is being tilted by only 5 degrees or less, we'll do nothing. We assume the user doesn't want any change unless they tilt the micro:bit by more than this.

Add an If block after the code which obtains the current roll value. Set its condition to check that the value of the measured roll is at least 5 degrees either to the right or to the left. Tilting to the right will give us a positive roll value such as +10 degrees. Tilting to the left will produce a negative roll value such as -10 degrees. Since we don't care which direction the micro:bit is being tilted in for this check, we can ignore the sign and effectively treat negative numbers as if they were positive by using the "absolute" function. So "absolute(-10)", for example, produces +10. We could have used a logical OR instead and had a longer If condition which tested for roll being greater than +5 or less then -5.

Inside this new If block, we need to add a long calculation, which determines the event value we need to send to the bitty mood lighting app over Bluetooth. This is a bit complicated so sit tight whilst I try to explain it.

Event value are 2 bytes long. In the first byte (the least significant part), we want to place a number which indicates the colour change amount. I'll explain more fully what this value is, shortly. In the other byte, we want to place our colour part value of 1, 2 or 3, for red, green or blue.

The calculation we want to do to derive the event value, in long form is this:

  roll_event_value = 256 * colour + ((roll - Math.abs(roll % 15)) / 15 + 7)

Let me try to explain it. This code...

256 x colour

...shifts the value of our colour variable so that it occupies the most significant of our two bytes. To fill the least significant byte with the colour change amount, we just calculate it and add it to the result.

This code...

(roll - Math.abs(roll % 15)) / 15 + 7

...treats the quarter-circle which we can tilt the micro:bit in, from flat (0 degrees) to vertical (90 degrees) as being divided into 6 equal sized segments, each 15 degrees wide. It then generates a simple number indicating which of the 12 possible 15 degree segments the micro:bit has been tilted to. In short, the calculation tells us how much the micro:bit is being tilted by and in which direction. It generates the following possible values:

COLOUR_CHANGE_NEGATIVE_1 = 6;
COLOUR_CHANGE_NEGATIVE_2 = 5;
COLOUR_CHANGE_NEGATIVE_3 = 4;
COLOUR_CHANGE_NEGATIVE_4 = 3;
COLOUR_CHANGE_NEGATIVE_5 = 2;
COLOUR_CHANGE_NEGATIVE_6 = 1;

COLOUR_CHANGE_POSITIVE_1 = 7;
COLOUR_CHANGE_POSITIVE_2 = 8;
COLOUR_CHANGE_POSITIVE_3 = 9;
COLOUR_CHANGE_POSITIVE_4 = 10;
COLOUR_CHANGE_POSITIVE_5 = 11;
COLOUR_CHANGE_POSITIVE_6 = 12;

Negative change values indicate a tilt to the left. Positive values indicate a tilt to the right. Large values mean bitty mood lighting will make large changes to the current colour and small values mean it will make small changes.

Switch to the JavaScript editor to add our calculation. It's much easier than assembling it in the block editor. While you're there, add a Raise Event call to send the calculated event over Bluetooth to bitty mood lighting. The affected section of your code should look like this:

  control.onEvent(9007, EventBusValue.MICROBIT_EVT_ANY, () => {
    while (connected == 1 && colour_unlocked == 1) {
        roll = input.rotation(Rotation.Roll)
        if (Math.abs(roll) > 5) {
            roll_event_value = 256 * colour + ((roll - Math.abs(roll % 15)) / 15 + 7)
            control.raiseEvent(9006, roll_event_value)
        }
        basic.pause(100)
    }
})

The final version of your event handler with while loop should now look like this:


step 11 - buttons A + B - standby / active mode

If buttons A and B are pressed at the same time, we need to switch to standby mode or active mode, depending on which state we're currently in.

Add a 3rd button handler block and set it to respond to buttons A+B. Add blocks to it until it looks exactly like the image below. This will cause the current standby/active mode indicator to be "toggled" (i.e. swapped) and an event sent to the bitty mood lighting app over Bluetooth to inform it of the change.

The final version of your button A+B handler should now look like this:


step 11 - final testing

You've now created the complete bitty mood lighting micro:bit code using MakeCode. This was a fairly challenging tutorial, so if you made it this far, well done!

You should now test your code carefully and fix any problems you find. Compile the MakeCode project and install the hex file. If you have pairing selected in your MakeCode project settings, you'll need to forget any pairing details on your smartphone and go through the pairing process again but once you've done that you should be able to systematically test all the various ways in which your micro:bit can interact with the bitty mood lighting application. Good luck and enjoy having fun with bitty mood lighting!


if you get stuck

Take a close look at the solution here. Look for anywhere where your project is different.

The full project can be edited here