基于Chappie-II的二次开发日志-2

news/2024/11/5 2:37:01/文章来源:https://www.cnblogs.com/k0maru3/p/18527071

紧接着上一次没做完的继续,完善settings页面功能,实现过程中顺便完善了WiFi连接和时间同步。
本文主要包括:

  • Settings菜单完善

    • 多级菜单和sidebar
    • lvgl表格
    • 文本和滚动
  • Esp32的smartconfig配网

  • 时间同步

    Setting菜单

    说实话其实上一次代码传错了,不过也无所谓,那本身就是官方例程,和实际需求差的还是比较远的。顺便从这次开始更正一些记录习惯,不要大段大段的贴代码,要一块一块的分析,毕竟完整代码GitHub里就有..
    用到的主要方法有:

  • static lv_obj_t * create_text(...);:创建文本标签(lv_label)

  • static lv_obj_t * create_slider(...);:创建滑动条

  • static lv_obj_t * create_switch(...);:创建开关

  • static lv_obj_t * create_WiFiInfo_table(...);:创建用于显示WiFi信息的表(table)

  • void set_sidebar_width(...);:更改sidebar大小

  • void App_Settings_menu(void);:界面生成

主要记录的是最后一个,参考官方的complex_menu例程魔改的半成品。

多级菜单

这一段没什么好说的,就是创建最基础的menu对象并设置背景颜色和占据屏幕的大小。

这里的menu对象是接下来所有页面的父对象。

        lv_obj_t * menu = lv_menu_create(lv_scr_act());lv_color_t bg_color = lv_obj_get_style_bg_color(menu, 0);if(lv_color_brightness(bg_color) > 127) {lv_obj_set_style_bg_color(menu, lv_color_darken(lv_obj_get_style_bg_color(menu, 0), 10), 0);}else {lv_obj_set_style_bg_color(menu, lv_color_darken(lv_obj_get_style_bg_color(menu, 0), 50), 0);}lv_obj_set_size(menu, lv_disp_get_hor_res(NULL), lv_disp_get_ver_res(NULL));lv_obj_center(menu);

接下来是创建需要的子页面,包括有:

  • sub_WIFI_page : 显示WiFi连接状态和连接的WiFi的信息

  • sub_BLE_page :显示蓝牙相关信息

  • 剩下的都是字面意思

在这里可以看到整个页面离不开lvgl中的contain和section概念,contain作为容纳各种组件的容器存在,而section则可以容纳多个contain,这也构成了“多级”菜单的基础。

代码中可以看到的就有cont = create_WiFiInfo_tablecont = create_text他们都返回了lv_obj_t对象,但一个是表一个是文本标签;同时在lv_menu_separator_create(sub_about_page)后,对于about这个section容纳了两个包含着文本标签(label)的cont。

