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

news2024/9/21 14:49:20

目录

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 area
        lv_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的分享

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

go中的值传递和指针传递

文章目录 1、& 和 *2、空指针3、nil4、用值传递还是指针传递&#xff1f;5、补充 1、& 和 * &后跟一个变量名&#xff0c;得到的是这个变量的内存地址*int类型的变量&#xff0c;代表这个变量里存的值是int类型的变量的内存地址数据类型的指针类型&#xff0c;即在…

顺序表的实现【数据结构】

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有线序列。线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表有&#xff1a;顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构&#xff0c;也就是说是连续的一条线…

医院设置(洛谷)

设有一棵二叉树&#xff0c;如图&#xff1a; 其中&#xff0c;圈中的数字表示结点中居民的人口。圈边上数字表示结点编号&#xff0c;现在要求在某个结点上建立一个医院&#xff0c;使所有居民所走的路程之和为最小&#xff0c;同时约定&#xff0c;相邻接点之间的距离为 11。…

C语言实现 -- 单链表

C语言实现 -- 单链表 1.顺序表经典算法1.1 移除元素1.2 合并两个有序数组 2.顺序表的问题及思考3.链表3.1 链表的概念及结构3.2 单链表的实现 4.链表的分类 讲链表之前&#xff0c;我们先看两个顺序表经典算法。 1.顺序表经典算法 1.1 移除元素 经典算法OJ题1&#xff1a;移除…

在服务器上使用Dockerfile创建springboot项目的镜像和踩坑避雷

1. 准备个文件夹 这是我的路径 /usr/local/springboot/docker-daka/docker_files2. 将jar包上传 springboot项目打包——maven的package 这是整个项目打包的模式&#xff0c;也可以分离依赖、配置和程序进行打包&#xff0c;详情看我这篇文章&#xff1a; springboot依赖 配…

java基础 之 集合与栈的使用(四)

文章目录 Queue栈Stack队列和栈的区别小扩展自己写个简单的队列自己写个简单的栈使用栈来实现个队列使用队列来实现个栈写在最后 前文回顾&#xff1a; 戳这里 → java基础 之 集合与栈的使用&#xff08;一&#xff09; 戳这里 → java基础 之 集合与栈的使用&#xff08;二&a…

windows中node版本的切换(nvm管理工具),解决项目兼容问题 node版本管理、国内npm源镜像切换(保姆级教程,值得收藏)

前言 在工作中&#xff0c;我们可能同时在进行2个或者多个不同的项目开发&#xff0c;每个项目的需求不同&#xff0c;进而不同项目必须依赖不同版本的NodeJS运行环境&#xff0c;这种情况下&#xff0c;对于维护多个版本的node将会是一件非常麻烦的事情&#xff0c;nvm就是为…

【Git】git 从入门到实战系列(二)—— git 介绍以及安装方法 (文末附带视频录制操作步骤)

文章目录 一、前言二、git 是什么三、版本控制系统是什么四、本地 vs 集中式 vs 分布式本地版本控制系统集中式版本控制系统分布式版本控制系统 五、安装 git 一、前言 本系列上一篇文章【Git】git 从入门到实战系列&#xff08;一&#xff09;—— Git 的诞生&#xff0c;Lin…

Linux系统编程 --- 基础IO

形成共识原理&#xff1a; 1、文件 内容 属性 2、文件分为打开的文件和没打开的文件 3、打开的文件&#xff1a;谁打开&#xff1f;进程&#xff01;--- 本质是研究进程和文件的关系&#xff01; 文件被打开&#xff0c;必须先被加载到内存&#xff01; 一个进程可以打开…

PyTorch 训练自定义功能齐全的神经网络模型的详细教程

在前面的文章中&#xff0c;老牛同学介绍了不少大语言模型的部署、推理和微调&#xff0c;也通过大模型演示了我们的日常的工作需求场景。我们通过大语言模型&#xff0c;实实在在的感受到了它强大的功能&#xff0c;同时也从中受益颇多。 今天&#xff0c;老牛同学想和大家一…

