Created
May 20, 2024 20:10
-
-
Save JarrettR/7c5bdf2491cffbd5753cb875f2f05b5c to your computer and use it in GitHub Desktop.
Convert LIRC config files to the ESP32 RMT peripheral boilerplate code.
This file contains 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
import re | |
import argparse | |
class LIRCConfigParser: | |
def __init__(self, file_path): | |
self.file_path = file_path | |
self.config = {} | |
self.header = None | |
self.one = None | |
self.zero = None | |
self.ptrail = None | |
self.gap = None | |
self._parse_file() | |
def _parse_file(self): | |
with open(self.file_path, 'r') as file: | |
content = file.read() | |
self._parse_timing_parameters(content) | |
remote_blocks = re.findall(r'begin remote(.*?)end remote', content, re.DOTALL) | |
for block in remote_blocks: | |
remote_name = re.search(r'name\s+(\S+)', block) | |
if remote_name: | |
remote_name = remote_name.group(1) | |
self.config[remote_name] = self._parse_remote_block(block) | |
def _parse_timing_parameters(self, content): | |
header = re.search(r'header\s+(\d+)\s+(\d+)', content) | |
if header: | |
self.header = (int(header.group(1)), int(header.group(2))) | |
one = re.search(r'one\s+(\d+)\s+(\d+)', content) | |
if one: | |
self.one = (int(one.group(1)), int(one.group(2))) | |
zero = re.search(r'zero\s+(\d+)\s+(\d+)', content) | |
if zero: | |
self.zero = (int(zero.group(1)), int(zero.group(2))) | |
ptrail = re.search(r'ptrail\s+(\d+)', content) | |
if ptrail: | |
self.ptrail = int(ptrail.group(1)) | |
gap = re.search(r'gap\s+(\d+)', content) | |
if gap: | |
self.gap = int(gap.group(1)) | |
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 | |
else: | |
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) | |
#define ON_OFF_CONTROL (GPIO_NUM_19) | |
#define PIN_INDICATOR (GPIO_NUM_5) | |
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)); | |
vTaskDelay(44/portTICK_RATE_MS); | |
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)); | |
vTaskDelay(2000/portTICK_RATE_MS); | |
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_config(&config)); | |
ESP_ERROR_CHECK(rmt_driver_install(config.channel, 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) { | |
send_on(); | |
} else { | |
send_off(); | |
} | |
vTaskDelay(1); | |
gpio_set_level(PIN_INDICATOR, 0); | |
vTaskDelay(40/portTICK_RATE_MS); | |
} | |
} | |
''' | |
def generate_c_code(config, output_path, timings): | |
with open(output_path, 'w') as file: | |
file.write(c_start) | |
if timings.header and timings.one and timings.zero and timings.ptrail and timings.gap: | |
file.write(f'#define PACKET_HEADER {{{{ {timings.header[0]}, 1, {timings.header[1]}, 0 }}}}\n') | |
file.write(f'#define ONE_BIT {{{{ {timings.one[0]}, 1, {timings.one[1]}, 0 }}}}\n') | |
file.write(f'#define ZERO_BIT {{{{ {timings.zero[0]}, 1, {timings.zero[1]}, 0 }}}}\n') | |
file.write(f'#define PACKET_TAIL {{{{ {timings.ptrail}, 1, {timings.gap}, 0 }}}}\n\n') | |
file.write(f'#define PACKET_ZERO_NIBBLE ZERO_BIT,ZERO_BIT,ZERO_BIT,ZERO_BIT\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"//0x{value}\n") | |
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") | |
else: | |
file.write(f" ZERO_BIT,\n") | |
file.write(f" PACKET_TAIL,\n") | |
file.write(f" RMT_END\n") | |
file.write(f"}};\n\n") | |
file.write(c_end) | |
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