I have already described a project based on the AVR-Stick hardware. The next thing I wanted to try with that hardware is to create an infrared receiver and logger. In fact I wanted to emulate the dongle I described some time ago. But to emulate it I needed to know the protocol that is used. The easiest way to find out what protocol is used is to analyze signals with an oscilloscope of course but I didn't have one. So first of all I needed to implement a signal logging functionality.
In fact signals sent by remote controls are short so it should be possible to record them into ATtiny85's memory (I have allocated 100 bytes from 256 total to record up to 50 pulses)
In fact signals sent by remote controls are short so it should be possible to record them into ATtiny85's memory (I have allocated 100 bytes from 256 total to record up to 50 pulses)
Hardware
To receive infrared signals different kinds of Vishay IR modules are usually used. Such modules do all the job necessary to demodulate and to amplify IR signal so that it can be directly sent to a microcontroller for furtherer analysis. I didn't have any of those modules but instead I had a small assembled IR module salvaged from an old videocassette recorder.
Project parts |
on that board there is a NEC uPC2800 preamplifier chip mounted together with a photodiode and all necessary passive elements. Module requires +5V, GND and has one output line which makes it as a perfect replacement for a Vishay module. I have cut modules PCB in halve, resoldered its capacitor on the other side and mounted it onto the AVR Stick connecting its output to the PB4 of the stick:
The final hardware |
Software
Bootloader
As with my previous AVR Stick project I flashed the bootloader first. But no matter how much I tried it refused to flash my own firmware onto AVR Stick on my Windows host. Despite the fact that I was using the same hardware and software it just failed to operate correctly for some reason. Most probably the bootloader software is buggy at the moment (I'm using USBaspLoader-tiny85.2012-05-13) and it has some timing issues. Workaround was to install AVR development environment on a Linux host:- Install libusb-1.0.0-dev
- Install gcc-avr avr-libc bison flex
- Download avrdude sources
- Download bootloader sources
- patch avrdude/usbasp.c with the bootloader/avrdude.patch -
cd avrdude-5.11; patch usbasp.c < avrdude.patch - ./configure
- make
- sudo cp avrdude-5.11/avrdude /usr/bin/
- sudo cp avrdude-5.11/avrdude.conf /usr/local/etc
Firmware
The firmware can be used to log received IR signal timings and to decode a Technisat Skystar remote control commands. It should be possible to decode other remote controls commands too by reimplementing the decodeBuffer method. To start with signal decoding I needed to know the protocol used by that specific remote control so first of all I've implemented logging functionality.Logging
To activate logging the firmware shall be compiled with the NODECODE define:
Additionally it might be necessary to adjust the following defines:
These defines are set for the particular remote control to reduce false triggering. START_LEN specifies the minimum start bit length and MIN_LEN specifies minimum length of a pulse low or high. When input signal changes its state a timer is started. By next state change the timer value is read and this is where these digits come from. Timer's prescaler is set to 1024 and as far as microcontroller runs at 16.5MHz then the timer increments each 1024/16500000 seconds which is 62uS. So these digits are converted into micro seconds by multiplying by 62. This value will be used later in a awk script to properly calculate and show signal's time span.
Recording is started after receiving of a start bit and is finished when no input state change detected before the timer is overflown (it's approximately 16mS because I'm using 8 bit timer). If a state change detected with a duration less then MIN_LEN before the timer is overflown then such recording is discarded. After recording is taken it will be transfered to a host computer per USB. It's enough to open any text editor and press a button an a remote control to get the recorded signal. For example pressing OK button on my remote results in the following text:
0 denotes start of a signal block and signal highs are denoted by adding 128 to the value. So the maximum pulse's low or high period that can be logged is 127 (approximately 8mS). As you can see the start bit length is 32 and that's why the START_LEN is set to 29. I allocated 100 bytes buffer to record the signal which can store up to 50-1 pulses. Buffer's length still can be increased if needed because the firmware uses 186 bytes of RAM from total 256.
Note: During development I reflashed the stick many times and I had to connect/disconnect it constantly. To make it more convenient I used a USB adapter which is much more easier to connect/disconnect:
And pressing Play button 4 times on the wdtv live remote control results in the following:
the start bit is skipped and the first bit is the toggle bit. So far so good. Next I recorded buttons from 0 to 9:
and decoding them resulted in:
It appears to be that different number of bits transferred for these buttons and OK button and it is still not clear how do I properly decode the stream (please drop me a note if you know better). The current solution is to take only the last 6 bits into account that works perfectly beside the fact that there are 2 buttons pairs with intersecting codes.
Next I've manually created a file with decoded ir codes, key codes to be sent to a host and remote control button names:
And the final step was to convert this description into a lookup table. Again awk is here to help:
where createlookup.awk is:
After the firmware was flashed I've found out that each command is sent 3 times by a remote control so I've added a simple counter to the main.c to discard two from 3 received commands. After this last change was flashed back everything was up and running. I disconnected the old dongle and my router was reacting the same as before to infrared commands (I'm using triggerhappy daemon to start different actions as a reaction to key presses)
PS: I've documented everything so scrupulously mainly for myself to not forget how it was done.
make USERDEFS=-DNODECODE
Additionally it might be necessary to adjust the following defines:
#define START_LEN 29 // start bit length. Recording is started after start bit #define MIN_LEN 9 // min length of a pulse. If less then recording is stopped
These defines are set for the particular remote control to reduce false triggering. START_LEN specifies the minimum start bit length and MIN_LEN specifies minimum length of a pulse low or high. When input signal changes its state a timer is started. By next state change the timer value is read and this is where these digits come from. Timer's prescaler is set to 1024 and as far as microcontroller runs at 16.5MHz then the timer increments each 1024/16500000 seconds which is 62uS. So these digits are converted into micro seconds by multiplying by 62. This value will be used later in a awk script to properly calculate and show signal's time span.
Recording is started after receiving of a start bit and is finished when no input state change detected before the timer is overflown (it's approximately 16mS because I'm using 8 bit timer). If a state change detected with a duration less then MIN_LEN before the timer is overflown then such recording is discarded. After recording is taken it will be transfered to a host computer per USB. It's enough to open any text editor and press a button an a remote control to get the recorded signal. For example pressing OK button on my remote results in the following text:
0 32 150 31 151 31 152 30 138 17 152 30 152 17 138 17 138 17
0 denotes start of a signal block and signal highs are denoted by adding 128 to the value. So the maximum pulse's low or high period that can be logged is 127 (approximately 8mS). As you can see the start bit length is 32 and that's why the START_LEN is set to 29. I allocated 100 bytes buffer to record the signal which can store up to 50-1 pulses. Buffer's length still can be increased if needed because the firmware uses 186 bytes of RAM from total 256.
Note: During development I reflashed the stick many times and I had to connect/disconnect it constantly. To make it more convenient I used a USB adapter which is much more easier to connect/disconnect:
USB adapter |
Plot the signal
To present received signal on a graph I've written a script that parses recorded values and calls gnuplot to create a graph. The script is an awful mix of bash, awk and gnuplot scripts:alex@xubuntu:~$ cat plotsignals.sh #!/bin/bash TC=62 # time constant GRAPH_HEIGHT=125 GRAPH_WIDTH=800 printParseScript() { cat <<\EOFAWK BEGIN {counter=0} { if ($1 ~ /^$/) next; if ($1 ~ /^0$/) { if (counter != 0) { print counter*tc " " 0; print counter*tc+3000 " " 0; } print ""; counter=0; print "-3000 0"; } else if ($1>127) { print counter*tc " " 0; counter=counter-128; } else { print counter*tc " " 1 } counter=counter+$1; } END {print counter*tc " " 0; print counter*tc+3000 " " 0} EOFAWK } printPlotScript() { local blocks=$(awk 'BEGIN {counter=0}{if ($1 ~ /^$/) counter++} END{print counter}' $1) local script counter [ $blocks -gt 0 ] || return script="set term wxt size ${GRAPH_WIDTH},$((GRAPH_HEIGHT*blocks))" script="${script}\nset yrange [-0.1:1.1]" [ -n "$2" ] && { script="${script}\nset xrange [:$2]" } script="${script}\nset multiplot layout ${blocks},1" counter=0 while [ $counter -lt $blocks ]; do script="${script}\nunset key" script="${script}\nplot '$1' every :$((blocks+1)):0:${counter} with steps" counter=$((counter+1)) done script="${script}\nunset multiplot" echo -e "$script" } GPFILE=/tmp/$(basename $1).gp awk -v tc=$TC "$(printParseScript)" $1 > $GPFILE && { /usr/bin/gnuplot -persist <(printPlotScript $GPFILE $2) }Script inverts the signal and creates a plot for each signal block in an input file. For example I recorded button OK pressed 4 times on a Technisat remote control into a text file and called the plotsignals.sh with that text file name as a parameter:
Technisat remote control OK Button pressed 4 times |
And pressing Play button 4 times on the wdtv live remote control results in the following:
WDTV Live remote control Play button pressed 4 times |
Decoding Technisat infrared protocol
Analyzing signal graphs (check the graph above for the OK button) I realized that this is some kind of a manchester encoding but it wasn't the RC5. There is a start bit and a toggle bit that is followed by number of bits. I'm using a very simple principal to decode the bit stream - every long pulse's low denotes a state transition from 0 to 1 and every long pulse's high denotes a state transition from 1 to 0. To test if this decoding scheme works and to create a lookup table I've written an additional awk script:alex@xubuntu:~$ cat ~/bin/decodesignals.sh #!/bin/bash printDecodeScript() { cat <<\EOFAWK function abs(value) { return (value<0?-value:value); } BEGIN {SS=10;SF=17;LS=23;LF=30;EPS=2;curbit=0;newblock=0;} { if ($1 ~ /^$/) next; if ($1 ~ /^0$/) { newblock=1; curbit=0; if (bits != "") print bits; next; } if (newblock == 1) { newblock=0; bits=""; next; } time = $1; if (time>127) time=time-128; if (abs(time-SS)<=EPS) { bits=bits""curbit; next; } else if (abs(time-SF)<=EPS) { next; } if (abs(time-LS)<=EPS) { curbit = 1; bits=bits""curbit; } else if (abs(time-LF)<=EPS) { curbit = 0; bits=bits""curbit; } else print time; } END {print bits} EOFAWK } awk "$(printDecodeScript)" $1Feeding the script with the same data file I used to create signal graphs I got the following result:
alex@xubuntu:~$ decodesignals.sh ~/Downloads/button_ok.txt 101010010111 001010010111 101010010111 001010010111
the start bit is skipped and the first bit is the toggle bit. So far so good. Next I recorded buttons from 0 to 9:
Buttons from 0 to 9 |
alex@xubuntu:~$ decodesignals.sh ~/Downloads/button_0-9.txt 1010000000 01010000001 1010000010 01010000011 1010000100 01010000101 1010000110 01010000111 1010001000 01010001001
It appears to be that different number of bits transferred for these buttons and OK button and it is still not clear how do I properly decode the stream (please drop me a note if you know better). The current solution is to take only the last 6 bits into account that works perfectly beside the fact that there are 2 buttons pairs with intersecting codes.
Next I've manually created a file with decoded ir codes, key codes to be sent to a host and remote control button names:
alex@xubuntu:~$ cat Downloads/decoded_buttons.txt 000000 KEY_0 0 000001 KEY_1 1 000010 KEY_2 2 000011 KEY_3 3 000100 KEY_4 4 000101 KEY_5 5 000110 KEY_6 6 000111 KEY_7 7 001000 KEY_8 8 001001 KEY_9 9 001100 KEY_W PWR/TOGGL 001101 KEY_M MUTE 001111 KEY_I HELP/INFO 010000 KEY_RIGHT VOLUP #010000 X UP 010001 KEY_LEFT VOLDOWN #010001 X DOWN 010010 KEY_F2 MENU 010011 KEY_F9 TV/RADIO 010101 X LEFT 010110 X RIGHT 010111 KEY_ENTER OK 100000 KEY_UP CHAN_UP 100001 KEY_DOWN CHAN_DOWN 100010 KEY_F PREV 100011 KEY_S INPUTAB 100110 KEY_L SLEEP 101001 KEY_P STOP 101011 KEY_F5 RED 101100 KEY_F6 GREEN 101101 KEY_F7 YELLOW 101110 KEY_F8 BLUE 101111 KEY_E GUIDE 110110 KEY_O OPTION 111000 KEY_V EXTERNAL 111100 KEY_T TELETEXT
And the final step was to convert this description into a lookup table. Again awk is here to help:
alex@xubuntu:~$ awk -f /home/alex/Downloads/createlookup.awk ~/Downloads/decoded_buttons.txt > lookup.c
where createlookup.awk is:
alex@xubuntu:~$ cat /home/alex/Downloads/createlookup.awk function bin2dec(value) { result=0; pow2bit=1; while (length(value)) { bitChar = substr(value, length(value)); if (bitChar == "1") result = result + pow2bit; pow2bit = pow2bit * 2; value = substr(value, 1, length(value)-1) } return result } BEGIN {prev=0;} { if ($1 ~ /^#/) next; cur = bin2dec($1); while (prev<cur) { print "0,"; prev = prev+1; } print $2",\t\t/*"$3"*/" prev = prev+1; } END { while (prev<64) {print "0,"; prev=prev+1}}
After the firmware was flashed I've found out that each command is sent 3 times by a remote control so I've added a simple counter to the main.c to discard two from 3 received commands. After this last change was flashed back everything was up and running. I disconnected the old dongle and my router was reacting the same as before to infrared commands (I'm using triggerhappy daemon to start different actions as a reaction to key presses)
The final product |
Still live: temperature logger and IR dongle connected to a USB hub |
PS: I've documented everything so scrupulously mainly for myself to not forget how it was done.
No comments:
Post a Comment