极米智驾仪表盘(开源!!)

时间:2024-10-15 17:36:57

1.演示:

驾驶界面图

有图无真相,下面视频展示:

汽车仪表盘展示

整个汽车中控仪表盘界面展示:

极米智驾仪表盘中控

极米智驾仪表盘在arm开发板上运行的效果:

(因为****只能上传到2M多的视频大小,所以视频只能压缩到2M,画质比较糊了,但是效果是和上面软件的效果一样的)

极米智驾仪表盘开发板展示

资源已绑定,自行下载哦

演示结束,接下来开始我们的极米智驾仪表盘的教程吧。

2.LVGL设计及移植

        关于LVGL设计我是在SquareLine软件上做的,具体UI设计大家可以看自己喜欢来做,我更多教大家怎么移植这样。(我的UI设计在资源里面可以直接下载)
移植:
在SquareLine里面设计好LVGL后,点击左上角的导出选择UI导出或者项目导出(这里我选择项目导出形式)

导出:

导出后点击导出的文件夹里,找到UI文件夹并复制出来到自己的项目工程文件夹下。

这里要提前自己移植好LVGL的标准库,具体方法网上会有教,或者直接拿我移植好的,这里我们把UI文件夹放到项目工程下。


Makefile改写:

把ui文件夹里的.c都包含到Makefile中。


头文件路径改写:

替换:

接着make clean,再make进行编译,会发现系统报错不认识两个函数,这是因为squareline这个软件生成的lvgl版本和我们自己移植的lvgl版本不一样导致的,版本不同,里面的函数有一些会不同,我们只要进行替换即可。
一个是lv_mem_malloc,我们直接在ui的文件夹下搜索这个函数,全局替换成lv_malloc即可;

一个是lv_mem_free,同样方法进行搜索,全局替换成lv_free即可。

(这里因为我已经替换过了就演示不了了,大家照着做就可以了)

最后再进行make clean和make,发现编译通过就可以使用了。

使用:

直接调用ui.c里面的ui_init();函数即可

3.代码:

lvgl:      

          lvgl有自己的维护组件的一套方法,我们只需要在主程序中调用lvgl初始化并将lvgl的维护组件的程序放到多线程里面即可。

注意:涉及到多线程的问题,那自然就要印出来线程资源抢夺了,当我们在线程中查询或者更改lvgl的组件时,为了不让线程之间互相抢夺组件资源而造成程序崩溃,在这里我们需要引入一个线程锁来维护线程资源不会互相抢夺。

/// @brief lvgl线程轮询函数
/// @param arg 线程传参
/// @return 无
void *pthreadFun_Lvgl(void *arg)
{
    while (1)
    {
        //线程锁,使lvgl轮询组件时不会和其他线程争抢组件资源
        pthread_mutex_lock(&suo);
        lv_timer_handler(); // 事务处理
        lv_tick_inc(5);     // 节拍累计  5ms
        //解锁
        pthread_mutex_unlock(&suo);
        //延时5000ms使CPU可以执行其他函数
        usleep(5000);
    }
}


/*用户节拍获取*/
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;
}

最后,演示一下天气获取的原理和时间显示的原理:


设置天气函数:

void set_weather()
{
    // 天气
    char cJSON_buff[65535] = {0}, weather_report_buff[1024] = {0}, weather_temperature_buff[5] = {0};
    memcpy(cJSON_buff, get_weather_report(), sizeof(cJSON_buff));
    printf("天气预报网址给我回复: %s\n", cJSON_buff);
    analyse_CJSON_data(cJSON_buff, weather_report_buff, weather_temperature_buff);
    printf("主函数获取到天气:%s,温度:%s\n", weather_report_buff, weather_temperature_buff);
    if (strstr(weather_report_buff, "晴") != NULL)
    {
        lv_img_set_src(ui_morentianqiimg, "S:/nfs/ZARD/SecondDemo/img/qingtian.png");
        lv_label_set_text(ui_tianwendulabel, weather_temperature_buff);
        printf("%d %s %s\n", __LINE__, __FUNCTION__, __FILE__);
        lv_img_set_src(ui_ditumorentianqiimg, "S:/nfs/ZARD/SecondDemo/img/qingtian.png");
        lv_label_set_text(ui_ditutainqilabel, weather_temperature_buff);
    }
    else if (strstr(weather_report_buff, "小雨") != NULL)
    {
        lv_img_set_src(ui_morentianqiimg, "S:/nfs/ZARD/SecondDemo/img/xiaoyu.png");
        lv_label_set_text(ui_tianwendulabel, weather_temperature_buff);
        printf("%d %s %s\n", __LINE__, __FUNCTION__, __FILE__);
        lv_img_set_src(ui_ditumorentianqiimg, "S:/nfs/ZARD/SecondDemo/img/xiaoyu.png");
        lv_label_set_text(ui_ditutainqilabel, weather_temperature_buff);
    }
    else if (strstr(weather_report_buff, "多云") != NULL)
    {
        // lv_img_set_src(ui_morentianqiimg, "S:/nfs/ZARD/SecondDemo/img/duoyun.png");
        lv_img_set_src(ui_morentianqiimg, "S:/duoyun.png");
        lv_label_set_text(ui_tianwendulabel, weather_temperature_buff);
        printf("%d %s %s\n", __LINE__, __FUNCTION__, __FILE__);
        lv_img_set_src(ui_ditumorentianqiimg, "S:/duoyun.png");
        // lv_img_set_src(ui_ditumorentianqiimg, "S:/nfs/ZARD/SecondDemo/img/duoyun.png");
        lv_label_set_text(ui_ditutainqilabel, weather_temperature_buff);
    }
    else if (strstr(weather_report_buff, "大雨") != NULL)
    {
        lv_img_set_src(ui_morentianqiimg, "S:/dayu.png");
        // lv_img_set_src(ui_morentianqiimg, "S:/nfs/ZARD/SecondDemo/img/dayu.png");
        lv_label_set_text(ui_tianwendulabel, weather_temperature_buff);
        printf("%d %s %s\n", __LINE__, __FUNCTION__, __FILE__);
        lv_img_set_src(ui_ditumorentianqiimg, "S:/dayu.png");
        lv_img_set_src(ui_ditumorentianqiimg, "S:/nfs/ZARD/SecondDemo/img/dayu.png");
        lv_label_set_text(ui_ditutainqilabel, weather_temperature_buff);
    }
}

连接阿里云并获取阿里云市场天气API数据:
 

char * get_weather_report()
{
	int tcpsock;
	int ret;
	char ip[20]={0};
	static char rbuf[65535]={0};
	
	//定义ipv4地址结构体变量
	struct sockaddr_in bindaddr;
	bzero(&bindaddr,sizeof(bindaddr));
	bindaddr.sin_family=AF_INET; //地址协议
	bindaddr.sin_addr.s_addr=htonl(INADDR_ANY);
	bindaddr.sin_port=htons(5548);  //客户端端口号
	
	//获取你要访问的http服务器ip地址
	//http没有加密的   https在http的基础上添加了加密层
	//ali-weather.showapi.com:万维易源网址地址
	struct hostent *p=gethostbyname("ali-weather.showapi.com"); //天气预报的网址
	//获取IP地址
	struct in_addr *q=(struct in_addr *)(*(p->h_addr_list));
	strcpy(ip,inet_ntoa(*q));
	
	//定义ipv4地址结构体变量存放服务器的ip和端口号
	struct sockaddr_in serveraddr;
	bzero(&serveraddr,sizeof(serveraddr));
	serveraddr.sin_family=AF_INET;
	serveraddr.sin_addr.s_addr=inet_addr(ip); //http服务器的ip
	serveraddr.sin_port=htons(80);  //http协议,端口默认就是80
	
	//创建tcp套接字 --》买手机
	tcpsock=socket(AF_INET,SOCK_STREAM,0);
	if(tcpsock==-1)
	{
		perror("创建tcp套接字失败了!\n");
		return -1;
	}
	
	//取消端口绑定限制
	int on=1; //非零
	setsockopt(tcpsock,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
	
	//绑定ip和端口号 --》绑定手机号
	ret=bind(tcpsock,(struct sockaddr *)&bindaddr,sizeof(bindaddr));
	if(ret==-1)
	{
		perror("绑定ip和端口号失败了!\n");
		return -1;
	}
	
	//连接服务器 --》拨号
	ret=connect(tcpsock,(struct sockaddr *)&serveraddr,sizeof(serveraddr));
	if(ret==-1)
	{
		perror("连接服务器失败了!\n");
		return -1;
	}
	
    // "areaCode": "440117"
	//拼接字符串得到完整的http请求
	char *httprequest="GET /hour24?area=%E4%B8%AD%E5%9B%BD%E5%B9%BF%E5%B7%9E%E4%BB%8E%E5%8C%96%E5%8C%BA%E6%A3%8B%E6%9D%86 HTTP/1.1\r\n"
					  "Host: ali-weather.showapi.com\r\n"
					  "Authorization: APPCODE b547a1cfb0694bd3a4d6c6a8eff836db\r\n\r\n";
	//发送刚才的请求
	send(tcpsock,httprequest,strlen(httprequest),0);
	
	//接收http服务器回复的应答信息
	recv(tcpsock,rbuf,20000,0);
	
	
	//关闭套接字
	close(tcpsock);

    return rbuf;
}

解析CJSON数据:

/// @brief 解析CJSON数据得到天气数据和温度
/// @param json_str CJSON数据
/// @param data 天气数据
/// @param weather_temperature 温度
void analyse_CJSON_data(char *json_str, char *data, char *weather_temperature)
{
    char *tmp = json_str;
    int i;
    //tmp遍历到{,即CSJON数据开头,因为阿里云发过来的数据开头有一些脏数据
    for (i = 0;; i++)
    {
        if (tmp[i] == '{')
        {
            break;
        }
    }
    //json_str指向CJSON数据开头
    json_str = &tmp[i - 1];
    /**CJSON数据其实就是数据一层包着一层,我们需要一层一层解析数据来取到我们想要的数据
     * 具体步骤:
     * 第一步先把数据丢到cJSON_Parse换成链表存储,cJSON_Parse返回数据的入口地址
     * 接着根据数据是[ {} ]数组包着对象,
     *  */
    
    
    
    // 第一步:把字符串格式的json数据转换成链表存放
    cJSON *obj = cJSON_Parse(json_str);

    // 第二步,将里面的元素一步一步解析出来


    
    cJSON *showapi_res_body_obj = cJSON_GetObjectItem(obj, "showapi_res_body");

    cJSON *hourlist_obj = cJSON_GetObjectItem(showapi_res_body_obj, "hourlist");

    cJSON *arr_now = cJSON_GetArrayItem(hourlist_obj, 0);

    cJSON *weather_obj = cJSON_GetObjectItem(arr_now, "weather");

    cJSON *weather_temperature_obj = cJSON_GetObjectItem(arr_now, "temperature");
    sprintf(weather_temperature, "%s°", weather_temperature_obj->valuestring);

    printf("获取到天气:%s,温度:%s\n", weather_obj->valuestring, weather_temperature);

    memcpy(data, weather_obj->valuestring, strlen(weather_obj->valuestring));

    return;
}

更新时间线程:

void *pthreadFun_time(void *arg)
{
    // 时间缓冲区
    char timeBuff[60] = {0}, tmp[32] = {0};
    // 获取时间变量
    time_t now;
    struct tm *currentTime;
    while (1)
    {
        pthread_mutex_lock(&suo);
        time(&now);
        currentTime = localtime(&now);
        sprintf(timeBuff, "%02d:%02d:%02d",
                currentTime->tm_hour,
                currentTime->tm_min,
                currentTime->tm_sec);
        memcpy(tmp, timeBuff, strlen(timeBuff));
        if (currentTime->tm_hour > 12)
        {
            sprintf(timeBuff, "%s%c%s", tmp, ' ', "pm");
        }
        else
        {
            sprintf(timeBuff, "%s%c%s", tmp, ' ', "am");
        }

        lv_label_set_text(ui_timelabel, timeBuff);
        lv_label_set_text(ui_daohangtimelabel, timeBuff);
        lv_label_set_text(ui_cartimelabel, tmp);
        pthread_mutex_unlock(&suo);
        usleep(10000);
    }
}