Friday, April 17, 2015

Battery Powered Temperature Sensor: Remote Control Added

The previous post in this series is here <link>.

So, I crawled into bed the other night and reached over to turn off the light and it didn't work. Poked the X10 control button several times and nothing. Crap! I had to get out of bed, go to another room and turn off the light from there. The next morning I replaced the button battery in the control and everything was fine again.

But this got me to thinking; this is the only X10 device I have left from years ago and it's time to think about doing something different. X10 failed completely in my house except for the room farthest away from everything else and it has been flaky for some time now. But, how would I implement a remote control that can just hang on the wall for months and get used maybe once a day?

Put a button on my battery operated temperature sensor! It's still in testing, but it appears to be working great, All I'd need to do is put a button on it and cause an XBee message to be sent to my house controller and then I can choose some switch device to put on the light circuit and have my button beside the bed. It would measure the temperature periodically and just wait for a button press to send the signal.

Sure, I could use my cell phone with the (brand spanking new) app I made for the house, A tablet, or a web browser. I could even call my neighbor and tell them to turn the light off for me, but I want a simple button on the nightstand that I can press without any hassle and turn the silly light off. But, how to do it without draining the battery?

Interrupts, yes, that's the ticket.

Remember from the previous (ton) of posts on this device, I put the device to sleep for 55 seconds at a time and wake it up for 5 seconds to transmit the battery level and temperature, all I should have to do is to hook a button to an interrupt pin and add some code to send a message to the house controller. At the controller I can add some code to do anything I have a controller for. I could open the garage door with it if I want to.

So, a few hours later after about 25 false starts, I got it working. I connected a little switch between pin 2 and ground of the Arduino board I'm using and it causes an interrupt which breaks the arduino out of sleep and then I just send the normal temperature message which now includes a new field to indicate that the button was pushed. At the house controller, I parse out the field, look at the value and do whatever I program in.

The reason it took several tries to get it working was because I was making it too hard. I thought I'd have to have a lot of special code to support the interrupt and proceeded down that path. It turned out the JeeLab routine I'm using for sleep already supports other interrupts, and all I had to do was check a variable and add a simple interrupt handler and everything worked ... well mostly.  Here's a code fragment showing how I sensed that the button had been pressed:

 if (millis() - savedmillis > AWAKETIME){
    Serial.print("was awake for ");
    Serial.println(millis() - savedmillis);
    delay(100); // delay to allow the characters to get out
    savedmillis = millis();
    digitalWrite(xbeeSleepReq, SLEEP); // put the XBee to sleep
    while (digitalRead(xbeeCTS) != SLEEP){} // Wait 'til it actually goes to sleep
    unsigned long timeSlept = 0;
    int result = 1;
    while (timeSlept < SLEEPTIME){
      attachInterrupt(0, buttonThing, LOW);
      result = Sleepy::loseSomeTime((unsigned int)7000);
      if (result == 0) // this is something other than a watchdog
      timeSlept = (millis() - savedmillis);
    Serial.print("was asleep for ");
    Serial.println(millis() - savedmillis);
    savedmillis = millis();
    if (result == 0)
      Serial.println("Woke up on a button press");
    digitalWrite(xbeeSleepReq, AWAKE); // wake that boy up now

I simply look at the return value from loseSomeTime() and if it's 0, then some interrupt other than the watchdog timer happened. I print a message for debugging and go to the XBee send code. The trick is in the interrupt handler:

// This is for the remote control buttons
void buttonThing(){
  buttonPressed = true;

The variable 'buttonPressed' is simply a global variable that I set to true and look at in the XBee send routine. If the variable is true, I set a new field in the mesage to say so, if it's false, I say 'nothing' in the field. I'll eventually work out what would be the best thing to put in the message, but that parts easy once you get it working. Notice that I attachInterrupt() for interrupt zero, which means button two on the Arduino, just before going to sleep, and detach it after the button is pressed. That prevents multiple interrupts from button bounce. The disadvantage to doing it this way is that it won't respond to a button press during the 5 seconds the board is awake. I don't currently consider this a problem, but that might change if I want to use it for something else.

So, now if I push the button, it gets an interrupt on pin 2, breaks out of sleep, sends the temperature and an indicator that the button was pushed to the house controller. The house controller records the temperature, then looks at the new field and does whatever I want.

The Xbee send code looks like this

void sendStatusXbee(){
  xbeeReadyWait(); // Make sure the XBee is ready

  char *command;
  if (buttonPressed)
    command = "toggle";
    command = "nothing";
  buttonPressed = false;
  sprintf(Dbuf, "{\"%s\":{\"name\":\"%s\",\"temperature\":\"%s\",\"command\":\"%s\",\"voltage\":\"%s\"}}\n", 
            deviceType, // this happens to be a temperature sensor
            deviceName, // originally read from the XBee
            dtostrf(readTemp(), 4, 1, t),
            dtostrf(readVcc(), 5, 3, v) // This is a text conversion of a float
  Serial.print(Dbuf); // notice this is only for the serial port
  sendXbee(Dbuf);     // out to the XBee
  Serial.println("Message sent");

Not much to it, just a field called 'command' in the JSON string that I can look at when the message comes in. Currently I'm sending the word 'toggle' if the button was pressed and 'nothing' if it wasn't; I love being able to read the messages, so that was descriptive enough for me.

There's another interrupt pin on the Arduino, pin 3, and I can use that also for two buttons. Maybe an on-off toggle. I could also multiplex it by putting a keypad on there. First button wakes up the Arduino and then watch for letters, digits, whatever and compose a more complex command. For now, It's just a button that turns off my bedside lights. 

Tomorrow though, it'll be a button that turns off the bedside light, turns off the outside lights, sets the thermostat to the temperature I use at night, closes the garage doors (if left open), and maybe turns off the water heater power. Call it my 'going to bed now' button.

Now, a bit about the sensor testing. I had to put in a voltage divider to measure the input power because it was driving me nuts monitoring the power after the regulator. To do this, use really high value resistors for the divider and then a .1 cap on the analog pin to ground to lower the impedance. The arduino has an input impedance of around 10K on the input pins, and if your external circuitry is too high, it will give the wrong readings. The easiest way to overcome this problem and still not use very much power is to use a cap. I used 10 Meg and 1 Meg, which put my quiescent current below a micro amp, that should be fine.

So watching it run on the almost dead batteries I installed, it started failing at around 3.6 volts. The regulator on the Pro Mini I'm using was junk. First, it didn't regulate well, the voltage wandered around 3.9 V and eventually dropped off to nothing when the supply went to 3.6. This is not what the spec sheet says. Fine, I have a solution to that, I'll just put a MCP1700 regulator in place and started it back up. Take a look at the specs for the MCP1700, it's a totally awesome low current, low dropout regulator designed for this kind of thing.The new regulator worked great and I've been running with 'dead' batteries for several days now.

I did kind of mess up though. I should have ordered a different MCP1700. I chose the 3.3V version, and I think I should have gone for a lower voltage. Fortunately, they cost me 37 cents each, so I haven't lost much money if I decide to go lower. I'll know more when I hit the cut off point on the 3.3V and how it performs when I bottom out the batteries.

Just for giggles, here's a picture of it right now:

I thought it was a mess of wires a few weeks ago, now you can't see some of the components for the wires connecting things. I haven't tried to condense it yet, I'll get to that later. The new switch is between the batteries and the other stuff so I can get to it. I plan on actually using it for a while to be sure it will do the job for me.

Overall though, this little bundle of wires has really performed well. This learning curve has actually been fun and gave me ideas for other things I can build around the house. Remember, it currently senses temperature, it could easily sense anything else you want to throw at it. Suppose you put a pressure pad in front of the driveway, you could tell when someone pulled in. A light sensor to tell you if the lights in the shed were left on. A moisture sensor beneath the water heater to tell you if it started leaking. An air flow sensor in one of the ducts running through the ceiling. All of these things could be at my beck and call.

One more step in my quest to conquer the world.

The next post in this series is here <link>.


  1. Dave,

    I too have problems with X10 reliability and would like to trash this part of my home automation completely but don't have a suitable replacement. Thanks for the update to your temperature sensor. I especially liked the insights on how to manage the input pins for the Arduino.

    Tradition Rider

  2. Not sure of best place to ask, so doing it here: are all your Xbee's configured for HA Profile, e.g KY= eventhough most are used with your own code to send data? (I assume that your couple of 'real' ZB devices are ont he same mesh.

    I am working with Digi's Xbee-Zigbeelib (on windows) to build a sensor network for the rooms in my house. Partially ported to Atmel Studio/AVR as well but not done to point of knowing the code size yet.

  3. No, I actually have two controllers running right now. One of them runs the Iris devices I got from Lowe's and the other runs my own network inside the house. When I work with the Centralab devices, I crank up another one, so for a while I have three of them.

    I haven't set down with the parameters in front of me to see if I can combine two of them, but it didn't look good the last time I thought about it. There's a distinct possibility I can run the Centralab devices on the same controller as my own house network, but the way the key is configured I haven't seen any way to combine the Iris with anything.

    If you come up with an idea, I'd love to hear it.

  4. I now have a Coordinator, router and end point running using the Digi Xbee-Zigbeelib from Git hub. All set up with HA profile in mind (KY is magic key etc.) I presently don't have any HA profile devices such as switches but only running as sensors. Which works fine. I also get a ZDO Device Announce when one of the devices joins the network. So at least at that level I expect the Coordinator will happily do both jobs for me.
    The Iris config though will be mutually exclusive to the Centralab as the KY value is different.

    ZS=2, KY is HA key, EE=1, EO=0
    I'll see if I can find a bargain ZB HA switch locally to try out. Moving into a house at end of June and want a sensor network running before hand :-) Your experiments and shared knowledge are VERY useful.

    1. The particular problem I have is that when you enable the ZigBee messages on an XBee, you lose the native messages. To combine my house network with either the ZigBee or Iris (AlertMe) stuff I would have to convert all my devices to use the ZigBee messages, and I'd still have to have two of them because the way AlertMe handles key exchange is different from ZigBee.

      That is so annoying.

    2. Actually though, thinking about it just now, it doesn't really matter to me if I have more than one XBee network. If I have three or even more controllers, it's just another radio around the house and I have a bunch of those already.

    3. Hi Dave
      I have ZS=2 but still get all the native messages, e.g. 0x92 for IO data at the interval that I programmed in the Xbee device. If I send out an ND for Node Discovery, I get the 0x95 responses. Or am I missing something?

    4. OK, now you've got me wondering about my memory. It wouldn't be the first time I messed up and went the wrong way because it had been more than 10 minutes since I looked at something.

      I'll check what I have and see if I'm being an idiot.

      But, I have some things I HAVE to get done first, so give me a couple of days.

  5. Ah yes, that memory thing. I use OneNote to keep track of my settings etc as I am going along. Learned the hard way.
    No rush at this end, retired and just working my way through all this fascinating stuff.

    1. Regarding missing messages in various settings: I'm still checking, but I noticed an update in the manual regarding the 'explicit' messages and it looks like it's possible. The trick is to use a specific cluster id and profile and then always use the two explicit messages to communicate.

      Once upon a time, they didn't tell me that in the manual. So, I could send everything under the HA profile, but then I'd have to visit all the devices I built and change them to use the explicit messages instead of the regular ol' send and receive. That's not going to happen short term.

      However, that does create a project for later on. I'm going to visit my devices and change the messages they send sometime this year, that'll be the time to convert them. Or, find out the manual was wrong and it doesn't work.

  6. I assume the float variables handle negative values?

  7. Well, the floats can handle them, but analog voltages can't be negative. They vary between zero and whatever you decide with a voltage divider.