Let’s start with the main part of this project, the development of a receiver and transmitter with and Arduino M0 board. When I started this project I didn’t know anything about IR protocols and how they work, so I thought to code a simple oscilloscope function to see some waveforms as a first step. The sketch in Arduino is very easy and consists of a loop of 4 seconds where a digital pin with a photodiode connected is being red repeatedly and plotted on screen thanks to a little program written with Processing.
This is the Arduino code used for this purprose:
The result is something like this:
At the beginning an analog pin was used but due to the high read time requested by the analogRead() function, about 208us, I switched to digital pin and digitalRead() function which takes about 1us for a reading. Moreover, the output signal of TSOP2238 is intrinsically digital, so if we use this one instead of the photodiode, digitalRead() function is certainly a better choice. In this case, the output rate is limited by Arduino serial communication rate, which takes hundreds of us for each transmission, even at the maximum baud rate of 115200bps, decreasing the effective reachable resolution.
Due to this high latency, it’s impossible to detect the PWM oscillation at the carrier frequency with Arduino. Maybe it would be using an array stored in the main memory, but in this case only few thousands of values can be stored (so, if a digitalRead() takes 1us, just few milliseconds would be recorded) and then sent through the serial port. But in this case, with this “burst” technique, there would be a lack of time between two consecutive bursts due to the time needed by the serial communication.
So, the carrier frequency check has been done only in a second moment using a digital oscilloscope in the laboratory of the university.
Now let’s analyze more deeply the code written to implement a receiver which must be able to automatically recognize and decode a protocol between those implemented.
The code is organized in classes:
The protocol recognition is based on the start frame duration, which is different for each one of them, and is performed by a function of the receiver class. Then the proper protocol object is used to perform the decoding phase. Decoding functions are different from protocol to protocol, either for simplicity and for didactic finalities.
For example, to decode the Philips protocol, which is based on a Manchester coding, an input pin is red at a regular interval equal to the bit-time but shifted inside the first half part of this period. Depending on the value, a 0 or 1 is decoded.
For NEC and Sony protocols it’s possible to discriminate a logical 1 from a logical 0 looking at the duration of the pause (for NEC) or the pulse (Sony). In these cases, an edge sensitive function has been used to calculate the time interval of interest.
This first part has been developed quite easily, without strange bugs or difficulties.
Some more problems have been found in the transmitter part of the project, with many revisions needed to work properly. The first implementation used the delayMicroseconds() function to simulate the PWM behavior of the carrier signal, turning on and off the digital output pin (set it HIGH or LOW).
A variant to this first approach uses a while() cycle, in which the output pin had to stay HIGH or LOW for a certain amount of time based on the chosen duty cycle.
These strategies worked well with two devices I have at home, both using NEC protocol. Unfortunately, in a subsequent test with a Sony TV it didn’t work in any way, even changing a little the timings used to reproduce the desired duty cycle.
This led to other tests and internet researches until I found some websites that suggested using Arduino built in timers for a more precise PWM wave shape. In fact delayMicroseconds() function has a poor precision of some us, which is ok for the bitrate but it’s too poor for an accurate PWM modulation of a signal at 40KHz and similar.
At this point I decided to use timers of Arduino, so I had to search some tutorial and study the proper chapters - 30 and 31 - of the Atmel sam-d21 datasheet. This has been the most challenging part of the hardware project because there are very few examples of code for my M0 board. Most of them are coded for Arduino Uno, which uses different register names and a different configuration. Moreover, it was my first time programming a microcontroller going so deeply and setting his hardware registers, not really a user-friendly operation for someone at the beginning. Even the Atmel datasheet is not very useful at a first read, because there aren’t code examples that explain how to implement what is written and sometimes there aren’t neither all the keyword and register names you can use to program it.
Most of the keywords or functions I used in my timer class don’t exist in the entire datasheet (performing a CTRL+F, no matches are found).
Fortunately, I finally found a useful tutorial that I used as a base for my proposals, changing it a little to fit my project and to stabilize it, because at the typical frequencies used in this project, that code sometimes failed, freezing the program execution.
In my timer implementation, I used the MFRQ wavegen option and Timer Counter Compare Channels (TC CC) 0 and 1 as mark points for interrupts handling to reproduce a PWM behaviour.
Max is the maximum value the counter can reach, in this case a 16 bit timer can reach the value of 216-1, increasing by one at each clock cycle. CC0 register has the top value which is computed to match the desired frequency, CC1 register has the value that matches the desired Duty Cycle - for example, for a duty cycle of 50%, CC1 is equal to CC0/2.
This is the main part of Timer class:
Finally, a new test on the Sony TV with this improvement led to the desired behavior, recognizing correctly all the transmitted commands. Maybe this TV has a more sensitive hardware/software and requires a “cleaner” waveform to work as expected.
Here a piece of code as example:
Now that the core part works fine, the last step is to add the HC-05 module, such that you can use Arduino even in wireless mode with a laptop, smartphone or anything else thanks to the Bluetooth connection. This is necessary also for the next Android app development.
Fortunately this upgrade has been quite easy to setup, it just needed the change of the input/output serial port and a little configuration of the HC-05. Only the configuration has been a little tricky because my module didn’t enter in the full-admin mode. I followed the instructions found online but there was no way to made it work, so I used the partial-admin mode which is good enough for change the baud rate from default value of 9600bps to 115200bps for a faster communication speed.
At this point a new strange problem arose: NEC protocol stopped working, even if something was still transmitted (IR Leds blinked). This only happens using Bluetooth. There is a file, mySerial.hpp, where a preprocessor directive specifies if SerialUSB or Serial5 has to be used, defining the variable mySerialPort as SerialUSB or Serial5 depending if you want to use USB or Bluetooth. If USB is selected, everything works fine, but with Serial5 (Bluetooth) I had to remove some prints from the NEC function codificaDatoDaTrasmettere() to makes it work. Fujitsu and Sony work well in both cases. I didn’t understood the reason of this very strange behavior but it’s solved moving away some lines of code.
This concludes the hardware part with Arduino. C++ classes should be modular enough for a future improvement if you want to add other protocols not yet implemented.
Just a couple of things: