I have followed some of the ideas put forward in reducing the current of the DS3231 RTC by removing the LED, cutting the track from the SQW to its pull up resistor and removing the charging resistor together with using a CR2032 cell. See DS3231 RTC on ZS-042 module - if low power is important.
I needed to power up an arduino every hour for a couple of seconds to obtain the time and date as well as do some further measurements. I used an auxilliary circuit comprising some gate logic and a P-Channel MOSFET to save power used by the arduino. This works fine and below is my circuit.
*----------------------------------------------------------------------*
See example from https://github.com/JChristensen/DS3232RTC/issues/5
Note the area with enumerations of alarm types in the library.
Example of setting a recurring hourly alarm with the DS3232 RTC which
turns on an arduino micro for the time as determined by an attached
monostable multivibrator.
----------------------------------------------------------------------*/
#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC
#include <Time.h> //http://playground.arduino.cc/Code/Time
#include <Wire.h> //http://arduino.cc/en/Reference/Wire
#include <TimeLib.h>
#include <SPI.h>
#include <LiquidCrystal_I2C.h>
#include <LCD.h>
#include <LiquidCrystal_I2C.h>
#define DS3232_I2C_ADDRESS 0x68;
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //new lcd
tmElements_t tm;
void setup(void)
{
Serial.begin(115200);
lcd.begin(16, 2);
setSyncProvider(RTC.get); // the function to get the time from the RTC
if (timeStatus() != timeSet)
Serial.println("Unable to sync with the RTC");
else
Serial.println("RTC has set the system time");
RTC.squareWave(SQWAVE_NONE);
RTC.setAlarm(ALM2_EVERY_MINUTE, 0, 0, 0, 1);//Testing setting gives once per minute
// RTC.setAlarm(ALM2_MATCH_MINUTES, 0, 0, 0, 1);//causes an alarm when the minutes
//match (once per hour)
RTC.alarm(ALARM_2);
RTC.alarmInterrupt(ALARM_2, true);
lcd.setCursor(0, 0); // set cursor to column 0, row 0
if (RTC.read(tm)); //read the RTC time and date
lcd.clear();
digitalClockDisplay();
}
void loop(void)
{
//Do things
}
void digitalClockDisplay(void)
{
lcd.print(hour());
lcd.print(":");
lcd.print(minute());
lcd.setCursor(0, 1); // set cursor to column 0, row 1
lcd.print(day());
lcd.print(' ');
lcd.print(month());
lcd.print(' ');
lcd.print(year());
}
The DS3231 gives a very narrow pulse so this circuit enables a greater turn on time. I am interested if there is a better way where the total circuit drain is achieved.
The SQW pin drives the mosfet gate directly. When the alarm time is reached, it goes low, and stays low until the alarm flag is cleared. So the Arduino powers up, does whatever it needs to do, powers up the ZS-042's Vcc pin, and sets the next alarm time. Its last official act is to clear the alarm flag, which turns off the mosfet and powers down the circuit. So you don't need any of the logic gates to provide a specific ON time. The Arduino turns off its power when it's done.
If the input power source is greater than 5V, you can use the same circuit if the regulator is upstream from the mosfet. But if the regulator is downstream or even on the Arduino, then you need to add an N-channel mosfet to protect the SQW pin from the high voltage:
The N-channel circuit is a bit unusual in that it is non-inverting. it takes advantage of the fact that you can turn it on either by raising the gate voltage relative to the source, or by lowering the source voltage relative to the gate. So when SQW goes low, the N-channel turns on, which grounds the gate of the P-channel.
Edit: Forgot to say that most DS3231 libraries automatically clear the alarm flag during initialization or ahead of writing the next alarm time. You may have to modify the library code to stop it from doing that.
Thanks alto777 and ShermanP for the replies. As I have come more from a hardware background than software I tended to favor more hardware than software solutions. By keeping the duty cycles of the micro to a minimum means that I did not need to pursue Gammon micro power saving techniques as much. However, I will try to implement the Gammon recommendations and do an A/B comparison with my approach. I did try the ShermanP method but seemed to get a bit lost in the software side. My method gives me the result I need and is easily understandable but I am not discounting other methods as outlined being just as effective or better than my method.
So, I think I would place the resistor between the 3v coin cell and the INT/SQW pin.
With that in mind, I think the circuit makes sense. N-Mosfet is off when SQW is off, as gate and source are both 3v (Provided pull-up resistor on SQW is added), so VGS is 0v. N-Mosfet is on when SQW is low, as gate is 3v and source is 0v, so VGS is +3v.
A BSS138 N-channel Mosfet, with max vgs(th) of 1.5V, seems appropriate, which will work across the entire voltage range of coin cell.
Originally I thought a pullup resistor was needed too. But I found that it works fine without one, which also saves on current draw from the coin cell when SQW turns on.
If the SQW output FET is off, then the source of the N-channel is effectively floating. But if anything were to cause a temporary turn-on of the N-channel, there's no place for the current to go. So any source capacitance would immediately charge up to 3V or more, and the N-channel would turn back off.
So in effect R1 serves as the SQW pullup resistor when SQW goes low, but there's no need for a pullup when SQW is floating - because the output of the N-channel is also floating, so it doesn't matter if the N-channel is on or off.
I think the BSS138 would work fine. As a practical matter, even a typical 2N7000 or BS170 would work, but the BSS138 would work down to a lower coin cell voltage.
I'm lost in the software as when the SQW goes low I find that any access of the RTC causes the SQW to be reset and goes high. That is why I came up with my hardware method to ensure the micro continues to be powered up to do other things such as doing sensor measurements. What software commands are needed to enable the SQW pin to remain low for a required time. I'm happy with my method but would still like to know an alternative software solution.
It's not just any access that clears the alarm flag. Looking at the library code, it appears the functions alarm() and clearAlarm() clear the alarm flag, but checkAlarm() does not. But you can set the new alarm time, and the alarm interrupt, without clearing the flag.
Include a DPST push-button (U3), to allow a user to manually turn on the circuit. 1 pole connects the source of N-FET to ground, which turns the circuit on when push button is pressed. The 2nd pole of the switch connects to an input pin of the microcontroller. On boot, the low input pin signal instructs the RTC to turn the Alarm output on, so as to latch the circuit. A second press of the push button can unlatch the circuit and turn it off.
If the wake-up's are infrequent enough, and only consume a tiny amount of current, then you could opt to use long-life alkaline batteries (e.g. EP91 w/ 12 year usable life), or lithium, (e.g. L91 w/ 20 year life).
Instead of having the RTC powered by a coin cell, you could get a separate specialized RTC linear regulator (U2), to allow the RTC to be powered off the main battery.
If the RTC battery ever needs to get changed, you could add a low-leakage capacitor (C3), to allow the RTC to keep operating for a short while after the battery is disconnected.
EDIT:
Here is the different approach, in which you have the MCU & RTC continually powered by the battery, through a low quiescent current regulator. The HT78xx LDO regulator is a popular choice for this. The ESP32 is also a popular choice, as it can reach down to 2.5 micro-amps in hibernation.
You can also include an additional P-channel mosfet switch (Q3) to turn off other external components, which have a high standby current.
I haven't actually used an RTC, or used an MCU in deep sleep, so some of this advice might be wrong. I'm also unsure which particular ESP32 GPIO pins are usable in hibernation.
Your additions look very good. But there's one thing you need to keep in mind about the DS3231. If you power it at the chip's VBAT pin, it will keep time at about 2uA. But if you power it at its Vcc pin, it will draw almost 100uA. Nobody knows why. So if you power the RTC module at the Vcc pin, it will draw about 0.1mA from your 6.8V battery pack, and that's going to significantly reduce your battery life.
One alternative for dealing with this is a modification to the ZS-042 module offered by Ed Mallon in this video:
This video is almost painful to watch, and I really don't like the diode drops. My circuit deliberately powers the module from the coin cell during the sleep period, and turns on Vcc only when you need to communicate with the RTC. Power at Vcc is needed for that because Vcc powers the pullup resistors for SDA and SCL. Without power at Vcc, the pullups become pulldowns, and I2C no longer works.
Also:
I'm not sure you can do that directly. I think the only way to turn on the alarm output is for the alarm time to match the current time. I don't think you can just set the flag manually. But I've never tried that.
ohh ok, probably easiest to just use the 2.75v or 3v version of that RTC regulator and hook it to VBAT.
Then have the primary regulator for the MCU go into VCC when the power turns on (5v or 3.3v)
ok, figured that may be a tricky one. plenty of other methods of getting the latching push button feature to work though.
thanks.
EDIT:
Here is a quick and dirty update. RTC is now powered by VBAT when MCU is off. The manual push button switch is now latched through an output pin of the MCU.
On boot, if you detect a LOW on the input_pullup pin, you know the manual switch has been pressed and the output pin goes LOW. To turn the mosfet off, you switch the output to input pullup.
Just to be safe, you can probably also add the pullup resistor back to the SQW output, as these AA batteries have significantly higher Mah than the coin cell.
Remember that there is a protection diode going from each Arduino GPIO pin to Vcc. So as Vcc drops to zero, the N-channel's source would have a path to 0.7V, which would turn it on. So I don't think the 5V power could ever be turned off. It seems you would need a second N-channel rigged up the normal way to ground the P-channel gate independently of the RTC's control.
Another approach would be to quickly read the alarm flag on boot, but without changing it, and if it is NOT set, meaning you must be pressing the button, then set the alarm for one second from now, and keep reading the flag until it shows as set, which would also mean that SQW has gone low. You could even flash the D13 LED to indicate that you can release the button now.
And ……some more ideas …..You could also look at building your project around a “bare” 328 processor ( see minimal Arduino ) .
There are some great low power techniques for that , and even when running it will take far less than a full Arduino .
You could also then run at 8Mz and power directly from two of those AA batteries
You can also arrange for the micro to seal itself on and turn itself off via that FET when it’s task is over
I've been using the DS3231 alarms in conjunction with its SQW pin to power ON my device, do something for 10 seconds and power off again. While it's off, only the CR2032 battery is powering the DS3231 with a few microamps.
I did look at checkAlarm() but when I tried to use it in my program I got the following error message and so I went with the hardware method.
RTC_original:29:6: error: 'class DS3232RTC' has no member named 'checkAlarm'; did you mean 'setAlarm'?
RTC.checkAlarm(ALARM_2);
Ok, extra n-channel sounds like the easiest route to go then. Keen to try this.
@hammy also raises a great point, in that 2 or 3 L91 AA batteries could power a 5v MCU without the regulator. Either with the deep sleep method, or with the P-fet switch-off method.
You could also power the RTC off those same batteries at the VBAT input, and then somehow switch over to the VCC input of RTC when you need to use it.
Will open up a new thread about this at some point.
Well, it's definitely there in the library's .cpp file, at least in the most recent version 2.0.1:
// Returns true or false depending on whether the given alarm has been
// triggered, without resetting the alarm flag bit.
bool DS3232RTC::checkAlarm(ALARM_NBR_t alarmNumber)
{
uint8_t statusReg = readRTC(DS32_STATUS);
uint8_t mask = _BV(DS32_A1F) << (alarmNumber - 1);
return (statusReg & mask);
}
And it's used in the example file alarm_ex9.ino, but is called differently than you did:
void loop()
{
enum states_t {LED_OFF, LED_ON};
static states_t state(LED_OFF);
switch (state) {
case LED_OFF:
if (myRTC.checkAlarm(DS3232RTC::ALARM_1)) { // time to turn on?
state = LED_ON;
myRTC.clearAlarm(DS3232RTC::ALARM_2);
digitalWrite(LED_BUILTIN, HIGH);
formatTime(timestamp, myRTC.get()); // get current RTC time
Serial << millis() << F(" Alarm 1 at ") << timestamp << endl;
}
break;
case LED_ON:
if (myRTC.checkAlarm(DS3232RTC::ALARM_2)) { // time to turn off?
state = LED_OFF;
myRTC.clearAlarm(DS3232RTC::ALARM_1);
digitalWrite(LED_BUILTIN, LOW);
formatTime(timestamp, myRTC.get()); // get current RTC time
Serial << millis() << F(" Alarm 2 at ") << timestamp << endl;
}
break;
}
}
Perhaps you are using an older version that doesn't have checkAlarm().
Either a bare 328P as hammy says, or my favorite hack is to remove the voltage regulator and the power indicator LED from a 3.3V 8Mhz Arduino Pro Mini. That has the same low-power current as a bare chip, but still has the crystal and all the needed caps and a reset button, and is something you can breadboard. And actually, a Pro Mini is probably going to be cheaper than the bare chip version.
And you could drive either from a single 18650 battery which, at 4.2V, is still under the maximum SQW voltage. So you wouldn't need the RTC N-channel mosfet at all. It would be like my first schematic in post #3. But if you want to add the push-button turnon, you would need an N-channel or NPN for that.
You don't really need to control power with the P-channel if you can turn off everything in the project from the MCU, and put it into deep sleep. That will be less than 1uA sleep current. But in a lot of projects you have other things you can't put to sleep, and that's where the power shutoff really makes a difference in battery life. Or if you use an ESP8266 or ESP32, those chips don't sleep very soundly.
I still like using the coin cell for the RTC when power is off. It's just an extra backup in case something goes wrong or you have to replace the main battery. And I think you would typically get years of life from it.
Yes, I have looked at the cpp file in the library I have used and there is no mention of checkAlarm.
Should I delete my library copy and just use the one from the arduino IDE or
use the rodan/ds3231:arduino library for DS3231RTC-GitHub or
DS3231-Rinki-Dink Electronics. ?
I will shortly investigate all these possibilities. What do you recommend?
Thanks.
If you are using the Arduino IDE, I think you can go to Tools, Manage Libraries, then search for DS3232, and it will show you which version you have installed, and let you update to the newest version. It should do the replacement for you. Well, that's the old version of the IDE. I don't know how it works in IDE v2, but it should be similar.
The one downside of using the DS3231 is all these libraries, all of which have different user interfaces, and none of which are very intuitive. I've used the Rodan library, and didn't like it much. If you are comfortable with the DS3232.h library, I don't see any benefit in changing to a different one. That's assuming you're running the latest version. Maybe someone else here has a DS3231 library they strongly prefer and would recommend.