Showing posts with label sleep. Show all posts
Showing posts with label sleep. Show all posts

Saturday, September 24, 2016

Reducing Power Consumption on Arduino Zero, MKR1000, or any SAMD21 Arduino Part 1

In this multiple part series we look at how to reduce power consumption for battery powered designs that utilize Arduino's with the Atmel SAMD21 MCU (Zero, MKR1000, etc). In part one we look at how to put the SAMD21 to sleep and wake it up with either the real time clock (RTC) or an external event on an input pin.



//***************Arduino Sketch from the video*********************.
//This code was used for a tutorial on the ForceTronics YouTube channel. It shows how to save power
//by putting Arduino's based on the SAMD21 MCU (MKR1000, Zero, etc) to sleep and how to wake them
//This code is public domain for anybody to use or modify

//#include "RTCZero.h"
#include <RTCZero.h>

/* Create an rtc object */
RTCZero rtc;

/* Change these values to set the current initial time */
const byte seconds = 0;
const byte minutes = 00;
const byte hours = 00;

/* Change these values to set the current initial date */
const byte day = 24;
const byte month = 9;
const byte year = 16;

void setup() 
{
  delay(5000); //delay so we can see normal current draw
   pinMode(LED_BUILTIN, OUTPUT); //set LED pin to output
  digitalWrite(LED_BUILTIN, LOW); //turn LED off

  rtc.begin(); //Start RTC library, this is where the clock source is initialized

  rtc.setTime(hours, minutes, seconds); //set time
  rtc.setDate(day, month, year); //set date

  rtc.setAlarmTime(00, 00, 10); //set alarm time to go off in 10 seconds
  
  //following two lines enable alarm, comment both out if you want to do external interrupt
  rtc.enableAlarm(rtc.MATCH_HHMMSS); //set alarm
  rtc.attachInterrupt(ISR); //creates an interrupt that wakes the SAMD21 which is triggered by a FTC alarm
  //comment out the below line if you are using RTC alarm for interrupt
 // extInterrupt(A1); //creates an interrupt source on external pin
  
  //puts SAMD21 to sleep
  rtc.standbyMode(); //library call
  //samSleep(); //function to show how call works
}

void loop() 
{
  //do nothing in main loop
}

//interrupt service routine (ISR), called when interrupt is triggered 
//executes after MCU wakes up
void ISR()
{
  digitalWrite(LED_BUILTIN, HIGH);
}


//function that sets up external interrupt
void extInterrupt(int interruptPin) {
  pinMode(interruptPin, INPUT_PULLUP);
  attachInterrupt(interruptPin, ISR, LOW);
}

//function to show how to put the 
void samSleep()
{
  // Set the sleep mode to standby
  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
  // SAMD sleep
  __WFI();
}

