Ameba Arduino: [RTL8195] AmebaMotors – 用手機控制有Ameba的4輪小車

材料準備

  • Ameba x 1
  • L298N H-Bridge x 1
  • 4輪車 or 2輪車+萬向輪
  • Android Phone

範例說明

這裡我們使用的範例是 “Files” -> “Examples” -> “AmebaMotors” -> “car2wd_mobile_control”
如果沒看到這個範例,請先下載library: AmebaMotors
下載之後,參考Arduino官方網站的教學文章將zip檔的library加入Ameba: https://www.arduino.cc/en/Guide/Libraries#toc4這個範例裡,我們做了幾件事:
1. 將控制小車的方式包成Car2wd的Class,裡面實作了OS thread、signal,讓小車的thread與main thread分開執行。
2. Ameba的main thread會啟動WiFi AP mode,並開啟TCP socket成為server 端,等待client端連進來並傳送控制小車的資料
3. 手機端在下載 “Ameba Car Remote”之後,將WiFi連到 ssid “mycar” 之後,打開 app,app會連上Ameba成為client端,使用者可以操作螢幕上的控制桿來控制小車

我們一步一步完成操作:
1. 接線:接線的方式請參考範例 “用Ameba控制4輪小車” 。
2. 上傳程式碼:在將Micro USB接上Ameba之前,先將L298N的電源拔掉避免小車亂跑。然後編譯並上傳程式碼。上傳完之後再將L298N的電源接上
3. 手機下載app:請到google play,輸入關鍵字 “Ameba Car Remote”,可以找到有螃蟹圖案app。如果找不到的話,也可以在手機瀏覽器打開底下的網址: https://play.google.com/store/apps/details?id=app.akexorcist.joystickcontroller
1
4. 將手機連上Ameba:進入 “設定” -> “Wi-Fi”,在ssid列表中找到 ssid “mycar”,點選之後會要求輸入密碼,請輸入密碼 “12345678”,然後確認已連線。因為Ameba並沒有連到Internet,所以有些Android手機會跳出訊息說沒有網路能力,甚至自動地幫你斷線連到其它ssid,請注意是否正確連上。
2
5. 打開app:會看到底下的操作畫面
3

  • 左邊的操縱桿往上拉,車子會往前走;往下拉,車子會倒退
  • 右邊的操縱桿往右,車子會向右順時鐘旋轉;往左,車子向左逆時鐘旋轉
  • 同時操作左邊與右邊操縱桿,車子會做出前進轉彎或後退轉彎

疑難排解:
- 如果發現操作App但車子沒有反應,請按手機的後退鍵離開app,檢查wifi的ssid是否還連著mycar。
- 如果還連著mycar,再打開app試看看。
- 如果mycar已經不見了,檢查Ameba開發板上的燈是否還亮著,如果沒亮代表Ameba可能因為瞬間電流造成電源無法反應、或電源無法供應太大的電流、也有可能MicroUSB的線材無法接受太大的電流(有些市面上的MicroUSB甚至只能接受0.2mA的電流)。

底下是我們demo的影片:

程式碼說明

  • Car2wd與OS thread, signal的使用
    Class Car2wd裡實作OS thread與signal,整個概念如下圖:
    4

    1. 在setup()裡面我們呼叫car.begin(),裡面使用 os_thread_create() 新增一個thread: tid = os_thread_create(carTask, this, OS_PRIORITY_REALTIME, 1024);
      - 第一個參數是function pointer,是thread起來之後要執行的程式,function pointer的prototype像這樣: void carTask(const void *arg)。一般來說,RTOS的thread都會讓thread function一直執行,並且有它結束的方式。在結束之前,會用無窮迴圈放在裡面執行。
      - 第二個參數是要帶給thread funtion的參數。參考第一個參數的 carTask(const void *arg) 裡面的 arg,這邊我們將Class Car2wd指向self-instance的pointer帶給carTask, 讓carTask可以操作Car2wd的內容。
      - 第三個參數是Thread的優先權。我們使用的OS底層是FreeRTOS,當有多個Thread的情況下,FreeRTOS會只執行優先權的值最高的Thread。如果這樣的Thread超過2個,那麼FreeRTOS會輪流執行(但不會執行優先權低的)。為了避免低優先權的Thread遇到Starvation,高優先權的Thread在執行完任務的時候,要經由non-busy delay釋放執行權。
      - 第四個參數是這個Thread需要的memory。這個memory的內容包括stack與local memory。設定太小的話,有可能會在stack增加的時候覆蓋到local memory。
      - os_thread_create()的回傳值是新增的這個thread的thread id,讓main thread可以使用這個thread id對carTask做一些操作。
    2. 啟動Thread之後,會在carTask()裡面執行,一進去就執行這行等待signal: evt = os_signal_wait(0, 0xFFFFFFFF);
      - 第一個參數是要等待的signal。填0代表等待任何signal
      - 第二個參數是要等待多久,單位是ms。填0xFFFFFFFF代表沒有timeout,要一直等下去。
      - 回傳值代表等帶的結果,內容包括是否timeout,等到的signal是什麼。
      在執行os_signal_wait之後,carTask就釋放執行權,讓其它thread可以執行。
    3. 通知carTask做事:我們在main thread呼叫 car.setAction(CAR_FORWARD, abs(yspeed));
      setAction裡面(這時候還在main thread),我們只是設定好signal,然後使用os_signal_set()通知carTask os_signal_set(tid, sig);
      - 第一個參數是被通知的thread id,我們填入carTask的thread id,第二個參數是signal的內容
    4. 在carTask裡面,原本在os_signal_wait()等待,這時候等待的signal出現了,就繼續往下執行,並且查看signal的內容做對應的工作
  • Ameba AP mode與建立TCP socket
    開啟AP mode的方式請參考 Ameba wifi ap的範例。在開啟AP mode之後,我們建立tcp socket的方式與之前一樣
    1. 設定server要聽的port
    WiFiServer server(5001);
    2. 設定server端
    WiFiClient client = server.available();
    3. 等待client連進來
    while (client.connected()) {
    4. 讀取client0傳送過來的資料
    client.read(buffer, sizeof(buffer))
    5. 我們parsing這份資料:我們會收到Android app “Ameba Car Remote”的資料,資料格式是文字:
    Y:12 - 代表Y軸(前後)的值是12(值的範圍是 -12 ~ +12),車子會全速往前跑
    Y:-6 - 車子會半速倒退
    X:12 - 車子會全速向右順時鐘旋轉
    Y:12, X:-6 - 車子會全速前進向左彎(當X跟Y都有速度的值時,使用Y的數值)