Skip to content

Instantly share code, notes, and snippets.

Created May 20, 2024 20:10
Show Gist options
  • Save JarrettR/7c5bdf2491cffbd5753cb875f2f05b5c to your computer and use it in GitHub Desktop.
Save JarrettR/7c5bdf2491cffbd5753cb875f2f05b5c to your computer and use it in GitHub Desktop.
Convert LIRC config files to the ESP32 RMT peripheral boilerplate code.
import re
import argparse
class LIRCConfigParser:
def __init__(self, file_path):
self.file_path = file_path
self.config = {}
self.header = None = None = None
self.ptrail = None = None
def _parse_file(self):
with open(self.file_path, 'r') as file:
content =
remote_blocks = re.findall(r'begin remote(.*?)end remote', content, re.DOTALL)
for block in remote_blocks:
remote_name ='name\s+(\S+)', block)
if remote_name:
remote_name =
self.config[remote_name] = self._parse_remote_block(block)
def _parse_timing_parameters(self, content):
header ='header\s+(\d+)\s+(\d+)', content)
if header:
self.header = (int(, int(
one ='one\s+(\d+)\s+(\d+)', content)
if one: = (int(, int(
zero ='zero\s+(\d+)\s+(\d+)', content)
if zero: = (int(, int(
ptrail ='ptrail\s+(\d+)', content)
if ptrail:
self.ptrail = int(
gap ='gap\s+(\d+)', content)
if gap: = int(
def _parse_remote_block(self, block):
remote_config = {}
lines = block.splitlines()
key_section = False
for line in lines:
line = line.strip()
if line.startswith('begin codes'):
key_section = True
remote_config['codes'] = {}
elif line.startswith('end codes'):
key_section = False
elif key_section:
parts = line.split()
if len(parts) == 2:
key, value = parts
remote_config['codes'][key] = value
if ' ' in line:
key, value = line.split(None, 1)
remote_config[key] = value
return remote_config
def get_config(self):
return self.config
def get_remote_names(self):
return list(self.config.keys())
def get_remote(self, remote_name):
return self.config.get(remote_name, None)
c_start = '''
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/rmt.h"
#include "driver/gpio.h"
#include "esp_system.h"
#include "esp_log.h"
#define RMT_IO (GPIO_NUM_18)
static const char* TAG = "ir_transmitter";
#define RMT_END {{{ 0, 1, 0, 0 }}}
c_end = '''static void send_on()
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, on_key, sizeof(on_key) / sizeof(on_key[0]), true));
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, tail_key, sizeof(tail_key) / sizeof(tail_key[0]), true));
static void send_off()
//Off needs to be pressed twice
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, off_key, sizeof(off_key) / sizeof(off_key[0]), true));
ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0, off_key, sizeof(off_key) / sizeof(off_key[0]), true));
void app_main(void)
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_IO, RMT_CHANNEL_0);
// set count to 1us
config.clk_div = 80;
config.tx_config.carrier_en = true;
config.tx_config.carrier_freq_hz = 38000;
config.tx_config.carrier_duty_percent = 35;
ESP_ERROR_CHECK(rmt_driver_install(, 0, 0));
gpio_set_direction(ON_OFF_CONTROL, GPIO_MODE_INPUT);
gpio_set_pull_mode(ON_OFF_CONTROL, GPIO_PULLUP_ONLY);
gpio_set_direction(PIN_INDICATOR, GPIO_MODE_OUTPUT);
while (1) {
gpio_set_level(PIN_INDICATOR, 1);
if (gpio_get_level(ON_OFF_CONTROL) == 1) {
} else {
gpio_set_level(PIN_INDICATOR, 0);
def generate_c_code(config, output_path, timings):
with open(output_path, 'w') as file:
if timings.header and and and timings.ptrail and
file.write(f'#define PACKET_HEADER {{{{ {timings.header[0]}, 1, {timings.header[1]}, 0 }}}}\n')
file.write(f'#define ONE_BIT {{{{ {[0]}, 1, {[1]}, 0 }}}}\n')
file.write(f'#define ZERO_BIT {{{{ {[0]}, 1, {[1]}, 0 }}}}\n')
file.write(f'#define PACKET_TAIL {{{{ {timings.ptrail}, 1, {}, 0 }}}}\n\n')
for remote_name, remote_config in config.items():
file.write(f"// Remote: {remote_name}\n")
if 'codes' in remote_config:
for key, value in remote_config['codes'].items():
file.write(f"static const rmt_item32_t {key.lower()}_key[] = {{\n")
file.write(f" PACKET_HEADER,\n")
for nibble in [value[i:i+4] for i in range(2, len(value), 4)]:
file.write(f" //{nibble}\n")
for bit in bin(int(nibble, 16))[2:].zfill(4):
if bit == '1':
file.write(f" ONE_BIT,\n")
file.write(f" ZERO_BIT,\n")
file.write(f" PACKET_TAIL,\n")
file.write(f" RMT_END\n")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Parse LIRC config files and generate a minimal C file.')
parser.add_argument('file_path', nargs='?', default='lircd.conf', help='Path to the LIRC config file')
parser.add_argument('--output', '-o', default='main.c', help='Output C file')
args = parser.parse_args()
lirc_parser = LIRCConfigParser(args.file_path)
config = lirc_parser.get_config()
generate_c_code(config, args.output, lirc_parser)
print(f"Generated C code has been written to {args.output}")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment