Skip to content

Instantly share code, notes, and snippets.

@jvanderberg
Last active April 26, 2025 21:27
Show Gist options
  • Save jvanderberg/5648712f63fccc94cfc08981b781de12 to your computer and use it in GitHub Desktop.
Save jvanderberg/5648712f63fccc94cfc08981b781de12 to your computer and use it in GitHub Desktop.
Pi Pico C SDK example of reading IR codes from a 'standard' 44 key IR LED remote.
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"
#include "stdio.h"
#include "pico/util/queue.h"
#define IR_PIN 16
#define BRIGHTNESS_UP 0x3A
#define BRIGHTNESS_DN 0xBA
#define PLAY 0x82
#define POWER 0x02
#define RED1 0x1A
#define GREEN1 0x9A
#define BLUE1 0xA2
#define WHITE1 0x22
#define RED2 0x2A
#define GREEN2 0xAA
#define BLUE2 0x92
#define WHITE2 0x12
#define RED3 0x0A
#define GREEN3 0x8A
#define BLUE3 0xB2
#define WHITE3 0x32
#define RED4 0x38
#define GREEN4 0xB8
#define BLUE4 0x78
#define WHITE4 0xF8
#define RED5 0x18
#define GREEN5 0x98
#define BLUE5 0x58
#define WHITE5 0xD8
#define RED_UP 0x28
#define GREEN_UP 0xA8
#define BLUE_UP 0x68
#define QUICK 0xE8
#define RED_DN 0x08
#define GREEN_DN 0x88
#define BLUE_DN 0x48
#define SLOW 0xC8
#define DIY1 0x30
#define DIY2 0xB0
#define DIY3 0x70
#define AUTO 0xF0
#define DIY4 0x10
#define DIY5 0x90
#define DIY6 0x50
#define FLASH 0xD0
#define JUMP3 0x20
#define JUMP7 0xA0
#define FADE3 0x60
#define FADE7 0xE0
volatile uint32_t ir_data = 0;
volatile int bit_index = 0;
volatile absolute_time_t last_fall_time;
queue_t ir_queue;
void gpio_callback(uint gpio, uint32_t events)
{
if (gpio != IR_PIN || !(events & GPIO_IRQ_EDGE_FALL))
{
return;
}
absolute_time_t now = get_absolute_time();
int64_t dt = absolute_time_diff_us(last_fall_time, now);
last_fall_time = now;
// The remote sends an 'attention' or frame reset which is a long pulse, which is ~9ms, then a short pulse of ~4.5ms,
// which adds up to ~13ms between falling edges. So here we just filter for something in about the right range. This signals
// that we are starting a new frame, and we need to reset the bit index and clear the ir_data.
if (dt > 10000 && dt < 20000)
{
bit_index = 31;
ir_data = 0;
}
else if (bit_index >= 0)
{
// Each bit: pulse ~562us, then space, which is either ~562us or ~1690us, so a
// low to low spaces is 1124us for a 0, or 2252us for a 1 see: https://www.vishay.com/docs/80071/dataform.pdf (page 2)
// So if dt is > 1700us, it's a 1, otherwise it's a 0 (1700 is roughtly half way between and a nice round number)
if (dt > 1700)
{
// Long space → "1" bit
ir_data |= (1UL << bit_index);
}
else
{
// Short space → "0" bit (already zeroed)
}
bit_index--;
if (bit_index == -1)
{
// We've gotten all 32 bits, they are in the format 0xAABBCCDD, where AA is the address which is always
// 0x00, BB is the inverse of AA, CC is the command, and DD is the inverse of CC.
// So for example, POWER is 0x00FF02FD. We verify that 0xFF == ~0x00 and 0x02 == ~0xFD, and then we can just take the
// command byte (0x02) and send it to the queue.
// Technically we could just verify that the address is 0x00 and the inverst is 0xFF, but who knows, maybes
// some day somebody will use the address bits.
uint8_t *data = (uint8_t *)&ir_data;
// check to make sure that the first two bytes equal the second two bytes inverted
if (data[2] == (uint8_t)~data[3] && data[1] == (uint8_t)~data[0])
{
uint8_t code = data[1];
queue_try_add(&ir_queue, &code);
}
}
}
}
int main()
{
stdio_init_all();
gpio_init(IR_PIN);
gpio_set_dir(IR_PIN, GPIO_IN);
gpio_pull_up(IR_PIN);
gpio_set_irq_enabled_with_callback(IR_PIN, GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
queue_init(&ir_queue, sizeof(uint8_t), 10);
uint8_t received_code;
while (1)
{
while (queue_try_remove(&ir_queue, &received_code))
{
switch (received_code)
{
case BRIGHTNESS_UP:
printf("Brightness Up\n");
break;
case BRIGHTNESS_DN:
printf("Brightness Down\n");
break;
case PLAY:
printf("Play\n");
break;
case POWER:
printf("Power\n");
break;
case RED1:
printf("Red 1\n");
break;
case GREEN1:
printf("Green 1\n");
break;
case BLUE1:
printf("Blue 1\n");
break;
case WHITE1:
printf("White 1\n");
break;
case RED2:
printf("Red 2\n");
break;
case GREEN2:
printf("Green 2\n");
break;
case BLUE2:
printf("Blue 2\n");
break;
case WHITE2:
printf("White 2\n");
break;
case RED3:
printf("Red 3\n");
break;
case GREEN3:
printf("Green 3\n");
break;
case BLUE3:
printf("Blue 3\n");
break;
case WHITE3:
printf("White 3\n");
break;
case RED4:
printf("Red 4\n");
break;
case GREEN4:
printf("Green 4\n");
break;
case BLUE4:
printf("Blue 4\n");
break;
case WHITE4:
printf("White 4\n");
break;
case RED5:
printf("Red 5\n");
break;
case GREEN5:
printf("Green 5\n");
break;
case BLUE5:
printf("Blue 5\n");
break;
case WHITE5:
printf("White 5\n");
break;
case RED_UP:
printf("Red Up\n");
break;
case GREEN_UP:
printf("Green Up\n");
break;
case BLUE_UP:
printf("Blue Up\n");
break;
case QUICK:
printf("Quick\n");
break;
case RED_DN:
printf("Red Down\n");
break;
case GREEN_DN:
printf("Green Down\n");
break;
case BLUE_DN:
printf("Blue Down\n");
break;
case SLOW:
printf("Slow\n");
break;
case DIY1:
printf("DIY 1\n");
break;
case DIY2:
printf("DIY 2\n");
break;
case DIY3:
printf("DIY 3\n");
break;
case AUTO:
printf("Auto\n");
break;
case DIY4:
printf("DIY 4\n");
break;
case DIY5:
printf("DIY 5\n");
break;
case DIY6:
printf("DIY 6\n");
break;
case FLASH:
printf("Flash\n");
break;
case JUMP3:
printf("Jump 3\n");
break;
case JUMP7:
printf("Jump 7\n");
break;
case FADE3:
printf("Fade 3\n");
break;
case FADE7:
printf("Fade 7\n");
break;
default:
break;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment