IR remote example

The following code shows one way to read an infrared remote control device (the sort used in TVs and stereo systems).

### SETUP

The device used is a SFH5110 (IR Receiver for remote control, carrier 38 kHz).
Pin 1 (left from front) may be connected to any spare gpio. Here it's connected via a 4K7 current limiting resistor. This isn't really needed as the device has an internal 23K resistor in-line. It does no harm though.
Pin 2 should be connected to a Pi ground pin.
Pin 3 should be connected to a Pi 5V pin.

Here pin 1 to gpio7 (P1-26) via a 4K7 resistor, pin 2 to ground (P1-14), and pin 3 to 5V (P1-2).

### CODE

`#include <stdio.h>
#include <pigpio.h>
#define IR_PIN 7
#define OUTSIDE_CODE 0
#define INSIDE_CODE 1
#define MIN_MESSAGE_GAP 3000
#define MAX_MESSAGE_END 3000
#define MAX_TRANSITIONS 500
/*
using the FNV-1a hash
from http://isthe.com/chongo/tech/comp/fnv/#FNV-param
*/
#define FNV_PRIME_32 16777619
#define FNV_BASIS_32 2166136261U
static volatile uint32_t ir_hash = 0;
typedef struct
{
int state;
int count;
int level;
uint16_t micros[MAX_TRANSITIONS];
} decode_t;
/* forward declarations */
void alert(int gpio, int level, uint32_t tick);
uint32_t getHash(decode_t * decode);
void updateState(decode_t * decode, int level, uint32_t micros);
int main(int argc, char * argv[])
{
if (gpioInitialise()<0)
{
return 1 ;
}
/* IR pin as input */
gpioSetMode(IR_PIN, PI_INPUT);
/* 5ms max gap after last pulse */
gpioSetWatchdog(IR_PIN, 5);
/* monitor IR level changes */
gpioSetAlertFunc(IR_PIN, alert);
while (1)
{
if (ir_hash)
{
/* non-zero means new decode */
printf("ir code is %u\ ", ir_hash);
ir_hash = 0;
}
gpioDelay(100000); /* check remote 10 times per second */
}
gpioTerminate();
}
void alert(int gpio, int level, uint32_t tick)
{
static int inited = 0;
static decode_t activeHigh, activeLow;
static uint32_t lastTick;
uint32_t diffTick;
if (!inited)
{
inited = 1;
activeHigh.state = OUTSIDE_CODE; activeHigh.level = PI_LOW;
activeLow.state = OUTSIDE_CODE; activeLow.level = PI_HIGH;
lastTick = tick;
return;
}
diffTick = tick - lastTick;
if (level != PI_TIMEOUT) lastTick = tick;
updateState(&activeHigh, level, diffTick);
updateState(&activeLow, level, diffTick);
}
void updateState(decode_t * decode, int level, uint32_t micros)
{
/*
We are dealing with active high as well as active low
remotes. Abstract the common functionality.
*/
if (decode->state == OUTSIDE_CODE)
{
if (level == decode->level)
{
if (micros > MIN_MESSAGE_GAP)
{
decode->state = INSIDE_CODE;
decode->count = 0;
}
}
}
else
{
if (micros > MAX_MESSAGE_END)
{
/* end of message */
/* ignore if last code not consumed */
if (!ir_hash) ir_hash = getHash(decode);
decode->state = OUTSIDE_CODE;
}
else
{
if (decode->count < (MAX_TRANSITIONS-1))
{
if (level != PI_TIMEOUT)
decode->micros[decode->count++] = micros;
}
}
}
}
int compare(unsigned int oldval, unsigned int newval)
{
if (newval < (oldval * 0.75)) {return 1;}
else if (oldval < (newval * 0.75)) {return 2;}
else {return 4;}
}
uint32_t getHash(decode_t * decode)
{
/* use FNV-1a */
uint32_t hash;
int i, value;
if (decode->count < 6) {return 0;}
hash = FNV_BASIS_32;
for (i=0; i<(decode->count-2); i++)
{
value = compare(decode->micros[i], decode->micros[i+2]);
hash = hash ^ value;
hash = (hash * FNV_PRIME_32);
}
return hash;
}
`

### BUILD

`cc -o ir_remote ir_remote.c -lpigpio -lrt -lpthread
`

### RUN

`sudo ./ir_remote`

A hash code is formed from the level transitions detected during a remote key press. This is likely to be unique over multiple remotes and keys.

While the program is running you can capture the waveform using the notification feature built in to pigpio. Issue the following commands on the Pi.

`pigs no
pig2vcd </dev/pigpio0 >ir.vcd &
pigs nb 0 0x80 # set bits for gpios 7 (0x80)
`

Press a few different remotes and keys. Then enter

`pigs nc 0`

The file ir.vcd will contain the captured waveform, which can be viewed using GTKWave.

Overview

Remote A typical waveform

Remote B typical waveform