Infrared Detector WIth 1Mhz Attiny85

Hi All,

Has anyone been able to get the attiny85 running at 1Mhz working for infrared detecting? I can get my attiny85 working at 8Mhz, but no luck so far at 1Mhz. The reason why I would like 1Mhz to work is because I would like for the power to be as low as possible. The way the main code goes is this:

  1. turn on and listen ir for .5 seconds
  2. sleep for 4 seconds, use wdt to wake up.

During (1) the current consumption @8Mhz, 3.3v is 3.75 mA, but it is about about .8mA @1Mhz. The problem is that when I use 1Mhz, I can’t seem to read any IR codes at all. During (2) I measure 4.5 uA

Here is the code I use to capture the IR

int listenForIR(uint16_t *pulses) {
  
  int currentpulse = 0;
  uint16_t highpulse, lowpulse;  // temporary storage timin
  
  while (true) {
    highpulse = lowpulse = 0; // start out with no pulse length
 
    pulses[2*currentpulse] = 0;
    pulses[2*currentpulse+1] = 0;
    
    while (IRpin_PIN & (1<<IRpin)) {
       
      delay2us();

      
       // pin is still HIGH
 
       // count off another few microseconds
       highpulse++;
       
       // If the pulse is too long, we 'timed out' - either nothing
       // was received or the code is finished, so print what
       // we've grabbed so far, and then reset
       //if ((highpulse >= MAXPULSE) && (currentpulse != 0)) {

       // added RETIRES here 
       if ((highpulse >= MAXPULSE)) {
         return currentpulse;
       }
    }
    // we didn't time out so lets stash the reading
    pulses[2*currentpulse] = highpulse;
 
    // same as above
    while (! (IRpin_PIN & (1<<IRpin))) {
      
       // pin is still LOW
       lowpulse++;
       
       delay2us();
       //if ((lowpulse >= MAXPULSE)  && (currentpulse != 0)) {
       if ((lowpulse >= MAXPULSE)) {
           return currentpulse;
       }
    }
    pulses[2*currentpulse+1] = lowpulse;
 
    // we read one high-low pulse successfully, continue!
    // return the max if the code is very fast and going on for a long time (this should not happen)
    currentpulse++;
    if( currentpulse == MAXPULSEPAIRS)
       return MAXPULSEPAIRS; 

  }
}

unsigned long listenForIRCode() { 
   int numPulses;
   unsigned long retVal = 0;
   uint16_t pulses[MAXPULSEPAIRS*2]; 
   
   numPulses =  listenForIR(pulses);
   
   if(numPulses >=32) { 
      for(int i=0; i< 32; i++) { 
         retVal = retVal <<1;
         if(pulses[2*i]*RESOLUTION >= 350) { 
            retVal|=1;
         }
      }
   }
   return retVal;
  
}

This is pretty much standard from some other places. RESOLUTION is set at 2 uS and MAXPULSE is 5000. The main loop retries for 50 or so tries before it gives up. Like so:

unsigned long IRCode;
void loop(void) {
  
   // go to sleep here
   goToSleep();
  
   
   // turn on the IR detector 
   digitalWrite(IRVDDPIN, HIGH);
   
   // listen for IR command; 
   for(int i=0; i < RETRIES; i++) {
      IRCode = 0; 
      IRCode = listenForIRCode();
      if(IRCode > 0 ) { 
         //Serial.print("Code: ");
         //Serial.println(IRCode, HEX);
      
         if(IRCode == OFF) { 
            fireOff();
            break;
         } else if(IRCode == ON) {   
            fireOn();
            break;
         }
      }
   }
   digitalWrite(IRVDDPIN, LOW);   
}

The functions fireOn/Off are going to ultimately control a fireplace, but are not quite implemented today. They just turn on/off an LED.

The function delay2us() is fine tuned to for the resolution using nop like so

// 8Mhz attiny shown, 1Mhz only has 2 nop
inline void delay2us() {
   #if defined(__AVR_ATtiny85__)
      // 16 nop x 1/8 us = 2 us
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      
   #else
      // 32 nop x 1/16 us = 2 us
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      __asm__ ("nop\n\t" "nop\n\t" "nop\n\t" "nop\n\t");
      //delayMicroseconds(timeout);
   #endif
       
}

I am thinking that perhaps the direct port read @ 1Mhz is still too slow, but have not really proven this theory.

    while (IRpin_PIN & (1<<IRpin)) { ...

Can someone point me in the right direction? Any help will be greatly appreciated.

Thanks,
Jose

at 1Mhz every instruction is going to take a minimum of 1uS so attempting to do delays of 2uS is tricky

And.. Its quite possible that the rest of the code (apart from the delay 2uS), won't get back around to the top of the while loop fast enough for you to stop loosing bits

You could try removing the delay2Us function entirely, or just inline one nop in its place.

Apart from that, you could look at attempting to optimize the code, to possibly speed it up a tiny bit e.g. you could try doing things like

    currentpulse++;
    if( currentpulse == MAXPULSEPAIRS)
       return MAXPULSEPAIRS;

to

    if( ++currentpulse == MAXPULSEPAIRS)
       return MAXPULSEPAIRS;

but its debatable whether this sort of thing would generate faster code even though in theory its less individual actions, as the compilor is incredibly smart in the first place.

You'd probably need to look at a diss-assembly to determine if it was better, or do some other speed tests in a stand alone program.

You could also look at the optimization settings in platforms.txt file for the IDE and perhaps see if there is a compiler option to optimize for speed rather than size (I've no idea what the default is for AVR)

Or re-code the time critical bits in assembly.

But overall, it could be that its just not possible to write even assembly code to do this on a 1mhz clock

Run the processor as fast as possible. Sleep as much as possible. Use pin-change interrupt to wake when the IR pin changes state.

Hi Everyone,

Thank you for the suggestions. I think running the processor as fast as possible doesn't make sense to me. The power consumption is so much higher than even sleeping as much as possible will not make the setup useful. I have found that sleeping more than 4s makes the whole thing seem very unresponsive. As far as using the detector for wake up...I don't think that works either. If you read through the code, you'll see that I have vdd connected to one of the output pin of the attiny. This is to control the power consumption on the irdetector. Unless there is something clever I am missing, having the detector connected all the time will yield worse power consumption since its active power is about .3mA which would burn all the time. This is actually more than running at 8Mhz and waking up the processor and the ir detector periodically.

I was hoping I did not have to resort to too much in assembly, but I guess I'll look into it. I am surprised that 1us per instruction is not fast enough since every protocol I have seen sends ir codes that are hundreds of us per bit, so even if I miss a few I would have thought I should have been OK with the granularity I have in my "listenForIRCode" routine.

Any more ideas are welcome.

Regards, Jose

The reason for running at a faster clock speed is that you only need to be awake for a lot less time.

1MHz vs 8Mhz ==> awake for 1/8th of the time. So if you only use 1/4 of the current at 1MHz vs 8Mhz, it should make sense to run at 8MHz. (Dont forget you can also do short sleeps during the 'active' period. As in when active you dont have to stay awake all of that time). In the scenarios you painted I would run at 8MHz and look for additional shorter sleep oportunities during an 'active' period.

The suggestion about PCINT would result in an optimum solution.

How are you measuring your current? It can be very tricky at low levels.

I think you can change the clock prescaler while it's running - take a look at pages 31-33 of the datasheet. That would let you get 2mhz or 4mhz, which may be fast enough even if you can't get it to work at 1.

Hi All,

Thanks again for all the suggestions. I still don't see how the PCINT will work. I will have to power the detector on all the time and this will be higher current consumption than the sleep/power detector & check ir loop. What am I missing there? Here is my calculation: every 4.5 seconds, I wake up the attiny85 for .5 seconds to check for IR, so:

.5/4.5 * 3.7ma + 4/4.5 * 4.5ua ~= 0.41ma

At 3.3v the IR detector is 0.45ma typical an the attiny85 is 2.8 ma, which equals 3.25ma on paper. To put it in perspective, at 1Mhz the attiny current consumption is around 0.75ma, so the computation becomes:

.5/4.5 * (0.75 + .45) + 4/4.5 * 4.5ua ~= 0.133ma

So the difference is very significant (3x power)

In order to measure the current, I connected my multi meter in series to the attiny. I measure close to what I get from the datasheets at 3.3v, 8Mhz so I think the measurement is correct.

I did not think of clocking 8Mhz down to 2Mhz, or 4Mhz though so I am going to try that. From page 173 of the datasheet, at 2Mhz it looks like I might be very close to 1ma consumption so that might be still pretty good!

Again, thanks!

Regards, Jose

Good point, if you power the IR Receiver from the AVR pin then you can save that current while sleeping.

However, if you are sleeping for say 4 secs and the IR receiver is OFF, you are likely to miss the IR signals.

It could become a good game trying to guess the which half second the AVR is listening and them making sure you get your IR signal to fire completely within that small 0.5sec time slot.

Unless you are prepared to press an unused button on the remote until the AVR wakes up or just keep the button pressed for 5 secs+

It might be a good idea to post the intended function/features of the project, other than the low power bit.

(FYI: I have an IR detector module that I use for testing & run it on 2xAA batteries which lasts for 2 months 24x7. No MCU just an IR receiver & red indicator LED)

Hi AnalysIR,

As I alluded to before, the intended project is to control a fire place. I currently have an RF fireplace setup where the fire place controller controls a receiver at the fireplace. It has 3 buttons (on/off/set) that control the fire place. The transmitter is powered with 2 cr2032 batteries. I have been able to reverse engineer the buttons and could easily take control over the controller with the attiny. For convenience, I want to add the ability to use my TV remote (actually it is a harmony…) to be able to turn the fire place on and off. And that’s it…

But, since everything is a battery operated rig, I need to be able to keep the “IR snifffer” as low power as possible. My first thought was in fact to use the ir receiver as the wakup, but simple math is that the ir receiver on its own will drain the battery like this:

200mAh x 2 / 0.45 ma (typ.) = 888 hours = 37 days. And that does not take into account any of the electronics in the fire place controller itself.

I agree that the 4.5 s loop make seeing the signal a “game” but I can also program the harmony remote to resend the signal many times. Unfortunately holding down the remote for 5 seconds in this setup does not work because the ir code is not a “resend” type, but a “repeat” type. Obviously, I can get my hands on a resend type remote code to make the problem better. Holding down a button for at most 5 seconds once in a while is not so bad in the grand scheme of the fire place, but I agree that it would not be good if you were controlling a tv.

If I could get my hands on a < 0.1ma ir receiver things would be great… I could use the receiver for PCINT wakeup, but I have not been able to find such a thing. I can also lower the voltage from 3V to maybe 2.5 by using a fast low vt diode, but even then the power consumption goes down to 0.27mA, which is still higher than I could get from the on/off loop assuming 1Mhz (or 2Mhz). If there is no way I can read codes unless everything is at 8Mhz, then the ir wakeup is probably the way to go, but perhaps just slightly so and probably at the expense of a diode.

Maybe there is something I am missing in the IR wakeup version. Is there a way to lower the power consumption of the ir receiver (TSOP38238) to lower than .1ma?

Thanks,
Jose

OK, that makes a lot more sense.

I think you best bet is as you said.

Get any learning remote and programme it with a repeated signal with the 'repeat' portion. You just need a sufficient gap. There is a good chance it is an NEC type signal which will be less than 100mS long with at least a 40ms gap between repeats. So enough room to get 3 signals into 500ms.

You should be able to create 6 second bursts of 'complete' repeated signals using IRremote in RAW mode for testing purposes and then learn that into your learning remote on any unused button/function.

Note: Now that you are creating the receiver setup, you can work with any IR signal you choose, as its all under your control. If you design your own signal, then you can run at any speed. You may want to look into calibrating the onboard oscillator of the AVR (see tinytuner).

Hi All,

I was able to bring down the frequency to 4Mhz using DrAzzy's suggestion and it still worked. This brought down the current consumption to around 2.1ma so it was a fairly substantial savings. Still not below 1ma, but better than 3.7. Pretty cool that I measured almost exactly what the datasheets said. I read 4Mhz @ 3.3v to give me around 1.8ma, and the TSOP38238 claims .35ma. So add they add up to 2.15. In reality I will get a little lower, since the batteries are 3v, not 3.3v

I am still all ears on how to get it to go lower. Still salivating for the 0.6ma at 1Mhz for the avr...

So far the 4.5s loop seems to be working. I can set my harmony remote to wait 2 seconds or so per retry of the command, and send the command as many times as I want to make up for the loop.

I'm thinking about playing other tricks where if the avr has not seen an ir command after a few days it turns off completely and the user has to use the buttons at the controller to wake it up (thus use a PCINT), but I am on the fence about it based on usability. It might seem like the ir stopped working, where in reality it is just sleeping.

Regards, Jose

Are you using every power saving trick the chip supports? (pg 35-37 of datasheet, if you're not)

You can also attack it from the other direction, and just put bigger batteries on it ;-) Unless there's only a small space for it, you could ditch the low capacity, high price coin cells, and just power it with inexpensive AA's, and it would last a lot longer.

I don't understand why you are using IR when your fireplace is RF controlled.

Have you considered looking to see what freq band the RF is on and possibly reverse engineer the transmitter or receiver

433Mhz OOK / ASK transmitters and receivers are cheap and there are several libraries which work with the standard protocols used by most cheap transmitters

@DrAzzy: Yes I think so. I have everything powerd off, ADC, Timers, etc. except for the WDT to do the 4s sleep loop. This is why I implement the delay2us function with nops. When in sleep it goes to deep sleep (powerdown) and I measure 4.5uA. Unfortunately the transmitter does not have too much space for batteries that are bigger, like aaa or anything like that. I looked into higher capacity coin cell batteries. There might be something there, but I have not found coin cells that are higher capacity at the same footprint of the cr2032.

@rogerClark: The point is that while I am watching tv in my living room, my wife or I can turn on/off the fire place directly from the remote we are using. The harmony remote version I have does not support the rf code to the receiver at the fire place (and probably never will). I did look into reverse engineering the rf transmission into the fire place receiver, but even if I knew the code, something has to understand that I want to turn on the fireplace from the remote. It defeats the purpose if I buy (or make) another remote for this. I can easily understand an ir code at the avr (there are plenty of very well documented examples) so it is easy to place the avr at the transmitter and then twiddle the on/off buttons at the transmitter from the avr. The challenge here is the power consumption.

Regards, Jose

Jose

How is the current RF receiver in the fire powered ?

Can't you just tap off that power to run your IR detector system?

Some crazy experiments...

Fig 3 shows the output responding in 600µs. Try using an Attiny output pin to drive pin 2 (GND) on the receiver. Change the pin mode of this pin to INPUT_PULLUP for 1 sec intervals and OUTPUT low for 5ms intervals (99% power savings?). If pin 2 goes low, keep pin 1 low until pin 2 returns high.

Unused pins on Attiny could be pulled high with INPUT_PULLUP to possibly save a bit more current.

@dlloyd: I think I see what you mean, but how long do I wait to see if pin 2 goes low? I think I have the same setup, except I turn on the vdd pin (instead of gnd to 0) and wait half a second for a code, then go back to sleep for 4s. How is your way better?

@rogerClark: yes, the receiver is light switch in the wall. The transmitter is out in the open so you can add the ir detector and 8 pin attiny85 to the transmitter box. Why do you ask?

I'm not sure why this device has to run on batteries, when ultimately the result is you are controlling something which must have electrical power to it.

So personally I'd take the fire apart and drill a hole for the IR receiver, then look at the power wires to its RF receiver and tap into them

Even if all you have is mains, just buy one of those cheap USB chargers and take it apart, they just have little PCB's inside them and you can solder onto the mains input wires and solder on some wires to where the USB connector is.

I had to do something similar for my garage door controller, the RF receiver board in the door motor thing packed up, so I now have an externally mounted Arduino with a RF receiver that decodes a new key fob remote that I bought from eBay, and I ran 2 wires to the door motor to simulate what the old remote input used to do. I power it from a USB PSU

@dlloyd: I think I see what you mean, but how long do I wait to see if pin 2 goes low? I think I have the same setup, except I turn on the vdd pin (instead of gnd to 0) and wait half a second for a code, then go back to sleep for 4s. How is your way better?

Pin 3 is +Vs, pin 2 is pulled up to Vs, so disconnecting the GND with INPUT_PULLUP should stop any current draw as all 3 pins are high or high impedance.

If the GND pin is left low, there would still be some leakage to GND through the other pins as you have measured.

I think you could squeeze a bit more with the timing as you have 4,000,000µs sleep, 500,000µs on ... could change to say 100,000µs on as the output only takes 600µs to respond.

Hi rogerClark,

No sorry I was not clear. The receiver is also running on batteries but it is inside of the wall behind what used to be the switch that turns on the fire place. The switch was never powered, just connects two low voltage wires together to get the fire started. The receiver takes over the job of the old switch. Drilling a hole in the wall does not pass the wife test plus there is no way that could work based on how hot the fireplace itself gets. Also, having the avr at the transmitter is better. What I have not told you is that the transmitter has a thermostat in it and you can actually set it to turn the fire place on and off based on the room temperature. Having the avr at the transmitter allows for in the future controlling the temperature setting, etc if I get more inclined. The receiver is just a dumb rf on/off.

But I think this is getting off-topic...

Regards, Jose