Arduino and lowpower choice

Hello all, i have a very simple question and i would like your advice. I am building a beehive scale which will work with GSM and li-on batteries. Due to that the minimum lower consumption is mandatory, so i connected the gsm to a standalone ATmega 328Pand also i am using a low power libray to my code. But cause my experience is very little i would like to ask you which low power library is the best , the code actually will run every 12 hours so thats the case. At the moment i am using this library https://github.com/n0m1/Sleep_n0m1 which as said in description u can put the microcontroller in sleep mode for days. For the record when running the code it actually doesnt send every 12 hours but 13 or 14 it changes every time but dunno why.
Thank you in advance.

I recon its your code! But based on the info provided, it’s line 16514 thats got the error.

1 Like

If the ‘sleep’ timer is based on the ATmega328P internal watchdog timer, then dont expect the sleep times to be accurate.

If you must have more accuracy for the sleep time then the use an external real time clock (RTC).

Do you have circuitry to power down the GSM and other sensors also?

You lost me with that line 16514. Anyway here is my code

#include <Sleep_n0m1.h>
#include <SoftwareSerial.h>
#include "HX711.h"    //load cell amplifier, gia tin zugaria.
#include <SparkFun_SHTC3.h>  //sensor gia 8ermokrasia kai ugrasia
SoftwareSerial gprsSerial(2,3);   //sundesi me to modem
 
#include <String.h>

#define DIGITALOUT  5     //HX711 DT
#define CLOCK       6     //HX711 CKL
SHTC3 mySHTC3;
HX711 scale;
float   SCALE =     -19660;  //gia to load cell, to calibrarei
long    OFFSET =    55529; //gia to load cell , to calibrarei
static const int PowerPin=9;
Sleep sleep;
unsigned long sleepTime;
String capacity;
String readString;
char readbattery[20];
 
void setup() {
  gprsSerial.begin(9600);               // the GPRS baud rate   
  //Serial.begin(9600);    // the GPRS baud rate 
  //while(Serial == false){}; // isos den xreaizetai
  Wire.begin();
  //unsigned char i=0; //sigoura den xreiazetai
  mySHTC3.begin();//errorDecoder(mySHTC3.begin());
  sleepTime = 43200000;//12 ores
  
  pinMode(PowerPin, OUTPUT);
  
  scale.begin(DIGITALOUT, CLOCK);
  scale.set_scale(SCALE);
  scale.set_offset(OFFSET);
  //long zero_factor = scale.read_average(); //Get a baseline reading
  //Serial.print("Zero factor / OFFSET: "); //This can be used to remove the need to tare the scale. Useful in permanent scale projects.
  //Serial.println(zero_factor);
  //zero_factor = scale.read_average(); //Get a baseline reading
  //Serial.print("Zero factor / OFFSET: "); //This can be used to remove the need to tare the scale. Useful in permanent scale projects.
  //Serial.println(zero_factor);

  //if(OFFSET){
      //scale.set_offset(OFFSET); //set offset
  //} else {
      //scale.tare(); //Else reset scale to zero
  //}
  
  delay(1000);
}
 
void loop() {
    
      digitalWrite(PowerPin, HIGH);//opens GSM when connected to pin 9, anoigei to GSM epeidi den mporoume xeirokinita
      delay(1000);
      digitalWrite(PowerPin,LOW);
      delay(10000);
      
      float RH,T,weight;
      delay(1000);  
      SHTC3_Status_TypeDef result = mySHTC3.update();
  //if(mySHTC3.lastStatus == SHTC3_Status_Nominal)   //Determine whether the SHTC3 status is normal
  //{
      RH = mySHTC3.toPercent(); //8ermokrasia
      T = mySHTC3.toDegC();   //ugrasia
      // }else{
    //Serial.print("Update failed, error: ");
   //errorDecoder(mySHTC3.lastStatus);
    //Serial.println();
 // }
        
      //Serial.print("Weight: ");     
      //Serial.print(scale.get_units(5), 1);    //pernei to varos
      weight = scale.get_units(5);
      //Serial.print(" kg");
      //Serial.println();  
      //Serial.print("Temperature = ");
      //Serial.print(T);
      //Serial.println(" °C");
      //Serial.print("Humidity = ");
      //Serial.print(RH);
      //Serial.println(" %");    
      delay(1000);
   
  //if (gprsSerial.available())              //apo edo pernei tis apantiseis apo to modem kai tis grafei sto serial monitor
    //Serial.write(gprsSerial.read());       // tis exo vgalei giati den 8a exo monitor otan 8a douleuei
 
  //gprsSerial.println("AT");
  //delay(1000);

  gprsSerial.write("AT+CBC\r");          //edo eixa 8emata. edo pairnei plirofories apo to modem sxetika me tin mpataria.Evala write giati me to print den douleue.
  delay(5000);                           //auto to delay to evalal ego giati me auto mou douleuei sosta.isos gia na prolavei na parei ta bytes apo to buffer prin steilei alli entoli to AT 
  CheckBattery();                        //perissoteres plirofories se auto to class poio kato
  delay(1000);
  
  //gprsSerial.println("AT+CPIN?");  //auto 8a vgei sigoura giati etsi kai allios den xrisimopoio pin
  //delay(1000);
 
  gprsSerial.println("AT+CREG?");
  delay(1000);
 
  gprsSerial.println("AT+CGATT?");
  delay(1000);
 
  gprsSerial.println("AT+CIPSHUT");
  delay(1000);
 
  gprsSerial.println("AT+CIPSTATUS");
  delay(2000);
 
  gprsSerial.println("AT+CIPMUX=0");
  delay(2000);
 
  //ShowSerialData();                  //auto ousiastika einai to idio me to parapano if, gia na diavazoume apo to modem. kai auto den 8a xreiastei ston teliko kodika.
 
  gprsSerial.println("AT+CSTT=\"internet\"");//start task and setting the APN,
  delay(1000);
 
  //ShowSerialData();
 
  gprsSerial.println("AT+CIICR");//bring up wireless connection
  delay(6000);
 
  //ShowSerialData();
 
  gprsSerial.println("AT+CIFSR");//get local IP adress
  delay(2000);
 
  //ShowSerialData();
 
  gprsSerial.println("AT+CIPSPRT=0");
  delay(3000);
 
  //ShowSerialData();
  
  gprsSerial.println("AT+CIPSTART=\"TCP\",\"api.thingspeak.com\",\"80\"");//start up the connection
  delay(6000);
 
  //ShowSerialData();
 
  gprsSerial.println("AT+CIPSEND");//begin send data to remote server
  delay(4000);
  //ShowSerialData();
  
  String str="GET https://api.thingspeak.com/update?api_key=Y92RLSQQQO5FIZON&field1=" + String(T) +"&field2=" + String(RH) +"&field3=" + String(weight) +"&field4=" + String(capacity);
  Serial.println(str);
  gprsSerial.println(str);//begin send data to remote server
  
  delay(4000);
  //ShowSerialData();
 
  gprsSerial.println((char)26);//sending
  delay(10000);//waitting for reply, important! the time is base on the condition of internet 
  gprsSerial.println();
 
  //ShowSerialData();
 
  gprsSerial.println("AT+CIPSHUT");//close the connection
  delay(100);
  //ShowSerialData();
  delay(5000);

  digitalWrite(PowerPin, HIGH);// close GSM
  delay(3000);
  digitalWrite(PowerPin,LOW);

  
  capacity=""; // clear buffer, auto den ksero an telika xreiazetai i oxi
  //Serial.print("sleeping for ");
  //Serial.println(sleepTime); 
  //delay(100); //delay to allow serial to fully print before sleep
  sleep.pwrDownMode();// set sleep mode
  sleep.sleepDelay(sleepTime);
} 
//void errorDecoder(SHTC3_Status_TypeDef message) // agnoise to auto den 8a mpei, apla kanei elegxo gia to temp sensor
//{
  //switch(message)
  //{
    //case SHTC3_Status_Nominal : Serial.println("Nominal"); break;
    //case SHTC3_Status_Error : Serial.print("Error"); break;
    //case SHTC3_Status_CRC_Fail : Serial.print("CRC Fail"); break;
    //default : Serial.print("Unknown return code"); break;
  //}
//} 
//void ShowSerialData()
//{
  //while(gprsSerial.available()!=0)
  //Serial.write(gprsSerial.read());
  //delay(5000); 
  
//}
void CheckBattery()
{
  while (gprsSerial.available()>0){            // loipon edo tora ti ginetai.pairnoume apantisi gia tis plirofories tis mpatarias. i apantisi pou pairnoume einai i eksis: +CBC (0,61,3889)
    char readbattery = gprsSerial.read();      //ego ousiastika 8elo mono to 61 pou einai to capacity tis mpatarias
    readString += readbattery;                 //apo oti exo katalavei pernei ena ena ta bytes kai ta proste8ei se ena string gia auto to logo exo valei auto to delay parapano giati allios pernaei to epomeno AT command sto buffer kai mperdeuetai to string
    capacity = readString.substring(16,18);    // edo pernoume to 61 sto capacity gia na to steiloume sto server.
    //if (readbattery =='\n'){                 //auto edo ousiastika den xreiazetai sto teliko kodika mono gia na do an douleuei. vevaia gia kapoio logo anti na mou epistrefei to 61 mia fora mou to evgaze 3 fores, kati paei la8os logika me to if.
      //Serial.println(capacity);
    //}
  }
 }

I didnt actually expect to be accurate at all but not like this cause the second cycle had 1 hour difference the third 2 and some minutes and the fourth 1 hour and some minutes and that was what confused me.

The GSM opens and closes through the atmega via code. The other sensonrs i presume that since the atmega will be on sleep mode they wont consume power or at least any big amount.

That sleep library looks overly complicated in my opinion. If it’s any help to you, take a look at a post I did last year that sleeps the 328p for multiple 8 second WDT intervals to achieve 1 hour sleep periods. It shows how the WDT sleep method compares to real time.

This is my own observation of one particular 328p and it will vary amongst other 328p micros. I suspect it will also vary on the same 328p as temperature changes.

Remember that the WDT sleep is a poor mans sleep timer to be used if you are happy with a 1 hour sleep being out by +/-60 seconds or so.

Thank you for your answer mark and nice post you have indeed, but thing is my arduino skills are limited :slight_smile: . I can understand that the watchdog timer is not accurate so i was thinking maybe changing the code from putting it to sleep for 12 hours to something that WDT can follow and adding a time counter. But from what i understand from your post is that even 8 secs sleep isnt accurate? I was thinking of putting in to sleep for 1 hour and have a counter to sum up to 12 times so we have 12 hours in total before the code will run. But according to your post that will mean 12 * 40 secs delay i pressume? Again i know it depends on the atmega328p.

It’s probably best if you can decide what sort of tolerance your project would accept on a 12 hour timeout.

The thing is, once you decide that the WDT isn’t good enough for your project, then you need some sort of external device. In which case you may as well go straight for an RTC module.

Yea i understand, but the thing is that i dont know how to program an RTC or if i need to add anything in my code and most important, i know that RTC communicate as I2C with arduino but i already have a sensor using those pins, can atmega328p communicate as I2C with 2 devices?

I2C is a bus, all devices connect to SDA & SCL in parallel. Each device then responds to its own address.
Another option is SPI for the RTC, such as DS3234. (vs I2C on DS3231).

@alexandier - there’s nothing really complicated in programming an RTC. You tell it the time (once - hopefully) and then you tell it what time you want to wake up (an alarm). There are lots of examples of using an RTC on Arduino. An RTC module using a DS3231 chip is considered one of the best as it has built in temperature compensation to try and maintain the accuracy of the clock.

As @CrossRoads says, I2C is a bus, meaning that you can connect more than one device to the same 2 pins. On the 328P, I2C uses pins PC4 & PC5 - A4 (SDA) and A5 (SCL) on an UNO board. You would also need an additional pin to detect (via an interrupt) the alarm signal from the RTC. Again, all this is very easy and there are lots of examples as well as plenty of forum users to help you out if you get stuck.

If you can’t move your sensor to other pins (maybe because they are analogue signals that you need to read), then there is the option of a software I2C implementation.

Here is some simple test code I wrote to write & read from an I2C RTC, thje DS1307. I think the DS3231 uses the same commands.

/*
Test of RTC DS1307 via I2C.
 Counts 
 Seconds, 
 Minutes, 
 Hours, 
 Date of the Month, 
 Month, 
 Day of the week, and 
 Year with Leap-Year
 
 56 bytes battery backed RAM
 Square Wave Output
 
 11/17/2012- Updated for Wire.write & Wire.read commands
 1/18/13 - fixed unclear reference to 0x00
 */

/*
Modified to Run thru IDE Serial port
*/
#include <Wire.h>

//variables
byte zeroByte = 0;
byte seconds_address = 0x00;
byte seconds; // bit 7 = Clock Halt, Enabled = 0, Halt = 1
// bits 6-5-3 = tens of seconds 0-6,  bits 3-2-1-0 = units of seconds, 0-9

byte minutes_address = 0x01;
byte minutes;  // bits 6-5-4 = tens of minutes, bits 3-2-1-0 = units of minutes

byte hours_address = 0x02; 
byte hours;  // 7=0. 6 = 1 for 12 hr, 0 for 24 hr.
// bit 5: 12 hr mode = AM(0)/PM(1). 24 hr mode = upper tens of hrs
// bit 4 =  lower tens of hrs, bits 3-2-1-0 = units of hours (0-9)

byte day_week_address = 0x03; 
byte day_week = 0; // range 01-07

byte date_month_address = 0x04;
byte date_month = 0; // range 01-31

byte month_address = 0x05;
byte month = 0; // range 01-12

byte year_address = 0x06;
int year = 0; // upper byte 0-9, lower byte 0-9

byte square_address = 0x07;
byte sqwe = 0;  // square wave enable
// Out-0-0-Sqwe-0-0-RS1-RS0
// Out, Sqwe = 0/0 - Square wave output = 0
// Out, Sqwe = 1/0 - Square wave output = 1
// Out, Sqwe = 0/1 or 1/1 - Square wave output per RS1/RS0
// RS1/RS0 = 00 = 1 Hz
// RS1/RSo = 01 = 4 KHz
// RS1/RS0 = 10 = 8 KHz
// RS1/RS0 = 11 = 32 KHz

byte RTC_ram_address = 0x08; //range = 08-63, 0x08-0x3F

int RTC_address = 0x68; // 1101 000 

byte incomingCommand = 0;
byte RTC_write_command = 0;
byte RTC_read_command = 0;
byte RTC_ram_command = 0;

// use F0xx, F1xx,F2xx, F3xx, F4xx, F5xx, F6xx, F7xx
// to send one register write commands
// use E0xx to read registers back - not coded yet
// use C0xx to read RAM back - not coded yet

byte incomingRegister = 0;
byte RTC_register = 0;
byte incomingData1 = 0;
byte incomingData2 = 0;
byte new_data = 0;
byte outgoingData = 0;
int delay_time = 100;

unsigned long currentMillis = 0;
unsigned long previousMillis = 0;
unsigned long duration = 5000;

void setup() {
  Wire.begin(); // no address, we are master
  Serial.begin (57600);  
  Serial.flush();
  currentMillis = millis();  
}

