How To: Interface an ultrasonic rangefinder with Sensorpedia via Twitter (guide 2) 3 Comments
Previously, we detailed how to read in an LM34 temperature sensor. This guide will show how to interface a Maxbotix-EZ ultrasonic rangefinder with Sensorpedia.

The Maxbotix-EZ measures the distance between its detector and the nearest solid object. It emits a sound at an inaudible 42kHz frequency and listens for an echo wave to be reflected. Based on the time between these events it is able to calculate the distance to a reflecting object.
The Maxbotix sensor is easy to interface. As with the LM34, we only need three wires: Vcc, ground, and analog output. Here are the pins we will use:

For reference, here is the Make Controller pinout:

The LM34 temperature sensor was easy to read because it is an analog sensor whose voltage varies linearly with temperature. Since the the Maxbotix has an analog output it is just as easy, but we want to be able to do more than simply check the current distance.
In the earlier LM34 guide we used the Make Contoller’s built in OpenSoundControl commands to check the current value of the analog-to-digital converter and then calculate the temperature. We used this command:
/analogin/0/value
We can do the same with the Maxbotix, but suppose we want to report an ‘event’ each time the door to our lab space is opened. First we will need to position the Maxbotix in line with the door’s path, and then check repeatedly for a change. If we do this over OSC we might miss someone coming in due to all of the overhead from message passing. It would be great if the Make Controller could alert us when a ‘door open event’ occurs, without having to use OSC commands to constantly check for changes in distance. We want to use a command structured like this:
/distance/event
Unfortunately for us, such a command does not exist. We will have to make it! We will create a custom OSC subsystem on the Make Controller to get this command. Information about the Make Controller OSC API is available on the makingthings website. We will need to define and use several functions. To simplify our explanation, we will only show function prototypes here (the full functions are described in the code snippet below):
This function will receive and route messsages that are requested through the “/distance” command:
int Distance_ReceiveMessage( int channel, char* message, int length );
Another function will be called within ReceiveMessage to define the functions used for getting and setting values, as well as to define our array of properties:
int Osc_IntReceiverHelper(int channel, char *message, int length, char *subsystemName,
int(*propertySet)(int property, int value),
int(*propertyGet)(int property), char *propertyNames[])
Accordingly, we will need a function to get values:
int Distance_PropertyGet( int property );
…and one to set values:
int Distance_PropertySet( int property, int value );
Note that the the property parameter of the two functions above is an integer. Our helper function takes the property names we have chosen and assigns sequential integer values to them. These integers will be used to switch between different ‘get’ and ‘set’ operations.
static char* Distance_PropertyNames[] = { "event", "current", 0 };//array must have a zero last
We will set the name of our new subsystem to “distance,” so that it may respond when we call “/distance/property”:
static char* Distance_Name = "distance";
Lastly, we must register our new subsystem:
Osc_RegisterSubsystem( Distance_Name, Distance_ReceiveMessage, NULL );
Here is a C code snippet that shows the entire process, along with our function to detect events:
#include "config.h"
#include "digitalin.h"
#include "appled.h"
#include "digitalout.h"
#include "string.h"
#include "stdio.h"
int MeasureEvent( int inputNo, long threshold );
int Distance_ReceiveMessage( int channel, char* message, int length );
int Distance_PropertySet( int property, int value );
int Distance_PropertyGet( int property );
static char* Distance_Name = "distance";
static char* Distance_PropertyNames[] = { "event", "current", 0 }; // these are the property names for the distance OSC command "/distance/property". The array must end with a zero
long samplePeriod = 1000000; //sample time in microseconds
int inputPin = 0; //input pin on the controller connected to the analog out pin on the maxbotix sensor
int eventThreshold = 24; //since scaling gives us 0.5 [inch] / A-D [level], we'll look at a threashold of 2 feet
void Run( ){ // this task gets called as soon as we boot up.
Usb_SetActive(1); // turn on USB
Osc_SetActive( true, true, true, true ); //turn on OSC support (before this, we have to include the OSC system)
Osc_RegisterSubsystem( Distance_Name, Distance_ReceiveMessage, NULL ); //this tells the OSC system the command name to look for and which function to route messages to when the command is found
}
//if we send OSC "/distance/event ##" we can set the change detection threshold
int Distance_PropertySet(int property, int value ){
long returnVal = 0;
switch ( property )
{
case 0: // change the threshold
eventThreshold = value;
break;
}
return returnVal;
}
//if we just send OSC "/distance/current" we will use this accessor function to grab the current distance value. it works for the event case as well
int Distance_PropertyGet(int property )
{
long returnVal=0;
switch ( property )
{
case 0: // wait for event
returnVal = MeasureEvent(inputPin, eventThreshold);
break;
case 1:
returnVal = AnalogIn_GetValue(inputPin);
break;
}
return returnVal;
}
// this is called when the OSC system finds a message for our custom distance command
int Distance_ReceiveMessage( int channel, char* message, int length )
{
return Osc_IntReceiverHelper( channel, message, length,
Distance_Name,
Distance_PropertySet,
Distance_PropertyGet,
Distance_PropertyNames );
}
//this function checks for a change in distance from a baseline value
int MeasureEvent( int inputNo, long threshold){
int baselineValue;
int lastValue=-1;
Led_SetState(1);//we'll turn on the LED while we're sampling
baselineValue = AnalogIn_GetValue(inputNo);
lastValue = baselineValue;
while ( (lastValue<(baselineValue+threshold)) && (lastValue>(baselineValue-threshold)) ){
Sleep(150); //delay to give the maxbotix output voltage a chance to change
lastValue = AnalogIn_GetValue(inputNo);
}
Led_SetState(0);
return lastValue;
}
We said previously that we wanted a “/distance/event” command. While we were at it we also added a second command. Now we have an OSC system with two custom commands:
/distance/event
/distance/current
The first command with the “event” property only responds after a distance change event has occurred. The second command just grabs the current value. We can also use the “event” property to set the threshold value for future detections:
/distance/event 65
So now we have Make Controller wired up and programmed to interface with the Maxbotix. Before we request data from it, we need relate the Maxbotix voltage output to real distance values. According to the Maxbotix datasheet, the output voltage is proportional to the distance to a detected object:
![]()
So in our case, with a 3.3 [volt] supply:
Now we know the amount of voltage increase for each additional inch of distance:
Similarly, we also need to voltage as read by the Make Controller:
Knowing both, we can calculate the distance in inches per A/D value:

Now we’ll use an adaptation of our earlier Lm34 Python script to watch for events and then publish them to Twitter (note that we have also included a conversion factor to output meters):
import os
import sys
import time
import threading
import osc # simpleosc
import serial # pyserial
import simplejson #simplejson
import twitter # python-twitter
COM_PORT = '/dev/ttyACM0' # set this to the serial port to which the Make Controller is connected, something like "COM0" for windows or "/dev/ttyACM0" for POSIX
SLIP_END = '\xC0' #end of an OSC message with SLIP wrapping (USB only)
SLIP_ESC = '\xDB' # used to escape embedded OSC messages
class MaxbotixRead(object):
def __init__(self, comPortId):
self._initializeSerial(comPortId)
self._inBuffer = ''
self.responseReceived = 0
def _initializeSerial(self, comPortId):
self._serial = serial.Serial(comPortId, 115200, timeout = 0) # open the virtual serial port created by the Make Controller's USB interface
def _write(self, address, value = None):
m = osc.OSC.OSCMessage() #create a new OSC messsage
m.setAddress(address) #set the address to the one specified by the parameter
if value is not None: # if we are passing a param along with the address...
m.append(value)
s = str(m).replace(SLIP_END, SLIP_ESC + SLIP_END) # escape any embedded SLIP_END characters in the message
self._serial.write(SLIP_END + s + SLIP_END) # begin and end with SLIP_END; doesn't seem to work otherwise
def _read(self):
if self._serial.inWaiting():
for c in self._serial.read(500): # read 500 bytes, or as much as is in the buffer
if c == SLIP_END and (len(self._inBuffer) == 0 or self._inBuffer[-1] != SLIP_ESC): #if the EOM char is in the message, and the buffer is empty or the last buffer char is not an escape character
self._handleInput() #process the data
else: #otherwise
self._inBuffer += c # append what was just read to the buffer
def _handleInput(self):
message = osc.OSC.decodeOSC(self._inBuffer) #decode the message and make an array of it
if len(message) != 0: #if the message is present
distance = message[2]*0.499*0.0254 output = "A distance event has occurred in the Sensorpedia lab. Something is "+ str(distance)+ " [meters] away from the sensor."
twitterObj.PostUpdate(output)
print output#print our temperature
t = threading.Timer(1, self.run).start() #make a new timer to run after this function finishes execution
self.responseReceived = 1
self._inBuffer = '' #wipe the serial buffer
def run(self):
self.responseReceived = 0
self._write('/distance/event') #send a message requesting the value of the zeroth analog input
time.sleep(0.5) #give the Make Controller a chance to poll and respond (value in seconds)
while not self.responseReceived:
self._read() # check for any new messages from the Make Controller if __name__ == "__main__":
app = MaxbotixRead(comPortId = COM_PORT)
twitterObj = twitter.Api("username", "password")
t = threading.Timer(1, app.run) #setup a timer to fire after 1.0 second
t.start() #start the timer
This outputs events :
A distance event has occurred in the Sensorpedia lab. Something is 2.1039836 [meters] away from the sensor.
A distance event has occurred in the Sensorpedia lab. Something is 0.4562856 [meters] away from the sensor.
A distance event has occurred in the Sensorpedia lab. Something is 0.2281428 [meters] away from the sensor.
A distance event has occurred in the Sensorpedia lab. Something is 2.1039836 [meters] away from the sensor.
And publishes them to Twitter:
