2015年12月26日 星期六

[Arduino] Ex6_3:用紅外線遙控MeArm

Ex6_3:用紅外線遙控MeArm[2]
Ted Lee@CAVEDU
Jan. 1, 2016
88x31.png[4]

本文將介紹常見的家電紅外線遙控器來操作MeArm用四顆伺服馬達來模機器擬四軸機器手臂的動作。我們採用各個撃破演算法將題目拆解成兩大塊:紅外線搖控處理(Lab6_3_1)MeArm操作,再搭配三個實驗(Lab6_3_1-3)來細細品甞Arduino在自動控制上的威力!
本範例过関後,可獲得紅外線控制卡一張。

難易度:中

紅外線(Infrared)簡稱IR,是波長0.75-1000mm電磁波。它的應用方式為發送器(transmitter)發射紅外線電波信号(signal),接收器(receiver)接收到此信號後進行解碼(decoding)以控制它对應的電器裝置(devices),如家電用品(如冷氣、音響…)、電腦週邊裝置(如無線滑鼠、鍵盤…)都配有紅外線遙控器供我們遠控(remote control)操作。
以下的3個實驗Lab6_1-3將使用家電中常見的紅外線遙控器來控制MeArm以4顆伺服馬達(servo motors),用以模擬機器人手臂(robotic arms)的四軸運動。

材料:
  1. 紅外線遙控器一支
  2. 38kHz紅外線接收器(例如:TSOP38238、TSOP2438、TSOP4438、PNA4602、FM6038TH2、FM8038TM25DN)一顆
  3. MeArm一台
  4. 杜邦線若干條
  5. 麵包板一塊
  6. Arduino Uno R3一塊
  7. A-B型USB傳輸線一條

Lab6_3_1:拆解版(紅外線遙控器內建的通信協定能被IRremote函式庫正確识別)
Lab6_3_2:速配版(紅外線遙控器內建的通信協定能被IRremote函式庫正確识別+按正確解碼)
Lab6_3_3:不知是何姓名版(無法识別遙控器)

Lab6_1-3都援用了Ken Shirriff[2]寫的IRremote函式庫,下載後請放到Arduino的函數庫內──若程式編譯時出現和函式庫衝突,請參考葉難的解方(solution)

Windows:My Documents\Arduino\libraries\
Mac:Documents/Arduino/libraries
Linux:sketchbook內的libraries資料夾

Lab6_3_1:拆解版
這個實驗使用Mac專用遙控器38kHz紅外線接收器(接腳由左至右依順為Vout, GND, Vcc)。
程式碼:
#include <IRremote.h>

int RECV_PIN = 2;        //使用數位腳位2接收紅外線訊號
IRrecv irrecv(RECV_PIN); //初始化紅外線訊號輸入[3]
decode_results results;  //儲存訊號的結構

void setup() {
  Serial.begin(9600);
  irrecv.blink13(true); //設為true的話,當收到訊號時,腳位13的LED便會閃爍
  irrecv.enableIRIn();  //啟動接收
}

void loop() {
  if (irrecv.decode(&results)) {   //接收紅外線訊號並解碼
    Serial.print("results value is "); //輸出解碼後的資料
    Serial.print(results.value, HEX);
    Serial.print(", bits is ");
    Serial.print(results.bits);
    Serial.print(", decode_type is ");
    Serial.println(results.decode_type);
    irrecv.resume(); //準備接收下一個訊號
  }
}

Lab6_3_2:速配版
接線图:(.fzz
程式流程图如下(.vsdx):
程式碼:程式撰寫逻輯採演算法的各個撃破(divide and conquer)技巧,共可分為紅外線搖控處理(Lab6_3_1)MeArm操作兩段落,MeArm四軸的伺服馬達基本控制請参考Ex2_1,馬達與程式的對應為
不同遙控器的按鍵內碼需修改switch中各方向控制的case选項
#include <Servo.h>
#include <SoftwareSerial.h>
#include <IRremote.h>

const int irReceiverPin = 2; //紅外線接收器輸入訊號接在Arduino第2腳
IRrecv irrecv(irReceiverPin); //宣告IRrecv物件來接收紅外線訊號
decode_results results;

Servo servo_0, servo_1, servo_2, servo_3;

int i0 = 110;
int i1 = 150;
int i2 = 110;
int i3 = 130;
int i0max = 180;
int i0min = 0;
int i1max = 180;
int i1min = 90;
int i2max = 150;
int i2min = 50;
int i3max = 160;
int i3min = 100;

void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn();
  delay(100); //等候信号穩定

  servo_0.attach(11); //buttom
  servo_0.write(i0);

  servo_1.attach(10); //right(面对爪子)
  servo_1.write(i1);

  servo_2.attach(9); //left
  servo_2.write(i2);

  servo_3.attach(5); //claw
  servo_3.write(i3);

  delay(100); //等候信號穩定
} //end of setup()

void loop() {
  if (irrecv.decode(&results)) {
    if (results.value != -1) {
      showIRProtocol(&results);   //顯示紅外線協定的種類
      Serial.println(results.value, HEX);

      switch (results.value) {
        case 0x38863BF8: //前進
          Serial.println("0x2F0:forward");

          if (i2 < i2max) i2++;
          break;
        case 0x38863BC4: //左轉
          Serial.println("0x2D0:turn left");

          if (i0 > i0min) i0++;
          break;
        case 0x38863BE4: //右轉
          Serial.println("0xCD0:turn right");

          if (i0 > i0min) i0--;
          break;
        case 0x38863BC8: //後退
          Serial.println("0xAF0:back");

          if (i2 >= i2min) i2--;
          break;
        case 0x38863BE8: //上移
          Serial.println("0x90:up");

          if (i1 < i1max) i1++;
          break;
        case 0x38863BD8: //下移
          Serial.println("0x890:down");

          if (i1 > i1min) i1--;
          break;
        case 0x38863BE0: //夾緊
          Serial.println("0x490:tight");

          if (i3 < i3max) i3++;
          break;
        case 0x38863BD0: //放鬆
          Serial.println("0xC90:loose");

          if (i3 > i3min) i3--;
          break;
      }//end of switch
    }//end of if

    irrecv.resume(); //Receive the next value

    servo_0.write(i0); //設定四軸初始的転角
    servo_1.write(i1);
    servo_2.write(i2);
    servo_3.write(i3);

    Serial.print("i0 pos ="); //顯示四軸的転角pos
    Serial.println(i0);
    Serial.print("i1 pos =");
    Serial.println(i1);
    Serial.print("i2 pos =");
    Serial.println(i2);
    Serial.print("i3 pos =");
    Serial.println(i3);
    delay(10); //等候信号穩定

    Serial.flush();
  } //end of if
} //end of loop()

void showIRProtocol(decode_results *results) {
  Serial.print("Protocol: ");

  //判斷紅外線協定種類
  switch (results->decode_type) {
    case NEC:
      Serial.print("NEC");
      break;
    case SONY:
      Serial.print("SONY");
      break;
    case RC5:
      Serial.print("RC5");
      break;
    case RC6:
      Serial.print("RC6");
      break;
    default:
      Serial.print("Unknown encoding");
  } //end of switch()

  //把紅外線編碼印到 Serial port
  Serial.print(", irCode: ");
  Serial.print(results->value, HEX);    // 紅外線編碼
  Serial.print(",  bits: ");
  Serial.println(results->bits);        // 紅外線編碼位元數
} //end of showIRProtocol()


以下為加上行号(line number)的程式碼,用以和流程圖相对照,並增進程式撰寫逻輯有更一步了解:
#include <Servo.h>
#include <SoftwareSerial.h>
#include <IRremote.h>

const int irReceiverPin = 2; //紅外線接收器輸入訊號接在Arduino第2腳
IRrecv irrecv(irReceiverPin); //宣告IRrecv物件來接收紅外線訊號
decode_results results;

Servo servo_0, servo_1, servo_2, servo_3;

int i0 = 110;
int i1 = 150;
int i2 = 110;
int i3 = 130;
int i0max = 180;
int i0min = 0;
int i1max = 180;
int i1min = 90;
int i2max = 150;
int i2min = 50;
int i3max = 160;
int i3min = 100;

void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn();
  delay(100); //等候信号穩定

  servo_0.attach(11); //buttom
  servo_0.write(i0);

  servo_1.attach(10); //right(面对爪子)
  servo_1.write(i1);

  servo_2.attach(9); //left
  servo_2.write(i2);

  servo_3.attach(5); //claw
  servo_3.write(i3);

  delay(100); //等候信號穩定
} //end of setup()

void loop() {
  if (irrecv.decode(&results)) {
    if (results.value != -1) {
      showIRProtocol(&results);   //顯示紅外線協定的種類
      Serial.println(results.value, HEX);

      switch (results.value) {
        case 0x38863BF8: //前進
          Serial.println("0x2F0:forward");

          if (i2 < i2max) i2++;
          break;
        case 0x38863BC4: //左轉
          Serial.println("0x2D0:turn left");

          if (i0 > i0min) i0++;
          break;
        case 0x38863BE4: //右轉
          Serial.println("0xCD0:turn right");

          if (i0 > i0min) i0--;
          break;
        case 0x38863BC8: //後退
          Serial.println("0xAF0:back");

          if (i2 >= i2min) i2--;
          break;
        case 0x38863BE8: //上移
          Serial.println("0x90:up");

          if (i1 < i1max) i1++;
          break;
        case 0x38863BD8: //下移
          Serial.println("0x890:down");

          if (i1 > i1min) i1--;
          break;
        case 0x38863BE0: //夾緊
          Serial.println("0x490:tight");

          if (i3 < i3max) i3++;
          break;
        case 0x38863BD0: //放鬆
          Serial.println("0xC90:loose");

          if (i3 > i3min) i3--;
          break;
      }//end of switch
    }//end of if

    irrecv.resume(); //Receive the next value

    servo_0.write(i0); //設定四軸初始的転角
    servo_1.write(i1);
    servo_2.write(i2);
    servo_3.write(i3);

    Serial.print("i0 pos ="); //顯示四軸的転角pos
    Serial.println(i0);
    Serial.print("i1 pos =");
    Serial.println(i1);
    Serial.print("i2 pos =");
    Serial.println(i2);
    Serial.print("i3 pos =");
    Serial.println(i3);
    delay(10); //等候信号穩定

    Serial.flush();
  } //end of if
} //end of loop()

void showIRProtocol(decode_results *results) {
  Serial.print("Protocol: ");

  //判斷紅外線協定種類
  switch (results->decode_type) {
    case NEC:
      Serial.print("NEC");
      break;
    case SONY:
      Serial.print("SONY");
      break;
    case RC5:
      Serial.print("RC5");
      break;
    case RC6:
      Serial.print("RC6");
      break;
    default:
      Serial.print("Unknown encoding");
  } //end of switch()

  //把紅外線編碼印到 Serial port
  Serial.print(", irCode: ");
  Serial.print(results->value, HEX);    // 紅外線編碼
  Serial.print(",  bits: ");
  Serial.println(results->bits);        // 紅外線編碼位元數
} //end of showIRProtocol()

程式的執行畫面為:



















Lab6_3_3:不知是何姓名版
基於Lab6_3_2需要相互「速配」才能蹦出火花(在Arduino IDE的串列顯示器上僅會顯示Unknown encoding),我們找了Epson投影机搖控器來測試,程式修改處如粉紅色标示處,我們拿到了遙控器识別的副程式showIRProtocol(),並逐一將按鍵的內碼填到方向控制的switch选項內(例如:0xC1AA09F6表示前進)。


程式碼:
#include <Servo.h>
#include <SoftwareSerial.h>
#include <IRremote.h>

const int irReceiverPin = 2; //紅外線接收器輸入訊號接在Arduino第2腳
IRrecv irrecv(irReceiverPin); //宣告IRrecv物件來接收紅外線訊號
decode_results results;

Servo servo_0, servo_1, servo_2, servo_3;

int i0 = 110;
int i1 = 150;
int i2 = 110;
int i3 = 130;
int i0max = 180;
int i0min = 0;
int i1max = 180;
int i1min = 90;
int i2max = 150;
int i2min = 50;
int i3max = 160;
int i3min = 100;

void setup() {
  Serial.begin(9600);
  irrecv.enableIRIn();
  delay(100);

  servo_0.attach(11); //buttom
  servo_0.write(i0);

  servo_1.attach(10); //right(面对爪子)
  servo_1.write(i1);

  servo_2.attach(9); //left
  servo_2.write(i2);

  servo_3.attach(5); //claw
  servo_3.write(i3);

  delay(100);
} //end of setup()

void loop() {
  if (irrecv.decode(&results)) {
    Serial.print("Key code=");
    Serial.println(results.value, HEX);
   
    switch (results.value) {
      case 0xC1AA09F6: //前進
        Serial.println("0xC1AA09F6:forward");

        if (i2 < i2max) i2++;
        break;
      case 0x38863BC4: //左轉
        Serial.println("0x2D0:turn left");

        if (i0 > i0min) i0++;
        break;
      case 0x38863BE4: //右轉
        Serial.println("0xCD0:turn right");

        if (i0 > i0min) i0--;
        break;
       case 0x38863BC8: //後退
        Serial.println("0xAF0:back");

        if (i2 >= i2min) i2--;
        break;
      case 0x38863BE8: //上移
        Serial.println("0x90:up");

        if (i1 < i1max) i1++;
        break;
       case 0x38863BD8: //下移
        Serial.println("0x890:down");

        if (i1 > i1min) i1--;
        break;
      case 0x38863BE0: //夾緊
        Serial.println("0x490:tight");

        if (i3 < i3max) i3++;
        break;
      case 0x38863BD0: //放鬆
        Serial.println("0xC90:loose");

        if (i3 > i3min) i3--;
        break;
    }//end of switch

    irrecv.resume(); //Receive the next value

    servo_0.write(i0);
    servo_1.write(i1);
    servo_2.write(i2);
    servo_3.write(i3);

    Serial.print("i0 pos =");
    Serial.println(i0);
    Serial.print("i1 pos =");
    Serial.println(i1);
    Serial.print("i2 pos =");
    Serial.println(i2);
    Serial.print("i3 pos =");
    Serial.println(i3);
    delay(10);

    Serial.flush();
  } //end of if
} //end of loop()


執行結果:
Key code=C1AA09F6
0xC1AA09F6:forward
i0 pos =110
i1 pos =150
i2 pos =110
3 pos =130
Key code=C1AA31CE
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130
Key code=C1AA29D6
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130
Key code=C1AA0EF1
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130
Key code=C1AA6E91
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130
Key code=C1AA2ED1
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130
Key code=C1AAFC03
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130
Key code=C1AAC23D
i0 pos =110
i1 pos =150
i2 pos =110
i3 pos =130

参考資料:
  1. Arduino練習:紅外線傳送與接收,葉難,http://yehnan.blogspot.tw/2013/05/arduino.html。
  2. A Multi-Protocol Infrared Remote Library for the Arduino,Ken Shirriff,http://www.righto.com/2009/08/multi-protocol-infrared-remote-library.html.
  3. IRrecv Class,CY's Tech Talk,http://tech.cyborg5.com/irlib/docs/1-irlib-reference/1-1-receiver-classes/1-1-3-irrecv-class/.
  4. 六種授權條款,台灣創用CC計畫,http://creativecommons.tw/explore。

2015年12月1日 星期二

[Cloud Services:AWS] Internet Of Things (IoT)搭上Amazon Web Services(AWS)@AWS re:Invent 2015 Recap | 台灣

在這個盛会上,Markku Lepisto首先介紹了Amazon最新穎的各种云端服務,我們可以從What is Amazon Web Services?這段影片一窺究竟。
接著,MediaTek(聯發科)KKBoxTrend Micro(趨勢科技)也各自分享了它們運用AWS來處理企業需求的各种cases(案例)。其中,Trend Micro為了解決全球8座已採用Hadoop來處理big data(大數据)的data centers(數據中心)而需汰舊更新servers(伺服器)的困扰,毅然導入AWS的成功經驗是最誏筆者為之shock的!

因為筆者的教學與研究興趣著重於IoT,所以我們选擇参加了下午場的「行動化與物聯網」的section。
Markku Lepisto使用AWS提供的服務,在Intel的Edison開發板上展示了「云(端遙)控」的magic!
此外,Olivier Klein也当場demo AWS的Lambda services
可以很立即地deploy出适合IoT的cloud infrastructure
最後,Qblinks也展示了他們利用AWS建構IFTTT(If This Then That)並實作出Qmote產品的經彩範例!

P.S.今日的訍程在此