//**********************Changed "begin" function from RTCZero Library**************
void RTCZero::begin(bool resetTime)
{
  uint16_t tmp_reg = 0;
  
  PM->APBAMASK.reg |= PM_APBAMASK_RTC; // turn on digital interface clock
  //config32kOSC();

  // If the RTC is in clock mode and the reset was
  // not due to POR or BOD, preserve the clock time
  // POR causes a reset anyway, BOD behaviour is?
  bool validTime = false;
  RTC_MODE2_CLOCK_Type oldTime;

  if ((!resetTime) && (PM->RCAUSE.reg & (PM_RCAUSE_SYST | PM_RCAUSE_WDT | PM_RCAUSE_EXT))) {
    if (RTC->MODE2.CTRL.reg & RTC_MODE2_CTRL_MODE_CLOCK) {

      validTime = true;
      oldTime.reg = RTC->MODE2.CLOCK.reg;
    }
  }
  // Setup clock GCLK2 with OSC32K divided by 32
  GCLK->GENDIV.reg = GCLK_GENDIV_ID(2)|GCLK_GENDIV_DIV(4);
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
    ;                                                         /*XOSC32K*/
  GCLK->GENCTRL.reg = (GCLK_GENCTRL_GENEN | GCLK_GENCTRL_SRC_OSCULP32K | GCLK_GENCTRL_ID(2) | GCLK_GENCTRL_DIVSEL );
  while (GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY)
    ;
  GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK2 | (RTC_GCLK_ID << GCLK_CLKCTRL_ID_Pos)));
  while (GCLK->STATUS.bit.SYNCBUSY)
    ;

  RTCdisable();

  RTCreset();

  tmp_reg |= RTC_MODE2_CTRL_MODE_CLOCK; // set clock operating mode
  tmp_reg |= RTC_MODE2_CTRL_PRESCALER_DIV1024; // set prescaler to 1024 for MODE2
  tmp_reg &= ~RTC_MODE2_CTRL_MATCHCLR; // disable clear on match
  
  //According to the datasheet RTC_MODE2_CTRL_CLKREP = 0 for 24h
  tmp_reg &= ~RTC_MODE2_CTRL_CLKREP; // 24h time representation

  RTC->MODE2.READREQ.reg &= ~RTC_READREQ_RCONT; // disable continuously mode

  RTC->MODE2.CTRL.reg = tmp_reg;
  while (RTCisSyncing())
    ;

  NVIC_EnableIRQ(RTC_IRQn); // enable RTC interrupt 
  NVIC_SetPriority(RTC_IRQn, 0x00);

  RTC->MODE2.INTENSET.reg |= RTC_MODE2_INTENSET_ALARM0; // enable alarm interrupt
  RTC->MODE2.Mode2Alarm[0].MASK.bit.SEL = MATCH_OFF; // default alarm match is off (disabled)
  
  while (RTCisSyncing())
    ;

  RTCenable();
  RTCresetRemove();

  // If desired and valid, restore the time value
  if ((!resetTime) && (validTime)) {
    RTC->MODE2.CLOCK.reg = oldTime.reg;
    while (RTCisSyncing())
      ;
  }

  _configured = true;
}

Thursday, September 15, 2016

Reducing Power Consumption on the Arduino Enabled ESP8266

In this tutorial we look at how to reduce the power consumption of your Arduino enabled ESP8266 WiFi module for battery powered applications.



//**************Arduino code: ESP8266_Sleep_Example *************
/*
 This sketch was created for a tutorial on saving power using the ESP8266 with the Arduino IDE 
 That was presented on the ForceTronics YouTube Channel. This code is public domain for anybody to use
 at their own risk
 */
#include <Arduino.h>
#include <ESP8266WiFi.h> //not using WiFi but need for some of the sleep commands

const int LED_PIN = 5; // Thing's onboard, green LED
const int sleepTimeS = 5; //sets deepsleep time to 5 sec

void setup() 
{
  pinMode(LED_PIN,OUTPUT); //setup LED pin 
  flashLED(); //function that flashes LED on and off
  WiFi.forceSleepBegin(0); //this function turns on modem sleep mode (turns off RF but not CPU)
  flashLED();
  WiFi.forceSleepWake(); //wakes modem up from sleep mode
  flashLED();
  // deepSleep time is defined in microseconds. Multiply seconds by 1e6 
  ESP.deepSleep(sleepTimeS * 1000000); //Can also add mode setting: WAKE_RF_DEFAULT, WAKE_RFCAL, WAKE_NO_RFCAL, WAKE_RF_DISABLED
  //ESP.deepSleep(0,WAKE_RF_DEFAULT); //In Deep-sleep mode, the chip can be woken up and initialized by a low-level pulse
    //generated on the EXT_RSTB pin via an external IO
}

void loop() 
{ //do nothing in the loop
}

//function that flashes LED at 1.5sec intervals
void flashLED() {
  digitalWrite(LED_PIN, HIGH);
  delay(1500);
  digitalWrite(LED_PIN, LOW);
  delay(1500);
}

