2014/06/06

108 大眼仔, 進化, (2) 當我們串在一起 (Arduino, SG90, Animatronic Eye, I2C)

arduino * 3 + 大眼仔 * 4, 當我們串在一起
先前大家聊天的時候, 有說過可用 I2C bus 來串連數片 Arduino 板子, 當時也試做了個版本出來, 可惜趕不及在 Maker Faire 呈現.

萊恩大兵趁空重新走訪這個主題, 本來以為小菜一碟的事, 竟也可以陷坑踢鐵板, 弄了好幾天才總算兜出 (Arduino x 3) + (大眼仔 x 4) 的示範來.

直接說結果. 整個的關鍵點就在於供電.
  • I2C bus 吃 arduino 板子上的 3.3V 電源, 自成一個迴路.
  • Arduino 板子與大眼仔, 則由外部的電源供應器供給 5V 的電源.

問題來了. 
  • 線路怎麼接才能供給 (Arduino x 3) + (大眼仔 x 4) 穩定的 5V 電源呢?

答案就是:
  • 改造馬爸做的神奇電源線. 多銲兩組 5V 與 Ground 的電源線.
    這條線真好用
有電源線沒電源供應器怎麼行? 萊恩大兵從家裡挖出一台電源供應器. 剥開包線, 確認是 5V 輸出沒錯, 銲一條 JST 接頭, 從此家裡就有穩定的 5V 電流供應了. yeah!
銲一條 JST 接頭上去


電源供應器是 maker 的家居必備品


Okay, 準備就緒. 把線接好, 打開電源, 從 master device 的 serial port 餵命令進去. 大眼仔果然就動起來了, 成功.
透過 serial port 下指令

以下是大眼仔 I2C 版本的程式碼與線路圖.


(for master device)
 
//
// OT108Eyeballs_i2c_m.ino
// - Use I2C bus to control multiple devices
// - Master device
//

#include <Wire.h>
#include <Servo.h>
#include <AniEyeball.h>

#define N_SLAVE   2
#define N_EYEBALL 2

AniEyeball ot108eyeball[N_EYEBALL];

// %u773C%u76AE_PIN # i
// %u773C%u7403_PIN # i 1
const byte servoPin[N_EYEBALL*2] = {2,3,4,5};

// for command string handling (from serial port)
char inData[80];
byte index = 0;
byte count = 0;

#define NO_PREFIX       0
#define S_PREFIX        1
#define P_PREFIX        1
#define E_PREFIX        1

#define TOKEN_S         "s"
#define TOKEN_P         "p"
#define TOKEN_E         "e"

byte pfxS = NO_PREFIX;
byte pfxP = NO_PREFIX; 
byte pfxE = NO_PREFIX; 
byte sAll = 0; // all slaves mode
byte sNum = 0; // slave-#
byte pNum = 0; // pattern-#
byte eNum = 0; // eyeball-#
byte fFinish = 0;

void setup()
{
  Wire.begin();
  
  Serial.begin(9600);

  for (int i=0; i<N_EYEBALL; i  )
  {
   ot108eyeball[i].setPinP(servoPin[i*2]);
   ot108eyeball[i].setPinB(servoPin[i*2 1]);
  }
}

void loop()
{
  while (Serial.available() > 0)
  {
    char aChar = Serial.read();
    if (aChar == '\n')
    {
      // End of string detected. Time to parse
      char *p = inData; //assign the string to *p
      int counter = 0;  //initialise the counter
      String str;
            
      str = strtok(p, ",");
      while (str != NULL)
      {
        Serial.println(str);
        ParseCommand(str);
        counter  ;
        str = strtok(NULL, ",");
      }

      index = 0;
      inData[index] = NULL;      
      Serial.write("done\n");
    }
    else
    {
      inData[index] = aChar;
      index  ;
      inData[index] = '\0'; // Keep the string NULL terminated
    }
  }

  if (fFinish)
  {
    // 全部命令解析完成, 來操控 slave 吧
    if (sAll)
    {
      for (int i=0; i<N_SLAVE; i  )
      {   
        //
        // 暫且忽略 e 命令吧, 以後有空再補上
        //
        Wire.beginTransmission(i 1);
        Wire.write(pNum);
        Wire.endTransmission();        
      }
    }
    else
    {
      // 特定 salves
      Wire.beginTransmission(sNum);      
      Wire.write(pNum);   
      Wire.endTransmission();
    }

    // 不光是 slave 要做事, master 自己也要做啊.
    for (int i=0; i<N_EYEBALL; i  )
    {
      ot108eyeball[i].acting(pNum);
    } 
  
    resetFlags();    
  }

  // if no inputs from remote, how about letting master device's eyeballs moving 
  // tweeting();
}

