Last active
April 26, 2025 21:27
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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