Yes, you read the title correct. Have you ever forgot your birthday, that you needed someone else to remind you about it? Or what if you could gift someone you love, a device that will wish them on their birthday for 50 times? I kid you not, this simple Arduino powered Birthday Reminder Device that runs on a single CR2450 coin cell can wish your loved ones or yourself a happy birthday for 50 years, before the battery run out of juice.
I think this is the first time somebody makes a birthday reminder/alarm of this kind, because I tried searching for similar projects but found nothing. This project is also available at Hackster.io and Hackaday.
I built this as part of the Hackaday Coin Cell Challenge and it won runner-up prize of $100 Tindie credits. This birthday alarm thing simply popped into my mind and I started researching about the feasibility of the idea. I have never used the sleep modes of any microcontrollers before. So I had to learn everything about making MCUs run at insanely low currents and save every bit of energy from the power source. It was a challenge really! I used Atmel ATmega168P as the microcontroller (actually I modified an Arduino Nano clone that has ATmega168P on it by removing all the unwanted components such as the voltage regulator, USB bridge etc.) and used Arduino IDE to develop the firmware.
The time and birthday date can be programmed via any serial monitor over USB. Once the time and alarm are set, the MCU goes to sleep mode. When the current RTC time matches your birthday every year, the LED will flash for a minute and will print a happy birthday message to the serial monitor if connected to a computer that time. The average current consumption is around 1.2uAh (including self-discharge of the cell) which makes it possible to run it for more than 50 years on a CR2450 (540mAh) Lithium coin cell.
For this project, you can use a bare microcontroller or use an Arduino Nano or Mini boards. All that required is we must run it with internal oscillator which is 1MHz and at full 1.8 - 5V operating voltage range. The CR2450 or similar Lithium cells have a nominal voltage of 3V, and so we can run the MCU without using a voltage regulator. Chinese clones of Nano and Mini are extremely cheap that you can buy them for the chip's price! I used such a Nano clone that has CH340G as the USB to serial bridge. Below is the one I used. You can download the schematic of this Arduino clone at the end.
I had both ATmega168 and 328 versions. I bought the 168 versions by mistake a few years ago (now I found a use for it). In this particular board you need to remove,
Normally, the chips on the Arduino boards will be coming with the Arduino bootloader and fuse bits. We need to change this in order to run the MCU at a lower power mode. In order to achieve this, we need to,
Above is the fuse settings for ATmega168P. Note that you need the "P" versions of the ATmega chips because they have the pico-power (a low power mode) feature. The normal versions (non P) don't support these extra power saving modes. So make sure you get the P versions. You might now wonder why I'm using 168 instead of 328. That's because when I was testing the circuit, 328 seemed consuming around 30uA for the same code and setting I used for 168 which only consumed around 2uA. I don't know why this is. Like I said before, this is the first time I'm fiddling around with power saving modes such as deep sleep. So I must've been missing something. If you know anything about it, please let me know in the comments.
To change the fuse bits, we need an ISP programmer. There are many ISP programmers and compatible softwares. I used the USBasp as programmer and ProgISP (Download) as programming software. The chip ID or signature of the ATmega168P-AU I used was 1E940B. This might change depending on the version you have. To change the fuse bits:
If successful, a message will be printed to the console. From now on, you'll need the ISP to flash the MCU. Below is the USBasp I used.
Now that we have changed the fuse bits of our microcontroller, we also need to tell the Arduino software and the compiler about the changes we made so that we can compile the code properly inside the Arduino IDE. How we do this is by adding a custom board definition in the boards.txt file that resides in the Arduino installation directory that is normally at <installed location>/Arduino/hardware/arduino/avr/boards.txt on Windows systems. This might be different for you depending on which OS you have, or version of the IDE you have. I'm using the IDE version 1.8.5
Once we locate the boards.txt file, you need to add a custom definition of a new Arduino Pro Mini board. Around line 655 will be the starting of the existing Pro Mini board definitions. There'll be many versions of the boards. We need to add a new variant. Add the following definition (copy-paste to the text file) to it and save.
## Arduino Pro or Pro Mini (3.3V, 1 MHz) w/ ATmega168
## --------------------------------------------------
pro.menu.cpu.1MHzatmega168=ATmega168 (3.3V, 1 MHz)
pro.menu.cpu.1MHzatmega168.upload.maximum_size=14336
pro.menu.cpu.1MHzatmega168.upload.maximum_data_size=1024
pro.menu.cpu.1MHzatmega168.upload.speed=19200
pro.menu.cpu.1MHzatmega168.bootloader.low_fuses=0x62
pro.menu.cpu.1MHzatmega168.bootloader.high_fuses=0xDF
pro.menu.cpu.1MHzatmega168.bootloader.extended_fuses=0xF9
pro.menu.cpu.1MHzatmega168.bootloader.file=atmega/ATmegaBOOT_168_pro_8MHz.hex
pro.menu.cpu.1MHzatmega168.build.mcu=atmega168
pro.menu.cpu.1MHzatmega168.build.f_cpu=1000000L
You need to edit the boards.txt while the Arduino IDE is not running. Once you save the new boards.txt file and restart Arduino IDE, you'll see the new board we just added in the list. Take a look at the screenshots below.
Now we're ready to compile Arduino codes for our new board. As we're not using the Arduino Bootloader (BL), we need to create a hex file of the program and use USBasp and ProgISP to flash the microcontroller. We can do this using the "Export compiled binary" option from Sketch menu of the IDE or hit Ctrl + Alt + S
. When we do that, two hex files (intel format) will be created in the same directory our sketch resides. One hex file is with BL, and the other is without BL.
Once we have the hex file, in the ProgISP choose the Load Flash option to load the hex file we want to flash the MCU with, then hit Auto button. If uploading is success, it'll be printed to the ProgISP's console.
The Intersil ISL1208 is a low power RTC IC with I2C interface. It uses an external 32.768 KHz crystal to keep track of the time and has month-date-hour-min-sec alarm registers. It only consumes around 400nA in battery opearation (VBAT) operation and a maximum of 1.2 uA at VDD. The operating voltage is from 1.8V to 5.5V. What make this a good candidate are the low power consumption and the month-date alarm feature. Normal RTCs such as DS1307 doesn't have a month setting in alarm register without which we can't generate a birthday alarm every year. It has an interrupt output pin which will generate a 250mS active LOW signal when the current time matches the alarm date and time. We'll use this to wake the MCU up from sleep mode which I'll explain further below. To learn more about ISL1208 and interfacing it with Arduino with a library, go to my tutorial - Interfacing ISL1208 RTC with Arduino.
As I had an SMD version of the ISL1208 I had to make a little breakout board in order to be plugged onto my main board. Below is what I made.
This will be the main power source of this project. Specifications for a Renata CR2450 are as follows,
Use the above schematic to solder the modules on a perfboard. The two 4.7K resistors are the I2C pull-ups. The values can range from 3.3K to above 5.6K. The R2 and R3 are pull-ups for the interrupt pins. Arduino Nano has two hardware interrupt pins - digital pin 3 and 2. Digital pin 2 will be used for the alarm wake up interrupt from the RTC and digital pin 3 will be used to wake the MCU when you need to set the time. Below is the CP2102 USB-to-Serial module I used. You can use any such USB bridge controllers such as FT232R.
The USB-to-Serial module will be used to communicate over the serial monitor. The RX and TX pins of CP2102 are connected to RX and TX pins of Nano respectively. Note that you shouldn't connect the +5V from the USB to the main VCC voltage. A full tutorial on CP2102 USB-UART bridge controller can be found here - CP2102 USB to Serial Converter.
The working of the device actually quiet simple. Let's see how the main algorithm works,
When powered up for the first time, all RTC registers will be zero and it doesn't increment until we first write to any of them. To set the time on the RTC,
Serial Established.
Ready to update time.
t
command will print the current time, a
will print the alarm date and time, and c
will cancel the time setting operation and put the MCU into sleep mode after 6 seconds.
TYYMMDDhhmmssp#
Where,
For example, to set the time and date 08:35:12 AM, 05-01-2018, we should send:
T1801050835120#
Where,
If the operation is successful, the MCU will print the received time to the console as:
Time update received = T1801050835120
Date and Time is 8:35:12 AM, 5-1-18
If the time string you entered is invalid, the below message will be printed:
Invalid time input - <original string>, <length of original string>
Once you successfully set the time, the RTC will keep track of it as long as there's power available to it. You can verify the time you just set by sending the t
command. Setting the alarm is similar to this except the data format is different. To set the alarm you need to send it as:
AMMDDhhmmssp#
Where,
Note that there's no year information with the alarm string because obviously we don't need it. For example to set my birthday 08:00:00 AM, 28-08, I need to send:
A08240800000#
You can check the alarm time anytime with the command a
. Once the alarm time and date are set it's time to put the MCU into sleep. Then the device will print the following message:
Everything's set. Please disable the time set pin now.
Now you need to turn the time setting switch OFF ie, pull the digital pin 3 HIGH (the 10K pull-up will do that). The system won't sleep until you do this. When the time setting switch is turned OFF, the device will enter into sleep mode in 6 seconds and print the below message before it.
Well done! Sleeping in 6 seconds..
So that's how you set the time and alarm. Now, whenever you need to check the time or update it, you can turn on the timer setting switch and the system will wake up, establish serial communication and prompt you to send the time. It will print the following message up on waking up,
Serial Established.
Time update wake up.
Ready to update time.
If you're just checking if time is correct and don't want to change anything, send c
command to cancel the operation and put the system into sleep again. You need to also disable the time setting switch at this point.
When the current time matches the alarm time ie. your birthday, the RTC will generate a 250mS interrupt signal to the digital pin 2 of the Nano board. This signal will wake up the system. Up on waking up, the device will know that it's your birthday and will establish serial communication (only if you have the USB connected) and print the following message,
Tada! It's your birthday! Happy B'Day <your name> :)
See you on your next birthday! TC. Bye!
Sleeping in 6 seconds..
And it'll also flash the LED connected to digital pin 13. Here's a screenshot of the Arduino serial monitor while I was testing the system.
So that's how you operate this device. To understand this in the code level, read the next section.
This project is completely open source and therefore I've published the source code for the firmware on my GitHub at https://github.com/vishnumaiea/Birthday-Alarm/ under MIT License. You're free to adapt, modify and redistribute without any restrictions. If you would add a backlink to this project from your modified one, that'd be appreciated. I've thoroughly commented the code and made it straight forward wherever possible.
We have total 13 functions/procedures in the code. They are:
1. void setup()
This is the Arduino's setup function that will initialize everything and set the configuration registers of the ISL1208 RTC.
2. void loop()
The main loop function.
3. void sleepNow()
This function terminates all communications, disables the MCU's internal peripherals, attaches the interrupts to digital pins 3 and 2, and puts the system into deep sleep mode. Up on any interrupt, the program execution continues from the line after sleep_mode()
. Note that before this normal program execution resumes, the MCU would've completed the interrupt service routines (ISRs) associated with the interrupt pins which are alarmInterrupt()
and timeUpdateInterrupt()
.
4. void alarmInterrupt()
The ISR associated with the INT0 interrupt on digital pin 2.
5. void timeUpdateInterrupt()
The ISR associated with the INT1 interrupt on digital pin 3.
6. void fetchTime()
fetchTime()
reads the time registers of the RTC and will print the current time to the console.
7. void blinkLED()
Blinks the LEDs obviously.
8. bool establishSerial()
Establishes serial communication with the help of USB-to-Serial module.
9. bool endSerial()
Ends serial communication.
10. byte bcdToDec(byte)
Accepts a BCD (Binary Coded Digits) value and translate it into corresponding decimal value. We need this because the RTC registers only stores and accepts BCD values. Therefore we need to convert to and from BCD occasionally.
11. byte decToBcd(byte)
Accepts a decimal value and translates it into corresponding BCD value.
12. void printTime()
Reads the RTC time registers and prints the current time to the console when t
command is received.
13. void printAlarmTime()
Reads the RTC alarm registers and prints the alarm time and date to the console when the a
command is received.
This would be the most interesting part of this project where you'll know how I ended up making a device that run for 50 years on a coin cell! I first prototyped the entire circuit on a breadboard and finalized the design. A Li-Ion battery (3.6V) was used for testing purpose so as to save my brand new coin cells. I used my Fluke 87 True RMS multimeter for the current measurements. It has a 0.1uA precision for the micro ampere range.
Let's see how we can put the Atmega168P into deep sleep mode and reduce the current consumption drastically.
noInterrupts (); //temporarily disable interrupts | |
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Choose our preferred sleep mode: | |
sleep_enable(); // Set sleep enable (SE) bit: | |
ADCSRA = 0; //disable ADC | |
power_all_disable(); //disables all modules | |
digitalWrite(LED_PIN, LOW); //turn LED off to indicate sleep | |
interrupts(); //re-enable interrupts | |
sleep_mode(); //goes to sleep |
As I've said before, this is the first time I;m using sleep modes in a microcontroller (MCU) because I've never needed it before. Most of the information related to AVR sleep modes was found from this forum thread and the AVR library documentation.
ATmega168P has five sleep modes.
SLEEP_MODE_IDLE
– least power savingsSLEEP_MODE_ADC
SLEEP_MODE_PWR_SAVE
SLEEP_MODE_STANDBY
SLEEP_MODE_PWR_DOWN
– most power savings
More info on the sleep modes can be found here and in this video. We're using the SLEEP_MODE_PWR_DOWN
mode as you can see there. At this mode, the current consumption at 3.6V is only around 0.2 uA. See the below graph from the ATmega168PA datasheet that shows the relation between active current vs supply voltage and power down current vs supply voltage.
Here's the actual reading of the current consumed by sleeping ATmega168P @1MHz.
The value hops between 0.1uA and 0.2uA due to the lack of enough precision. Such a precise measurement isn't necessary but would've been interesting to see.
The power consumption of ISL1208 RTC at max is 1.2uA. So if we add this with the power down mode current consumption of the MCU we get 1.2 + 0.2 = 1.4uA. My meter measured between 1.4uA and 1.6uA which justifies the calculations. The variations is only due to the lack of precision and our approximation or rounding of numbers.
Here's an unlisted video from my YouTube channel where I show the testing.
Now let's do the simple math to find how long we can the system on a coin cell. The CR2450N from Reneta has a nominal capacity of 540mAh. I have two red SMD LEDs on the system which consume about 6mA (even with two LEDs) with when turned ON. So that is the peak current consumption of the device at worst. How long these LEDs light up can be summarized as,
Average Current = [((6 x 10^-3) x 180) + ((1.6 x 10^-6) x 31557420))] / 31557600
= (1.08 + 50.491872) / 31557600
= 51.571872 / 31557600
= 1.634 x 10^-6 = 1.634 uAh
If the average current consumption is 1.634uAh, then the 540 mAh cell can run the device for:
Time Span (approx) = (540 x 10^-3) / (1.634 x 10^-6) = 330477.3562 hours
= 13769.88 days
= 37.699 years
Note that this is an approximation and does not consider self-discharge of the battery. It'll be taken into account later. You can also use the ElectroDroid app to calculate battery life. Here's a screenshot of the calculations we just did.
But wait... Can we reduce the current consumption further ? YES WE CAN! I made further optimizations to the design to limit the average current consumption to 0.6uA. Now let's see what optimizations I did,
LOW_POWER
mode bit (LPMOD) of the RTC set to 1, activating the low power mode. So now you might think if the low power mode is set, then the chip should be consuming the lowest current. But that's not the case when we have the VDD tied to VBAT. Because low power mode bit is only useful if we have VDD > VBAT all the time. At such situations, the RTC's internal power switch will select VBAT as power source reducing the current further by 600nA when VDD >= VBAT (from typical 1.2uA which I've mentioned before). But if we can run the RTC in VBAT only with VDD = 0, the current consumption can be reduced to the minimum ie, 400nA as per the datasheet. So what I did was, first I disabled the low power mode by setting LPMOD to 0. Then added a jumper to the RTC breakout board to disconnect the VDD pin from VBAT when we don't need it. Why need the jumper is because, the VDD pin must be greater than or equal to VBAT in order for the I2C to work. So we can connect the jumpers when we need I2C while we're setting the time, and can disconnect it after. This will let the RTC to consume the targeted 400nA current. Tada! We did it. Read more about this on the ISL1208 RTC Tutorial.Now that we have reduced the current consumption of the RTC from 1.2uA to 0.4uA (400nA), we can do the math again!
System Active Current = 3mAh max
System Sleep Mode Current = 0.4uA (RTC) + 0.2uA (MCU) = 0.6uAh
System ON time = 60s (time setting) + 19s (birthday flashing) + 6s (before sleep) = 85 seconds
System Sleeping Time = 31557600s - 85s = 31557515 seconds
Total time in a year = 31557600 seconds
Battery capacity = 540mAh
If we put those new values to the ElectroDroid's battery life calculator, we get, 101 years and 136 days. A theoretical operating time of more than a century! The average current consumption is now only 608 nA. Here's the screenshot.
Here's the current consumption test video after the optimizations and mods.
Batteries aren't perfect, nor anything we design. So let's also consider the 1% self discharge of the cell into account.
1% of initial capacity of 540mAh CR2450N = 5.4mAh
Self-discharge current = 5.4mA per year or 616.4nAh (5.4mA / hours in a year)
Adding this 616.4nAh with the 600nAh sleep current = 1.216uAh
Expected operating time with average current of 1.224uAh = 50 years, and 131 days
That's the actual operating time if the cell will be fine. Below is a table of actual operating times of different types of coin cells with the 1% self-discharge of initial capacity every year.
Cell Capacity | Self-discharge Current Isd | *Average Current Consumption | Expected operating time |
225mAh (CR2032) | 256nAh | 864nAh | 29 years, 264 days |
540mAh (CR2450N) | 616.4 nAh | 1224nAh (1.22uAh) | 50 years, 131 days |
950mAh (CR2477N) | 1084 nAh (1.084uAh) | 1692nAh (1.692uAh) | 64 years, 33 days |
*Average current is calculated with 3mA active current and (Isd + 600 nA) sleeping current and doing the same math we previously did.
The main practical concerns associated with running the device for such long periods are,
Self-discharging not only reduces the capacity but also reduces the voltage. Both ATmega168P and ISL1208 are designed to be operated fine at voltages as low as 1.8V. So the reduction in voltage might not be a problem. You can learn more about running systems with coin cells here.
To ensure long operating span, we must make sure the device is properly concealed against much of the environmental changes such as temperature, humidity, corrosion etc. These are some things you can do to protect your circuits,
I used a perfboard to solder everything as per the schematic. Used berg connectors for the battery, switch and LED so that it'll be easy to remove them if needed. Below are some images of the PCB.
To build the enclosure I used a 4" x 4" switch box which I bought from an electrical shop. I made two rectangular holes for the switch and USB. You can 3D print an enclosure if you want; sadly I don't have one. The dome was snatched from a cheap LED light and used super glue to fix it on the box. I painted it with silver spray paint.
Use your ingenuity to build it.
What missing is some decorations. I'm not good at decorating things. If you are going to gift this to someone, you know what to do.
The final output is satisfying to the extend of my hard work. I might find someone else to decorate it.
There's always room for improvement. Some of my suggestions are,