I want to use an ATtiny as data logger for the opening and closing of a door/window. For that I am using a reed-switch. Since I want to run it from a 3V coin battery, long term power consumption is crucial. Therefore the ATtiny (currently 13A) is in system down mode most time and is only listening to Pin-Change-Interrupts. Depending on the state of the reed-switch the power consumption differs in an order of magnitude.
- switch open: 0.1µA
- switch closed: 74.5µA
- my Multimeter has a tolerance of ±0.1µA
This is a difference of an order of magnitude. But it is plausible as the datasheet of the ATtiny13A states in chapter 10 that a GPIO pin can only be pulled up and is otherwise dangling. The pull-up resistor is somewhere between 20k and 50k as stated in chapter 18.2.
So in my setup the pin is pulled up and the reed-switch is grounded.
How can I reduce the power consumption in the case where the switch is closed?
Alternatively: Can I somehow revert the state of the switch, as windows and doors are closed most of the time?
The MCU is running with the 128kHz internal Clock with CKDIV8 so practically running @ 16kHz.
The minimum viable code:
/*
3V0 SW0
| | | |
-----------------------
| |
| |
| ATtiny13A |
| |
| |
-----------------------
| | | |
TX GND
*/
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <iso646.h>
#include <stdbool.h>
#include <util/delay.h>
#define BIT_SET( PORT, BIT_FIELD ) PORT |= BIT_FIELD
#define BIT_CLR( PORT, BIT_FIELD ) PORT &= ~BIT_FIELD
#define TX_PIN PB4
#define SW0_PIN PB0
#define INT0_MASK PCINT0
// MARK: - POWER
static void power_down(void) {
BIT_SET(GIFR, _BV(PCIF)); // Clear interrupt signal
BIT_SET(GIMSK, _BV(PCIE)); // Pin Change Interrupt Enabled
sleep_enable();
sleep_cpu(); // Enter sleep mode
sleep_disable();
BIT_CLR(GIMSK, _BV(PCIE)); // Pin Change Interrupt Disabled
}
ISR(PCINT0_vect) {}
// MARK: - Setup
static void setup(void) {
cli();
// Set up output pins
DDRB = _BV(TX_PIN);
BIT_CLR(PORTB, _BV(TX_PIN));
BIT_SET(PORTB, _BV(SW0_PIN)); // pull up interrupt pins
BIT_SET(PCMSK, _BV(INT0_MASK)); // set PCINT0 to trigger interrupt
BIT_SET(GIFR, _BV(PCIF)); // Clear interrupt signal
BIT_SET(GIMSK, _BV(PCIE)); // Pin Change Interrupt Enabled
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sei();
}
static void loop() {
BIT_SET(PORTB, _BV(TX_PIN));
_delay_ms(1);
BIT_CLR(PORTB, _BV(TX_PIN));
power_down();
}
int main(void) {
setup();
while (true) {
loop();
}
}
INPUT
. Once in a while, revert toINPUT_PULLUP
and, if still closed, go back toINPUT
to save battery. You can use the watchdog as a low-power timer for this periodic polling.OUTPUT LOW
. But beware that you cannot switch betweenINPUT PULLUP
andOUTPUT LOW
atomically: you will have to go through eitherINPUT HiZ
(safe) orOUTPUT HIGH
(very bad).