Feed on
Posts
Comments

So I was making a modification to the Standalone Temperature Logger (SATL) to make it log voltage instead of temperature and happen to discover an issue with the Brown-out Detector which is set to 2.7V. Download the SATL v2.1 or visit the SATL project page.

It turns out that I had a 3V coin cell which was a bit discharged as it was sitting at 2.95 volts and when I connected it to the circuit it would drop to 2.7 volts and draw about 0.5mA of current which was very odd. At 2.95 volts I thought it should still have some life left. After trying to program very simple programs to the ATtiny85 like a blinking LED, making it sleep right away, etc I had no success. Eventually I reset the fuse bits and it all started working well again which lead me to discovering that BOD was the issue.

I tried adding 0.1uF, 4.7uF and 470uF capacitors which sometimes worked but it wasn’t reliable. I’m now recommending that BOD be disabled for the SATL unless you know the 3V coin cell is brand new or near new condition. The BOD issue brought my attention to other places within the SATL that could be improved upon.

Increase the button pull down resistor

Connected to the button we have a standard 10k pull down resistor and each time the button is pressed or held 0.3mA is consumed, seems sort of wasteful.

Looking through the Atmel datasheet it only seems to mention pull up resistors and nothing about pull down resistors. So what about the pull down resistor? I did some hunting around and found a post on AVRfreaks on this very question.

The formula provided by the post is Vil x VCC / Iil. Lets open up the Atmel datasheet and find the values to use for 3 volts VCC. We’ll substitute in our values: 0.3 x 3V / 1uA gives us a 900k resistor so I think a 100k should be good enough.

Using watchdog timer for input delays or timings

Next I found that when using the SATL to configure the delay time or start logging, it would consume 1.4mA for a few seconds. Once it’s logging, it’s all good but 1.4mA concerned me and I knew it could be improved. At the moment delays were done when blinking the LED, when waiting for input or timing button pressing. The specific delay time (e.g. 1 second exactly) isn’t that important so I decided to use the watchdog timer to handle all of the conditions for us.

Blinking LED function

Since we blink the LED so much I converted it into a function where we can input the number of times to blink, the watchdog timer delay time and if we can skip the LED off delay at the end.

void blinkLed (byte times, byte wdttime, byte skipLedoffdelay) {
  for (int x = 0; x < times; x++) {
    PORTB |= (1<<ledPin); // LED ON
    setup_watchdog(wdttime);
    system_sleep();

    PORTB &= ~(1<<ledPin); // LED OFF
    if (skipLedoffdelay == false) {
      setup_watchdog(wdttime);
      system_sleep();
    }
  }
  turnOffwatchdog();
}

It’s a simple function, just turn on the LED, configure the watchdog timer for x amount of time, go to sleep then wake up, turn off the LED and go back to sleep for x amount of time.

void turnOffwatchdog(void) {
  cli(); // Turn interrupts off
  MCUSR &= ~(1<<WDRF);
  WDTCR |= (1<<WDCE) | (1<<WDE);
  WDTCR = 0;
  sei(); // Turn interrupts back on
}

Another function was made to turn off the watchdog as I’ll be using it more regularly now. I also removed the switching of the LED to an input before sleeping because we’ll be sleeping and need the LED on sometimes.

Button pressing at the start

It’s time to re-think the button pressing code at the very start where we detect whether the button is pressed three times in a row or held down.

volatile boolean f_pin = 0; // Pin change variable
volatile boolean buttonUsed = 0; // Keep track of button presses and releases
...
ISR(PCINT0_vect) {
  f_pin = 1; // Set global button pressed (or released) flag
  buttonPressed++;
}

I’ve decided to use the pin change interrupt to keep track of how many presses and releases the user made instead of just checking if the f_pin variable is 1 or not.

setup_watchdog(T2S); // Sleep for 2 seconds
system_sleep();
if (buttonPressed != 1) { // Was the button released?
  int newbutton = buttonPressed;
  while (true) { // Sleep for 1 second after each button press, if no button press was made then exit
    setup_watchdog(T1S);
    system_sleep();
    if (newbutton != buttonPressed) {
      newbutton = buttonPressed;
    }
    else {
      break;
    }
  }
}
turnOffwatchdog();

// User held down the button
if (buttonPressed == 1) { // Change delay time
  cbi(GIMSK,PCIE); // Disable pin change interrupt (so that interrupt is not triggered when user releases button)
  functionSelect = CONFIGDELAY;
  delayTime = -1;
  blinkLed(1, T2S, SKIPLEDOFFDELAY);
  sbi(GIMSK,PCIE); // Re-enable pin change interrupt
}

// Start logging
else if (buttonPressed == 6) { // Check if 6, as buttonPressed is incremented when user pushes and releases button
  functionSelect = STARTLOGGING;
  dataCount = 1;
  blinkLed(3, T500MS, 0);
}

The updated code will make it sleep for 2 seconds if the button has been held down by testing if buttonPressed is still 1. If the button was released it bypasses the 2 seconds of sleep and goes into a loop. The loop sleeps for 1 second and then checks if the buttonPressed variable has been updated. If it has been updated then it continues the loop until the button hasn’t being pressed for 1 second.

When we want to start logging, the total changes to the button pin is 6 (3 presses, 3 releases). When the button is held, we disable the pin change interrupt and then light the LED which gives the user an indication to release the button. If we didn’t disable the pin interrupt, the user might see the LED turn off just slightly when they let go of the button which may seem like something is wrong.

As you can see I’ve also added labels instead of using numbers (e.g  STARTLOGGING, T500MS) to make things a little easier to read.

Button pressing for delay time

We can also change the button pressing for delay time to use the watchdog timer. In this case, we blink the LED after the button is pressed so we don’t need a while loop like we did above.

if (functionSelect == CONFIGDELAY) {
  system_sleep(); // Wait for next button press
  f_pin = 0;

  setup_watchdog(T1S);
  system_sleep();
  turnOffwatchdog();

  if (f_pin == 1) { // Button let go
    delayTime++;
    blinkLed(1, T500MS, SKIPLEDOFFDELAY);
    if (delayTime > 10) { // Incorrect value
      delayTime = -1;
      blinkLed(1, T2S, SKIPLEDOFFDELAY);
    }
  }

  // User held down the button to save setting
  else if (f_pin == 0) {
    cbi(GIMSK,PCIE); // Disable pin change interrupt (so that interrupt is not triggered when user releases button)
    if (delayTime >= 0 && delayTime < 10) { // Save to EEPROM
      functionSelect = WAITINPUT;
      eeprom_write_byte((uint8_t*)0, delayTime);
      blinkLed(3, T500MS, 0);
    }
    sbi(GIMSK,PCIE); // Re-enable pin change interrupt
  }
}

We just set the watchdog to time out after 1 second, when we reach the sleep and if the user releases the button, f_pin will be 1 so now we know the user released the button. If f_pin stayed at 0, the user didn’t release the button and we woke up from the sleep because of the watchdog so now we save the configuration.

As a benefit of using the watchdog, we no longer use delays when the SATL isn’t connected to the PC so we can remove the need for timer0, millis and delay_ms_at_1mhz related code.

Power saved

With the changes to v2.1 we’ve saved 0.27mA whenever the button was pressed or held (0.3mA vs 0.03mA) and now that we don’t use delay we’ve saved 1mA (1.4mA vs 0.4mA) when it was waiting for input as a button had being pressed or blinking the LED.

Leave a Reply