IR Emitter and Receiver

Hello! I’m working on creating a Laser Tag gun using the Seeed Studio Grove IR Emitter and Receiver.
Currently I’m having an issue with my code, my receiver will receive signals, but as soon as I try to “shoot” the receiver will stop working and no longer will get any signals from the other gun.
Is there any advice on running a receiver and emitter on the same board that will avoid this?

Code is below…

void Receiver()
{
Serial.println(“BANG! you got shot”);
IR.Recv(dta); // receive data to dta

Serial.println(“±-----------------------------------------------------+”);
Serial.print("LEN = ");
Serial.println(dta[BIT_LEN]);
Serial.print("START_H: “);
Serial.print(dta[BIT_START_H]);
Serial.print(”\tSTART_L: ");
Serial.println(dta[BIT_START_L]);

Serial.print("DATA_H: “);
Serial.print(dta[BIT_DATA_H]);
Serial.print(”\tDATA_L: ");
Serial.println(dta[BIT_DATA_L]);

Serial.print("\r\nDATA_LEN = ");
Serial.println(dta[BIT_DATA_LEN]);

Serial.print(“DATA: “);
for(int i=0; i<dta[BIT_DATA_LEN]; i++)
{
Serial.print(“0x”);
Serial.print(dta[i+BIT_DATA], HEX);
Serial.print(”\t”);
}
Serial.println();

Serial.print(“DATA: “);
for(int i=0; i<dta[BIT_DATA_LEN]; i++)
{
Serial.print(dta[i+BIT_DATA], DEC);
Serial.print(”\t”);
}
Serial.println();
Serial.println(“±-----------------------------------------------------+\r\n\r\n”);
}

void Shoot()
{
IR.Send(dtaSend, 38); //send signal when pushbutton is HIGH
Serial.println(“pew pew”);
Serial.println("Player: ");
Serial.println(PlayerName);
Serial.println("fired! \nPlayer Number: ");
Serial.println(dta[BIT_DATA + 0]);
Serial.println("Team: ");
Serial.println(dta[BIT_DATA + 1]);

delay(500);
}

void loop()
{
buttonState = digitalRead(buttonPin); //read state of pushbutton value
if(IR.IsDta()) // get IR data
{
Receiver();
}

else if(buttonState == HIGH) //check if the pushbutton is pressed, if it is the state will be HIGH
{
Shoot();
buttonState == LOW;
}

}

Does it print anything when it shoots? There is no state change detection on the button, so it should keep shooting over and over, as long as the button is pressed.

This line does nothing:

      buttonState == LOW;

You probably meant:

      buttonState = LOW;

but the program logic reassigns buttonState on every pass of loop(), so it also does nothing.

Yes it will print to Serial when it shoots, if the button is held it will continue to shoot. I have a slight delay in there so it only shoots once. I noticed that without the delay it shoots twice with 1 button press. I assume this is because it takes time for the state to change.
Also yes I know that line doesn't really do anything, I had it in there as an experiment to see if it would help. But the button does change states on its own after being pressed.
I have 2 controllers with the same code, when I shoot from controller A B will receive and print back, but then when I shoot from B it will print to serial that it shot, and then will no longer receive signals anymore if I try to shoot from A again.

For how long won't it receive? The receiver might take some time to recover from the strong TX signal. Is it okay if you reset the Arduino immediately after sending?

aarg:
For how long won't it receive? The receiver might take some time to recover from the strong TX signal. Is it okay if you reset the Arduino immediately after sending?

Yes it works immediately after being reset.

You did not post the entire sketch. Please do so, and place it inside code tags to make it forum compatible.

#include <IRSendRev.h>

#define BIT_LEN         0
#define BIT_START_H     1
#define BIT_START_L     2
#define BIT_DATA_H      3
#define BIT_DATA_L      4
#define BIT_DATA_LEN    5
#define BIT_DATA        6

const int ir_freq = 38;             // 38k
const int pinRecv = 2;              // ir receiver connect to D2
const int buttonPin = 4;            //push button connected to D4

int buttonState = 0;

unsigned char dtaSend[20];
char PlayerName[100] = "xXPew^3Xx";

void dtaInit() 
{
    dtaSend[BIT_LEN]        = 11;      // all data that needs to be sent
    dtaSend[BIT_START_H]    = 179;    // the logic high duration of "Start"
    dtaSend[BIT_START_L]    = 90;     // the logic low duration of "Start"
    dtaSend[BIT_DATA_H]     = 11;     // the logic "long" duration in the communication
    dtaSend[BIT_DATA_L]     = 33;     // the logic "short" duration in the communication

    dtaSend[BIT_DATA_LEN]   = 2;      // Number of data which will sent. If the number is other, you should increase or reduce dtaSend[BIT_DATA+x].

    dtaSend[BIT_DATA + 0]     = 0;    // Player ID
    dtaSend[BIT_DATA + 1]     = 101;  // Team no.

    //We can add more data bits later on if need be
}

void setup()
{
    Serial.begin(115200);
    Serial.println("init start");
    IR.Init(pinRecv);
    pinMode(buttonPin, INPUT); //initialize the pushbutton as an input
    Serial.println("init over");
    dtaInit();
}

unsigned char dta[20];

void Receiver()
{
  Serial.println("BANG! you got shot");
        IR.Recv(dta);               // receive data to dta

        Serial.println("+------------------------------------------------------+");
        Serial.print("LEN = ");
        Serial.println(dta[BIT_LEN]);
        Serial.print("START_H: ");
        Serial.print(dta[BIT_START_H]);
        Serial.print("\tSTART_L: ");
        Serial.println(dta[BIT_START_L]);

        Serial.print("DATA_H: ");
        Serial.print(dta[BIT_DATA_H]);
        Serial.print("\tDATA_L: ");
        Serial.println(dta[BIT_DATA_L]);

        Serial.print("\r\nDATA_LEN = ");
        Serial.println(dta[BIT_DATA_LEN]);

        Serial.print("DATA: ");
        for(int i=0; i<dta[BIT_DATA_LEN]; i++)
        {
            Serial.print("0x");
            Serial.print(dta[i+BIT_DATA], HEX);
            Serial.print("\t");
        }
        Serial.println();

        Serial.print("DATA: ");
        for(int i=0; i<dta[BIT_DATA_LEN]; i++)
        {
            Serial.print(dta[i+BIT_DATA], DEC);
            Serial.print("\t");
        }
        Serial.println();
        Serial.println("+------------------------------------------------------+\r\n\r\n");
}

void Shoot()
{
  IR.Send(dtaSend, 38); //send signal when pushbutton is HIGH
      Serial.println("pew pew");
      Serial.println("Player: ");
      Serial.println(PlayerName);
      Serial.println("fired! \nPlayer Number: ");
      Serial.println(dta[BIT_DATA + 0]);
      Serial.println("Team: ");
      Serial.println(dta[BIT_DATA + 1]);
      
      delay(500);
}

void loop() 
{
    buttonState = digitalRead(buttonPin); //read state of pushbutton value
    if(IR.IsDta())                  // get IR data
    {
        Receiver();
    }
    
    else if(buttonState == HIGH) //check if the pushbutton is pressed, if it is the state will be HIGH
    {
      Shoot();
      buttonState == LOW;
    }

    
}

I was working in IRLib2 and found the same issue. I had to re-enable the IR input after I’d sent something.

I’m guessing what I see as myReceiver.enableIRIn(); is your IR.Init(pinRecv); and you need to reinitialize the input pin.

My Code:

void theSender (){
	mySender.send(Tutorial,0x61a0a11b,0);//NEC TV power button=0x61a0f00f
    Serial.print(F("Sent signal & micros() is: "));
	Serial.print(micros());
	Serial.print(" | ");
	Serial.println(millis());
	
	newTime = millis();
	myReceiver.enableIRIn();      // <-- Restart receiver (I added this to the included examples to test)
  }

I suggest adding it to the end of your Shoot function as so:

void Shoot()
{
  IR.Send(dtaSend, 38); //send signal when pushbutton is HIGH
      Serial.println("pew pew");
      Serial.println("Player: ");
      Serial.println(PlayerName);
      Serial.println("fired! \nPlayer Number: ");
      Serial.println(dta[BIT_DATA + 0]);
      Serial.println("Team: ");
      Serial.println(dta[BIT_DATA + 1]);

      IR.Init(pinRecv); // <---- This here.
     
      delay(500);
}

Please let us know how you make out.

These changes should get rid of that delay and let the receive code run while the shoot code waits.

Untested but likely close to bug-free. Or was that free bugs? nahhhhhh

byte  shootFlag;

.....

void Shoot()
{
  static  unsigned long  startShoot, waitShoot;  // unsigned integer math handles rollover through subtraction

  if ( waitShoot > 0 )
  {
    if ( millis() - startShoot < waitShoot )  // (<<== unsigned subtraction) until waitShoot millis AFTER startShoot....
    {
      return;                                            // .... let other functions run, don't hold up code execution
    }
    else            // so the wait is up, initialize triggers for the next shot
    {
      waitShoot = 0;
      shootFlag = 0;
    }
  }
  else if ( shootFlag > 0 )
  {
      IR.Send(dtaSend, 38); //send signal when pushbutton is HIGH
      Serial.println("pew pew");
      Serial.println("Player: ");
      Serial.println(PlayerName);
      Serial.println("fired! \nPlayer Number: ");
      Serial.println(dta[BIT_DATA + 0]);
      Serial.println("Team: ");
      Serial.println(dta[BIT_DATA + 1]);
      
      // delay(500);
      waitShoot = 500;            // setting wait time
      startShoot = millis();       // synchronize the start to millis "clock"
  }
}

..........

void loop() 
{
    buttonState = digitalRead(buttonPin); //read state of pushbutton value

    if(IR.IsDta())                  // get IR data
    {
        Receiver();
    }
    else if ((buttonState == HIGH) && ( startShoot == 0 )) // won't read button bounces because....
    {
      shootFlag = 1;                // .... Shoot() will not clear this flag until after a wait
    }

    Shoot();  // must always run when loop() does to check time  
}

If you can understand WHY I moved the Shoot() function outside of the if-else in loop() then you didn’t get lost in the code and did get the lesson.

GoForSmoke:
These changes should get rid of that delay and let the receive code run while the shoot code waits.

Untested but likely close to bug-free. Or was that free bugs? nahhhhhh

byte  shootFlag;

void Shoot()
{
  static  unsigned long  startShoot, waitShoot;  // unsigned integer math handles rollover through subtraction

if ( waitShoot > 0 )
  {
    if ( millis() - startShoot < waitShoot )  // (<<== unsigned subtraction) until waitShoot millis AFTER startShoot…
    {
      return;                                            // … let other functions run, don’t hold up code execution
    }
    else            // so the wait is up, initialize triggers for the next shot
    {
      waitShoot = 0;
      shootFlag = 0;
    }
  }
  else if ( shootFlag > 0 )
  {
      IR.Send(dtaSend, 38); //send signal when pushbutton is HIGH
      Serial.println(“pew pew”);
      Serial.println("Player: ");
      Serial.println(PlayerName);
      Serial.println("fired! \nPlayer Number: ");
      Serial.println(dta[BIT_DATA + 0]);
      Serial.println("Team: ");
      Serial.println(dta[BIT_DATA + 1]);
     
      // delay(500);
      waitShoot = 500;            // setting wait time
      startShoot = millis();      // synchronize the start to millis “clock”
  }
}

void loop()
{
    buttonState = digitalRead(buttonPin); //read state of pushbutton value

if(IR.IsDta())                  // get IR data
    {
        Receiver();
    }
    else if ((buttonState == HIGH) && ( startShoot == 0 )) // won’t read button bounces because…
    {
      shootFlag = 1;                // … Shoot() will not clear this flag until after a wait
    }

Shoot();  // must always run when loop() does to check time 
}




If you can understand WHY I moved the Shoot() function outside of the if-else in loop() then you didn't get lost in the code and did get the lesson.

Hi thank you for your response. When I implemented your code it didnt want to work unless I made static unsigned long startShoot, waitShoot;global. Also it shoots once and then stop running anything. So i will play with it, but I understand where your trying to go with this. I appreciate the input.

Are you getting a buffer overrun?

void dtaInit()
{
    dtaSend[BIT_LEN]        = 11;      // <-- should this not be 7?


    dtaSend[BIT_START_H]    = 179;    // the logic high duration of "Start"
    dtaSend[BIT_START_L]    = 90;     // the logic low duration of "Start"
    dtaSend[BIT_DATA_H]     = 11;     // the logic "long" duration in the communication
    dtaSend[BIT_DATA_L]     = 33;     // the logic "short" duration in the communication

    dtaSend[BIT_DATA_LEN]   = 2;      // Number of data which will sent. If the number is other, you should increase or reduce dtaSend[BIT_DATA+x].

    dtaSend[BIT_DATA + 0]     = 0;    // Player ID
    dtaSend[BIT_DATA + 1]     = 101;  // Team no.

    //We can add more data bits later on if need be
}

From the github page https://github.com/Seeed-Studio/IRSendRev:

dta_buf[0] len of the buf(not include dta_buf[0]) ← This here makes me think you should shorten it

dta_buf[1] start signal high time

dta_buf[2] start signal low time

dta_buf[3] bit high time

dta_buf[4] bit low time

dta_buf[5] data lenght(how many byte to send)

dta_buf[6] dta_buf[n] : data to send

ifreq: frequence, eg:38(means 38k)

KatarinaNicole21:
Hi thank you for your response. When I implemented your code it didnt want to work unless I made static unsigned long startShoot, waitShoot;global. Also it shoots once and then stop running anything. So i will play with it, but I understand where your trying to go with this. I appreciate the input.

Funny, waitShoot is only used in Shoot(). The flag should be all that's needed to shoot, but the code is not tested.

Coder's intuition would have try this but it seems useless.

static unsigned long startShoot = 0; // unsigned integer math handles rollover through subtraction
static unsigned long waitShoot = 0;

I'll have to give it a try w/o the IR and substitute Serial input instead.

KatarinaNicole21:
Hi thank you for your response. When I implemented your code it didnt want to work unless I made static unsigned long startShoot, waitShoot;global. Also it shoots once and then stop running anything. So i will play with it, but I understand where your trying to go with this. I appreciate the input.

That’s because startShoot gets used by more than the Shoot() function, loop() uses it too.
The stop running is because it doesn’t get cleared for loop() to use.

This compiles and runs to demo the logic. Enter h to get hit or s to shoot and get hit. -wink-

#define WAITMILLIS 5000
unsigned long  startShoot; // does get used in 2 functions!
char ascii;
byte shootFlag;

void setup()
{
  Serial.begin(115200);
  
  delay( 10 );     // hopefully start with clear monitor now
  Serial.flush();  // doesn't clear something........

  Serial.println( F( "\n\ntesting -- enter s to shoot or h to hit\n\n" ));
}


void Receiver()
{
  Serial.print( millis() );
  Serial.println( F( " *** BANG! you got shot" ));
  Serial.println();
  Serial.println("+------------------------------------------------------+\r\n\r\n");
}

void Shoot()
{
  static  unsigned long  waitShoot;  // unsigned integer math handles rollover through subtraction

  if ( waitShoot > 0 )
  {
    if ( millis() - startShoot < waitShoot )  // (<<== unsigned subtraction) until waitShoot millis AFTER startShoot....
    {
      return;                                            // .... let other functions run, don't hold up code execution
    }
    else            // so the wait is up, initialize triggers for the next shot
    {
      waitShoot = 0;
      startShoot = 0;
      shootFlag = 0;
      ascii = 'h';
    }
  }
  else if ( shootFlag > 0 )
  {
    Serial.print( millis() );
    Serial.println( F( " >>> pew pew" ));

    waitShoot = WAITMILLIS;            // setting wait time
    startShoot = millis();       // synchronize the start to millis "clock"
  }
}


void loop()
{
  if ( Serial.available()) 
  {
    ascii = Serial.read();
  }
  else
  {
    ascii = 0;
  }

  Shoot();  // this is here so ascii can be changed

  if ( ascii == 'h' )                 // hit
  {
    Receiver();
  }
  else if (( ascii == 's' ) && ( startShoot == 0 ))
  {
    shootFlag = 1;
  }
}

edit – now it shows time in millis, just to show the wait.
edit2 – set the wait to 5 seconds, hits are processed during that time.

Does this example now show a non-blocking wait pretty clearly?

Allister_McRae:
I was working in IRLib2 and found the same issue. I had to re-enable the IR input after I’d sent something.

I’m guessing what I see as myReceiver.enableIRIn(); is your IR.Init(pinRecv); and you need to reinitialize the input pin.

My Code:

void theSender (){
mySender.send(Tutorial,0x61a0a11b,0);//NEC TV power button=0x61a0f00f

Serial.print(F("Sent signal & micros() is: “));
Serial.print(micros());
Serial.print(” | ");
Serial.println(millis());

newTime = millis();
myReceiver.enableIRIn();      // <-- Restart receiver (I added this to the included examples to test)

}




I suggest adding it to the end of your Shoot function as so:



void Shoot()
{
  IR.Send(dtaSend, 38); //send signal when pushbutton is HIGH
      Serial.println(“pew pew”);
      Serial.println("Player: ");
      Serial.println(PlayerName);
      Serial.println("fired! \nPlayer Number: ");
      Serial.println(dta[BIT_DATA + 0]);
      Serial.println("Team: ");
      Serial.println(dta[BIT_DATA + 1]);

IR.Init(pinRecv); // <---- This here.
   
      delay(500);
}




Please let us know how you make out.

This ended up working! Thank you! It works now, I appreciate all the advice, you guys are amazing.

If you shoot, can you also be hit during that 1/2 second or does shooting give you a shield?

That's why I show the shoot and hit millis and made the shoot wait 5 seconds just to be sure you could be hit any time.

You can use an interrupt to get around a delay() but wasting an interrupt on a button is pretty poor and interrupts have overhead and need to be kept shorter than most get-around-delay uses require since they can't just flag and time an event for the delay-blocked sketch but have to do it all in the interrupt service routine. Long ISRs tend to throw the system off, Serial and millis() also use interrupts (correctly) and the long ISR blocks those as well.

You can go either way, delays and fix-arounds or event-driven. How much the sketch can grow depends on which.