Thursday, February 6, 2014

Arduino and the Iris Zigbee switch, part 3

I've been working with the Iris Smart Switch for three weeks or so and I believe I have enough hacked out of it to make a useful addition to my home automation.  As I mentioned before, it controls an appliance as well as measuring the power usage of the device plugged into it.  Below is the code I ended up with in testing the device.  It's pretty verbose in the output as well as having a ton of comments to help the next person that wants to dig into this switch.

There are eight selections that can be chosen by typing a number into the arduino IDE input line and pressing send:

0 - turn the switch off
1 - turn the switch on
2 - special command routine
3 - get version data
4 - get current switch state (on, off)
5 - reset switch to normal mode
6 - range test
7 - local lock mode
8 - silent Mode

The special command routine is where you can try various commands to the switch.  Simply add code to your requirements and have at it. I used this selection to find various commands.  Range test returns the RSSI value for the switch and can be useful to tell if you have an RF path back to the controller.  Local lock mode disables the button on the switch, it can still be remote controlled, in this mode it doesn't return the periodic power data.  Silent Mode allows local control with the button as well as remote control, but the periodic data is not returned.

There's probably commands and operational characteristics to be found, but I think this is the critical set to put the switch into use.  My next project will be to actually use the darn thing to do something useful.

The Arduino Sketch
/**
This is an examination of Zigbee device communication using an XBee
and an Iris Smart Switch from Lowe's
*/
 
#include <XBee.h>
#include <SoftwareSerial.h>
#include <Time.h>
#include <TimeAlarms.h>

XBee xbee = XBee();
XBeeResponse response = XBeeResponse();
// create reusable response objects for responses we expect to handle 
ZBExpRxResponse rx = ZBExpRxResponse();

// Define NewSoftSerial TX/RX pins
// Connect Arduino pin 2 to Tx and 3 to Rx of the XBee
// I know this sounds backwards, but remember that output
// from the Arduino is input to the Xbee
#define ssRX 2
#define ssTX 3
SoftwareSerial nss(ssRX, ssTX);

XBeeAddress64 switchLongAddress;
uint16_t switchShortAddress;
uint16_t payload[50];
uint16_t myFrameId=1;

void setup() {  
  // start serial
  Serial.begin(9600);
  // and the software serial port
  nss.begin(9600);
  // now that they are started, hook the XBee into 
  // Software Serial
  xbee.setSerial(nss);
  // I think this is the only line actually left over
  // from Andrew's original example
  setTime(0,0,0,1,1,14);  // just so alarms work well, I don't really need the time.
  Serial.println("started");
}

void loop() {
    // doing the read without a timer makes it non-blocking, so
    // you can do other stuff in loop() as well.  Things like
    // looking at the console for something to turn the switch on
    // or off (see waaay down below)
    xbee.readPacket();
    // so the read above will set the available up to 
    // work when you check it.
    if (xbee.getResponse().isAvailable()) {
      // got something
//      Serial.println();
//      Serial.print("Frame Type is ");
      // Andrew called the XBee frame type ApiId, it's the first byte
      // of the frame specific data in the packet.
//      Serial.println(xbee.getResponse().getApiId(), HEX);
      //
      // All ZigBee device interaction is handled by the two XBee message type
      // ZB_EXPLICIT_RX_RESPONSE (ZigBee Explicit Rx Indicator Type 91)
      // ZB_EXPLICIT_TX_REQUEST (Explicit Addressing ZigBee Command Frame Type 11)
      // This test code only uses these and the Transmit Status message
      //
      if (xbee.getResponse().getApiId() == ZB_EXPLICIT_RX_RESPONSE) {
        // now that you know it's a Zigbee receive packet
        // fill in the values
        xbee.getResponse().getZBExpRxResponse(rx);
        
        // get the 64 bit address out of the incoming packet so you know 
        // which device it came from
//        Serial.print("Got a Zigbee explicit packet from: ");
        XBeeAddress64 senderLongAddress = rx.getRemoteAddress64();
//        print32Bits(senderLongAddress.getMsb());
//        Serial.print(" ");
//        print32Bits(senderLongAddress.getLsb());
        
        // this is how to get the sender's
        // 16 bit address and show it
        uint16_t senderShortAddress = rx.getRemoteAddress16();
//        Serial.print(" (");
//        print16Bits(senderShortAddress);
//        Serial.println(")");
        
        // for right now, since I'm only working with one switch
        // save the addresses globally for the entire test module
        switchLongAddress = rx.getRemoteAddress64();
        switchShortAddress = rx.getRemoteAddress16();

        //Serial.print("checksum is 0x");
        //Serial.println(rx.getChecksum(), HEX);
        
        // this is the frame length
        //Serial.print("frame data length is ");
        int frameDataLength = rx.getFrameDataLength();
        //Serial.println(frameDataLength, DEC);
        
        uint8_t* frameData = rx.getFrameData();
        // display everything after first 10 bytes
        // this is the Zigbee data after the XBee supplied addresses
//        Serial.println("Zigbee Specific Data from Device: ");
//        for (int i = 10; i < frameDataLength; i++) {
//          print8Bits(frameData[i]);
//          Serial.print(" ");
//        }
//        Serial.println();
        // get the source endpoint
//        Serial.print("Source Endpoint: ");
//        print8Bits(rx.getSrcEndpoint());
//        Serial.println();
        // byte 1 is the destination endpoint
//        Serial.print("Destination Endpoint: ");
//        print8Bits(rx.getDestEndpoint());
//        Serial.println();
        // bytes 2 and 3 are the cluster id
        // a cluster id of 0x13 is the device announce message
//        Serial.print("Cluster ID: ");
        uint16_t clusterId = (rx.getClusterId());
        print16Bits(clusterId);
        Serial.print(": ");
        // bytes 4 and 5 are the profile id
//        Serial.print("Profile ID: ");
//        print16Bits(rx.getProfileId());
//        Serial.println();
//        // byte 6 is the receive options
//        Serial.print("Receive Options: ");
//        print8Bits(rx.getRxOptions());
//        Serial.println();
//        Serial.print("Length of RF Data: ");
//        Serial.print(rx.getRFDataLength());
//        Serial.println();
//        Serial.print("RF Data Received: ");
//        for(int i=0; i < rx.getRFDataLength(); i++){
//            print8Bits(rx.getRFData()[i]);
//            Serial.print(' ');
//        }
//        Serial.println();
        //
        // I have the message and it's from a ZigBee device
        // so I have to deal with things like cluster ID, Profile ID
        // and the other strangely named fields that these devices use
        // for information and control
        //
        if (clusterId == 0x13){
          Serial.println("*** Device Announce Message");
          // In the announce message:
          // the next bytes are a 16 bit address and a 64 bit address (10) bytes
          // that are sent 'little endian' which means backwards such
          // that the most significant byte is last.
          // then the capabilities byte of the actual device, but
          // we don't need some of them because the XBee does most of the work 
          // for us.
          //
          // so save the long and short addresses
          switchLongAddress = rx.getRemoteAddress64();
          switchShortAddress = rx.getRemoteAddress16();
          // the data carried by the Device Announce Zigbee messaage is 18 bytes over
          // 2 for src & dest endpoints, 4 for cluster and profile ID, 
          // receive options 1, sequence number 1, short address 2, 
          // long address 8 ... after that is the data specific to 
          // this Zigbee message
//          Serial.print("Sequence Number: ");
//          print8Bits(rx.getRFData()[0]);
//          Serial.println();
//          Serial.print("Device Capabilities: ");
//          print8Bits(rx.getRFData()[11]);
//          Serial.println();
        }
        if (clusterId == 0x8005){ // Active endpoint response
          Serial.println("*** Active Endpoint Response");
          // You should get a transmit responnse packet back from the
          // XBee first, this will tell you the other end received 
          // something.
          // Then, an Active Endpoint Response from the end device
          // which will be Source Endpoint 0, Dest Endpoint 0,
          // Cluster ID 8005, Profile 0
          // it will have a payload, but the format returned by the 
          // Iris switch doesn't match the specifications.
          //
          // Also, I tried responding to this message directly after
          // its receipt, but that didn't work.  When I moved the 
          // response to follow the receipt of the Match Descriptor
          // Request, it started working.  So look below for where I 
          // send the response
        }
        if (clusterId == 0x0006){ // Match descriptor request
          Serial.println("*** Match Descriptor Request");
          // This is where I send the Active Endpoint Request 
          // which is endpoint 0x00, profile (0), cluster 0x0005
          uint8_t payload1[] = {0,0};
          ZBExpCommand tx = ZBExpCommand(switchLongAddress,
            switchShortAddress,
            0,    //src endpoint
            0,    //dest endpoint
            0x0005,    //cluster ID
            0x0000, //profile ID
            0,    //broadcast radius
            0x00,    //option
            payload1, //payload
            sizeof(payload1),    //payload length
            myFrameId++);   // frame ID
          xbee.send(tx);
//          Serial.println();
          //sendSwitch(0, 0, 0x0005, 0x0000, 0, 0, 0);

          Serial.print("sent active endpoint request ");
          //
          // So, send the next message, Match Descriptor Response,
          // cluster ID 0x8006, profile 0x0000, src and dest endpoints
          // 0x0000; there's also a payload byte
          //
          // {00.02} gave clicks
          uint8_t payload2[] = {0x00,0x00,0x00,0x00,0x01,02};
          tx = ZBExpCommand(switchLongAddress,
            switchShortAddress,
            0,    //src endpoint
            0,    //dest endpoint
            0x8006,    //cluster ID
            0x0000, //profile ID
            0,    //broadcast radius
            0x00,    //option
            payload2, //payload
            sizeof(payload2),    //payload length
            myFrameId++);   // frame ID
          xbee.send(tx);
//          Serial.println();
          Serial.print("sent Match Descriptor Response frame ID: ");
          Serial.println(myFrameId-1);
            
          //
          // Odd hardware message #1.  The next two messages are related 
          // to control of the hardware.  The Iris device won't stay joined with
          // the coordinator without both of these messages
          //
          uint8_t payload3[] = {0x11,0x01,0x01};
          tx = ZBExpCommand(switchLongAddress,
            switchShortAddress,
            0,    //src endpoint
            2,    //dest endpoint
            0x00f6,    //cluster ID
            0xc216, //profile ID
            0,    //broadcast radius
            0x00,    //option
            payload3, //payload
            sizeof(payload3),    //payload length
            myFrameId++);   // frame ID
            xbee.send(tx);
//            Serial.println();
            Serial.print("sent funny hardware message #1 frame ID: ");
            Serial.println(myFrameId-1);
            //
            // Odd hardware message #2
            //
            uint8_t payload4[] = {0x19,0x01,0xfa,0x00,0x01};
            tx = ZBExpCommand(switchLongAddress,
              switchShortAddress,
              0,    //src endpoint
              2,    //dest endpoint
              0x00f0,    //cluster ID
              0xc216, //profile ID
              0,    //broadcast radius
              0x00,    //option
              payload4, //payload
              sizeof(payload4),    //payload length
              myFrameId++);   // frame ID
              xbee.send(tx);
//              Serial.println();
              Serial.print("sent funny hardware message #2 frame ID: ");
              Serial.println(myFrameId-1);
            
        }
        else if (clusterId == 0xf6){
          // This is The Range Test command response.
          Serial.print("Cluster Cmd: ");
          Serial.print(rx.getRFData()[2],HEX);
          Serial.print(" ");
          Serial.print("*** Cluster ID 0xf6 ");
          if (rx.getRFData()[2] == 0xfd){
            Serial.print("RSSI value: ");
            print8Bits(rx.getRFData()[3]);
            Serial.print(" ");
            print8Bits(rx.getRFData()[4]);
            Serial.print(" ");
            Serial.print((int8_t)rx.getRFData()[3]);
            Serial.println();
          }
          else if (rx.getRFData()[2] == 0xfe){
            Serial.println("Version information");
            // bytes 0 - 2 are the packet overhead
            // This can be decoded to give the data from the switch,
            // but frankly, I didn't know what I would do with it 
            // once decoded, so I just skip it.
          }
          // This is to catch anything that may pop up in testing
          else{
            Serial.print(rx.getRFData()[2],HEX);
            Serial.print(" ");
            for(int i=0; i < rx.getRFDataLength(); i++){
              print8Bits(rx.getRFData()[i]);
              Serial.print(' ');
            }
            Serial.println();
          }
        }
        if (clusterId == 0x00f0){
//          Serial.println("Most likely a temperature reading; useless");
//          for(int i=0; i < rx.getRFDataLength(); i++){
//              print8Bits(rx.getRFData()[i]);
//              Serial.print(' ');
//          }
//          Serial.println();
          Serial.print("Cluster Cmd: ");
          Serial.print(rx.getRFData()[2],HEX);
          Serial.print(" ");
          uint16_t count = (uint8_t)rx.getRFData()[5] + 
              ((uint8_t)rx.getRFData()[6] << 8);
          Serial.print("Count: ");
          Serial.print(count);
          Serial.print(" ");
          Serial.print(rx.getRFData()[12]);
          Serial.print(" ");
          Serial.print(rx.getRFData()[13]);
          Serial.print(" ");
          uint16_t temp =  (uint8_t)rx.getRFData()[12] + 
              ((uint8_t)rx.getRFData()[13] << 8);
          Serial.println(temp);
//          temp = (temp / 1000) * 9 / 5 + 32;
//          Serial.println(temp);

        }
        if (clusterId == 0x00ef){
          //
          // This is a power report, there are two kinds, instant and summary
          //
          Serial.print("Cluster Cmd: ");
          Serial.print(rx.getRFData()[2],HEX);
          Serial.print(" ");
          Serial.print("*** Power Data, ");
          // The first byte is what Digi calls 'Frame Control'
          // The second is 'Transaction Sequence Number'
          // The third is 'Command ID'
          if (rx.getRFData()[2] == 0x81){
            // this is the place where instant power is sent
            // but it's sent 'little endian' meaning backwards
            int power = rx.getRFData()[3] + (rx.getRFData()[4] << 8);
            Serial.print("Instantaneous Power is: ");
            Serial.println(power);
          }
          else if (rx.getRFData()[2] == 0x82){
            unsigned long minuteStat = (uint32_t)rx.getRFData()[3] + 
              ((uint32_t)rx.getRFData()[4] << 8) + 
              ((uint32_t)rx.getRFData()[5] << 16) + 
              ((uint32_t)rx.getRFData()[6] << 24);
            unsigned long uptime = (uint32_t)rx.getRFData()[7] + 
              ((uint32_t)rx.getRFData()[8] << 8) + 
              ((uint32_t)rx.getRFData()[9] << 16) + 
              ((uint32_t)rx.getRFData()[10] << 24);
            int resetInd = rx.getRFData()[11];
            Serial.print("Minute Stat: ");
            Serial.print(minuteStat);
            Serial.print(" watt seconds, Uptime: ");
            Serial.print(uptime);
            Serial.print(" seconds, Reset Ind: ");
            Serial.println(resetInd);
          }
        }
        if (clusterId == 0x00ee){
          //
          // This is where the current status of the switch is reported
          //
          // If the 'cluster command' is 80, then it's a report, there
          // are other cluster commands, but they are controls to change
          // the switch.  I'm only checking the low order bit of the first
          // byte; I don't know what the other bits are yet.
          if (rx.getRFData()[2] == 0x80){
            Serial.print("Cluster Cmd: ");
            Serial.print(rx.getRFData()[2],HEX);
            Serial.print(" ");
            if (rx.getRFData()[3] & 0x01)
              Serial.println("Light switched on");
            else
              Serial.println("Light switched off");
          }
        }
      }
      else if (xbee.getResponse().getApiId() == ZB_TX_STATUS_RESPONSE) {
        ZBTxStatusResponse txStatus;
        xbee.getResponse().getZBTxStatusResponse(txStatus);
        Serial.print("Status Response: ");
        Serial.println(txStatus.getDeliveryStatus(), HEX);
        Serial.print("To Frame ID: ");
        Serial.println(txStatus.getFrameId());
      }
      else {
        Serial.print("Got frame type: ");
        Serial.println(xbee.getResponse().getApiId(), HEX);
      }
    }
    else if (xbee.getResponse().isError()) {
      // some kind of error happened, I put the stars in so
      // it could easily be found
      Serial.print("************************************* error code:");
      Serial.println(xbee.getResponse().getErrorCode(),DEC);
    }
    else {
      // I hate else statements that don't have some kind
      // ending.  This is where you handle other things
    }
    if (Serial.available() > 0) {
      char incomingByte;
      
      incomingByte = Serial.read();
      Serial.print("Selection: ");
      Serial.println(atoi(&incomingByte), DEC);
      // This message set will turn the light off
      if (atoi(&incomingByte) == 0){
        uint8_t payload1[] = {0x01}; //
        uint8_t payloadOff[] = {0x00,0x01};
        sendSwitch(0x00, 0x02, 0x00ee, 0xc216, 0x01, payload1, sizeof(payload1));
        sendSwitch(0x00, 0x02, 0x00ee, 0xc216, 0x02, payloadOff, sizeof(payloadOff));
      }
      // This pair of messages turns the light on
      else if (atoi(&incomingByte) == 1){
        uint8_t payload1[] = {0x01}; //
        uint8_t payloadOn[] = {0x01,0x01};
        sendSwitch(0x00, 0x02, 0x00ee, 0xc216, 0x01, payload1, sizeof(payload1));
        sendSwitch(0x00, 0x02, 0x00ee, 0xc216, 0x02, payloadOn, sizeof(payloadOn));
      }
      // this goes down to the test routine for further hacking
      else if (atoi(&incomingByte) == 2){
        testCommand();
      }
      // This will get the Version Data, it's a combination of data and text
      else if (atoi(&incomingByte) == 3){
        uint8_t data[] = {0x00, 0x01};
        sendSwitch(0x00, 0x02, 0x00f6, 0xc216, 0xfc, data, sizeof(data));
      }
      // This command causes a message return holding the state of the switch
      else if (atoi(&incomingByte) == 4){
        uint8_t data[] = {0x01};
        sendSwitch(0x00, 0x02, 0x00ee, 0xc216, 0x01, data, sizeof(data));
      }
      // restore normal mode after one of the mode changess that follow
      else if (atoi(&incomingByte) == 5){ 
        uint8_t databytes[] = {0x00, 0x01};
        sendSwitch(0, 0x02, 0x00f0, 0xc216, 0xfa, databytes, sizeof(databytes));
      }
      // range test - periodic double blink, no control, sends RSSI, no remote control
      // remote control works
      else if (atoi(&incomingByte) == 6){ 
        uint8_t databytes[] = {0x01, 0x01};
        sendSwitch(0, 0x02, 0x00f0, 0xc216, 0xfa, databytes, sizeof(databytes));
      }
      // locked mode - switch can't be controlled locally, no periodic data
      else if (atoi(&incomingByte) == 7){ 
        uint8_t databytes[] = {0x02, 0x01};
        sendSwitch(0, 0x02, 0x00f0, 0xc216, 0xfa, databytes, sizeof(databytes));
      }
      // Silent mode, no periodic data, but switch is controllable locally
      else if (atoi(&incomingByte) == 8){ 
        uint8_t databytes[] = {0x03, 0x01};
        sendSwitch(0, 0x02, 0x00f0, 0xc216, 0xfa, databytes, sizeof(databytes));
      }
    }
    Alarm.delay(0); // Just for the alarm routines

}

uint8_t testValue = 0x00;

void testCommand(){
  Serial.println("testing command");
  return;
  Serial.print("Trying value: ");
  Serial.println(testValue,HEX);
  uint8_t databytes[] = {};
  sendSwitch(0, 0xf0, 0x0b7d, 0xc216, testValue++, databytes, sizeof(databytes));
  if (testValue != 0xff)
    Alarm.timerOnce(1,testCommand); // try it again in a second
}

/*
  Because it got so cumbersome trying the various clusters for various commands,
  I created this send routine to make things a little easier and less prone to 
  typing mistakes.  It also made the code to implement the various commands I discovered
  easier to read.
*/
void sendSwitch(uint8_t sEndpoint, uint8_t dEndpoint, uint16_t clusterId,
        uint16_t profileId, uint8_t clusterCmd, uint8_t *databytes, int datalen){
          
  uint8_t payload [10];
  ZBExpCommand tx;
//  Serial.println("Sending command");
  //
  // The payload in a ZigBee Command starts with a frame control field
  // then a sequence number, cluster command, then databytes specific to
  // the cluster command, so we have to build it up in stages
  // 
  // first the frame control and sequence number
  payload[0] = 0x11;
  payload[1] = 0;
  payload[2] = clusterCmd;
  for (int i=0; i < datalen; i++){
    payload[i + 3] = databytes[i];
  }
  int payloadLen = 3 + datalen;
  // OK, now we have the ZigBee cluster specific piece constructed and ready to send
  
  tx = ZBExpCommand(switchLongAddress,
    switchShortAddress,
    sEndpoint,    //src endpoint
    dEndpoint,    //dest endpoint
    clusterId,    //cluster ID
    profileId, //profile ID
    0,    //broadcast radius
    0x00,    //option
    payload, //payload
    payloadLen,    //payload length
    myFrameId++);   // frame ID
    
    xbee.send(tx);
    Serial.print("sent command: ");
    Serial.print(payload[2], HEX);
    Serial.print(" frame ID: ");
    Serial.println(myFrameId-1);
}

