I'm working on my 18650 charger still and all is almost done but then I noticed that whenever I sleep in any mode the unit restarts like its been reset clearing any counters. Now I know I could use the EEPROM but that has only 100,000 write cycles and using it every 8 seconds would burn it out hella quick. Is there any other way anyone knows of to just hold a value during sleep? Perhaps keep the SRAM powered up but the rest sleeps or something. My intended application needs to count how many times it did an 8 second sleep so that after 12 hours it can start to disconnect the solar cells periodically from the battery to check the cells float voltage is above the battery charge voltage. I also figured worst case I could just not sleep and work in a reduced active mode at a lower clock with brownout off but really id like to save as much power as possible by using sleep mode power down in which I get nearly no draw.
Please forgive the messyness I've disabled lots just so I could isolate if it was adding the counter each wdt 8 second sleep. Thanks for taking a look.
//AceTK - Deepsleep powersaving single cell lion charger
#include <avr/sleep.h> // includes library for sleep
#include <avr/power.h> // includes library for sleep
#include <avr/wdt.h> // watchdog timer
unsigned short int SolVlts = 2;
volatile unsigned long AuxVolts = 0;
byte intCounter, adcsra, mcucr1, mcucr2;
volatile int f_wdt=1;
short unsigned int auxcharging = 0;
short unsigned int Relay1on = 9;
short unsigned int Relay1off = 10;
float AuxBatt = 0;
volatile short unsigned int val = 0;
volatile unsigned long PowerCheckCount = 0;
ISR(WDT_vect)
{
if(f_wdt == 0)
{
f_wdt=1;
}
else
{
//Serial.println("WDT Overrun Error!!!");
}
}
void enterSleep(void)
{
// * SLEEP_MODE_IDLE -the least power savings
// * SLEEP_MODE_ADC
// * SLEEP_MODE_PWR_SAVE
// * SLEEP_MODE_STANDBY
// * SLEEP_MODE_PWR_DOWN -the most power savings
sleep_enable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
// #ifdef WDTO_8S
wdt_enable(WDTO_8S); // enable WDT 8 seconds
wdt_reset();
adcsra = ADCSRA; //save the ADC Control and Status Register A
ADCSRA = 0; //disable ADC
cli();
mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
mcucr2 = mcucr1 & ~_BV(BODSE);
MCUCR = mcucr1;
MCUCR = mcucr2;
/* Now enter sleep mode. */
sleep_mode();
/* The program will continue from here after the WDT timeout*/
sleep_disable(); /* First thing to do is disable sleep. */
ADCSRA = adcsra;
/* Re-enable the peripherals. */
power_all_enable();
}
void SleepCheck()
{
int val;
val = digitalRead(SolVlts); // read the 1.092v/5v charge ready input pin
if (val > 0 && auxcharging == 0) // if 1.092v present boolean equals 1 / true
{
sleep_enable();
wdt_reset();
wdt_disable();
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
EIMSK |= _BV(INT0); //enable INT0
adcsra = ADCSRA; //save the ADC Control and Status Register A
ADCSRA = 0; //disable ADC
cli();
mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector
mcucr2 = mcucr1 & ~_BV(BODSE);
MCUCR = mcucr1;
MCUCR = mcucr2;
sei(); //ensure interrupts enabled so we can wake up again
sleep_cpu(); //go to sleep
sleep_disable(); //wake up here
ADCSRA = adcsra; //restore ADCSRA
}
}
ISR(INT0_vect)
{
intCounter++;
EIMSK &= ~_BV(INT0); //one interrupt to wake up only
}
void AuxBattMan()
{
AuxVolts = analogRead(A2);
AuxVolts = analogRead(A2);
AuxVolts = analogRead(A2);
AuxVolts = analogRead(A2);
AuxBatt = AuxVolts / 240.70; // Aux cell resistive divide ratio converter 1023 / 240.70 = 4.25
if (AuxBatt < 4.1 && auxcharging == 0)
{
digitalWrite(Relay1on, HIGH);
//delay(4);
digitalWrite(Relay1on, LOW);
auxcharging=1;
}
if (AuxBatt > 4.1 && auxcharging == 1)
{
digitalWrite(Relay1off, HIGH);
//delay(4);
digitalWrite(Relay1off, LOW);
auxcharging=0;
}
}
void MiniSleep()
{
if(f_wdt == 1)
{
f_wdt = 0;
if (auxcharging == 1)
{
PowerCheckCount = PowerCheckCount+1;
}
// Re-enter sleep mode. //
PowerCheckCount = PowerCheckCount+1;
enterSleep();
}
else
{
// Do nothing. //
}
}
void PowerChecker()
{
if (PowerCheckCount >= 7) //12 hours = 4800 1 hour = 400 1 min = 7
{
digitalWrite(Relay1off, HIGH);
delay(1);
digitalWrite(Relay1off, LOW);
PowerCheckCount = 0;
}
Serial.println(PowerCheckCount);
}
void StatusFlash()
{
if (auxcharging == 0)
{
digitalWrite(13, HIGH);
delay(1);
digitalWrite(13, LOW);
delay(1);
}
if (auxcharging == 1)
{
digitalWrite(13, HIGH);
delay(1);
digitalWrite(13, LOW);
delay(1);
digitalWrite(13, HIGH);
delay(1);
digitalWrite(13, LOW);
delay(1);
}
}
void setup() {
//CLKPR = (1<<CLKPCE);
//CLKPR = B00000011; // prescaler powersaver / rampup
// 0000 - divide crystal by 1
// 0001 - divide crystal by 2
// 0010 - divide crystal by 4
// 0011 - divide crystal by 8
// 0100 - divide crystal by 16
// 0101 - divide crystal by 32
// 0110 - divide crystal by 64
// 0111 - divide crystal by 128
// 1000 - divide crystal by 256
analogReference(INTERNAL); // sets reference to internal 1.092v
pinMode(13, OUTPUT); // charge active light
pinMode(SolVlts, INPUT); // specifies pin 2 as available solar volts suitable for charge
EICRA = 0x00; //configure INT0 to trigger on low level
Serial.begin(9600);
delay(2);
if (PowerCheckCount > 0)
{
Serial.println(" Remembers Yay ");
delay(3);
}
if (PowerCheckCount == 0)
{
Serial.println(" Starting Up Forgotten ");
delay(3);
}
delay(4);
/*** Setup the WDT ***/
/* Clear the reset flag. */
MCUSR &= ~(1<<WDRF);
/* In order to change WDE or the prescaler, we need to
* set WDCE (This will allow updates for 4 clock cycles).
*/
WDTCSR |= (1<<WDCE) | (1<<WDE);
/* set new watchdog timeout prescaler value */
WDTCSR = 1<<WDP0 | 1<<WDP3; /* 8.0 seconds */
/* Enable the WD interrupt (note no reset). */
WDTCSR |= _BV(WDIE);
//digitalWrite(Relay1on, HIGH);
//delay(4);
//digitalWrite(Relay1on, LOW);
//delay(4);
//digitalWrite(Relay1off, HIGH);
//delay(4);
//digitalWrite(Relay1off, LOW);
auxcharging=0;
}
void loop()
{
SleepCheck(); // divide#mosfet#pin2 12v solar ready triggers pin
//SerialDiagnostics();
//AuxBattMan();
//Serial.print(AuxBatt);
//delay(2);
Serial.println("Working");
delay(3);
//Serial.print(AuxVolts);
//delay(2);
StatusFlash();
delay(1000);
MiniSleep(); // WDT Sleep for 8 seconds
//PowerChecker();
}
However I adapted my example from another page to prove that RAM is not cleared:
#include <avr/sleep.h>
#include <avr/wdt.h>
#define LED 13
// watchdog interrupt
ISR (WDT_vect)
{
wdt_disable(); // disable watchdog
} // end of WDT_vect
unsigned int counter;
void myWatchdogEnable (const byte interval)
{
MCUSR = 0; // reset various flags
WDTCSR |= 0b00011000; // see docs, set WDCE, WDE
WDTCSR = 0b01000000 | interval; // set WDIE, and appropriate delay
wdt_reset();
byte adcsra_save = ADCSRA;
byte prr_save = PRR;
ADCSRA = 0; // disable ADC
PRR = 0xFF; // turn off various modules
set_sleep_mode (SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_mode (); // now goes to Sleep and waits for the interrupt
ADCSRA = adcsra_save; // stop power reduction
PRR = prr_save;
} // end of myWatchdogEnable
void setup ()
{
} // end of setup
void loop()
{
Serial.begin (115200);
Serial.println (counter++);
Serial.flush ();
while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer
UCSR0A |= 1 << TXC0; // mark transmission not complete
while (!(UCSR0A & (1 << TXC0))); // Wait for the transmission to complete
Serial.end ();
// sleep bit patterns:
// 1 second: 0b000110
// 2 seconds: 0b000111
// 4 seconds: 0b100000
// 8 seconds: 0b100001
// sleep for 8 seconds
myWatchdogEnable (0b100001); // 8 seconds
} // end of loop
Serial output:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
I can't see what is wrong with your code, but it will be something to do with all that fiddling around with registers. Take the simpler example, and expand on that.
Serial.println (counter++);
Serial.flush ();
while (!(UCSR0A & (1 << UDRE0))) // Wait for empty transmit buffer
UCSR0A |= 1 << TXC0; // mark transmission not complete
while (!(UCSR0A & (1 << TXC0))); // Wait for the transmission to complete
Serial.end ();
Why is this part needed please? Is it in place of delays to allow serial to work right? Also do I have to do it after every single serial println so that each line waits to complete? Then just serial end at end but why end serial if we are only sleeping? Also in your code I'm not sure if I see brownout detection disabled or not but I need that too. Do you have a simpler version than the one that uses all those registers I had before?
Serial data is output using interrupts. No interrupts during nap time. So, to ensure that all the data is output, you need to wait for the last character to be in the output byte. That's what Serial.flush() does. Then, you need to wait until that last byte has been shifted out.
Also do I have to do it after every single serial println so that each line waits to complete?
Create a function to do your printing. And waiting.
Thanks Nick and Paul I appreciate the help. I just need to integrate the second sleep mode now and find out a simple brownout disable command to replace the register version I was using in 1st example code. Also still a little curious what in my original code was causing the issue. But the code from nick does indeed work and I'm going to try to adapt and move over functions from my broken code a function at a time.