首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【开源硬件】小安派AiPi-Eyes-R2详细测评+DIY天气时钟

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

原创
作者头像
安信可科技
发布2025-08-13 11:22:41
发布2025-08-13 11:22:41
3700
举报
文章被收录于专栏:DIYDIY

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

产品开箱

首先谢谢安信可官方送的开发板,安信可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

具体开箱流程照片如下:

环境搭建

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

安装VMware

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

安装Ubuntu22.04

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

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

下载ios镜像文件

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

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

往下滑找到“22.04.05”。

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

创建虚拟机

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

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

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

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

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

完成虚拟机创建。

安装虚拟机

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

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

点击【开启此虚拟机】。

选择第一项install ubuntu,回车。

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

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

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

选择现在安装。

点击继续。

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

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

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

安装小安派开发环境

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

代码语言:javascript
复制
sudo apt-get install make gcc vim cmake git ninja-build -y

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

代码语言:javascript
复制
git clone -b master https://gitee.com/Ai-Thinker-Open/AiPi-Open-Kits.git

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

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

代码语言:javascript
复制
cd AiPi-Open-Kitsgit submodule init git submodule update

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

代码语言:javascript
复制
cd aithinker_Ai-M6X_SDK/. install.sh
代码语言:javascript
复制
.  export.sh

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

误用VSCode远程访问Ubuntu

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

安装VSCode插件Remote-SSH

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

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

代码语言:javascript
复制
ifconfig

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

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

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

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

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

编译程序

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

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

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

代码语言:javascript
复制
. update_sdk.sh. 
代码语言:javascript
复制
export.sh

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

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

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

下载程序

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

代码语言:javascript
复制
make flash

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

代码语言:javascript
复制
# 配置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

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

\AiPi-Open-Kits\aithinker_Ai-M6X_SDK\bsp\board\bl616dk\config

edata.bin在以下目录:

\AiPi-Open-Kits\AiPi-Eyes-Rx\board\config

性能测试

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

DIY项目

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

效果展示:

工程搭建

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

环境搭建

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

工程框架

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

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

连接WiFi

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

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

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

代码语言:javascript
复制
// 设置recv超时时间
struct timeval tv_out;
tv_out.tv_sec = 5;
tv_out.tv_usec = 0;

获取天气

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

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

代码语言:javascript
复制
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]\r\n", cJSON_GetErrorPtr());
cJSON_Delete(root); /*每次调用cJSON_Parse函数后,都要释放内存*/
return -1;
	}
//    ESP_LOGI(TAG, "%s\r\n", 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 %d\n",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]\r\n", 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:%s\n",  user_sen_config.id);	/*逐个打印*/
		item = cJSON_GetObjectItem(Plocation, "name");
		user_sen_config.name = cJSON_Print(item);
//	        ESP_LOGI(TAG, "name:%s\n", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "country");
		user_sen_config.country = cJSON_Print(item);
//	        ESP_LOGI(TAG, "country:%s\n", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "path");
		user_sen_config.path = cJSON_Print(item);
//	        ESP_LOGI(TAG, "path:%s\n", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "timezone");
		user_sen_config.timezone = cJSON_Print(item);
//	        ESP_LOGI(TAG, "timezone:%s\n", cJSON_Print(item));
		item = cJSON_GetObjectItem(Plocation, "timezone_offset");
		user_sen_config.timezone_offset = cJSON_Print(item);
//	        ESP_LOGI(TAG, "timezone_offset:%s\n", cJSON_Print(item));
/*-------------------------------------------------------------------*/
		cJSON *Pdaily = cJSON_GetObjectItem(results_root, "daily");
		daily_array_size = cJSON_GetArraySize(Pdaily);
//			ESP_LOGI(TAG, "Pdaily array size is %d\n",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]\r\n", 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-Kits\aithinker_Ai-M6X_SDK\examples下的lvgl示例也不能正确执行,不要怀疑,配置可能是没问题的。

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

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

在mian.c中加入头文件

代码语言:javascript
复制
// lvgl
#include "gui_guider.h"
#include "events_init.h"

mian函数中实例化对象

代码语言:javascript
复制
setup_ui(&guider_ui);
events_init(&guider_ui);

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

代码语言:javascript
复制
void lvgl_task(void *pvParameters)
{
while (1)
	{
// LOG_I("lvgl_task is runing...\r\n");
lv_task_handler();
vTaskDelay(1);
// bflb_mtimer_delay_ms(1);
	}
}

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

产品不足和建议

不足

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

建议

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

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

原贴地址

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

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 产品开箱
  • 环境搭建
  • 工程搭建
  • 产品不足和建议
  • 【小安派R2测评】安信可小安派R2开箱、环境搭建、性能测试、天气时钟、不足与建议
  • 【小安派R2测评】安信可小安派R2 天气时钟
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档