当前位置: 首页 > news >正文

【开源】嵌入式Linux(IMX6U)应用层综合项目(1)--云平台调试APP

目录

1.简介

1.1功能介绍

1.2技术栈介绍

1.3演示视频

1.4硬件介绍

2.软件设计

2.1连接阿里云

2.2云平台调试UI

2.3Ui_main.c界面切换处理文件

2.4.main函数

3.结尾(附网盘链接)


1.简介

此文章并不是教程,只能当作笔者的学习分享,只会做一些简单的介绍,其他的各位结合着代码和运行现象自己分析吧,相信通过函数名和注释,基本上是不难看懂代码的,其中涉及到的一些技术栈,也请各位学习到的时候多查阅资料。

本篇的内容为嵌入式Linux应用层的一个综合性比较强的项目,结尾会将源码放在网盘中开源出来,笔者能力有限,只是简单的把功能实现了,代码开源供大家一起交流学习,有什么好的建议,请各位一定不吝赐教!!!

1.1功能介绍

项目包括了四个app:

1.云平台的调试窗口,用于查看订阅主题所下发的数据,另一个为输入Json格式的数据来控制STM32单片机上的外设。

2.智能家居的界面,有4个图片按钮用于控制STM32板子上的LED灯、门(舵机)、蜂鸣器,量计分别为温度、湿度和亮度的值,同样是STM32获取发布到云平台的。

3.通过一个摄像头模块做的一个相机功能,可以拍照、录像,以及查看拍摄的照片,和播放录制视频的回放。

4.简易的音乐播放器:能够切换歌曲,以及暂停播放音乐。

1.2技术栈介绍

虽然项目简单,但是所涉及到的技术栈还是比较杂,我简单在此列出:

 1.LVGL库用于绘制UI。

2.MQTT协议,连接阿里云平台与STM32通讯。

3.alsa库用于音频处理。

4.LED、BEEP

5.V4L2 摄像头应用编程

1.3演示视频

【开源】嵌入式Linux应用层物联网小项目|通过MQTT协议与STM32通讯_哔哩哔哩_bilibili

1.4硬件介绍

硬件使用的是正点原子的阿尔法开发板,芯片是IMX6U,类似开发板应该都可以运行。

2.软件设计

2.1连接阿里云

要注意的部分就是要让开发板连上网,然后将三元组替换为自己所创建产品和设备对应的三元组。如何创建就不在本篇中赘述了,不清楚的可以看看之前发的文章:一篇文章将带你从0到1让Linux系统连接阿里云--MQTT协议【傻瓜式教程】_linux用mqtt连接云-CSDN博客

本文件干的事,就是让开发板连上云平台,然后通过订阅了STM32下发数据的主题,在回调函数中处理STM32下发的传感器数据。

/** 这个例程适用于`Linux`这类支持pthread的POSIX设备, 它演示了用SDK配置MQTT参数并建立连接, 之后创建2个线程** + 一个线程用于保活长连接* + 一个线程用于接收消息, 并在有消息到达时进入默认的数据回调, 在连接状态变化时进入事件回调** 需要用户关注或修改的部分, 已经用 TODO 在注释中标明**/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include "aiot_state_api.h"
#include "aiot_sysdep_api.h"
#include "aiot_mqtt_api.h"
#include "mqtt_aliyun.h"
#include "cJSON.h"
#include "ui_main.h"/* TODO: 替换为自己设备的三元组 */
char *product_key       = "k1h2hJkoTA7";
char *device_name       = "Linux_ATK";
char *device_secret     = "00708c34c4e4b75f035c07e13613285f";
char  *mqtt_host = "iot-06z00jlxn39wea0.mqtt.iothub.aliyuncs.com";
const uint16_t port = 8883;
//const uint16_t port = 1883;/* 位于portfiles/aiot_port文件夹下的系统适配函数集合 */
extern aiot_sysdep_portfile_t g_aiot_sysdep_portfile;/* 位于external/ali_ca_cert.c中的服务器证书 */
extern const char *ali_ca_cert;static pthread_t g_mqtt_process_thread = NULL;
static pthread_t g_mqtt_recv_thread = NULL;
static uint8_t g_mqtt_process_thread_running = 0;
static uint8_t g_mqtt_recv_thread_running = 0;extern g_sensor_t g_sensor;
extern lv_obj_t * temp_bar;
lv_anim_t a;//char *sub_topic1 = "/k1h2hJkoTA7/Linux_ATK/user/get";
char *sub_topic = "/sys/k1h2hJkoTA7/Linux_ATK/thing/service/property/set";// 比较主题的函数
int compare_topic(const char* received_topic, const char* sub_topic, size_t sub_topic_len) {// 确保接收到的主题长度至少与订阅的主题长度相同if (strlen(received_topic) < sub_topic_len) {return 0; // 主题长度不足,无法比较}// 截取接收到的主题的前缀部分char received_prefix[sub_topic_len + 1];strncpy(received_prefix, received_topic, sub_topic_len);received_prefix[sub_topic_len] = '\0'; // 确保截取的字符串以 null 结尾printf("received_prefix = %s\r\n", received_prefix);// 比较前缀return strcmp(received_prefix, sub_topic) == 0;
}void Get_Data_From_Cloud(char *payload, int payload_len) 
{char buf[25];// 根据需要解析 JSON 数据cJSON *json = cJSON_Parse(payload);if (json != NULL) {// 解析并处理 params 数据cJSON *params = cJSON_GetObjectItem(json, "params");if (cJSON_IsObject(params)) {// 解析并处理 Temp 数据cJSON *Temp = cJSON_GetObjectItem(params, "Temp");if (cJSON_IsNumber(Temp)) {printf("Temp: %d\n", Temp->valueint);// 在这里处理 Temp 数据g_sensor.temp_value = Temp->valueint;lv_snprintf(buf, 25, "Temp:%d\n", g_sensor.temp_value);ui_update_sublish_message(buf, payload_len);memset(buf, 0, 25);} // 解析并处理 Humi 数据cJSON *humi = cJSON_GetObjectItem(params, "Humi");if (cJSON_IsNumber(humi)) {printf("Humi: %d\n", humi->valueint);// 在这里处理 Humi 数据g_sensor.humi_value = humi->valueint;lv_snprintf(buf, 25, "Humi:%d\n", g_sensor.humi_value);ui_update_sublish_message(buf, payload_len);memset(buf, 0, 25);} // 解析并处理 light 数据cJSON *light = cJSON_GetObjectItem(params, "light");if (cJSON_IsNumber(light)) {printf("light: %d\n", light->valueint);// 在这里处理 light 数据g_sensor.light_value = light->valueint;lv_snprintf(buf, 25, "Light:%d\n", g_sensor.light_value);ui_update_sublish_message(buf, payload_len);memset(buf, 0, 25);} } else {printf("Error: params is not an object\n");}cJSON_Delete(json);} else {printf("Error: Unable to parse JSON\n");}
}int mqtt_payload_handle(char *payload, int payload_len)
{if(payload_len > 0){payload[payload_len] = '\0';}ui_update_sublish_message(payload, payload_len);if(!strcmp(payload, "led_on")){set_led_mode("on");return LED_ON;} else if(!strcmp(payload, "led_off")){set_led_mode("off");return LED_OFF;}else if(!strcmp(payload, "beep_on")){set_beep_mode("on");return BEEP_ON;}else if(!strcmp(payload, "beep_off")){set_beep_mode("off");return BEEP_OFF;}return 0;
}/* TODO: 如果要关闭日志, 就把这个函数实现为空, 如果要减少日志, 可根据code选择不打印** 例如: [1577589489.033][LK-0317] mqtt_basic_demo&gb80sFmX7yX** 上面这条日志的code就是0317(十六进制), code值的定义见core/aiot_state_api.h**//* 日志回调函数, SDK的日志会从这里输出 */
int32_t demo_state_logcb(int32_t code, char *message)
{//printf("%s", message);//return 0;
}/* MQTT事件回调函数, 当网络连接/重连/断开时被触发, 事件定义见core/aiot_mqtt_api.h */
void demo_mqtt_event_handler(void *handle, const aiot_mqtt_event_t *event, void *userdata)
{switch (event->type) {/* SDK因为用户调用了aiot_mqtt_connect()接口, 与mqtt服务器建立连接已成功 */case AIOT_MQTTEVT_CONNECT: {printf("AIOT_MQTTEVT_CONNECT\n");/* TODO: 处理SDK建连成功, 不可以在这里调用耗时较长的阻塞函数 */}break;/* SDK因为网络状况被动断连后, 自动发起重连已成功 */case AIOT_MQTTEVT_RECONNECT: {printf("AIOT_MQTTEVT_RECONNECT\n");/* TODO: 处理SDK重连成功, 不可以在这里调用耗时较长的阻塞函数 */}break;/* SDK因为网络的状况而被动断开了连接, network是底层读写失败, heartbeat是没有按预期得到服务端心跳应答 */case AIOT_MQTTEVT_DISCONNECT: {char *cause = (event->data.disconnect == AIOT_MQTTDISCONNEVT_NETWORK_DISCONNECT) ? ("network disconnect") :("heartbeat disconnect");printf("AIOT_MQTTEVT_DISCONNECT: %s\n", cause);/* TODO: 处理SDK被动断连, 不可以在这里调用耗时较长的阻塞函数 */}break;default: {}}
}/* MQTT默认消息处理回调, 当SDK从服务器收到MQTT消息时, 且无对应用户回调处理时被调用 */
void demo_mqtt_default_recv_handler(void *handle, const aiot_mqtt_recv_t *packet, void *userdata)
{switch (packet->type) {case AIOT_MQTTRECV_HEARTBEAT_RESPONSE: {printf("heartbeat response\n");/* TODO: 处理服务器对心跳的回应, 一般不处理 */}break;case AIOT_MQTTRECV_SUB_ACK: {printf("suback, res: -0x%04X, packet id: %d, max qos: %d\n",-packet->data.sub_ack.res, packet->data.sub_ack.packet_id, packet->data.sub_ack.max_qos);/* TODO: 处理服务器对订阅请求的回应, 一般不处理 */}break;/* TODO: 云-->客户端 处理服务器下发的业务报文 */case AIOT_MQTTRECV_PUB: {//printf("pub, qos: %d, topic: %.*s\n", packet->data.pub.qos, packet->data.pub.topic_len, packet->data.pub.topic);//printf("pub, payload: %.*s\n", packet->data.pub.payload_len, packet->data.pub.payload);/* TODO: 处理服务器下发的业务报文 */printf("payload = %.*s\n",  packet->data.pub.payload_len, packet->data.pub.payload);//printf("packet->data.pub.topic = %s\n",  packet->data.pub.topic);Get_Data_From_Cloud(packet->data.pub.payload, packet->data.pub.payload_len);}break;case AIOT_MQTTRECV_PUB_ACK: {printf("puback, packet id: %d\n", packet->data.pub_ack.packet_id);/* TODO: 处理服务器对QoS1上报消息的回应, 一般不处理 */}break;default: {}}
}/* 执行aiot_mqtt_process的线程, 包含心跳发送和QoS1消息重发 */
void *demo_mqtt_process_thread(void *args)
{int32_t res = STATE_SUCCESS;while (g_mqtt_process_thread_running) {res = aiot_mqtt_process(args);if (res == STATE_USER_INPUT_EXEC_DISABLED) {break;}sleep(1);}return 0;
}/* 执行aiot_mqtt_recv的线程, 包含网络自动重连和从服务器收取MQTT消息 */
void *demo_mqtt_recv_thread(void *args)
{int32_t res = STATE_SUCCESS;while (g_mqtt_recv_thread_running) {res = aiot_mqtt_recv(args);if (res < STATE_SUCCESS) {if (res == STATE_USER_INPUT_EXEC_DISABLED) {break;}sleep(1);}}return 0;
}void *mqtt_handle = NULL;
int net_mqtt_aliyun_init(void)
{int32_t     res = STATE_SUCCESS;aiot_sysdep_network_cred_t cred; /* 安全凭据结构体, 如果要用TLS, 这个结构体中配置CA证书等参数 *//* 配置SDK的底层依赖 */aiot_sysdep_set_portfile(&g_aiot_sysdep_portfile);/* 配置SDK的日志输出 */aiot_state_set_logcb(demo_state_logcb);/* 创建SDK的安全凭据, 用于建立TLS连接 */memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));cred.option = AIOT_SYSDEP_NETWORK_CRED_SVRCERT_CA;  /* 使用RSA证书校验MQTT服务端 */cred.max_tls_fragment = 16384; /* 最大的分片长度为16K, 其它可选值还有4K, 2K, 1K, 0.5K */cred.sni_enabled = 1;                               /* TLS建连时, 支持Server Name Indicator */cred.x509_server_cert = ali_ca_cert;                 /* 用来验证MQTT服务端的RSA根证书 */cred.x509_server_cert_len = strlen(ali_ca_cert);     /* 用来验证MQTT服务端的RSA根证书长度 *//* 创建1个MQTT客户端实例并内部初始化默认参数 */mqtt_handle = aiot_mqtt_init();if (mqtt_handle == NULL) {printf("aiot_mqtt_init failed\n");return -1;}/* TODO: 如果以下代码不被注释, 则例程会用TCP而不是TLS连接云平台 *//*{memset(&cred, 0, sizeof(aiot_sysdep_network_cred_t));cred.option = AIOT_SYSDEP_NETWORK_CRED_NONE;}*//* 配置MQTT服务器地址 */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_HOST, (void *)mqtt_host);/* 配置MQTT服务器端口 */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PORT, (void *)&port);/* 配置设备productKey */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_PRODUCT_KEY, (void *)product_key);/* 配置设备deviceName */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_NAME, (void *)device_name);/* 配置设备deviceSecret */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_DEVICE_SECRET, (void *)device_secret);/* 配置网络连接的安全凭据, 上面已经创建好了 */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_NETWORK_CRED, (void *)&cred);/* 配置MQTT默认消息接收回调函数 */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_RECV_HANDLER, (void *)demo_mqtt_default_recv_handler);/* 配置MQTT事件回调函数 */aiot_mqtt_setopt(mqtt_handle, AIOT_MQTTOPT_EVENT_HANDLER, (void *)demo_mqtt_event_handler);/* 与服务器建立MQTT连接 */res = aiot_mqtt_connect(mqtt_handle);if (res < STATE_SUCCESS) {/* 尝试建立连接失败, 销毁MQTT实例, 回收资源 */aiot_mqtt_deinit(&mqtt_handle);printf("aiot_mqtt_connect failed: -0x%04X\n\r\n", -res);printf("please check variables like mqtt_host, produt_key, device_name, device_secret in demo\r\n");return -1;}/* MQTT 订阅topic功能示例, 请根据自己的业务需求进行使用 */{res = aiot_mqtt_sub(mqtt_handle, sub_topic, NULL, 1, NULL);if (res < 0) {printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);return -1;}} /* MQTT 发布消息功能示例, 请根据自己的业务需求进行使用 */// {//     char *pub_topic = "/k1h2hJkoTA7/Linux_ATK/user/update";//     char *pub_payload = "{\"id\":\"1\",\"version\":\"1.0\",\"params\":{\"LightSwitch\":0}}";//     res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)pub_payload, (uint32_t)strlen(pub_payload), 0);//     if (res < 0) {//         printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);//         return -1;//     }// }/* 创建一个单独的线程, 专用于执行aiot_mqtt_process, 它会自动发送心跳保活, 以及重发QoS1的未应答报文 */g_mqtt_process_thread_running = 1;res = pthread_create(&g_mqtt_process_thread, NULL, demo_mqtt_process_thread, mqtt_handle);if (res < 0) {printf("pthread_create demo_mqtt_process_thread failed: %d\n", res);return -1;}printf("pthread_create demo_mqtt_process_thread successfully!\n");/* 创建一个单独的线程用于执行aiot_mqtt_recv, 它会循环收取服务器下发的MQTT消息, 并在断线时自动重连 */g_mqtt_recv_thread_running = 1;res = pthread_create(&g_mqtt_recv_thread, NULL, demo_mqtt_recv_thread, mqtt_handle);if (res < 0) {printf("pthread_create demo_mqtt_recv_thread failed: %d\n", res);//lv_textarea_set_text(recv_ta, "aliyun unconnected!");return -1;}//lv_textarea_set_text(recv_ta, "aliyun connected!");// /* 断开MQTT连接, 一般不会运行到这里 */// g_mqtt_process_thread_running = 0;// g_mqtt_recv_thread_running = 0;// sleep(1);// pthread_join(g_mqtt_process_thread, NULL);// pthread_join(g_mqtt_recv_thread, NULL);// res = aiot_mqtt_disconnect(mqtt_handle);// if (res < STATE_SUCCESS) {//     aiot_mqtt_deinit(&mqtt_handle);//     printf("aiot_mqtt_disconnect failed: -0x%04X\n", -res);//     return -1;// }// /* 销毁MQTT实例, 一般不会运行到这里 */// res = aiot_mqtt_deinit(&mqtt_handle);// if (res < STATE_SUCCESS) {//     printf("aiot_mqtt_deinit failed: -0x%04X\n", -res);//     return -1;// }return 0;
}int net_mqtt_aliyun_thread_delete(void)
{if(pthread_join(g_mqtt_process_thread, NULL) != 0){printf("delete g_mqtt_process_thread false\n");return -1;}if(pthread_join(g_mqtt_recv_thread, NULL) != 0){printf("delete g_mqtt_recv_thread false\n");return -1;}return 0;
}

2.2云平台调试UI

大致内容分为两部分,用lvgl创建了两个框,一个只接收显示STM32采集到的传感器数据,另一个框点击会出现一个键盘,可以输入对应的Json格式数据去控制STM32的外设。这一块就是利用了MQTT的订阅和发布主题的功能,不了解的可以自行百度。

#include "ui_app_yunpintai.h"lv_obj_t * recv_ta;
static lv_obj_t * kb;
extern void *mqtt_handle;
extern char *pub_topic;/*打印云下发的payload 到Sublish栏中*/
void ui_update_sublish_message(char *payload, int payload_len)
{if(payload_len > 25){payload_len = 25;}char buf[26];strncpy(buf, payload, payload_len);buf[payload_len] = '\0';printf("buf = %s\n", buf);if(recv_ta == NULL){printf("Error: recv_ta is NULL\n");return;}lv_textarea_add_text(recv_ta, buf);
}static void publish_event_cb(lv_event_t * e)
{int res;lv_event_code_t code = lv_event_get_code(e);lv_obj_t * ta = lv_event_get_target(e);if (code == LV_EVENT_FOCUSED) {lv_keyboard_set_textarea(kb, ta); // Set the keyboard to the focused text arealv_obj_clear_flag(kb, LV_OBJ_FLAG_HIDDEN); // Show the keyboard} else if (code == LV_EVENT_DEFOCUSED) {lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN); // Hide the keyboard}else if(code == LV_EVENT_READY){//char *pub_payload = "{\"LED\":1}";const char * text = lv_textarea_get_text(ta);printf("Sent text: %s\n", text);res = aiot_mqtt_pub(mqtt_handle, pub_topic, (uint8_t *)text, (uint32_t)strlen(text), 0);if (res < 0) {printf("aiot_mqtt_sub failed, res: -0x%04X\n", -res);return -1;}}
}void ui_app_yunpintai(void)
{//ui_app_clear_area();clear_area(0, 0, 800, 480, lv_color_hex(MY_UI_COLOR_DEEP_WHITE));ui_left_app_bar(20, 70);lv_obj_t * img = lv_img_create(lv_scr_act());lv_img_set_src(img, &img_yunpintai_on);lv_obj_align(img, LV_ALIGN_TOP_LEFT, 32, 90);lv_obj_t * mid_label = lv_label_create(lv_scr_act());lv_label_set_text(mid_label, "Cloud");static lv_style_t mid_label_style;lv_style_init(&mid_label_style);  lv_style_set_text_font(&mid_label_style, &lv_font_montserrat_24); // 设置字体lv_style_set_text_color(&mid_label_style, lv_color_hex(MY_UI_COLOR_BLACK)); // 设置字体颜色lv_obj_add_style(mid_label, &mid_label_style, LV_PART_MAIN);lv_obj_align(mid_label, LV_ALIGN_TOP_MID, 0, 20);/*创建Sub订阅信息栏*/lv_obj_t * sub_label = lv_label_create(lv_scr_act());lv_label_set_text(sub_label, "Sublish");static lv_style_t sub_label_style;lv_style_init(&sub_label_style);  lv_style_set_text_font(&sub_label_style, &lv_font_montserrat_48); // 设置字体lv_style_set_text_color(&sub_label_style, lv_color_hex(MY_UI_COLOR_BLUE)); // 设置字体颜色lv_obj_add_style(sub_label, &sub_label_style, LV_PART_MAIN);lv_obj_align(sub_label, LV_ALIGN_TOP_LEFT, 195, 65);/*设置sub发布信息调试框*//* Create the receive text area */recv_ta = lv_textarea_create(lv_scr_act());printf("recv_ta after create: %p\n", recv_ta);lv_textarea_set_one_line(recv_ta, false);lv_textarea_set_password_mode(recv_ta, false);lv_textarea_set_text(recv_ta, "");lv_obj_set_size(recv_ta, 320, 320);lv_obj_align(recv_ta, LV_ALIGN_TOP_LEFT, 128, 130);lv_obj_set_style_text_font(recv_ta, &lv_font_montserrat_24, 0);/*创建Pub发布信息栏*/lv_obj_t * pub_label = lv_label_create(lv_scr_act());lv_label_set_text(pub_label, "Publish");static lv_style_t pub_label_style;lv_style_init(&pub_label_style);lv_style_set_text_font(&pub_label_style, &lv_font_montserrat_48); // 设置字体lv_style_set_text_color(&pub_label_style, lv_color_hex(MY_UI_COLOR_BLUE)); // 设置字体颜色lv_obj_add_style(pub_label, &pub_label_style, LV_PART_MAIN);lv_obj_align(pub_label, LV_ALIGN_TOP_LEFT, 525, 65);/*设置Pub发布信息调试框*//*Create the one-line mode text area*/lv_obj_t * pub_ta = lv_textarea_create(lv_scr_act());lv_textarea_set_one_line(pub_ta, false);lv_textarea_set_password_mode(pub_ta, false);lv_obj_add_event_cb(pub_ta, publish_event_cb, LV_EVENT_ALL, NULL);lv_obj_set_size(pub_ta, 320, 320);lv_obj_align(pub_ta, LV_ALIGN_TOP_LEFT, 462, 130);/*Create temp_anim keyboard*/kb = lv_keyboard_create(lv_scr_act());lv_obj_set_size(kb,  LV_HOR_RES, LV_VER_RES / 2);lv_keyboard_set_textarea(kb, pub_ta); lv_obj_set_style_text_font(kb, &lv_font_dejavu_16_persian_hebrew, 0);lv_obj_set_style_text_font(pub_ta, &lv_font_montserrat_24, 0);lv_obj_add_flag(kb, LV_OBJ_FLAG_HIDDEN);
}

2.3Ui_main.c界面切换处理文件

此文件中,创建了一个线程来处理我们项目中所有UI的处理,左边的app功能栏中的4个图片按钮用于切换4个不同功能的app,其他的诸如清屏,按键回调函数,各位自行看看即可。

#include "ui_main.h"
#include "ui_app_yunpintai.h"
#include "ui_app_smarthome.h"
#include "ui_app_camera.h"
#include "ui_app_music.h"static app_imgbtn_t app_imgbtn[NUM_APPS];
static int current_active_app = -1;
static pthread_t g_ds_ui_page_thread = NULL;
extern void *mqtt_handle;
extern lv_obj_t *recv_ta;
char *pub_topic = "/k1h2hJkoTA7/Linux_ATK/user/Android_STM32";
int app_index = 0;// 创建一个与背景颜色相同的对象来覆盖指定区域
void clear_area(lv_coord_t x, lv_coord_t y, lv_coord_t width, lv_coord_t height, lv_color_t value)
{lv_obj_t *clear_obj = lv_obj_create(lv_scr_act());   // 创建一个对象lv_obj_remove_style_all(clear_obj);                  // 移除默认样式lv_obj_set_size(clear_obj, width, height);           // 设置对象大小lv_obj_set_pos(clear_obj, x, y);                     // 设置对象位置lv_obj_set_style_bg_color(clear_obj, value, 0);      // 设置对象背景颜色,假设背景是白色lv_obj_set_style_bg_opa(clear_obj, LV_OPA_COVER, 0); // 设置对象不透明度
}void ui_draw_backgroud_bar(lv_coord_t x, lv_coord_t y, lv_coord_t width, lv_coord_t height, lv_color_t value)
{static lv_style_t style;lv_style_init(&style);/*Set temp_anim background color and temp_anim radius*/lv_style_set_radius(&style, 25);lv_style_set_bg_opa(&style, LV_OPA_COVER);lv_style_set_bg_color(&style, value);/*Create an object with the new style*/lv_obj_t *obj = lv_obj_create(lv_scr_act());lv_obj_add_style(obj, &style, 0);lv_obj_set_size(obj, width, height);lv_obj_align(obj, LV_ALIGN_TOP_LEFT, x, y);
}/* 功能栏按键 切换app */
static void imgbtn_event_handler(lv_event_t *e)
{lv_obj_t *imgbtn = lv_event_get_target(e);lv_event_code_t code = lv_event_get_code(e);if (code == LV_EVENT_CLICKED){printf("Button clicked!\n");/* Find the app_index of the pressed button */for (int i = 0; i < NUM_APPS; i++){if (app_imgbtn[i].imgbtn == imgbtn){app_index = i;break;}}printf("app_index = %d\n", app_index);/* Update the active app */current_active_app = app_index;/* Call ui_smarthome_page to update the UI */ui_page_main();}
}void ui_create_app_imagebtn(unsigned int x, unsigned int y)
{/* Define the image resources for each app button */app_imgbtn[0].img_on = &img_yunpintai_on;app_imgbtn[0].img_off = &img_yunpintai_off;app_imgbtn[1].img_on = &img_smarthome_on;app_imgbtn[1].img_off = &img_smarthome_off;app_imgbtn[2].img_on = &img_camera_on;app_imgbtn[2].img_off = &img_camera_off;app_imgbtn[3].img_on = &img_music_on;app_imgbtn[3].img_off = &img_music_off;/* Create an image button */for (int i = 0; i < NUM_APPS; i++){app_imgbtn[i].imgbtn = lv_imgbtn_create(lv_scr_act());lv_imgbtn_set_src(app_imgbtn[i].imgbtn, LV_IMGBTN_STATE_RELEASED, NULL, NULL, app_imgbtn[i].img_off);lv_imgbtn_set_src(app_imgbtn[i].imgbtn, LV_IMGBTN_STATE_PRESSED, NULL, NULL, app_imgbtn[i].img_on);/* Add event handler to switch images on press */lv_obj_add_event_cb(app_imgbtn[i].imgbtn, imgbtn_event_handler, LV_EVENT_CLICKED, NULL);/* Set size and position */lv_obj_set_size(app_imgbtn[i].imgbtn, 64, 64);lv_obj_align(app_imgbtn[i].imgbtn, LV_ALIGN_TOP_LEFT, x + 12, y + 20 + 90 * i); // Adjust the positions as needed}
}/*创建左app栏*/
void ui_left_app_bar(unsigned int x, unsigned int y)
{/* 创建左功能栏 */static lv_style_t style_bg;lv_style_init(&style_bg);lv_style_set_radius(&style_bg, 25);lv_style_set_bg_color(&style_bg, lv_color_hex(MY_UI_COLOR_WHITE));lv_style_set_bg_opa(&style_bg, LV_OPA_COVER);/* Create temp_anim background object */lv_obj_t *bg = lv_obj_create(lv_scr_act());lv_obj_add_style(bg, &style_bg, 0);lv_obj_set_size(bg, 90, 380);lv_obj_align(bg, LV_ALIGN_TOP_LEFT, x, y);ui_create_app_imagebtn(x, y);
}static void destroy_previous_objects(void)
{if (recv_ta != NULL){lv_obj_del(recv_ta);recv_ta = NULL;printf("recv_ta destroyed\n");}
}/* 页面主函数 */
void ui_page_main(void)
{destroy_previous_objects();lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(MY_UI_COLOR_DEEP_WHITE), LV_PART_MAIN);ui_left_app_bar(20, 70);if (app_index == UI_APP_YUNPINTAI){ui_app_yunpintai();}else if (app_index == UI_APP_SMARTHOME){ui_app_smarthome();}else if (app_index == UI_APP_CAMERA){ui_app_camera();}else if (app_index == UI_APP_MUSIC){ui_app_music();}
}void *ds_ui_page_thread(void *args)
{ui_page_main();// setup_time_update();while (1){lv_task_handler();usleep(5000);}return NULL;
}int ui_app_page_init(void)
{int res;res = pthread_create(&g_ds_ui_page_thread, NULL, ds_ui_page_thread, NULL);if (res != 0){printf("pthread_create ds_ui_page_thread failed: %d\n", res);return -1;}printf("ds_ui_page_thread created successfully\n");return 0;
}

2.4.main函数

在main函数中先需要初始化lvgl的相关内容,然后调用ui_app_page_init();和net_mqtt_aliyun_init();来对界面初始化和连接上阿里云。

#include "app_main.h"extern lv_obj_t *recv_ta;
#define DISP_BUF_SIZE (800 * 480)
static void my_lvgl_init(void);int main(void)
{printf("Hello Linux!!!\n\r");set_led_mode("off");my_lvgl_init();ui_app_page_init();net_mqtt_aliyun_init();while (1){sleep(1);}return 0;
}/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{static uint64_t start_ms = 0;if (start_ms == 0){struct timeval tv_start;gettimeofday(&tv_start, NULL);start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;}struct timeval tv_now;gettimeofday(&tv_now, NULL);uint64_t now_ms;now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;uint32_t time_ms = now_ms - start_ms;return time_ms;
}static void my_lvgl_init(void)
{/*LittlevGL init*/lv_init();/*Linux frame buffer device init*/fbdev_init();/*A small buffer for LittlevGL to draw the screen's content*/static lv_color_t buf[DISP_BUF_SIZE];/*Initialize a descriptor for the buffer*/static lv_disp_draw_buf_t disp_buf;lv_disp_draw_buf_init(&disp_buf, buf, NULL, DISP_BUF_SIZE);/*Initialize and register a display driver*/static lv_disp_drv_t disp_drv;lv_disp_drv_init(&disp_drv);disp_drv.draw_buf = &disp_buf;disp_drv.flush_cb = fbdev_flush;disp_drv.hor_res = 800;disp_drv.ver_res = 480;lv_disp_drv_register(&disp_drv);/* Linux input device init */evdev_init();/* Initialize and register a display input driver *//* Segmentation fault:*/lv_indev_drv_t indev_drv;lv_indev_drv_init(&indev_drv); /*Basic initialization*/indev_drv.type = LV_INDEV_TYPE_POINTER;indev_drv.read_cb = evdev_read;lv_indev_t *my_indev = lv_indev_drv_register(&indev_drv);/* Segmentation fault:*/// printf("LVGL_Init OK!\r\n");
}

3.结尾(附网盘链接)

链接:百度网盘 请输入提取码

提取码:2jia

--来自百度网盘超级会员V5的分享

相关文章:

【开源】嵌入式Linux(IMX6U)应用层综合项目(1)--云平台调试APP

目录 1.简介 1.1功能介绍 1.2技术栈介绍 1.3演示视频 1.4硬件介绍 2.软件设计 2.1连接阿里云 2.2云平台调试UI 2.3Ui_main.c界面切换处理文件 2.4.main函数 3.结尾&#xff08;附网盘链接&#xff09; 1.简介 此文章并不是教程&#xff0c;只能当作笔者的学习分享&…...

AI人工智能分析王楚钦球拍被踩事件的真相

在2024年巴黎奥运会乒乓球混双决赛的热烈氛围中&#xff0c;中国队王楚钦与孙颖莎以出色的表现夺得金牌&#xff0c;然而&#xff0c;赛后发生的一起意外事件——王楚钦的球拍被踩坏&#xff0c;引起了广泛关注和热议。为了探寻这一事件的真相&#xff0c;我们可以借助AI人工智…...

C++客户端Qt开发——多线程编程(一)

多线程编程&#xff08;一&#xff09; ①QThread 在Qt中&#xff0c;多线程的处理一般是通过QThread类来实现。 QThread代表一个在应用程序中可以独立控制的线程&#xff0c;也可以和进程中的其他线程共享数据。 QThread对象管理程序中的一个控制线程。 run() 线程的入口…...

安装pnpm

安装pnpm&#xff08;Performant npm&#xff09;&#xff0c;即高性能的npm包管理工具&#xff0c;可以通过多种方式进行。以下是详细的安装步骤&#xff1a; 一、通过npm全局安装 打开命令行工具&#xff1a;在你的计算机上打开命令行工具&#xff0c;例如Windows的CMD、Pow…...

CSS平移实现双开门效果

CSS平移实现双开门效果 一共要三张图片&#xff0c;一张作为父级背景&#xff0c;两张为兄弟左右布局 父子结构布局 一张作为父级背景&#xff0c;两张为兄弟左右布局。之后添加鼠标悬停效果&#xff0c;两张子图分别从左右平移 [外链图片转存失败,源站可能有防盗链机制,建议…...

3096. 得到更多分数的最少关卡数目

3096. 得到更多分数的最少关卡数目 题目链接&#xff1a;3096. 得到更多分数的最少关卡数目 代码如下&#xff1a; class Solution { public:int minimumLevels(vector<int>& possible) {int s0;//两个玩家能得到的分数和for(int x:possible){sx0?-1:1;}int t0;/…...

AGI思考探究的意义、价值与乐趣Ⅳ

探究in context或Prompt对于LLM来说其根本意义&#xff0c;in context & Prompt Learning带给我们更深一层的提示是什么&#xff1f; 文章里的探索希望能够将in context或Prompt置身于一个更全局的视角来看待&#xff1a;将其视为在真实世界中时空认知流形所映射为数据流形…...

《数据结构》(C语言版)第1章 绪论(上)

第1章 绪论 1.1 数据结构的研究内容1.2 基本概念和术语 1.1 数据结构的研究内容 N.沃思&#xff08;Niklaus Wirth)教授提出&#xff1a; 程序算法数据结构 电子计算机的主要用途 早期&#xff1a;主要用于数值计算 后来&#xff1a;非数值计算&#xff0c;复杂的具有一定结构…...

【Pyhton】数据类型之详讲字符串(上)

本篇文章将详细讲解字符串&#xff1a; 1、定义 定义字符串时&#xff0c;字符串的内容被双引号&#xff0c;单引号&#xff0c;三单引号&#xff0c;三双引号中的其中一个被括住。 例如&#xff1a; 双引号&#xff1a; v1"haha" 单引号&#xff1a; v1hahah…...

算法小白的进阶之路(力扣6~8)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…...

【期货】收盘点评。昨天说的,p2409棕榈油在今天或者周一会走出行情

收盘点评 昨天说的&#xff0c;p2409棕榈油在今天或者周一会走出行情。事实就是如此。震荡了几天了&#xff0c;波幅不大的来回震荡&#xff0c;其实主力是不想震荡的&#xff0c;但是不震荡自己的货和行情走不出来。所以我昨天就说&#xff0c;应该就是这一两天会走出一波小行…...

LBS 开发微课堂|Polyline绘制优化:效果更丰富,性能更佳!

为了让广大的开发者 更深入地了解 百度地图开放平台的技术能力 轻松掌握满满的技术干货 更加简单地接入 开放平台的服务 我们特别推出了 “位置服务&#xff08;LBS&#xff09;开发微课堂” 系列技术案例 第一期的主题是 《Polyline 绘制优化升级》 你还想了解哪些…...

VS Code设置C++编译器路径

C_Cpp.default.compilerPath是C/C编译器路径; python.condaPath是conda路径....

laravel项目配置

创建laravel项目 composer create-project --prefer-dist laravel/laravel 项目名称生成项目key php artisan key:generate.清理配置缓存 php artisan config:clearlaravel生成代码 官网链接 php artisan make:model Flight --all生成Flight类相关的文件&#xff0c;对应数…...

Python试讲

Python试讲 导语Python简介Python及其特点如何使用Python Python与计算计算变量 导语 本次试讲内容如下&#xff1a;Python简介与使用&#xff0c;Python与基本运算 辅助教材为 《趣学Python编程》和《Python编程从入门到实践》 Python简介 Python是目前入门最简单最好学的…...

RESTful API

RESTful API是一种基于REST (Representational State Transfer) 架构风格的应用程序编程接口。它通过使用HTTP协议的不同方法&#xff08;如GET、POST、PUT、DELETE等&#xff09;来对资源进行操作和传输数据。 使用RESTful API构建web应用程序需要遵循以下几个步骤&#xff1…...

NEEP-EN2-2020-Text1

英二-2020-Text 1 摘自新科学家&#xff08;New scientist&#xff09;2018年11月的文章《Rats can make friends with robot rats and will rescue them when stuck》。 以下为个人解析&#xff0c;非官方公开标准资料&#xff0c;可能有误&#xff0c;仅供参考。&#xff08;…...

摩托罗拉E6系统研究

这是很久以前研究摩托罗拉E6刷机包时总结的一些经验&#xff0c;不一定准确但留个纪念&#xff0c;希望会制作刷机包的高手交流学习。 ------------------------------------------------------------------------------------------------------------------------------- 摩…...

Spring中,ApplicationContext主要的实现类型包括?

Spring中&#xff0c;‌ApplicationContext主要的实现类型包括FileSystemXmlApplicationContext、‌ClassPathXmlApplicationContext、‌XmlWebApplicationContext、‌AnnotationConfigWebApplicationContext。‌ FileSystemXmlApplicationContext&#xff1a;‌这个实现从一个…...

JavaScript青少年简明教程:事件及处理

JavaScript青少年简明教程&#xff1a;事件及处理 在编程语言中&#xff0c;事件&#xff08;Event&#xff09;是一种使程序能够响应特定操作或条件发生的机制。它允许程序中的不同部分&#xff08;比如对象、类或模块&#xff09;在发生某些特定情况时互相通信或协作。事件驱…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

基于当前项目通过npm包形式暴露公共组件

1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹&#xff0c;并新增内容 3.创建package文件夹...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

网络编程(UDP编程)

思维导图 UDP基础编程&#xff08;单播&#xff09; 1.流程图 服务器&#xff1a;短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

libfmt: 现代C++的格式化工具库介绍与酷炫功能

libfmt: 现代C的格式化工具库介绍与酷炫功能 libfmt 是一个开源的C格式化库&#xff0c;提供了高效、安全的文本格式化功能&#xff0c;是C20中引入的std::format的基础实现。它比传统的printf和iostream更安全、更灵活、性能更好。 基本介绍 主要特点 类型安全&#xff1a…...