void loop() {

  if (Serial.available() >3){
    incomingCommand = Serial.read();
    incomingRegister = Serial.read();
    incomingData1 = Serial.read();
    incomingData1 = incomingData1 - 0x30; // convert ASCII to HEX
    incomingData2 = Serial.read();
    incomingData2 = incomingData2 - 0x30;  // convert ASCII to HEX
    new_data = (incomingData1 << 4) + incomingData2;  // put the Upper/Lower nibbles together
    Serial.print ("command ");
    Serial.println (incomingCommand);
    Serial.print ("register ");
    Serial.println(incomingRegister);
    Serial.print ("data1 ");
    Serial.println (incomingData1, HEX);
    Serial.print ("data2 ");
    Serial.println (incomingData2, HEX);
    Serial.print ("combined data ");    
    Serial.println (new_data, HEX);
    
  }
  // *******************************************
//  RTC_write_command = incomingCommand & 0xF0;  // mask off high byte
//  if (RTC_write_command == 0xF0){  // check for Write command
if ((incomingCommand == 'F') | (incomingCommand == 'f')){
  incomingCommand = 0;  // reset for next pass
//    RTC_register = incomingCommand & 0x0F;  // mask off low btye
//    incomingCommand = 0;
//    new_data = incomingData;
    Serial.println (" Sending a command ");
//    switch (RTC_register){
switch (incomingRegister){
  case '0': // write seconds
        Serial.println ("Seconds ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(seconds_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
      delay (delay_time);
      break;
    case '1': // write minutes
    Serial.println ("Minutes ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(minutes_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
      delay (delay_time);
      break;
    case '2': // write hours
        Serial.println ("Hours ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(hours_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
    case '3': // write day
        Serial.println ("Day ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(day_week_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
    case '4': // write date of month
        Serial.println ("Day of Month ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(date_month_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
    case '5': // write month
        Serial.println ("Month ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(month_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
    case '6': // write year
        Serial.println ("Year ");
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(year_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
    case '7': // write square wave
        Serial.println ("Square Wave ");
    Serial.println (RTC_register, HEX);
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(square_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
    case '8': // write RAM
        Serial.print ("RAM ");
    Serial.println (RTC_register, HEX);
      Wire.beginTransmission(RTC_address); // select device
      Wire.write(RTC_ram_address);          // queue the register
      Wire.write(new_data);                  // queue data
      Wire.endTransmission();            // send it
     delay (delay_time);
      break;
      // all others,do nothing
      Serial.println ("Invalid command ");
    }  // end Switch
  } // end if command == 'F'
  // ************************************

  currentMillis = millis();
  if ( (currentMillis - previousMillis) >= duration){
    previousMillis = currentMillis;  
    // Reset the register pointer  
    Wire.beginTransmission(RTC_address);  
    Wire.write(zeroByte);  
    Wire.endTransmission();   

    Wire.requestFrom(RTC_address,8 );  
    seconds = Wire.read();  
    minutes = Wire.read();  
    hours = Wire.read();  
    day_week = Wire.read();  
    date_month = Wire.read();  
    month = Wire.read();  
    year = Wire.read();  
    sqwe = Wire.read();

    // Seconds 
    // bit 7 = Clock Halt, Enabled = 0, Halt = 1
    // bits 6-5-3 = tens of seconds 0-6,  bits 3-2-1-0 = units of seconds, 0-9 

    // Hours
    // 7=0. 6 = 1 for 12 hr, 0 for 24 hr.
    // bit 5: 12 hr mode = AM(0)/PM(1). 24 hr mode = upper tens of hrs
    // bit 4 =  lower tens of hrs, bits 3-2-1-0 = units of hours (0-9)

    Serial.print ("Hrs " );
    Serial.print (hours, HEX);
    Serial.print (" Mins ");
    Serial.print (minutes, HEX);
    Serial.print (" Secs ");
    Serial.print (seconds, HEX);
    Serial.print (" Day ");
    Serial.print (day_week, HEX);
    Serial.print (" Date ");
    Serial.print (date_month, HEX);
    Serial.print (" Month ");
    Serial.print (month, HEX);
    Serial.print (" Year 20");
    Serial.print (year, HEX);
    Serial.print (" Square Wave ");
    Serial.println (sqwe, HEX);

  }
}

Ok guys you convinced me ill try my way out with an RTC :). As for the I2C i have a temperature sensor on PC4 and PC5 on my atmega328p but since i declare on my code that those pins are DATA and CLK for the sensor i am confused how those pins can worked in parallel with the RTC too. My temp sensor is SHTC3 btw and dont think i can use other pins.

If your using hardware I2C, on a Atmega328p, you dont and cannot specify the SDA or SDA pins, these are fixed.

If you using some hardware library that requires you to specify the pin numbers for ‘I2C’ (SCL and SDA) then it does not sound like its using hardware I2C.

@alexandier - a quick look on the Sensirion website for the sensor says that it uses I2C. As we’ve previously said, I2C is a bus so you can have more than 1 device connected to those 2 pins.

The way each device is selected is done in the I2C protocol. Each I2C device has a 7-bit address that is generally fixed for that specific chip (some chips give you the option of specifying slightly different addresses - but ignore that for now). The datasheet says that the address of the SHTC3 is 0x70.

I’m not sure I understand when you say you declare those pins as DATA and CLK for the sensor? That sounds like you might be using a software I2C implementation rather than the built in hardware I2C interface.

I checked my code again guys and you are right i confused with another sensor. For the SHTC3 there is no declaration on the pins, my bad. As for the addresses you mention do i have to put them on my code or no need its automatic?

@alexandier - a quick google search for “SHTC3 arduino” gave me a link to the Adafruit website where they have information on how to hook up an SHTC3 to an Arduino. They also have a library that you can download that has examples of how to talk to it.

Never had problem with that. I know and already have wire it and use it :slight_smile: