How do I change a pin state based a series of (IR) received characters?

Hi all-

Here’s what I am trying to do: I have an ATTiny85 receiving (via IR) a stream of ones sent at 5 Hz from another micro. The stream of ones is intermittent, as the IR receiver isn’t always pointed at the transmitter. In fact, orientation sensing of the receiver is what I’m after.

[incidentally, getting Software serial and IRrecv BOTH working at the same time was quite the adventure, but that’s another story!]

What I’d like to have happen is that a pin on the receiving end go HIGH as long as it’s seeing ones, and LOW if they stop coming. I have an LED on that pin to monitor its state, btw.

However, I don’t want the pin state to follow EVERY one/zero, which my code currently does, and which leads to a blinking LED. Seems like I need to ‘average’ the input over some small time interval, and set the pin accordingly. Having said that, though, and having tried a variety of schemes to make it work the solution still evades me. So I thought I’d ask if anyone has a nice hint for me!

Here’s my receive code at the moment:

#include "tiny_IRremote.h"
#include <SoftwareSerial.h>

int RECV_PIN = 1;
IRrecv irrecv(RECV_PIN);
decode_results results;

SoftwareSerial mySerial(3,2);
 
void setup(){
  mySerial.begin(9600);
  irrecv.enableIRIn();
  pinMode(1, INPUT);   // IR receive pin        [physical pin 6]
  pinMode(2, OUTPUT);  // Serial TX pin         [physical pin 7]
  pinMode(0, OUTPUT);  // LED output pin        [physical pin 5]
}

void loop(){
  
  if(irrecv.decode(&results)){
    
    unsigned long value = results.value;
    
    mySerial.print(value);  // For testing purposes. Based on what 
    mySerial.print("\n");   // I see in the monitor, I know when/what characters
                            // are being received and that's all good.
                            
    if(value == 1){
      digitalWrite(0, HIGH);
    }

    irrecv.enableIRIn();
  }    
  
digitalWrite(0, LOW);
}

Well, thanks in advance.

regards,
Patrick

Seems like a debouncing routine could be bent to the purpose. There's one in IDE -> file/examples/digital.

Maybe something like this? Didn’t compile because I don’t have the tiny_IRremote library.

#include "tiny_IRremote.h"
#include <SoftwareSerial.h>

int RECV_PIN = 1;
IRrecv irrecv(RECV_PIN);
decode_results results;


SoftwareSerial mySerial(3, 2);

void setup() {
  mySerial.begin(9600);
  irrecv.enableIRIn();
  pinMode(1, INPUT);   // IR receive pin        [physical pin 6]
  pinMode(2, OUTPUT);  // Serial TX pin         [physical pin 7]
  pinMode(0, OUTPUT);  // LED output pin        [physical pin 5]
}

void loop()
{
  int max_values;  // number of values to receive before averaging
  static count = 0;  // to keep track of how many times 1 was received
  static float ave = 0.0;  // the average of the received 1s
  if (irrecv.decode(&results))
  {

    unsigned long value = results.value;

    mySerial.print(value);  // For testing purposes. Based on what
    mySerial.print("\n");   // I see in the monitor, I know when/what characters
    // are being received and that's all good.

    if (value == 1)
    {
      count += 1;
    }
    if count >= max_values
    {
      ave = (float(count) + ave) / (float(max_values) + 1.0);  // (number of 1s received plus last average) divided by (the maximum number of values plus 1 for previous result)
      count = 0;  //  reset for next
      if (ave >= 0.5)
      {
        digitalWrite(0, HIGH);  //  if ave is 1 turn on LED, else turn it off.
      }
      else
      {
        digitalWrite(0, LOW);
      }
    }
    irrecv.enableIRIn();
  }
}

The IRremote (or tiny_IRremote) library is NOT a suitable way to detect 5 Hz pulses from an IR LED. The IR receiver module is only sensitive to IR modulated with a carrier frequency near 38 kHz (typically 36 to 40 kHz depending on model). If your IR is not modulated you will not receive a signal.

If your IR pulses are modulated you don't need the IRremote library to detect them. Just read the signal directly from the IR receiver module.

Most IR receiver modules will ignore continuous modulated IR but some are designed for continuous signals. Two models that do that are TSOP58038 and TSOP4038. If you use one of those you will not have to pulse your modulated IR at 5 Hz and will get faster feedback when a signal is detected.

Hi John-

I was perhaps not clear in my description. 5 Hz is not the frequency at which the IR is modulated (that’s the usual 38 kHz), it is the frequency at which the transmitter sends numbers!

It is similar to a debounce situation, except that the ‘switch’ bounces at 5 Hz, essentially, and starts/stops doing so depending on the relative orientation of the sender and receiver.

What I’m trying to accomplish is for the LED to be on continuously when the two are “seeing” each other, without it blinking in between characters (even if they are all ones).

tnx,
Patrick

What characters are being sent and in what encoding?

Hello-

I’m sending single digits (ones) using the NEC protocol. Everything related to the transmission/reception of these ones is working fine. In other words, what I see on the Serial monitor of the receiver is:

11111111111111 (vertically, of course but I’m omitting the LF to save space!)

This is what the receiver produces when it is pointing at the transmitter. When it isn’t, it prints nothing. The behavior that I’m looking for is basically this:

1111111111111111 111111111111111111111
LED on here…LED off here…LED on here…LED off here

What I’m getting is the LED blinking, instead of following the overall trend. It seems as though what I need to do is 'average out" the gaps between the 1s, or make the (intermittent) presence of a 1 persist for longer than the time in between 1s (when nothing is being received). I think the lack of synchronization between sender and receiver is part of the issue.

I have the strong feeling that the solution to this problem is a lot simpler than I am thinking, and I’m getting ready to give myself a swift kick when it dawns on me!!

tnx,
Patrick

So the 1’s are arriving at 5 per second? If so, record the time (millis()) each time a ‘1’ arrives. If the time since then is below 300, count that as ON. If it is greater than or equal to 300, count it as OFF.

#include "tiny_IRremote.h"
#include <SoftwareSerial.h>


int RECV_PIN = 1;


IRrecv irrecv(RECV_PIN);
decode_results results;


SoftwareSerial mySerial(3, 2);


unsigned long LastTime1Received = 0;


void setup()
{
  mySerial.begin(9600);
  irrecv.enableIRIn();
  pinMode(1, INPUT);   // IR receive pin        [physical pin 6]
  pinMode(2, OUTPUT);  // Serial TX pin         [physical pin 7]
  pinMode(0, OUTPUT);  // LED output pin        [physical pin 5]
}


void loop()
{
  if (irrecv.decode(&results))
  {
    unsigned long value = results.value;


    mySerial.print(value);  // For testing purposes. Based on what
    mySerial.print("\n");   // I see in the monitor, I know when/what characters
    // are being received and that's all good.


    if (value == 1)
    {
      LastTime1Received = millis();
    }


    irrecv.enableIRIn();
  }


  // Turn ON the LED if it has been less than 300 milliseconds since we last received a '1'.
  digitalWrite(0, millis() - LastTime1Received < 300);
}

John-

I was right...it was pretty simple! Thanks! I had come up with more or less the same idea while on a long car trip today...I guess after looking over the debounce example I had millis() on the brain!!!!

Patrick

Hi again-

Sorry in advance that that this is such a long post…thanks for sticking with it!

Well I thought I’d found the solution to my problem, but not quite. In the interest of clearly defining the problem, let me better describe my ultimate goal:

To make a handheld (remote) device that can tell roughly which way it is facing by receiving different numbers from several IR ‘beacons’, one of which is transmitting a steady stream of ones, say, at 5 Hz. Another beacon might be sending twos, indicating a different direction. The rep rate is pretty much arbitrary, and would (I believe) only affect the response time of the remote. [Unless of course it was too fast, in which case it might bump up against the capability of the receiver to decode inputs fast enough.]

Importantly, the remote should indicate which direction it is facing with an LED that does not pay attention to the short times (0.2 s) between ones, and so does not blink at 5 Hz. In other words, it should have a short memory for what number it saw last…!

I’m pretty certain that the following pseudo-code should suffice:

if ( a character is received )

if ( it’s a 1 )

turn on the LED
re-enable the receiver
record the current time with millis()

while( the next character hasn’t arrived yet ) {

if( 300 mS has passed ) turn off the LED
}

re-enable the receiver

I should say that I have the whole sending/receiving IR thing working just fine in general. I even have my transmitter hooked up with a momentary switch to send either a single 1 at whatever rate I push it (!) or a series of 1s if I hold it down. So the problems do not stem from some general issue with either IR or softwareSerial on my ATTiny85.

Here’s how that looks in my current (non-working) code:

#include "tiny_IRremote.h"
#include <SoftwareSerial.h>

int RECV_PIN = 1;
IRrecv irrecv(RECV_PIN);
decode_results results;
unsigned long value = results.value;
unsigned long recv_time;

SoftwareSerial mySerial(3,2);

void setup(){
  
  mySerial.begin(9600);
  irrecv.enableIRIn();
  pinMode(1, INPUT);   // IR receive pin        [physical pin 6]
  pinMode(2, OUTPUT);  // Serial transmit pin   [physical pin 7]
  pinMode(0, OUTPUT);  // LED output pin        [physical pin 5]

  for(int z = 0; z<10; z++) {
     digitalWrite(0, HIGH);
     delay(100);
     digitalWrite(0, LOW);
     delay(100);
  }
}


void loop(){
  
  if(irrecv.decode(&results)){
    
    if(results.value == 1){
      digitalWrite(0, HIGH);
      recv_time = millis();
      irrecv.enableIRIn(); 
  }
  
     while(!irrecv.decode(&results)){
        if(millis() > recv_time + 300) {
         digitalWrite(0, LOW);
         recv_time = 0;
        }
     }
  }
  irrecv.enableIRIn();
}

The LED stays off with this code. I’m beginning to think that there’s something about the irrecv object (?) that escapes me, and here’s why: I tried the following code, thinking that I’d get a stream of 0s punctuated by 1s every time I hit the button on the transmitter:

void loop(){

  
  mySerial.print(irrecv.decode(&results));
  irrecv.enableIRIn();


}

but all I get are zeros. This leads me to believe that there is a subtle (at least to me!) timing issue that I need to get a handle on, which I think is preventing my while loop from functioning the way I want it to.

Any help is greatly appreciated!

Patrick

With that code you will get thousands of 0's and one 1 each time time you press the button. Maybe you are just missing the 1's? You also enable the IR In every time through loop(). Maybe that is preventing it from receiving a complete code? Perhaps you should put in a delay so you only get a few 0's a second and only re-enable input when the receive was successful.

void loop(){
  int returnCode = irrecv.decode(&results);
  mySerial.print(returnCode);
  if (returnCode) {
    irrecv.enableIRIn();
  }
  else {
    delay(100);  // Ten 0's a second
  }
}