D133物联网电子秤项目有哪些应用场景?

摘要:D133物联网电子秤项目 项目开始日期:2025,2,28 需求 硬件采购需求: 3.5寸RGB屏幕(目前已适配4.3寸(480x272)RGB565屏幕) 语音播报(语音模块+喇叭) 软件需求: lvgl用户交互界面 处
D133物联网电子秤项目 项目开始日期:2025,2,28 需求 硬件采购需求: 3.5寸RGB屏幕(目前已适配4.3寸(480x272)RGB565屏幕) 语音播报(语音模块+喇叭) 软件需求: lvgl用户交互界面 处理电子称串口数据 语音播报消息 wifi上传到服务器 作用: RGB屏幕实现电子秤输入,同时语音播报。 数据上传服务器 等待中流程: 采购:屏幕,语音播报模块+喇叭 技术:串口包数据包格式,WiFi上传格式 拆分任务: 实现串口收发(解析) 移植RGB屏幕 移植LVGL 初步实现UI界面交互 [x ] 实现wifi上传数据到服务器 增加语音播报 要求:lvgl+屏幕+WiFi上传数据包到服务器(已实现) 拉取SDK git clone --depth=1 https://gitee.com/lcsc/luban-lite.git git代码管理 git remote add origin https://gitee.com/sword-level_0/digital-scale-d133.git git push -u origin "master" 移植UART 串口0为Debug调试打印接口 使用 在user_uart3.c文件中将函数导出为命令,可以在终端开启串口 // 导出函数为命令 MSH_CMD_EXPORT(uart3_test_on, Test transmission and reception using UART3 serial port); 在终端开启 uart3_test_on 发送 send_demoData 接受串口数据 移植外设 外设框架 移植0.96IIC 复制驱动文件夹至路径 application\rt-thread\helloworld\user_bsp\0-96-iic-single-screen 在上层路径Kconfig添加 在配置中,配置开启 需要将冲突配置取消: LVGL相关和数字视频接口 Application options ---> [*] LVGL (official): powerful and easy-to-use embedded GUI library ---- [*] ArtInChip LVGL demo ---- Board options ---> [*] Using DVP 外设路径 外设上层路径 test_0_96_iic_single_screen 进程间通信(消息队列) 消息队列的使用 发送 /* 格式化时间字符串 */ snprintf(msg_buf, sizeof(msg_buf), "time=%d", time); // 使用snprintf函数将时间变量格式化为字符串,存储在msg_buf中 /* 发送消息到队列 */ result = rt_mq_send(mq, msg_buf, sizeof(msg_buf)); // 调用rt_mq_send函数将msg_buf中的消息发送到队列mq中 if (result != RT_EOK) // 如果发送失败 { rt_kprintf("Failed to send message\n"); // 打印错误信息 } 接收 char msg_buf[16]; // 定义一个字符数组,用于存储接收到的消息,数组大小为16字节 rt_err_t result; // 定义一个变量result,用于存储函数返回的错误码 while (1) // 无限循环,程序将一直执行这个循环体 { /* 阻塞等待消息 */ result = rt_mq_recv(mq, msg_buf, sizeof(msg_buf), RT_WAITING_FOREVER); // 调用rt_mq_recv函数从消息队列mq中接收消息 if (result == RT_EOK) rt_kprintf("Received: %s\n", msg_buf); else rt_kprintf("Recv failed: %d\n", result); } 同一文件 #include <stdlib.h> #include <stdio.h> #include <string.h> #include <getopt.h> #include <sys/time.h> #include <rtthread.h> #include "rtdevice.h" #include "aic_core.h" #include "aic_hal_gpio.h" #define RT_USING_OVERFLOW_CHECK static rt_mq_t mq = RT_NULL; // 消息队列句柄 /* 消息队列初始化 */ static int msg_queue_init(void) { /* 创建消息队列:名称、消息大小、队列容量、队列模式 */ mq = rt_mq_create("time_mq", 20, 10, RT_IPC_FLAG_FIFO); if (mq == RT_NULL) { rt_kprintf("Failed to create message queue\n"); return -1; } return 0; } INIT_APP_EXPORT(msg_queue_init); // 系统启动时自动初始化 /* 线程1:发送时间信息 */ static rt_thread_t user_app_thread = RT_NULL; static void user_APP_thread_entry(void *param) { int time = 0; int count = 0; char msg_buf[20]; rt_err_t result; while (1) { /* 格式化时间字符串 */ snprintf(msg_buf, sizeof(msg_buf), "time=%d", time); /* 发送消息到队列 */ result = rt_mq_send(mq, msg_buf, sizeof(msg_buf)); if (result != RT_EOK) { rt_kprintf("Failed to send message\n"); } /* 每两次发送(1秒)增加时间 */ if (++count % 2 == 0) time++; rt_thread_mdelay(500); // 500ms间隔 } } /* 启动线程1的命令 */ static void usr_APP_run(int argc, char **argv) { // 修改线程创建时的栈大小参数(原512改为2048) user_app_thread = rt_thread_create( "user_app_thread", user_APP_thread_entry, RT_NULL, 2048, // 修改点:栈大小增加到2048字节 25, 5); if (user_app_thread) rt_thread_startup(user_app_thread); else rt_kprintf("Create thread1 failed\n"); } MSH_CMD_EXPORT(usr_APP_run, "Start time sender thread"); /* 线程2:接收并打印信息 */ static rt_thread_t user_app2_thread = RT_NULL; static void user_APP2_thread_entry(void *param) { char msg_buf[16]; // 原20改为16("time=2147483647"仅需14字节) rt_err_t result; while (1) { /* 阻塞等待消息 */ result = rt_mq_recv(mq, msg_buf, sizeof(msg_buf), RT_WAITING_FOREVER); if (result == RT_EOK) rt_kprintf("Received: %s\n", msg_buf); else rt_kprintf("Recv failed: %d\n", result); } } /* 启动线程2的命令 */ static void usr_APP2_run(int argc, char **argv) { // 修改线程创建时的栈大小参数(原512改为2048) user_app2_thread = rt_thread_create( "user_app2_thread", user_APP2_thread_entry, RT_NULL, 2048, // 修改点:栈大小增加到2048字节 25, 5); if (user_app2_thread) rt_thread_startup(user_app2_thread); else rt_kprintf("Create thread2 failed\n"); } MSH_CMD_EXPORT(usr_APP2_run, "Start message receiver thread"); 不同文件 #include <stdlib.h> #include <stdio.h> #include <string.h> #include <getopt.h> #include <sys/time.h> #include <rtthread.h> #include "rtdevice.h" #include "aic_core.h" #include "aic_hal_gpio.h" extern rt_mq_t mq; // 消息队列句柄 /* 外部线程:接收并打印信息 */ static rt_thread_t user_External_app_thread = RT_NULL; static void user_External_app_thread_entry(void *param) { char msg_buf[16]; // 原20改为16("time=2147483647"仅需14字节) rt_err_t result; while (1) { /* 阻塞等待消息 */ result = rt_mq_recv(mq, msg_buf, sizeof(msg_buf), RT_WAITING_FOREVER); if (result == RT_EOK) rt_kprintf("Received: %s\n", msg_buf); else rt_kprintf("Recv failed: %d\n", result); } } /* 启动线程的命令 */ static void usr_External_app_run(int argc, char **argv) { // 修改线程创建时的栈大小参数(原512改为2048) user_External_app_thread = rt_thread_create( "usr_External_app_thread", // 线程名 user_External_app_thread_entry, RT_NULL, 2048, // 修改点:栈大小增加到2048字节 25, 5); if (user_External_app_thread) rt_thread_startup(user_External_app_thread); else rt_kprintf("Create _External——thread failed\n"); } MSH_CMD_EXPORT(usr_External_app_run, "Start message receiver thread"); 改动 在声明句柄的文件中: static rt_mq_t mq = RT_NULL; // 消息队列句柄 去掉 static //静态修饰 改成 rt_mq_t mq = RT_NULL; // 消息队列句柄 在外部文件中 extern rt_mq_t mq; // 声明引用外部消息队列句柄 终端命令 发送消息队列 usr_APP_run 接收消息队列,打印 usr_APP2_run oled显示+接收消息队列 test_0_96_iic_single_screen 网路(2.4GWIFI) 终端 扫描WIFI wlan wifi_scan 连接wifi: 输入名称,密码 wlan wifi_connect SSID PASSWORD wlan wifi_connect jianzhiji 8765432111 开启网卡 dhcpc WL0 start 查看ip ifconfig 例程分析 例程结构 网络概念解析 LwIP(Light Weight IP)是一个小型开源的TCP/IP协议栈,由瑞典计算机科学院(SICS)的Adam Dunkels开发。它的设计初衷是用少量的资源消耗(尤其是RAM)实现一个较为完整的TCP/IP协议栈,其中“完整”主要指的是TCP协议的完整性。 DNS域名解析 当在浏览器中输入 www.baidu.com 并解析时,整个过程涉及多个步骤和技术协议,最终目的是将域名转换为服务器IP地址并完成网页加载。以下是解析的主要流程及相关技术细节: 1. DNS域名解析 DNS(Domain Name System)负责将域名转换为对应的IP地址,具体步骤如下: 本地缓存查询 浏览器首先检查自身缓存(如Chrome的DNS缓存)中是否有该域名的IP记录214。 若无,则查询操作系统缓存(如Windows的hosts文件)和本地DNS缓存14。 本地DNS服务器查询 若本地缓存未命中,请求会发送至用户配置的本地DNS服务器(如运营商提供的114.114.114.114)414。 本地DNS服务器若缓存了域名记录,直接返回IP;否则进入递归查询流程14。 递归查询与根域名服务器 本地DNS服务器依次向根域名服务器、顶级域名服务器(.com)、权威域名服务器(百度注册的DNS服务器)发起迭代查询,最终获取www.baidu.com的IP地址714。 返回IP地址 查询结果逐级返回至浏览器,例如常见的百度IP地址可能为 220.181.111.147 或 202.108.22.5(实际IP可能因负载均衡而变化)27。 相关命令: 使用 nslookup www.baidu.com 或 ping www.baidu.com 可直接查看解析后的IP地址3。 2. 建立TCP连接 获取IP地址后,浏览器通过TCP协议与服务器建立连接: 三次握手 客户端发送SYN包,服务器返回SYN-ACK包,客户端再发送ACK包确认,完成可靠连接的建立27。 端口与协议 服务器默认监听80端口(HTTP)或443端口(HTTPS)212。 3. 发起HTTP请求与响应 HTTP请求 浏览器发送HTTP GET请求,包含请求头(如User-Agent、Cookie等)和请求路径(如/表示首页)712。 服务器处理 百度服务器根据请求生成响应,可能涉及负载均衡、动态内容生成或静态资源返回1213。 HTTP响应 服务器返回状态码(如200 OK)、响应头(如Content-Type)及HTML内容27。 4. 浏览器渲染页面 解析HTML与资源加载 浏览器解析HTML代码,并加载其中的CSS、JavaScript、图片等资源,可能触发多次HTTP请求212。 渲染引擎处理 构建DOM树、CSSOM树,合并为渲染树,最终布局和绘制页面712。 5. 连接释放 数据传输完成后,通过TCP四次挥手释放连接27。 技术协议总结 协议层级 协议与作用 应用层 HTTP(传输网页)、DNS(域名解析)2 传输层 TCP(可靠连接)、UDP(DNS查询)27 网络层 IP(路由选择)、ARP(IP转MAC地址)47 链路层 以太网帧(物理传输)4 以下是关于 lwIP 的线程堆栈大小、MQTT/HTTP 协议使用、DHCP 服务器配置以及调试选项的详细说明: 1. lwIP 线程堆栈大小 作用: lwIP 在多线程环境下运行时,每个网络线程(如 TCP/IP 主线程、协议处理线程等)需要分配足够的堆栈空间。堆栈大小不足会导致栈溢出、系统崩溃或数据损坏。 配置方法: 默认值: lwIP 默认堆栈大小取决于移植的平台(如 FreeRTOS、裸机等),通常在 lwipopts.h 或平台配置文件中定义。 例如,在 FreeRTOS 中,TCP/IP 线程堆栈可能默认为 1KB~4KB。 调整堆栈: 根据协议复杂度和数据量调整。例如: 简单应用(如 UDP Echo):1KB~2KB 复杂应用(如 HTTP 文件传输):4KB~8KB MQTT/HTTP + TLS:可能需要 8KB~16KB(加密算法占用更多栈空间) 在 FreeRTOS 中修改示例: // FreeRTOS 任务创建时指定堆栈大小 xTaskCreate(tcpip_thread, "lwIP", 4096, NULL, 2, NULL); 检测方法: 使用工具(如 FreeRTOS 的堆栈溢出检测钩子函数 vApplicationStackOverflowHook)或手动填充魔法值检查溢出。 2. 使用 MQTT 协议 实现步骤: 启用 lwIP MQTT 模块: 在 lwipopts.h 中启用 MQTT 支持: #define LWIP_MQTT 1 集成 MQTT 客户端: lwIP 提供轻量级 MQTT 客户端,需包含头文件: #include "mqtt.h" 连接与通信示例: // MQTT 连接配置 struct mqtt_connect_client_info_t ci; memset(&ci, 0, sizeof(ci)); ci.client_id = "client_id"; // 连接到 Broker mqtt_client_t *client = mqtt_client_new(); mqtt_connect(client, &ip_addr, 1883, mqtt_connection_cb, NULL, &ci); // 发布消息 const char *message = "Hello MQTT"; mqtt_publish(client, "topic", message, strlen(message), MQTT_QOS_0, 0, NULL); 3. 使用 HTTP 协议 实现步骤: 启用 HTTP 服务器: 在 lwipopts.h 中启用 HTTP 支持: #define LWIP_HTTPD 1 #define LWIP_HTTPD_SSI 1 // 可选:支持 SSI #define LWIP_HTTPD_CGI 1 // 可选:支持 CGI 实现 HTTP 回调函数: 定义页面处理和 CGI 函数: // 处理 HTTP 请求 const char *http_response = "<html><body>Hello HTTP</body></html>"; err_t http_req_handler(struct pbuf *p, struct tcp_pcb *pcb) { tcp_write(pcb, http_response, strlen(http_response), TCP_WRITE_FLAG_COPY); tcp_close(pcb); return ERR_OK; } // 注册 HTTP 路径 http_set_cgi_handlers(http_cgi_handlers, LWIP_ARRAYSIZE(http_cgi_handlers)); 启动 HTTP 服务器: httpd_init(); 4. 使用 DHCP 服务器 配置方法: 启用 DHCP 服务器功能: 在 lwipopts.h 中启用: #define LWIP_DHCP 1 // 启用 DHCP 客户端 #define LWIP_DHCPS 1 // 启用 DHCP 服务器 设置地址池: 在代码中配置 DHCP 地址池: ip4_addr_t dhcp_start, dhcp_end; IP4_ADDR(&dhcp_start, 192, 168, 1, 100); IP4_ADDR(&dhcp_end, 192, 168, 1, 200); dhcp_server_init(netif_default, &dhcp_start, &dhcp_end); 处理 DHCP 请求: lwIP 自动处理客户端请求,分配 IP 并管理租期。 5. 启用 lwIP 调试选项 作用: 调试选项用于输出网络状态、协议解析细节和错误信息,帮助定位连接问题、内存泄漏或协议错误。 启用方法: 全局调试开关: 在 lwipopts.h 中设置: #define LWIP_DEBUG 1 // 启用调试 #define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL // 输出所有级别日志 模块级调试: 按需启用特定模块的调试: #define DHCP_DEBUG LWIP_DBG_ON // DHCP 调试 #define TCP_DEBUG LWIP_DBG_ON // TCP 调试 #define HTTP_DEBUG LWIP_DBG_ON // HTTP 调试 输出调试信息: 实现 lwip_log 函数或重定向到串口: void lwip_log(const char *fmt, ...) { va_list args; va_start(args, fmt); vprintf(fmt, args); // 输出到控制台或串口 va_end(args); } 典型调试场景: 内存泄漏:启用 MEM_DEBUG 和 MEMP_DEBUG。 TCP 重传:启用 TCP_CWND_DEBUG 查看拥塞窗口变化。 HTTP 请求解析:启用 HTTP_DEBUG 跟踪请求处理。 总结 堆栈大小:根据协议复杂度和线程任务调整,避免溢出。 协议支持:通过 lwipopts.h 启用 MQTT/HTTP/DHCP,并实现回调函数。 调试:启用调试宏并实现日志输出,快速定位问题。 建议结合具体平台(如 FreeRTOS、Linux 等)和硬件资源(内存、CPU)进行参数调优。 开启网络 help 以下是 RT-Thread Shell 命令的中文翻译: RT-Thread Shell 命令列表 命令 功能描述 list_fd 列出文件描述符 sensor 传感器测试功能 sensor_polling 传感器轮询模式测试功能 sensor_int 传感器中断模式测试功能 sensor_fifo 传感器 FIFO 模式测试功能 free 显示系统内存使用情况 ps 列出系统中的线程 help 显示 RT-Thread Shell 帮助 list 列出系统对象 list_device 列出系统中的设备 list_timer 列出系统中的定时器 list_mempool 列出内存池 list_memheap 列出内存堆 list_msgqueue 列出消息队列 list_mailbox 列出邮箱 list_mutex 列出互斥锁 list_event 列出事件 list_sem 列出信号量 list_thread 列出线程 version 显示 RT-Thread 版本信息 clear 清空终端屏幕 tail 输出文件的最后 N 行数据 echo 将字符串写入文件 df 显示磁盘剩余空间 umount 从文件系统卸载设备 mount 挂载设备到文件系统:mount <设备> <挂载点> <文件系统类型> mkfs 格式化磁盘为指定文件系统 mkdir 创建目录 pwd 显示当前工作目录路径 cd 切换工作目录 rm 删除文件 cat 查看文件内容 mv 重命名文件或移动文件 cp 复制文件 ls 列出目录或文件信息 ulog_filter 显示 ulog 日志过滤设置 ulog_kw 设置 ulog 全局关键字过滤 ulog_tag 设置 ulog 全局标签过滤 ulog_lvl 设置 ulog 全局日志级别过滤 ulog_tag_lvl 按标签设置 ulog 日志级别过滤 ulog_be_lvl 按后端设置 ulog 日志级别过滤 f 运行一个函数 m 修改内存值 p 打印内存值 meminfo 显示内存信息 run_in_loop_thre 在循环线程中运行命令 run_in_thread 在线程中运行命令 reboot 重启系统 reset 重置设备模块 list_pinmux 列出引脚功能配置 arecord 录制音频为 WAV 文件 aplay 播放 WAV 文件 test_clock 测试时钟模块(CMU CLK) test_dma_memset 测试 DMA 内存填充(参数:值 长度) test_dma_memcpy 测试 DMA 内存复制(参数:长度) test_dvp 测试 DVP 和摄像头模块 test_gpai 测试 GPAI 设备 test_gpio 测试 GPIO 设备 test_eth 网络回环测试 iperf 网络性能测试(-s 为服务端/-c 为客户端) sf SPI Flash 操作 fal FAL(Flash 抽象层)操作 test_tsen 温度传感器(TSen)测试 top 显示 CPU 使用率 dhcpc 启动/停止 DHCP 客户端(动态主机配置协议),自动分配IP、子网掩码、网关和DNS,这样用户就不用手动配置了 ifconfig 显示或配置网络信息 ping 网络连通性测试(输入 ping help 查看帮助) test_0_96_iic_si 测试 0.96 英寸 IIC 屏幕 usr_APP2_run 启动消息接收线程 usr_APP_run 启动时间发送线程 usr_External_app 启动外部应用线程(消息接收) send_demoData 发送测试数据 dma_dump 显示 DMA 寄存器(参数:通道号) efuse 操作 EFUSE epwm_status 显示 EPWM 状态 mtop 测试 mtop 功能 pwm_set_tb 设置 PWM 时基 pwm_status 显示 PWM 状态 tsen_status 显示温度传感器状态 wdt_status 显示看门狗状态 list_irq 列出系统中断 canstat 显示 CAN 设备状态 adc ADC 操作(输入 adc help 查看选项) pin 引脚操作(输入 pin help 查看选项) pwm PWM 操作(输入 pwm help 查看选项) lptimer_dump 显示低功耗定时器(LPTimer)信息 pm_dump 显示电源管理状态 pm_run 切换电源管理模式 pm_module_delay 设置模块延迟休眠请求 pm_module_reques 请求模块电源管理模式 pm_module_releas 释放模块电源管理模式计数 pm_module_releas 释放模块电源模式 pm_request 请求电源管理模式 pm_release_all 释放所有电源管理模式计数 pm_release 释放电源管理模式 list_alarm 列出闹钟信息 date 获取/设置日期和时间(本地时区):date [年 月 日 时 分 秒] player_demo 播放器演示 audio_player_dem 音频播放器演示 wlan WiFi 操作(输入 wlan help 查看子命令) 使用说明 格式:命令 [参数],例如 ping 192.168.1.1。 帮助:输入 命令 help 查看详细用法(如 ping help)。 硬件相关命令(如 test_gpio, pwm)需确保硬件支持。 网络相关概念 DHCP ''(Dynamic Host Configuration Protocol,动态主机配置协议)是一种网络协议,用于自动分配和管理网络设备的IP地址及相关配置参数,从而简化网络配置并避免手动操作带来的错误。 一、核心功能 IP地址自动分配 动态分配:从地址池中临时分配IP(有租期限制) 静态绑定:为特定设备(如服务器)固定分配IP lwIP (Lightweight IP) 是一个轻量化的开源 TCP/IP 协议栈,专为资源受限的嵌入式系统设计。它由瑞典计算机科学研究院的 Adam Dunkels 开发,广泛应用于物联网设备、工业控制、传感器网络等场景。 模块化架构 支持按需裁剪功能模块(如 DHCP、DNS、IPv6),开发者可灵活配置协议栈功能。 多协议支持 包含完整的 TCP/IP 协议族: 网络层:IPv4、IPv6、ICMP、IGMP 传输层:TCP、UDP 应用层:HTTP、SNMP、MQTT(需扩展实现) lwIP 的典型应用场景 场景 说明 物联网设备 智能家居设备(如 Wi-Fi 插座、传感器)通过 lwIP 实现网络通信。 工业控制 PLC、工业网关使用 lwIP 接入以太网,支持 Modbus TCP 等协议。 嵌入式 Web 服务器 通过 lwIP 的 HTTP 模块,在设备上运行轻量级 Web 页面(如配置界面)。 低功耗设备 结合低功耗无线技术(如 LoRa、BLE),实现电池供电设备的间歇性网络通信。 iperf iperf 是一款开源的网络性能测试工具,主要用于测量 TCP/UDP 带宽、延迟、抖动和丢包率等网络性能指标。它通过在客户端和服务器之间发送数据流,评估网络的吞吐量和稳定性,是网络工程师和开发者的常用工具。 一、核心功能 功能 说明 带宽测试 测量网络最大传输速率(如测出实际带宽是否达标) 协议支持 支持 TCP、UDP、SCTP 协议 多线程测试 可并行多连接测试(模拟多用户场景) 数据方向控制 支持上行(客户端→服务器)、下行(服务器→客户端)、双向测试 统计报告 实时输出带宽、抖动(Jitter)、丢包率(Loss%)等详细指标 SDMC 通常指 同步数字主控制器(Synchronous Digital Master Controller),是一种用于协调多设备同步操作的高精度时序控制核心模块,尤其在高速数字系统、通信设备和复杂嵌入式系统中广泛应用。 WLAN连接 wifi(RTL8189FTV-WIFI模块2.4GHz,SDIO接口) 扫描wifi wlan wifi_scan 连接wifi (输入名称,密码) wlan wifi_connect SSID PASSWORD wlan wifi_connect jianzhiji 8765432111 wlan wifi_connect jianzhiji 8765432111 ifconfig 需出现IP地址才算链接成功 可以 通过ping ip来再次确认是否连接到互联网还是内网: ping www.baidu.com //DNS解码的百度ip地址 ping 183.2.172.177 无线网络 本章节讲解如何在 Luban-Lite 系统中配置 SDIO 接口的无线网络,需要配置的主要有以下几部分: 配置 SDMC 接口; 配置 WiFi 模组; 配置 lwIP 协议栈; 配置内核; 配置 SDMC 接口和 WiFi 模组 使用 SDMC 接口前,需将对应 SDMC 接口选中,以 D133CBV-QFN88 开发板为例,使用 scons --menuconfig 命令进行配置: Board options [ * ] Using SDMC0 # 硬件连接哪个 SDMC 接口,选择哪个SDMC [ * ] Enable the interrupt of sdmc [ * ] Using Wireless lan ---> [ * ] Using Realtek wlan driver ---> Select Realtek wlan0 modul (rtl8189)--- > # 以 RTL8189 为例 Realtek wlan0 paramete ---> (PC.7) realtek wlan0 power on gpio (192.168.3.20) wlan0 ip addr (192.168.3.1) wlan0 gateway addr (255.255.255.0) wlan0 netmask [*] Enable Realtek driver debug information 使用RTL8189FTV--WIFI模块该选 例程地址:packages -> third-party -> webclient ->samples 配置 lwIP 使用 scons --menuconfig 命令进入配置页面,配置如下: Local packages options ---> Third-party packages options ---> LwIP: light weight TCP/IP stack [*] LwIP Examples ---> [*] Using net tools [*] Enable DNS for name resolution [*] Enable alloc ip address through DHCP [*] Enable ping features ---> 配置内核 Rt-Thread options ---> RT-Thread Kernel ---> (4) The priority level value of timer thread (4096) The stack size of timer thread RT-Thread Components ---> Device Drivers ---> (4096) The stack size for sdio irq thread (5) The priority level value of sdio irq thread WLAN基础管理命令 以下是这些 wlan 相关命令的详细功能解析和用法说明(按用途分类整理): 一、WiFi基础管理命令 1. 网络开关 命令 功能 示例 wifi_on 启动WiFi模块 wifi_on wifi_off 关闭WiFi模块 wifi_off 2. 连接管理 命令 功能 参数说明 示例 wifi_connect 通过SSID和密码连接网络 <SSID> <密码> wifi_connect MyWiFi 12345678 wifi_connect_bssid 指定BSSID连接(防AP同名欺骗) <SSID> <密码> <BSSID> wifi_connect_bssid MyWiFi 1234 AA:BB:CC:DD:EE:FF wifi_disconnect 断开当前连接 无 wifi_disconnect 二、网络扫描与诊断 1. 扫描相关 命令 功能 参数说明 示例 wifi_scan 执行全频段WiFi扫描 无 wifi_scan wifi_reoder_scan 按信号强度排序扫描结果 无 wifi_reoder_scan wifi_scan_with_ssid 扫描指定SSID是否存在 <SSID> wifi_scan_with_ssid MyWiFi wifi_scan_with_multissid 批量扫描多个SSID <SSID1> <SSID2>... wifi_scan_with_multissid Home Office 2. 诊断工具 命令 功能 参数说明 示例 ping 网络连通性测试 <IP> [-t] [-n count] ping 192.168.1.1 -n 5 get_auto_chl 获取自动信道选择结果 无 get_auto_chl 三、高级调试与监控 1. 混杂模式(抓包) 命令 功能 参数说明 示例 wifi_promisc 开启混杂模式监听原始数据包 <enable/disable> wifi_promisc enable rawdata_enable 启用原始数据捕获 无 rawdata_enable rawdata_disable 禁用原始数据捕获 无 rawdata_disable rawdata_send 发送原始数据帧(需指定格式) <hex数据> rawdata_send 00AAFF... 2. 调试接口 命令 功能 参数说明 示例 iwpriv 设置/读取私有驱动参数 <参数> [值] iwpriv set_power 10 wifi_debug 开启调试日志(级别可调) <debug_level:0-5> wifi_debug 3 四、AP模式操作 命令 功能 参数说明 示例 wifi_ap 启动SoftAP热点模式 <SSID> <密码> [channel] wifi_ap MyHotspot 12345678 6 五、系统信息与退出 命令 功能 参数说明 示例 wifi_info 显示当前连接状态(IP/RSSI/BSSID等) 无 wifi_info exit 退出命令行界面 无 exit help 显示命令帮助 无 help 常见问题处理指南 1. 连接失败 # 检查驱动状态 wifi_info # 扫描确认AP存在 wifi_scan_with_ssid MyWiFi # 尝试指定BSSID连接 wifi_connect_bssid MyWiFi password AA:BB:CC:DD:EE:FF 2. 网络延迟高 # 持续ping测试 ping 192.168.1.1 -t # 查看当前信道质量 iwpriv get_rssi 3. 抓包分析 wifi_promisc enable rawdata_enable # 此时数据会输出到指定接口(需配合tcpdump或wireshark) 参数顺序规则(重要!) 对于ping命令,选项顺序影响参数有效性:# 正确:发送5次请求 ping 192.168.1.1 -n 5 # 错误:-t出现在-n之后会被忽略 ping 192.168.1.1 -n 5 -t # 实际只执行5次 调试日志级别说明 级别 日志详细程度 0 关闭所有调试输出 1 关键错误信息 2 基本连接状态变更 3 协议交互过程(推荐调试级别) 4 详细数据包跟踪 5 底层寄存器操作(开发者用) 建议调试时先设置 wifi_debug 3 查看关键交互信息。 HTTP [单片机] → (连接Wi-Fi) → [TCP握手] → [发送HTTP请求] → [接收响应] → [处理数据] E:\D133EBS\WIFI\luban-lite\kernel\rt-thread\components\net\lwip\lwip-2.1.2\src\apps\http\http_client.c E:\D133EBS\WIFI\luban-lite\packages\third-party\lwip\src\apps\http\http_client.c 1. 包含头文件**: 在你的源文件中包含 `http_client.h` 头文件,以便使用 HTTP 客户端的接口和数据结构。 \#include "lwip/apps/http_client.h" 2. **配置 HTTP 客户端连接**: 配置 `httpc_connection_t` 结构体,设置代理地址、端口和回调函数等。 *httpc_connection_t* settings; *memset*(&*settings*, 0, sizeof(settings)); settings.result_fn = my_result_callback; settings.headers_done_fn = my_headers_done_callback; 3. **定义回调函数**: 实现 HTTP 客户端的回调函数,用于处理 HTTP 请求的结果和接收的头部信息。 void *my_result_callback*(void **arg*, *httpc_result_t* *httpc_result*, *u32_t* *rx_content_len*, *u32_t* *srv_res*, *err_t* *err*) { *// 处理 HTTP 请求结果* } *err_t* *my_headers_done_callback*(*httpc_state_t* **connection*, void **arg*, struct pbuf **hdr*, *u16_t* *hdr_len*, *u32_t* *content_len*) { *// 处理接收到的 HTTP 头部信息* return ERR_OK; } 4. **发起 HTTP 请求**: 使用 `httpc_get_file` 或 `httpc_get_file_dns` 函数发起 HTTP 请求。 *ip_addr_t* server_addr; *IP4_ADDR*(&*server_addr*, 192, 168, 1, 1); *// 设置服务器 IP 地址* const char *uri = "/path/to/resource"; *httpc_state_t* *connection; *err_t* err = *httpc_get_file*(&*server_addr*, HTTP_DEFAULT_PORT, uri, &*settings*, my_recv_callback, NULL, &*connection*); if (err != ERR_OK) { *// 处理错误* } 5. **接收数据回调函数**: 实现数据接收回调函数,用于处理接收到的数据。 *err_t* *my_recv_callback*(void **arg*, struct altcp_pcb **pcb*, struct pbuf **p*, *err_t* *err*) { if (p != NULL) { ​ *// 处理接收到的数据* ​ *altcp_recved*(pcb, p->tot_len); ​ *pbuf_free*(p); } return ERR_OK; } 通过上述步骤,你可以使用 [http_client.c](vscode-file://vscode-app/d:/VS Code/resources/app/out/vs/code/electron-sandbox/workbench/workbench.html) 文件中的 HTTP 客户端功能发起 HTTP 请求并处理响应。确保在你的项目中正确配置 lwIP 库,并根据需要调整回调函数的实现。 menuconig开启http 例程路径 luban-lite\packages\third-party\webclient\samples 例程运行逻辑: 终端执行: tab提示命令: 使用例程: 先连接WIFI wlan wifi_connect jianzhiji 8765432111 检查是否分配ip地址 ifconfig ping 183.2.172.177 请求:webclient_get_sample: 运行命令: web_get_test 请求网站并返回数据 作用: 演示如何使用 WebClient 软件包发送 HTTP GET 请求并接收响应数据。文件中的代码展示了如何创建 WebClient 会话、发送 GET 请求、接收响应数据并处理内存分配错误等操作。 例程实现的功能 HTTP GET请求发送 支持向指定URI(如 http://www.rt-thread.com/service/rt-thread.txt)发送GET请求。 可处理 普通请求(分块传输或固定长度)和 简化请求(短数据场景)。 响应数据处理 接收服务器返回的响应数据,并打印输出到终端。 自动处理 分块传输编码(Chunked Transfer Encoding) 和 固定内容长度(Content-Length) 两种HTTP响应格式。 命令行交互支持 通过RT-Thread的 FINSH/MSH 命令行接口,提供用户可配置的测试命令,支持自定义URI和请求模式。 终端输出结果: 使用函数命令,访问 web_get_test url 例如访问官方测试网站 web_get_test http://www.rt-thread.com/service/rt-thread.txt 终端返回的结果 网站内容: 内容可以对应上,证明成功请求网站并返回了内容 自定义URI——访问个人网站 web_get_test https://www.cnblogs.com/tianwuyvlianshui 该问题表示未启用https,确实如其言,我们只开启了http,后续开启https再尝试 测试其他http网站 # 测试 GET 请求 curl http://httpbin.org/get # 测试 POST 请求 curl -X POST http://httpbin.org/post -d "key=value" # 返回指定状态码(如 404) curl http://httpbin.org/status/404 1、测试 GET 请求 web_get_test http://httpbin.org/get httpbin.org 用途:最全面的 HTTP 测试服务,支持 GET/POST/PUT/DELETE 等方法,可返回请求头、IP、状态码等。 该网站是测试网站,用于测试 HTTP 请求的公共服务,它会将你的请求信息原样返回。 测试带参数请求 web_get_test http://httpbin.org/get?name=RT-Thread&id=123 成功添加访问参数 "name": "RT-Thread", "id": "123" 测试html网站 web_get_test http://example.com 成功返回该静态网站的html文件 将返回的html保存打开就是该网站 测试 返回指定状态码(如 404) web_get_test http://httpbin.org/status/404 webclient_post_sample: 运行命令: web_post_test web_post_test url web_post_test http://www.rt-thread.com/service/echo 作用: 演示如何使用 WebClient 软件包发送 HTTP POST 请求并接收响应数据。文件中的代码展示了如何创建 WebClient 会话、发送 POST 请求、接收响应数据并处理内存分配错误等操作。 实现了如何使用 WebClient 软件包发送 HTTP POST 请求并接收响应数据。具体实现了以下功能: 定义常量和变量:定义了响应缓冲区大小、请求头缓冲区大小和默认的 POST 请求 URI。 发送普通 POST 请求: 函数 webclient_post_comm 创建 WebClient 会话,构建请求头,发送 POST 请求,并接收和打印响应数据。 发送简化 POST 请求: 函数 webclient_post_smpl 创建请求头,发送 POST 请求,并接收和打印响应数据。 测试函数: 函数 webclient_post_test 根据命令行参数选择发送普通 POST 请求或简化 POST 请求,并处理 URI 内存分配和释放。 命令行集成: 使用 FinSH 命令行系统集成测试命令 web_post_test,可以在命令行中执行 POST 请求测试。 终端输出结果: webclient_shard_download_sample: 运行命令: web_shard_test 作用: 演示如何使用 WebClient 软件包进行分片下载。文件中的代码展示了如何发送 HTTP GET 请求并分片接收响应数据,以及如何处理和打印接收到的数据。 终端输出结果: 测试http pose: 如果服务器是回显服务(如 http://www.rt-thread.com/service/echo),响应内容应与发送的 post_data 完全一致 终端测试命令: web_post_test http://www.rt-thread.com/service/echo pose发送内容: 终端打印pose响应 响应内容应与发送的 post_data 完全一致 D133 ping 电脑 电脑与板子同连手机热点 电脑ip 板卡ip 可以看到在同一局域网192.168.106.xxx 理论上可以ping通,实际则可能不行,原因是电脑防护墙阻挡 关闭则可以ping通(测试完记得重新开启) 抓包 web_post_test http://192.168.106.31:8080 带参数 web_post_test http://192.168.106.31:8080?a=1&b=2&c=3 测试单片机http pose请求 使用python搭建内网服务器 from http.server import BaseHTTPRequestHandler, HTTPServer # 从http.server模块导入BaseHTTPRequestHandler和HTTPServer类 import time # 导入time模块,用于处理时间相关功能 host_ip = "0.0.0.0" # 监听所有IP地址,表示服务器将接受来自任何IP的请求 port = 8080 # 端口号 class RequestHandler(BaseHTTPRequestHandler): # 定义一个处理POST请求的方法 def do_POST(self): # 从请求头中获取Content-Length字段,该字段表示请求体的长度 content_length = int(self.headers['Content-Length']) # 读取请求体数据,长度为content_length,并将其解码为utf-8字符串 post_data = self.rfile.read(content_length).decode('utf-8') # 打印接收到的POST数据 print(f"Received POST data: {post_data}") # 发送HTTP响应状态码200,表示请求成功 self.send_response(200) # 发送响应头,设置Content-type为text/plain,表示响应体是纯文本 self.send_header('Content-type', 'text/plain') # 结束响应头 self.end_headers() # 向客户端发送响应体数据"OK",表示处理成功 self.wfile.write(b"OK") server = HTTPServer((host_ip, port), RequestHandler) print(f"Server running at http://{host_ip}:{port}") server.serve_forever() D133端(客户端,发送端,发送HTTP POST请求到服务器(内网)): 请求的数据(默认POST请求数据) 将数据通过http post 发送到服务器 命令(命令+网址): web_post_test http://192.168.106.31:8080 在PC PC端(内网服务器,服务端,作接收端) 数据上报对接api URL头部 http://xxx URL头部+api web_post_test http://xxx/api/yyy Postman Postman 是一款广泛应用于 API 开发与测试 的协作工具,旨在简化 API 的生命周期管理(设计、开发、测试、部署、监控等)。 主要功能 API 请求测试与调试 支持发送 HTTP/HTTPS 请求(GET、POST、PUT、DELETE 等)。 可自定义请求头(Headers)、参数(Params)、身份验证(OAuth、JWT 等)和请求体(Body)。 直观的界面帮助快速调试 API,查看响应状态码、响应时间和返回数据(JSON、XML 等)。 自动化测试 使用 JavaScript 编写测试脚本,验证 API 响应是否符合预期。 支持断言(Assertions)、动态变量和环境变量,提升测试灵活性。 集成 CI/CD 工具(如 Jenkins、GitHub Actions)通过 Newman 命令行工具运行自动化测试。 协作与文档 团队可共享 API 集合(Collections) 和环境配置,提升协作效率。 自动生成 API 文档,支持一键发布,便于前后端开发者或第三方调用者查阅。 Mock 服务器 创建虚拟 API 端点,模拟真实响应,支持前端开发与后端 API 并行工作。 监控与性能测试 定期运行 API 测试,监控服务可用性与性能,生成报告分析潜在问题。 适用场景 开发者:快速调试接口,验证功能逻辑。 测试工程师:设计自动化测试用例,确保 API 稳定性。 团队协作:统一管理 API 资产,减少沟通成本。 文档编写:自动生成易读的 API 文档,替代手动维护。 版本与平台 免费版:满足个人基础需求(请求测试、简单自动化)。 付费版(Pro/Enterprise):解锁团队协作、高级监控、私有 API 文档等功能。 跨平台支持:提供 Windows、macOS、Linux 客户端及网页版。 优势 用户友好:图形化操作降低学习成本,替代命令行工具(如 cURL)。 生态丰富:支持 OpenAPI 规范、WebSocket 测试、GraphQL 等扩展场景。 云端同步:数据云端存储,多设备无缝切换。 简单示例 在 Postman 中新建请求,输入 API 地址。 设置请求方法(如 POST),添加 JSON 格式的请求体。 添加测试脚本验证响应状态码是否为 200。 保存到集合,分享给团队成员或生成文档。 无论是独立开发还是团队协作,Postman 都能显著提升 API 开发效率,是现代软件工程中不可或缺的工具之一。 API文档解析与使用 api接口网址 http://xxx 涉及实际产品,略 Postman测试应用 api接口网址 http://xxx 时间戳转换网站 https://tool.lu/timestamp/ 网站头URL http://xxx 使用方式:网站头URL+API尾部 例如 则使用该api的URL为 http://xxx/api/yyy 添加CJSON解析库 在串口终端控制HTTP_API访问服务器 /* 演示如何使用 WebClient 软件包发送 HTTP POST 请求并接收响应数据。 文件中的代码展示了如何创建 WebClient 会话、发送 POST 请求、 接收响应数据并处理内存分配错误等操作。 */ #include <string.h> #include <rtthread.h> #include <webclient.h> #include <stdio.h> // 定义POST请求响应缓冲区的大小为1024字节 #define POST_RESP_BUFSZ 1024 // 定义POST请求头部缓冲区的大小为1024字节 #define POST_HEADER_BUFSZ 1024 **涉及实际产品,略** typedef enum { API_GET_BALANCE_NO, // 远程编码 API_LOAD_KEY, // 获取密钥 **涉及实际产品,略** } API_TYPE; // API地址映射表 static const char* API_URI_MAP[] = { [API_GET_BALANCE_NO] = "**涉及实际产品,略**", ....... }; // 获取API地址的宏 #define GET_API_URI(api_type) (API_URI_MAP[(api_type)]) /* 静态函数获取URI */ static inline const char* get_api_uri(API_TYPE type) { return API_URI_MAP[type]; } // /* 函数调用初始化 */ // const char* POST_LOCAL_URI = NULL; // void post_api_uris(char str) { // POST_LOCAL_URI = get_api_uri(str); // } // 获取时间戳函数(get) // 定义一个静态函数get_timestamp,用于获取时间戳并存储在buffer中 static int get_timestamp(char *buffer, size_t buffer_size) { // 定义一个指向webclient_session结构体的指针session,并初始化为RT_NULL struct webclient_session *session = RT_NULL; // 定义一个指向字符数组的指针response_buf,用于存储HTTP响应,并初始化为RT_NULL char *response_buf = RT_NULL; // 定义一个整型变量ret,用于存储函数返回值,并初始化为RT_EOK(表示成功) int ret = RT_EOK; // 定义一个常量字符串url,指向获取时间戳的URL const char *url = "http://acs.m.taobao.com/gw/mtop.common.getTimestamp/"; // 创建一个webclient会话,缓冲区大小为512字节 session = webclient_session_create(512); // 如果会话创建失败,打印错误信息并返回内存不足错误码 if (!session) { rt_kprintf("[ERROR] Create session failed\n"); return -RT_ENOMEM; } // 使用webclient发送GET请求,如果返回值不是200(HTTP OK),打印错误信息并设置返回值为错误码 if ((ret = webclient_get(session, url)) != 200) { rt_kprintf("[ERROR] GET failed (%d)\n", ret); ret = -RT_ERROR; // 跳转到退出标签,进行资源释放 goto __exit; } // 分配256字节的内存用于存储HTTP响应 response_buf = web_malloc(256); // 如果内存分配失败,设置返回值为内存不足错误码并跳转到退出标签 if (!response_buf) { ret = -RT_ENOMEM; goto __exit; } // 从webclient会话中读取响应数据到response_buf,最多读取256字节 int total_read = webclient_read(session, response_buf, 256); // 如果读取失败或没有读取到数据,设置返回值为错误码并跳转到退出标签 if (total_read <= 0) { ret = -RT_ERROR; goto __exit; } // 在response_buf的末尾添加字符串结束符 response_buf[total_read] = '\0'; // 在response_buf中查找时间戳的开始位置 char *t_start = strstr(response_buf, "\"t\":\""); // 如果找到了时间戳的开始位置 if (t_start) { // 移动指针到时间戳的实际开始位置 t_start += 5; // 查找时间戳的结束位置 char *t_end = strchr(t_start, '\"'); // 如果找到了时间戳的结束位置 if (t_end) { // 计算时间戳的长度 int t_len = t_end - t_start; // 如果时间戳长度大于等于buffer_size,打印错误信息并设置返回值为错误码 if (t_len >= buffer_size) { rt_kprintf("[ERROR] Timestamp too long\n"); ret = -RT_ERROR; goto __exit; } // 将时间戳复制到buffer中 strncpy(buffer, t_start, t_len); // 在buffer的末尾添加字符串结束符 buffer[t_len] = '\0'; // 打印获取到的时间戳 rt_kprintf("[INFO] Time value: %s\n", buffer); } else { // 如果没有找到时间戳的结束位置,设置返回值为错误码 ret = -RT_ERROR; } } else { // 如果没有找到时间戳的开始位置,打印错误信息并设置返回值为错误码 rt_kprintf("[ERROR] Invalid response format\n"); ret = -RT_ERROR; } // 退出标签,用于释放资源 __exit: // 如果会话指针不为空,关闭会话 if (session) webclient_close(session); // 如果响应缓冲区指针不为空,释放内存 if (response_buf) web_free(response_buf); // 返回函数执行结果 return ret; } // 定义一个指向常量字符数组的指针post_data // const char *post_data = "{\"uniqueCode\":\"0102250310001\"}"; // 转义后的合法字符串 /* send HTTP POST request by common request interface, it used to receive longer data */ // 定义一个静态函数webclient_post_comm,用于发送POST请求并处理响应 /*核心功能 发送 HTTP POST 请求 将 post_data 数据通过 POST 方式发送到指定的 uri(服务器地址)。 自动添加必要的 HTTP 头部(如 Content-Length 和 Content-Type)。 支持处理二进制或文本数据(通过 application/octet-stream 类型)。 接收服务器响应 读取服务器返回的响应数据,并通过串口打印输出。 支持分块读取响应内容(循环读取直到数据结束)。 错误处理 内存分配失败时返回错误码(-RT_ENOMEM)。 检测 HTTP 响应状态码,若非 200 则视为错误。 确保资源释放(会话关闭、内存回收),避免内存泄漏*/ static int webclient_post_comm(const char *uri, const void *post_data, size_t data_len) { // 定义一个指向webclient会话结构的指针session,并初始化为RT_NULL struct webclient_session* session = RT_NULL; // 定义一个指向unsigned char类型的指针buffer,并初始化为RT_NULL unsigned char *buffer = RT_NULL; // 定义一个整型变量index,用于循环计数 int index, ret = 0; // 定义一个整型变量bytes_read,用于存储每次读取的字节数 int bytes_read, resp_status; // 1. 分配响应缓冲区 buffer = (unsigned char *) web_malloc(POST_RESP_BUFSZ); // 如果内存分配失败,打印错误信息并返回内存不足错误码 if (buffer == RT_NULL) { rt_kprintf("no memory for receive response buffer.\n"); ret = -RT_ENOMEM; goto __exit;// 内存不足时退出 } // 2. 创建 WebClient 会话 /* create webclient session and set header response size */ session = webclient_session_create(POST_HEADER_BUFSZ);// 初始化 HTTP 会话 if (session == RT_NULL) { ret = -RT_ENOMEM; goto __exit; // 创建失败时退出 } // 3. 添加 HTTP 头部 /* build header for upload 用于上传的 build 标头*/ webclient_header_fields_add(session, "Content-Length: %d\r\n", strlen(post_data));// 数据长度 webclient_header_fields_add(session, "Content-Type: application/json\r\n");// 数据类型 /* send POST request by default header */ // 4. 发送 POST 请求,返200表示成功 if ((resp_status = webclient_post(session, uri, post_data, data_len)) != 200) // 发送数据 { rt_kprintf("webclient POST request failed, response(%d) error.\n", resp_status);//打印POST 请求失败 ret = -RT_ERROR; goto __exit; } // 5. 读取并打印服务器响应数据 rt_kprintf("webclient post response data: \n"); do { bytes_read = webclient_read(session, buffer, POST_RESP_BUFSZ);// 分块读取 if (bytes_read <= 0) { break; } for (index = 0; index < bytes_read; index++) { rt_kprintf("%c", buffer[index]);// 逐字符打印响应内容 } } while (1); rt_kprintf("\n"); __exit: if (session) { webclient_close(session); session = RT_NULL; } if (buffer) { web_free(buffer); } return ret; } /* send HTTP POST request by simplify request interface, it used to received shorter data */ // 定义一个静态函数webclient_post_smpl,用于发送HTTP POST请求并处理响应 // 参数uri:请求的URI // 参数post_data:POST请求的数据 // 参数data_len:POST请求数据的长度 static int webclient_post_smpl(const char *uri, const char *post_data, size_t data_len) { // 定义一个指针response,用于存储服务器响应的数据,初始为RT_NULL char *response = RT_NULL; // 定义一个指针header,用于存储HTTP请求头,初始为RT_NULL char *header = RT_NULL; // 定义一个变量resp_len,用于存储服务器响应的数据长度,初始为0 size_t resp_len = 0; // 定义一个变量index,用于循环遍历响应数据,初始为0 int index = 0; // 添加Content-Length头到HTTP请求头中,值是post_data的长度 webclient_request_header_add(&header, "Content-Length: %d\r\n", strlen(post_data)); // 添加Content-Type头到HTTP请求头中,值是application/json webclient_request_header_add(&header, "Content-Type: application/json\r\n"); // 发送HTTP POST请求,并接收服务器响应 // 如果请求失败,打印错误信息,释放header内存,返回-RT_ERROR if (webclient_request(uri, header, post_data, data_len, (void **)&response, &resp_len) < 0) { rt_kprintf("webclient send post request failed."); web_free(header); return -RT_ERROR; } // 打印发送POST请求成功的提示信息 rt_kprintf("webclient send post request by simplify request interface.\n"); // 打印服务器响应的数据提示信息 rt_kprintf("webclient post response data: \n"); // 遍历响应数据并逐字符打印 for (index = 0; index < resp_len; index++) { rt_kprintf("%c", response[index]); } // 打印换行符 rt_kprintf("\n"); // 如果header不为空,释放header内存 if (header) { web_free(header); } // 如果response不为空,释放response内存 if (response) { web_free(response); } // 返回0表示函数执行成功 return 0; } //命令使用的函数 int webclient_post_test_API(int argc, char **argv, char* post_data) { char *uri = RT_NULL; // 初始化URI指针为NULL // init_api_uris(API_GET_BALANCE_NO); if (argc < 2) // 如果参数个数为1 { uri = web_strdup(argv[1]); // 复制默认的POST本地URI if(uri == RT_NULL) // 如果内存分配失败 { rt_kprintf("no memory for create post request uri buffer.\n"); // 打印错误信息 return -RT_ENOMEM; // 返回内存不足错误码 } //const char *post_data = "RT-Thread is an open source IoT operating system from China!"; webclient_post_comm(uri, (void *)post_data, strlen(post_data)); // 发送普通POST请求 } else if (argc == 2) // 如果参数个数为2 { if (strcmp(argv[1], "-s") == 0) // 如果第一个参数是"-s" { uri = web_strdup(argv[1]); // 复制默认的POST本地URI if(uri == RT_NULL) // 如果内存分配失败 { rt_kprintf("no memory for create post request uri buffer.\n"); // 打印错误信息 return -RT_ENOMEM; // 返回内存不足错误码 } webclient_post_smpl(uri, (void *)post_data, strlen(post_data)); // 发送简化POST请求 } else // 如果第一个参数不是"-s" { uri = web_strdup(argv[1]); // 复制用户提供的URI if(uri == RT_NULL) // 如果内存分配失败 { rt_kprintf("no memory for create post request uri buffer.\n"); // 打印错误信息 return -RT_ENOMEM; // 返回内存不足错误码 } webclient_post_comm(uri, (void *)post_data, strlen(post_data)); // 发送普通POST请求 } } else if(argc == 3 && strcmp(argv[1], "-s") == 0) // 如果参数个数为3且第一个参数是"-s" { uri = web_strdup(argv[2]); // 复制用户提供的URI if(uri == RT_NULL) // 如果内存分配失败 { rt_kprintf("no memory for create post request uri buffer.\n"); // 打印错误信息 return -RT_ENOMEM; // 返回内存不足错误码 } webclient_post_smpl(uri, (void *)post_data, strlen(post_data)); // 发送简化POST请求 } else // 如果参数个数不符合要求 { rt_kprintf("web_post_test [uri] - webclient post request test.\n"); // 打印使用说明 rt_kprintf("web_post_test -s [uri] - webclient simplify post request test.\n"); // 打印使用说明 return -RT_ERROR; // 返回错误码 } if (uri) // 如果URI指针不为NULL { web_free(uri); // 释放URI内存 } return RT_EOK; // 返回成功码 } /* 增强版API请求函数 */ int post_api_uris(API_TYPE api_type) { int ret = RT_EOK; char* post_json = RT_NULL; // 需要发送的json数据 const char *uri = RT_NULL; //对接的api地址 char time_val[32] = {0}; // 定义一个时间戳数组,用于存储时间戳字符串 // 校验码 使用 HMAC SHA256 (Base64) 签名方法进行签名,非空参数 int* sha256_sign = 0; // 电子秤加载全部配置信息,0不加载,1全部加载 int* dzc_isload = 1; /* 根据API类型生成不同JSON数据 */ switch (api_type) { // 获取电子秤密钥 case API_LOAD_KEY: { post_json = web_malloc(256); // 分配256字节的内存空间给post_json变量,用于存储JSON数据 // 时间戳获取,转化为秒 ret = (get_timestamp(time_val, sizeof(time_val))/1000); if (ret != RT_EOK) { rt_kprintf("[ERROR] Failed to get timestamp\n"); return ret; } snprintf(post_json, 256, "{\"key\":\"%s\",\"isload\":\"%d\",\"timestamp\":\"%s\",\"sign\":\"%d\"}", xinpian, dzc_isload, time_val, sha256_sign); break; } // 获取台秤基础信息 case API_BALANCE_INFO: { post_json = web_malloc(256); // 时间戳获取,转化为秒 ret = (get_timestamp(time_val, sizeof(time_val))/1000); if (ret != RT_EOK) { rt_kprintf("[ERROR] Failed to get timestamp\n"); return ret; } snprintf(post_json, 256, "{\"key\":\"%s\",\"isload\":\"%d\",\"timestamp\":\"%s\",\"sign\":\"%d\"}", xinpian, dzc_isload, time_val, sha256_sign); break; } // 订单状态查询 case API_PAY_STATUS: { post_json = web_malloc(256); // 时间戳获取,转化为秒 ret = (get_timestamp(time_val, sizeof(time_val))/1000); if (ret != RT_EOK) { rt_kprintf("[ERROR] Failed to get timestamp\n"); return ret; } snprintf(post_json, 256, "{\"key\":\"%s\",\"isload\":\"%d\",\"timestamp\":\"%s\",\"sign\":\"%d\"}", xinpian, dzc_isload, time_val, sha256_sign); break; } default: rt_kprintf("暂未实现的API类型: %d\n", api_type); return -RT_EINVAL; } memset(time_val, 0, sizeof(time_val)); // 清空时间戳 /* 检查内存分配 */ if (!post_json) { rt_kprintf("Memory allocation failed\n"); return -RT_ENOMEM; } /* 获取API地址 */ uri = get_api_uri(api_type); if (!uri) { rt_kprintf("Invalid API URI\n"); web_free(post_json); return -RT_ERROR; } /* 发送请求 */ char* argv[] = { "web_post_test", (char*)uri }; ret = webclient_post_test_API(2, argv, post_json); web_free(post_json); return ret; } // 旧命令入口函数 // #ifdef FINSH_USING_MSH // #include <finsh.h>// 命令入口函数 开启命令 // MSH_CMD_EXPORT_ALIAS(webclient_post_test_API, web_post_test, webclient post request test.); // #endif /* FINSH_USING_MSH */ #ifdef FINSH_USING_MSH #include <finsh.h> /* API类型映射表 */ static const struct { const char *name; API_TYPE type; } api_type_map[] = { {"get_balance", API_GET_BALANCE_NO}, // 通过远程编码获得在线通信编码 **涉及实际产品,略** }; /* 新命令入口函数 */ static void api_cmd(int argc, char **argv) { if (argc != 2) { rt_kprintf("Usage: api_call <type>\n" "Available types:\n" "get_balance, load_key, balance_info, pay_status, get_pay, order_add, set_file, set_status, get_breed, set_price, get_vip, log_report, add_points, get_time, get_version\n"); return; } /* 查找匹配的API类型 */ for (size_t i = 0; i < sizeof(api_type_map)/sizeof(api_type_map[0]); i++) { if (strcmp(argv[1], api_type_map[i].name) == 0) { int ret = post_api_uris(api_type_map[i].type); rt_kprintf("API调用结果: %s\n", ret == RT_EOK ? "成功" : "失败"); return; } } rt_kprintf("无效的API类型!\n"); } /* 注册新命令 */ MSH_CMD_EXPORT_ALIAS(api_cmd, api_call, Call specified API interface); #endif /* FINSH_USING_MSH */ /* 编译并运行程序: 确保你的 RT-Thread 环境已经正确配置,并且该文件已经编译进你的固件中。启动你的设备并进入 RT-Thread 的命令行界面。 使用命令调用 API: 你可以使用 api_call 命令来调用不同的 API 接口。命令格式如下: api_call <type> 示例: 获取远程编码: api_call get_balance 串口 涉及实际产品,略 术语 组成 净重 物品自身重量 Mg 毛重 净重 + 皮重 Mg+mg 皮重 包装/容器重量 mg 命令 0X20——一键发送重量 1 2 3 4 5 6-9 10 11-13 14 15 包头 长度 命令 正负+单位值 小数位 净重 稳定状态 皮重 验证码 包尾 0xAA 0x0B 0x20 +KG:0x81;-KG:0x01 UCHAR UINT32 YES:0x0A;NO:0x08 3 BYTE UCHAR 0x2F 试验数据 1 2 3 4 5 6-9 10 11-13 14 15 包头 长度 命令 正负+单位值 小数位 净重(4) 稳定状态 皮重 验证码 包尾 数据1: 01 03 00 01 86 A0 0A 00 00 00 结果1: -KG -100.00 100 Yes 数据2: 04 结果2 -10.00 小数位分析: 结论:输出数据=净重位数据*10^-(小数位数据) 例如 净重位数据=(0x64-》100) 小数位数据位=0x02 结果=100*10^(-2)=1 验证: 结论正确 数据1: AA 0B 20 01 03 00 01 86 A0 0A 00 00 00 FA 2F AA 0B 20 01 03 00 01 86 A0 0A 00 00 00 FA 2F 整数部分长度 = 6-小数位 00: 100000(6),6-0=6 03:100(3),6-3=3 04:10(2),6-4=2 净重位(4)分析: 数据=净重位16进制转10进制 想接收2580 需发送:符号位:81(+),小数位:00(数据x10^-0),净重:00 00 0A 14(2580) 验证: 发送:00 00 0A 14 接收:2.58Kg(小数位03,) 解析: 包头 AA -- 长度 0B 11 命令 20 一键发送重量 正负+单位 01 -x 小数位 03 净重(3byte) 00 01 86 稳定状态 0A yes 皮重(3byte) 00 00 00 验证码 FA 忽略 包尾 2F -- Raw: AA 0B 20 01 03 00 01 86 A0 0A 00 00 00 FA 2F Data=Net: -100.00 KG | Stable: Yes | Tare: 0 AA 0B 20 01 03 00 01 86 A0 0A 00 00 00 FA 2F 解析: 使用 包头 AA 长度 0B 11 命令 20 一键发送重量 正负+单位 01 -x 小数位 03 数据=净重*10^(-小数位) 净重(3byte) 00 01 86 数据=净重(16进制)转(10进制) 稳定状态 0A yes 皮重(3byte) 00 00 00 验证码 FA 忽略 待了解 包尾 2F -- Raw: AA 0B 20 01 03 00 01 86 A0 0A 00 00 00 FA 2F Data=Net: -100.00 KG | Stable: Yes | Tare: 0 皮重(3byte): 00 00 00:0 00 00 32 :50 00 00 64: 100 校验码: 校验码的计算规则为: 将包头(0xAA)到皮重字段(第13字节)的所有字节进行累加,取累加和的低8位作为校验码。以下是完整验证过程和示例解析: 校验码计算规则 计算范围 起始:包头 0xAA(第1字节) 结束:皮重字段的第3字节(第13字节) 计算方法 $$ \text{校验码} = \left( \sum_{i=1}^{13} \text{字节}_i \right) \mod 256校验码=(i=1∑13字节i)mod256 $$ 终端控制串口发送命令 #include "user_uart3.h" uint8_t cmd20[] = {//一键发送重量One-click send weight 0xAA,0x01,0x20,0xCB,0x2F, 0x00 // 终止符 }; uint8_t cmd21_GW[] = {//取毛重,发送有问题,终止符和命令相同导致Take the gross weight. 0xAA,0x02,0x21,0x00,0xCD,0x2F, 0x00 // 终止符 }; //一键发送重量One-click send weight void send_command_0x20(void) { Serial_Send_String(cmd20); }MSH_CMD_EXPORT(send_command_0x20, CMD_20 One-click send weight); //取毛重,发送有问题,终止符和命令相同导致Take the gross weight. void send_command_0x21_GW(void) { Serial_Send_String(cmd21_GW); }MSH_CMD_EXPORT(send_command_0x21_GW,CMD_21_GW Take the net weight);// 导出函数为命令 //略...... 串口解析代码 #include "Serial_Port_Analysis.h"//添加串口解析命令头文件 // 十六进制字符串转字节数组 uint8_t* hex_str_to_bytes(const char* hex_str, size_t* out_len) { char* tmp = strdup(hex_str); char* token = strtok(tmp, " "); uint8_t* bytes = malloc(strlen(hex_str)/3 + 1); size_t count = 0; while (token != NULL) { bytes[count++] = (uint8_t)strtol(token, NULL, 16); token = strtok(NULL, " "); } *out_len = count; free(tmp); return bytes; } // 数据包分割(改进版) PacketList split_packets(const uint8_t* data, size_t len) { PacketList list = {NULL, 0}; size_t start = 0; int in_packet = 0; for (size_t i = 0; i < len; ++i) { if (data[i] == HEADER && !in_packet) { start = i; in_packet = 1; } else if (data[i] == FOOTER && in_packet) { size_t pkt_len = i - start + 1; list.packets = realloc(list.packets, (list.count+1)*sizeof(Packet)); list.packets[list.count].data = malloc(pkt_len); memcpy(list.packets[list.count].data, data+start, pkt_len); list.packets[list.count].length = pkt_len; list.count++; in_packet = 0; } } return list; } // 函数声明:计算校验和 // 参数:data - 指向数据数组的指针,length - 数据数组的长度 // 返回值:计算得到的校验和(8位无符号整数) uint8_t calculate_checksum(uint8_t *data, size_t length) { uint32_t checksum = 0; // 使用32位整数来避免溢出 for (size_t i = 0; i < length - 2; i++) { checksum += data[i]; } return checksum % 256; // 取低8位 } // 解析单个数据包(支持多命令) ParsedPacket parse_packet(const uint8_t* data, size_t len) { ParsedPacket result = {0}; // ADD_ParsedPacket ADD_result = {0}; // 基础校验 if (len < 5 || data[0] != HEADER || data[len-1] != FOOTER) { rt_kprintf("无效数据包\n"); return result; } // 通用字段解析 result.header = data[0];//包头 result.length = data[1];//数据长度 result.command = data[2];//命令 result.footer = data[len-1];//包尾 // 按命令类型处理 switch (result.command) { case 0x20: { // 重量数据包 if (len < 11) break;//15-4:data_len-4 //校验码 result.checksum = data[len-2]; // 校验和校验 uint8_t calculated_checksum = calculate_checksum(data, len-2); if (result.checksum != calculated_checksum) { rt_kprintf("校验和错误\n"); return result; } // 符号和单位 switch (data[3]) { case 0x81: strcpy(result.sign_unit, "+KG"); break; case 0x01: strcpy(result.sign_unit, "-KG"); break; default: strcpy(result.sign_unit, "未知"); } // 小数位 result.decimal = data[4]; // 净重(4字节大端) uint32_t net_weight_raw = (data[5] << 24) | (data[6] << 16) | (data[7] << 8) | data[8]; result.net_weight = net_weight_raw / pow(10, result.decimal); // 稳定状态 strcpy(result.stability, (data[9] == 0x0A) ? "稳定" : "不稳定"); // 皮重(3字节) uint32_t tare = (data[10] << 16) | (data[11] << 8) | data[12]; result.tare = tare / pow(10, result.decimal); break; } case 0x21: { // 取毛重或净重数据包 if (len < 7) break; // 符号和单位 switch (data[3]) { case 0x81: strcpy(result.sign_unit, "+KG"); break; case 0x01: strcpy(result.sign_unit, "-KG"); break; default: strcpy(result.sign_unit, "未知"); } // 小数位 result.decimal = data[4]; // 净重/毛重(4字节大端) uint32_t net_weight_raw = (data[5] << 24) | (data[6] << 16) | (data[7] << 8) | data[8]; result.net_weight = net_weight_raw / pow(10, result.decimal); //校验码 result.checksum = data[len-2]; // 校验和校验 uint8_t calculated_checksum = calculate_checksum(data, len-2); if (result.checksum != calculated_checksum) { rt_kprintf("校验和错误\n"); return result; } break; } case 0x22: { // 去皮应答 // 包头1 + 长度1 + 命令1 + 状态1 + 校验1 + 包尾1 = 6字节 if (len != 6) { rt_kprintf("ERR: 去皮包长度错误\n"); // return result; } // 状态字段解析 switch (data[3]) { case 0x00: strncpy(result.ACK, "去皮成功", sizeof(result.ACK)); // rt_kprintf("去皮成功\n"); break; case 0x01: strncpy(result.ACK, "拥堵去皮", sizeof(result.ACK)); // rt_kprintf("拥堵,去皮失败\n"); break; case 0x02: strncpy(result.ACK, "非法命令", sizeof(result.ACK)); // rt_kprintf("非法命令\n"); break; default: strncpy(result.ACK, "未知状态", sizeof(result.ACK)); // rt_kprintf("未知状态\n"); } break; //校验码 result.checksum = data[len-2]; // 校验和校验 uint8_t calculated_checksum = calculate_checksum(data, len-2); if (result.checksum != calculated_checksum) { rt_kprintf("校验和错误\n"); return result; } } case 0x23: { // 回皮(取皮重) if (len < 7) break; // 符号和单位 switch (data[3]) { case 0x81: strcpy(result.sign_unit, "+KG"); break; case 0x01: strcpy(result.sign_unit, "-KG"); break; default: strcpy(result.sign_unit, "未知"); } // 小数位 result.decimal = data[4]; // 皮重(4字节大端) uint32_t net_weight_raw = (data[5] << 24) | (data[6] << 16) | (data[7] << 8) | data[8]; result.net_weight = net_weight_raw / pow(10, result.decimal); //校验码 result.checksum = data[len-2]; // 校验和校验 uint8_t calculated_checksum = calculate_checksum(data, len-2); if (result.checksum != calculated_checksum) { rt_kprintf("校验和错误\n"); return result; } break; } case 0x24: { // 置零应答 if (len != 6) { rt_kprintf("ERR: 置零包长度错误\n"); // return result; } // 状态字段解析 switch (data[3]) { case 0x00: strncpy(result.ACK, "置零成功", sizeof(result.ACK)); // rt_kprintf("去皮成功\n"); break; case 0x01: strncpy(result.ACK, "置零去皮", sizeof(result.ACK)); // rt_kprintf("拥堵,去皮失败\n"); break; case 0x02: strncpy(result.ACK, "非法命令", sizeof(result.ACK)); // rt_kprintf("非法命令\n"); break; default: strncpy(result.ACK, "未知状态", sizeof(result.ACK)); // rt_kprintf("未知状态\n"); } break; //校验码 result.checksum = data[len-2]; // 校验和校验 uint8_t calculated_checksum = calculate_checksum(data, len-2); if (result.checksum != calculated_checksum) { rt_kprintf("校验和错误\n"); return result; } } default: rt_kprintf("未知命令: 0x%02X\n", result.command); } return result; } // 解析数据包,打印结果 void print_packet(const ParsedPacket* pkt) { char buffer[100]; rt_kprintf("包头: 0x%02X\n", pkt->header); rt_kprintf("长度: 0x%02X\n", pkt->length); rt_kprintf("命令: 0x%02X\n", pkt->command); if (pkt->command == 0x20) { rt_kprintf("单位: %s\n", pkt->sign_unit); rt_kprintf("小数位: %d\n", pkt->decimal); snprintf(buffer, sizeof(buffer), "%.*f", pkt->decimal, pkt->net_weight); rt_kprintf("净重: %s\n", buffer); // rt_kprintf("净重: %.*f\n", pkt->decimal, pkt->net_weight); rt_kprintf("稳定状态: %s\n", pkt->stability); snprintf(buffer, sizeof(buffer), "%.*f", pkt->decimal, pkt->tare); rt_kprintf("皮重: %s\n", buffer); // rt_kprintf("皮重: %.*f\n", pkt->decimal, pkt->tare); } else if (pkt->command == 0x21) { rt_kprintf("单位: %s\n", pkt->sign_unit); rt_kprintf("小数位: %d\n", pkt->decimal); snprintf(buffer, sizeof(buffer), "%.*f", pkt->decimal, pkt->net_weight); rt_kprintf("净重/毛重: %s\n", buffer); // rt_kprintf("净重: %.*f\n", pkt->decimal, pkt->net_weight); }else if (pkt->command == 0x22) { rt_kprintf("去皮状态: %s\n", pkt->ACK); }else if (pkt->command == 0x23) { rt_kprintf("单位: %s\n", pkt->sign_unit); rt_kprintf("小数位: %d\n", pkt->decimal); snprintf(buffer, sizeof(buffer), "%.*f", pkt->decimal, pkt->net_weight); rt_kprintf("皮重: %s\n", buffer); }else if (pkt->command == 0x24) { rt_kprintf("置零状态: %s\n", pkt->ACK); } rt_kprintf("包尾: 0x%02X\n\n", pkt->footer); } 作用:串口输入十六进制数据流,自动按照包头AA,包尾2F进行分包,然后按照分别解析各数据包命令和数据。 LVGL 4.3寸RGB565(480*272)的屏幕,触摸芯片是GT911。 menuconfig配置 Application options ---> *** Filesystem related *** [*] Using File System Image 0 ---> --- Using File System Image 0 Select File System Type (FATFS) ---> (packages/artinchip/lvgl-ui/aic_demo/base_demo/lvgl_src/) Data Directory (app.fatfs) Image Name [*] auto calcuate image size [ ] Using File System Image 1 ---- *** lvgl demo select related *** -*- LVGL (official): powerful and easy-to-use embedded GUI library ---> (20) Priority of LVGL thread (32768) Stack size of LVGL thread (5) Display refresh period (ms) [ ] Support SquareLine Studio [ ] Enable built-in examples [ ] Enable built-in demos -*- ArtInChip lvgl demo select lvgl demo (lvgl demo with basic function) ---> (X) lvgl demo with basic function ( ) lvgl demo of meter (16) LVGL color depth(32/16) (8) LVGL image cached number (/rodata/lvgl_data) LVGL Resource Directory 文件系统映像设置 lvgl设置 lvgl例程设置 移植EEZ 1、复制EEZ Studio生成的ui文件夹至\packages\artinchip\lvgl-ui\aic_demo下 2、在ui.c和ui.h中屏蔽EEZ_FLOW相关定义,防止找不到EEZ_FLOW类头文件报错 ui.c ui.h 3、在ui文件夹下添加SConscript文件 from building import * # 导入`building`模块的所有内容,是用于构建系统的工具和配置。 import os # 导入`os`模块,用于文件和路径操作。 cwd = GetCurrentDir() # 获取当前工作目录路径,赋值给变量`cwd`。 group = [] # 初始化一个空列表`group`,用于存储构建的代码组信息。 src = Glob('*.c') # 使用 `Glob` 函数查找当前目录下的所有 `.c` 文件,并将其文件路径存储到变量 `src` 中。 # `Glob` 是构建系统中的常见工具函数,用于匹配文件。 CPPPATH = [cwd] # 定义预处理器头文件路径列表 `CPPPATH`,初始包含当前目录路径。 list = os.listdir(cwd) # 使用 `os.listdir` 列出当前目录下的所有文件和文件夹,存储在变量 `list` 中。 for d in list: # 遍历目录内容 `list` 中的每一项。 path = os.path.join(cwd, d) # 构造完整路径 `path`,将 `cwd` 和当前项 `d` 拼接起来。 if os.path.isfile(os.path.join(path, 'SConscript')): # 检查当前路径是否包含名为 `SConscript` 的文件。 group = group + SConscript(os.path.join(d, 'SConscript')) # 如果存在 `SConscript` 文件,则调用 `SConscript` 函数执行,并将返回的结果添加到 `group` 列表中。 # `SConscript` 是 SCons 构建工具中的函数,用于加载子目录的构建脚本。 group = group + DefineGroup('LVGL-port', src, depend = ['EEZ_FOR_LVGL'], CPPPATH = CPPPATH) #修改的地方 ‘EEZ_FOR_LVGL’ # 定义一个新的代码组 `LVGL-port`: # - `src`: 包含所有 `.c` 文件路径。 # - `depend`: 定义依赖项,指定该组依赖于 `EEZ_FOR_LVGL`。 # - `CPPPATH`: 将当前目录路径添加到编译器的头文件搜索路径。 # 最终返回的结果与 `group` 合并。 # 如果定义了 EEZ_FOR_LVGL 则将代码组加入编译! Return('group') # 返回 `group` 变量给调用者,作为最终的代码组信息供后续使用。 4、在\application\Kconfig中添加 6、在menuconfig中选择使用ui例程 Prompt: LVGL EEZ_FOR_LVGL demo │ │ Location: │ │ -> Application options │ │ -> ArtInChip LVGL demo (AIC_LVGL_DEMO [=y]) │ │ -> select LVGL demo (<choice> [=y]) │ │ Defined at application/Kconfig:261 7、在\luban-lite\application\Kconfig中255行添加 # 增加 config EEZ_FOR_LVGL bool "LVGL EEZ_FOR_LVGL demo" E:\D133EBS\ezz_d133_2_font\luban-lite\packages\artinchip\lvgl-ui\aic_demo\ui -- 自己添加的文件夹 ui E:\D133EBS\ezz_d133_2_font\luban-lite\packages\artinchip\lvgl-ui\aic_demo\ui\SConscript #添加 E:\D133EBS\ezz_d133_2_font\luban-lite\packages\artinchip\lvgl-ui\aic_ui.c //130 行 #注释 E:\D133EBS\ezz_d133_2_font\luban-lite\application\Kconfig //255 行 #添加 修改程序结构使LVGL运行在线程里 逻辑:在aic_ui.c中创建线程,将lvgl初始化和运行放入线程中 aic_ui.c中修改过的代码片段 #include "lvgl.h" #include "aic_ui.h" #include "aic_osal.h" #include "lvgl/demos/lv_demos.h" //lvgl线程 static void lvgl_entry(void *parameter) { /* LVGL初始化(如显示驱动、输入设备初始化) */ ui_init(); // 初始化用户界面,包括显示驱动和输入设备等 /* 主循环:周期性调用LVGL任务处理函数 */ while (1) { // lv_task_handler(); // LVGL任务处理 ui_tick(); // UI刷新 rt_thread_mdelay(5); // 延时5ms,避免过度占用CPU } } void aic_ui_init() { /* qc test demo is only for aic internal qc testing, please ignore it. */ // // 修改的地方 // #ifdef EEZ_FOR_LVGL // extern void ui_init(void); // ui_init(); // #endif //创建线程 rt_thread_t lvgl_thread = rt_thread_create("lvgl", lvgl_entry, RT_NULL, 4096, 20, 10); if (lvgl_thread != RT_NULL) { rt_thread_startup(lvgl_thread); } return; } 解释:将lvgl放入线程中,更好由RT-Thread系统管理。 官方例程代码默认逻辑只执行一次初始化,后续lvgl相关操作不易管理,在其余线程中操作lvgl函数导致线程卡死。 将lvgl放入线程中避免线程卡死,同时可以在该线程中刷新显示,实时更新变量和页面 修改变量显示 EEZ默认显示浮点型和字符型变量会报错类型不匹配 生成的错误代码: 类型不匹配get_var_net_weight_val();函数返回float类型变量数据,将其复制给字符指针cur_val导致类型不匹配报错 需要手动修改相应变量定义和显示代码 将float类型变量转化成字符串保存,在通过函数显示 // 在需要使用字符串的地方: char weight_str[50]; // 声明一个足够大的字符数组来存储转换后的字符串 float weight_val = get_var_net_weight_val(); // 获取 float 类型的权重值————<定时器回调函数中更新> // weight_val+=0.01; // 将 float 转换为字符串,保留两位小数 sprintf(weight_str, "=%.2fkg", weight_val); // 保留两位小数 // 现在您可以使用 weight_str 作为 const char* 类型 // 例如,如果您想更新标签的文本: lv_label_set_text(objects.obj1, weight_str); 更新变量 由于芯片方适配的LVGL默认没有实时刷新,变量改变后并不能更新显示 需要手动更新变量标签并刷新显示 1、使用LVGL定时器回调函数间隔刷新页面,以此更新变量 创建定时器: lv_timer_create(timer_callback*, ms, NULL); timer_callback是定时器到期时要调用的回调函数* ms是定时器的时间间隔,单位为毫秒* NULL是传递给回调函数的用户数据,这里没有传递任何数据* 封装成函数便于在ui_init()中初始化 //定时器回调函数 void timer_callback(lv_timer_t * timer) { // counter++; // 增加计数器值 // set_var_lvnum(counter); // 更新全局变量 // update_counter_display(); // 更新显示 } //初始化定时器 // 定义一个函数,用于显示计数器 // 参数ms表示定时器的时间间隔,单位为毫秒 void lv_timer_init(int ms) { // 创建一个定时器,并指定回调函数和定时时间 // timer_callback是定时器到期时要调用的回调函数 // ms是定时器的时间间隔,单位为毫秒 // NULL是传递给回调函数的用户数据,这里没有传递任何数据 lv_timer_create(timer_callback, ms, NULL); } 在lvgl中初始化lvgl定时器 void ui_init() { // 调用 create_screens 函数,创建所有需要的屏幕 create_screens(); // 调用 loadScreen 函数,加载主屏幕,SCREEN_ID_MAIN 是主屏幕的标识符 loadScreen(SCREEN_ID_MAIN); //初始化定时器 lv_timer_init(1000);//定时器周期1000ms } 2、使lvgl运行在线程中,线程中刷新lvgl 详细在该篇前文:目录——LVGL——修改程序结构使LVGL运行在线程里 改变字符型变量 char network_analysis_A_val[100] = {0}; sprintf(network_analysis_A_val, "密钥内容: %s\n", storage[i].data.key_data.key); // 保留两位小数 set_var_network_analysis_val(network_analysis_A_val); 使用sprintf函数将storage[i].data.key_data.key字符串添加到字符串末尾%s处,保存到字符数组network_analysis_A_val[]中。 使用eez生成更改字符变量的函数set_var_network_analysis_val()将字符传入变量 整合 开启 阶段性成果(图片) 整体框架: 串口终端显示执行过程: RGB屏幕显示数据