/*-------------------------------------------------------*/
// these routines are just to print the data with
// leading zeros and allow formatting such that it 
// will be easy to read.
void print32Bits(uint32_t dw){
  print16Bits(dw >> 16);
  print16Bits(dw & 0xFFFF);
}

void print16Bits(uint16_t w){
  print8Bits(w >> 8);
  print8Bits(w & 0x00FF);
}
  
void print8Bits(byte c){
  uint8_t nibble = (c >> 4);
  if (nibble <= 9)
    Serial.write(nibble + 0x30);
  else
    Serial.write(nibble + 0x37);
        
  nibble = (uint8_t) (c & 0x0F);
  if (nibble <= 9)
    Serial.write(nibble + 0x30);
  else
    Serial.write(nibble + 0x37);
}
Here's a chunk of the sample output:

00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 37753 213 159 40917
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 37873 214 159 40918
00EF: Cluster Cmd: 82 *** Power Data, Minute Stat: 0 watt seconds, Uptime: 58620 seconds, Reset Ind: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 37993 214 167 42966
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 38113 216 149 38360
00EF: Cluster Cmd: 82 *** Power Data, Minute Stat: 0 watt seconds, Uptime: 58680 seconds, Reset Ind: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 38233 216 146 37592
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 38353 217 147 37849
00EF: Cluster Cmd: 82 *** Power Data, Minute Stat: 0 watt seconds, Uptime: 58740 seconds, Reset Ind: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 38473 216 150 38616
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00F0: Cluster Cmd: FB Count: 38593 217 150 38617
00EF: Cluster Cmd: 82 *** Power Data, Minute Stat: 0 watt seconds, Uptime: 58800 seconds, Reset Ind: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
00EF: Cluster Cmd: 81 *** Power Data, Instantaneous Power is: 0
I didn't have anything plugged into it at the time, but you an see what the general output is.

Have fun

22 comments:

  1. Chalk me up as another success. It took a little work as I had to jumper too with the official arduino xbee shield. the sparkfun shield didn't have this issue, since by default it uses pins 2/3 when switched. It also managed to see the Iris door/window switch, though it didn't know what to do with it. I hope it's ok, but I forwarded your pages to the developers of the Almond+ router. My hope is that your findings will help them make the iris plugs accessible via the Almond firmware, so more people can use them with it, otherwise, i'll have to do it as a post process thing. Thanks for figuring all this out.

    ReplyDelete
    Replies
    1. Remember, only a few of the Iris devices are Zigbee, most of them are Z-wave. What's up with the 'Z' thing anyway?

      Delete
    2. yeah, i know which are zigbee and which are z-wave.. the ZWave things I've noticed are all the 3rd party stuff like the locks, GE wall switches.. aka everything NOT made by AlertMe.co.uk. The Almond+ supports Zigbee and Z-Wave both.. I don't know what's up with the Z thing. You could ask the same thing about Digi calling their stuff XBee.

      Delete
    3. I think I'm 90% there, but having a puzzling problem. I'm using a Centralite Azela Appliance module, which seems virtually identical to the Lowes plug in appearance, function, and parameters. (http://www.docstoc.com/docs/159393119/Azela-Zigbee-HA-Appliance-Module-Installation-Guide---CentraLite). It pairs with the XBee in XCTU very nicely when the XBee has the parameters you recommend (and not otherwise). In the XCTU's network view with the XBee connected and the Centralite plug reset to get it to search, the two devices show up connected.

      I've worked with Arduinos a lot, and followed all your instructions explicitly. The sketch does setup, but then doesn't work at the very beginning of the loop: after the xbee.readPacket(), xbee.getResponse().isAvailable() never shows any data read. I've tried tons of variations and tests (hardware and software), but no good. It's puzzling because the plug is sending data and the XBee reads it when in XCTU. Nobody else seems to have this problem. Can someone help me diagnose this? Maybe a problem with SoftSerial?

      Delete
    4. Well, xbee.getResponse has to be working, otherwise you wouldn't have gotten it to join. Go into the XBee library and find the actual serial read and print out the data received. That should tell you what is going on.

      Delete
    5. Excellent suggestion--I forgot I could so easily change the library code. I put in the following:

      int XBee::readPacket() { //wrh was void XBee::readPacket() {
      int diag = 1; //wrh diagnostic
      // reset previous response
      if (_response.isAvailable() || _response.isError()) {
      // discard previous packet and start over
      resetResponse();
      }
      diag=2;//wrh
      while (available()) {
      diag++;//wrh
      b = read();
      etc.,
      then at the bottom I return the value of diag and print it in Serial. It was always 2, meaning that available() always returned false. I reset the plug during the test a few times and it was blinking to indicate searching, but still nothing except 2's.

      Then here is the code for available():
      bool XBee::available() {
      return _serial->available();
      }
      So _serial_available is always false. Is this just a case of SoftSerial not working properly? I have the very same setup as in your sketch, using D2 and D3 as SoftSerial Rx and Tx respectively connected to DOut and DIn (pins 2 and 3) on the XBee.

      This is puzzling because as I said, in XCTU the plug is quickly identified and connected, though in repeated scans it sometimes disconnects and then reconnects after a few more scans. When connected, it shows the other as its connected party. Is there a simple test for SoftwareSerial working?

      I hope this points at something obvious. I'm determined to solve this.

      Delete
    6. You can also go to the actual character by character read for the XBee and print whatever is coming in on a character basis. To test software serial is a bit tough depending on what you have around the house. There's example code you can get and then hook something up to the pins. Things like a GPS, another ftdi device, another arduino, things like that will work.

      In XBee.cpp, look for XBee::read() and stick a Serial.write() in there to see if you're actually getting anything. Sure, it might fill up your screen, but that's what you want.

      There's another possibility, with the very latest XBee library you have to do a listen() on the port sometimes for it to work. Take a look at the SoftwareSerial documentation to see what I mean.

      Delete
    7. In studying SoftwareSerial more, I found out something important: on a Mega2560 like I'm using, SS RX is only supported on pins 10 and above (only those support Change Interrupts). So I thought my problem was solved when I switched to using 10 and 11. Unfortunately, it still doesn't work, so I will keep studying, and hoping for advice.

      Delete
    8. Wait a minute, you're using a 2560? Just change the code a bit and use one of the uart ports and don't even bother with SS. A 2560 can support the XBee and a console and a couple of other things directly without any software tricks.

      Drop me a note by email and we can get into this deeply. My email address is under the 'about me' on the right hand side cleverly disguised so the spam bots don't pick up on it.

      Delete
  2. Hello!

    I'm feeling like banging my head against a wall. I have same problem as William has. So far I have tried the following:

    1. One xbee connected to xctu running on PC and one xbee to arduino
    2. Communication works fine when both xbees are in AT mode. Messages are passed from xctu to arduino serial terminal via softwareserial
    3. Added serial.write to xbee.cpp
    uint8_t XBee::read() {
    Serial.write(_serial->read());
    return _serial->read();
    }
    4. Keeping the xbees in AT mode and running the sketch form you in arduino. Messages between the xbees are working ok
    5. Change the both xbees to API 2 mode (co-ordinator with arduino and router connected to xctu). xctu sees the devices. Nothing shows up in serial terminal screen. I also tried to pair the adhoco (yep, I'm the same dude who asked you about the adhoco devices) device but nothing comes through to arduinos serial terminal screen, even that the serial.write -line is still in xbee.cpp

    I also added line nss.listen();, before xbee.readpacket(). No help.

    Any ideas what to try next? Or have I understood something totally wrong. Just to confirm that is it so that every time I turn on the xbee router or make the adhoco device try to join to the network, some packet should show up in the xbee-arduinos terminal screen?

    Br.
    J

    ReplyDelete
    Replies
    1. OK, you may be having a basic problem here. In AT mode, whatever you send to the XBee gets sent to the destination, however in API mode, you have to shape the data into a special packet that gets sent. There's a lot more to sending and receiving, but you get all the features of the XBee.

      As for whether or not you should be seeing a message, that depends on the device. There's a lot of transmissions that take place between XBee devices that you won't see because the XBee handles them for you. Just getting a 16 bit address assigned takes several messages between the two of them, but you won't see any of those out the serial port.

      Delete
  3. Yes I understand the difference between AT and API mode. I used the AT mode just to make sure that the sofwareserial is working. I added the xbee router just increase the possibility to get something out of the xbee coordinators serial. So far the coordinators serial has be dead silent when xbees are in API mode.

    When I turn on my adhoco S2 unit, it first seems to connect to the xbee because the led indicator on S2 turns green. After about 10 seconds the leds start blinking (three time, pause, three times, pause, etc). This means that S2 hasn't joined the network and that there has been an error in the joining process. From your posts I have understood that, to make the S2 to join the network, I need to receive the Device Announce Message from S2 and generate proper reply message with right cluster ID, etc.

    The challenge is that, xbee doesn't transmit any of the received messages through the serial line. I don't actually know what of the received messages it should push to the serial line.

    If I use the Xbee coordinator through xctu and turn on the network discovery, the S2 show up for a brief moment (only if I have done full reset to the S2 unit). XCTU show the adhocos address and mode of the device (router in this case).

    The following code should print all traffic, which going through Xbee serial connection, to the terminal screen. Right?

    #include
    #define ssRX 2
    #define ssTX 3
    SoftwareSerial nss(ssRX, ssTX);
    void setup() {
    Serial.begin(9600);
    nss.begin(9600);
    Serial.println("started");
    }
    void loop() {
    if (nss.available() > 0) {
    Serial.write(nss.read());
    }
    }

    I noticed that you have used different API modes in different zigbee projects and Encryption options for iris and centralite devices. Iris uses API2 and EO 1 and centralite uses API1 and EO0. So the Iris using escape characters and centralite doesn't but what is the difference EO options?
    Any ideas what to try next?

    I could try to send a AT ND command to the coordinator throught terminal screen. I can create right message with the xctu message creating to…

    ReplyDelete
    Replies
    1. I'd forgotten about encryption, and the explanation in the XBee manual is the very best I've found for how it works, take a look there. However, it won't stop you from getting the very first Zigbee messages. They have to do some route record stuff before they can exchange keys so that should be showing up.

      You're right, the code you have should show up anything that comes in. I recommend going back to what you know works and make sure you can get things on the controller from anywhere and then change one item at a time and see where it stops working for you using only XBees. When you can receive api mode 3 messages, you should be receiving the Zigbee messages from the target device if it is sending them.

      I had some trouble getting API mode 3 to work, but I can't remember what it was and it was relatively easy to fix. I do remember it being a pain switching between API and AT modes to try and figure out what was going wrong since I had to reprogram them each time.

      Delete
    2. I have read key parts of the xbee manual and also read through the Building Wireless Sensor Networks book but I think that I will do it once more...

      One weird thing happened yesterday. I was once again play with the xbee trough XCTU and suddenly in the network topology there was about 12 adhoco devices. I have quite extensive adhoco system running already in my appartment. I save the logs but I forgot to save the xbee settings. Anyway somehow my xbee cordinator was able to read some of the traffic in the completely different zigbee network. I haven't able to duplicate this again...

      Anyway I won't give up until I have nailed this:)

      I read from the digi -forum that to get zigbee HA working more reliable, the xbee ZB SMT units should be used. The newest firmware of those units is dated to mid 2015.

      Delete
    3. If you were seeing the adhoco messages, you probably had the XBee programmed as a router instead of a controller. That's actually a plus in that you might be able to watch traffic somewhat between the devices.

      Delete
    4. Nope, it was programmed to coordinator. Here is the logs from XCTU I mentioned earlier:

      MAC,Role,Network Address,Parent,Last scan,Connections
      0013A200403B0CBD,Coordinator,0000,,1,"0013A20040E6EBE5 Router [1287] ? Active,0018E50000010140 Router [714E] ? Active,0018E5000001039D Router [7A65] ? Active,0018E500000100B5 Router [C16A] ? Active,0018E500000100CA Router [CAC8] ? Active,0018E50000002FDB Router [F58A] ? Active,0018E5000000547C Router [FB1F] ? Active"
      0013A20040E6EBE5,Router,1287,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"
      0018E50000010140,Router,714E,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"
      0018E5000001039D,Router,7A65,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"
      0018E500000100B5,Router,C16A,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"
      0018E500000100CA,Router,CAC8,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"
      0018E50000002FDB,Router,F58A,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"
      0018E5000000547C,Router,FB1F,,1,"0013A200403B0CBD Coordinator [0000] ? Unknown"

      and the com log:
      09-30-2015 17:31:43.067,25,RECV,7E0043910013A200403B0CBD0000000080310000012400070202B588E64C8CF755999D03010000E51800657A35020FF0B588E64C8CF75599B500010000E518006AC135020FFFFE
      09-30-2015 17:31:44.114,26,SENT,7E001611240013A200403B0CBDFFFE000000310000000024047B
      09-30-2015 17:31:44.130,27,SENT,7E001611250013A20040E6EBE5FFFE00000032000000002500CA
      09-30-2015 17:31:44.255,28,RECV,7E0043910013A200403B0CBD0000000080310000012400070402B588E64C8CF75599CA00010000E51800C8CA35020FFFB588E64C8CF75599DB2F000000E518008AF535020FFF68
      09-30-2015 17:31:44.255,29,RECV,7E00078B24000000000050
      09-30-2015 17:31:45.310,30,SENT,7E001611240013A200403B0CBDFFFE0000003100000000240679
      09-30-2015 17:31:45.467,31,RECV,7E002D910013A200403B0CBD0000000080310000012400070601B588E64C8CF755997C54000000E518001FFB35020FFF85
      09-30-2015 17:31:45.467,32,RECV,7E00078B24000000000050
      09-30-2015 17:31:45.795,33,RECV,7E00078B25FFFD0024012E
      09-30-2015 17:31:47.225,34,SENT,7E001611260018E50000010140FFFE0000003200000000260034
      09-30-2015 17:31:48.878,35,RECV,7E00078B26FFFD0C240121
      09-30-2015 17:31:50.316,36,SENT,7E001611270018E5000001039DFFFE00000032000000002700D3
      09-30-2015 17:31:51.989,37,RECV,7E00078B27FFFD0024012C
      09-30-2015 17:31:53.344,38,SENT,7E001611280018E500000100B5FFFE00000032000000002800BC
      09-30-2015 17:31:54.985,39,RECV,7E00078B28FFFD0024012B
      09-30-2015 17:31:56.407,40,SENT,7E001611290018E500000100CAFFFE00000032000000002900A5
      09-30-2015 17:31:58.058,41,RECV,7E00078B29FFFD0024012A
      09-30-2015 17:31:59.471,42,SENT,7E0016112A0018E50000002FDBFFFE00000032000000002A0064
      09-30-2015 17:32:01.113,43,RECV,7E00078B2AFFFD00240129
      09-30-2015 17:32:02.535,44,SENT,7E0016112B0018E5000000547CFFFE00000032000000002B009C
      09-30-2015 17:32:04.163,45,RECV,7E00078B2BFFFD00240128
      09-30-2015 17:32:09.884,46,SENT,7E00050846414F0021
      09-30-2015 17:32:09.967,47,RECV,7E00058846414F00A1

      This offer is still valid:
      hey, if you want I can ship one of these to you:
      http://www.lumiere.co.za/custpics/0/38/133/adhoco-c1-product-description.pdf

      I have some extras to share:)

      Delete
    5. Normally I'd jump on a chance to play with a new device, but I'm still up to my ears in projects around the house that I procrastinated on for too long. It would probably be a month or more before I could even think about playing with it.

      Delete
    6. No problem, I have extras to share. If you find the time before end of this year, that would be awesome, if not that's fine too. My target is to crack this problem before you anyway;) If you want that C1 sensor, send me you post address and I will ship the sensor to you.

      Delete
    7. Julli, drop me an email. My address is over in the 'about me' link on the right encoded slightly to stop the spam bots from flooding my mail.

      Delete
    8. The sensor is on it's way.

      Had some success with the adhoco s1 sensor. By setting the EE = 0, the S1 joins to the network. Xbee also receives the following api frames:

      Frame type: 91 (Explicit RX Indicator)
      64-bit source address: 00 18 E5 00 00 01 03 50
      16-bit source address: 0C 82
      Source endpoint: 0B
      Destination endpoint: 0B
      Cluster ID: 00 04
      Profile ID: 0F 05
      Receive options: 01
      RF data: 02 00 18 E5 00 00 01 03 50 06 02 06 0B 64 22 8E 00 56 31 2E 34 2E 32 00 31 2E 31 2E 30 00 B1 16

      Source endpoint: 00
      Destination endpoint: 00
      Cluster ID: 00 13
      Profile ID: 00 00
      Receive options: 02
      RF data: 00 82 0C 50 03 01 00 00 E5 18 00 0C

      Source endpoint: 0B
      Destination endpoint: 0B
      Cluster ID: 00 03
      Profile ID: 0F 05
      Receive options: 01
      RF data: 02 00 18 E5 00 00 01 03 50 06 02 06 31 00 00 00 00 87 FB

      If the EE is set to 1, S1 tries to join but quits and no messages is passed to xbee's UART. Strange...

      Sad thing is that the C1 sensors don't want to join at all.

      Delete