【开源硬件】小安派AiPi-Eyes-R2详细测评+DIY天气时钟

描述

以下作品由安信可社区用户小浪先生制作

产品开箱

首先谢谢安信可官方送的开发板,安信可AiPi-Eyes-R2包含清单如下:

AiPi-Eyes-R2开发板  
腔体喇叭-2pin间距1.25mm黄色超薄插头 2
1.25mm4pin转1.25mm端子反向连接线-200mm(连接摄像头) 1
转接线-1.25mm转2.54mm6p 1
摄像头模组-酷视-CV-031C50-1.25mm4pin接口-130W像素 1
显示器-4.0寸-RGB接口40寸-像素480x480 1
咪头-2pin-1.25mm间距-交叉绞线100mm 2

具体开箱流程照片如下:

Linux

环境搭建

因为之前笔者已经用过Windows开发小安派-Eyes-S1了,这次使用Linux进行开发。

安装VMware

笔记本电脑中已经安装过VMware了,可以参考安信可官方给出的安装教程,具体链接如下:安信可官方教程链接

安装Ubuntu22.04

为什么用22.04版本,不用18.04版本呢??

因为ubuntu官方推出了24.04LST版本,之前的版本应该比较稳定了,这次尝试用新版本。也可选用安信可官方推荐的18.04版本,旧版本用得久了网上资源也比较多。

下载ios镜像文件

登录ubuntu官网链接,点击如下图所示位置“check out our alternative downloads”。

Linux

往下滑找到“All past releases ›”。

Linux

往下滑找到“22.04.05”。

Linux

往下滑找到“ubuntu-22.04.5-desktop-amd64.iso”,点击进行下载。

Linux

创建虚拟机

打开VMware,点击【创建新的虚拟机】,选择【典型】,点击下一步。

Linux

选择【稍后安装操作系统】,点击下一步。

Linux

选择客服及操作系统为Linux,版本为Ubuntu64位。

Linux

命名虚拟机,并选择合适的保存路径。

Linux

指定磁盘容量大小,最少20G,建议40G。

Linux

完成虚拟机创建。

Linux

安装虚拟机

设置CD/DVD类型,内存至少为4G,笔者的电脑是32G的,所以设置为8G。双击下图框选处,打开设置。

Linux

设置内存、选择下载的ISO镜像文件,点击确定。

Linux

点击【开启此虚拟机】。

Linux

选择第一项install ubuntu,回车。

Linux

选择中文简体,点击【安装Ubuntu】,这里看着窗口比较小,不要担心,后面会恢复的。

Linux

键盘布局默认即可,选择继续。

Linux

选择最小安装,并取消勾选“安装Ubuntu时更新”。

Linux

选择现在安装。

Linux

点击继续。

Linux

时区默认shanghai即可,点击继续。

Linux

设置计算机名及密码,然后点击继续,等待安装即可。

Linux

安装好后,直接用密码登录即可。

Linux

安装小安派开发环境

Ctrl+Alt+T快捷键打开终端,输入以下质量+回车,安装依赖:

 

sudo apt-get install make gcc vim cmake git ninja-build -y

 

Linux

mkdir创建新的文件夹,cd进入文件夹,克隆SDK

 

git clone -b master https://gitee.com/Ai-Thinker-Open/AiPi-Open-Kits.git

 

Linux

显示隐藏文件,并将文件中的github改为gitee,保存。

更新子模块,以防部分文件夹为空,依次执行以下命令

 

cd AiPi-Open-Kitsgit submodule init git submodule update

 

Linux

进入SDK文件夹,执行脚本文件

 

cd aithinker_Ai-M6X_SDK/. install.sh

 

Linux

 

.  export.sh

 

Linux

至此,环境就按照完成了。

误用VSCode远程访问Ubuntu

VSCode我Windows本地已经安装好了,VSCode是一个免费的软件,这里就不再介绍如何安装,比较简单,下面介绍一下如何用本地VSCode远程访问Ubuntu。

安装VSCode插件Remote-SSH

在VSCode扩展中搜索,Remote-SSH,进行安装。

Linux

在ubuntu中终端输入以下命令,获取ubuntu的ip地址。

 

ifconfig

 