//**************Arduino code: ESP8266_Sleep_Cloud_Example *************
/*
 This sketch was used for a tutorial on saving power with the ESP8266 using Arduino IDE 
 That was presented on the ForceTronics YouTube Channel. This code is public domain for anybody to 
 use or modify at your own risk

 Note that this code was leveraged from a Sparkfun example 
 on using their cloud service Phant
 */
#include <Arduino.h>
// Include the ESP8266 WiFi library.
#include <ESP8266WiFi.h>
// Include the SparkFun Phant library.
#include <Phant.h>

//Set your network name and password
const char WiFiSSID[] = "YourNetwork";
const char WiFiPSK[] = "YourPassword";

//define constants for pin control and node number
const int LED_PIN = 5; // Thing's onboard, green LED
const int ANALOG_PIN = A0; // The only analog pin on the Thing
const int NODE_NUM = 1; //node identifier

//declare phant address and security keys
const char PhantHost[] = "data.sparkfun.com";
const char PublicKey[] = "YourPublicKey";
const char PrivateKey[] = "YourPrivateKey";

//specify the rate that you post data to cloud
const unsigned long postRate = 15000;
unsigned long lastPost = 0;
const int sleepTimeS = 15;

void setup() 
{
  initHardware(); //setup arduino hardware
  connectWiFi(); //Connect your WiFi network
  digitalWrite(LED_PIN, HIGH);
  while (postToPhant() != 1) //post to cloud in setup code because we will reset after sleep
  {
    delay(100);
  }
  digitalWrite(LED_PIN, LOW);
  // deepSleep time is defined in microseconds. Multiply
  // seconds by 1e6 
  ESP.deepSleep(sleepTimeS * 1000000); //This is where we go to sleep, will reset upon waking up
}

void loop() 
{ //do nothing here
}

//function used to connect to WiFi network and where we set transmit power level
void connectWiFi()
{
  byte ledStatus = LOW;
  //Set transmit power level
  WiFi.setOutputPower(0.0); //sets transmit power to 0dbm to lower power consumption, but reduces usable range
  // Set WiFi mode to station (as opposed to AP or AP_STA)
  WiFi.mode(WIFI_STA);
  // WiFI.begin([ssid], [passkey]) initiates a WiFI connection
  // to the stated [ssid], using the [passkey] as a WPA, WPA2,
  // or WEP passphrase.
  WiFi.begin(WiFiSSID, WiFiPSK);
  
  // Use the WiFi.status() function to check if the ESP8266
  // is connected to a WiFi network.
  while (WiFi.status() != WL_CONNECTED)
  {
    // Blink the LED
    digitalWrite(LED_PIN, ledStatus); // Write LED high/low
    ledStatus = (ledStatus == HIGH) ? LOW : HIGH;
    
    // Delays allow the ESP8266 to perform critical tasks
    // defined outside of the sketch. These tasks include
    // setting up, and maintaining, a WiFi connection.
    delay(100);
    // Potentially infinite loops are generally dangerous.
    // Add delays -- allowing the processor to perform other
    // tasks -- wherever possible.
  }
}

//function that sets up some initial hardware states
void initHardware()
{
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
}

//this function takes data and posts it to the cloud
int postToPhant()
{
  // LED turns on when we enter, it'll go off when we 
  // successfully post.
  digitalWrite(LED_PIN, HIGH);
  
  // Declare an object from the Phant library - phant
  Phant phant(PhantHost, PublicKey, PrivateKey);
  //These functions build data and field string that will be sent to phant cloud
  phant.add("adcdata", analogRead(ANALOG_PIN));
  phant.add("wifinode", NODE_NUM);
  
  // Now connect to data.sparkfun.com, and post our data:
  WiFiClient client; //declare client object that will post the data
  const int httpPort = 80; //specify port to post through
  
  if (!client.connect(PhantHost, httpPort)) //attempt to connect to phant
  {
    // If we fail to connect, return 0.
    return 0;
  }
 //Send post to phant
  client.print(phant.post());
  
  // Read all the lines of the reply from server and print them to Serial
  while(client.available()){
    String line = client.readStringUntil('\r');
    //Serial.print(line); // Trying to avoid using serial
  }
  
  // Before we exit, turn the LED off.
  digitalWrite(LED_PIN, LOW);
  
  return 1; // Return success
}
 

