Go Down

Topic: Sleep and wake with external interrupt (Read 275 times) previous topic - next topic

groovydude

(Sorry I have had to do this in 2 posts. Apparently I have exceeded the maximum size limit for a post)

Hey all :) . I am very new to this and a little confused about how somethings work. First some background. I have bullt a data logger with an arduino nano, an RTC (DS3231), a micro SD module, and a solenoid. Basically I would like my device to log sensor data to the SD card and If the sensor returns a certain value I would like it to open up the solenoid for 10 seconds. Here is my code and it is working perfectly.

Code: [Select]
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include "RTClibExtended.h"

// A simple data logger for the Arduino analog pins

// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
#define LOG_INTERVAL  3000 // mills between entries (reduce to take more/faster data)

// how many milliseconds before writing the logged data permanently to disk
// set it to the LOG_INTERVAL to write each time (safest)
// set it to 10*LOG_INTERVAL to write all data every 10 datareads, you could lose up to
// the last 10 reads if power is lost but it uses less power and is much faster!
#define SYNC_INTERVAL 5000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

#define ECHO_TO_SERIAL   1 // echo data to serial port
#define WAIT_TO_START    0 // Wait for serial input in setup()

// The analog pins that connect to the sensors
#define Voltage A1                // analog 1
int solenoidPin = A3;    //This is the output pin on the Arduino we are using

RTC_DS1307 RTC; // define the Real Time Clock object

// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;

// the logging file
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
  


  while(1);
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println();
  
  
#if WAIT_TO_START
  Serial.println("Type any character to start");
  while (!Serial.available());
#endif //WAIT_TO_START
 pinMode(solenoidPin, OUTPUT);           //Sets the pin as an output

  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
  
  // create a new file
  char filename[] = "ETC2.CSV";
  //for (uint8_t i = 0; i < 100; i++) {
    //filename[6] = i/10 + '0';
    //filename[7] = i%10 + '0';
    //if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE);
      //break;  // leave the loop!
   // }
 // }
  
  if (! logfile) {
    error("couldnt create file");
  }
  
  Serial.print("Logging to: ");
  Serial.println(filename);

  // connect to RTC
  Wire.begin();  
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL

 }
  

  logfile.println("millis,stamp,datetime,voltage");    
#if ECHO_TO_SERIAL
  Serial.println("millis,stamp,datetime,voltage");
#endif //ECHO_TO_SERIAL
 
}

void loop(void)
{
  DateTime now;

  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
  
  
  // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");    
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", ");  
#endif

  // fetch the time
  now = RTC.now();
  // log time
  logfile.print(now.unixtime()); // seconds since 1/1/1970
  logfile.print(", ");
  logfile.print('"');
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print('"');
#if ECHO_TO_SERIAL
  Serial.print(now.unixtime()); // seconds since 1/1/1970
  Serial.print(", ");
  Serial.print('"');
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print('"');
#endif //ECHO_TO_SERIAL

 
  analogRead(Voltage);
  delay(10);
  int tempReading = analogRead(Voltage);    
  
  // converting that reading to voltage, for 3.3v arduino use 3.3, for 5.0, use 5.0
//  float voltage = tempReading * aref_voltage / 1024;  
  float voltage = tempReading * (5.0 / 1023.0);
  
  
  logfile.print(", ");    
  logfile.print(voltage);
  logfile.println();
#if ECHO_TO_SERIAL

  Serial.print(", ");    
  Serial.print(voltage);
  Serial.println();
#endif //ECHO_TO_SERIAL

 
  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
  logfile.flush();
 
analogRead(Voltage);
 if (tempReading <297) {
    digitalWrite(solenoidPin, HIGH);
    delay(10000); // time in milliseconds as parameter
    digitalWrite(solenoidPin, LOW);
  } else {
    digitalWrite(solenoidPin, LOW);
}  
}



groovydude

So to save power I would like it to only log a few times an hour. 1 min of logging and then 10 min of sleep would be ideal. Although every half an hour or even once an hour would be acceptable if thats the way it has to be.  So here is some code I have found to put the arduino to sleep.

Code: [Select]
#include <Wire.h>
#include <RTClibExtended.h>
#include <LowPower.h>

#define wakePin 2    //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
#define ledPin 13    //use arduino on-board led for indicating sleep or wakeup status

RTC_DS3231 RTC;      //we are using the DS3231 RTC

byte AlarmFlag = 0;
byte ledStatus = 1;

//-------------------------------------------------

void wakeUp()        // here the interrupt is handled after wakeup
{
}

//------------------------------------------------------------

void setup() {
  //Set pin D2 as INPUT for accepting the interrupt signal from DS3231
  pinMode(wakePin, INPUT);

  //switch-on the on-board led for 1 second for indicating that the sketch is ok and running
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  delay(1000);

  //Initialize communication with the clock
  Wire.begin();
  RTC.begin();
  RTC.adjust(DateTime(__DATE__, __TIME__));   //set RTC date and time to COMPILE time
  
  //clear any pending alarms
  RTC.armAlarm(1, false);
  RTC.clearAlarm(1);
  RTC.alarmInterrupt(1, false);
  RTC.armAlarm(2, false);
  RTC.clearAlarm(2);
  RTC.alarmInterrupt(2, false);

  //Set SQW pin to OFF (in my case it was set by default to 1Hz)
  //The output of the DS3231 INT pin is connected to this pin
  //It must be connected to arduino D2 pin for wake-up
  RTC.writeSqwPinMode(DS3231_OFF);

  //Set alarm1 every day at 18:33
  RTC.setAlarm(ALM1_MATCH_SECONDS, 20, 0, 0, 1);   //set your wake-up time here
  RTC.alarmInterrupt(1, true);
}

//------------------------------------------------------------

void loop() {

  //On first loop we enter the sleep mode
   {  
    //Set alarm1 every day at 18:33
    RTC.setAlarm(ALM1_MATCH_SECONDS, 20, 0, 0, 1);   //set your wake-up time here
    RTC.alarmInterrupt(1, true);
    
    attachInterrupt(0, wakeUp, LOW);                       //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
    digitalWrite(ledPin, LOW);                             //switch-off the led for indicating that we enter the sleep mode
    ledStatus = 0;                                         //set the led status accordingly
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);   //arduino enters sleep mode here
    detachInterrupt(0);                                    //execution resumes from here after wake-up

//    When exiting the sleep mode we clear the alarm
    RTC.armAlarm(1, false);
   RTC.clearAlarm(1);
   RTC.alarmInterrupt(1, false);
    AlarmFlag++;
  }

  //cycles the led to indicate that we are no more in sleep mode
  if (ledStatus == 0) {
    ledStatus = 1;
    digitalWrite(ledPin, HIGH);
  }
  else {
    ledStatus = 0;
    digitalWrite(ledPin, LOW);
  }

  delay(5000);
}



So my question is how do I integrate these 2 together? I am not sure where I put my code into the sleep code to get it to work. Also I am not sure how to set the interrupt to once every 10min instead of the same time every day like it is currently. Or even at the same time every hour would be okay if thats what I am limited too. I am also open to the idea of using a watchdog timer But i am very noob and at first glance it looked a little tricky to set up. Any help would be GREATLY appreciated thankyou!!

JaBa

You can't use that code exactly as is. That code is designed to waken the CPU upon receipt of an external Signal. You are wanting to awaken the CPU based on time.  The answer is something called the "Watchdog Interrupt" or "Watchdog Timer".  Research that on the Forum and you will find tons of samples. Sometimes, it's just knowing what to look for.

groovydude

Thanks for your reply. But i'm not sure we understand each other. I was wanting to send an external signal from the RTC (ds3231). I was under the impression that it kept the time independently of the nano and could send an interrupt?? Maybe I have misunderstood though.

CrossRoads

DS3231 has alarms, yes?
"Two programmable time-of day alarms and a programmable square-wave output are provided."

Connect the INT/SQWV pin, with a pullup resistor, to an Arduino interrupt pin, and set the next alarm time to wake up after an alarm occurs:
"Active-Low Interrupt or Square-Wave Output. This open-drain pin requires an external pullup resistor connected to a supply at 5.5V or less. This multifunction pin is determined by the state of the INTCN bit in the Control Register (0Eh). When INTCN is set to logic 0, this pin outputs a square wave and its frequency is determined by RS2 and RS1 bits. When INTCN is set to logic 1, then a match between the timekeeping registers and either of the alarm registers activates the INT/SQW pin (if the alarm is enabled). Because the INTCN bit is set to logic 1 when power is first applied, the pin defaults to an interrupt output with alarms disabled."
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

groovydude

Ahhh ok..... Thanks??? I'm not too sure what any of that means. I have the rtc successfully connected. In my question I was specifically asking about the code. IE how to set the alarm to go off every 10 min or every hour. (in the code i posted it is set too once a day and I am not sure how to change that) Also where in the sleep code do i put my datalogging sketch? I tried putting it in the void loop below where it clears the alarm but it didn't work.

Here is what I tried

Code: [Select]
#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <RTClibExtended.h>
#include <LowPower.h>

// A simple data logger for the Arduino analog pins

#define wakePin 2    //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW

#define ledPin 13    //use arduino on-board led for indicating sleep or wakeup status

byte AlarmFlag = 0;
byte ledStatus = 1;

// how many milliseconds between grabbing data and logging it. 1000 ms is once a second
#define LOG_INTERVAL  1000 // mills between entries (reduce to take more/faster data)

// how many milliseconds before writing the logged data permanently to disk
// set it to the LOG_INTERVAL to write each time (safest)
// set it to 10*LOG_INTERVAL to write all data every 10 datareads, you could lose up to
// the last 10 reads if power is lost but it uses less power and is much faster!
#define SYNC_INTERVAL 5000 // mills between calls to flush() - to write data to the card
uint32_t syncTime = 0; // time of last sync()

#define ECHO_TO_SERIAL   1 // echo data to serial port

// The analog pins that connect to the sensors
#define Voltage A1                // analog 1
int solenoidPin = A3;    //This is the output pin on the Arduino we are using

RTC_DS3231 RTC; // define the Real Time Clock object

// for the data logging shield, we use digital pin 10 for the SD cs line
const int chipSelect = 10;

// the logging file
File logfile;

void error(char *str)
{
  Serial.print("error: ");
  Serial.println(str);
 


  while(1);
}
void wakeUp()        // here the interrupt is handled after wakeup
{
}

void setup(void)
{
  Serial.begin(9600);
  Serial.println();
 
   //Set pin D2 as INPUT for accepting the interrupt signal from DS3231
  pinMode(wakePin, INPUT);
  pinMode(solenoidPin, OUTPUT);           //Sets the pin as an output
 
  //switch-on the on-board led for 1 second for indicating that the sketch is ok and running
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  delay(1000);

 

  // initialize the SD card
  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
 
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    error("Card failed, or not present");
  }
  Serial.println("card initialized.");
 
  // create a new file
  char filename[] = "LOGGER00.CSV";
  for (uint8_t i = 0; i < 100; i++) {
    filename[6] = i/10 + '0';
    filename[7] = i%10 + '0';
    if (! SD.exists(filename)) {
      // only open a new file if it doesn't exist
      logfile = SD.open(filename, FILE_WRITE);
      break;  // leave the loop!
    }
  }
 
  if (! logfile) {
    error("couldnt create file");
  }
 
  Serial.print("Logging to: ");
  Serial.println(filename);

  // connect to RTC
  Wire.begin(); 
  if (!RTC.begin()) {
    logfile.println("RTC failed");
#if ECHO_TO_SERIAL
    Serial.println("RTC failed");
#endif  //ECHO_TO_SERIAL
  }
  RTC.adjust(DateTime(__DATE__, __TIME__));   //set RTC date and time to COMPILE time
 
 //clear any pending alarms
  RTC.armAlarm(1, false);
  RTC.clearAlarm(1);
  RTC.alarmInterrupt(1, false);
  RTC.armAlarm(2, false);
  RTC.clearAlarm(2);
  RTC.alarmInterrupt(2, false);

  //Set SQW pin to OFF (in my case it was set by default to 1Hz)
  //The output of the DS3231 INT pin is connected to this pin
  //It must be connected to arduino D2 pin for wake-up
  RTC.writeSqwPinMode(DS3231_OFF);

  logfile.println("millis,stamp,datetime,voltage");   
#if ECHO_TO_SERIAL
  Serial.println("millis,stamp,datetime,voltage");
#endif //ECHO_TO_SERIAL
 
}

