IoTで飲み会の楽しさ、カンパイの瞬間を共有する「カンパイシェア」の足跡(その2)

野良ハックチームが　<A HREF="http://gadget.renesas.com/ja/contest/2017/">GRデザインコンテスト2017</A>　へエントリーした作品「カンパイシェア」の足跡についての続き (その2) です。
<A HREF="https://qiita.com/kitazaki/items/14e5d8d24b181c16e106">前回</A> は <A HREF="https://tokyo.hackjunction.com/2017event/">JUNCTION TOKYO 2017</A> で生まれた「カンパイシェア」の原点について触れ、今回は作品のエントリーに至るまでの変遷について記事に残しておこうと思う。

# 第一回社内ハッカソン
2016年10月6〜8日に第一回社内ハッカソンが行われ、通信トラフィックのビックデータを用いて新しいサービスを作ることがお題だった。

[![スクリーンショット 2018-01-01 9.35.19.png](https://qiita-image-store.s3.amazonaws.com/0/72479/e07f6b6c-0261-b887-3363-4d5c65a4d566.png)](https://www.slideshare.net/kitazaki/trips-sb-innoventure-x-hackathon)

社内で初めて開催ということもあり、手探り状態で参加者は少なく社員・新入社員・インターン大学生混合の5チーム (3名 x 5チーム = 15名) でハックタイムに突入することになった。この時はプレーヤーとして参加し 3日間 (一泊三日？！) であったが、内容の濃い非常に充実した経験をすることができた。

## 課題と解決(案)
お父さんの休日は非常に忙しい。家族サービスとなれば事前の下調べから当日お出かけして自宅へ帰るまで一大イベントである。平日は仕事が忙しく、家族の要望がマチマチであれば下調べも大変である。さらに家族の"ウケ"が悪いとお父さんの評価も下がり、踏んだり蹴ったりである。

![スクリーンショット 2018-01-01 9.56.36.png](https://qiita-image-store.s3.amazonaws.com/0/72479/585439c4-e5de-bd6a-7469-8fd2808067ad.png)

そこで思いついたのが通信トラフィックから他の人の行動パターンを解析して旅行プランを提案するサービスである。出発地と目的地を入力するだけでおススメのルート情報が表示される。ルート情報を地図に表示するだけのシンプルなサービスだが、時刻、場所、天気予報、ランドマークが表示され、将来的にはユーザプロファイルに応じたリコメンドや施設予約・広告/クーポン表示などの機能追加が考えられる。

![スクリーンショット 2018-01-01 10.07.59.png](https://qiita-image-store.s3.amazonaws.com/0/72479/05862b29-93cb-ebf8-1cec-8b22b6182d7c.png)

![スクリーンショット 2018-01-01 10.56.01.png](https://qiita-image-store.s3.amazonaws.com/0/72479/e90d5f01-8dc4-9717-582c-14bc4739abc7.png)

予め通信トラフィックから匿名ユーザの時刻と位置情報を抽出してルート情報へ変換しておき、入力された出発地・目的地の緯度・経度を含む複数のルート情報の中から一番近いルート情報を選択する。あとは出発地・目的地・ルート情報の緯度・経度データを時刻と天気予報とランドマークの情報と合わせて地図データへマッピングして表示する仕組みである。

![スクリーンショット 2018-01-01 10.33.54.png](https://qiita-image-store.s3.amazonaws.com/0/72479/83f346f2-a75b-065c-49ee-3b5c056beb35.png)

日本中、世界中、遠く離れていても家族・同僚・ボッチを問わずカンパイの感動を共有する「カンパイヒートマップ」の原形がこの時にできた。

# 第二回社内IoTハッカソン
2017年7月14〜16日に第二回社内IoTハッカソンが行われ、この時は運営メンバーとして参加することになった。

![スクリーンショット 2018-01-01 21.05.25.png](https://qiita-image-store.s3.amazonaws.com/0/72479/8b4d4e44-36aa-bb96-d3f8-c81e948363be.png)

「日常を少し便利にするIoT」というテーマで 6 チーム (27 名) がアイデアソンからスタートした。運営側として完全にサポート役に回るつもりだったが、開発者の不足で運営メンバーの 1人 (M 君) がプレーヤーに駆り出されたことをきっかけにして、他の運営メンバーも勝手に (= 競技とは関係なく ) ハックを始めることになった。
この時に「野良ハックチーム」の名前が生まれた。

[![スクリーンショット 2018-01-01 8.44.58.png](https://qiita-image-store.s3.amazonaws.com/0/72479/c8ebadc4-7bb4-37b6-1d08-0e3308dcac2f.png)](https://www.slideshare.net/kitazaki/20170714-16)

![SBiv_IoT_Hackathon_2.JPG](https://qiita-image-store.s3.amazonaws.com/0/72479/f238c54b-89a8-81fb-ee2f-09e45e2f55de.jpeg)

偶然、TechLounge (ラボルーム) に <A HREF="http://meshprj.com/jp/">MESH</A> の傾きセンサーがいくつかあったため、<A HREF="https://qiita.com/kitazaki/items/14e5d8d24b181c16e106">前回</A> 作った傾きセンサー付きのビアグラスを複数製作し、実際の"飲み会"の雰囲気を演出することにした。また、この時に足りない分を Wio Node と Grove I2C 三軸加速度センサー (IoT ハッカソンの部材として提供された) で代替した。

![スクリーンショット 2018-01-01 8.44.36.png](https://qiita-image-store.s3.amazonaws.com/0/72479/0cb8cab8-71bf-d60d-26e4-0e95fac63037.png)

傾きセンサーと MESH アプリ (スマホ、または、タブレット) の間は Bluetooth で接続され、スマホ、または、タブレットは Wi-Fi に接続される。また、Wio Node は直接 Wi-Fi に接続されてインターネット上の Node-RED と通信を行う。(この時は標準の Wio Node アプリ (Wio Link) を用いて実装したため、Node-RED 側からセンサーの値を取得していた。また、リングLEDを使用したパリピ感は全く無い。)
Node-RED から Milkcocoa (MQTT Broker) へ 傾きデータを Publish し、クライアントはMilkcocoa へ Subscribe することにより「カンパイ」が共有される仕組みである。

この後、IoTハッカソンはピッチ・審査発表・交流会を経て無事閉幕になったが、当然、野良ハックチームは正式に競技へ参加していないため評価の対象外である。幸いにも交流会での余興 (の一部？！) として成果発表をさせてもらえたが、勝手ハック (= 野良ハック) の評価の是非を失ったことが作品をコンテストへエントリーするきっかけになったように思う。

# GRデザインコンテスト2017
ちょうど IoT ハッカソンの前日に <A HREF="https://iotlt.connpass.com/event/56277/">IoT 縛りの勉強会！IoTLT vol.29</A> があり、いつものように (LTへの登壇経験は無く、只々聞いているだけ・・) 参加していると、そこで常連で登壇されているがじぇるねの鈴木さんがGRデザインコンテスト2017の宣伝 (エントリーして一次審査を通過すればGRボードがもれなく提供される) をされていたと記憶している。
社内IoTハッカソンでの野良ハックの"モヤモヤ感"とGRボードがもらえるという誘惑が相まって、勢いでエントリーしたことを覚えている。
2017年8月7日に一次選考通過のお知らせと共に、二次エントリーに向けて本格的に「カンパイシェア」の検討と開発に突入していくことになる。

![スクリーンショット 2018-01-01 22.46.52.png](https://qiita-image-store.s3.amazonaws.com/0/72479/ef3772de-b480-dbe5-5428-dc6da5a5515a.png)

## 二次エントリー
光るビアグラスの着想は頭の片隅にうっすらとあったと記憶している。

(Google 画像検索結果　"ビールジョッキ" "光る")
![スクリーンショット 2018-01-01 23.12.11.png](https://qiita-image-store.s3.amazonaws.com/0/72479/902c669c-dd45-f399-9fdb-b524d0e776da.png)

また、飲む人 (情報を発信する人) も 一緒に飲む人 (情報を受信する人) も楽しくなるようなサービスがいいなと考えていたことも記憶している。ガラスに模様細工を施して光を入れて美しく反射させる伝統技術「江戸切子」と IoT とを組み合わせるに至った背景である。

![スクリーンショット 2018-01-02 2.32.21.png](https://qiita-image-store.s3.amazonaws.com/0/72479/669bf1eb-03c8-e89a-a0d0-873328e98100.png)

![スクリーンショット 2018-01-02 2.30.19.png](https://qiita-image-store.s3.amazonaws.com/0/72479/363e5003-7af7-37c2-216f-134e65338cbe.png)

・<A HREF="https://www.slideshare.net/kitazaki/gr2017-85494766">企画書</A>
・<A HREF="https://www.slideshare.net/kitazaki/gr2017-85494635">開発日誌</A>

(回路図: GR-COTTON プロトタイプ実装)
![スクリーンショット 2018-01-02 0.25.25.png](https://qiita-image-store.s3.amazonaws.com/0/72479/2d8172ad-31b5-ea17-101c-2bd39a162f62.png)

(回路図: GR-COTTON グラス実装)
![スクリーンショット 2018-01-02 0.25.40.png](https://qiita-image-store.s3.amazonaws.com/0/72479/d3e62e5a-9670-133e-94f6-0188bbe23873.png)

![スクリーンショット 2018-01-02 1.00.29.png](https://qiita-image-store.s3.amazonaws.com/0/72479/ff9f4623-28e9-ce54-7608-e0d6da0147ba.png)

![スクリーンショット 2018-01-02 1.00.17.png](https://qiita-image-store.s3.amazonaws.com/0/72479/b5726193-9c1a-f3de-e8d8-35a9110759f2.png)

(回路図: Wio Node グラス実装)
![スクリーンショット 2018-01-02 1.45.04.png](https://qiita-image-store.s3.amazonaws.com/0/72479/15563efd-e26d-e32b-7227-9e86d806f9d3.png)

![スクリーンショット 2018-01-02 1.33.16.png](https://qiita-image-store.s3.amazonaws.com/0/72479/a817b272-6569-bf6c-a694-5d9b27464bbd.png)

![スクリーンショット 2018-01-02 1.42.52.png](https://qiita-image-store.s3.amazonaws.com/0/72479/7d47bbea-f6cb-c35d-ccdb-5347826fe2ba.png)

・<A HREF="https://github.com/kitazaki/led">ソースコード</A>

``` gr_sketch_final.cpp
#include "Arduino.h"
#include "ESP8266.h"
#include "Milkcocoa.h"
#include "Client_ESP8266.h"

#define ESP_Serial      Serial1

#define LOOPTIME  1000
#define ACCMAX    200

// Acc
#define PIN_X  A3
#define PIN_Y  A4
#define PIN_Z  A5

// LED
#define TAPELED_PIN     3 //pin for LEDs WS2812B
#define TAPELED_NUM     12 // number of led

// COTTON LED
#define LED_R   22
#define LED_G   23
#define LED_B   24

// LED OP
#define DI()      asm("di")
#define EI()      asm("ei")
//#define NOP()     asm("nop")

// register for rapid access to pin
#define TAPELED_PINREG  P1.BIT.bit6
#define TAPELED_ON      TAPELED_PINREG = 1
#define TAPELED_OFF     TAPELED_PINREG = 0

// Wait routine to construct the signal for LEDs.
// Needed to transfer the signal during DI because tight timing.
#define TAPELED_ON_WAIT0   NOP();NOP();NOP();NOP();NOP();NOP();
#define TAPELED_OFF_WAIT0  NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();
#define TAPELED_ON_WAIT1   NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();
#define TAPELED_OFF_WAIT1  NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();NOP();

//WiFi config
#define WLAN_SSID       ""  // Wi-Fi SSID
#define WLAN_PASS       ""  // Wi-Fi Password

//Milkcocoa config
#define MILKCOCOA_APP_ID      ""  // Milkcocoa APP ID
#define MILKCOCOA_DATASTORE   ""  // Milkcocoa Datastore
#define MILKCOCOA_SERVERPORT  1883

// Create an ESP8266Client class to connect to the MQTT server.
ESP8266Client wifi;

const char MQTT_SERVER[] PROGMEM    = MILKCOCOA_APP_ID ".mlkcca.com";
const char MQTT_CLIENTID[] PROGMEM  = __TIME__ MILKCOCOA_APP_ID;
Milkcocoa milkcocoa = Milkcocoa(&wifi, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);

void onpush(DataElement *pelem);

int x,y,z,ax,ay,az,ox,oy,oz,acc,kanpai;
int r,g,b;
int temp;

void sendLedBuf(uint8_t* red, uint8_t* green, uint8_t* blue, uint8_t num);
void sendLedData(uint8_t red, uint8_t green, uint8_t blue);

void updateAccInfo(){
    
    acc = 0;

    for(int i=0;i < 10;i++){
  // 3 axis read
  ox = x; oy = y; oz = z;
  x = analogRead(PIN_X);
  y = analogRead(PIN_Y);
  z = analogRead(PIN_Z);
  z = 1023-z; // 基盤の取付向きが逆
  ax = x-ox; ay = y-oy; az = z-oz;
  acc = max(acc, abs(ax) + abs(ay) + abs(az));
  delay(100);
    }

}

void logging(){
  Serial.println("3axis x:"+String(x)+" y:"+String(y)+" z:"+String(z)+" ax:"+String(ax)+" ay:"+String(ay)+" az:"+String(az));
  Serial.println("LED r:"+String(r)+" g:"+String(g)+" b:"+String(b));
  Serial.print("acc:");
  Serial.println(acc);
  Serial.print("kanpai:");
  Serial.println(kanpai);
  Serial.print("temp:");
  Serial.println(temp);
}

void setup() {
  // Serial
  Serial.begin(9600);
  delay(10);
  Serial.println("wait..");
  delay(5000);
  Serial.println();

  // Connect to WiFi access point.
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

  wifi.begin(ESP_Serial, 115200);

  Serial.print("FW Version:");
  Serial.println(wifi.getVersion().c_str());

  while (!wifi.setOprToStation()) {
    Serial.println("to station retry");
    delay(3000);
  }
  Serial.println("to station ok");

  if (wifi.joinAP(WLAN_SSID, WLAN_PASS)) {
      Serial.println("Join AP success");
      Serial.print("IP: ");
      Serial.println(wifi.getLocalIP().c_str());    
  } else {
      Serial.println("Join AP failure");
  }

  if (wifi.disableMUX()) {
      Serial.println("single ok");
  } else {
      Serial.println("single err");
  }

  if(milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush)){
    Serial.println("milkcocoa on sucesss");   
  }
  else {
    Serial.println("milkcocoa on failure");   
    }

  // LED
  //setPowerManagementMode(PM_STOP_MODE);
  pinMode(TAPELED_PIN, OUTPUT);
  //pinMode(2, INPUT_PULLUP);
  //attachInterrupt(0, modeChange, FALLING);

  // Acc
  pinMode(PIN_X, INPUT);
  pinMode(PIN_Y, INPUT);
  pinMode(PIN_Z, INPUT);
  x=0,y=0,z=0,ax=0,ay=0,az=0,ox=0,oy=0,oz=0,acc=0,kanpai=0;
  r=0,g=0,b=0;
  temp=0;

  Serial.println("setup end");
  
}

void loop() {
  
  updateAccInfo();
  temp = (int) getTemperature(0);
  logging();
  
  if(acc > ACCMAX){
      kanpai = 1;
  }
  else{
      kanpai = 0;
  }
  
  if(kanpai){
      Serial.println("LED ON");
      for (int i = 0; i < TAPELED_NUM; i++){
          sendLedData(255, 0, 0);
      }
      analogWrite(LED_R, 0);
      analogWrite(LED_G, 255);
      analogWrite(LED_B, 255);

        milkcocoa.loop();
      DataElement elem = DataElement();
      if (abs(ax) > 255) {
	    elem.setValue("r", 255);
      } else {
        elem.setValue("r", abs(ax));
      }      		
      if (abs(ay) > 255) {
        elem.setValue("g", 255);
      } else {
        elem.setValue("g", abs(ay));
      }
      if (abs(az) > 255) {
        elem.setValue("b", 255);
      } else {
        elem.setValue("b", abs(az));
      }
//      elem.setValue("temp", temp);
//      elem.setValue("acc", acc);
//      elem.setValue("ax", ax);
//      elem.setValue("ay", ay);
//      elem.setValue("az", az);
      elem.setValue("kanpai", kanpai);
        milkcocoa.push(MILKCOCOA_DATASTORE, &elem);

  }
  else {
      Serial.println("LED OFF");
      for (int i = 0; i < TAPELED_NUM; i++){
      sendLedData(0, 0, 0);
    }
      analogWrite(LED_R, 255);
      analogWrite(LED_G, 255);
      analogWrite(LED_B, 255);
  }

  delay(LOOPTIME);

}

void onpush(DataElement *pelem) {
  Serial.println("onpush");
//  Serial.println(pelem->getInt("v"));
}

void sendLedBuf(uint8_t* red, uint8_t* green, uint8_t* blue, uint8_t num){
  for(int i = 0; i < num; i++){
    sendLedData(*(red + i), *(green + i), *(blue + i));
  }
}

void sendLedData(uint8_t red, uint8_t green, uint8_t blue){
  DI();
  if ( green & 0x80 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x40 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x20 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x10 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x08 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x04 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x02 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( green & 0x01 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }


  // 襍､LED
  if ( red & 0x80 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x40 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x20 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x10 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x08 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x04 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x02 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( red & 0x01 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }


  // 髱鱈ED
  if ( blue & 0x80 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x40 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x20 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x10 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x08 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x04 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x02 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }

  if ( blue & 0x01 )
  {   // on data send
    TAPELED_ON;
    TAPELED_ON_WAIT1;
    TAPELED_OFF;
    TAPELED_OFF_WAIT1;
  }
  else
  {   // off data send
    TAPELED_ON;
    TAPELED_ON_WAIT0;
    TAPELED_OFF;
    TAPELED_OFF_WAIT0;
  }
  EI();
}
```

``` wionode_final.ino
#include <ESP8266WiFi.h>
#include <Adafruit_NeoPixel.h>
#include <Milkcocoa.h>

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

#define MILKCOCOA_APP_ID      ""  // Milkcocoa APP ID
#define MILKCOCOA_DATASTORE   ""  // Milkcocoa Datastore
#define MILKCOCOA_SERVERPORT  1883

#define MOTOR 5 // Grove Port 1 IO4 or IO5
#define PIN 4   // Grove Port 1 IO4 or IO5
#define NUMPIXELS 16   // 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 = 100;
  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
    vibration_w(200);
    led_wipe_a(red, green, blue, strength);
  } else if (type == 1) {
    //  type 2
    vibration(300);
    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);
  }
  // vibration(500);
}

void vibration(int duration) {
  digitalWrite(MOTOR, 1);
  delay(duration);
  digitalWrite(MOTOR, 0);
  delay(duration);
}

void vibration_w(int duration) {
  digitalWrite(MOTOR, 1);
  delay(duration);
  digitalWrite(MOTOR, 0);
  delay(duration);
  digitalWrite(MOTOR, 1);
  delay(duration);
  digitalWrite(MOTOR, 0);
  delay(duration);
}

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);
  pinMode(MOTOR, OUTPUT);
  Serial.begin(115200);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
//  delay(10);
  setupWiFi();
  Serial.println( milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush) );
  vibration(500);
}

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

2017年11月7日に二次選考通過のお知らせと共に、グランドフィナーレに向けて最終的に「カンパイシェア」の魅せ方を検討していくことになる。

## グランドフィナーレ
2017年12月9日当日のスケジュールは以下の通り。
 10:00 ～ 開場、デモ準備
 13:00 ～ グランドフィナーレ開会、審査説明
 13:30 ～ デモンストレーション、審査
 17:00 ～ 表彰式
 18:00 ～ 懇親会

150cm × 60cm の長机の上にプロトタイプ機材を設置してデモンストレーションを行う形式。A3 サイズの卓上パネルは事務局側で作成していただいた。

![image002.jpg](https://qiita-image-store.s3.amazonaws.com/0/72479/63006b1f-06af-477d-ca64-8b6f2c404346.jpeg)

![スクリーンショット 2018-01-02 2.47.39.png](https://qiita-image-store.s3.amazonaws.com/0/72479/da2f7396-c239-1980-5206-4d61cec7a551.png)

![スクリーンショット 2018-01-02 1.05.57.png](https://qiita-image-store.s3.amazonaws.com/0/72479/ae87716f-4115-5e49-8cb8-461e57772788.png)

![スクリーンショット 2018-01-02 1.24.47.png](https://qiita-image-store.s3.amazonaws.com/0/72479/73fbe74d-901f-667e-6bb2-64d2927ead7f.png)

・<A HREF="https://photos.app.goo.gl/fqENoY1wrKPGPe443">フォトアルバム</A>

当日のデモンストレーションは緊張するというより、同じファイナリストの方々と交流したりデモを見るのが楽しく、学生・家族連れや子供が一緒に遊んだり非常にリラックスした雰囲気で、他所でのアイデアソンやハッカソンとは全く毛色が異なる印象だった。
もちろん「カンパイシェア」は懇親会が一番活躍する場になったことは言うまでも無い。

(最終プレゼン資料)
[![スクリーンショット 2018-01-01 23.03.21.png](https://qiita-image-store.s3.amazonaws.com/0/72479/f47bf1b2-050a-d6a0-c623-6770c6cb209c.png)](https://www.slideshare.net/kitazaki/20171209gr2017)

※ 2017年12月28日更新
グランドフィナーレ表彰式の Web サイトと作品記事サイトが公開されたのでリンクを貼っておく。
・<A HREF="http://gadget.renesas.com/ja/contest/2017/grandfinale/">グランドフィナーレのページ</A>
・<A HREF="https://tool-cloud.renesas.com/ja/atelier/">がじぇるね工房の記事</A>