Monday, June 20, 2016

Building a Wireless Sensor Network with the nRF24L01 Part 6


In part 6 we look at the final hardware design, we switch to the TMRh20 library for the nRF24L01, and we look at a library wrapper that makes getting started with your own wireless sensor network real easy. Go to ForceTronics.com to purchase a wireless flex node and go to Github to access the code and PCB design files.

Monday, February 8, 2016

The Watchdog Timer on Arduino

In this video we take a look at the Watchdog Timer on Arduino and the three different ways to configure it. We show a simple example using the Watchdog Timer and you can find the code from the example below.



//**********************Arduino Code*******************************
#include <avr/wdt.h> //Watch dog timer functions
#include <EEPROM.h> //library for using EEPROM

// Pin 13 has an LED connected on most Arduino boards.
int led = 13;
volatile int wSetting = 1; //variable to store WDT setting, make it volatile since it will be used in ISR

void setup() {
  wdt_disable(); //Datasheet recommends disabling WDT right away in case of low probabibliy event
  pinMode(led, OUTPUT); //set up the LED pin to output
  pinMode(2,INPUT_PULLUP); //setup pin 2 for input since we will be using it with our button
  getSettings(); //start serial settings menu to choose WDT setting
  //setup the watchdog Timer based on the setting
  if(wSetting == 1) setWDT(0b01000000); //set for interrupt
  else if(wSetting == 2) setWDT(0b00001000); //set for reset
  else setWDT(0b01001000); //set for interrupt and then reset
}

void loop() {
  
  if(!digitalRead(2)) { //check if button to reset WDT was pressed
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    wdt_reset(); //Reset the WDT 
  }
}

//This function gets the WDT setting from the user using Serial comm
void getSettings() {
  Serial.begin(57600);
  Serial.println("We just started up......");
  byte b; //variable to store interrupt / reset tracker
  if(EEPROM.get(10, b)) { //check if interrupt occurred before reset
    Serial.println("...and an interrupt occurred.....");
    b = 0; //reset variable
    EEPROM.put(10,b);
  }
  Serial.println("Select the WDT setting: 1 --> interrupt, 2 --> reset, 3 --> interrupt and reset");
  //wait for user to input setting selection
  while (!Serial.available()) { }
  wSetting = Serial.parseInt(); //get entry and ensure it is valid
  if(wSetting < 1 || wSetting > 3) {
      wSetting = 1;
   }
   Serial.print("WDT setting is ");
   Serial.println(wSetting);
   Serial.println();
   Serial.end(); //don't want to mix serial comm and interrupts together
}

/*
void setSleepInterval(byte timer) {
  sleep_enable(); //enable the sleep mode
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set the type of sleep mode. Default is Idle. power down saves the most power
  ADCSRA &= ~(1<<ADEN); //Turn off ADC before going to sleep (set ADEN bit to 0). this saves even more power
  WDTCSR |= 0b00011000;    //Set the WDE bit and then clear it when set the prescaler, WDCE bit must be set if changing WDE bit   
  WDTCSR = 0b01000000 | timer; //This sets the WDT to the interval specified in the function argument
  wdt_reset(); //Reset the WDT 
  sleep_cpu(); //enter sleep mode. Next code that will be executed is the ISR when interrupt wakes Arduino from sleep
  sleep_disable(); //disable sleep mode
  ADCSRA |= (1<<ADEN); //Turn the ADC back on
}*/

//this function setups up and starts the watchdog timer
void setWDT(byte sWDT) {
   WDTCSR |= 0b00011000;
   WDTCSR = sWDT |  WDTO_2S; //Set WDT based user setting and for 2 second interval
   wdt_reset();
}


