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的数值)