ArduinoOSCとI2C通信でNeoPixels複数台制御


#実装
<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">LEDパターンの制御 <a href="https://t.co/8uuUXgosau">pic.twitter.com/8uuUXgosau</a></p>&mdash; マコてん (@juin9_mako) <a href="https://twitter.com/juin9_mako/status/1096804277547294720?ref_src=twsrc%5Etfw">February 16, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


#準備
ハードウェア
-Arduino Uno * 5台
https://www.switch-science.com/catalog/789/
-NeoPixels 1m * 4本, 2m * 4本　
https://www.switch-science.com/catalog/1399/
-細径3心並列線（必要な分だけ）
https://www.sengoku.co.jp/mod/sgk_cart/detail.php?code=4A8P-MDLY
-ACアダプター * 4本
https://www.amazon.co.jp/gp/product/B075132TRK/ref=ppx_yo_dt_b_asin_title_o00__o00_s01?ie=UTF8&psc=1

ソフトウェア
-Max8 (8.0.0)
-Arduino (1.8.8)
-ArduinoOsc (0.2.1)
-Adafruit NeoPixel (1.1.8)

#プログラムの概要
1.ArduinoOSC
Max-MSPとArduino間でSerial通信を行い、Maxからの値をArduinoに受け渡します。ファイルの保存場所は注意が必要で、jsonやjsファイルも同じファイルに入れておく必要があります。
<img width="411" alt="スクリーンショット 2019-02-17 0.37.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/361940/fe957039-1ed2-20a2-8d9f-39d4fc746e30.png">


2.I2C通信
master_writerとslave_receiverの関係のI2C通信を用いて、Arduino1台(master)の指令をArduino4台(slave)に渡します。
![LAC_Brain_Orchestra.png](https://qiita-image-store.s3.amazonaws.com/0/361940/ab97ffb9-45f5-7f81-c7d4-eba0076bee41.png)
上記の黒、黄色、緑のワイヤーがI2C通信になります。masterにはパソコンから電源供給を行い、slave一つ一つは5V2AのACアダプターを接続します。

3.NeoPixels
![led@4x.png](https://qiita-image-store.s3.amazonaws.com/0/361940/87952c02-9456-17d4-4549-ea6153834bed.png)

1台のArduinoから1mのNeoPixelsと2mのNeoPixelsの二つに指令を与えます。5V電力とGNDはそれぞれ1mのテープと2mのテープに分岐させ接続します。

#プログラムの流れ
**Max-MSP -> Serial通信 -> Arduino -> I2C通信 -> Arduino -> NeoPixels制御**
Maxで受け取った数値をArduino(master) -> Arduino(slave)へ受け渡していくプログラムになっています。最終的には数値によってLEDの点灯パターンが変化します。それぞれのArduinoには前もってプログラムを書き込んでおき、ACアダプターから電源をもらって稼働させます。

#プログラム
###Max
MaxからSerial通信で"/light1 0"の形でArduinoにメッセージを送ります。たまに調子が悪い時があり、その時はserialオブジェクト(serial d 9600)のportのアルファベットを一度変更してから行うと良さそうです。
<img width="648" alt="スクリーンショット 2019-02-16 22.56.07.png" src="https://qiita-image-store.s3.amazonaws.com/0/361940/2a7a48bd-1dd2-953f-5e59-b0d2794f96a5.png">

###Arduino(master)
Maxからラベル付けされて来る数値を各SlaveのArduinoへ送ります。
/light1 -> slave1(I2C通信のDevice_Id1)
/light2 -> slave2(I2C通信のDevice_Id2)
/light3 -> slave3(I2C通信のDevice_Id3)
/light4 -> slave4(I2C通信のDevice_Id4)

```ruby:master.ino
#include <ArduinoOSC.h>
#include <Wire.h>

// Wire ID 
#define DEVICE_ID1 1
#define DEVICE_ID2 2
#define DEVICE_ID3 3
#define DEVICE_ID4 4

OscSerial osc;

void setup() {
  Serial.begin(9600);
  osc.attach(Serial);
  osc.subscribe("/light1", callbackLight1);
  osc.subscribe("/light2", callbackLight2);
  osc.subscribe("/light3", callbackLight3);
  osc.subscribe("/light4", callbackLight4);
  Wire.begin();
}

void loop() {
  osc.parse();
}

void callbackLight1(OscMessage& m){
  Wire.beginTransmission(DEVICE_ID1);
  Wire.write(m.getArgAsInt32(0));
  Wire.endTransmission();
}

void callbackLight2(OscMessage& m){
  Wire.beginTransmission(DEVICE_ID2);
  Wire.write(m.getArgAsInt32(0));
  Wire.endTransmission();
}

void callbackLight3(OscMessage& m){
  Wire.beginTransmission(DEVICE_ID3);
  Wire.write(m.getArgAsInt32(0));
  Wire.endTransmission();
}

void callbackLight4(OscMessage& m){
  Wire.beginTransmission(DEVICE_ID4);
  Wire.write(m.getArgAsInt32(0));
  Wire.endTransmission();
}
```

###Arduino(slave)
masterから送られた値によってLEDの光るパターンを変更するプログラムです。1mと2mのLEDテープを9つのパターンで点灯させています。1mのLEDテープは6ピンで、2mのLEDテープは7ピンに接続をします。
slave2の場合はWire.begin(1)がWire.begin(2)になります。slave3、slave4も同じ要領で考えてください。

```ruby:slave1.ino
#include <Adafruit_NeoPixel.h>
#include <Wire.h>

#ifdef __AVR__
#include <avr/power.h>
#endif

#define PIN1            6
#define PIN2            7

#define NUMPIXELS1      60
#define NUMPIXELS2      120


Adafruit_NeoPixel pixels1 = Adafruit_NeoPixel(NUMPIXELS1, PIN1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel pixels2 = Adafruit_NeoPixel(NUMPIXELS2, PIN2, NEO_GRB + NEO_KHZ800);

int pattern = 0;
int delayval = 20;

void setup() {
  Wire.begin(1);
  Wire.onReceive(receiveEvent);
  Serial.begin(9600);

#if defined (__AVR_ATtiny85__)
  if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif

  pixels1.begin();
  pixels2.begin();
  pixels1.setBrightness(100);
  pixels2.setBrightness(100);
  pixels1.show();
  pixels2.show();
}

void receiveEvent(int howMany) {
  pattern = Wire.read();
  Serial.print(pattern);
}

void loop() {
  switch (pattern) {
    case 0:
    //No Light
      noLight(pixels1.Color(0, 0, 0), pixels2.Color(0, 0, 0), delayval);
      break;
    case 1:
    //dualStraight
      straight(pixels1.Color(255, 255, 255), pixels2.Color(255, 255, 255), delayval);
      break;
    case 2:
    //Short Straight, Long No Light
      straight(pixels1.Color(255, 255, 255), pixels2.Color(0, 0, 0), delayval);
      break;
    case 3:
    //Short No Light, Long Straight
      straight(pixels1.Color(0, 0, 0), pixels2.Color(255, 255, 255), delayval);      
      break;
    case 4:
    //dualReverse
      reverse(pixels1.Color(255, 255, 255), pixels2.Color(255, 255, 255), delayval);
      break;
    case 5:
    //Short Reverse, Long No Light
      reverse(pixels1.Color(255, 255, 255), pixels2.Color(0, 0, 0), delayval);
      break;
    case 6:
    //Short No Light, Long Reverse
      reverse(pixels1.Color(0, 0, 0), pixels2.Color(255, 255, 255), delayval);
      break;
      case 7:
      //Short Straight, Long Reverse
      sSt_lRe(pixels1.Color(255, 255, 255), pixels2.Color(255, 255, 255), delayval);
      break;
      case 8:
      //Short Reverse, Long Straight
      sRe_lSt(pixels1.Color(255, 255, 255), pixels2.Color(255, 255, 255), delayval);
    default:
      break;
  }
}

void straight(const uint32_t& c1, const uint32_t& c2, uint8_t wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i + q, c1);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i + q, c2);
      }
      pixels1.show();
      pixels2.show();

      delay(wait);

      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i + q, 0);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i + q, 0);
      }
    }
  }
}

void reverse(const uint32_t& c1, const uint32_t& c2, const uint8_t& wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i - q, c1);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i - q, c2);
      }
      pixels1.show();
      pixels2.show();

      delay(wait);

      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i - q, 0);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i - q, 0);
      }
    }
  }
}

void sSt_lRe(const uint32_t& c1, const uint32_t& c2, const uint8_t& wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i + q, c1);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i - q, c2);
      }
      pixels1.show();
      pixels2.show();

      delay(wait);

      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i + q, 0);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i - q, 0);
      }
    }
  }
}

void sRe_lSt(const uint32_t& c1, const uint32_t& c2, const uint8_t& wait) {
  for (int j = 0; j < 10; j++) {
    for (int q = 0; q < 3; q++) {
      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i - q, c1);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i + q, c2);
      }
      pixels1.show();
      pixels2.show();

      delay(wait);

      for (uint16_t i = 0; i < pixels1.numPixels(); i = i + 3) {
        pixels1.setPixelColor(i - q, 0);
      }
      for (uint16_t i = 0; i < pixels2.numPixels(); i = i + 3) {
        pixels2.setPixelColor(i + q, 0);
      }
    }
  }
}

void noLight(const uint32_t& c1, const uint32_t& c2, const uint8_t& wait) {
  for (uint16_t i = 0; i < pixels1.numPixels(); i = ++i) {
    pixels1.setPixelColor(i, 0);
  }
  for (uint16_t i = 0; i < pixels2.numPixels(); i = ++i) {
    pixels2.setPixelColor(i, 0);
  }
  pixels1.show();
  pixels2.show();

  delay(wait);
}
```

#参考文献
ArduinoOSCの説明についてはこちらを参考にしました。(本家)
-https://qiita.com/hideakitai/items/f50f99e38b520a0ace37
Qiitaの記事からのアップデートもありましたので、下記のGithubもチェックしてください。
-https://github.com/hideakitai/ArduinoOSC

Node.jsのインストール方法等(brewインストール済みの場合)
-https://qiita.com/jaxx2104/items/2277cec77850f2d83c7a

I2Cの説明についてはこちらの方を参考にしました。
- https://qiita.com/MergeCells/items/20c3c1a0adfb222a19cd
- https://qiita.com/hikoalpha/items/7bf563ee286a59bfd2f4

NeoPixelsの購入方法や説明についてはこちらに記載されています。
-https://www.switch-science.com/catalog/1399/

#おわりに
質問やアドバイスがありましたら、ぜひコメントをください。