//////////// FUNCTIONS \\\\\\\\\\\\\\\\ // Rob Faludi // http://www.faludi.com // version 1.09 // function to check for the Header bit, and read in the two following length bits int checkHeader(int timeout) { // timeout is in milliseconds long startTime = millis(); int length = 0; int inByte = 0; // during the timeout period, if we haven't gotten the start byte yet... while (((millis() - startTime) < timeout) && (inByte != 0x7E)) { if (Serial.available() > 0) { // if a byte is waiting in the buffer inByte = Serial.read(); // read a byte from the buffer } } if (inByte == 0x7E) { // if we got the API start byte while (Serial.available() < 2); // wait for at least two bytes to be available int lengthMSB = Serial.read(); // read the most significant length byte int lengthLSB = Serial.read(); // read the least significant length byte length = (lengthMSB << 8) + lengthLSB; // put the two bytes together } //// The Arduino serial buffer can only hold 64 bytes, so we don't wait for more than 20 //// to enter the buffer before moving on with the code. //// Wait for either timeout, full packet or at least 20 bytes... while (((millis() - startTime) < timeout) && Serial.available() < length+1 && Serial.available() < 20) ; // wait if (Serial.available() < length && Serial.available() < 20) { // test to see if we didn't meet criteria length = -1; // and set length negative to indicate an error } return length; } // function that checks to see if the API Identifier matches a requested value boolean getIdentifier(byte idWanted) { long startTime = millis(); byte apiIdentifier = 254; // set apiIdentifier to an impossible value while (Serial.available() < 1 && (millis() - startTime) < 500) ; // wait for a byte or timeout if (Serial.available() > 0) { // if a byte is waiting in the buffer apiIdentifier = Serial.read(); } if (apiIdentifier == idWanted) { return true; } else { return false; } } // function that puts together a command packet boolean sendCommand(char* command, unsigned long data) { static byte frameID = 0; frameID++; // add one to the frame ID each time, okay to overflow if (frameID == 0) frameID = 1; //skip zero because this disables status response packets byte checksum = 0xFF; // checksums are the hex FF minus the total of all bytes // calculate length of the packet int dataLength = countBytes(data); int length = (dataLength + 4); // data length + API id + frame id + two command bytes // send the packet Serial.print(0x7E, BYTE); // send start delimiter Serial.print(0x0, BYTE); // send length MSB (always zero because packets can't be more than 100 bytes) Serial.print(length, BYTE); // send length LSB Serial.print(0x8, BYTE); // send API command identifier checksum = checksum - 0x8; Serial.print(frameID, BYTE); // send frame ID (set to 0 if no response is required) checksum = checksum - frameID; Serial.print(command); // send two-character AT command for (int i=0; i < strlen(command); i++) { checksum = checksum - command[i]; // add in the AT command bytes } // DIVIDE DATA INTO BYTES AND ITERATE THROUGH EACH ONE for (int i = dataLength; i > 0; i--) { byte dataByte = data >> 8 * (i-1); // shift over one byte at a time, MSB first Serial.print(dataByte, BYTE); // send command data checksum = checksum - dataByte; } Serial.print(checksum); // send checksum return getCommandResults(); } // function that receives the results of a command request boolean getCommandResults() { // MAYBE ADD FRAME ID AS AN ARGUMENT HERE? int packetLength = checkHeader(500); // check for a start byte with a 1/2 second timeout // debug.print("packetLength: "); // debug.println(packetLength); if (getIdentifier(0x88)) { // if this is indeed the results of a command byte frameID = Serial.read(); // get the frame ID we're receiving information about // debug.print("frameID: "); // debug.println(frameID); // right now we're not doing anything with the frame ID, so this is a formality char commandReceived[3]; // debug.print("commandReceived: "); for (int i=0; i < 2; i++) { commandReceived[i] = Serial.read(); // get the AT command we're receiving // debug.print(commandReceived[i]); // (right now we're not doing anything with the AT command info, so this is a formality) } // debug.println(""); boolean status = !Serial.read(); // OK is equal to zero in the API, so invert this value when reporting it byte checksum = Serial.read(); // read in the checksum. We ignore this for now return status; } else { return false; // if Identifier indicates wrong packet then give negative feedback } } // function that receives the results of a transmit request boolean getTXstatus() { int packetLength = checkHeader(500); // check for a start byte with a 1/2 second timeout // debug.print("packetLength: "); // debug.println(packetLength); if (getIdentifier(0x89)) { // if this is indeed the results of a command byte frameID = Serial.read(); // get the frame ID we're receiving information about // debug.print("frameID: "); // debug.println(frameID,DEC); // right now we're not doing anything with the frame ID, so this is a formality boolean status = Serial.read(); // OK is equal to zero in the API, so invert this value when reporting it // debug.print(" status: "); // debug.println(status,BIN); byte checksum = Serial.read(); // read in the checksum. We ignore this for now // debug.print(" checksum: "); // debug.println(checksum, HEX); if (status == 0) { return true; } else { return false; // if Identifier indicates wrong packet then give negative feedback } } } // function that puts together a data packet and transmits it, using 16-bit addressing boolean sendData(char* data, int destinationAddr) { static byte frameID = 0; frameID++; // add one to the frame ID each time, okay to overflow if (frameID == 0) frameID = 1; //skip zero because this disables status response packets byte checksum = 0xFF; // checksums are the hex FF minus the total of all bytes // calculate length of the packet int dataLength = strlen(data); int length = (dataLength + 5); // data length + API id + frame id + two address bytes + options byte // send the packet Serial.print(0x7E, BYTE); // send start delimiter Serial.print(0x0, BYTE); // send length MSB (always zero because packets can't be more than 100 bytes) Serial.print(length, BYTE); // send length LSB Serial.print(0x1, BYTE); // send API command identifier checksum = checksum - 0x1; Serial.print(frameID, BYTE); // send frame ID (set to 0 if no response is required) checksum = checksum - frameID; for (int i = 2; i > 0; i--) { byte destinationByte = destinationAddr >> 8 * (i-1); // shift over one byte at a time, MSB first Serial.print(destinationByte, BYTE); // send destination address checksum = checksum - destinationByte; } Serial.print(0x0, BYTE); // send options: 0x0 = none, 0x1 = disable ACK, 0x4 use broadcast PAN ID Serial.print(data); // send all the data bytes for (int i=0; i < strlen(data); i++) { checksum = checksum - data[i]; // add in the AT command bytes } Serial.print(checksum); // send checksum return getTXstatus(); } // function that receives data which was sent using 16-bit addressing char* getData(char* dataIn, int timeout) { int packetLength = checkHeader(timeout); // check for a start byte with a timeout // debug.print("pktLen: "); // debug.println(packetLength,DEC); if (packetLength > 0 && getIdentifier(0x81)) { // if we didn't get an error from check header and this is indeed a data rx packet if((dataIn = (char*) malloc(packetLength - 5+1)) == NULL) { // attempt to allocate string memory // debug.println("malNO"); // report if allocation fails } else { // debug.println("malOK"); // otherwise report that allocation succeeded } memset(dataIn,0,packetLength); // initialize all incomingResponse string positions to null byte addrMSB = Serial.read(); // get the MSB of the source address byte addrLSB = Serial.read(); // get the LSB of the source address // debug.print("MSB: "); // debug.println(MSB); // debug.print("LSB: "); // debug.println(LSB); int lastSender = (addrMSB << 8 ) + (addrLSB); // update global variable with this sender's address byte RSSI = Serial.read(); // get the Received Signal Strength Indicator value // debug.print("RSSI: "); // debug.println(RSSI); byte options = Serial.read(); // get the options. 0 = reserved, 1 = Addr broadcast, 2 = PAN broadcast, 3 -7 = reserved // debug.print("options: "); // debug.println(options); long startTime = millis(); for (int i=0; i < (packetLength - 5); i++) { while (((millis() - startTime) < timeout) && Serial.available() < 1) ; // wait for data if (Serial.available() > 0) { // if a byte is waiting in the buffer dataIn[i] = Serial.read(); // get a byte of data // debug.print(dataIn[i]); } } // debug.println(""); byte checksum = Serial.read(); // debug.print("checksum: "); // debug.println(checksum); return(dataIn); } else { return NULL; // if this packet is bad then give negative feedback } } // puts the XBee in API mode void setAPIMode() { delay(14); // 14 ms delay allows XBee to wake up from sleep mode boolean success = false; while(success == false) { // blinkLED(ledPin, 5, 50); // strobe led delay(1100); // put the XBee in command mode Serial.print("+++"); delay(1100); // wait for a response from the XBee for 2000 ms, or start over if no valid response comes // blinkLED(ledPin, 5, 50); // strobe led // select API Mode Serial.println("ATRE,AP1"); // reset to factory defaults and set API mode // exit command mode (note that we use Serial.printLN here to issue a linefeed that completes the command sequence) Serial.flush(); // remove any prior "OK" responses from the serial buffer Serial.println("ATCN"); long startTime = millis(); while (((millis() - startTime) < 500) && Serial.available() < 2) ; //wait for two characters to come in if (Serial.read() == 'O') { // test for the first letter of "OK" success = true; Serial.flush(); } } } // this function counts the number of bytes in a long int countBytes (long myLong) { int length=0; do { myLong = myLong >> 8; // shift one byte over length++; // and add this byte to the count } while (myLong != 0); // as long as there's still a non-zero number remaining return length; } // function that receives data which was sent using 16-bit addressing int getIOData(int timeout) { int returnVal = -1; long startTime = millis(); int packetLength = checkHeader(500); // check for a start byte with a 1/2 second timeout if (packetLength > 0) { // if we didn't get an error on checkHeader while (Serial.available() < packetLength) { ; // do nothing while waiting for the whole packet to come in. // packets greater than 64 bytes aren't really supported by this code } if (getIdentifier(0x83)) { // if this is indeed an IO data rx packet long startTime = millis(); while (((millis() - startTime) < 1000) && Serial.available() < 4) ; // wait for at least four bytes or timeout byte addrMSB = Serial.read(); // get the MSB of the source address byte addrLSB = Serial.read(); // get the LSB of the source address //debug.print("addrMSB: "); // debug.println(addrMSB,HEX); // debug.print("addrLSB: "); // debug.println(addrLSB,HEX); byte RSSI = Serial.read(); // get the Received Signal Strength Indicator value // debug.print("RSSI: "); // debug.println(RSSI,HEX); byte options = Serial.read(); // get the options. 0 = reserved, 1 = Addr broadcast, 2 = PAN broadcast, 3 -7 = reserved // debug.print("options: "); // debug.println(options,DEC); //char* dataIn = (char*) malloc(packetLength - 5); byte totalSamples = Serial.read(); // this is the number of sample packages that we're receiving if (totalSamples > 43) { // check for impossible values totalSamples = 43; // reset to a possible value, which will probably cause a checksum error } //debug.print("totalSamples: "); //debug.println(totalSamples); byte channelIndicatorHigh = Serial.read(); // this tells us which analog channels (pins) are in use (and one digital channel) // debug.print(" CI High: "); // debug.println(channelIndicatorHigh,BIN); byte channelIndicatorLow = Serial.read(); // this tells us which digital channels (pins) are in use. // debug.print(" CI Low: "); // debug.println(channelIndicatorLow,BIN); // process the channel and sample data to figure out how much we are going to read in byte analogChannels = channelIndicatorHigh >> 1; // shift out the one digital channel indicator that doesn't interest us now byte numAnalogChannels=0; // a variable to count how many analog channels are in use for (int i=0; i<5; i++) { // add up the active channel indicators numAnalogChannels = numAnalogChannels + (analogChannels & 1); // by masking so we only see the last bit analogChannels = analogChannels >> 1; // then shifting over one bit at a time as we go } // to calculate the checksum, first add all the bytes after the length byte together //except the checksum byte itself (we'll add the data bytes in a loop below) byte localChecksum = (0x83 + addrMSB + addrLSB + RSSI + options + totalSamples + channelIndicatorHigh + channelIndicatorLow); byte analogLength = (numAnalogChannels * totalSamples); byte dataADCMSB[analogLength]; byte dataADCLSB[analogLength]; int dataADC[analogLength]; for (int i=0; i