1

I am an Arduino (and C/C++) newbie so apologies if I am missing something obvious.

I am using IRremote (github link) as my IR library.

I can get the simple send and receive demos working individually.

However, I cannot get send AND receive working in the same Arduino at the same time.

I do not wish to do something like the "IRrecord" example but what I do want to do is have Arduino send and receive at all times and then trigger alarm/LED once the "beam" is broken.

I am using a TSOP 22 series as my IR receiver. I have tried to use protothreads but no luck. Any tips appreciated.

My current code snippet:

#include <IRremote.h>

int RECV_PIN = 11;

IRrecv irrecv(RECV_PIN);
IRsend irsend;

decode_results results;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
  Serial.println("IR Receiver... Started");
}

void loop() {
  //single threaded nature seems to break below 

  // i want to start receiver here
  if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
  }
  //delay(5000);

  //trying to send here
  for (int i = 0; i < 3; i++) {
    irsend.sendSony(0xa90, 12);
    Serial.println("Sent Sony......");
    delay(40);
  }
  //delay(5000); //5 second delay between each signal burst
}

3 Answers 3

5

I managed to do this using solution from Ken Sheriff's Blog. Ken provides a simple example and documents it perfectly. Due to the nature of my IR Receiver (rejecting continuous beam) I used the sample code provided by Stephane Deniaud in the comments section. Thanks to both...

#include <IRremote.h>

#define PIN_DETECT 2
IRsend irsend;

void setup()
{ 
    pinMode(PIN_DETECT, INPUT); 

    Serial.begin(115200);
    Serial.println("READY");

    attachInterrupt(digitalPinToInterrupt(PIN_DETECT), checkIRBeamBreak, RISING);

    // Note : the IR emitter is on PIN #3
    irsend.enableIROut(38);
    IREmitterOn();
}

// Use this function instead of delay() as delay() does not work in interrupt callbacks
void pause(int ms) { 
    // We need a loop as the largest value for delayMicroseconds that will produce an accurate delay is 16383
    for (int i = 0; i < ms; i++) {
        delayMicroseconds(1000);
    }
}

void IREmitterOff() {
    irsend.space(0);
    pause(60); // 60 ms is OK for my TSOP but should be tuned
}

void IREmitterOn() {
    irsend.mark(0);
    pause(10);
}

void switchOffOnIREmitter() {
    IREmitterOff();
    IREmitterOn();
}

void checkIRBeamBreak() { 
    int val = digitalRead(PIN_DETECT);
    // LOW : no beam break
    // HIGH : beam break
    if (val == LOW)
        return;

    detachInterrupt(digitalPinToInterrupt(PIN_DETECT));
    switchOffOnIREmitter();
    if (digitalRead(PIN_DETECT) == HIGH) {
        Serial.println("IR BEAM BREAK !!!!"); 
    }
    attachInterrupt(digitalPinToInterrupt(PIN_DETECT), checkIRBeamBreak, RISING);
}


void loop() {

}
2

based on the concept of user1843591 answer, i researched some more and found the example of Nick Gammon in the arduino forum.

after some tweaks i came up with the following: (based on both) it uses Timer1 to generate a 38kHz signal and Timer2 to generate a 760Hz cycle that switches the 38kHz signal on and off. this way the recommend pulse-cycles and gap times for the Receiver (for me TSOP4438) is preserve. it also implements global flags to decouple the ISRs & mainloop.

(both files should be in a folder IR_TSOP4438_light_barrier)

IR_TSOP4438_light_barrier.ino

// simple light barrier test

extern volatile bool light_barrier_broken;

void setup(){ 
    Serial.begin(115200);
    Serial.println("IR_TSOP4438_light_barrier");
    Serial.println("setup...");
    setupIRLightBarrier();
    Serial.println("running.");
}

void loop() {
  if(light_barrier_broken) {
    Serial.println("light barrier 1 break detected"); 
    delay(20);
    digitalWrite (LED_BUILTIN, LOW);
    light_barrier_broken = false;
  }
}

ir_light_barrier.ino

// based on
// Example of modulating a 38 KHz carrier frequency at 500 Hz with a variable duty cycle
// Author: Nick Gammon
// Date: 24 September 2012
// https://forum.arduino.cc/t/how-to-create-a-38-khz-pulse-with-arduino-using-timer-or-pwm/100217/44

// tweaked for TSOP4438
// https://www.vishay.com/docs/82459/tsop48.pdf
// find max burst length and min gap times:
//
// Minimum burst length 10 cycles/burst
// After each burst of length 10 to 40 cycles
//  a minimum gap time is required of ≥ 10 cycles
// Maximum number of continuous short bursts/second: 1500
//
// translates to:
// 38kHz = 26,3 us / Pulse → *10= 263us
// max burst length: <= 1052us  (26,3*40)
// min gap   length: >=  263us  (26,3*10)
// total cycle time of (1052+263=) 1315us 
// that violates the max bursts/second (666us/burst)
//
// on option is to optimize for the most bursts/second:
// so we use the min gap time as given and use the rest of the available time.
// 666us - 263us = 403 us burst length
// hopefully this way the AGC does not filter our stream...
// we now have to fit this to the best available prescaler / counter values:
// 672us fits good (this way we have less than the 1500 burst)
// this translates to 42 counts a' (0,0625us*256=) 16us
// so we use a gap length of 16us*17 = 272us 
// this gives us a burst length of 16us*(42-17)= 400us
//
// second option is to optimize for the longest burst length and have less bursts/second.
// here we will use a in-between leaning towards longer bursts:
// 0,0625us*256=16us
// 16us*50counts =  800us = 0,80ms = 1250,00Hz = 1250bursts/s
// 16us*80counts = 1280us = 1,28ms =  781,25Hz = 781,25bursts/s
// gap   length: 16us*17      =  272us 
// burst length: 16us*(80-17) = 1008us

// http://www.gammon.com.au/forum/?id=11504
// Timer 1
//   OC1A: D9
//   OC1B: D10
const byte LED = 9;

// Timer 2 (8bit)
//   OC2A: D11
//   OC2B: D3

// https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
const byte light_barrier_PIN = 2;
// const byte light_barrier_PIN = 3;

// global flags to be handled in the main loop...
volatile bool light_barrier_broken = false;


// Clock frequency divided by 38 kHz frequency desired
const long timer1_OCR1A_Setting = F_CPU / 38000L;
// (16000000 / 38000) = 421,05
// this only works on Timer 1 - as it is a 16bit timer.


// // ------------------------------------------
// // this is about the slowest possible setting.
// // timer2 target counts:
// // CPU          16MHz (0,0625us)
// // target       63Hz (16000us = 16ms)
// // prescaler    1024
// const long timer2_top = F_CPU / 63L / 1024;
// // (16000000 / 63 / 1024) = 250

// // ------------------------------------------
// // optimized for ~1500 bursts /second
// // target counts:
// // CPU          16MHz (0,0625us)
// // prescaler    256
// // target       1,488kHz (672us = 0,672ms)
// const long timer2_top = (F_CPU / 256L) / 1488L;
// // (16000000 / 256) / 1488 = 42
// // calculate on / off ratio (toggle point)
// const long timer2_compare = timer2_top * 400L / 672L;
// // 42 * 400 / 672   = 42 - 17   = 25 

// ------------------------------------------
// in-between -  leaning for longer bursts
// target counts:
// CPU          16MHz (0,0625us)
// prescaler    256
// target       781kHz (1280us = 1,28ms)
const long timer2_top = (F_CPU / 256L) / 781L;
// (16000000 / 256) / 781 = 80
// calculate on / off ratio (toggle point)
const long timer2_compare = timer2_top * 1008L / 1280L;
// 80 * 1008 / 1280   =  80 - 17  =  63



volatile bool sender_active = false;

void light_barrier_ISR() {
    if (
        sender_active 
        && digitalRead(light_barrier_PIN) == HIGH
    ) {
        digitalWrite (LED_BUILTIN, HIGH);
        light_barrier_broken = true;
        // if() {
        // }
    } else {
        digitalWrite (LED_BUILTIN, LOW);
    }
}

ISR (TIMER2_COMPA_vect) {
    // used to combine the two timers...

    // toggle D13
    // PINB = bit (5);
    // digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)) ;
    
    if (sender_active == false) {
        // enable timer1 output
        TCCR1A |= bit(COM1A0) ;  // Toggle OC1A on Compare Match
        // digitalWrite (LED_BUILTIN, HIGH);
        delayMicroseconds(100);
        sender_active = true;
    } else {
        sender_active = false;
        delayMicroseconds(100);
        // disable IR-Receiver Input check
        //detachInterrupt(digitalPinToInterrupt(light_barrier_PIN));
        // disable timer1 output
        TCCR1A &= ~bit(COM1A0) ;  // DO NOT Toggle OC1A on Compare Match
        digitalWrite (LED, LOW);  // ensure off
        // digitalWrite (LED_BUILTIN, LOW);
    }
}

void setupIRLightBarrier() {
    pinMode(LED, OUTPUT);
    digitalWrite(LED, LOW);
    pinMode(LED_BUILTIN, OUTPUT);
    digitalWrite(LED_BUILTIN, LOW);

    // set up Timer 1 - gives us 38.095 KHz
    TCCR1A = bit (COM1A0); // toggle OC1A on compare
    TCCR1B = _BV(WGM12) | _BV (CS10);   // CTC to OCR1A, No prescaler
    OCR1A =  (16000000L / 38000L / 2) - 1;  // zero relative

    // setup Timer 2
    TCCR2A = 0;
    TCCR2B = 0;
    // toggle OC2A on compare
    // TCCR2A |= bit(COM2A0); 
    // fast pwm to OCR2A
    TCCR2A |= bit(WGM21) | bit(WGM20);
    TCCR2B |= bit(WGM22); 
    // prescaler 1024
    // TCCR2B |= bit(CS22) | bit(CS21) | bit(CS20);
    // prescaler 265
    TCCR2B |= bit(CS22) | bit(CS21);
    // top
    OCR2A = timer2_top - 1;  // zero relative
    // switch point
    OCR2B = timer2_compare - 1;  // zero relative
    // enable interrupts
    TIMSK2 = bit(OCIE2A);
    // TIMSK2 = bit(OCIE2B) | bit(OCIE2A);

    // enable IR-Receiver Input check
    attachInterrupt(
        digitalPinToInterrupt(light_barrier_PIN), 
        light_barrier_ISR, 
        CHANGE
    );
}

1

I THINK the problem is that the Arduino takes some time (very minimal) to send the signal, but when it comes to receive it, the signal has already dissipated though your room.


send and receive at all times and then trigger alarm/LED once the "beam" is broken.

Use a laser, it's cheaper, more acurate, and more "James Bond'ish'" :)

2
  • 1
    Thanks for the idea @Dat Ha. I considered it, it is cooler!....but I ruled it out as this project will be used by kids so I thought best to avoid lasers! thanks for taking the time to respond Nov 17, 2016 at 23:36
  • 1
    This theory here is not really correct - while the light propagation is fast enough to ignore, the response time of a demodulating detector is Arduino-scale. The issue is one of software architecture - IR remote control schemes are not applicable here, rather what is needed is to be sending modulation (pulsing on and off) and checking the receiver at points in between. But also note as the correct answer clarifies that the modulated signal itself must be pulsed on and off at a slower cycle time, or the detector will reject it. Nov 18, 2016 at 16:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.