Well I’ve finally got round to posting some video of the Brockenspiel doing its thing. I’ll be discussing how it works in more detail at Dorkbot, but you can get a little preview of the action below. If you can make it to the event, you’ll have a chance to try out your own swipecard and discover what song is encoded on it.
This project was a proof-of-concept to get accelerometer data into Processing where it can be used for more interesting things. This basic project was simply to see how it could be done. When the program is running, the window show a plot of the X, Y, and Z data traces that are being read from the serial port. I also added a little red “worm” which makes it into a kind of game almost.
Here’s the video:
And here’s the code:
/* A program to read and plot the X,Y,Z values that are output by the
SparkFun WiTilt 2.5 3-degree bluetooth accelerometer.
For a little fun, you can also turn on the “Worm” to practice your
drawing skills! With thanks to Jean-Baptiste Labrune for some of the serial-in code.Brock Craft, 2007 | http://www.brock.craft.org
Produced at the London Knowledge Lab
*/
int prevX=0; // values to store the previous location so that we can draw a line
int prevY=0;
int prevZ=0;
float wormX=0; // if we want to draw the worm, we need to save his coordinates
float wormY=0;
PFont fontA; // font for printing:
int X, Y, Z = 0; // 3 Coordinates of the 3 axis accelero
int[] serialInArray = new int[28]; // Where we’ll put what we receive
int serialCount = 0; // A count of how many bytes we receive
boolean firstContact = false; // Whether we’ve heard from the microcontroller
delay(300); // If the WiTilt has been reset, we need to trigger data send mode, first we wait 300 ms
char tosendz = 061; // Tell the WiTilt to send the dataz by sending a 1 (061 in ascii) to the menu
port.write(tosendz+”\r”);
println(”+++++++++++++++++ Init WiTilt normal”); // everything is ok
}
void draw() {
// Debugging mode that lets you see the value entered in the console
// text(”Received: ” + char(thisByte), 10, 130);
// text(”Sent: ” + char(whichKey), 10, 100);
readPort();
plot(); // draw a trace of the X, Y, and Z axes
addText();
updateWorm(); // un-comment these to draw the worm.
drawWorm();
prevX=X; // since we draw a line from each value to the next, we have to store the current value
prevY=Y; // that we will be using for the line segment origination point in the next draw cycle
prevZ=Z;
}
void keyPressed() {
// This is for debugging, lets you talk to the WiTilt
//
// Well, it is a bit of a balagan to explain what i do here but it kinda works
// Basically, the WiTilt ASCII mode sends series of 28 decimals that represents the data :
//
// 88 61 45 48 46 51 56 49 32 89 61 32 49 46 48 52 50 32 90 61 45 48 46 56 48 52 10 13
// X = - 0 . 3 8 1 SP Y = SP 1 . 0 4 2 SP Z = - 0 . 8 0 4 \n \r
//
// SP : space
// \n : Line feed
// \r : Carriage return
//
// Every serie is separated by a \r char meaning 13 in decimal. We localize it , then do an array of the 28
// next values and then parse 3 substring with my X, Y, and Z values.
//
// There are occasional synchronization issues with the serial data stream that can cause an
// exception because of trying to read the array out of bounds. Some error-checking code
// would be a good thing to add here.
// localize the Carriage return byte to know where we are in the data stream
if (inByte == 13){
debut = true;
}
if (debut) {
// Add the latest byte from the serial port to array:
serialInArray[serialCount] = inByte;
serialCount++;
if (serialCount == 24 ) {
serialCount = 0;
String serialInString = new String(char(serialInArray));
// Since we know that the WiTilt spits out an ‘=’ sign before each value,
// we use that to determine where the next data value is coming from
// to parse serialInString into three different substrings
String parse[] = split(serialInString, ‘=’);
String strX = new String(parse[1].substring(0,2));
if (int(strX)!=0){ // to avoid an Exception, only parse the string if we have a value > 0
X = int(10*Integer.parseInt(strX));
}
String strY = new String(parse[2].substring(0,2));
if (int(strY)!=0){
Y = int(10*Integer.parseInt(strY));
}
String strZ = new String(parse[3].substring(0,2));
if (int(strZ)!=0){
Z = int(5*Integer.parseInt(strZ));
}
debut = false;
}
}
void addText(){
fill(120,120,120);
rect(0,0,width,60); // erase the old text by drawing a rect matchng the background
rect(20,300,60,22); // could have used the background() method, with some more code…
rect(20,500,60,22);
rect(20,660,60,22);
fill(255);
text(”WiTilt 2.5 - X, Y, Z plot. Sample time 220Hz max.”,20,40);
text(”X: “+X,20,320);
text(”Y: “+Y,20,520);
text(”Z: “+Z,20,680);
}
void readPort(){
while (port.available() > 0) {
thisByte = port.read();
if (thisByte == 84){
if (menuWiTilt){
delay(300);
//Tell the WiTilt to send the dataz by sending a 1 (061 in ascii) to the menu
char tosendz = 061;
port.write(tosendz+”\r”);
println(”+++++++++++++++++ Init WiTilt abnormal”); // happens when the delay above is not enough or when we loose the bluetooth signal…
menuWiTilt = false;
}
}
// println(X+” “+Y+” “+Z); // DEBUG
processByte(char(thisByte)); // Process the input buffer (see below)
}
}
void updateWorm(){
if(X>490){
wormX=(wormX+(X/200));
}
if(X<470){
wormX=(wormX-(X/200));
}
if(Y>470){
wormY=(wormY+(Y/200));
}
if(Y<450){
wormY=(wormY-(Y/200));
}
if(wormX>width){ // keep it on the screen
wormX=0;
}
if(wormY>height){
wormY=0;
}
if(wormX<0){ // keep it on the screen
wormX=width;
}
if(wormY<0){
wormY=height;
}
if(Z<100){ // we can flick the WiTilt sensor in the Z dimension
wormX=width/2; // to re-home the worm at the screen center.
wormY=height/2;
}
}