ATTiny85 + RFM12B low-power issues

Added by jpadie about 7 years ago

Hello again.

I am trying to measure how much power my circuit is using, to determine how long I can run it on batteries.

The connections are very straight forward. AtTiny85 connected to RFM12B at 5V without the IRQ pin connected. An LM35 is hooked up to an analogue pin.

the sketch uses Martin’s code RFM12B code primarily, with a function to grab the internal temp sensor value (thanks again, Martin) and some code to measure bandgap (thanks Jeelabs) and calculate the value from the LM35. The intent is to use the LM35 to calibrate the internal sensor.

the sketch is pasted below. It is probably more complex than it needs as I don’t understand it all enough to simplify yet….

the rf12_jsleep function I added rather than change the rf12 library directly.

I find that if I issue a command rf12_cmd(0x8200) the board does not wake up again. Annoying, I must have misunderstood.

I’m also finding that powering the board from 2AAA cells doesn’t work. the chip powers up but the RFM12B board does not send anything. But that’s by the by. I’m not using any voltage converter at the moment.

at 5v (being powered from a 5v pin on an arduino in ISP mode) I am measuring a resting current at 7.2mA and during the packet sending, an increase to 7.9mA.

on batteries (2 * AAA) I’m measuring a current of 1mA quiescent (and the RFM12B is not sending anything, so I can’t measure the burst at the moment).

At the quiescent rate my battery is not going to last long. and I was kind of hoping for a year or more ….

So what am I doing wrong here? or if nothing, what more can I do to bring the current draw down into the microAmp levels (the LM35 will eventually be taken out of the circuit bringing us down by a further 60uA).

the sketch is currently sampling every 10 seconds. i notice no current draw increase whilst sampling (other than when powered at 5v when the RF12 works). When finished my intent would be to run the sampling and sending every five minutes or so. the devices never need to receive anything.

s. I apologise for posting all this code but I’m not savvy enough yet to know what is helpful for you to see and what is irrelevant
pps. I don’t have an oscilloscope regrettably, so i’m measuring with a multimeter …


// This sketch will send a RFM12b packet that is compatible with the Jeelib and can be picked up by a JeeNode running RFM12 demo sket
//for packet design


#define GROUP  212
#define HEADER  17
//433mhz = 1, 868mhz = 2, 915mhz = 3
#define RF12_FREQ     2
#define __AVR_ATtiny_85__

#define RF12_NOT_CS() PORTB |= _BV(PB3)
#define RF12_CS() PORTB &= ~_BV(PB3)
#define MOSI_LOW() PORTB &= ~_BV(PB1)
#define MISO_LEVEL() (PINB & _BV(PB0))
#define RF12_TRANSMIT 0xB8

// RF12 command codes
#define RF_RECEIVER_ON  0x82DD
#define RF_XMITTER_ON   0x823D
#define RF_IDLE_MODE    0x820D
#define RF_SLEEP_MODE   0x8205
#define RF_WAKEUP_MODE  0x8207
#define RF_TXREG_WRITE  0xB800
#define RF_RX_FIFO_READ 0xB000
#define RF_WAKEUP_TIMER 0xE000

#define MAXINT 32767
#define MININT -32767
#define coefficient 1

typedef struct{
      float k;
      float c;
      float f;
      float lsb;

typedef struct{
      int node;
      int eventID;
      long batt;
      float eTemp;
      temp iTemp;

payload data;



int pos=0;
int readings[ TEMPERATURE_SAMPLES ];
      unsigned char byte;
            char ATS_RSSI:
            1;    //ATS=Antenna tuning circuit detected strong enough RF signal
            //RSSI=The strength of the incoming signal is above the pre-programmed limit
            char FFEM:
            1;        //FIFO is empty
            char LBD:
            1;            //Low battery detect, the power supply voltage is below the pre-programmed limit
            char EXT:
            1;            //Logic level on interrupt pin (pin 16) changed to low (Cleared after Status Read Command)
            char WKUP:
            1;        //Wake-up timer overflow (Cleared after Status Read Command )
            char RGUR_FFOV:
            1;    //RGUR=TX register under run, register over write (Cleared after Status Read Command )
            //FFOV=RX FIFO overflow (Cleared after Status Read Command )
            char POR:
            1;            //Power-on reset (Cleared after Status Read Command )
            char RGIT_FFIT:
            1;    //RGIT=TX register is ready to receive the next byte
            //(Can be cleared by Transmitter Register Write Command)
            //FFIT=The number of data bits in the RX FIFO has reached the pre-programmed limit
            //(Can be cleared by any of the FIFO read methods)

      unsigned char byte;
            char OFFS:
            4;        //Offset value to be added to the value of the frequency control parameter (Four LSB bits)
            char OFFS6:
            1;        //MSB of the measured frequency offset (sign of the offset value)
            char ATGL:
            1;        //Toggling in each AFC cycle
            char CRL:
            1;            //Clock recovery locked
            char DQD:
            1;            //Data quality detector output

#define TSENSE         0xf   // Temperature sensor

static void setPrescaler (uint8_t mode) {
      CLKPR = bit(CLKPCE);
      CLKPR = mode;

void enableADC( bool b){
      if (b == true){
            bitClear(PRR, PRADC); // power up the ADC
            ADCSRA |= bit(ADEN); // enable the ADC
      else {
            ADCSRA &= ~ bit(ADEN); // disable the ADC
            bitSet(PRR, PRADC); // power down the ADC

void getITemp(){
      sensorInit() ;
      data.iTemp.c = in_c();
      data.iTemp.f = in_f();
      data.iTemp.k = in_k();
      data.iTemp.lsb = in_lsb();
void readVcc() {
      // Read 1.1V reference against AVcc
      // set the reference to Vcc and the measurement to the internal 1.1V reference
#if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
      ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
#elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
      ADMUX = _BV(MUX5) | _BV(MUX0);
#elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
      ADMUX = _BV(MUX3) | _BV(MUX2);
      ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);

      delay(2); // Wait for Vref to settle
      ADCSRA |= _BV(ADSC); // Start conversion
      while (bit_is_set(ADCSRA,ADSC)); // measuring

      uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
      uint8_t high = ADCH; // unlocks both

      long result = (high<<8) | low;

      result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
      data.batt = result; // Vcc in millivolts

void getETemp(){
      analogReference( DEFAULT );
      pinMode(4, INPUT);
      float temps;
      for(int i = 0; i <= 10; i++){
            float z =  (data.batt / 1000 ) * analogRead( 2 ) * 100/1024;
            if( i > 0) temps += z;
      pinMode(4, OUTPUT);
      digitalWrite(4, LOW);
      data.eTemp = 100 * (floor(temps/5))/2;

void setup(){
      DDRB = _BV(PB1) | _BV(PB2) | _BV(PB3) | _BV(PB4);
      // MOSI, SCK, SEL
      PORTB = _BV(PB3); // deselect RFM12
      data.eventID = 1;
      data.node = 1;

void loop(){
      enableADC ( true );  
      enableADC ( false );
      bitClear(PRR, PRUSI);
      rf12_jsleep( -1 ); //wake the rf12 u
      rf12_cmd(0x82,0x39); //Enable transciever
      rf12_send((uint8_t *)&data, sizeof(data));
      //rf12_cmd(0x8200); //disable everything
      rf12_jsleep( 0 ); //put the rf12 to sleep
      for(int i=0; i<10;i++){

static void spi_run_clock () {
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);
      USICR = _BV(USIWM0) | _BV(USITC);

void rf12_cmd(uint8_t highbyte, uint8_t lowbyte)
      USIDR = highbyte;
      USIDR = lowbyte;

void rf12_loop_until_FFIT_RGIT(void)
      while (!status_H.bits.RGIT_FFIT);

/*    rf12_read_status_MSB
 RX Mode: FFIT = The number of data bits in the RX FIFO has reached the pre-programmed limit.
 Can be cleared by any of the FIFO read methods
 TX Mode: RGIT = TX register is ready to receive the next byte
 (Can be cleared by Transmitter Register Write Command)
uint8_t rf12_read_status_MSB(void)
      asm volatile("nop");
      if (MISO_LEVEL())
      return status_H.bits.RGIT_FFIT;

void rf12_read_status(void)
      USIDR = 0x00;    //Status Read Command
      status_H.byte = USIDR;
      USIDR = 0x00;     //Status Read Command
      status_L.byte = USIDR;

void rf12_TX(uint8_t aByte)
      //FFIT wird gepollt um zu erkennen ob das FIFO TX
      //Register bereit ist.
      //Alternativ ist es auch möglich(wenn verbunden)
      //den Interrupt Ausgang des RF12 zu pollen: while(INT1_LEVEL());
      while (!rf12_read_status_MSB()){}

uint8_t rf12_RX(void)
      USIDR = 0xB0;
      USIDR = 0x00;
      return USIDR;

static __inline__ uint16_t _crc16_update(uint16_t __crc, uint8_t __data)
      uint8_t __tmp;
      uint16_t __ret;

      __asm__ __volatile__ (
                            "eor %A0,%2" "\n\t"
                            "mov %1,%A0" "\n\t"
                            "swap %1" "\n\t"
                            "eor %1,%A0" "\n\t"
                            "mov __tmp_reg__,%1" "\n\t"
                            "lsr %1" "\n\t"
                            "lsr %1" "\n\t"
                            "eor %1,__tmp_reg__" "\n\t"
                            "mov __tmp_reg__,%1" "\n\t"
                            "lsr %1" "\n\t"
                            "eor %1,__tmp_reg__" "\n\t"
                            "andi %1,0x07" "\n\t"
                            "mov __tmp_reg__,%A0" "\n\t"
                            "mov %A0,%B0" "\n\t"
                            "lsr %1" "\n\t"
                            "ror __tmp_reg__" "\n\t"
                            "ror %1" "\n\t"
                            "mov %B0,__tmp_reg__" "\n\t"
                            "eor %A0,%1" "\n\t"
                            "lsr __tmp_reg__" "\n\t"
                            "ror %1" "\n\t"
                            "eor %B0,__tmp_reg__" "\n\t"
                            "eor %A0,%1"
                            "=r" (__ret), "=d" (__tmp)
                            "r" (__data), "0" (__crc)
      return __ret;

void rf12_send(const uint8_t* buf, uint8_t cnt)
      if (!cnt) return;
      uint16_t chksum=~0;


      rf12_TX(0xAA);  //PREAMBLE
      rf12_TX(0xAA);  //PREAMBLE
      rf12_TX(0xAA);  //PREAMBLE
      rf12_TX(0x2D);  //SYNC HI BYTE
      rf12_TX(GROUP); //SYNC LOW BYTE (group 210)
      chksum = _crc16_update(chksum, GROUP);
      rf12_TX(HEADER);            // Header byte
      chksum = _crc16_update(chksum, HEADER);
      chksum = _crc16_update(chksum, cnt);
      while (cnt--)
            chksum = _crc16_update(chksum,*buf++);
      rf12_TX(0xAA);    //dummy byte

void rf12_init(void)
      USICR = _BV(USIWM0); // 3-wire, software clock strobe
      rf12_cmd(0x80, 0xC7 | (RF12_FREQ << 4)); // EL (ena TX), EF (ena RX FIFO), 12.0pF
      rf12_cmd(0xA6,0x40); // 868MHz
      rf12_cmd(0xC6,0x06); // approx 49.2 Kbps, i.e. 10000/29/(1+6) Kbps
      rf12_cmd(0x94,0xA2); // VDI,FAST,134kHz,0dBm,-91dBm
      rf12_cmd(0xC2,0xAC); // AL,!ml,DIG,DQD4
      rf12_cmd(0xCA,0x83); // FIFO8,2-SYNC,!ff,DR
      rf12_cmd(0xCE,0x00 | GROUP); // SYNC=2DXX;
      rf12_cmd(0xC4,0x83); // @PWR,NO RSTRIC,!st,!fi,OE,EN
      rf12_cmd(0x98,0x50); // !mp,90kHz,MAX OUT
      rf12_cmd(0xCC,0x77); // OB1,OB0, LPX,!ddy,DDIT,BW0
      rf12_cmd(0xE0,0x00); // NOT USE
      rf12_cmd(0xC8,0x00); // NOT USE
      rf12_cmd(0xC0,0x40); // 1.66MHz,2.2V


void rf12_cmd (uint16_t cmd){
  rf12_cmd( highByte(cmd), lowByte(cmd));

void rf12_jsleep (char n) {
    if (n < 0)
    else if (n == 0) {

void sensorInit() {
      //analogReference( INTERNAL1V1 );
      // ATTiny85 datasheet p140 (17.13.2), p137 (17.12)
      // Configure ADMUX
      ADMUX = B1111;                // Select temperature sensor
      ADMUX &= ~_BV( ADLAR );       // Right-adjust result
      ADMUX |= _BV( REFS1 );                      // Set Ref voltage
      ADMUX &= ~( _BV( REFS0 ) | _BV( REFS2 ) );  // to 1.1V
      // Configure ADCSRA
      ADCSRA &= ~( _BV( ADATE ) |_BV( ADIE ) ); // Disable autotrigger, Disable Interrupt
      ADCSRA |= _BV(ADEN);                      // Enable ADC
      ADCSRA |= _BV(ADSC);          // Start first conversion
      // Seed samples
      int raw_temp;
      while( ( ( raw_temp = raw() ) < 0 ) );
      for( int i = 0; i < TEMPERATURE_SAMPLES; i++ ) {
            readings[i] = raw_temp;

float in_lsb() {
  int readings_dup[ TEMPERATURE_SAMPLES ];
  int raw_temp;
  // remember the sample
  if( ( raw_temp = raw() ) > 0 ) {
    readings[pos] = raw_temp;
  // copy the samples
  for( int i = 0; i < TEMPERATURE_SAMPLES; i++ ) {
    readings_dup[i] = readings[i];
  // bubble extremes to the ends of the array
  int swap;
  for( int i = 0; i < extremes_count; ++i ) { // percent of iterations of bubble sort on small N works faster than Q-sort
    for( int j = 0;j< TEMPERATURE_SAMPLES - 1;j++ ) {
      if( readings_dup[i] > readings_dup[i+1] ) { 
        swap = readings_dup[i];
        readings_dup[i] = readings_dup[i+1];
        readings_dup[i+1] = swap;
  // average the middle of the array
  int sum_temp = 0;
  for( int i = extremes_count; i < TEMPERATURE_SAMPLES - extremes_count; i++ ) {
    sum_temp += readings_dup[i];
  return sum_temp / ( TEMPERATURE_SAMPLES - extremes_count * 2 );

float in_c() {
      return in_k() - 273;

float in_f() {
      return in_c() * 9 / 5 + 32;

float in_k() {
      //return in_lsb() + offset; // for simplicty I'm using k=1, use the next line if you want K!=1.0
      return (float) (( in_lsb() * coefficient ) + offset);

int raw() {
      if( ADCSRA & _BV( ADSC ) ) { 
            return -1;
      } else {
            int ret = ADCL | ( ADCH << 8 );   // Get the previous conversion result            
            ADCSRA |= _BV(ADSC);              // Start new conversion
            return ret;

Replies (9)

ATTiny85 + RFM12B low-power issues - Added by martynj about 7 years ago

jpadie, please be kinder to your RFM12B ;-)
5V is way outside the operational spec for the module and the current draw you measure won’t tell you much about when Vdd  is in the correct range (<3.8v).

RE: ATTiny85 + RFM12B low-power issues - Added by jpadie about 7 years ago

sorry, I should have said in my first post.

the tests are being done on two different rfm12bs. both are wired to the rf12 breakout boards sold here.

the 5v tests are being done on a breakout board with all the components in place (although a slightly different regulator is being used)
the 3v version uses the a breakout board with the solder jumpers.

both boards are tested working.

the specs say that the minimum operating voltage for the board is 2.2v. I didn’t note down the measurement but I recollect that when i briefly tested it, the voltage at the board was 2.4v.

just as a rough experiment on the batteries (with slightly more refined figures than above):

with just the attiny85 in the circuit, the current draw is 260uA
adding the rfm12b back in increases the draw to c.800uA
adding the LM35 back in boots it up to c880uA

and I’m getting 3v across the power pins of the rfm12b

RE: ATTiny85 + RFM12B low-power issues - Added by jpadie about 7 years ago

there’s something awry in the kingdom of jpadie …

wired it all back up to the batteries after reflashing the chip (i’m finding this attiny85 is losing its programming rather frequently)

using the 5v board in a 3v context (yes I know):

the sketch sends just fine.
the quiescent current draw is 3.8mA
the ‘burst’ draw is 4.28mA

swapping out the 5v board for the 3v,

the sketch sends ok
the quiescent current draw is 3.38mA
the ‘burst’ draw appears variable but between 4mA and 5.4mA. I suspect my wee multimeter is not sampling often enough.

so …

this looks as though things are now working (the chip must have lost/garbled its programming before).
BUT the quiescent draw is still much higher than I’d anticipated with this circuit. I’d expected in the order of 2uA for the attiny85 in power down mode.

so am I perhaps using the loseSomeTime class member incorrectly? or perhaps the set_sleep_mode macro directive doesn’t work with the attiny85 (although the right bits seem to be being set in sleep.h) ?

or am I misunderstanding the nature of power_down and I shouldn’t expect the low uA current drain after all?

all help, as always, most gratefully received.


RE: ATTiny85 + RFM12B low-power issues - Added by ozzy about 7 years ago

This piece of code have been working for almost a year on three AA batteries. The setup is a standard JeeNode with a LM35 (or the like).
A couple of days ago I measured the voltage on the batteries, still over 4V. So I guess it will keep going for another month or two.


byte payload[4];
int sensorPin = A1;
ISR (WDT_vect) { Sleepy::watchdogEvent(); }
void setup () {
rf12_initialize(2, RF12_868MHZ, 33);

void loop () {
while (!rf12_canSend())
int temp= analogRead(sensorPin);
temp= analogRead(sensorPin);
temp= analogRead(sensorPin);
temp= analogRead(sensorPin);
payload[0] = lowByte (temp);
payload[1] = highByte (temp);
rf12_sendStart(1, &payload, sizeof(payload) );

RE: ATTiny85 + RFM12B low-power issues - Added by jpadie about 7 years ago

Thanks ozzy
Looks very similar to the code I posted if you change the jeelib library for Martin’s.
can you confirm what chip you used this on?

RE: ATTiny85 + RFM12B low-power issues - Added by ozzy about 7 years ago

The standard JeeNode is the ATMega328

RE: ATTiny85 + RFM12B low-power issues - Added by jpadie about 7 years ago

Ah yes. Sorry I didn’t put 2+2 together reading your post.

So I guess in still left with some incompatibility with the tiny85 or more likely a foul up in the way in sending the chip to sleep.

Back to the breadboard I guess!

RE: ATTiny85 + RFM12B low-power issues - Added by jpadie about 7 years ago

Just to close this thread off for the time being, I tried a very simple sketch to test for power consumption. With all the RF12 and temperature measurement complexity taken out.

The code is below.

The consumption stayed static at about 1000uA. No flux beyond the ordinary variances you’d expect from a multimeter.

I had a thought that the chip might be fried (it was losing its programming). So I tried another attiny85 and, with the same code, am now getting 4mA in the ‘doing something’ mode and 4uA in the doing nothing mode.

the datasheet summary suggests that the power down mode should draw only 0.1uA at 1.8v. I’m running it at 3v.

in the graphs at the back, there is a suggestion that at 8MHz and 3v and 25C the active current is 2.8mA (Figure 22-3). I’m clocking at 4 so that sounds within range when turning a pin on.

power-down current is closer than I was ;-). Figure 22-12 suggests that with the watchdog enabled I should get 4uA at 3v. which is spot on.

and the battery life calculator at oregon embedded blithely tells me that the circuit will be powered for more than 10 years …

So it looks like a hardware fault this time. Which is annoying having debugged it for most of last night and much of today!

I will (over the weekend) reassemble the circuit with the sensor and the RFM12B and recheck the figures but hopefully this cracks it.

thanks for helping.

/* at-tiny power test */
#define __AVR_ATtiny_85__

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

int pinLed PIN_B1;

volatile boolean f_wdt = 1;

void setup(){
  setup_watchdog(8); // approximately 4 seconds sleep

void loop(){
    if (f_wdt==1) {  // wait for timed out watchdog / flag is set when a watchdog timeout occurs
      f_wdt=0;       // reset flag

      digitalWrite(pinLed,HIGH);  // let led blink

      pinMode(pinLed,INPUT); // set all used port to intput to save power
      pinMode(pinLed,OUTPUT); // set all ports into state before sleep

// set system into the sleep state 
// system wakes up when wtchdog is timed out
void system_sleep() {
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here

  sleep_mode();                        // System sleeps here

  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON

// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {

  byte bb;
  int ww;
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<

RE: ATTiny85 + RFM12B low-power issues - Added by yattapl almost 7 years ago


Can You post your wiring and rf12.cpp config for attiny85.
I’m trying to run simple code on attiny85v. I have runnig config on 2 atmegas 328p, but when i try to switch to attiny85 communication fails.