以下作品由安信可社區(qū)用戶小浪先生制作
產(chǎn)品開箱
首先謝謝安信可官方送的開發(fā)板,安信可AiPi-Eyes-R2包含清單如下:
AiPi-Eyes-R2開發(fā)板 | |
腔體喇叭-2pin間距1.25mm黃色超薄插頭 | 2 |
1.25mm4pin轉(zhuǎn)1.25mm端子反向連接線-200mm(連接攝像頭) | 1 |
轉(zhuǎn)接線-1.25mm轉(zhuǎn)2.54mm6p | 1 |
攝像頭模組-酷視-CV-031C50-1.25mm4pin接口-130W像素 | 1 |
顯示器-4.0寸-RGB接口40寸-像素480x480 | 1 |
咪頭-2pin-1.25mm間距-交叉絞線100mm | 2 |
具體開箱流程照片如下:
環(huán)境搭建
因為之前筆者已經(jīng)用過Windows開發(fā)小安派-Eyes-S1了,這次使用Linux進(jìn)行開發(fā)。
安裝VMware
筆記本電腦中已經(jīng)安裝過VMware了,可以參考安信可官方給出的安裝教程,具體鏈接如下:安信可官方教程鏈接
安裝Ubuntu22.04
為什么用22.04版本,不用18.04版本呢??
因為ubuntu官方推出了24.04LST版本,之前的版本應(yīng)該比較穩(wěn)定了,這次嘗試用新版本。也可選用安信可官方推薦的18.04版本,舊版本用得久了網(wǎng)上資源也比較多。
下載ios鏡像文件
登錄ubuntu官網(wǎng)鏈接,點擊如下圖所示位置“check out our alternative downloads”。
往下滑找到“All past releases ?”。
往下滑找到“22.04.05”。
往下滑找到“ubuntu-22.04.5-desktop-amd64.iso”,點擊進(jìn)行下載。
創(chuàng)建虛擬機(jī)
打開VMware,點擊【創(chuàng)建新的虛擬機(jī)】,選擇【典型】,點擊下一步。
選擇【稍后安裝操作系統(tǒng)】,點擊下一步。
選擇客服及操作系統(tǒng)為Linux,版本為Ubuntu64位。
命名虛擬機(jī),并選擇合適的保存路徑。
指定磁盤容量大小,最少20G,建議40G。
完成虛擬機(jī)創(chuàng)建。
安裝虛擬機(jī)
設(shè)置CD/DVD類型,內(nèi)存至少為4G,筆者的電腦是32G的,所以設(shè)置為8G。雙擊下圖框選處,打開設(shè)置。
設(shè)置內(nèi)存、選擇下載的ISO鏡像文件,點擊確定。
點擊【開啟此虛擬機(jī)】。
選擇第一項install ubuntu,回車。
選擇中文簡體,點擊【安裝Ubuntu】,這里看著窗口比較小,不要擔(dān)心,后面會恢復(fù)的。
鍵盤布局默認(rèn)即可,選擇繼續(xù)。
選擇最小安裝,并取消勾選“安裝Ubuntu時更新”。
選擇現(xiàn)在安裝。
點擊繼續(xù)。
時區(qū)默認(rèn)shanghai即可,點擊繼續(xù)。
設(shè)置計算機(jī)名及密碼,然后點擊繼續(xù),等待安裝即可。
安裝好后,直接用密碼登錄即可。
安裝小安派開發(fā)環(huán)境
Ctrl+Alt+T快捷鍵打開終端,輸入以下質(zhì)量+回車,安裝依賴:
sudo apt-get install make gcc vim cmake git ninja-build -y
mkdir創(chuàng)建新的文件夾,cd進(jìn)入文件夾,克隆SDK
git clone -b master https://gitee.com/Ai-Thinker-Open/AiPi-Open-Kits.git
顯示隱藏文件,并將文件中的github改為gitee,保存。
更新子模塊,以防部分文件夾為空,依次執(zhí)行以下命令
cd AiPi-Open-Kitsgit submodule init git submodule update
進(jìn)入SDK文件夾,執(zhí)行腳本文件
cd aithinker_Ai-M6X_SDK/. install.sh
. export.sh
至此,環(huán)境就按照完成了。
誤用VSCode遠(yuǎn)程訪問Ubuntu
VSCode我Windows本地已經(jīng)安裝好了,VSCode是一個免費(fèi)的軟件,這里就不再介紹如何安裝,比較簡單,下面介紹一下如何用本地VSCode遠(yuǎn)程訪問Ubuntu。
安裝VSCode插件Remote-SSH
在VSCode擴(kuò)展中搜索,Remote-SSH,進(jìn)行安裝。
在ubuntu中終端輸入以下命令,獲取ubuntu的ip地址。
ifconfig
在VSCode左側(cè)的“遠(yuǎn)程資源管理器”中,按下圖操作,在框中輸入ubuntu的ip地址+回車,選擇選項的第一個C:Usersxxx.sshconfig。
按下圖操作可以打開配置文件,Host可以任意配置,但是User必須是Ubuntu的用戶名,如果不知道自己的用戶名可以查看終端命令行前面的“@”前面的內(nèi)容就是用戶名,可以直接復(fù)制。
選擇在當(dāng)前窗口建立遠(yuǎn)程連接(->)或者點擊右側(cè)的按鈕,在新窗口建立連接。
選擇遠(yuǎn)程平臺的系統(tǒng),此處為Linux。
輸入系統(tǒng)用戶的密碼+回車,即可控制系統(tǒng)了。
編譯程序
首先在Wiindows下的VSCode打開文件夾,點擊確定。
從../AiPi-Open-Kits/aithinker_Ai-M6X_SDK/examples/helloworld文件夾中復(fù)制helloword文件夾到AiPi-Open-Kits目錄下,我這里將文件名修改成了Smart_dvc,修改SDK路徑為圖中所示。
此時還不能編譯,需要將小安派的配置同步至SDK內(nèi)部,進(jìn)入aithinker_Ai-M6X_SDK文件夾,分步執(zhí)行以下命令:
. update_sdk.sh.
export.sh
然后進(jìn)入到Smart_dvc文件夾,進(jìn)行編譯,發(fā)現(xiàn)會報錯:fatal error: lwip/dns.h: No such file or directory,解決方法是注釋board.c中的#include "lwip/dns.h" 和 ip_addr_t dns_addr 變量定義這兩行代碼。
再次編譯,顯示編譯成功!
此種方法并沒有一勞永逸的解決環(huán)境變量問題,需要每次打開終端都需要執(zhí)行一次.install.sh和. export.sh,具體操作參照我剛發(fā)的一篇文章:一勞永逸解決編譯問題。
下載程序
下載程序就USB轉(zhuǎn)TTL的TX接板子的RX、RX接板子的TX;原則上需要共地,我是通過電腦TypeC對小安派Eyes-R2供電,所以就不用共地了,在終端執(zhí)行以下命令即可完成程序的下載。
make flash
到這里看串口就會發(fā)現(xiàn),程序執(zhí)行不了。那是因為AiPI-Eyes-R1/R2 出廠時做了加密處理,所以在運(yùn)行的時候,需要指定特定的 boot2及驗證固件,否則程序可能無法正常運(yùn)行。將工程中的flash_prog_cfg.ini增加以下代碼:
# 配置boot2固件,否則無法使用復(fù)位燒錄功能(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
將兩個文件復(fù)制到 ./build/build_out/文件夾下,boot2在以下目錄中:
AiPi-Open-Kitsaithinker_Ai-M6X_SDKbspboardbl616dkconfig
edata.bin在以下目錄:
AiPi-Open-KitsAiPi-Eyes-Rxboardconfig
性能測試
下載AiPi-Eyes-Rx工程編譯后的文件,可以看到屏幕幀率8FPS、CPU使用率40%。
DIY項目
利用GUI Guider設(shè)計的界面和心知天氣API獲取近三天的天氣。在工程中使用了FreeRTOS。自己建的工程,自己一點一點搭起來的,程序框架還算可以(還有進(jìn)步的空間,后續(xù)優(yōu)化好了再開源。屏幕刷新率杠杠的)。
效果展示:
工程搭建
工程搭建我遇到的問題,估計大家都會遇到,在我往期的帖子里可以看到。我是利用helloword工程一點點搭建的,對理解工程框架很有幫助,有問題可以留言探討。以下是我往期的帖子:
環(huán)境搭建
查找AiPI-Eyes-R1/R2 特定的 boot2及驗證固件
工程框架
天氣時鐘工程大致可以分為三部分:
1.連接wifi
2.請求api,獲取天氣
3.GUI界面顯示
連接WiFi
連接wifi和http請求部分是參考的這篇文章【教程貼】M61-32S系列連接Wifi 且發(fā)送HTTP請求。
連接wifi部分不需要改動,http請求部分,確實會像這位博主所說的,會有卡死的的幾率,查閱資料發(fā)現(xiàn),使用的recv函數(shù)是死等待,請求沒有返回時,程序會跑飛。
筆者在recv前面加入了下面幾行代碼設(shè)置了超時時間,算是暫時解決了這個問題,跑程序一兩個小時沒有卡死,不過還需進(jìn)一步驗證測試。
// 設(shè)置recv超時時間 struct timeval tv_out; tv_out.tv_sec = 5; tv_out.tv_usec = 0;
獲取天氣
天氣api是采用的新知天氣api獲取近3天的天氣,前面http請求獲取的數(shù)據(jù)是json數(shù)據(jù),利用開源的cJSON庫進(jìn)行解析。cJSON庫下載鏈接。
網(wǎng)上很多人說,在cJSON庫解析過程中,很容易卡死,那是因為沒有及時刪除cJSON對象。每次解析完都要刪除,不然解析次數(shù)多了就會卡死。
cJSON_Delete(root); /*每次調(diào)用cJSON_Parse函數(shù)后,都要釋放內(nèi)存*/ 解析代碼如下: int weather_info_parse(char *weather_data_buf) { // 對接收到的數(shù)據(jù)作相應(yīng)的處理 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); /*每次調(diào)用cJSON_Parse函數(shù)后,都要釋放內(nèi)存*/ return -1; } // ESP_LOGI(TAG, "%srn", cJSON_Print(root)); /*將完整的數(shù)據(jù)以JSON格式打印出來*/ cJSON *Presult = cJSON_GetObjectItem(root, "results"); /*results 的鍵值對為數(shù)組,*/ result_array_size = cJSON_GetArraySize(Presult); /*求results鍵值對數(shù)組中有多少個元素*/ // 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); /*每次調(diào)用cJSON_Parse函數(shù)后,都要釋放內(nèi)存*/ 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); /*每次調(diào)用cJSON_Parse函數(shù)后,都要釋放內(nèi)存*/ } /*-------------------------------------------------------------------*/ item = cJSON_GetObjectItem(results_root, "last_update"); user_sen_config.last_update = cJSON_Print(item); cJSON_Delete(results_root); /*每次調(diào)用cJSON_Parse函數(shù)后,都要釋放內(nèi)存*/ } cJSON_Delete(root); /*每次調(diào)用cJSON_Parse函數(shù)后,都要釋放內(nèi)存*/ return 0; }
GUI界面設(shè)計
在配置完工程所需的LVGL配置后可能會發(fā)現(xiàn),自己配置的LVGL程序并不能執(zhí)行,而且..AiPi-Open-Kitsaithinker_Ai-M6X_SDKexamples下的lvgl示例也不能正確執(zhí)行,不要懷疑,配置可能是沒問題的。
具體解決方法參考小安派R2工程移植LVGL后不運(yùn)行【暫時解決】這篇帖子。
GUI設(shè)計是利用NXP的GUI Guider進(jìn)行設(shè)計的,這里介紹一下怎么移植文件,當(dāng)程序配置好LVGL組件并能正常執(zhí)行demo后,將GUI工程中的custom和generated文件夾放入自己的工程中,在Cmake.List文件中配置好路徑,可能generated文件夾中的images文件夾中的生成的圖片可能會報錯,只需要將lgvllvgl.h改為lvgl.h即可。
在mian.c中加入頭文件
// lvgl #include "gui_guider.h" #include "events_init.h"
在mian函數(shù)中實例化對象
setup_ui(&guider_ui); events_init(&guider_ui);
就可以正常顯示通過GUI Guider設(shè)計的界面了,我這里是用的FreeRTOS的任務(wù)調(diào)用的lvgl處理函數(shù):
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設(shè)計,很簡單的一個界面。
產(chǎn)品不足和建議
不足
不知道大家在使用過程中有沒有發(fā)現(xiàn)模塊屏幕的接口和USB供電接口放在了一側(cè),這使得插上屏幕后,Type-C很難插上,即使插上Type-C了,屏幕也會翹起來??赡軙O(shè)想設(shè)計的初衷是想讓開發(fā)板折在屏幕的背后,但是這樣做,會看不見屏幕或者本身插好了下載口,屏幕正對人,會很難看到按鍵等。
建議
修改一下Type-C口和屏幕口布局,不方便做單片機(jī)開發(fā)板利用。
最后,歡迎大家來安信可論壇,筆者發(fā)布的原貼下一起交流討論:
原貼地址
【小安派R2測評】安信可小安派R2開箱、環(huán)境搭建、性能測試、天氣時鐘、不足與建議
【小安派R2測評】安信可小安派R2 天氣時鐘
審核編輯 黃宇
-
Linux
+關(guān)注
關(guān)注
88文章
11576瀏覽量
216651 -
開源硬件
+關(guān)注
關(guān)注
8文章
223瀏覽量
30917 -
開發(fā)板
+關(guān)注
關(guān)注
25文章
5999瀏覽量
110056
發(fā)布評論請先 登錄
小安派立式桌擺外殼設(shè)計制作

零基礎(chǔ)開發(fā)小安派-Eyes-S1 進(jìn)階篇 ——通過屏幕輸入連接 Wi-Fi

零基礎(chǔ)開發(fā)AiPi-Eyes-S1——通過屏幕輸入連接Wi-Fi

用小安派 DSL做一個天氣站

零基礎(chǔ)開發(fā)小安派-Eyes-S1【進(jìn)階篇】——初識 LVGL 并搭建最小工程

零基礎(chǔ)開發(fā)小安派-Eyes-S1——初識LVGL并搭建最小工程
零基礎(chǔ)開發(fā)小安派-Eyes-S1 外設(shè)篇——DAC

零基礎(chǔ)開發(fā)小安派-Eyes-S1外設(shè)篇——I2S

零基礎(chǔ)開發(fā)小安派-Eyes-S1【外設(shè)篇】——FLASH

零基礎(chǔ)開發(fā)小安派-Eyes-S1 外設(shè)篇 ——I2C

用小安派 AiPi-Eyes-R2 玩植物大戰(zhàn)僵尸

零基礎(chǔ)開發(fā)小安派-Eyes-S1【外設(shè)篇】——PWM

零基礎(chǔ)開發(fā)小安派-Eyes-S1外設(shè)篇——GPIO中斷編程

零基礎(chǔ)開發(fā)小安派-Eyes-S1外設(shè)篇——GPIO 輸入輸出

評論