Arduino
IoT
milkcocoa
ESP8266
WioNode

デカ顔箱をIoT化してみた。

デイリーポータルZのデカ顔箱(The Big Face Box)がキット化されてSwitch Scienceから発売されたが、そのままではつまらないので付属のLEDテープをIoT化してみた。

スクリーンショット 2017-11-02 22.42.04.png

購入(ポチった)

中身はフレネルレンズ※(320mm x 400mm)とLEDテープ(400mm x 2本)と単三電池ケース(1個)である。

※ 通常のレンズを同心円状の領域に分割し厚みを減らしたレンズであり、照明や簡易な拡大鏡などの光学機器に使用される。

IMG_6325.JPG

image-1497843248932.32.27.png

外側のダンボールは別途用意する必要がある。
スクリーンショット 2017-11-03 16.45.19.png

追加購入(フルカラーシリアルLED(WS2812)テープ 1m)

キットに付属の5V駆動白色LEDテープをフルカラーシリアルLED(WS2812)テープへ換装する。
IMG_9206.JPG

フルカラーシリアルLED(1m)にはWS2812が60個搭載されている。
400mm(20個) x 2本に切り分ける。
IMG_1099.JPG

IMG_4385.JPG

接続構成図

・Wio NodeのPORT0のGND、PORT0A(IO1)(Tx)、PORT0B(IO3)(Rx)をUSBシリアル変換ボード(FTDI FT232RL)へ接続し、USBシリアル変換ボードとPCをUSB接続する。
※TxとRxを接続することに注意。
・Wio NodeのPORT1の3.3V、GND、PORT1A(IO4)をフルカラーシリアルLED(WS2812)へ接続する。
・JST PH2.0コネクタにはLi-Poバッテリーを接続する。

スクリーンショット 2017-11-04 15.47.46.png

フルカラーシリアルLEDの終端はGroveコネクタにすると着脱し易く便利。
IMG_0721.JPG

PC、スマホ等のブラウザからインターネット(MilkCocoa(MQTT Broker))経由でフルカラーシリアルLEDを操作する構成を試す。JSON形式でR/G/B/Sの要素値をPublish/Subscribeすることで色をコントロールする。
R: Red(赤色要素) 0 - 255
G: Green(緑色要素) 0 - 255
B: Blue(青色要素) 0 - 255
S: Strength, Brightness(光の強さ) 0 - 255
スクリーンショット 2017-11-04 15.24.06.png

Wio NodeをArduino化

以下のサイトを参考にしてWio NodeをArduino化する。
https://blogs.yahoo.co.jp/carcon999/39480359.html
http://dev-moyashi.hatenablog.com/entry/2016/09/08/185933

ソースコードはこちら。(wionode.ino)
https://github.com/kitazaki/led

wionode.ino
#include <ESP8266WiFi.h>
#include <Adafruit_NeoPixel.h>
#include <Milkcocoa.h>

#define WLAN_SSID       ""  // Wi-Fi
#define WLAN_PASS       ""  // Wi-Fi

#define MILKCOCOA_APP_ID      ""  // MilkCocoa Application ID
#define MILKCOCOA_DATASTORE   ""  // MilkCocoa Datastore Name
#define MILKCOCOA_SERVERPORT  1883

#define PIN 5   // Grove Port 1 IO4 or IO5
#define NUMPIXELS 40   // Number of LEDs
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

WiFiClient client;

const char MQTT_SERVER[] PROGMEM    = MILKCOCOA_APP_ID ".mlkcca.com";
const char MQTT_CLIENTID[] PROGMEM  = __TIME__ MILKCOCOA_APP_ID;

Milkcocoa milkcocoa = Milkcocoa(&client, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);

int red, green, blue, strength;

