0
\$\begingroup\$

I've been trying to get an Arduino to replicate my air conditioner remote signal for quite a while and I'm almost giving up just based on how annoying this task is. First things first:

  • The Arduino I'm using is the Mega 2560 which uses the Mega2560 AVR chip, and the IR receiver is the VS1838b (logic level low).

  • The IR carrier frequency is 38 KHz (13 uS timer cycles), and the maximum speed I've been able to get reliable readings out of the Arduino is around 250 KHz (4 uS timer cycles). IR signals are decoded via the length of a signal corrupted by a 38 KHz carrier signal, but the VS1838b returns only the length of the signal (without the 38 KHz portion).

  • The Mega has 8192 bytes of memory, which means I need to be very efficient. Serial prints are out of the question, since they add a significant delay to the program which would affect the reading. I've been using an array of bytes, storing the information sequentially from left to right, but this means I can have a maximum of 64,000 samples.

  • The IR LED I'm using is fully functional, and Arduino's read/write speed is sufficient, since I was able to open up an AC remote, solder a wire to one of the remote's IR terminals, hook it up to an Arduino and relay that result to my IR LED.

  • This is how the timer is configured:

void setup()
{
//  Serial.begin (115200);
  pinMode(8, INPUT);
  pinMode(4, OUTPUT);

  TCCR1A = B00000000;
  TCCR1B = B00001001; // CTC mode with 1:1 prescaler
  TIMSK1 = B00000010; // Enables Timer 1 A compare interrupt
  OCR1A = 0x003E; // 3E = 4us, 7F = 8us, 9F = 10us, CF = 13us, 19F = 26us
}

I figured it would be an easy implementation. All I needed to do was copy the signal exactly as it was read (directly from the AC remote), and it would work fine (aside from being inefficient in storage). I copied the signal in 4 uS cycles, like so:

ISR (TIMER1_COMPA_vect)
{
  temp = temp << 1;

  if (PINH == 32)
    temp++;

  j++;
  if (j == 8)
  {
    store[k] = temp;
    k++;
    j = 0;
    temp = 0;
  }

  if (k == len)
  {
    k = 0;
    j = 0;
    flag = 1;
    StopTimer();
  }
}

Where PINH == 32 is the receive pin for the AC remote (32 because I'm reading directly from the register). Then, using the same timer interrupt, I wrote the signal when it was finished reading:

ISR (TIMER1_COMPA_vect)
{
  if (k == len)
  {
    StopTimer();
    CLR(5);
    k = 0;
    flag = 1;
  }
  
  if (bitRead(store[k], j) == 1)
  {
    SET(5);
  }
  else
  {
    CLR(5);
  }

  j--;

  if (j == -1)
  {
    k++;
    j = 7;
  }
}

But this didn't work and I had no idea why. I then tried to use the VS1838b, and couple it with a fixed PWM frequency of 38 KHz through the timer, and enabling/disabling the timer depending on the sensor's readings.

  • Summary: Arduino reads VS1838b constantly, and enables/disables the timer depending on the result. It didn't work.

I figured maybe the PWM frequency was being affected by the program somehow, so I used a different Arduino to write the PWM frequency and another to read the VS1838b. I then connected the fixed 38 KHz PWM write to the base of a TIP41C transistor, the Arduino control pin to the collector.

  • Summary: Two Arduinos, one outputs a PWM frequency of 38 KHz to the base of a transistor, and the other outputs the NOT result of the VS1838b to the collector. It didn't work.

I'm at a loss of what could possibly be the cause, I've double checked the timer datasheet, I've even manually calibrated the timer using micros() functions, but nothing seems to work. Is there something I'm fundamentally doing wrong?

edit: I've simplified the circuit and uploaded pictures of how it looks. Basically, D10 is an input to the VS1838b sensor, D6 is used by the timer to write a PWM frequency of 38 KHz and D3 is used to output the NOT result of the sensor. The code looks like this:

#define CLR(x) (PORTD&=(~(1<<x)))
#define SET(x) (PORTD|=(1<<x))
#define TOGGLE(x) (PORTD^=(1<<x))

void setup()
{
  Serial.begin(2000000);
  pinMode(10, INPUT);
  pinMode(3, OUTPUT);
  pinMode(6, OUTPUT);
  TCCR1A = B00000000;
  TCCR1B = B00001001;
  TIMSK1 = B00000010;
  OCR1A = 0x00CF; // 3E = 4us, 7F = 8us, 9F = 10us, CF = 13us, 19F = 26us
}

void loop()
{

  if ((PINB && B00000100) == 0)
  {
    SET(3);
  }
  else
  {
    CLR(3);
  }

}

ISR (TIMER1_COMPA_vect)
{
  TOGGLE(6);
}

And it's still not working.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ " the Arduino control pin to the collector." Why? Show schematic of driver \$\endgroup\$ Commented Feb 15, 2021 at 23:29
  • \$\begingroup\$ Rb = 1k from output with Carrier AND data to base, if using 5V , IR LED = 1.3V @ 100mA needs Rc series = 3.5V/0.1 = 35 Ohms \$\endgroup\$ Commented Feb 15, 2021 at 23:44
  • \$\begingroup\$ @TonyStewartSunnyskyguyEE75 Well, turns out I thought the TIP41C was a N-type MOSFET, and was using pull-down resistors. Will use an IRFZ44n and update on the results. The other attempts without using a transistor should have worked though. \$\endgroup\$
    – Alex RD
    Commented Feb 16, 2021 at 0:19
  • \$\begingroup\$ Still, a schematic of how you connect things seems very necessary to our understanding of your system. Don't assume there's just one possible way to do it! \$\endgroup\$
    – mmmm
    Commented Feb 16, 2021 at 0:27
  • \$\begingroup\$ @mmmm Updated the post \$\endgroup\$
    – Alex RD
    Commented Feb 16, 2021 at 1:58

2 Answers 2

1
\$\begingroup\$

I am unsure what the issue was, but my initial logic was correct.

To troubleshoot it, I swapped microcontrollers from the Arduino Nano to the new Arduino Nano Every, swapped the IR LED to a new one (even though the old one was supposedly working) and re-wrote the code using the new Arduino (same logic but with the new Atmega4809 timer):

// Macros for easier bit manipulation
#define CLR(x) (VPORTB.OUT&=(~(1<<x)))
#define SET(x) (VPORTB.OUT|=(1<<x))
#define TOGGLE(x) (VPORTB.OUT^=(1<<x))

void setup()
{
  pinMode(5, OUTPUT); // IR LED output
  pinMode(9, INPUT); // VS1838b input sensor

  TCA0.SINGLE.PER = 0x00D2;
  TCA0.SINGLE.CMP1 = 0x00D2;
  TCA0.SINGLE.INTCTRL = B00100000;
  TCA0.SINGLE.CTRLA = B00000001;
}

void loop()
{
}

ISR(TCA0_CMP1_vect) {
    TCA0.SINGLE.INTFLAGS |= B00100000;
    if ((VPORTB.IN & B00000001) == 0)
    {
      TOGGLE(2);
    }
    else
    {
      CLR(2);
    }
}

This now works. Thanks everyone for your help!

\$\endgroup\$
0
\$\begingroup\$

IR is kind of like AM radio, kinda. You have a signal and a carrier, but a square wave thing. You and the signal and the carrier together. To transmit you need to do this backward. Although I managed to bit bang this with a PIC at one point, assume you cannot, and do not bother. You basically need to generate a carrier (38Khz) and and it together with your signal, ms pulses or fractions of. Assume that this is not always possible within an MCU, you have to evaluate each mcu to see if it can do it in some way.

For some you use the timer to generate a signal (you are not using this for interrupts to bit bang you are generating the signal itself) and then some let you gate the output based on another timer or something else. Some STM32's specifically have an IR timer which is essentially an and gate between two timers.

You might pull it off with a PWM, where you generate a 50% duty cycle sometimes and a 0% duty cycle sometimes, timing each.

Some you can pull this of with a SPI controller using the mosi sending some number of 0x55s and other values to generate the right number of clock cycles and then the right number of zeros to generate the gaps. This is probably the easier way to do it, if the MCU has a deep enough fifo or a way to dma and you have enough cycles and or memory to keep the fifo/dma fed.

Or you could just put a discrete and gate outside the mcu...

Then like any other led you feed this output signal into the IR led with a resistor to limit/control the voltage across the led.

If you do not have a scope you are going to find this extremely difficult. You may have to slow it all down by adjusting your divisors and then feed it into another mcu or raspberry pi or fpga board or something being used as a homebrew logic analyzer to examine the timing. good scopes are very inexpensive these days so it is worth getting a four channel if you plan to continue doing mcu work (this will cover uart, spi (two data line), i2c, and a number of other things)(learn how to read the protocols, do not use the software that tries to read them for you they fail as much as they succeed and make life much worse, these protocols are simple to read from the raw signals).

\$\endgroup\$
3
  • \$\begingroup\$ sbprojects.net/knowledge/ir/index.php this is my go-to site when I am doing IR work. I use it to figure out what IR protocols I am looking at with a particular remote...A scope helps for receiving but is not necessary been decoding them for very many years without a scope. But for transmitting it makes a huge difference. \$\endgroup\$
    – old_timer
    Commented Feb 16, 2021 at 4:40
  • \$\begingroup\$ interrupts complicate life only use them when needed. I so far have not needed. I did a repeater perhaps similar to yours, I had an old tv (one of the first with infrared) that did not use a standard protocol (it predated the standard protocols). I made a decoder repeater to translate a newer code to the older codes and could use a universal remote, worked great. now that I think about it it is quite possible I bit banged that one too but had much more memory and flash than you did and basically did nops and/or writes to I/O in asm. as in hundreds. today I would use an irtim in an stm32 \$\endgroup\$
    – old_timer
    Commented Feb 16, 2021 at 4:44
  • \$\begingroup\$ or a spi controller in just about any mcu (look at how they are doing a similar thing to control the WS2812B leds using spi, turning spi into a timed signal generator) \$\endgroup\$
    – old_timer
    Commented Feb 16, 2021 at 4:45

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.