【Android Studiio】default activity 原生安卓和uniapp默认启动分析

文章目录 思路&#xff1a; 一、原生安卓二、uniapp 探究方向&#xff1a;找到Default Activity 思路&#xff1a; 在Android开发中&#xff0c;"default activity"这个概念通常指的是应用启动时默认会加载和显示的那个Activity。AndroidManifest.xml文件是Android…

基于Selenium实现操作网页及操作windows桌面应用

Selenium操作Web页面 Why? 通常情况下&#xff0c;网络安全相关领域&#xff0c;更多是偏重于协议和通信。但是&#xff0c;如果协议通信过程被加密或者无法了解其协议构成&#xff0c;是无法直接通过协议进行处理。此时&#xff0c;可以考虑模拟UI操作&#xff0c;进而实现相…

声音和数据之间的调制解调 —— 电报机和电传打字机如何影响计算机的演变

注&#xff1a;机翻&#xff0c;未校对。 The Squeal of Data The through line between the telegraph and the computer is more direct than you might realize. Its influence can be seen in common technologies, like the modem. 电报和计算机之间的直通线比你想象的要…

基于IOT架构的数据采集监控平台!

LP-SCADA数据采集监控平台是蓝鹏测控推出的一款聚焦于工业领域的自动化数据采集监控系统&#xff0c; 助力数字工厂建设的统一监控平台。 为企业提供从下到上的完整的生产信息采集与集成服务&#xff0c;从而为企业综合自动化、工厂数字化及完整的"管控一体化”的解决方案…

LockSupport详解

文章目录 理解可重入锁LockSupport线程等待唤醒机制&#xff08;wait/notify&#xff09; waitNotify限制awaitSignal限制LockSupport重点说明 理解可重入锁 可重入锁的种类&#xff1a; 隐式锁&#xff08;即synchronized关键字使用的锁&#xff09;默认是可重入锁。 同步代…

站在临床数据科学的角度,药物试验归根结底是这两大假设

在临床数据科学的领域中&#xff0c;药物试验的设计和实施是评估药物效果及其安全性的关键环节。药物试验的基础无外乎两大核心假设&#xff1a;有效性与安全性。这两个假设不仅是药物试验的起点&#xff0c;也是整个研究过程中的重要指导原则。 药物试验的核心主旨在于对待测试…

Python高性能计算:进程、线程、协程、并发、并行、同步、异步

这里写目录标题 进程、线程、协程并发、并行同步、异步I/O密集型任务、CPU密集型任务 进程、线程、协程 进程、线程和协程是计算机程序执行的三种不同方式&#xff0c;它们在资源管理、执行模型和调度机制上有显著的区别。以下是对它们的详细解释和比较&#xff1a; 进程&…

一款有趣的工具,锁定鼠标键盘,绿色免安装

这是一款完全免费的程序&#xff0c;可以实现在不锁定屏幕的情况下锁定鼠标键盘&#xff0c;让鼠标键盘无法操作。比较适合防止误碰鼠标键盘&#xff0c;以及离开电脑时不希望别人操作自己的电脑。 ★★★★★锁定鼠标键盘工具&#xff1a;https://pan.quark.cn/s/e5c518a2165…

路由配置修改(五)

一、默认约定式路由 1、umi 会根据 pages 目录自动生成路由配置。 * name umi 的路由配置* description 只支持 path,component,routes,redirect,wrappers,name,icon 的配置* param path path 只支持两种占位符配置&#xff0c;第一种是动态参数 :id 的形式&#xff0c;第二种…

win11 intel新显卡控制面板无自定义分辨率选项解决

问题 下图是现在的intel显卡控制面板&#xff0c;不知道为啥变得很傻瓜式了&#xff0c;连所有显卡控制面板都有的分辨率自定义也被干掉了。 解决方式 其实解决很简单&#xff0c;因为自定义分辨率对显卡玩游戏来说还是很常用的&#xff0c;intel在beta版又加回来了&#x…