The Stalker Sensor Platform : Part 6 - Power

Previously - The Stalker sensor platform : Part 5 - Logging data using Raspberry Pi

The Stalker board includes everything to use it as solar charged, battery powered stand-alone sensor platform. The board has built in connectors for a LiPo battery, and for a solar panel. There is also a dedicated charge management chip to handle the battery.

 

   1. Connecting solar panel and battery


Connecting solar panel and battery is by far the easiest part of this tutorial. Just plug them into the dedicated connectors.





Let's check how long battery powered Stalker lasts with the sketch we used until now. The current consumption is about 16mA with some higher peaks during XBEE data transmission. That means by using a 2000 mAh LiPo battery we'll get a battery life of about 125 hours which is about 5 days (without charging the battery). That's actually not that bad, but during this tutorial we'll find ways to reduce the standby current consumption to ~2mA, which allows us to achieve a outstanding standby battery life of more than 40 days!!!

 

   2. Putting the Stalker in sleep mode and waking up using interrupt


There are 2 main power consumer in our project, the XBEE module and the microcontroller of the Stalker. We can put the micro controller on sleep and reduce the current consumption of the board from ~6mA to <1mA (without XBEE module connected), that's great, but somebody has to wake up the microcontroller once every time to get the temperature and date data and send it.

We can configure the on board RTC chip to create an interrupt impulse on an specified interval. This pulse will wake up the microcontroller so that we can run our code to get the data and send it. After that we can reset the interrupt flag on the RTC chip and go to sleep again, waiting for the next interrupt impulse.





Put a solder blob between INT and PD2 to connect the interrupt pin of the RTC chip to pin 2 of the Stalker.


The following code is a appended sketch form part 3 of the Stalker tutorial series. All new lines are highlighted, there is a lot of new code, but it is heavily commented. We split the code into different tabs, like we did in the previous tutorial for a better overview.


Main tab:

#include 
#include 
#include &lt;avr/sleep.h&gt; //Needed for sleeping

OneWire  ds(8);  // pin 8
byte Int = 2;  // Define Interrupt pin
boolean clockInterrupt= false;
//Flag if a clock interrupt occurred

void setup() {
  Wire.begin();
  Serial.begin(9600);
  attachInterrupt(0, clockInt, FALLING);
  //Attach Interrupt 0 (Int 0 = pin 2)
  pinMode(Int, INPUT_PULLUP);
  //Turn on internal pull up resistor
  setalarm(); //Excecute setalarm function
  clearClockTrigger();
  //Excecute clearClockTrigger function
}

void clockInt()
//Does nothing, we need to point to a function
//to use Interrupts
{
}

void sleepNow() //Function puts Stalker to sleep
{
  sleep_enable();
  //Enables the sleep bit in the mcucr register
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  //Sleep mode is set here
  attachInterrupt(0,clockInt, FALLING);
  //Attach Interrupt again
  sleep_mode();  //Put Stalker to sleep
  // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP
  sleep_disable();  //First action after wakeup
  detachInterrupt(0);
  //Detach Interrupt to prevent to be interrupted
  //during the Stalker is awake
  clockInterrupt = true; //Set clockInterrupt flag
}

void loop() {
   if(clockInterrupt){ //If a clockInterrupt happend
     Serial.print(temp()); //Get and send temperature
     Serial.println("/" + getDate()); //Get and send date
     clearClockTrigger();
     //Excecute clearClockTrigger function
   }
   delay(200);
   sleepNow(); //Put the Stalker again to sleep
}

 

RTC tab:

String bcdToStr(int val)  {
// Convert binary coded decimal to normal decimal numbers
  if (val/16 == 0)
    return ("0" + String(val%16));
    //If vlaue is just one digit add a "0" in front
  else
    return ( String((val/16*10) + (val%16)) );
}

String getDate(){
  Wire.beginTransmission(0x68);
  //Start sending data, 0x indicator hex,
  //68 address specified by manufacturer
  Wire.write(0);  //Set pointer register 0
  Wire.endTransmission(); //Stop sending data
  Wire.requestFrom(0x68, 7); //Request 7 bytes from address 68

  String second = bcdToStr(Wire.read());
  String minute = bcdToStr(Wire.read());
  String hour = bcdToStr(Wire.read() &amp; 0b111111); //24 hour time
  String weekDay = bcdToStr(Wire.read()); //0-6 -&gt; sunday - Saturday
  String monthDay = bcdToStr(Wire.read());
  String month = bcdToStr(Wire.read());
  String year = bcdToStr(Wire.read());
  return(monthDay + "." + month + "." + year + "/" + hour + ":" + minute + ":" + second);

}
void setalarm(){
  Wire.begin();
  Wire.beginTransmission(0x68); //Start transmission
  Wire.write(0x07);            //Set pointer to register 7h
  Wire.write(0b00000000);      //Write seconds
  Wire.write(0b10000000);      //Write minutes
  Wire.write(0b10000000);      //Write hours
  Wire.write(0b10000000);      //Write day
  Wire.endTransmission();

  Wire.begin();
  Wire.beginTransmission(0x68); //Start transmission
  Wire.write(0x0E);             //Set pointer to register 0E
  Wire.write(0b00011101);       //Write control reg
  Wire.write(0b00000000);       //Write status/control reg
  Wire.endTransmission();
}

void clearClockTrigger(){
  Wire.beginTransmission(0x68); //Start transmission
  Wire.write(0x0F);             //Set pointer to register 0F
  Wire.write(0b00000000);       //Reset alarm (last bit)
  Wire.endTransmission();
  clockInterrupt=false;         //Clear flag
}

 

The "temp" tab remains untouched, please find it at part 3 of the tutorial series.


In this example we set the RTC chip to create an interrupt impulse once a minute (we set the alarm in setalarm() function). If you want you use other values please refer to the datasheet of the RTC chip DSB3231.


It may happen that with this code the XBEE module isn't sending data at all. By default the sleep mode of the XBEE module in end device configuration is set to cycle sleep, which means that after a defined amount of time (default 1.3 seconds) the module goes into sleep mode and wakes up periodically. Until now we used a sending interval of ~1 sec, in that way the module isn't sleeping at all. By setting the sending interval to 1 min we have to wake up the XBEE module somehow.

 

   3. Waking up a the XBEE module to send data


The XBEE module causes the main power consumption on the board. There are two ways to reduce it:

  • Shut down the supply voltage for the XBEE module
  • Wake the XBEE module up using pin 9 of the XBEE module



The Stalker board provides a circuit to simply shut down the supply voltage for the XBEE module. That may seem like the most power saving variant, but it might cause connection problems because the network has to be re-established every time the XBEE module is switched on. Therefore we prefer to use the second option.





Connect Pin 9 of the XBEE module to pin 3 of the Stalker. We're going to use this pin to wake up the XBEE module.


The sleep control pin of the XBEE modul is by default disabeld, that's why we have to change the configuration using again the X-CTU tool.





Set the Sleep Mode to 5 - CYCLIC SLEEP PIN-WAKE, to enable the sleep pin of the XBEE module.


Now lets add some code to wake up the XBEE module when needed. All the changes are done in the main tab:

...
OneWire  ds(8);  // pin 8
byte XBEE_sleep = 3; //Pin 3 = XBEE sleep pin
byte Int = 2; // Define Interrupt pin
...

First we define which pin is our XBEE_sleep pin.

...
void setup() {
  Wire.begin();
  Serial.begin(9600);
  pinMode(XBEE_sleep, OUTPUT);
  //Set XBEE sleep pin as output
  pinMode(Int, INPUT_PULLUP);
  //Turn on internal pull up resistor
  attachInterrupt(0, clockInt, FALLING);
  //Attach Interrupt 0 (Int 0 = pin 2)
  digitalWrite(XBEE_sleep,LOW);
  //Wake up XBEE Module
  setalarm();
...

Then we set the pinMode to output and set the pin initially low to wake up the XBEE module at startup for an optional initialization message.

...
void loop() {
   if(clockInterrupt){ //If a clockInterrupt happened
     digitalWrite(XBEE_sleep, LOW); //Wake up XBEE module
     Serial.print(temp()); //Get and send temperature
     Serial.println("/" + getDate());
     //Get and send date
     clearClockTrigger();
     //Execute clearClockTrigger function
   }
   digitalWrite(XBEE_sleep, HIGH);
   //Put XBEE sleep pin high again
   delay(200);
   sleepNow(); //Put the Stalker again to sleep
...

Finally we're going to wake up the XBEE module by setting the XBEE_sleep pin low. At the end of every interrupt session set the pin again to high level.

 

  4. Adding a hardware wired station serial number


If we want to have several Stalker sensor platforms  sending data to one collector (the Raspberry Pi in our case), then we have to distinguish which station is sending data. Because we want to use the same code for every station we are generating a serial number reading in 4 pins of the Stalker. In that way we can create  16 (2^4) different station serial numbers by hardwiring  pins to GND. The default serial number ( no pins connected to GND) is 15 because we enabled the internal pull up resistors at this pins and binary 1111 is 15.


Lets have a look at the code (main tab):

...
OneWire  ds(8);  //Pin 8
byte SN_byte0 = 4;
//Station number byte 0 = pin 4
byte SN_byte1 = 5;
//Station number byte 1 = pin 5
byte SN_byte2 = 6;
//Station number byte 2 = pin 6
byte SN_byte3 = 7;
//Station number byte 3 = pin 7
byte XBEE_sleep = 3; //Pin 3 = XBEE sleep pin
...

Here we are defining the pins which we're going to use to generate the serial number.

...
void setup() {
  Wire.begin();
  Serial.begin(9600);
  pinMode(SN_byte0, INPUT_PULLUP);
  pinMode(SN_byte1, INPUT_PULLUP);
  pinMode(SN_byte2, INPUT_PULLUP);
  pinMode(SN_byte3, INPUT_PULLUP);
  pinMode(XBEE_sleep, OUTPUT);
  //Set XBEE sleep pin as output
...

Then we're activating the internal pull up resistor of the station serial number pins.

 

...
     Serial.print(temp()); //Get and send temperature
     Serial.println("/" + getDate() + "/" + getSN());
     //Get and send date and station serial number
     clearClockTrigger();
...

The last change in the main tab is to call the "getSN()" function to get the serial number. We're going create this function in a new tab called SN.


SN:

int getSN(){
  //Reads in the 4 pins and returns a number from 0-15
  byte SN_byte0_state = digitalRead(SN_byte0);
  byte SN_byte1_state = digitalRead(SN_byte1);
  byte SN_byte2_state = digitalRead(SN_byte2);
  byte SN_byte3_state = digitalRead(SN_byte3);
  // Reads in the serial number pins
  return ((SN_byte3_state &lt;&lt; 3) + (SN_byte2_state &lt;&lt; 2) + (SN_byte1_state &lt;&lt; 1) + SN_byte0_state);
  // creates bitwise the Serial Number from the 4 inputs
}



The getSN function reads in first all 4 pins we defined as serial number pins and creates then a serial number by shifting every bit binary in it's position. In that way we get a serial number with a value from 0 to 15 depending on the pin states.

 

5. Battery voltage charging status


The Stalker board provides also some circuits to measure the current battery voltage and charging status. We can't connect the battery voltage directly to one of the analog input pins of the micro controller because the battery voltage exceeds 3.3V, we need a voltage divider. Luckily the Stalker has all the needed circuits on board.





The voltage value on the pin is < 1V, thats why we can use the internal voltage reference for the analog inputs for a better accuracy. We have to set the reference voltage in the main tab.


Main tab:

...
  //Attach Interrupt 0 (Int 0 = pin 2)
  analogReference(INTERNAL);
  //Set reference voltage for the analog inputs to 1.1V
  digitalWrite(XBEE_sleep,LOW);
...

Here were setting the analog reference to internal.

...
     Serial.print(temp()); //Get and send temperature
     Serial.print("/" + getDate() + "/" + getSN() + "/");
     //Get and send date and station serial number
     Serial.print(getbat());
     //Get battery voltage
     Serial.print("/");
     Serial.println(read_charge_status());
     //Get charging status
     clearClockTrigger();
...

Then we have to send the result of the two functions "getbat()" and "read_charge_status()" to the XBEE module.


We're going to create this two new functions in a new tab for the battery voltage measurement and charging state detection.


Bat tab:

float getbat(){
  //Reads in the pin connected to the voltage divider and
  //returns the voltage value of the battery
  int raw_bat_val = analogRead(A7);
  float bat_voltage=  raw_bat_val * (1.1 / 1024)* (10 + 2) / 2;
  return bat_voltage;
}



First we read in the pin connected to the on board voltage divider, then we have to convert the A/D value in the battery voltage value and return it.


The charging circuit on the Stalker signalizes with different voltage values on analog pin A6 what's the actual charging status.


Bat tab:

...

byte read_charge_status()
{
  byte CH_Status=0;
  int ADC6=analogRead(A6);

  if(ADC6&gt;900)//Charge circuit is sleeping (battery mode)
  {
    CH_Status = 0;
  }
  else if(ADC6&gt;550) //Charging battery
  {
    CH_Status = 1;
  }
  else if(ADC6&gt;350) //Battery fully charged
  {
    CH_Status = 2;
  }
  else //Error
  {
    CH_Status = 3;
  }
  return CH_Status;
}

In the "read_charge_status()" function we're returning a status number depending on the voltage level on the charge status pin A6 of the Stalker.

 

A data string now looks like:


25.62/11.03.00/01:44:01/15/3.67/0


That means:


Temperature: 25.62 °C
Date: 11.03.00 (date not set)
Time: 01:44:01
Station: 15
Battery voltage: 3.67 V
Charging status: 0 (battery mode - not charging)


Here you can find the full code.

 

   6. Further application ideas


At the end of our Stalker tutorial we want to give you some additional ideas for further improvements:

  • Check if data was sent properly through the XBEE.
  • Backup data on a micro SD-Card (the Stalker board has a built in micro SD-Card slot) and send data after a longer interval to save additional power (e.g. every 10 data readings).
We hope you've enjoyed this tutorial series, have fun with your Stalker!!