preload
Jul 29

Two motion sensors v2 are in use right now, yippee!

I upgraded an existing v1 sensor to a v2 and built a new one. The v2 sensor specs are very different from the v1 but much more promising, yet all i needed to do to was soldering the digital output of the PIR to another JeeNode port (namely ATmega INT1) and use a different digital port for serial I/O with the XBee. Oh, and upload a new sketch of course. Now i have 2 identical v2 sensors:

v1 upgraded to v2

Looking back at how this motion sensor evolved i am wondering, why didn’t i think of interrupts in the first place… not very smart actually..

Or maybe i did think about interrupts but unconsciously thought it would be to hard for me to handle already, cause that’s what you read all the time: interrupts are a tough subject! Maybe it is, but i didn’t notice that yet! :-)

So now I’ve got 2 motion sensors with the following features:

  • powered by 3 * 1.2V, 2000 mAh rechargeable batteries;
  • PIR with digital output, 100µA standby power usage and 5m detection range;
  • a JeeNode that’s effectively running only a total of about 180 seconds per day (0.2 %), even with 350-400 motion reports and lots of heartbeats. The rest of the time the JeeNode is in power down mode;
  • it’s ZigBee based which is better than 433 MHz in my opinion, especially when you’ve got a lot of sensors;
  • relatively small sensor housing compared to commercial (mostly big and ugly) products;
  • completely DIY so everything is how i want it to be;
  • interrupt-based, the best you can get I’d say;
  • need i go on?  :-)

I’m gonna monitor this sensor very closely in the coming months and learn from it. Here you see one of the sensors being used in the kitchen:

Motion v2 in the kitchen

The sketch that’s currently running on these sensors can be found here.

Finished? Almost; some loose ends in the sketch. Maybe it can be made even smaller/faster/less energy consuming. But I’ll look into that when the 3rd sensor is built.

Tagged with:
Jul 27

Here it is, Motion Sensor v2. Not much different from v1 really, the only thing that has changed is that the PIR output has moved to an interrupt pin. And the sketch looks somewhat different here and there. Later this week i’ll build my 2nd motion sensor and start using it right away; i’m very curious about the results! I’ll also change my 1st motion sensor that’s already in use, to an interrupt driven model. Don’t want to wait for what will happen with those batteries…

The JeeNode is completely powered down most of the time. It only gets woken up by the Watchdog timer every 8 seconds or when motion is detected. But even when motion is detected, it’s powered down most of the time, cause a ‘blip’ sent to my Domotica System every 4 seconds during continuous motion is more than enough.

When the motion interrupt is triggered, the ‘blip’ is sent to my system, followed by a power down for 4 seconds without interrupt trigger set. When those 4 seconds have passed (wake up is done by the watchdog timer) a power down is done with interrupt trigger and the whole loop starts all over again. Capice? It doesn’t get much easier than this :-)

And of course there’s a heartbeat sent every 64 (8 times 8 ) seconds.

Tagged with:
Jul 26

While i was quite happy yesterday with the new way i’m going to handle things on the JeeNode regarding the motion sensor (PIR), i already knew i wasn’t finished yet; this was nice, but only a first step.

Cause what i had created was an IRQ being fired when the input becomes low. Well, the PIR output becomes high when there’s motion detected, so this is unusable, actually. For a brief moment i considered using a transistor as a NOT gate to invert the PIR output, but there had to be an easier way. Why would you only be able to activate an External Interrupt when a pin becomes low?? I read some topics on various forums, but those were very contradictory; some said i could, some said otherwise. OK, in that case, i’ll sort it out myself .. cause i really need interrupts based on a rise or a change!

Page 71 of the ATmega datasheet learned me that INT0 and INT1 indeed have Interrupt Sense Control and it’s only a matter of setting the right bits in the External Interrupt Control Register A, aka EICRA:

Bit 3 and 2 of EICRA

So it can be done, only thing is to figure out how…

The Arduino attachInterrupt() function has a 2nd parameter with which you can control what triggers an interrupt; the values are the constants LOW, CHANGE, RISING and FALLING (found in wiring.h). So why didn’t that nice Sleep library i found earlier have this feature? All i can do is call this function:

void SleepClass::powerDownAndWakeupExternalEvent(uint8_t interruptNumber)
{
 attachInterrupt(interruptNumber, SleepClass::external_event_handler, LOW);
 set_sleep_mode_and_sleep(SLEEP_MODE_PWR_DOWN);
 detachInterrupt(interruptNumber);
}

OK; changing things in the library depending on the kind of Sense Control i need for a specific sensor type is not an option of course, so i added a 2nd parameter to that function:

void SleepClass::powerDownAnd...Event(uint8_t interruptNumber, int mode)
{
 attachInterrupt(interruptNumber, SleepClass::external_event_handler, mode);
 set_sleep_mode_and_sleep(SLEEP_MODE_PWR_DOWN);
 detachInterrupt(interruptNumber);

}

That’s better; now i can choose whatever Sense Control I want; I’ve checked all of them right away:

Sleep.powerDownAndWakeupExternalEvent(0, RISING);

What a surprise, its working, all of them! RISING is just what i need right now for the PIR; CHANGE is what i would need for a door sensor.

Now i’m already modifying libraries and I can even understand what

EICRA = (EICRA & ~((1 << ISC00) | (1 << ISC01))) | (mode << ISC00);

does… i wonder where or when (if ever) this will end…

But I’m still not finished! Cause with all this powering down going on, it has become increasingly hard to keep a notion of time; millis() has become useless cause how can i determine the time that has passed since the last power down? On the other hand, do i really need to know that? Time will tell :-)

Tagged with:
Jul 25

Having a first set of batteries empty wasn’t  funny.. they lasted only 7 weeks! Way to short. And i kept on thinking that it might not have been caused by a bad battery.. what could i do next? Wait another 7 weeks to find out that I’m wrong? Neh.. that didn’t feel the right thing to do; Power down as much as possible and use an external interrupt would possibly give the best results anyway, so why not start with that right now!

I started with attaching a push-button to an Arduino, using a pull-up resistor to keep the input high when the button isn’t pressed:

Arduino with push-button

After that i started reading about interrupts. Here’s a table that shows the Wake-up sources that are available in the various sleep modes:

ATmega48PA/88PA/168PA/328P Wake-up sources

Both things i need are available: INT0 and/or INT1 and the Watchdog Timer, so it was time to start sketching :-)

After 5 different sketches with all the same miserable results (freezing Arduino), i finally found a sleep library that did exactly what i wanted. I combined this library with code i had found earlier regarding setting up and using the Watchdog Timer, and what i have now, is just what i need. Below a sample of the Serial Monitor output and here is the sketch that produced it.

39171 I'm awake, caused by the WDT
39205 Sleeping...
39327 I'm awake, caused by the WDT
39362 Sleeping...
39484 I'm awake, caused by the WDT
39519 Sleeping...
39640 I'm awake, caused by the WDT
39674 Sleeping...
39796 I'm awake, caused by INT0
Doing some work...finished!
40257 Sleeping...
40377 I'm awake, caused by INT0
Doing some work...finished!
40839 Sleeping...
40960 I'm awake, caused by INT0
Doing some work...finished!
41421 Sleeping...
41542 I'm awake, caused by the WDT
41577 Sleeping...
41698 I'm awake, caused by INT0
Doing some work...finished!
42160 Sleeping...
42280 I'm awake, caused by the WDT
42315 Sleeping...
42437 I'm awake, caused by the WDT
42472 Sleeping...
42593 I'm awake, caused by the WDT
42628 Sleeping...

etc. etc.

Cool stuff! :-) Let’s see how we can change the motion sensor to make use of this!

Tagged with:
Jul 24

XBee Supply Voltage dropping fast!

Oops… the Supply Voltage on my motion sensor is already down to 3060 millivolts. Suddenly it’s dropping very fast; this sensor has been operational for more then 7 weeks now, with IIRC 2 new and 1 older rechargeable battery of a brand with which i’ve had bad experiences before. I hope that’s what is causing this, cause if not, i have some work to do :-(

The other 2 battery powered sensors are still doing fine, BTW:

Supply Voltages (click to view the realtime values)

Tagged with:
Jul 17

Another project is almost finished; my JeeLabs Lux Plug moved inside an enclosure.

JeeNode Lux Plug

JeeNode Lux Plug

The Lux Plug is inside the enclosure with the rest: battery holder, JeeNode and XBee module. In my opinion there was no need to put the sensor in it’s own small enclosure like i did with the motion sensor; there are enough places where i can place this enclosure in such a way that the sensor can measure the light intensity and still be relatively invisible. So this Plug is fitted directly into one of the 4 JeeNode ports and a hole in the enclosure is enough for this sensor to work well.

dsc_3396c_resize2

Another fun project! I saw a Z-Wave sensor a few days ago, costing >100 Euros. Ok, it has a LCD and a beep tone ofcourse (who can live without that!), but basically, it does the same thing: measuring lux! In my opinion, i got more value for money… but hey, who am i :-)

With a bit of soldering, some code and a lot of fun you can create your own!

#include <Ports.h>
#include <RF12.h>
#include <avr/sleep.h>
#include <NewSoftSerial.h>

#define DEBUG 0

NewSoftSerial XBSerial = NewSoftSerial(2, 3);
PortI2C myBus (1);
LuxPlug sensor (myBus, 0x39);

int pinCTS=6;                         // to monitor CTS
int pinXBee=5;                        // to Control XBee on/off

static int SampleInterval = 30000;
static int HeartBeatInterval = 90000;
static int CTS=0;                     // value of XBee CTS pin

static void lowPower (byte mode) {
    // prepare to go into power down mode
    set_sleep_mode(mode);
    // disable the ADC
    byte prrSave = PRR, adcsraSave = ADCSRA;
    ADCSRA &= ~ bit(ADEN);
    PRR &= ~ bit(PRADC);
    // zzzzz...
    sleep_mode();
    // re-enable the ADC
    PRR = prrSave;
    ADCSRA = adcsraSave;
}

EMPTY_INTERRUPT(WDT_vect); // just wakes us up to resume

static void watchdogInterrupts (uint8_t mode) {
    MCUSR &= ~(1<<WDRF); // only generate interrupts, no reset
    cli();
    WDTCSR |= (1<<WDCE) | (1<<WDE); // start timed sequence
    WDTCSR = bit(WDIE) | mode; // mode is a slightly quirky bit-pattern
    sei();
}

static byte loseSomeTime (word msecs) {
    // only slow down for periods longer than twice the watchdog granularity
    if (msecs >= 32) {
        for (word ticks = msecs / 16; ticks > 0; --ticks) {
            lowPower(SLEEP_MODE_PWR_DOWN); // now completely power down
            // adjust the milli ticks, since we will have missed several
            extern volatile unsigned long timer0_millis;
            timer0_millis += 16L;
        }
        return 1;
    }
    return 0;
}

static MilliTimer SampleTimer;     // Interval for reading a sample from the BMP085
static MilliTimer HeartBeatTimer;  // forced maximum interval (Heartbeat)
word payload;

static byte periodicSleep (word msecs) {
    // switch to idle mode while waiting for the next event
    lowPower(SLEEP_MODE_IDLE);
  // see http://news.jeelabs.org/2009/12/18/battery-life-estimation/
  if (loseSomeTime(SampleTimer.remaining()))
    SampleTimer.set(1); // really did a power down, trigger right now

  // return true if the time has come to do something meaningful
  return SampleTimer.poll(msecs);
}

static unsigned long tBusy0;
static unsigned long tBusy1;
static unsigned long tBusyPrev;

static void Send() {

  unsigned long tXB1;    // time XBee was woken up
  unsigned long tXB0;    // time XBee was put to sleep

  HeartBeatTimer.set(0);  // disable heartbeat

  CTS = HIGH;
  tXB0=millis();
  // wake up Xbee
  digitalWrite(pinXBee,LOW);

  // wait for CTS to become LOW
  do
  {
    CTS=digitalRead(pinCTS);
  } while (CTS != LOW);
  tXB1=millis();
  // wait 2 msec otherwise data will be received all messed up
  delay(3);

#if DEBUG
  Serial.print(millis());
  Serial.print(" Send ");
  Serial.print(tXB1-tXB0);
  Serial.print(" ");
  Serial.println(payload);
#endif

  // send dummy data
  XBSerial.print(tBusyPrev);
  XBSerial.print(" ");
  XBSerial.print(tXB1-tXB0);
  XBSerial.print(" ");
  XBSerial.println(payload);

  // wait for XBee to finish transmission
  delay(4);

  // switch off XBee
  digitalWrite(pinXBee,HIGH);    

  HeartBeatTimer.set(HeartBeatInterval);
}

int ReadSensor() {

#if DEBUG
  Serial.print(millis());
  Serial.println(" Readsensor");
#endif

    sensor.begin();
    sensor.getData();
    payload = sensor.calcLux();

    return 1;        // always report for now.
}

void setup() {

  // setup XBee
  pinMode(pinXBee,OUTPUT);
  digitalWrite(pinXBee,LOW);
  pinMode(pinCTS,INPUT);
  digitalWrite(pinCTS,LOW);
  delay(10);

  XBSerial.begin(9600);
#if DEBUG
  Serial.begin(9600);
#endif
  // give XBee some time to join PAN
  delay(5000);

  // let the world know we're here
  XBSerial.println("[Lux002]");
#if DEBUG
  Serial.println("[Lux002]");
#endif
 delay(5);

 watchdogInterrupts(0); // 16ms
}

