Testing the Arduino IR remote library

I wrote an IR remote library for the Arduino (details) that has turned out to be popular. I want to make sure I don't break things as I improve the library, so I've created a test suite that uses a pair of Arduinos: one sends data and the other receives data. The receiver verifies the data, providing an end-to-end test that the library is working properly.

Overview of the test

The first Arudino repeatedly sends a bunch of IR codes to the second Arduino. The second Arduino verifies that the received code is what is expected. If all is well, the second Arduino flashes the LED for each successful code. If there is an error, the second Arudino's LED illuminates for 5 seconds. The test cycle repeats forever. Debugging information is output to the second Arduino's serial port, which is helpful for tracking down the cause of errors.

Hardware setup

The test hardware is pretty simple: one Arduino transmits, and one Arduino receives. An IR LED is connected to pin 3 of the first Arduino to send the IR code. An IR detector is connected to pin 11 of the second Arduino to receive the IR code. A LED is connected to pin 3 of the second Arduino to provide the test status.
schematic of test setup

Details of the test software

One interesting feature of this test is the same sketch runs on the sending Arduino and the receiving Arduino. The test looks for an input on pin 11 to decide if it is the receiver:
void setup()
{
  Serial.begin(9600);
  // Check RECV_PIN to decide if we're RECEIVER or SENDER
  if (digitalRead(RECV_PIN) == HIGH) {
    mode = RECEIVER;
    irrecv.enableIRIn();
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, LOW);
    Serial.println("Receiver mode");
  } 
  else {
    mode = SENDER;
    Serial.println("Sender mode");
  }
}
Another interesting feature is the test suite is expressed very simply:
  test("SONY4", SONY, 0x12345, 20);
  test("SONY5", SONY, 0x00000, 20);
  test("SONY6", SONY, 0xfffff, 20);
  test("NEC1", NEC, 0x12345678, 32);
  test("NEC2", NEC, 0x00000000, 32);
  test("NEC3", NEC, 0xffffffff, 32);
...
Each test call has a debugging string, the type of code to send/receive, the value to send/receive, and the number of bits.

On the sender, the testmethod sends the code, while on the receiver, the method verifies that the proper code is received. The SENDER code calls the appropriate send method based on the type, and then delays before the next test. The RECEIVER code waits for a code. If it's correct, it flashes the LED. Otherwise, it sets the state to ERROR.

void test(char *label, int type, unsigned long value, int bits) {
  if (mode == SENDER) {
    Serial.println(label);
    if (type == NEC) {
      irsend.sendNEC(value, bits);
    } 
    else if (type == SONY) {
...
    }
    delay(200);
  } 
  else if (mode == RECEIVER) {
    irrecv.resume(); // Receive the next value
    unsigned long max_time = millis() + 30000;
    // Wait for decode or timeout
    while (!irrecv.decode(&results)) {
      if (millis() > max_time) {
        mode = ERROR; // timeout
        return;
      }
    }
    if (type == results.decode_type && value == results.value && bits == results.bits) {
      // flash LED
    } 
    else {
      mode = ERROR;
    }
  }
}
The trickiest part of the code is synchronizing the sender and the receiver. This happens in loop(). The receiver waits for 1 second without any transmission, while the sender pauses for 2 seconds after each time through the tests. Thus, the receiver will wait while the sender is running through tests, and then will start listening just before the sender starts the next cycle of tests. One other thing to point out is if there is an error, the receiver will skip through all the remaining tests, light the LED to indicate the error, and then will wait to sync up again. This avoids the problem of one bad test getting the receiver permanently out of sync; the receiver is able to re-sync and continue successfully after a failed test.
void loop() {
  if (mode == SENDER) {
    delay(2000);  // Delay for more than gap to give receiver a better chance to sync.
  } 
  else if (mode == RECEIVER) {
    waitForGap(1000);
  } 
  else if (mode == ERROR) {
    // Light up for 5 seconds for error
    mode = RECEIVER;  // Try again
    return;
  }
The test also includes some raw mode tests. These are a bit more complicated, since I want to test the various combinations of sending and receiving in raw mode.

Download and running

I'm gradually moving my development to GitHub at https://github.com/shirriff/Arduino-IRremote.

The code fragments above have been slightly abbreviated; the full code for the test sketch is here.

To download the library and try out the two-Arduino test:

  • Download the IRremote library zip file.
  • Unzip the download
  • Move/rename the shirriff-Arduino-IRremote-nnnn directory to arduino-000nn/libraries/IRremote. The test sketch is in examples/IRtest2.

To run the test, install the sketch on two Arduinos. The test should automatically start running. Note that it is a bit tricky to use two Arduinos at once. They will probably get assigned different serial ports, and you can switch ports using the Tools menu. If you get confused, you can plug one Arduino in at a time, and then you can be sure about which one is getting installed.

My plan is to do more development on the library, now that I have a reasonably solid test suite and I can be more confident that I don't break things. Let me know if there are specific features you'd like.

Thanks go to SparkFun for giving me the second Arduino that made this test possible.

11 comments:

  1. Is it possible to use the same arduino board for both sending and receiving IR data?

    ReplyDelete
  2. Corrected Example Link;
    https://github.com/Thorsten-Sick/Arduino-IRremote/tree/master/examples

    ReplyDelete
  3. Can the send/recieve be implemented on the same Arduino board? I know this has been asked before but I didn't see an answer.
    Thanks in anticipation

    ReplyDelete
  4. Answer to above poster - Yes - this shouldn't be a problem.


    Hi Ken, I am hopelessly stuck at a certain point in getting my arduino uno universal remote to work.

    I have set up my Uno with PWM pin 3 IR output and pin 13 for the ir receiver.

    I have tested IR receive and it works lovely. It prints out the Hex commands:

    10EF708F
    - Vol Down
    10EF58A7
    - Vol Up


    I have tried and tried to modify your IRsendDemo file to send those commands via pin 3 but it just wont work.

    I tried:

    irsend.sendNEC(10EF58A7, 3); // as I thought it was 32 bit hex?

    I am mightily confused at the moment. I am struggling to read through IRremote.h and IRremoteInt.h


    Please help!

    ReplyDelete
  5. I also got this working with another remote - which is where i stole the IR led from,


    if (irrecv.decode(&results)) {

    long int decCode = results.value;
    Serial.println(decCode);
    switch (results.value) {
    case 805778:
    Serial.println("Forward");
    digitalWrite(forwardPin, HIGH); // sets the LED on
    break;



    It was turning on and off LED's when I pressed the right button.

    Slightly different to your:

    if (irrecv.decode(&results)) {
    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
    }

    ReplyDelete
  6. in your tutorial you say repeat this 3 times for Sony with the 40ms delay.

    irsend.sendSony(0xa90, 12); // Sony TV power code


    How should I modify that for my Hex codes and also, how do I know what protocol this remote is using?

    All I appear to know is that it is 32bits long - which your program told me.

    ReplyDelete
  7. hi,
    can i use the same port sending and receiving ir?
    i need to use it on attiny and i have limited number of ports.

    thanks
    mor

    ReplyDelete
  8. Please i need help. Have tried several times to add Ken sheriff's IR remote library to my Arduino library, but have failed. Only wrote invalid library

    ReplyDelete