//This is the interrupt service routine for the WDT. It is called when the WDT times out and is in interrupt mode. 
//This ISR must be in your Arduino sketch or else the WDT will not work correctly
ISR (WDT_vect) 
{
  digitalWrite(led, HIGH); 
  if(wSetting == 3) {
    byte b = 1;
    EEPROM.put(10, b);
  }
  //wdt_disable();
}  // end of WDT_vect

Thursday, December 11, 2014

Reducing Arduino’s Power Consumption Part 3

Welcome to part 3 of reducing Arduino's power consumption, a must watch series for anybody building a battery powered project with Arduino. In part 3 we will look at how to use the Watch Dog Timer like an alarm clock to wake Arduino up from sleep mode. We we also look at some additional techniques to save power.


*****************************************Arduino Code*************************************************
/*
Example program for using sleep modes and watch dog timer in Arduino. This example code was used in a sleep mode tutorial video on the ForceTronics YouTube Channel.
This code is open for anybody to use at their own risk*/
     
/*WDT BYTE variables for setting timer value
     WDTO_15MS
WDTO_30MS
WDTO_60MS
WDTO_120MS
WDTO_250MS
WDTO_500MS
WDTO_1S
WDTO_2S
WDTO_4S
WDTO_8S */
     
#include <avr/sleep.h>
//We use part of the WDT library, but have to use registers as well since library does not support interrupt mode for WDT
#include <avr/wdt.h> 

int led = 13; //variable for pin that the LED is on
int tog = 1; //variable that toggles between traditional delay() function and WDT sleep delay function

void setup() {
  wdt_disable(); //Datasheet recommends disabling WDT right away in case of low probabibliy event
   pinMode(led, OUTPUT); //set up the LED pin to output
}

void loop() {
  
  if(tog) { //use traditional delay function
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);               // wait 
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);       // wait 
    tog = 0; //toggle variable
  }
  else { //after blinking LED setup interrupt and then go to sleep. Note that sleep will only happen once sinc
    digitalWrite(led, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);   // turn the LED on (HIGH is the voltage level)//  
    digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
    delayWDT(WDTO_1S);   // Use WDT sleep delay function, argument is byte variable from WDT Library
    //delayWDT(0x06);      //Use WDT sleep delay function, argument is byte value that sets timer to 1 second
    tog = 1; //toggle variable
  }
   
}

//This function serves as a power saving delay function. The argument is a Byte type variable that is used to set the delay time
//The function sets up sleep mode in power down state. The function then sets up the WDT timer in interrupt mode and sets it.
//It then puts the Arduino to sleep for the set time. Upon wake up the WDT and sleep mode are shut off
void delayWDT(byte timer) {
  sleep_enable(); //enable the sleep capability
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); //set the type of sleep mode. Default is Idle
  ADCSRA &= ~(1<<ADEN); //Turn off ADC before going to sleep (set ADEN bit to 0)
  WDTCSR |= 0b00011000;    //Set the WDE bit and then clear it when set the prescaler, WDCE bit must be set if changing WDE bit   
  WDTCSR =  0b01000000 | timer; //Or timer prescaler byte value with interrupt selectrion bit set
 // WDTCSR = 0b01000110; //This sets the WDT to 1 second
  wdt_reset(); //Reset the WDT 
  sleep_cpu(); //enter sleep mode. Next code that will be executed is the ISR when interrupt wakes Arduino from sleep
  sleep_disable(); //disable sleep mode
  ADCSRA |= (1<<ADEN); //Turn the ADC back on
}

//This is the interrupt service routine for the WDT. It is called when the WDT times out. 
//This ISR must be in your Arduino sketch or else the WDT will not work correctly
ISR (WDT_vect) 
{
  wdt_disable();
   MCUSR = 0; //Clear WDT flag since it is disabled, this is optional

}  // end of WDT_vect