Read on to find out how I made a Raspberry Pi talk to a SenseBoard using Python, and how you can do it too.
Fair warning: what follows is quite a lot of largely irrelevant ramble. To skip straight to the code, click here.
A while ago I studied the Open University’s TU100 – My Digital Life course. A big part of this course involved [extremely] basic programming using an OU developed version of Scratch; Sense; and a sensor equipped piece of hardware called the SenseBoard.
Around the same time as starting this, I was excitedly awaiting delivery of my Raspberry Pi. Instantly, the idea of somehow making the SenseBoard and R-Pi work together appealed to me. Of course Arduino users were immediately off the starting mark with Python, easily writing programs to make their R-Pis and Arduinos interact with one another in no time. Unfortunately, the only way to talk to the SenseBoard (as far as I knew at the time) was via the OU’s Sense software, which although fairly flexible didn’t allow much interaction outside of it’s own VM (although it does have the ability to post to an RSS feed and read/write files so it’s still a great jump off point for new programmers).
Time passed, uni work got more involved and I largely forgot about my plans for the R-Pi and SenseBoard, until now.
I recently volunteered as a mentor at the Young Rewired State Festival of Code – a really fantastic event organised every year all over the UK (in fact, it’s now spread to Germany and New York too!) to get young programmers aged 18 and under together for a week to code something together. It was a fantastic and inspiring week, and I recommend you go and read more about it here: https://youngrewiredstate.org/
By chance, Martin O’Hanlon (of <Stuff about=”code” /> fame) was a mentor at the same centre as myself and showed us some of the interesting projects he’s been doing with his multitude of R-Pis (Car Cam anyone?). After spending the weekend seeing some of the fantastic projects the youngsters had come up with, including many utilising the R-Pi and Arduino, I was inspired to get home and start working again on my SenseBoard.
My first port of call, of course, was the Raspberry Pi forums where I immediately came across this post. I read with interest about serial interaction (something I’ve never looked at before) and then discovered a very useful post from a user by the name of berry120. They had provided a link to a document they had been working on, detailing all of the possible commands that can be sent to the SenseBoard as well as the responses to expect – exactly what I needed to get started! (The link inside that post appears to be broken, however a Google search turned up a working link here)
After a couple of hours of playing with PySerial, I finally managed to get connected to the SenseBoard and could turn LEDs on and off. Great! With a little more work, I was also receiving sensor values and in no time at all had written a basic Python script to turn LEDs on in sequence as I slid the SenseBoard’s slider. Exciting! And so, here’s how to talk to your SenseBoard (and some pitfalls to avoid!)
The first thing we need to do is determine which port the SenseBoard is connected to. Under Raspbian, I found the default port was ‘/dev/ttyUSB0’. We also need to tell PySerial the baudrate that the SenseBoard operates at which, thanks to berry120’s document, we know is 115200. I wasted a lot of time here as I’d previously skim read Sense’s source code (available here) which stated the baudrate is 38400 – presumably this refers to the GoGo board which the SenseBoard seems to be based on.
Once the serial interface is connected, it’s simply a case of transmitting the necessary messages to the SenseBoard as detailed in berry120’s document. Here’s where I hit another stumbling block – these messages are always preceded by two header messages; all three bytes must be transmitted individually! This may be common to all serial interfaces but as this is my first time using one I feel it’s worth mentioning; I won’t admit how long I spent head scratching that my byte array did nothing!
I’m working on a wrapper class which I’m calling PySense which will make interacting with the SenseBoard as simple as possible. The problem at the moment is that each set of sensor values is sent as 3 bytes and these must be read and parsed to get a usable value. Doing this on the fly in Python (at least on the R-Pi) causes you to gradually fall out of sync with the data being transmitted back from the SenseBoard. The aim of PySense is to provide a set of common functions (such as toggling LEDs on and off) and constantly up to date sensor values, as well as providing buffered IO to prevent situations whereby you are trying to read and write to the SenseBoard at the same time, causing PySerial to throw an exception. It’s almost ready for an initial release, but still needs a couple of bugs ironing out.
In the meantime, here is a simple Python script demonstrating how to connect to the SenseBoard, send a message to start monitoring the status of the button and then cycle all of the LEDs on and off when it is pressed. There’s a short video showing what should happen, then the full code for you to play with. Enjoy!
import serial import time # connect to SenseBoard 'sb' sb = serial.Serial('/dev/ttyUSB0', '115200', timeout=0.2) # send message to sb that we want to reset it # I find this is good practice when testing to ensure no LEDS # or sensors from previous code runs are still active sb.write(chr(0x54)) # header part 1 sb.write(chr(0xFE)) # header part 2 sb.write(chr(0x10)) # 'Reset' time.sleep(2) # send message to sb that we want to receive data from the button sensor sb.write(chr(0x54)) # header part 1 sb.write(chr(0xFE)) # header part 2 sb.write(chr(0xA0)) # 'Burst Mode' sb.write(chr(0b00001000)) # sensors selected by on bit, # from right to left (button is sensor 4) time.sleep(2) while sb.isOpen(): rcv = sb.read(1) # read first byte if rcv: # check we succesffully read the byte before # acting on it """ as PySerial communicates in chars and 0xC (the receiving message header) is equal to 'U', I found it best to always convert the byte to an int for readability """ if type(rcv) == str: rcv = int(ord(rcv)) else: rcv = int(rcv) if rcv == int('0xC', 16): # if we received a header, we know the next # two bytes are data data = bytearray([sb.read(1), sb.read(1)]) # read our two data bytes """ next we seperate out the high 2 bits for the sensor number and the remaining 10 bits are the sensor's value if you're unsure how this works, there is a good article on bit manipulation here: <insert link> """ sensor = data >> 4 value = (data << 8) & 1023 | data & 0xFF if sensor == 3: # confirm value corresponds to button """ all of the SenseBoard's sensors return a 10 bit value some, like the button, return a boolean value in the form of all 10 bits 'on' (1111111111 == 1023) for True or all 'off' (0000000000 == 0) for False """ if value == 0: # indicates button is pressed """ each LED can be turned on/off by sending the appropriate message followed by a series of bits, with each of the bits 1 thru 7 representing an LED. Here, we will use bit shifting to turn on all the LEDs one by one, then off again in reverse """ LEDbits = 0b00000001 for i in range(0,6): sb.write(chr(0x54)) # header part 1 sb.write(chr(0xFE)) # header part 2 sb.write(chr(0xC1)) # 'LED On' sb.write(chr(LEDbits)) LEDbits = LEDbits << 1 # shift bits 1 to the left time.sleep(0.2) for i in range(0,7): sb.write(chr(0x54)) # header part 1 sb.write(chr(0xFE)) # header part 2 sb.write(chr(0xC0)) # 'LED Off' sb.write(chr(LEDbits)) LEDbits = LEDbits >> 1 # shift bits 1 to the right time.sleep(0.2)