Let's Make Robots!

Sending IR codes in background

Allows you to send IR / remote codes in "background"

Hello guys.

After hours spent on the internet trying to understand how stuff works i came up with this piece of code. It simply sends out SONY remote codes, but the cool thing is that it does this in background, meaning that while your MCU sends out the code, you can carry on with your main loop, and you don't have to wait till the code has been sent to carry on with you sketch.

Even if this code sends out SONY codes, it can be easily adapted to send other protocols or even work at different frequencies and/or duty cycles (current is 50%). Here is the code...and please tell me if there's something you don't understand (i am willing to explain the different parts it's made of as soon as i have more time ;=) ).

Oh wait...i forgot to tell you how to use it. This is the function you're looking for: SonyIR(x, n). x is the code you want to send, while n is the number of bits it is made of. In the below example im sending it 3 times at a constant interval, because SONY "things" (tvs, recorders, etc..) only accept the code if repeated 3 times. You can use this code in combination with Ken Shirriff's library for receiving sony codes found at www.arcfn.com (got a lot of inspiration from his library).

 

 

#define TOPBIT 0x80000000
int timertx=0; int resettx=0; int bitstx=0; float elapsed=0; unsigned long datatx=0; boolean spacetx = false;

 

void setup(){

  //set PWM: FastPWM, OC2A as top, OC2B sets at bottom, clears on compare
  //COM2B1=1 sets PWM active on pin3, COM2B1=0 disables it
  //Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(11, OUTPUT);
  TCCR2A = _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS21);
  OCR2A = 49;
  OCR2B = 24;
  delay(500);

}

 

void loop(){

  delay(2500);
  elapsed=micros();
  SonyIR(0xa90, 12);
  elapsed=45-((micros()-elapsed)/1000);
  delay(elapsed);
  SonyIR(0xa90, 12);
  delay(elapsed);
  SonyIR(0xa90, 12);

 

}

 

boolean SonyIR(unsigned long data, int nbits) {

 

  if(bitstx == 0) { //if IDLE then transmit 
    timertx=0; //reset timer
    TIMSK2 |= _BV(TOIE2); //activate interrupt on overflow (TOV flag, triggers at OCR2A)
    resettx=96; //initial header pulse is 2400us long. 2400/25us ticks = 96
    spacetx = false;  
    datatx=data << (32 - (nbits + 1)); //unsigned long is 32 bits. data gets   shifted so that the leftmost (MSB) will be the first of the 32.
    TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output for transmission of header
    bitstx=nbits + 1; //bits left to transmit, including header
    return true;
  }
  else {
    return false;
  }

}

 

ISR(TIMER2_OVF_vect, ISR_NOBLOCK){

  //RESET_TIMER2;

 

  //TRANSMISSION

 

  if(bitstx != 0) {  //if we have got something to transmit

 

    timertx++; 
    if(timertx >= resettx) {  //if reset value is reached

 

      timertx=0;
      if(spacetx){  //it was a space that has just been sent thus a total "set" (bit + space) so..  
        spacetx = false; //we are not going to send a space now  
        bitstx = bitstx - 1;  //we got 1 less bit to send
        datatx <<= 1;  //shift it so MSB becomes 1st digit
        TCCR2A |= _BV(COM2B1);  //activate pin 3 PWM (ONE)
          if((datatx & TOPBIT)&&(bitstx != 0)) {  
            resettx = 48;
          }
          else if(!(datatx & TOPBIT)&&(bitstx != 0)){
            resettx = 24;
          }
          else{
            TCCR2A &= ~(_BV(COM2B1));  //deactivate pin 3 PWM (end of transmission, no more bits to send)
            TIMSK2 &= ~(_BV(TOIE2));   //deactivate interrupts on overflow
          }
      }
      else {  //we sent the bit, now we have to "send" the space
        spacetx = true;  //we are sending a space
        resettx = 24; //600us/25us = 24
        TCCR2A &= ~(_BV(COM2B1));
      }
    }
  }

}

EDIT:

Just wanted to add a quick explanation on how that ISR function works. Basically in the setup function, i've started a timer (timer2 to be precise) which is basically a value that increments very rapidily, starting from 0 of course. Now, when this value reaches the value of OCR2A (check out the setup function), an overflow occurs and the timing value goes back to zero, and so on until it reaches OCR2A again. Now, by setting TIMSK2 |= _BV(TOIE2) in the SonyIR function i'm basically setting to 1 the TOIE2 bit in the TIMSK2 register, and if you check the manual, this corresponds to activating the ISR (or interrupt service routine) on overflow. This means that when an overflow occurs (aka timing value reaches OCR2A in our case), the ISR function is called. Basically an ISR is a function that occurs every "x" microsecond/millisecond, depending on how you set OCR2A and other stuff (like prescaling factors).

In the above code, the ISR takes place every 25 microseconds. 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Small update: Just wanted to tell you guys that i have made the tone command ( not exactly the tone() function, but the result is the same) work with the above code, using the same timer. As before, there are no delays or no data missing when receiving IR and using tones.

I'm a little fuzzy on how this works. I understand that the timers run in the background, but in terms of what you do with the timers when it comes to sending a signal out, I don't quite understand that part or how that could be a background process. Unless you are just sending out a constant repeat signal but even then, you'd need some sort of code to handle the interrupt(which you explain that you are)....am I missing something? I probably am... :)

I'll just sum up how it works: 

Basically i have a function called SonyIR that sets everything up, activates interrupts, takes in the data to be transmitted, etc..and MOST importantly of all sets the resettx variable, which is the key of the whole "system".

Now basically every time the ISR gets exectued, there's a variable called timer which increments, and the ISR does nothing else UNTIL this timer value equals the reset value. EG: Say i want to send a 2400ms long pulse (the headers) and i know the ISR gets executed every 25ms, then i let the resettx value be = 2400/25 = 96, so the timer will count up to 96. 

Now once 96 or whatever other value is reached, there's a variable which is spacetx which is boolean, and tells us whether it was a low signal we just sent or a high signal (not to be confused with 1s and 0s in the protocol). Alright so we just sent the header which was 96 "ticks" long, and spacetx is false (cause we sent a high signal), that means that now we gotta sent a space because it is required by the sony protocol. The spacing of a space is 600ms, which in ticks is 24, so again we set the resettx variable to 24, and wait until the timertx variable gets up to that value. Then again, space tx is gonna be true, meaning that now we'll have to send a high signal, and here starts the real communication (this one was only to send the header).

Data comm. works exactly as above, with the spacetx / resettx trick, only extra thing is the shifting and TOPBIT constant we use to send data, but this is very straight forward. Basically we shift the data so that the bit we have to send matches the position of the 1 in the topbit constant which is: B 10000000000000000000000000000000. Knowing the total bits of the data to be transmitted this becomes quite easy. Now we just AND (bitwise and) the topbit and the data (now shifted to match the the topbit) so that we get out a 1 or 0 bit from it, and we just send it with the above mecahnism.

Hope it makes things a bit (:D) more clear! But keep on asking if there's something you want to know.

Looks wicked, Tuna. Please tag it Arduino, IR and stuff, so it will be found in Google searches better, help more people :)

Oops, forgot to do that, thanks for reminding!

Thanks Captain Tuna. This could be really helpful. How does this affect PWM, servo and tone commands?

I have made one robot recently that is controlled via a universal remote set up for Sony codes. I originally tried using an interupt pin to detect the signal but had some trouble getting it to work well.

I ended up using the pulsin command instead of the delay command with the timeout value set for the same period. This allowed the Arduino to monitor the IR receiver when it would otherwise sit there twiddling it's thumbs. This method works great if your robot has a lot of delays greater than 2400uS as that is the width of the start bit.

I'm not sure about servos, haven't tried them yet. But i can tell you this: my code uses timer2, and i've been using it with the arduino waveshield library which uses timer1. This last software has to play audio at 44Khz so it calls an interrupt at a frequency of 44Khz and so far i've had no problems and even no delays at ALL when combining the two together. Music played normally and IR got sent normally as they would "on their own".

Now...PWM on the atmega uses timer0, and so does tone i guess (it is written that tone may interfere with PWM). I'm not sure what the servo command uses, because i can't see it on the arduino.cc reference. My guess is: if it doesn't use timer2 it's gonna work fine, this after seeing how the waveshield library and my code hang along well together.

One last note: it can be changed to work with timer1 with ease actually :=)

Nice tip, Captain Tuna. The standard Arduino servo lib uses timer1, so servos shouldn't be affected. But the tone lib uses timer2.

Thanks! :=)

Unfortunately you're right about the tone() function :( . But i think it's not hard to solve. You could "bit-bang" (i know it's not the right word, but i hope it gives the idea) it. What i mean is have the pin the speaker is connected to go on and off at certain frequencies using the ISR already used for SonyIR.

EDIT: most simple thing ever! Just checked the tone.cpp file, this is how the ISR works:

 

ISR(TIMER1_COMPA_vect)

{

  // toggle the pin

  *timer1_pin_port ^= timer1_pin_mask;

}

Of course the library lets you choose the tone's frequency so it varies the frequency of the ISR being called, while we have a fixed frequency, but you know better than me how easy it is to adapt it :=) ...only thing is you can't have a higher frequency than the one used by the IR.

 

Yes, it would be great to have an Arduino library for stuff like:

  • play Tones,
  • send IR codes,
  • send UART,
  • more PWM,
  • send Manchester Code
  • or whatever,

Only one timer for everything, output on every Arduino pin. You can divide every signal above into a series of pulses with a high time and a low time. A table for each output with the timer values would be needed. The timer interrupt should be fast enough to poll through all tables and set/clear the outputs according to the timer values.Similar to the servo lib where you can add and replace a servo and connect it to any digital pin you want.

Unfortunately these lib doesn't exist (at the moment), but it's worth to think about it.