#include "RS-485-SP3485EEN.h" #include #include "MQTT_ESP.h" #include "STATUS_LED.h" #include "MODBUS_ESP.h" #include "cJSON.h" #define TAG "RS485_DRIVER" // Timeout threshold for UART = number of symbols (~10 tics) with unchanged state on receive pin #define ECHO_READ_TOUT (3) // 3.5T * 8 = 28 ticks, TOUT=3 -> ~24..33 ticks // ---------------------------- // 通道数组定义 // ---------------------------- rs485_channel_t rs485_channels[] = { {RS_485_SP3485EEN_UART_PORT, RS_485_SP3485EEN_DI_PIN, RS_485_SP3485EEN_RO_PIN, RS_485_SP3485EEN_DE_RE_PIN, "RS485-1"}, {RS_485_SP3485EEN_2_UART_PORT, RS_485_SP3485EEN_2_DI_PIN, RS_485_SP3485EEN_2_RO_PIN, RS_485_SP3485EEN_2_DE_RE_PIN, "RS485-2"}}; // ============================ // UART 发送函数 // ============================ void rs485_send(uart_port_t uart_num, const uint8_t *data, size_t len) { if (data == NULL || len == 0) { ESP_LOGW(TAG, "RS485发送: 数据为空"); return; } // 清空 RX,防止残留帧污染 uart_flush_input(uart_num); // 发送数据 int written = uart_write_bytes(uart_num, (const char *)data, len); if (written < 0) { ESP_LOGE(TAG, "UART%d 发送写入错误 (%d)", uart_num, written); return; } if ((size_t)written != len) { ESP_LOGW(TAG, "UART%d 发送部分数据 (%d/%d)", uart_num, written, len); } // RS485 半双工模式下,uart_write_bytes 会自动等待发送完成 // 这里只需要等待一个额外的延时确保所有数据都已发送完成 // 根据波特率和字节数计算传输时间(11位/字节:1起始位+8数据位+1停止位+1奇偶校验位) uint32_t transmit_time_ms = (len * 11 * 1000) / (BAUD_RATE > 0 ? BAUD_RATE : 1); vTaskDelay(pdMS_TO_TICKS(transmit_time_ms + 5)); // Modbus RTU 3.5T 帧间静默(保留短延时) vTaskDelay(pdMS_TO_TICKS(5)); ESP_LOGI(TAG, "UART%d 发送完成 (%d 字节)", uart_num, written); } // ============================ // UART 接收函数 // ============================ int rs485_receive(uart_port_t uart_num, uint8_t *buffer, size_t buf_size, uint32_t timeout_ms) { if (buffer == NULL || buf_size == 0) { ESP_LOGW(TAG, "RS485接收: 缓冲区无效"); return -1; } int len = uart_read_bytes(uart_num, buffer, buf_size, pdMS_TO_TICKS(timeout_ms)); if (len > 0) { ESP_LOGI(TAG, "UART%d RX (%d bytes)", uart_num, len); // 不在这里打印数据,交由调用者(task)处理,避免重复日志 } else if (len == 0) { ESP_LOGD(TAG, "UART%d 接收超时", uart_num); } else { ESP_LOGW(TAG, "UART%d 接收错误 (%d)", uart_num, len); } return len; } // ============================ // RS485 初始化函数 // ============================ void RS_485_init(uart_port_t uart_num, int tx_pin, int rx_pin, int de_re_pin) { uart_config_t uart_config = { .baud_rate = BAUD_RATE, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .source_clk = UART_SCLK_DEFAULT, .rx_flow_ctrl_thresh = 122, }; ESP_ERROR_CHECK(uart_driver_install( uart_num, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, 0)); ESP_ERROR_CHECK(uart_param_config(uart_num, &uart_config)); ESP_ERROR_CHECK(uart_set_pin( uart_num, tx_pin, rx_pin, de_re_pin, UART_PIN_NO_CHANGE)); ESP_ERROR_CHECK(uart_set_mode(uart_num, UART_MODE_RS485_HALF_DUPLEX)); ESP_ERROR_CHECK(uart_set_rx_timeout(uart_num, ECHO_READ_TOUT)); ESP_LOGI(TAG, "RS485 init OK: UART%d TX=%d RX=%d DE/RE=%d", uart_num, tx_pin, rx_pin, de_re_pin); } // ============================ // 初始化指定RS485通道 // ============================ void init_specific_rs485_channel(int channel_num) { if (channel_num < 0 || channel_num >= NUM_CHANNELS) { ESP_LOGE(TAG, "Invalid channel number: %d", channel_num); return; } rs485_channel_t *ch = &rs485_channels[channel_num]; // 初始化RS485硬件 RS_485_init(ch->uart_num, ch->tx_pin, ch->rx_pin, ch->de_re_pin); ESP_LOGI(TAG, "Channel %d: %s (UART%d) initialized", channel_num, ch->name, ch->uart_num); } // ============================ // 初始化所有RS485通道 // ============================ void init_all_rs485_channels(void) { ESP_LOGI(TAG, "Initializing %d RS485 channels", NUM_CHANNELS); for (int i = 0; i < NUM_CHANNELS; i++) { init_specific_rs485_channel(i); } } /* 接收任务:参数为通道索引 (int cast via intptr_t) */ static void rs485_rx_task(void *arg) { int channel = (int)(intptr_t)arg; if (channel < 0 || channel >= NUM_CHANNELS) { ESP_LOGE(TAG, "rs485_rx_task: invalid channel %d", channel); vTaskDelete(NULL); return; } rs485_channel_t *ch = &rs485_channels[channel]; /* 使用堆分配,避免栈溢出 */ uint8_t *buf = malloc(BUF_SIZE); if (buf == NULL) { ESP_LOGE(TAG, "rs485_rx_task: malloc failed"); vTaskDelete(NULL); return; } ESP_LOGI(TAG, "%s RX task started", ch->name); while (1) { int len = rs485_receive(ch->uart_num, buf, BUF_SIZE, 100); // 100ms timeout for Modbus if (len > 0) { // 数据接收成功,LED2 短暂亮起表示有数据 status_led_set(2, 1); ESP_LOGI(TAG, "%s UART%d RX (%d bytes)", ch->name, ch->uart_num, len); ESP_LOG_BUFFER_HEX(TAG, buf, len); // 尝试解析MODBUS响应 modbus_response_t response; if (modbus_parse_response(buf, len, &response)) { // MODBUS解析成功,构建JSON格式数据 cJSON *root = cJSON_CreateObject(); if (root != NULL) { // 添加基本信息 cJSON_AddStringToObject(root, "channel", ch->name); cJSON_AddNumberToObject(root, "slave_addr", response.slave_addr); cJSON_AddNumberToObject(root, "function_code", response.function_code); if (response.is_exception) { // 异常响应 cJSON_AddStringToObject(root, "status", "exception"); cJSON_AddNumberToObject(root, "exception_code", response.exception_code); ESP_LOGW(TAG, "Modbus exception response: code=0x%02X", response.exception_code); } else { // 正常响应 cJSON_AddStringToObject(root, "status", "success"); cJSON_AddNumberToObject(root, "byte_count", response.byte_count); cJSON_AddNumberToObject(root, "register_count", response.register_count); // 添加寄存器数组 cJSON *reg_array = cJSON_CreateArray(); if (reg_array != NULL) { for (uint8_t i = 0; i < response.register_count; i++) { cJSON_AddItemToArray(reg_array, cJSON_CreateNumber(response.registers[i])); } cJSON_AddItemToObject(root, "registers", reg_array); } ESP_LOGI(TAG, "Modbus response parsed: slave=%d, func=0x%02X, regs=%d", response.slave_addr, response.function_code, response.register_count); } // 转换为JSON字符串 char *json_str = cJSON_Print(root); if (json_str != NULL) { // 发布JSON数据到MQTT int ret = mqtt_publish_message(CONFIG_MQTT_PUB_TOPIC, json_str, strlen(json_str), 0, 0); if (ret < 0) { ESP_LOGW(TAG, "Failed to publish Modbus JSON to MQTT topic %s", CONFIG_MQTT_PUB_TOPIC); } else { ESP_LOGI(TAG, "Published Modbus JSON to MQTT topic %s, msg_id: %d", CONFIG_MQTT_PUB_TOPIC, ret); ESP_LOGD(TAG, "JSON payload: %s", json_str); } free(json_str); } cJSON_Delete(root); } // 释放响应中的寄存器内存 modbus_free_response(&response); } else { // MODBUS解析失败,原始数据直接上报 ESP_LOGW(TAG, "Failed to parse Modbus frame, publishing raw data"); int ret = mqtt_publish_message(CONFIG_MQTT_PUB_TOPIC, (char *)buf, len, 0, 0); if (ret < 0) { ESP_LOGW(TAG, "Failed to publish RS485 data to MQTT topic %s", CONFIG_MQTT_PUB_TOPIC); } } vTaskDelay(pdMS_TO_TICKS(50)); // 保持亮起50ms status_led_blink_mode(2, 2); // 恢复心跳模式 } else if (len == 0) { vTaskDelay(pdMS_TO_TICKS(50)); // 无数据短延时 } else { // 接收错误,不记录日志避免刷屏 vTaskDelay(pdMS_TO_TICKS(200)); } } /* 永久任务通常不会到这里;若退出则释放 */ free(buf); vTaskDelete(NULL); } /* 启动单个通道的接收任务 返回 pdPASS 或 pdFAIL */ BaseType_t start_rs485_rx_task_for_channel(int channel_num, UBaseType_t priority, uint32_t stack_size) { if (channel_num < 0 || channel_num >= NUM_CHANNELS) { ESP_LOGE(TAG, "start_rs485_rx_task_for_channel: invalid channel %d", channel_num); return pdFAIL; } char tname[16]; snprintf(tname, sizeof(tname), "rs485_rx_%d", channel_num); return xTaskCreate(rs485_rx_task, tname, stack_size, (void *)(intptr_t)channel_num, priority, NULL); } /* 可选:启动所有通道的接收任务(示例默认优先级与栈)*/ void start_all_rs485_rx_tasks(UBaseType_t priority, uint32_t stack_size) { for (int i = 0; i < NUM_CHANNELS; i++) { if (start_rs485_rx_task_for_channel(i, priority, stack_size) != pdPASS) { ESP_LOGW(TAG, "Failed to start RX task for channel %d", i); } else { ESP_LOGI(TAG, "Started RX task for channel %d", i); } } }