如何使用回调函数优化ESP32串口指令处理代码以减少嵌套If并提升可读性
重构ESP32-CAM串口指令处理:用回调函数替代嵌套If
咱们先把核心需求理清楚:把原来嵌套在无限循环里、可读性拉胯的串口指令判断逻辑,改成用handle_for这种回调式的调用,让代码更清爽、后续加新指令也更方便。下面一步步来实现你想要的效果:
1. 先给回调函数定个统一类型
首先得定义回调函数的标准格式,这样handle_for才知道要接收什么样的函数作为参数。这里咱们先按无参数无返回值来定义,你可以根据实际需求调整:
// 定义回调函数类型,可根据需求修改参数/返回值 typedef void (*CommandCallback)(void);
2. 实现核心的handle_for函数
这个函数的职责很简单:对比输入数据和目标指令,匹配上就执行对应的回调。注意要用安全的字符串比较函数,还要确保输入是合法的C字符串(结尾有\0):
#include <string.h> // 指令匹配与回调执行核心函数 void handle_for(const char* input_data, const char* match_str, CommandCallback callback) { // 检查输入与匹配字符串是否完全一致 if (strcmp(input_data, match_str) == 0) { if (callback != NULL) { callback(); // 匹配成功,触发回调 } } }
3. 拆分LED控制逻辑到独立回调函数
把原来写在嵌套If里的LED开关逻辑拆出来,每个回调只做一件事,职责更清晰:
#include "driver/gpio.h" // 假设LED接在GPIO4,根据你的硬件配置自行修改 #define LED_GPIO_PIN GPIO_NUM_4 // LED初始化(建议在app_main开头调用) void led_init(void) { gpio_config_t io_conf = { .pin_bit_mask = (1ULL << LED_GPIO_PIN), .mode = GPIO_MODE_OUTPUT, .pull_up_en = GPIO_PULLUP_DISABLE, .pull_down_en = GPIO_PULLDOWN_DISABLE, .intr_type = GPIO_INTR_DISABLE }; gpio_config(&io_conf); gpio_set_level(LED_GPIO_PIN, 0); // 默认关闭LED } // 点亮LED的回调 void when_led_on(void) { gpio_set_level(LED_GPIO_PIN, 1); printf("LED turned ON\n"); } // 熄灭LED的回调 void when_led_off(void) { gpio_set_level(LED_GPIO_PIN, 0); printf("LED turned OFF\n"); }
4. 重构app_main里的串口处理循环
现在可以把原来的嵌套If替换成简洁的handle_for调用,同时保留“未匹配时返回接收长度”的逻辑:
void app_main(void) { // 初始化外设(串口初始化代码请根据你的配置自行补充) led_init(); uart_init(); // 假设你已经实现了串口初始化函数 char uartBuf[64]; // 接收缓冲区,大小按需调整 int recv_len; while (1) { // 读取串口数据,超时100ms recv_len = uart_read_bytes(UART_NUM_0, (uint8_t*)uartBuf, sizeof(uartBuf)-1, pdMS_TO_TICKS(100)); if (recv_len > 0) { uartBuf[recv_len] = '\0'; // 手动添加字符串结束符,确保strcmp能正常工作 // 检查指令是否以$开头 if (uartBuf[0] == '$') { const char* cmd_content = uartBuf + 1; // 去掉开头的$,取实际指令内容 int is_cmd_matched = 0; // 处理LED ON指令 if (strcmp(cmd_content, "led on\n") == 0) { handle_for(cmd_content, "led on\n", when_led_on); is_cmd_matched = 1; } // 处理LED OFF指令 else if (strcmp(cmd_content, "led off\n") == 0) { handle_for(cmd_content, "led off\n", when_led_off); is_cmd_matched = 1; } // 未匹配任何指令时,打印接收长度 if (!is_cmd_matched) { printf("Received length: %d\n", recv_len); } } } vTaskDelay(pdMS_TO_TICKS(10)); // 短暂延时,避免CPU占用过高 } }
5. 进阶优化:用指令映射表批量处理(可选)
如果后续要加更多指令,每次写handle_for会有点重复。可以把指令和回调做成一个映射数组,遍历处理,扩展性更强:
// 定义指令-回调映射结构体 typedef struct { const char* cmd_str; CommandCallback callback; } CommandMap; // 指令映射表:新增指令直接在这里加一行就行 CommandMap cmd_map[] = { {"led on\n", when_led_on}, {"led off\n", when_led_off}, // 示例:{"take_photo\n", when_take_photo} }; // 批量处理指令的函数 void process_commands(const char* input_data) { int cmd_count = sizeof(cmd_map) / sizeof(cmd_map[0]); int matched = 0; for (int i = 0; i < cmd_count; i++) { if (strcmp(input_data, cmd_map[i].cmd_str) == 0) { if (cmd_map[i].callback != NULL) { cmd_map[i].callback(); } matched = 1; break; } } if (!matched) { printf("No matched command\n"); } } // 然后在app_main里替换成: if (uartBuf[0] == '$') { const char* cmd_content = uartBuf + 1; process_commands(cmd_content); printf("Received length: %d\n", recv_len); }
这样后续加新指令完全不用改循环逻辑,只需要在映射表里加一行就行,可读性和扩展性都提升了一大截。
内容的提问来源于stack exchange,提问作者Citra Dewi
相关产品推荐
相关产品推荐

