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)



實作, 電容感應音樂樹

沒有留言:

張貼留言