ESP32经UART向Telit LE910C1发送at#httpsnd指令异常问题
问题背景
定制板卡牌载Telit LE910C1-EUX模块,模块通过UART连接ESP32处理器,ESP32基于ESP-IDF v4.3.2框架开发。
现有实现代码
指令发送函数
esp_err_t TelitSerialCommand(const char* command) { // Send command over UART uart_write_bytes(UART_NUM_1, command, strlen(command)); if(uart_wait_tx_done(UART_NUM_1, 100) != ESP_OK) { ESP_LOGE(TAG, "Could not send Telit command"); return ESP_ERR_TIMEOUT; } // Wait for two seconds vTaskDelay(2000/portTICK_RATE_MS); return ESP_OK; }
响应读取函数
esp_err_t TelitSerialRead(char* RxBuf, size_t length) { // First, clean current string holding past message strncpy(RxBuf, "", length); // Check how many data bytes are present in UART buffer esp_err_t status = uart_get_buffered_data_len(UART_NUM_1, &length); if (status != ESP_OK) { ESP_LOGE(TAG, "Could not get UART rx buffer size"); return status; } // Read those bytes length = uart_read_bytes(UART_NUM_1, RxBuf, length, 100); // Clean UART buffer uart_flush(UART_NUM_1); return ESP_OK; }
故障现象
上述两个函数可完成常规AT指令收发,但执行at#httpsnd POST操作时指令异常。
使用的AT指令序列如下:
at+cmee=2\r at#sgact=1,1\r at#httpcfg=0,"www.httpbin.org",80,0,,,0\r at#httpsnd=0,0,"/post",15,"application/json"\r
- 预期表现:最后一条指令发送后,Telit模块返回
>>>提示符,标识已准备好接收POST载荷数据 - 实际表现:发送后仅收到指令回显,模块无
>>>响应,状态与指令未携带\r结束符、持续等待回车输入完全一致;只有发送下一条AT指令后,才会收到延后的>>>响应。已确认at#httpsnd指令末尾确实添加了回车符,无法定位根因。
已完成验证项
- 绕过ESP32,通过USB转串口接minicom直接和Telit模块通信,所有指令均可正常执行
- 模块网络连接正常,可ping通8.8.8.8
- GET类指令
AT#HTTPQRY=0,0,"/get"\r可正常执行,无异常
排查思路与解决方案
根因是现有UART交互逻辑和AT#HTTPSND的响应时序不匹配,和指令是否带回车无关,具体问题点:
- 固定2秒延时的逻辑完全不可靠:普通AT指令响应快,2秒延时足够等到完整返回,但
AT#HTTPSND触发后模块需要内部完成HTTP连接建立、接收缓冲区准备等操作,处理时长波动很大。固定2秒到期时,模块往往还没输出>>>提示符,此时调用读函数只能读到缓冲区里存的指令回显,后续到达的>>>会被读函数末尾的uart_flush直接清空。等发送下一条AT指令时,模块刚好把之前积压的>>>输出,就出现了响应延后到下一条指令发送后才收到的假象。 - 读函数存在逻辑硬伤:
uart_get_buffered_data_len会修改传入的长度参数为当前缓冲区已就绪的字节数,直接用这个值作为uart_read_bytes的读取长度,再加100ms超时,只能读到调用读函数那一刻已经存在缓冲区里的数据,后续陆续到达的响应根本读不到,最后还被flush操作丢弃。 - 不用排查指令行结束符:minicom直连测试全流程正常,说明模块配置的
\r指令结束符是匹配的,这部分不用浪费时间。
具体修复方案:
- 删掉收发流程里所有固定时长的
vTaskDelay等响应逻辑,改成基于响应标志位的轮询读取:普通AT指令以OK/ERROR作为读取结束标志,AT#HTTPSND单独判断,读到>>>就停止读取进入载荷发送流程,等待超时可以设为10秒,给模块留足网络操作的时间。 - 立刻去掉读函数里无条件执行的
uart_flush,这个操作会直接丢弃硬件缓冲区里未被应用读取的所有数据,是丢响应的核心诱因。 - 修正读函数的长度处理逻辑:单独定义变量存储UART缓冲区当前可读字节数,不要用这个值覆盖传入的接收缓冲区总长度,避免内存越界。
- 发送POST载荷时,严格按照指令里声明的长度发送对应字节数的JSON数据,不要额外加回车换行,发完后继续轮询等待模块返回最终的
OK/ERROR和HTTP状态码即可。
核心参考实现:
// 等待指定响应串的工具函数 esp_err_t TelitWaitForResponse(const char* expected_resp, char* rxBuf, size_t buf_len, uint32_t timeout_ms) { size_t recv_len = 0; memset(rxBuf, 0, buf_len); uint32_t start_tick = xTaskGetTickCount(); while ((xTaskGetTickCount() - start_tick) * portTICK_RATE_MS < timeout_ms) { size_t available = 0; uart_get_buffered_data_len(UART_NUM_1, &available); if (available > 0) { size_t read_to_len = available; if (recv_len + available >= buf_len) { read_to_len = buf_len - recv_len - 1; } int readn = uart_read_bytes(UART_NUM_1, rxBuf + recv_len, read_to_len, pdMS_TO_TICKS(20)); if (readn > 0) { recv_len += readn; rxBuf[recv_len] = '\0'; if (strstr(rxBuf, expected_resp) != NULL) { return ESP_OK; } } } vTaskDelay(pdMS_TO_TICKS(10)); } return ESP_ERR_TIMEOUT; }
AT#HTTPSND调用流程:
- 发送指令
at#httpsnd=0,0,"/post",15,"application/json"\r - 调用上述工具函数等待
>>>,超时设为10000ms - 收到
>>>后,直接发送15字节的JSON载荷,不要附加回车换行 - 再次调用工具函数等待
OK响应,获取POST请求结果
内容的提问来源于stack exchange,提问作者Sandro Sartoni
相关产品推荐
相关产品推荐