about页面

    lv_obj_t * cont;lv_obj_t * section;/*Create sub pages*/lv_obj_t * sub_WIFI_page = lv_menu_page_create(menu, NULL);lv_obj_set_style_pad_hor(sub_WIFI_page, lv_obj_get_style_pad_left(lv_menu_get_main_header(menu), 0), 0);section = lv_menu_section_create(sub_WIFI_page);//cont = create_WiFiInfo_table(section,WiFi.SSID().c_str(),WiFi.localIP().toString().c_str(),(const char *)WiFi.status());cont = create_WiFiInfo_table(section,"123","123","123");lv_menu_set_load_page_event(menu, cont, sub_WIFI_page);lv_obj_t * sub_BLE_page = lv_menu_page_create(menu, NULL);lv_obj_set_style_pad_hor(sub_BLE_page, lv_obj_get_style_pad_left(lv_menu_get_main_header(menu), 0), 0);lv_menu_separator_create(sub_BLE_page);section = lv_menu_section_create(sub_BLE_page);lv_obj_t * sub_software_info_page = lv_menu_page_create(menu, NULL);lv_obj_set_style_pad_hor(sub_software_info_page, lv_obj_get_style_pad_left(lv_menu_get_main_header(menu), 0), 0);section = lv_menu_section_create(sub_software_info_page);create_text(section, NULL, "Version 1.1", LV_MENU_ITEM_BUILDER_VARIANT_1);lv_obj_t * sub_legal_info_page = lv_menu_page_create(menu, NULL);lv_obj_set_style_pad_hor(sub_legal_info_page, lv_obj_get_style_pad_left(lv_menu_get_main_header(menu), 0), 0);section = lv_menu_section_create(sub_legal_info_page);create_text(section, NULL,"Chappie-II modified by K0maru3",LV_MENU_ITEM_BUILDER_VARIANT_1);lv_obj_t * sub_about_page = lv_menu_page_create(menu, NULL);lv_obj_set_style_pad_hor(sub_about_page, lv_obj_get_style_pad_left(lv_menu_get_main_header(menu), 0), 0);lv_menu_separator_create(sub_about_page);section = lv_menu_section_create(sub_about_page);cont = create_text(section, NULL, "Software information", LV_MENU_ITEM_BUILDER_VARIANT_1);lv_menu_set_load_page_event(menu, cont, sub_software_info_page);cont = create_text(section, NULL, "Legal information", LV_MENU_ITEM_BUILDER_VARIANT_1);lv_menu_set_load_page_event(menu, cont, sub_legal_info_page);

接下来是创建侧边栏(sidebar),这里同样体现了lvgl中section和cont的关系。create_text中使用的如LV_SYMBOL_WIFILV_SYMBOL_BLUETOOTH这样的参数是lvgl提供的一些默认的icon。

在实际使用的时候发现侧边栏的大小很奇怪,在网上搜索发现有的人很宽有的人很窄,我的出现了窄到自都看不见的情况,所以用到了set_sidebar_width(lv_obj_t* menu, lv_coord_t width);来直接调整侧边栏的大小。

值得注意的是在较新版本的lvgl中scroll效果是默认启用的,只要布局超过父对象就会启用(不清楚其他情况),为了手感考虑使用lv_obj_clear_flag关掉了滚动。

    root_page = lv_menu_page_create(menu, "Settings");lv_obj_set_style_pad_hor(root_page, lv_obj_get_style_pad_left(lv_menu_get_main_header(menu), 0), 0);section = lv_menu_section_create(root_page);/*WIFI页面计划用于显示Wifi连接情况*/cont = create_text(section, LV_SYMBOL_WIFI, "WIFI", LV_MENU_ITEM_BUILDER_VARIANT_1);lv_menu_set_load_page_event(menu, cont, sub_WIFI_page);/*BLE页面计划用于显示蓝牙连接情况*/cont = create_text(section, LV_SYMBOL_BLUETOOTH, "BLE", LV_MENU_ITEM_BUILDER_VARIANT_1);lv_menu_set_load_page_event(menu, cont, sub_BLE_page);create_text(root_page, NULL, "Others", LV_MENU_ITEM_BUILDER_VARIANT_1);section = lv_menu_section_create(root_page);cont = create_text(section, NULL, "About", LV_MENU_ITEM_BUILDER_VARIANT_1);lv_menu_set_load_page_event(menu, cont, sub_about_page);lv_menu_set_sidebar_page(menu, root_page);set_sidebar_width(menu, 100);lv_obj_clear_flag(menu, LV_OBJ_FLAG_SCROLLABLE);lv_event_send(lv_obj_get_child(lv_obj_get_child(lv_menu_get_cur_sidebar_page(menu), 0), 0), LV_EVENT_CLICKED,NULL);

其实更改sidebar的宽度不是一个非常复杂的事情,毕竟sidebar也是一个menu对象,直接修改宽度就行。

    /*** @brief Set the sidebar width object* @param  menu             * @param  width            */void set_sidebar_width(lv_obj_t* menu, lv_coord_t width) {lv_menu_t* obj = (lv_menu_t*)menu;lv_obj_set_width(obj->sidebar, width);}

表格

其实我对lvgl还是一个纯萌新的状态,现在纯是碰到什么学什么,学到什么用什么,理解了什么说什么的一个状态,这个表格的绘制也是第一次接触。绘制了一个两列四行的表格,没什么技术含量。

    /*** @brief 获取显示WIFI信息* @param  parent           * @param  ssid             * @param  ip_adress        * @param  wifi_state       * @return lv_obj_t* */static lv_obj_t * create_WiFiInfo_table(lv_obj_t * parent, const char * ssid,const char * ip_adress,const char * wifi_status){lv_obj_t * obj = lv_menu_cont_create(parent);lv_obj_t * table = lv_table_create(obj);lv_table_set_col_cnt(table, 2);/*Fill the first column*/lv_table_set_cell_value(table, 0, 0, "Type");lv_table_set_cell_value(table, 1, 0, "Status");lv_table_set_cell_value(table, 2, 0, "SSID");lv_table_set_cell_value(table, 3, 0, "IP");/*Fill the second column*/lv_table_set_cell_value(table, 0, 1, "Strings");lv_table_set_cell_value(table, 1, 1, wifi_status);lv_table_set_cell_value(table, 2, 1, ssid);lv_table_set_cell_value(table, 3, 1, ip_adress);lv_obj_set_size(table, 140, 160);return obj;}

唯一值得说道的就是这个表格默认使用的尺寸是全屏尺寸,而我的这页表其实最多只能用到屏幕的一版左右,所以最开始直接就是只能看见表的左边,要scroll的时候它偏偏不来了(恼。后面知道了用lv_obj_set_size(..); 设定固定的值的时候,若大小不足以对象的布局展开,就会启用scroll。

WiFi功能

原作者的代码中,wifi和时间的同步都在settings app执行oncreate时候完成,这也是为什么我写着写着开始折腾这两个功能。而-1屏上的wifi按钮对于wifi功能一点用都没有,因此我修改了部分代码使得wifi按钮可以触发esp32的配网,把wifi连接和时间同步和settings app的创建分开了。

考虑到如果进入联网那其他什么事都要放到后面做实在是不人性化,所以用到了FreeRTOS的多任务并行。

static void xTaskOne(void *xTask1){ while (1){uint8_t i = 0;WiFi.mode(WIFI_STA);UI_LOG("[WiFi] WiFi mode : STA\n");UI_LOG("[WiFi] try connect\n");WiFi.begin();WiFi.beginSmartConfig();UI_LOG("[WiFi] Waiting for SmartConfig...\n");while (!WiFi.smartConfigDone()) { vTaskDelay(200); }UI_LOG("[WiFi] SmartConfig received, connecting WiFi...\n");while (WiFi.status() != WL_CONNECTED) { vTaskDelay(200); }UI_LOG("[WiFi] Connected. IP: %s\n", WiFi.localIP().toString().c_str());vTaskDelete(NULL);}}
void App_Launcher::WiFi_config(){UI_LOG("[WiFi] WiFi config start\n");xTaskCreatePinnedToCore(xTaskOne, "TaskOne", 4096*10, NULL, 1, NULL, 0);UI_LOG("[WiFi] WiFi config done\n");}

配网的参考代码网上有很多,比如创建事件组,设定回调状态,编写回调函数。但是我学艺不精只能用最最简单的直接WiFi.begin();然后WiFi.beginSmartConfig();然后使用官方开源的手机app——EspTouch就能快速配网了。

Esp32中WiFi主要是三种模式:

  • STA模式:STA是Station的简称,类似于无线终端,STA本身并不接受无线的接入,它可以连接到AP,简单来说就是和手机连接WIFI热点的工作状态相同,可以连接其它的热点。

  • AP模式:AP,也就是无线接入点,是一个无线网络的创建者,是网络的中心节点。一般家庭或办公室使用的无线路由器就一个AP。

  • AP,STA混合模式:既可以连别人,也可以别人连自己。

而smartconfig至少需要STA模式(好像是)。

而FreeRTOS的多任务并行,则是依靠xTaskCreatePinnedToCore(xTaskOne, "TaskOne", 4096*10, NULL, 1, NULL, 0);这一行实现的。需要注意的是最后一个参数,决定了任务在哪个核心上运行。Esp32的FreeRTOS是设计运行在单核上。但ESP32是双核的,包含Protocol CPU(称为CPU 0或PRO_CPU)和Application CPU(称为CPU 1或APP_CPU)。这两个核实际上是相同的,并且共享相同的内存。这也为任务在两个核心上交替运行提供了硬件基础。

对了一定要记住,及时释放任务vTaskDelete()

网络时间同步

本来想通过SNTP实现网络时间同步的,但是我发现正常调用接口居然一点反应都没有,而原作者的同步方式如果不放在onCreate里面又会导致系统重启。秉持着能跑就不动原则,决定之后有空再去折腾通过sntp来实现时间同步

    void App_Settings_onCreate(){UI_LOG("[%s] onCreate1\n", App_Settings_appName().c_str());UI_LOG("[WiFi] try to sync time\n");http.begin("http://quan.suning.com/getSysTime.do"); //HTTP beginint httpCode = http.GET();if (httpCode > 0){// httpCode will be negative on errorif (httpCode == HTTP_CODE_OK) // 收到正确的内容{UI_LOG("[HTTP] Connected\n");String resBuff = http.getString();UI_LOG("[HTTP] Getting string\n");char *pEnd;char charArray[100];char str [30];resBuff.toCharArray(charArray, 61);for(uint8_t i = 0;i<=15;i++){str[i]=charArray[i+46];}UI_LOG("[HTTP] string to charArray\n");int nums[6];extract_ints(str, nums, 6);rtc_date.year = nums[0];rtc_date.month = nums[1];rtc_date.date = nums[2];rtc_time.hours = nums[3];rtc_time.minutes = nums[4];rtc_time.seconds = nums[5]+1;//补偿数据处理的时间            uint16_t y = nums[0];uint8_t m = nums[1];uint8_t d = nums[2];if(m<3){m+=12;y-=1;}rtc_date.weekDay=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7;UI_LOG("[SYS] rtc_date struct already\n");UI_LOG("[SYS] waiting applauncher restart\n");}}http.end();UI_LOG("[HTTP] disConnected\n");device->Rtc.setDate(&rtc_date);device->Rtc.setTime(&rtc_time);UI_LOG("[SYS] time sync done\n");App_Settings_menu();}

这里是通过访问quan.suning.com/getSysTime.do获得一串{"sysTime2":"2024-11-05 02:16:45","sysTime1":"20241105021645"}来获得当前时间参数,然后分部分读出来以后写入RTC。

感觉还是SNTP比较优雅

总结

一开始写东西就会逐渐发现自己的不足,以编写代码为需求驱动会让我快速学会怎么用,而写下来记录下来的时候就会逐步发现很多东西还是只知其然不知其所以然,再去逐步查找资料,让我学到了更多的东西。

随着对项目理解的加深,我也在逐渐理解这个系统的运行逻辑,希望之后编写功能APP的时候不要翻车

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/826902.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

给 Ollama 穿上 GPT 的外衣

上一篇我们介绍了如何在本地部署 ollama 运行 llama3 大模型。过程是相当简单的。但是现在给大模型交流只能在命令行窗口进行。这样的话就只能你自己玩了。独乐乐不如众乐乐嘛。我们接下来说一下如何部署 open-webui 给 ollama 加一个 webui,这样用户就可以通过浏览器访问我们…

18. 使用MySQL之全文本搜索

1. 理解全文本搜索 注意:并非所有引擎都支持全文本搜索: 正如第21章所述,MySQL支持几种基本的数据库引擎。并非所有的引擎都支持这里所描述的全文本搜索。 两个最常使用的引擎为MyISAM和InnoDB,前者支持全文本搜索,而后者不支持。这就是为什么虽然本书中创建的多数样例表使…

深入 Pod

kubectl 操作 Pod 的命令,探针以及 Pod 的生命周期。Author: ACatSmiling Since: 2024-11-05kubectl 命令 创建 Pod 方式一:使用配置文件创建。首先,需要创建一个 Pod 的配置文件(通常是.yaml或.yml格式)。例如,创建一个简单的 Nginx Pod 配置文件 nginx-pod.yaml: apiV…

数据结构-逻辑关系物理关系、时间复杂度、空间复杂度、顺序表

一、数据结构概述 基本概念 数据结构指的是计算机存储数据和组织数据的方式,存储数据和组织数据的目的是为了后期对数据的再次利用,所以存储的数据一般是具有一个或者多个特定关系的集合,利用不同的数据结构可以提高数据的访问效率。 思考:为什么大家来到新教室选好座位之后…

windows 消息断点

windows 消息循环 以下是一个简单的处理按钮点击的示例: #include <windows.h>#define BUTTON_ID 1 // 定义按钮IDLRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {switch (uMsg){case WM_CREATE:{// 创建一个按钮HWND hButton = Cr…

FindResource详解

#include <iostream> #include <Windows.h>int main() {// 获取当前模块的句柄HMODULE hModule = GetModuleHandle(NULL);HRSRC hRes = ::FindResource(hModule, MAKEINTRESOURCE(1), RT_MANIFEST);if (hRes == NULL) {std::cerr << "无法找到清单文件资…

11.4做洛谷深基到发疯记录

今日校队每日一题依然是不看题解就不…… 日,是题解都看不懂的一集 : ) 然后就去接着做洛谷深基的题了 当我做到P1098的时候还没意识到问题严重性 当我一看到如此复杂的情况分类的时候,以为只是写起来麻烦点罢了,遂敲 敲着敲着就感觉出来不对劲了,要是每种情况都分个类那if…

【spring开发】Spring Cloud Bus快速入门Demo

一、什么是Spring Cloud Bus?二、环境搭建三、代码工程四、测试五、引用原创 Harries HBLOG一、什么是Spring Cloud Bus? Spring Cloud Bus 是一个用于将分布式系统的节点连接起来的框架,它使用了轻量级消息代理来实现节点之间的通信。Spring Cloud Bus 可以将配置变更事件、…

大模型-训练推理 模型大小与硬件GPU 选取的大致关系-05

目录0. 计算公式1. 市面上常见的显卡2. 训练2. 推理 0. 计算公式 重点:显存大小 = 模型参数占用 + 梯度占用 + 优化器占用 + CUDA kernel占用 + 中间计算结果 每个神经元节点 不仅仅有自身的权重值 在进行反向传播的时候还有梯度的累计值 1. 市面上常见的显卡 英伟达GPU 3060 …

MSSprinkler:一款针对MS账号的密码喷射安全测试工具

原创 Alpha_h4ck FreeBuf关于MSSprinkler MSSprinkler是一款功能强大的密码喷射安全测试工具,可以帮助广大研究人员从外部角度测试其 Microsoft Online 帐户的安全性。 MSSprinkler 是用 PowerShell 编写的,可以直接作为模块导入,并且没有其他依赖项。MSSprinkler 依靠 Micr…

Unbound数据结构分析

mesh结构msg_cache + rr_cache缓存数据 msg_cache里的entry是msgreply_entry,rr_cache里的entry是ub_packed_rrset_key。 ub_packed_rrset_key的rrset_id_type通过alloc_get_id方法获取。 ub_packed_rrset_key通过alloc_special_obtain方法从env->alloc分配。

开发中常用到的10个数据结构

开发中常用到的10个数据结构 ▪️列表(List):用于存储有序集合,如Twitter动态流🐦。 ▪️数组(Array):连续存储的元素集合,适用于数学运算和大数据集📊。 ▪️栈(Stack):后进先出(LIFO)的数据结构,常用于撤销/重做功能🔄。 ▪️队列(Queue):先进先出(F…