Linux

在VSCode左侧的“远程资源管理器”中,按下图操作,在框中输入ubuntu的ip地址+回车,选择选项的第一个C:Usersxxx.sshconfig。

Linux

按下图操作可以打开配置文件,Host可以任意配置,但是User必须是Ubuntu的用户名,如果不知道自己的用户名可以查看终端命令行前面的“@”前面的内容就是用户名,可以直接复制。

LinuxLinux

选择在当前窗口建立远程连接(->)或者点击右侧的按钮,在新窗口建立连接。

Linux

选择远程平台的系统,此处为Linux。

Linux

输入系统用户的密码+回车,即可控制系统了。

Linux

编译程序

首先在Wiindows下的VSCode打开文件夹,点击确定。

Linux

从../AiPi-Open-Kits/aithinker_Ai-M6X_SDK/examples/helloworld文件夹中复制helloword文件夹到AiPi-Open-Kits目录下,我这里将文件名修改成了Smart_dvc,修改SDK路径为图中所示。

Linux

此时还不能编译,需要将小安派的配置同步至SDK内部,进入aithinker_Ai-M6X_SDK文件夹,分步执行以下命令:

 

. update_sdk.sh. 

 

 

export.sh

 

Linux

然后进入到Smart_dvc文件夹,进行编译,发现会报错:fatal error: lwip/dns.h: No such file or directory,解决方法是注释board.c中的#include "lwip/dns.h" 和 ip_addr_t dns_addr 变量定义这两行代码。

Linux

再次编译,显示编译成功!

Linux

此种方法并没有一劳永逸的解决环境变量问题,需要每次打开终端都需要执行一次.install.sh和. export.sh,具体操作参照我刚发的一篇文章:一劳永逸解决编译问题。

下载程序

下载程序就USB转TTL的TX接板子的RX、RX接板子的TX;原则上需要共地,我是通过电脑TypeC对小安派Eyes-R2供电,所以就不用共地了,在终端执行以下命令即可完成程序的下载。

 

make flash

 

Linux

到这里看串口就会发现,程序执行不了。那是因为AiPI-Eyes-R1/R2 出厂时做了加密处理,所以在运行的时候,需要指定特定的 boot2及验证固件,否则程序可能无法正常运行。将工程中的flash_prog_cfg.ini增加以下代码:

 

# 配置boot2固件,否则无法使用复位烧录功能(Configure boot2 firmware, otherwise the reset burn function cannot be used) 
[boot2] 
filedir = ./build/build_out/Rx_boot2*.bin 

address = 0x000000 
[edata] filedir = ./build/build_out/edata.bin 
address = 0x3e0000 

# 配置partition固件,这是必要的(Configuring partition firmware is necessary) 
[partition] 
filedir = ./build/build_out/partition*.bin 
address = 0xE000

 

Linux

将两个文件复制到 ./build/build_out/文件夹下,boot2在以下目录中:

AiPi-Open-Kitsaithinker_Ai-M6X_SDKbspboardbl616dkconfig

edata.bin在以下目录:

AiPi-Open-KitsAiPi-Eyes-Rxboardconfig

性能测试

下载AiPi-Eyes-Rx工程编译后的文件,可以看到屏幕帧率8FPS、CPU使用率40%。

Linux

DIY项目

利用GUI Guider设计的界面和心知天气API获取近三天的天气。在工程中使用了FreeRTOS。自己建的工程,自己一点一点搭起来的,程序框架还算可以(还有进步的空间,后续优化好了再开源。屏幕刷新率杠杠的)。

效果展示:

Linux

工程搭建

工程搭建我遇到的问题,估计大家都会遇到,在我往期的帖子里可以看到。我是利用helloword工程一点点搭建的,对理解工程框架很有帮助,有问题可以留言探讨。以下是我往期的帖子:

环境搭建

查找AiPI-Eyes-R1/R2 特定的 boot2及验证固件

工程框架

天气时钟工程大致可以分为三部分:

1.连接wifi
2.请求api,获取天气
3.GUI界面显示

连接WiFi

连接wifi和http请求部分是参考的这篇文章【教程贴】M61-32S系列连接Wifi 且发送HTTP请求。

