Allright, here it is.
First of all this is what I made: ballding on Vimeo
The basics: Arduino, Xbee 1.0, Rgb Leds, Resistors, Battery, Mobile phone shake 'things' for extra feedback, Power supply (for recharging).
Based on the RSSI values of Xbee transmitters the distance between two balls is measured. Based on this the speed two balls have going towards eachother the color changes. The color depends on the color of the ball itself as well. Two blue balls become blue (pretty fast); two red balls become red (slower); one red and one blue ball becomes purple (hardest thing to do).
This installation was build to show differences in building and maintaining relationships in China and Western countries.
the stuff you want to know:
I use arduino and xbee v1.0 to measure the distances. I dont really use a mesh network because I just couldn't figure that out on xBee v1.0 shields.
I use a sort of 'receive and wait for 3 signals -> then send' program.
Doing it like this gives me a distance of all the balls between each other at least every 1/2 second. This was actually good enough for me.
some codesnippets:
The init:
void initXbee(){
// clear the serial port so no buggy data will be there
Serial.flush();
// delay needed for.. I dont have a f*ckin clue..
delay(1200);
// put xbee in command mode
Serial.print("+++");
// a check could be build in:
/*
char thisByte = 0;
while (thisByte != '\r'){ // wait for the xbee to respond, being in command mode
if (Serial.available() > 0){
thisByte = Serial.read();
}
}
*/
// but that is not really needed in my opinion
// and I just use a delay
delay(500);
// set the low part of the address to 0 and the high part to FFFF => broadcast mode
Serial.print("ATDH0,DLFFFF,");
delay(10);
// set the xbee's own address
Serial.print("MY1,"); // of 4
delay(10);
// set the network id, choose a strange key
// in this case 8 = lucky number in china
Serial.print("ID8888,");
delay(10);
// set the api mode to 1
// 0 = no rssi
// 1 = rssi
// 2 = rssi with escaping
Serial.print("AP1,");
delay(10);
// set the rssi value
// I think this is only usefull if you use the analog pin on the xbee itself to read the rssi ** after finishing this project i read something different about this, cant remember where **
// see the xbee manual for more details
Serial.print("RP40,");
delay(10);
// set the boudrate to 9600
// this will give the highest speed with no data buffering on the xbee
// see the xbee manual for more details
Serial.print("BD3,");
delay(10);
// exit command mode
Serial.print("CN");
// wait for a while
// delay(1200);
}
to receive a signal from other xbee's
void rxSignal(){
//delay(5000);
digitalWrite(rxPin,HIGH);
int packetLength = checkHeader(100);
// Serial.print("packetlength: ");
// Serial.println(packetLength);
// 0x81 == 129 == (u met een dakje)
if (packetLength > 0 && getIdentifier(0x81)){
byte addrMSB = Serial.read();
byte addrLSB = Serial.read();
int address = (addrMSB << 8) + addrLSB;
byte RSSI = Serial.read();
//Serial.print("RSSI: ");
//Serial.println(RSSI,DEC);
byte options = Serial.read();
long startTime = millis();
for (int i = 0; i < (packetLength - 5); i++){
while(((millis() - startTime) < 500) && Serial.available() < 1);
if(Serial.available() > 0){
char dataIn = Serial.read();
//int getal = (dataIn,HEX);
// Serial.println(getal);
// Serial.println(dataIn,HEX);
}
}
byte checksum = Serial.read();
handleSignal(address,RSSI);
}
else {
// Serial.println("BAD IDENTIFIER");
//txSignal();
// resetLoop();
}
digitalWrite(rxPin,LOW);
}
the check header function to check if a received packet has a start bit (0x7E)
int checkHeader(int timeout){
long startTime = millis();
int length = 0;
int inByte = 0;
while (((millis() - startTime) < timeout) && (inByte != 0x7E)){
if (Serial.available() > 0){
inByte = Serial.read();
//Serial.println(inByte);
}
}
if (inByte == 0x7E){
while (Serial.available() < 2);
int lengthMSB = Serial.read();
int lengthLSB = Serial.read();
//Serial.print("msb: ");
// Serial.println(lengthMSB);
//Serial.print("lsb: ");
//Serial.println(lengthLSB);
//length = (lengthMSB << 8) + lengthLSB;
delay(10);
length = lengthLSB;
}
while ((millis() - startTime < timeout) && Serial.available() < length + 1 && Serial.available() < 20) // wait
if (Serial.available() < length && Serial.available() < 20){
length = -1;
}
return length;
}
function to see if the identifier is correct (dont know why anymore
see footnote)
boolean getIdentifier(byte wanted){
long startTime = millis();
byte apiIdentifier = 254;
//Serial.print("wanted: ");
//Serial.println(wanted);
while (Serial.available() < 1 && (millis() - startTime) < 500){
// Serial.println("waiting");
}
if (Serial.available() > 0){
apiIdentifier = Serial.read();
// Serial.print("api ident: ");
// Serial.println(apiIdentifier);
}
else {
//Serial.println("no data");
}
if (apiIdentifier == wanted){
return true;
}
else{
return false;
}
}
well, you can do whatever you want with the address and the rssi..
void handleSignal(int address, int rssi){}
well, it took me a while to figure out how to send and receive data without leaving API mode 1. First i tried switching between API 1 and API 0. That takes a lot of time so I had to figure out a way to send in API1 mode.
This is how I send (broadcast) a signal:
void txSignal(){
digitalWrite(txPin,HIGH);
Serial.flush();
delay(100);
Serial.print(0x7E,BYTE);
Serial.print(0x00,BYTE);
Serial.print(0x06,BYTE);
Serial.print(0x01,BYTE);
Serial.print(0x00,BYTE);
Serial.print(0xFF,BYTE);
Serial.print(0xFF,BYTE);
Serial.print(0x01,BYTE);
Serial.print(0x28,BYTE);
Serial.print(0xD7,BYTE);
Serial.flush();
delay(20);
digitalWrite(txPin,LOW);
Serial.println(" ");
}
Well, with these functions as the base I got some more code to have the balls communicate in the right way. Like checking if 3 balls are received before sending and some reset if it takes to long before all the signals are received and a global reset if something strange fails or the arduino reboots. In my case a reboot happened picking the ball up from the loader because i had to switch the battery. After a switch a ball generally is back in the routine within a second.
I also used some code to make sure the 4 ball routine changed into a three or two ball routine in case of a 'fail' of one ball for a longer period. (When the ball is on a charger, it doesn't send)
I hope this is usefull, if there are more questions, just reply here.
cheers
I re-used some code, the checkheader and getIdentifier functions, but since I somehow erased my favourites I cant find it anymore. If you find your code here and feel in some way offended I did not mention your name or whatsoever, please dont be mad, just contact me.