void onpush(DataElement *elem) {
  Serial.println("onpush");
  Serial.print("r:");
  Serial.print(elem->getInt("r"));
  Serial.print(",g:");
  Serial.print(elem->getInt("g"));
  Serial.print(",b:");
  Serial.print(elem->getInt("b"));
  Serial.print(",s:");
//  Serial.println(elem->getInt("s"));
//  strength = elem->getInt("s");
  Serial.print("50");
  strength = 50;
  Serial.println();

//  led(elem->getInt("r"), elem->getInt("g"), elem->getInt("b"), elem->getInt("s"));
// set RGBs to global parameters
  red = elem->getInt("r");
  green = elem->getInt("g");
  blue = elem->getInt("b");

  int type;
  type = (red + green + blue) % 6;

  //  types of coloring
  if (type == 0 ) {
    //  type 1
    led_wipe_a(red, green, blue, strength);
  } else if (type == 1) {
    //  type 2
    led_wipe_b(red, green, blue, strength);
  } else if (type == 2) {
    //  type 3
    rainbow(strength, 20);
  } else if (type == 3) {
    //  type 4
    rainbowCycle(strength, 20);
  } else if (type == 4) {
    //  type 5
    theaterChaseRainbow(strength, 50);
  } else if (type == 5) {  
    //  type 6
    theaterChase(strip.Color(red, green, blue), strength, 50);
  } else {
    led();
  }
};

void led() {
  strip.setBrightness(10);
  for(int i=0;i<NUMPIXELS;i++){
    strip.setPixelColor(i, strip.Color(255,255,255));
    strip.show();
    delay(50);
  }
}

void led_wipe_a(int v_r, int v_g, int v_b, int v_s) {
  strip.setBrightness(v_s);
  for(int i=0;i<NUMPIXELS;i++){
    strip.setPixelColor(i, strip.Color(v_r,v_g,v_b));
    strip.show();
    delay(50);
  }
  for(int i=0;i<NUMPIXELS;i++){
//    strip.setPixelColor(i, strip.Color(0,0,0));
    strip.setPixelColor(NUMPIXELS - i - 1, strip.Color(0,0,0));
    strip.show();
    delay(50);
  }
}

void led_wipe_b(int v_r, int v_g, int v_b, int v_s) {
  strip.setBrightness(v_s);
  for(int i=0;i<NUMPIXELS;i++){
    strip.setPixelColor(i, strip.Color(v_r,v_g,v_b));
    strip.show();
    delay(50);
  }
  for(int i=0;i<NUMPIXELS;i++){
    strip.setPixelColor(i, strip.Color(0,0,0));
//    strip.setPixelColor(NUMPIXELS - i - 1, strip.Color(0,0,0));
    strip.show();
    delay(50);
  }
}