连接wifi部分不需要改动,http请求部分,确实会像这位博主所说的,会有卡死的的几率,查阅资料发现,使用的recv函数是死等待,请求没有返回时,程序会跑飞。

笔者在recv前面加入了下面几行代码设置了超时时间,算是暂时解决了这个问题,跑程序一两个小时没有卡死,不过还需进一步验证测试。

 

// 设置recv超时时间
struct timeval tv_out;
tv_out.tv_sec = 5;
tv_out.tv_usec = 0;

 

获取天气

天气api是采用的新知天气api获取近3天的天气,前面http请求获取的数据是json数据,利用开源的cJSON库进行解析。cJSON库下载链接。

网上很多人说,在cJSON库解析过程中,很容易卡死,那是因为没有及时删除cJSON对象。每次解析完都要删除,不然解析次数多了就会卡死。

 

cJSON_Delete(root); /*每次调用cJSON_Parse函数后,都要释放内存*/
解析代码如下:
int weather_info_parse(char *weather_data_buf)
{
// 对接收到的数据作相应的处理
uint8_t i, j;
uint8_t result_array_size = 0;
uint8_t daily_array_size = 0;
	cJSON *root = NULL;
	cJSON *item = NULL;
	cJSON *results_root = NULL;
	cJSON *daily_root = NULL;
	root = cJSON_Parse(weather_data_buf);
if (!root)
	{
// ESP_LOGI(TAG, "Error before: [%s]n", cJSON_GetErrorPtr());
LOG_I("Error before: [%s]rn", cJSON_GetErrorPtr());
cJSON_Delete(root); /*每次调用cJSON_Parse函数后,都要释放内存*/
return -1;
	}
//    ESP_LOGI(TAG, "%srn", cJSON_Print(root)); /*将完整的数据以JSON格式打印出来*/
	cJSON *Presult = cJSON_GetObjectItem(root, "results"); /*results 的键值对为数组,*/
	result_array_size = cJSON_GetArraySize(Presult); /*求results键值对数组中有多少个元素*/
//	ESP_LOGI(TAG, "Presult array size is %dn",result_array_size);
for (i = 0; i < result_array_size; i++)
	{
		cJSON *item_results = cJSON_GetArrayItem(Presult, i);
char *sresults = cJSON_PrintUnformatted(item_results);
		results_root = cJSON_Parse(sresults);
if (!results_root)
		{
// ESP_LOGI(TAG, "Error before: [%s]n", cJSON_GetErrorPtr());
LOG_I("Error before: [%s]rn", cJSON_GetErrorPtr());
cJSON_Delete(root); /*每次调用cJSON_Parse函数后,都要释放内存*/
return -1;
		}
/*-------------------------------------------------------------------*/
		cJSON *Plocation = cJSON_GetObjectItem(results_root, "location");
		item = cJSON_GetObjectItem(Plocation, "id");
		user_sen_config.id = cJSON_Print(item);
// ESP_LOGI(TAG, "id:%sn",  user_sen_config.id);	/*逐个打印*/
		item = cJSON_GetObjectItem(Plocation, "name");
		user_sen_config.name = cJSON_Print(item);
//	        ESP_LOGI(TAG, "name:%sn", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "country");
		user_sen_config.country = cJSON_Print(item);
//	        ESP_LOGI(TAG, "country:%sn", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "path");
		user_sen_config.path = cJSON_Print(item);
//	        ESP_LOGI(TAG, "path:%sn", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "timezone");
		user_sen_config.timezone = cJSON_Print(item);
//	        ESP_LOGI(TAG, "timezone:%sn", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "timezone_offset");
		user_sen_config.timezone_offset = cJSON_Print(item);
//	        ESP_LOGI(TAG, "timezone_offset:%sn", cJSON_Print(item));
/*-------------------------------------------------------------------*/
		cJSON *Pdaily = cJSON_GetObjectItem(results_root, "daily");
		daily_array_size = cJSON_GetArraySize(Pdaily);
//			ESP_LOGI(TAG, "Pdaily array size is %dn",daily_array_size);
for (j = 0; j < daily_array_size; j++)
		{
			cJSON *item_daily = cJSON_GetArrayItem(Pdaily, j);
char *sdaily = cJSON_PrintUnformatted(item_daily);
			daily_root = cJSON_Parse(sdaily);
if (!daily_root)
			{
LOG_I("Error before: [%s]rn", cJSON_GetErrorPtr());
return -1;
			}
			item = cJSON_GetObjectItem(daily_root, "date");
			user_sen_config.day_config[j].date = item- >valuestring; // cJSON_Print(item)
			item = cJSON_GetObjectItem(daily_root, "text_day");
			user_sen_config.day_config[j].text_day = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "code_day");		user_sen_config.day_config[j].code_day = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "text_night");
			user_sen_config.day_config[j].text_night = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "code_night");
			user_sen_config.day_config[j].code_night = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "high");
			user_sen_config.day_config[j].high = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "low");
			user_sen_config.day_config[j].low = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "rainfall");
			user_sen_config.day_config[j].rainfall = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "precip");
			user_sen_config.day_config[j].precip = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "wind_direction");
			user_sen_config.day_config[j].wind_direction = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "wind_direction_degree");
			user_sen_config.day_config[j].wind_direction_degree = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "wind_speed");
			user_sen_config.day_config[j].wind_speed = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "wind_scale");
			user_sen_config.day_config[j].wind_scale = cJSON_Print(item);
			item = cJSON_GetObjectItem(daily_root, "humidity");
			user_sen_config.day_config[j].humidity = cJSON_Print(item);
