這篇文章將會接續上一篇「FPV Car 第一人稱手機遙控車(Part 1)」。
教學原文參考:FPV Car 第一人稱手機遙控車
在燒錄成程式之前需要先到 config.h 設定 Wi-Fi 連線的模式,預設 Pixel:Bit 會以 AP 模式的方式運作,也就是讓手機直接連進 Pixel:Bit 進行操作。這樣的好處是不管在室內或室外,即使沒有 Wi-Fi 網路也可以進行操作。
若環境中是有 Wi-Fi 網路的,也可以將 Pixel:Bit 設定為 Satation 模式,讓手機跟 Pixel:Bit 都同時連到場域中的熱點,如此一來也可以降低 Pixel:Bit的運算負荷。SSID 與 Password 也依照環境與喜好修改完成之後即可以進行燒錄。分別燒錄 ESP32 程式碼與 ATmega328 程式碼完成後,即可以將 UART 選擇開關切至「go」進行操作了!(如果還不清楚怎麼樣燒錄程式碼,可以參考前面的單元(二)教學)
#define WIFI_AP_MODE 0
#define WIFI_STA_MODE 1
#define WIFI_MODE WIFI_AP_MODE
#define WIFI_SSID "PixelBit"
#define WIFI_PASS "circuspi"
大家在使用物聯網開發板時,經常都會有一種困擾,就是不知道開發板現在的 Wi-Fi SSID、密碼與 IP 等資訊,時常都還需要連接序列埠到電腦監看,甚至在輸入密碼時可能因為手抖而不小心輸入錯誤,真的相當的惱人啊!
在 Pixel:Bit 上有一個 TFT 顯示器,我們可以很方便的把 Wi-Fi 網路資訊顯示在上面。而連線 Wi-Fi 的 QR code 格式為 WIFI:T:WPA;S:YourSSID;P:YourPass;;,詳細的說明如下表所示:
舉例來說,我們只要將 WIFI:T:WPA;S:PixelBit;P:circuspi;; 這串文字轉換為 QR Code 顯示在 TFT 顯示器上,再使用手機掃描,手機就會自動地連入 Pixel:Bit 這組 Wi-Fi 網路,是不是相當方便呢?
產生可以快速連接 Wi-Fi 的QR Code
在此範例中我們引用的函式庫是 github 上開發者 ricmoo 所編寫的 library,為了方便起見,我們直接內嵌在此範例的程式碼中。我們使用版本 3 的 QR Code,像素為 29×29,不同版本像素與訊息量皆有不同,例如版本 4 的像素為 33×33、版本 5 的像素為 37×37 等,完整資訊可以參閱 QR Code標準。
ESP32 的程式碼則是由國外的開發者 Rui Santos 旗下專案所修改而來的,如下圖所示。網頁伺服器的程式碼結構由原生範例 CameraWebServer 差異不大。在即時串流的部分使用 <IP>:81/stream 讀取影像資料,而各個對應按鈕則是用 Command Handler 的方式在各個按鈕被按下時,觸發 <IP>/action?go= HTTP GET 方法呼叫,並再 handler 中將對應的指令透過序列埠 Serial.print() 傳送給 ATmega328 執行動作。
if(!strcmp(variable, "forward")) {
Serial.print(ESP32_CMD_FORWARD);
}
else if(!strcmp(variable, "left")) {
Serial.print(ESP32_CMD_LEFT);
}
else if(!strcmp(variable, "right")) {
Serial.print(ESP32_CMD_RIGHT);
}
else if(!strcmp(variable, "backward")) {
Serial.print(ESP32_CMD_BACK);
}
else if(!strcmp(variable, "stop")) {
Serial.print(ESP32_CMD_STOP);
}
else {
res = -1;
}
馬達控制將會透過 ATmega328p 經由邊緣連接器連到登月小車 MoonCar 上的馬達。從下方的 Pixel:Bit 與登月小車腳位對照圖可以看出,左輪的控制對應到 ATmega328p 的 2 跟 14 腳控制,而右輪的控制對應到 A1 與 13 腳控制,因此只要依照下表的方式控制這幾隻 pin 腳的電位,就能夠控制小車的動作姿態囉!
為了方便我們閱讀程式碼,先在 PixelBit_FPV_Car_328p\config.h 檔案中定義好 ATmega328 各個腳位的名稱,也避免後續忘記各個 pin 腳的功能。
如下程式所示,第 37~55 行先定義出 Pixel:Bit 邊緣連接器所對應 ATmega328 的 IO。第 57~62 行則延伸定義好左右輪的功能名稱,方便我們在做 IO 控制時使用。
#define MICRO_BIT_PIN_0 3
#define MICRO_BIT_PIN_1 A0
#define MICRO_BIT_PIN_2 A1
#define MICRO_BIT_PIN_3 A2
#define MICRO_BIT_PIN_4 A3
#define MICRO_BIT_PIN_5 4
#define MICRO_BIT_PIN_6 6
#define MICRO_BIT_PIN_7 7
#define MICRO_BIT_PIN_8 2
#define MICRO_BIT_PIN_9 8
#define MICRO_BIT_PIN_10 A7
#define MICRO_BIT_PIN_11 5
#define MICRO_BIT_PIN_12 9
#define MICRO_BIT_PIN_13 13
#define MICRO_BIT_PIN_14 12
#define MICRO_BIT_PIN_15 11
#define MICRO_BIT_PIN_16 10
#define MICRO_BIT_PIN_19 A5
#define MICRO_BIT_PIN_20 A4
#define APIN_BUTTON_A MICRO_BIT_PIN_5
#define APIN_BUTTON_B MICRO_BIT_PIN_11
#define APIN_WHEEL_R_A MICRO_BIT_PIN_2
#define APIN_WHEEL_R_B MICRO_BIT_PIN_13
#define APIN_WHEEL_L_A MICRO_BIT_PIN_8
#define APIN_WHEEL_L_B MICRO_BIT_PIN_14
此外我們也將每個小車的動作,例如前進、後退、左轉、右轉等,分別獨立寫成函式 function,後續在呼叫與閱讀上可以更為簡潔。完成各馬達的驅動函式後就接著要處理 ESP32 與 ATmeag328 之間 UART 溝通的程式,以利在使用者按下網頁上的按鈕時,可以立即做出對應的動作控制。
void m_forward(void) {
#if DEBUG
Serial.println("forward");
#endif
digitalWrite(APIN_WHEEL_R_A, HIGH);
digitalWrite(APIN_WHEEL_R_B, LOW);
digitalWrite(APIN_WHEEL_L_A, HIGH);
digitalWrite(APIN_WHEEL_L_B, LOW);
}
void m_back(void) {
#if DEBUG
Serial.println("back");
#endif
digitalWrite(APIN_WHEEL_R_A, LOW);
digitalWrite(APIN_WHEEL_R_B, HIGH);
digitalWrite(APIN_WHEEL_L_A, LOW);
digitalWrite(APIN_WHEEL_L_B, HIGH);
}
void m_left(void) {
#if DEBUG
Serial.println("left");
#endif
digitalWrite(APIN_WHEEL_R_A, HIGH);
digitalWrite(APIN_WHEEL_R_B, LOW);
digitalWrite(APIN_WHEEL_L_A, LOW);
digitalWrite(APIN_WHEEL_L_B, LOW);
}
ESP32 和 ATmega328 之間藉由序列埠 UART 相互連接,在此範例中,當我們操作網頁上的按鈕時,ESP32 就會收到事件觸發,並且將所要執行的動作(如前進、後退)傳送給 ATmega328,ATmega328 在收到對應的指令後,便會去執行相應的動作。
聰明如你應該不難想到,我們只要設計好前進、後退這些動作的對應的指令碼,在 ESP32 這一邊送出,ATmega328 這一邊接收並且判斷,即可完成這些功能了。沒有錯喔!概念即是如此而已,但我們在範例程式碼當中為了尋求程式碼的簡潔與擴充性,使用指令清單的程式技法來處理這個問題。
在 ATmega328 的程式碼第 86~95 行之間,可以看到這裡有個 CMD_T 結構的陣列,每個陣列中的成員就是一個指令以及要執行的指標函數,並且在最底部塞入一個 {NULL, 0, NULL} 代表已經沒有其他的指令要判斷了。
使用這種陣列的好處,就是我們只要調整這一個陣列的內容,就可以輕鬆的加入或刪除指令,不再需要更動到其他的程式敘述句,程式也可以看起來更加簡潔與容易閱讀喔!
// Command Handler
CMD_T cmd_list[] = {
{ESP32_CMD_FORWARD, strlen(ESP32_CMD_FORWARD), &m_forward},
{ESP32_CMD_BACK, strlen(ESP32_CMD_BACK), &m_back},
{ESP32_CMD_LEFT, strlen(ESP32_CMD_LEFT), &m_left},
{ESP32_CMD_RIGHT, strlen(ESP32_CMD_RIGHT), &m_right},
{ESP32_CMD_LSPIN, strlen(ESP32_CMD_LSPIN), &m_lspin},
{ESP32_CMD_RSPIN, strlen(ESP32_CMD_RSPIN), &m_rspin},
{ESP32_CMD_STOP, strlen(ESP32_CMD_STOP), &m_stop},
{NULL, 0, NULL} //keep this elemnet in the end of list
};
Pixel:Bit 有相容 micro:bit 的連接器,這樣的設計可以任意的搭配所有 micro:bit 擴充套件,同時結合自身的攝影鏡頭與 TFT 顯示螢幕等,打造更多的創意想像空間。
本篇教學示範 Pixel:Bit 搭配登月小車做出手機遙控車,各位夥伴們也可以額外增加在網頁控制的功能,例如:全彩 LED 控制、顏色感測器、蜂鳴器等。更多不同的可能性就讓各位玩家們自由發揮創意吧!