void rainbow(int v_s, uint8_t wait) {
  uint16_t i, j;
  strip.setBrightness(v_s);
  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

void rainbowCycle(int v_s, uint8_t wait) {
  uint16_t i, j;
  strip.setBrightness(v_s);
  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

void theaterChase(uint32_t c, int v_s, uint8_t wait) {
  strip.setBrightness(v_s);
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();
      delay(wait);
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

void theaterChaseRainbow(int v_s, uint8_t wait) {
  strip.setBrightness(v_s);
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();
      delay(wait);
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

void setupWiFi() {
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);
  WiFi.begin(WLAN_SSID, WLAN_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}


// main
void setup() {
  pinMode(15, OUTPUT);
  digitalWrite(15, 1);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  Serial.begin(115200);
  delay(10);
  Serial.println(F("Milkcocoa SDK demo"));
  setupWiFi();
  Serial.println( milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush) );
}

void loop() {
  milkcocoa.loop();
  led();
//  delay(100);
}

ウェブサイトを用意

カラーボタンを押下してフルカラーLEDの色を変える。
・index.html
・main.js
スクリーンショット 2017-11-03 18.23.27.png

赤色/緑色/青色/光の強さの値を指定してフルカラーLEDの色を変える。
・debug.html
・main_debug.js
スクリーンショット 2017-11-03 18.23.14.png

ソースコードはこちら。
https://github.com/kitazaki/led/tree/master/html

index.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>simple LED</title>
  <script src="http://cdn.mlkcca.com/v0.6.0/milkcocoa.js"></script>
  <script src="main.js"></script>
</head>
<body>
 <button name="white" style="color:#000000;background-color:#ffffff;font-size:15;width:100px;height:30px" onClick="clickEvent_white()">white</button></br>
 <button name="black" style="color:#ffffff;background-color:#000000;font-size:15;width:100px;height:30px" onClick="clickEvent_black()">black</button></br>
 <button name="blue" style="color:#ffffff;background-color:#0000ff;font-size:15;width:100px;height:30px" onClick="clickEvent_blue()">blue</button></br>
 <button name="yellow" style="color:#000000;background-color:#ffff00;font-size:15;width:100px;height:30px" onClick="clickEvent_yellow()">yellow</button></br>
 <button name="red" style="color:#ffffff;background-color:#ff0000;font-size:15;width:100px;height:30px" onClick="clickEvent_red()">red</button></br>
<ul id="board"></ul>
</body>
</html>
main.js
var milkcocoa = new MilkCocoa('***.mlkcca.com');  // replace *** as MilkCocoa Application ID
var chatDataStore = milkcocoa.dataStore("");  // MilkCocoa Datastore Name
var board;
window.onload = function(){
  board = document.getElementById("board");
}

function clickEvent_white(){
  var v_r = 255; var v_g = 255; var v_b = 255;
  sendText(v_r, v_g, v_b);
}

function clickEvent_black(){
  var v_r = 0; var v_g = 0; var v_b = 0;
  sendText(v_r, v_g, v_b);
}

function clickEvent_blue(){
  var v_r = 0; var v_g = 0; var v_b = 255;
  sendText(v_r, v_g, v_b);
}

function clickEvent_yellow(){
  var v_r = 255; var v_g = 255; var v_b = 0;
  sendText(v_r, v_g, v_b);
}

function clickEvent_red(){
  var v_r = 255; var v_g = 0; var v_b = 0;
  sendText(v_r, v_g, v_b);
}

function sendText(v_r, v_g, v_b){
  chatDataStore.push({r : v_r, g : v_g, b : v_b},function(data){
    console.log("送信完了!");
  });
}

chatDataStore.on("push",function(data){
  addText(data.value.r, data.value.g, data.value.b);
});

function addText(v_r, v_g, v_b){
  var msgDom = document.createElement("li");
  msgDom.innerHTML = v_r + "," + v_g + "," + v_b;
  board.insertBefore(msgDom, board.firstChild);
}
debug.html
<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>simple LED</title>
  <script src="http://cdn.mlkcca.com/v0.6.0/milkcocoa.js"></script>
  <script src="main_debug.js"></script>
</head>
<body>
R:<textarea name="r" id="r" cols="10" rows="1"></textarea></br>
G:<textarea name="g" id="g" cols="10" rows="1"></textarea></br>
B:<textarea name="b" id="b" cols="10" rows="1"></textarea></br>
S:<textarea name="s" id="s" cols="10" rows="1"></textarea></br>
<button name="button" onClick="clickEvent()">send</button>
<ul id="board"></ul>
</body>
</html>
main_debug.js
var milkcocoa = new MilkCocoa('***.mlkcca.com');  // replace *** as MilkCocoa Application ID
var chatDataStore = milkcocoa.dataStore("");  // MilkCocoa Datastore Name
var v_r, v_g, v_b, v_s, board;

window.onload = function(){
  v_r = document.getElementById("r");
  v_g = document.getElementById("g");
  v_b = document.getElementById("b");
  v_s = document.getElementById("s");
  board = document.getElementById("board");
}

function clickEvent(){
  var red = parseInt(v_r.value);
  var green = parseInt(v_g.value);
  var blue = parseInt(v_b.value);
  var strength = parseInt(v_s.value);
  sendText(red, green, blue, strength);
}

function sendText(v_r, v_g, v_b, v_s){
  chatDataStore.push({r : v_r, g : v_g, b : v_b, s : v_s},function(data){
    console.log("送信完了!");
  });
}

chatDataStore.on("push",function(data){
  addText(data.value.r, data.value.g, data.value.b, data.value.s);
});

function addText(v_r, v_g, v_b, v_s){
  var msgDom = document.createElement("li");
  msgDom.innerHTML = v_r + "," + v_g + "," + v_b + "," + v_s;
  board.insertBefore(msgDom, board.firstChild);
}

クラブ、ストリートでデビューする

SIX TOKYOの中
IMG_9814.JPG

六本木交差点前
IMG_8254.JPG

ドン・キホーテ前
IMG_5339.JPG

まとめ

タダでさえ目立つデカ顔箱がフルカラーLEDでさらに目立って良さみになる。
みなさまのパリピーライフの一助になれば幸いです。