void ParseCommand(String str)
{
  // the input commands formats:
  //   * s
  //      - all: all slaves
  //      - (num): slave-#(num)   
  //   * e
  //      - (num): eyeball-#(num)
  //   * p
  //      - (num): pattern-#(num)  
  //
  // the command example:
  //   s,all,p,1    -> all slaves, run pattern-1
  //   s,5,e,1,p,3  -> slave-5, eyeball-1, run pattern-3
  //   s,5,p,3      -> slave-5, all eyeballs, run pattern-3   

   if (str == TOKEN_S && pfxS == NO_PREFIX) // 開始 s 命令
   {
      pfxS = S_PREFIX; 
   }
   else if (str == "all" && pfxS == S_PREFIX) // s 命令, all
   {                               
      sAll = 1;
      pfxS = NO_PREFIX; // s 命令結束      
   }
   else if (pfxP == NO_PREFIX && pfxE == NO_PREFIX && pfxS == S_PREFIX) // 仍在解析 s 命令, 尚未走到 p 或 e 命令
   {
      sNum = str.toInt();
      pfxS = NO_PREFIX; // s 命令結束
   }
   else if (str == TOKEN_E && pfxE == NO_PREFIX && pfxP == NO_PREFIX && pfxS == NO_PREFIX) // 開始 e 命令
   {
      pfxE = E_PREFIX;
   }
   else if (str == TOKEN_P && pfxP == NO_PREFIX && pfxE == NO_PREFIX && pfxS == NO_PREFIX) // 開始 p 命令
   {
      pfxP = P_PREFIX;
   }
   else if (pfxE == E_PREFIX && pfxP == NO_PREFIX && pfxS == NO_PREFIX) // 解析 e 命令的參數
   {
     eNum = str.toInt();
     pfxE = NO_PREFIX; // e 命令結束
   }
   else if (pfxP == P_PREFIX && pfxE == NO_PREFIX && pfxS == NO_PREFIX) // 解析 p 命令的參數
   {
      pNum = str.toInt();
      pfxP = NO_PREFIX; // p 命令結束
      fFinish = 1;
   }
}

void resetFlags()
{
  pfxS = NO_PREFIX;
  pfxP = NO_PREFIX; 
  pfxE = NO_PREFIX; 
  sAll = 0;
  sNum = 0;
  pNum = 0;
  eNum = 0;
  fFinish = 0;
}

(for slave device)
 
//
// OT108Eyeballs_i2c_s.ino
// - Use I2C bus to control multiple devices
// - Slave device
//

#include <Wire.h>
#include <Servo.h>
#include <AniEyeball.h>


#define N_EYEBALL 1

AniEyeball ot108eyeball[N_EYEBALL];

// 眼皮_PIN # i
// 眼皮_PIN # i+1
const byte servoPin[N_EYEBALL*2] = {2,3};

const int SLAVE_ADDRESS = 2;
char inByte = 0;

void setup()
{
  Wire.begin(SLAVE_ADDRESS);    // join I2C bus as a slave with address 1
  Wire.onReceive(receiveEvent); // register event
  
  Serial.begin(9600);

  for (int i=0; i<N_EYEBALL; i  )
  {
   ot108eyeball[i].setPinP(servoPin[i*2]);
   ot108eyeball[i].setPinB(servoPin[i*2 1]);
  }
}

void loop()
{
  // 可能要有緩衝, 以免灌進太多指令, 來不及處理
  if (inByte)
  {
    for (int i=0; i<N_EYEBALL; i  )
    {
      ot108eyeball[i].acting(inByte);
    }    
    inByte = 0;    
  }
  else
  {
    //ot108eyeball[i].tweeting();
  }

}

void receiveEvent(int howMany)
{
  // master 傳來的命令格式為一數值, 代表 pattern-#.
  while (Wire.available()) 
  {
    inByte = Wire.read();
  }
}


話說, 這個程式碼有幾個可以改進的地方. 萊恩大兵留著以後慢慢改吧.

  • command parser, 可改用 shifting bits 的方法來做.
  • 沒處理 e command.

再下來, 萊恩大兵要想辦法讓大眼仔做人臉追蹤了.


[參考資料]



[萊恩大兵的其它文章]

自製大四軸

* 自製大四軸, 實作分享@華山文創文區
* 自製大四軸, (1) 零組件篇, 遙控器 (Drone, Quadcopter, Futaba, Maker, Arduino, Animatronic Eye)
* 自製大四軸, (2) 零組件篇, 飛控板 (Drone, Quadcopter, MultiWii, Arduino, Futaba, Maker)
* 自製大四軸, (3) 零組件篇, 自行雷切木質機架 (Drone, Quadcopter, Maker, Laser Cut)
* 自製大四軸, (4) 零組件篇, 馬達與電變調整 (Drone, Quadcopter, Maker, Electric Speed Control, Motor)
* 自製大四軸, (5) 組裝篇, 四軸飛行器成形 (Drone, Quadcopter, MultiWii, Arduino, Maker, Electric Speed Control, Motor)
* 自製大四軸, (6) 調整篇, 飛行前兩三事 (Drone, Quadcopter, Maker, Futaba, Arduino, MultiWii)
* 自製大四軸, (7) 充電篇, iMax B6 充電器操作記要 (Charger, Battery)
* 自製大四軸, (8) 問題篇, 機架損壞維修 (Drone, Quadcopter, Laser Cut)
* 自製大四軸, (9) 改良篇, 方便拆卸的木質機架 (Drone, Quadcopter, Maker, Laser Cut)

自動報球速的棒球



CC2540 Bluetooth Low Energy
* 筆記, CC2540 Bluetooth Low Energy, (1) 開發環境 架設 (Bluetooth, CC2540)
* 筆記, CC2540 Bluetooth Low Energy, (2) 跑第一個範例程式 (Bluetooth, CC2540)
* 筆記, CC2540 Bluetooth Low Energy, (3) SimpleBLEPeripheral 簡單介紹 (Bluetooth, CC2540)
* 筆記, CC2540 Bluetooth Low Energy, (4) 在智慧手機上執行範例程式 (Bluetooth, CC2540)
* 筆記, CC2540 Bluetooth Low Energy, (5) 偵測與發送 iBeacon 訊號 (Bluetooth, CC2540, iBeacon)
* 實作, iBeacon 發訊器 x 防丟器 (Bluetooth, CC2540, iBeacon)
* 實作, iBeacon 尋寶遊戲 (Bluetooth, CC2540, iBeacon, iOS app)
* 實作, BLE + iOS app, 遙控燈泡君 (Bluetooth, CC2540, iOS app)
* 做實驗, 用 iBeacon 做自動控制的可行性 (Bluetooth, iBeacon, CC2540, Automation, URL Scheme, iOS app)

藍色小鋪一起來做

* 藍色小鋪一起來做, (1) 用 beacon 控制開關的枱燈
* 藍色小鋪一起來做, (2) 講解 BLE CC2540 UART 通訊範例程式 (Bluetooth, CC2540, UART)
* 藍色小鋪一起來做, (3) 藍牙枱燈專案實作 (上) (Bluetooth, CC2540)
* 藍色小鋪一起來做, (4) 藍牙枱燈專案實作 (下) (Bluetooth, CC2540)
* 藍色小鋪一起來做, (5) iBeacon scanner 專案示範與解說 (Bluetooth, CC2540, iBeacon)
* 藍色小鋪一起來做, (6) 完成, 用 iBeacon 控制開關的枱燈 (Bluetooth, CC2540, iBeacon)

小惡魔 無線溫度感測器

108 大眼仔
Plot Clock
* 體驗, 原住民互動故事書@宜蘭大同鄉泰雅生活館
* 體驗, 蛋生音互動裝置@兒童美術館 (Arduino, 3D Printing, HC-SR04, Interactive)



* 實作, 電容感應音樂樹

沒有留言:

張貼留言