void loop(void)
    //On first loop we enter the sleep mode
   { 
    //Set alarm1 20th sec every min
    RTC.setAlarm(ALM1_MATCH_SECONDS, 20, 0, 0, 1);   //set your wake-up time here
    RTC.alarmInterrupt(1, true);
   
    attachInterrupt(0, wakeUp, LOW);                       //use interrupt 0 (pin 2) and run function wakeUp when pin 2 gets LOW
    digitalWrite(ledPin, LOW);                             //switch-off the led for indicating that we enter the sleep mode
    ledStatus = 0;                                         //set the led status accordingly
    LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);   //arduino enters sleep mode here
    detachInterrupt(0);                                    //execution resumes from here after wake-up

    delay(10000);
  //    When exiting the sleep mode we clear the alarm
    RTC.armAlarm(1, false);
    RTC.clearAlarm(1);
    RTC.alarmInterrupt(1, false);
    AlarmFlag++;
 
  //cycles the led to indicate that we are no more in sleep mode
  if (ledStatus == 0) {
    ledStatus = 1;
    digitalWrite(ledPin, HIGH);
  }
  else {
    ledStatus = 0;
    digitalWrite(ledPin, LOW);
 
  DateTime now;

  // delay for the amount of time we want between readings
  delay((LOG_INTERVAL -1) - (millis() % LOG_INTERVAL));
 
 
  // log milliseconds since starting
  uint32_t m = millis();
  logfile.print(m);           // milliseconds since start
  logfile.print(", ");   
#if ECHO_TO_SERIAL
  Serial.print(m);         // milliseconds since start
  Serial.print(", "); 
#endif

  // fetch the time
  now = RTC.now();
  // log time
  logfile.print(now.unixtime()); // seconds since 1/1/1970
  logfile.print(", ");
  logfile.print('"');
  logfile.print(now.year(), DEC);
  logfile.print("/");
  logfile.print(now.month(), DEC);
  logfile.print("/");
  logfile.print(now.day(), DEC);
  logfile.print(" ");
  logfile.print(now.hour(), DEC);
  logfile.print(":");
  logfile.print(now.minute(), DEC);
  logfile.print(":");
  logfile.print(now.second(), DEC);
  logfile.print('"');
#if ECHO_TO_SERIAL
  Serial.print(now.unixtime()); // seconds since 1/1/1970
  Serial.print(", ");
  Serial.print('"');
  Serial.print(now.year(), DEC);
  Serial.print("/");
  Serial.print(now.month(), DEC);
  Serial.print("/");
  Serial.print(now.day(), DEC);
  Serial.print(" ");
  Serial.print(now.hour(), DEC);
  Serial.print(":");
  Serial.print(now.minute(), DEC);
  Serial.print(":");
  Serial.print(now.second(), DEC);
  Serial.print('"');
#endif //ECHO_TO_SERIAL

 
  analogRead(Voltage);
  delay(10);
  int tempReading = analogRead(Voltage);   
 
  // converting that reading to voltage, for 3.3v arduino use 3.3, for 5.0, use 5.0
//  float voltage = tempReading * aref_voltage / 1024; 
  float voltage = tempReading * (5.0 / 1023.0);
 
 
  logfile.print(", ");   
  logfile.print(voltage);
  logfile.println();
#if ECHO_TO_SERIAL

  Serial.print(", ");   
  Serial.print(voltage);
  Serial.println();
#endif //ECHO_TO_SERIAL

 
  // Now we write data to disk! Don't sync too often - requires 2048 bytes of I/O to SD card
  // which uses a bunch of power and takes time
  if ((millis() - syncTime) < SYNC_INTERVAL) return;
  syncTime = millis();
  logfile.flush();
 
 analogRead(Voltage);
 if (tempReading > 500) {
    digitalWrite(solenoidPin, HIGH);
    delay(10000); // time in milliseconds as parameter
    digitalWrite(solenoidPin, LOW);
  } else {
    digitalWrite(solenoidPin, LOW);
}}  }

groovydude

Ok. I Re-read. I think I understand what you are saying. But I still do not know how to write the code.

JaBa

You are correct. We misunderstood each other and it's because I was reading too fast. My fault.

If you are using the library I think you are using, it contains two functions.


void RTC_DS3231::setAlarm(Ds3231_ALARM_TYPES_t alarmType, byte seconds, byte minutes, byte hours, byte daydate)

and

void RTC_DS3231::alarmInterrupt(byte alarmNumber, bool interruptEnabled)

As I understand it, this will not cause an Interrupt every two seconds. Each time the Interrupt is triggered, it will have to be reset for two seconds in the future.

Go Up