Programming the STEMBoT Remote, Part 2

In the previous post, I explained how to access the pairing and reading functions of the remote controller, as well as how to read joystick values. This post will explain how to access the buttons on the remote and how to use this data to control the STEMBoT’s LED. We’ll assume the remote module has been imported already and that the Remote() and RemoteData() classes have been instantiated.

As detailed in the previous post, once the remote and the SB2 are paired, the remote (while on) will continuously send signals to the SB2 to indicate the remote’s most recent button and joystick states. Capturing these signals is done with the read() method, and the data is stored in the RemoteData() class (or rather, the variable to which this class has been assigned, rdata). Getting the joystick values is done by appending the rdata variable with a period and the name of the joystick value, as indicated in the User’s Manual. Getting the button data is a bit trickier, and uses a process called masking. (If you’re already familiar with the concept, or just want to dive into programming the remote, skip to the paragraph starting with the remote).

Masking is a process that allows a programmer to set bits in a stream of data to either 1 or 0. This is done using bitwise operators. Operators most people are familiar with include mathematical operators, such as plus (+) and minus (-) signs. Bitwise operators perform operations on individual bits, and include AND (&) and OR (|) signs. These operators will produce an output depending on the inputs and the truth table associated with the operator. For example, the following truth tables define the output (Z) of an operator on two inputs (X and Y):

You can see that in the case of the AND operation, the output Z will be 1 if and only if both X and Y are 1. For the OR operation, the output Z will be 1 if X or Y (or both) are 1.

Let’s say we wanted to change one variable from 0b1111 to 0b1000 (the 0b prefix tells Python that the value is in binary, so really it’s 1111 and 1000). The code would look like this:

>>>binaryvalue=0b1111
>>>binaryvalue = binaryvalue & 0b1000
>>>bin(binaryvalue)
‘0b1000’

The first line defines the variable. The second reassigns it to the original value AND-ed with the desired value. The third line returns the value (the bin() function is used to return the value in binary, otherwise it will return a decimal value).

One might think that this isn’t very useful: after all, you could just reassign the original variable with the new value and skip the bitwise operation altogether. Instead of using bin(), try using bool() and examine the result:

>>>bool(binaryvalue)
True

Boolean values are either True or False; if the object in question is 0, its Boolean value is False. If the object is anything other than 0, its Boolean value is True. This is where these bitwise operators come in handy: they can tell us whether or not a specific bit is a one or a zero. In the above case, we’re being told that the third bit (closest to the ‘b’) is a one. Try the above code again, except this time set binaryvalue equal to 0b0111. The end result will be False because that bit closest to the ‘b’ is a 0, and so the result of the bitwise operation is 0b0000. In terms of the truth tables above, ‘binaryvalue’ is our X and ‘0b1000’ is our Y.

The remote sends its button states as a sixteen-bit value (actually 17, but the first bit is always 1) where each bit represents a button. If a button is pressed, that bit is set to 1, otherwise it remains 0. Fetch the button data by using the remote’s read() method. The data is stored in the ‘buttons’ variable, and its raw binary value can be read with the bin() function, like so:

>>>r.read(rdata)
>>>bin(rdata.buttons)
‘-0b10000000000000000’

Since none of the buttons were being pressed, each of the relevant bits are set to zero. Now, hold down the green A button and capture the signal with the read() method.

>>>r.read(rdata)
>>>bin(rdata.buttons)
‘-0b11000000000000000’

The 16th bit has been set to 1 by pressing the green button. If we want to know that this has been set to 1 without visually examining it each time, we can perform an AND operation with the known value for the green button, which can be found in section 4.4.2.2 of the User’s Manual.

>>>bool(rdata.buttons & remote.A_BUTTON)
True

If you print out the value of remote.A_BUTTON in binary, you’ll see that there’s a single bit (out of 16) set to one, in the same place as the rdata.buttons value. AND-ing these ones results in a one according to the truth table above, which is a True Boolean value. The following code uses masking to test the state of the X, A, and B buttons and changes the color of the LED accordingly:

>>>import pyb
>>>red=pyb.LED(1)
>>>grn=pyb.LED(2)
>>>blu=pyb.LED(3)
>>>def allOff():
. . .     red.off()
. . .     grn.off()
. . .     blu.off()
. . .
>>>X = remote.X_BUTTON
>>>Y = remote.Y_BUTTON
>>>A = remote.A_BUTTON
>>>B = remote.B_BUTTON
>>>while(1):
. . .     r.read(rdata)
. . .     if rdata.buttons & X:
. . .         blu.on()
. . .     if rdata.buttons & A:
. . .         grn.on()
. . .     if rdata.buttons & B:
. . .         red.on()
. . .     if rdata.buttons & Y:
. . .         allOff()
. . .

The first part of the code sets up the LEDs and builds a function called allOff() to turn them all off. The next part assigns each of the button values to a one character variable (which is not necessary, but it’s done to make writing the main function easier). The while(1) loop continuously reads the remote data and turns on or off the LED, depending on which button is pressed. In most applications involving the remote, continuously reading the data is more useful than a one-shot operation, and a while(1) loop is a way to do this.

In the next post, I’ll provide and explain a complete example program that uses each button and joystick on the remote.

Leave a Reply