cJSON_Delete(daily_root); /*每次调用cJSON_Parse函数后,都要释放内存*/
		}
/*-------------------------------------------------------------------*/
		item = cJSON_GetObjectItem(results_root, "last_update");
		user_sen_config.last_update = cJSON_Print(item);
cJSON_Delete(results_root); /*每次调用cJSON_Parse函数后,都要释放内存*/
	}
cJSON_Delete(root); /*每次调用cJSON_Parse函数后,都要释放内存*/
return 0;
}

 

GUI界面设计

在配置完工程所需的LVGL配置后可能会发现,自己配置的LVGL程序并不能执行,而且..AiPi-Open-Kitsaithinker_Ai-M6X_SDKexamples下的lvgl示例也不能正确执行,不要怀疑,配置可能是没问题的。

具体解决方法参考小安派R2工程移植LVGL后不运行【暂时解决】这篇帖子。

GUI设计是利用NXP的GUI Guider进行设计的,这里介绍一下怎么移植文件,当程序配置好LVGL组件并能正常执行demo后,将GUI工程中的custom和generated文件夹放入自己的工程中,在Cmake.List文件中配置好路径,可能generated文件夹中的images文件夹中的生成的图片可能会报错,只需要将lgvllvgl.h改为lvgl.h即可。

在mian.c中加入头文件

 

// lvgl
#include "gui_guider.h"
#include "events_init.h"

 

在mian函数中实例化对象

 

setup_ui(&guider_ui);
events_init(&guider_ui);

 

就可以正常显示通过GUI Guider设计的界面了,我这里是用的FreeRTOS的任务调用的lvgl处理函数:

 

void lvgl_task(void *pvParameters)
{
while (1)
	{
// LOG_I("lvgl_task is runing...rn");
lv_task_handler();
vTaskDelay(1);
// bflb_mtimer_delay_ms(1);
	}
}

 

下面是用GUI Guider设计,很简单的一个界面。
 

Linux

产品不足和建议

不足

不知道大家在使用过程中有没有发现模块屏幕的接口和USB供电接口放在了一侧,这使得插上屏幕后,Type-C很难插上,即使插上Type-C了,屏幕也会翘起来。可能会设想设计的初衷是想让开发板折在屏幕的背后,但是这样做,会看不见屏幕或者本身插好了下载口,屏幕正对人,会很难看到按键等。

建议

修改一下Type-C口和屏幕口布局,不方便做单片机开发板利用。

最后,欢迎大家来安信可论坛,笔者发布的原贴下一起交流讨论:

原贴地址

【小安派R2测评】安信可小安派R2开箱、环境搭建、性能测试、天气时钟、不足与建议

【小安派R2测评】安信可小安派R2 天气时钟


审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分