void loop() {
    if (periodicSleep(SampleInterval)) {
      // sensor values changed or heartbeat interval elapsed?
      tBusy0 = millis();
      if (ReadSensor() || (HeartBeatTimer.poll(HeartBeatInterval)))
      {
         Send();
         tBusy1 = millis();
         tBusyPrev = tBusy1-tBusy0;
       }
    }
}
Tagged with:
Jun 26

This is what you get when a battery powered XBee is struggling to stay alive cause the batteries aren’t supplying enough ‘juice’ anymore:

FF FF FF FF FF FD 7A 1F 33 40 00 A2 13 00 01 FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF 00 1D 00
01 53 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF 00
00 73 A0 73 B6 70 E0 73 CC 73 E2 06 40 75 5C 76 F0 76 3C 76 B4 76 3C
76 3C 73 F8 74 0E 74 22 75 1C 75 EC 75 60 74 2C 46 50 E6 00 01 02 00
00 00 00 E8 00 01 02 00 00 00 00 F3 E0 58 A0 FF FE 00 EC 30 BC 75 CA
A8 D8 B6 D5 9F FD 5A 7B FF FE 00 00 FF FE 00 00 FF FE 00 00 FF FE 00
00 FF FE 00 00 FF FE 00 00 FF FE FF FF 79 BE 01 4A 01 B0 01 62 01 4E
01 66 01 AC 01 60 01 82 01 9C 01 68 01 B2 01 94 08 1C 08 29 08 34 02
etc. etc. ...

For those not really experienced in recognizing XBee API frames from a byte stream: there’s not a single valid frame to be found inside all those bytes. But this is what my Coordinator received today.

But that’s OK; i intentionally left the XBee running on low batteries; I wanted to see what would happen. My code that should recognize valid XBee API packets just got it’s ultimate real life test. Cause normally, the byte stream coming from the Zigbee Coordinator is so clean you can hardly test it without writing a special tool that creates a mix of garbage, valid and invalid frames. Now i know for sure my code works! :-)

Another good thing i saw, is that this ‘dying’ XBee didn’t create that much traffic resulting in other XBee’s not being able to get their messages to the Coordinator anymore.

This is really important actually; imagine you’re on holiday and a sensor runs out of battery power; do you really want to monitor your Home Automation system for that kind of trouble while you’re at the other side of the world? Become a slave of your own system? And have to call your neighbor that feeds the cat to remove the batteries or worse ? (“I don’t care how you do it, just do it.. Cut some red wires if you have to!”) Just to keep the system running? Not me!

In the meantime, this really nice weather only makes you want to play outside; sitting behind a computer with an airco blowing cold air in your neck is not my favorite right now:

RC Cars

Batteries are being charged! :-)

Tagged with:
Jun 25

Here are the results of the test i started 3 days ago. After 3 days of monitoring the XBee supply voltage the alkaline batteries were empty and with a bit of tweaking i got the data out of the logs and into an Excel sheet so i could create a chart:

XBee Supply Voltage

XBee Supply Voltage

OK, this looks good and it’s easy to do; all you need are some changed settings on the XBee, a Domotica system that can fire the ‘IS’ remote AT Command to your XBee and your done. Well, almost :-)

The JeeNode has been running a small sketch that transmits a dummy string every 5 seconds; without any sleeping and also keeping the XBee on all the time:

#include <NewSoftSerial.h>

#define SERDBG 1
NewSoftSerial XBSerial = NewSoftSerial(2, 3);

int CTSpin = 7;
int XBpin  = 6;

static void Send () {
  // send a dummy string
  XBSerial.print(millis());
  XBSerial.print(" ");
  XBSerial.println("XBeeaaaaaa");
#if SERDBG
  Serial.print(millis());
  Serial.print(" ");
  Serial.println("XBeeaaaaaa");
#endif
}

void setup() {
  // setup XBee
  pinMode(CTSpin,INPUT);
  pinMode(XBpin,OUTPUT);
  digitalWrite(XBpin,LOW);
  digitalWrite(CTSpin,LOW);
  delay(10);

  XBSerial.begin(9600);
#if SERDBG
  Serial.begin(9600);
#endif
  // give XBee some time to join PAN
  delay(5000);

  // let the world know we're here
  XBSerial.println("[XBeeVoltage]");
#if SERDBG
  Serial.println("[XBeeVoltage]");
#endif
}

void loop() {
  delay(5000);
  Send();
}

The JeeNode stopped working at around 2/3 of the horizontal axis, so after 48 hours or so where the voltage had already dropped to 2,64 V. The XBee is still working as i write this, with a voltage of 2,3 V. I wonder how long it takes for the XBee to give up too…

Conclusion: this can definitely be used as a way to remotely monitor my battery powered sensors; Yippee!

Tagged with:
Jun 21

One thing i would really like, is being able to monitor the voltage the JeeNode and XBee are running on. Now the XBee has a %V command, specially for reading the voltage on the Vcc pin. So let’s see of i can use this.

The first thing i noticed was that the XBee i configured with the AT End Device firmware, didn’t respond to the %V command. Hmm, correct; the manual was very clear about it: the %V command is only supported by a Router  and not by a Coordinator or an End Device. And it’s those End Devices  (which will run on batteries) that i want to monitor…

OK.. next command to have a look at was the ‘IS‘ command, which stands for Force Sample; this command forces a read of all enabled digital and analog input lines and a small comment told me it could also return the supply voltage: “Analog samples are ordered sequentially from AD0/DIO0 to AD3/DIO3, to the supply voltage.”  So lets try this one, shouldn’t be to hard i guess?

So i added support for this command to my API and started sending frames to the XBee:

Sending frame:7E 00 0F 17 01 00 13 A2 00 40 33 1F 7A A2 ED 00 49 53 FB

I won’t go into detail of every byte in the frame above, but the 2 bytes marked in red are the ASCII codes for ‘I’ and ‘S’: yes, ‘IS’, the command we want to be executed on the remote XBee. The response that came back from the XBee was encouraging, however, no supply voltage was present, only the values of some random Analog input lines i enabled:

Rcvd Frame:97 01 00 13 A2 00 40 33 1F 7A A2 ED 49 53 00 01 00 00 02 02 09
FrameID=1, Command=IS, Status=0, Command Data size = 6 (01 00 00 02 02 09)

With the help of an example i found out the meaning of the Command data:

IS response example

IS response example

OK… the frames that were being received looked nice, but it’s not what i want… where’s the supply voltage? The example above doesn’t show it either.. but it must be possible, right?

After searching for more than an hour, i suddenly bumped upon a setting in the X-CTU tool (in the I/O Sampling section), that had the following description:

Configure the supply voltage high threshold.  If the supply voltage measurement equals or drops below this threshold, the supply voltage will be included in an IO sample transmission.  RANGE:0-0XFFFF

Aahh, that must be it… this setting has a default of 0, causing the supply voltage to never be included in an IO sample! I set the value to FFFF, disabled all other I/O lines on the XBee et voila:

Rcvd Frame:97 01 00 13 A2 00 40 33 1F 7A A2 ED 49 53 00 01 00 00 80 0A BC
FrameID=1, Command=IS, Status=0, Command Data size = 6 (01 00 00 80 0A BC)

This means there is 1 sample set, containing the supply voltage, and the value is: 0ABC.

Multiply this with 1200/1024 and you get the supply voltage in mV:

($0ABC =) 2748 * 1200/1024 = 3220 mV. Great!

Tomorrow i’ll add some code to my HA system to periodically send an IS command to this XBee and store the results. And then just wait and see what happens when i let a JeeNode and XBee running on alkaline batteries 24/7…

Tagged with:
Jun 20

After my problems with the MS13 sensors yesterday, i managed to find some spare time (even though, or maybe because it was father’s day) to write a sketch for the new motion sensor i created during the last couple of weeks. Always taking one step at a time, i located the first prototype of this sensor at a place where i can see the enclosure LED from the living room: i want to know how it performs…

New motion sensor

New motion sensor

Now powered by 3 rechargable NiCd batteries and with all hardware nicely put away in the enclosure. In due time the enclosure will be moved out of sight so only the PIR will be visible. I’ve decided to use 3.5 mm jack plugs to be able to disconnect the enclosure from the sensor, so i can easily take the enclosure back to my office, because i don’t think the JeeNode is already running the final version of the ‘PIR’ sketch… :-)

A new class, 2 database records and 1 line of code were enough to get the sensor working in my Domotica system:

procedure TAMN31112.ProcessInterfaceData;
begin
  Pin(1).AsBoolean:= (O.Data[1]='1');
end;

The new PIR can be viewed from my website (it’s called BP PIR):

Battery powered PIR

Battery powered PIR

The sketch is still under development; for instance, how much difference would it make if i change the DigitalRead() into a bitRead()? I’ll have to read more about that on JeeLabs … Other questions are: should i increase the heartbeat interval (currently 45 seconds) or the Motion report interval? Can i create some sort of battery power monitoring with the XBee? But that will all be investigated when i’ve finished building my second motion sensor based on JeeNode and XBee..

Tagged with: