Pages: [1] 2   Go Down
Author Topic: IR Library Receiver deactivation issue (Ken Shirrif IR)  (Read 2010 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

hey there, I'm using Ken Shirrif's famous IR interfacing library and its working great.
(If you are not familiar with it here is the website) http://www.arcfn.com/2009/08/multi-protocol-infrared-remote-library.html

This is a question to anyone who has previously used it before as i'm not seeming to get any response from the author or on the blog.

I'm basically trying to make two arduino boards communicate over IR and create an IR handshake.

So:
1. Arduino A sends.
2. Ardunio B receives, then replies.
3. Ardunio A receives.

The challenge i'm having is when I start the "irrecv.enableIRIn();" in setup and then I try:

irsend.sendSony(code, 12);

It's basically sending rubbish over the LED - except if I remove "irrecv.enableIRIn();" then it works perfectly.

What i'm basically looking for - is there a way to completely remove the affect of "irrecv.enableIRIn();" such as a "irrecv.disableIRIn();" so I can switch it off and get back to sending mode.

I have tried "irsend.enableIROut(38);" but it doesn't do anything to solve the issue.

I believe it may be something in the C coding of the actual library or something I don't know how to call to reverse its effects ( as I believe the sending and receiving functions use the same timer)

Thanks in advance to anyone who can help!
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The library can not send and recieve IR codes at the same time.

So what you want is:
Arduino A sends the message and then call "irrecv.enableIRIn()"
Arduino B needs to call "irrecv.enableIRIn()" and then stop the reciever after the message has arrived and then send the reply.

The problem is, I don't know if the library has a "irrecv.disableIRIn()" command (I am pretty sure it doesn't). So I don't know how to turn the reciever off. Possibly worth emailing Ken.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Wow I didn't expect such a fast reply - Targettio you are 100% correct that is EXACTLY the situation I'm in. I've emailed Ken a few days ago but had no response yet - I wonder if I posted the EnableIRin code from the c code if with some help here If we could basically undo all the changes it is making and allocate the resources back for sending - so effectively make our own disabler?
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Here is the code with the IRenabler function, are there are great coders out there who know how to remove the effects of this enableIRin function?

Code:
// initialization
void IRrecv::enableIRIn() {
  // setup pulse clock timer interrupt
  TCCR2A = 0;  // normal mode

  //Prescale /8 (16M/8 = 0.5 microseconds per tick)
  // Therefore, the timer interval can range from 0.5 to 128 microseconds
  // depending on the reset value (255 to 0)
  cbi(TCCR2B,CS22);
  sbi(TCCR2B,CS21);
  cbi(TCCR2B,CS20);

  //Timer2 Overflow Interrupt Enable
  sbi(TIMSK2,TOIE2);

  RESET_TIMER2;

  sei();  // enable interrupts

  // initialize state machine variables
  irparams.rcvstate = STATE_IDLE;
  irparams.rawlen = 0;


  // set pin modes
  pinMode(irparams.recvpin, INPUT);
}


Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

http://arcfn.com/files/IRremote.zip

The libraries link for more information
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Unfortuantly modifying the library is beyond my skill but hopefully someone can help. It's something I would be interested in as I want to make an ir repeater which would require receiving and. then sending.
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have had a little look into this, and 90% of it is going well over my head. But I think I know why it isn't possible to send and recieve at the same time.

Arudino has 3 timers (timer0, timer1 and timer2), each of which controls 2 PWM pins (to get 6 PWM pins). Ken has used pins 3 and 11 which are both on timer2. So the reciver and transmitter functions are interfering with each other (as they are both changing settings within timer2).

There maybe a way to write a 'turn off reciever' and 'turn off transmitter' functions that re-defaults the timers settings, but that is beyond me.

One other solution is to change either the reciever or transmitter to another pwm pin and therefore a different timer. This would require re-writing the library as it is currently all written around timer2. But as far as I understand, each of the 3 timers on the chip behave slightly differently, so it might not work on another timer.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Great work mate!

I've found this from Ken's Blog
Quote
"Ken Shirriff said...
As some people mentioned above, the library uses timer2 so it will conflict with other software that uses timer2. Timer0 is used by the Arduino framework for millis() and delay(), so the library can't easily use timer0. Timer1 is a 16-bit timer, so it's not directly compatible with timer2. I'm looking into modifying the library to use timer1.

January 18, 2010 9:57 AM"


Heres another interesting post
Quote
Ken Shirriff said...
TiStyle: running 30-50 independent receivers sounds like a lot! It sounds like an interesting project; do you want to give more details?
To outline what you'd need to do to support multiple independent IR receivers...
The code currently uses a single irparams global structure to keep track of the received code. The timer 2 interrupt code checks the status of the input pin and updates the irparams structure.
You'd need a separate irparams structure for each pin. You could have an array of them, or maybe have irparams part of the IRrecv class. The array would make it easier for the interrupt handler, but putting it in the class would be architecturally cleaner. Then the interrupt code would need to loop through all of them, updating each one according to the input.
Then the decoding routines would need to process a particular irparams structure, rather than the global one.
I'm not sure how many inputs you could process before you'd run out of time in the interrupt handler.?
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Maybe we should try something along the lines of this?

Quote
CaptainTuna said...
Hey guys. I modified the library a little, so that it can send IR in the background. Still have to test it out. Here is what i've done so far:

-----------------------------------

void IRsend::sendSony(unsigned long data, int nbits) {

if(irparams.bitstx == 0) { //if IDLE then transmit
enableIROut(40);
irparams.timertx=0; //reset timer
irparams.resettx=48; //initial header pulse is 2400us long. 2400/50us ticks =

48
irparams.bitstx=nbits + 1; //bits left to transmit, including header
irparams.datatx=data << (32 - bitstx); //unsigned long is 32 bits. data gets

shifted so that the leftmost (MSB) will be the first of the 32.
irparams.spacetx = false;
TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output for transmission of header

}
}

-----------------------------------

ISR(TIMER2_OVF_vect)
{
RESET_TIMER2;

//TRANSMISSION

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

irparams.timertx++;
if(irparams.timertx == irparams.resettx) { //if reset value is reached
if(irparams.spacetx){ //it was a space that has just been sent thus a

total "set" (bit + space) so..
irparams.spacetx = false; //we are not going to send a space now
irparams.bitstx = irparams.bitstx - 1; //we got 1 less bit to send
irparams.datatx <<= 1; //shift it so MSB becomes 1st digit
if(irparams.datatx & TOPBIT) {
irparams.resettx = 24;
TCCR2A |= _BV(COM2B1); //activate pin 3 PWM (MARK)
}
else {
irparams.resettx = 12;
TCCR2A &= ~(_BV(COM2B1)); //disable pin 3 PWM (SPACE)
}
}
else { //we sent the bit, now we have to "send" the space
irparams.spacetx = true; //we are sending a space
irparams.resettx = 12; //600us/50us = 12
TCCR2A &= ~(_BV(COM2B1));
}
}
}

....continues with the rest of the ISR routine.

-----------------------------------
this goes in the IRremoteInt.h

// information for the interrupt handler
typedef struct {
uint8_t recvpin; // pin for IR data from detector
uint8_t rcvstate; // state machine
uint8_t blinkflag; // TRUE to enable blinking of pin 13 on IR processing
unsigned int timer; // state timer, counts 50uS ticks.
unsigned int timertx; // timer for counting 50us ticks (Tx)
unsigned int resettx; // reset value for above timer (Tx)
unsigned int bitstx; // number of bits left to send (Tx)
unsigned long datatx; // data to be transmitted (Tx)
boolean spacetx // true if space has been sent (Tx)
unsigned int rawbuf[RAWBUF]; // raw data
uint8_t rawlen; // counter of entries in rawbuf
}
irparams_t;

-----------------------------------


July 5, 2010 10:18 AM


Code:
ben said...
Awesome code, Ken. Thanks!
I thought I would add my twist: I need to send IR to 2 different applicances (TV and cable set top box). So I need to send IR out on pin 3 (as you do) and another pin. This means of course using another timer (Timer 1 for me).
My result is below for pin 9. It works, but it's hacked together and I would appreciate anyone improving it!
Ben

void IRsend::enableIROutNine(int khz) {
TIMSK1 &= ~_BV(TOIE1); // clears the timer (Timer1) overflow interrupt enable bit

pinMode(9, OUTPUT); // New output pin nine
digitalWrite(9, LOW); // When not sending PWM, we want it low

// Alternative, hacked together from the arduino eg (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1199535681)
TCCR1A = _BV(WGM13) | _BV(WGM11) | _BV(WGM10);
TCCR1A = _BV(COM1A0); //start the timer
TCCR1A &= ~(_BV(COM1A0)); // Disable pin 9 PWM output
TCCR1B = ( _BV(WGM12) | 1); //set the predivide, in this case 1 = 001 means no prescaling

OCR1A = SYSCLOCK / 2 / khz / 1000;
OCR1B = OCR1A / 3; // 33% duty cycle
}


void IRsend::markNine(int time) {
// Sends an IR mark for the specified number of microseconds.
// The mark output is modulated at the PWM frequency.
//TCCR2A |= _BV(COM2B1); // Enable pin 3 PWM output
TCCR1A |= _BV(COM1A0); // Enable pin 9 PWM output
delayMicroseconds(time);
}

/* Leave pin off for time (given in microseconds) */
void IRsend::spaceNine(int time) {
// Sends an IR space for the specified number of microseconds.
// A space is no output, so the PWM output is disabled.
//TCCR2A &= ~(_BV(COM2B1)); // Disable pin 3 PWM output
TCCR1A &= ~(_BV(COM1A0)); // Disable pin 9 PWM output
delayMicroseconds(time);
}
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

sorry about the intense amount of posts:
but there is a word limit on the amount of code we can post.

I THINK i've found it!!
Code:
#define TOPBIT 0x80000000
int timertx=0;
int resettx=0;
int bitstx=0;
int 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(COM2A0) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS21);
  OCR2A = 49;
  OCR2B = 24;
  TIMSK2 |= _BV(TOIE2);
  delay(500);
}

void loop(){
  delay(1000);
  SonyIR(0xa90, 12);
   
}

boolean SonyIR(unsigned long data, int nbits) {
 
  if(bitstx == 0) { //if IDLE then transmit
    timertx=0; //reset timer
    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){
 
  //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 (ONE)
          }
      }
      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));
      }
    }
  }
}

This is from: http://www.laserforums.com/forum/showthread.php/4793-McSTUFF-s-Attempt-at-a-Simple-Arduino-Laser-Tag

Try it out - i will probably this weekend when I get some time - flat out this week
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I was looking at the lazer tag example you found and was confused as that guy hasn't made any reall changes so I looked at the examples  and I am not sure how I missed this, but it seems you can send and recieve in the same sketch.

Checkout IRrecord.pde, it is an example within the library and basically it records an IR code and when you press a button it replays that code.

Quote
The IRrecv class performs the decoding, and is initialized with enableIRIn(). The decode() method is called to see if a code has been received; if so, it returns a nonzero value and puts the results into the decode_results structure. (For details of this structure, see the examples/IRrecvDump sketch.) Once a code has been decoded, the resume() method must be called to resume receiving codes. Note that decode() does not block; the sketch can perform other operations while waiting for a code because the codes are received by an interrupt routine.
Re-reading that, IRrecv basically turns itself off after receiveing a code, and will only restart when Resume or enableIRIn is called.

So going back to your original problem:
* On Arduino A you need send the message and then enable the IR in.
* On Arduino B you need to enable the IR in at the begining of the sketch but without calling resume. Then send the reply.

Bare in mindthat the code isn't blocking so you need to include some short delays after sending a message before restarting the reciever
« Last Edit: March 21, 2011, 09:50:56 am by Targettio » Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I hear you and my code already performs that same function.

Arduino A sends a code- then enablesIRin to hear If a code has been sent back. Then it loops and sends again - it's that when it loops the code being sent now is garbled and non decipherable by Arduino B. I understand the EnableIRin must always be called after irsend but I think the fact they both use the same timer causes some serious issues. I might try one of these methods ASAP
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Targettio did this solve your problem? I still haven't had an opportunity to test it as I've been focused on other projects
Logged

Offline Offline
Jr. Member
**
Karma: 1
Posts: 95
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I gave up on my IR project for a long time, but decided to have another look yesterday and found:

http://arduino.cc/forum/index.php/topic,50728.msg366980.html#msg366980

It seems, for some reason you can't do what you want (or what I want), at least not with the newest version of the library.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 24
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That's a real annoying pain aye!

I've decided to change the direction of how I wanted to solve this problem now, which isn't ideal, but basically it involves only one way communication which the library supports to reduce complexity

Least then I'll be sure it works! And the hardware requirement can also be halved.

Thanks for your research mate - to those who read this in the future it's still questionable if it's possible, perhaps the answer lies in the previously referenced links.
Logged

Pages: [1] 2   Go Up
Jump to: