news 2026/7/22 10:48:58

可替代bmc sol(serial over lan)的serial over wlan,无线网络串口传输方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
可替代bmc sol(serial over lan)的serial over wlan,无线网络串口传输方案

sol (serial over lan)非常方便的实现了通过有线网络来访问串行数据;由此,我们可以设计一款通过无线网络来访问串口数据;

实现方法有两种:

1. 第一种是被测机器通过CPU上的TTL串口输出数据,然后通过无线网络传输,客户端通过APP抓取对应的数据

2.第二种是被测机器通过USB接口输入串口数据,然后通过无线网络传输,客户端通过APP抓取对应的数据

如此,选择了芯片ESP32S3, 兼容两个设计方案。分为如下几个模块

1. 网络配置模块,通过手机App发送Wifi的名称 跟 密码给到芯片,配置Wifi 自动连接

2. 显示模块,自动连接Wifi后,显示本地IP地址,方便远程App读取数据

3. 网络数据传输模块,网络接收数据直接转发到USB模块/CPU TTL串口;

4. USB2TTL模块,直连被测机器USB端口,接收USB端口传输过来的串口数据,并转发到Wifi网络

5. UART TTL模块,直连被测机器UART TTL端口,接收TTL数据,并转发到Wifi网络

网络配置模块 static void initialise_wifi(void) { ESP_ERROR_CHECK(esp_netif_init()); s_wifi_event_group = xEventGroupCreate(); ESP_ERROR_CHECK(esp_event_loop_create_default()); esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); assert(sta_netif); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK( esp_wifi_init(&cfg) ); esp_err_t err = nvs_ssid_password(); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to connect to WiFi: %s", esp_err_to_name(err)); ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_start() ); }else{ // 尝试读取保存的IP信息 esp_netif_ip_info_t ip_info; if (nvs_get_ip_info(&ip_info) == ESP_OK) { // 设置静态IP esp_netif_dhcpc_stop(sta_netif); esp_netif_set_ip_info(sta_netif, &ip_info); ESP_LOGI(TAG, "Static IP set from NVS"); } ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) ); ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) ); ESP_ERROR_CHECK( esp_wifi_start() ); ESP_ERROR_CHECK( esp_wifi_disconnect() ); ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); esp_wifi_connect(); } }

网络配置模块,第一步是读取保存的Wifi名称以及密码,如果有保存尝试连接Wifi;如果没有读取到保存的Wifi,则进入smartconfig 模式,可以通过手机app发送对应的Wifi SSID 以及密码

显示模块 static void lvgl_ui_update_task(void *arg) { esp_netif_ip_info_t labelmsg; while (1) { if (xQueueReceive(ip_update_queue, &labelmsg, portMAX_DELAY) == pdTRUE) { ESP_LOGI(TAG, "Updating LVGL UI with new IP info"); //lvgl_port_lock(0); char ip_full[32], mask_full[32], gw_full[32]; snprintf(ip_full, sizeof(ip_full), "%d . %d . %d . %d", IP2STR(&labelmsg.ip)); snprintf(mask_full, sizeof(mask_full), "%d . %d . %d . %d", IP2STR(&labelmsg.netmask)); snprintf(gw_full, sizeof(gw_full), "%d . %d . %d . %d", IP2STR(&labelmsg.gw)); ESP_LOGI(TAG, "IP: %s, Mask: %s, Gateway: %s", ip_full, mask_full, gw_full); _lock_acquire(&lvgl_api_lock); lv_label_set_text(ip_label, ip_full); lv_label_set_text(netmask_label, mask_full); lv_label_set_text(gw_label, gw_full); _lock_release(&lvgl_api_lock); //lv_refr_now(NULL); //lvgl_port_unlock(); } } }

显示模块选择的是lvgl,添加了IP/NetMask/GateWay显示,只要网络连接上,显示屏会显示对应的IP地址

网络传输模块 static void do_retransmit(const int sock) { int len; char rx_buffer[128]; do { len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0); if (len < 0) { ESP_LOGE(TAG, "Error occurred during receiving: errno %d", errno); } else if (len == 0) { ESP_LOGW(TAG, "Connection closed"); } else { rx_buffer[len] = 0; // Null-terminate whatever is received and treat it like a string ESP_LOGI(TAG, "Received %d bytes: %s", len, rx_buffer); // send() can return less bytes than supplied length. // Walk-around for robust implementation. //int to_write = len; //while (to_write > 0) { // int written = send(sock, rx_buffer + (len - to_write), to_write, 0); // if (written < 0) { // ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); // Failed to retransmit, giving up // return; // } // to_write -= written; //} /* write back */ if (tud_cdc_n_connected(msg.itf)) { memcpy(msg.buf, rx_buffer, len); tinyusb_cdcacm_write_queue(msg.itf, msg.buf, len); esp_err_t err = tinyusb_cdcacm_write_flush(msg.itf, 0); if (err != ESP_OK) { ESP_LOGE(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err)); } }else{ ESP_LOGI(TAG, "CDC ACM not connected, cannot send data"); } uart_data_msg_t *uartmsg = (uart_data_msg_t*) malloc(sizeof(uart_data_msg_t)); if (uartmsg == NULL) { ESP_LOGI(TAG, "Failed to allocate message, data dropped!"); continue; } memcpy(uartmsg->data, rx_buffer, len); uartmsg->length = len; if (xQueueSend(uart_data_rx_queue, &uartmsg, 0) != pdPASS) { ESP_LOGW(TAG, "Queue full! Data dropped (length: %d)", len); free(uartmsg); // 释放未发送的消息 } else { // 数据成功入队,可以继续读取下一批UART数据 ESP_LOGI(TAG, "Data enqueued: %d bytes", len); } } } while (len > 0); }

网络传输模块,接收到网络数据后,发送到USB CDC以及 TTL UART

USB TTL模块 static void cdc_task(void *pvParameters){ while (1) { if (xQueueReceive(app_queue, &msg, portMAX_DELAY)) { if (msg.buf_len) { /* Print received data*/ ESP_LOGI(TAG, "Data from channel %d:", msg.itf); ESP_LOG_BUFFER_HEXDUMP(TAG, msg.buf, msg.buf_len, ESP_LOG_INFO); if (sock < 0) { ESP_LOGI(TAG, "Unable to accept connection: errno %d", errno); continue; } /* write back */ //tinyusb_cdcacm_write_queue(msg.itf, msg.buf, msg.buf_len); //esp_err_t err = tinyusb_cdcacm_write_flush(msg.itf, 0); //if (err != ESP_OK) { // ESP_LOGE(TAG, "CDC ACM write flush error: %s", esp_err_to_name(err)); //} int to_write = msg.buf_len; while (to_write > 0) { int written = send(sock, msg.buf + (msg.buf_len - to_write), to_write, 0); if (written < 0) { ESP_LOGI(TAG, "Error occurred during sending: errno %d", errno); // Failed to retransmit, giving up return; } to_write -= written; } } } } }

USB CDC TTL模块,接收到USB数据之后,转发到网络sock

UART TTL模块 void tcp_send_task(void *pvParameters) { ESP_LOGI(TAG, "TCP send task started, socket: %d", sock); uart_data_msg_t *msg = NULL; while (1) { // 1. 从队列中取出消息(阻塞等待) if (xQueueReceive(uart_data_queue, &msg, portMAX_DELAY) == pdPASS) { if (msg == NULL) { continue; } ESP_LOGI(TAG, "Dequeued message of length: %d", msg->length); if (sock < 0) { ESP_LOGI(TAG, "Unable to accept connection: errno %d", errno); free(msg); msg = NULL; continue; } // 2. 发送数据到TCP(可能会阻塞) int to_write = msg->length; int offset = 0; while (to_write > 0) { int written = send(sock, msg->data + offset, to_write, 0); if (written < 0) { ESP_LOGI(TAG, "Send error: errno %d, socket may be closed", errno); free(msg); // 这里可以触发重连逻辑,但不要退出任务 // 等待网络恢复 vTaskDelay(1000 / portTICK_PERIOD_MS); break; // 退出内层循环 } to_write -= written; offset += written; } // 3. 释放消息内存 free(msg); msg = NULL; } } vTaskDelete(NULL); }

UART TTL模块,接收到UART TTL数据之后,转发到sock无线网络

抓取系统内核打印或者BIOS串口打印

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/22 10:48:53

AI Agent如何破解跨境电商自动化运营难题

1. 跨境电商运营的自动化困局与破局思路跨境电商行业长期面临"人肉搬运"的运营痛点——运营人员需要手动在多个平台间重复录入商品信息、处理订单、更新库存。我曾服务过一家同时运营亚马逊、eBay和独立站的卖家&#xff0c;他们的运营团队每天要花6小时在不同平台间…

作者头像 李华
网站建设 2026/7/22 10:48:35

Linux 运维常用命令(CPU、内存、磁盘

Linux 运维常用命令&#xff08;CPU、内存、磁盘&#xff09;&#xff0c;也是日常排查服务器性能最常用的一套&#xff0c;适合写进运维手册。一、查看 CPU 使用率 1. top&#xff08;最常用&#xff09; top例如&#xff1a; %Cpu(s): 5.7 us, 1.3 sy, 0.0 ni, 92.7 id, 0.2 …

作者头像 李华
网站建设 2026/7/22 10:47:52

python安装包安装到哪里路径区分

一、先分 3 种场景&#xff08;你是 PyCharm 项目&#xff0c;重点看第 1 种&#xff09;场景 1&#xff1a;PyCharm 终端执行 pip install pandas&#xff08;90% 情况&#xff0c;虚拟环境&#xff09;你的路径&#xff1a;C:\Users\user\PyCharmMiscProject PyCharm 新建项目…

作者头像 李华
网站建设 2026/7/22 10:44:56

EDMA3 Ping-Pong缓冲与传输链技术:嵌入式系统数据搬运性能优化实战

1. 项目概述与核心价值 在嵌入式系统开发&#xff0c;尤其是涉及音频处理、图像采集或高速通信的领域&#xff0c;数据搬运的效率直接决定了整个系统的实时性和性能上限。当CPU被频繁的中断和数据拷贝任务所拖累时&#xff0c;再强大的算法也无用武之地。这时&#xff0c;直接内…

作者头像 李华
网站建设 2026/7/22 10:44:44

赋能未来教育:TsingtaoAI团队成功交付大模型应用工程师高级师资研修

引言&#xff1a;大模型时代下的师资赋能 2025年4月22日至24日&#xff0c;TsingtaoAI工程团队在北京为某职业大学的教师团队成功交付了为期三天的“大模型应用工程师&#xff08;高级&#xff09;”师资研修。本次研修不仅是技术的传递&#xff0c;更是对“新质生产力”人才培…

作者头像 李华
网站建设 2026/7/22 10:40:25

音频生成的 Latency 优化:从 GPU 推理到扬声器的全链路加速

音频生成的 Latency 优化&#xff1a;从 GPU 推理到扬声器的全链路加速 一、用户输入 prompt 后 15 秒才听到声音&#xff0c;而竞品 3 秒就出结果了 音频生成的延迟问题比文本生成更棘手。文本生成可以流式输出——模型生成 3 个 token 就能开始展示&#xff0c;用户感知等待时…

作者头像 李华