BLIP2を使ってIoT駐車場管理システムを試してみる（ArduinoでSPI通信し、I2CでGR-Sakuraと接続、ATコマンドでESP8266を使い、Milkcocoaでデータをアップ）

#目的
APSのプレゼント企画で入手したADZS-BF707-BLIP2を使用し、IoTに欠かせないセンサーアプリケーションの実験をします。カメラの画像を解析し、駐車場内に車がいるかいないかの物体検出をマイコン上で行い、クラウドにデータをアップします。
![complete.jpg](https://qiita-image-store.s3.amazonaws.com/0/76959/4c3a9583-861b-5857-937f-d4bc004d154f.jpeg)

#使用するパーツ
###ADZS-BF707-BLIP2
BLIP2は、Blacfin+であるBF707を搭載しており、カメラからの画像を処理し、結果のみ情報として出力するセンサーです。従来、このようなシステムは、高いパフォーマンスのプロセッサとOpenCVなどのコンピュータビジョン用ソフトウェアを統合し、装置としての最適化をユーザーで行う必要がありました。しかし、BLIP2は、低消費電力かつ、内蔵メモリだけで実行することができます。 
実験に使用するメインパーツになります。
![blip2.jpg](https://qiita-image-store.s3.amazonaws.com/0/76959/ac7ce55c-49b1-cbca-e0c7-014e7324efde.jpeg)

###Arduino
BLIP2に接続したカメラからの物体検出データを取得するために使用するマイコンです。
通信方式は、SPIを使用します。

###GR-Sakura
Arduinoに比べ、処理能力が高く、搭載メモリが大きいマイコンです。
Arudinoだけではメモリが足りなかったため、GR-SakuraにMilkcocoaのライブラリを組み込み
クラウドへアップさせます。
尚、GR-SakuraをMaster、ArudinoをSlaveでI2C接続します。

###ESPWroom2（ESP8266）
ATコマンドでWifi接続するために使用します。

#準備
###ADZS-BF707-BLIP2を使って物体検出をためしてみる
BLIP2単体で、物体検出の能力を試してみます。
[サイト](http://www.analog.com/jp/design-center/evaluation-hardware-and-software/evaluation-boards-kits/adzs-bf707-blip2.html)から、ADVisionSensorController GUIをダウンロードし、インストールしてください。
下記の手順を参考に動作確認してください。
1.付属のUSBケーブル2本を、ボードに接続するとLEDが点灯します。
2.ADVisionSensorController.exeを起動します。
　Terminalウィンドウにはメッセージが流れます。
3.タブメニューの「VOS Configuration」を選択します。
 　ManageFilesボタンをクリックすると、ファイルリストを表示し、
 「default_outdoor_parking_iot」を選択して、右ボタンをクリックして、[Set as active setting]を選択します。
4.タブメニューの「VOS」を選択し、「Enable VOS」をクリックします。
　青の画面を右クリックし、すべての項目にチェックをつけます。

カメラに映っている物体を動すと、物体検出ができていることを確認できます。

###ADZS-BF707-BLIP2のファームを書き換える
初期状態ではSPI通信が行えないため、ファームウェアを書き換えます。
CrossCore Embedded Studioを使って、ソースコードをビルドする必要があるのですが、
面倒な方は[APSサンプルプログラム](https://www.aps-web.jp/lab/adi/201704/05/)内のファームウェアを使用してください。
ファームウェアの書き換えは、BLIP2にICE1000を接続後、ADVisionSensorController.exeを起動し、
firmware upgradeタブから、ファームアップデートを選んでください。
尚、事前にCrossCoreEmbeddedStudioのVersionのバージョン「2.1.0」をインストールしておいてください。
Version2.1.0以外の場合は、GUIフォルダのConfigFilesフォルダ内の「GeneralConfiguration.xml」ファイル、<CCES_Version>2.1.0</CCES_Version>を対応バージョンに書き換えてください。

#接続
![pincomplete.png](https://qiita-image-store.s3.amazonaws.com/0/76959/e6313837-d186-97d1-fdac-a1620da4e40c.png)
![line.jpg](https://qiita-image-store.s3.amazonaws.com/0/76959/28bffb78-55ba-1bf9-98e7-073f45426225.jpeg)

#プログラム
###Arduino
[ArduinoIDE](https://www.arduino.cc/en/main/software)を使用してプログラムを作成します。

```blip2.ino
#include <SPI.h>
#include <Wire.h>
#define SSPin 10

const byte WSN = 2;
volatile byte state = LOW;
volatile byte vos[32];
volatile byte result;
volatile byte occupancyType = 0;

void setup() {
    // put your setup code here, to run once:
    Serial.begin (56700); 
    Serial.println("BLIP2 Master");

    Wire.begin(8);// Slave ID #8
    Wire.onRequest(requestEvent);
  
    pinMode(SS,OUTPUT); //SSピンを出力設定
    pinMode(WSN, INPUT);
    attachInterrupt(digitalPinToInterrupt(WSN), wsn_int, RISING); 

    SPI.setBitOrder(MSBFIRST);  //最上位ビット(MSB)から送信
    SPI.setClockDivider(SPI_CLOCK_DIV4);  //通信速度をデフォルト
    SPI.setDataMode(SPI_MODE3);   //アイドル5Vで0V→5Vの変化で送信する
    SPI.begin();  //開始
}

int occupancyState(byte data){
    switch(data) {
    case 0x81:
        if(vos[8] == 0) {
            Serial.println(" No Occupancy");
            return 1;
        }
        else if(vos[8] == 1) {
            Serial.println(" Occupancy");
            return 2;
        }
        else if(vos[8]==2) {
            Serial.println(" No meta data");
            return 3;
        }
        break;

    case 0x82:
        Serial.println(" Usage");
        return 4;
        break;
    
    case 0x83:
        Serial.println("*** Blob Info ***");
        return 5;
 
    case 0x0F:
    case 0xFF:
        break;
    default:
        //Serial.println("Unnknown command\n");
        break;
    }  

     return 0;
}

void requestEvent() {
  Wire.write(occupancyType);
}

void loop() {
    while(1){
        snd_cmd(0x1);
       
        if(state == HIGH){
            snd_cmd(0x03);
         
            result = get_occupancy();            
            occupancyType = occupancyState(result);
            state=LOW;
        }
        
        SPI.transfer(0);
        delay(40);
    }
}

/*****************************
 *  Send BLIP2 Command       *
 ****************************/
void snd_cmd(unsigned char cmd){
    digitalWrite(SSPin, LOW);
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    SPI.transfer(0x00);
    SPI.transfer(cmd);
    SPI.transfer(0x00);
    digitalWrite(SSPin, HIGH);
}

/*****************************
 *  Get Occupancy Info       *
 ****************************/
byte get_occupancy(void){
    digitalWrite(SSPin, LOW);
    for(int i=0; i < 10; i++){
      vos[i]=SPI.transfer(0x00);
      //Serial.print(vos[i], HEX);
      //Serial.println(" ");
    }
    SPI.transfer(0);
    digitalWrite(SSPin, HIGH);
    return vos[0];
}

/*****************************
 *  WSN_INT handler          *
 ****************************/
void wsn_int(){
  state = HIGH;
}
```

###GR-Sakura
[がじぇるね](http://gadget.renesas.com/ja/)のオンラインコンパイラを使用してプログラムを作成します
テンプレートは、GR-Sakura_Milkcocoa_V2.12.zipを使用しました。
Wifi接続先のSSIDとパスワードは、お使いの環境に合わせて書き換えてください。
また、クラウドにデータをアップするのに、[Milkcocoa](https://mlkcca.com/)を使用するため、アカウントを取得し、AppIDとDatastoreを任意の値に設定してください。

テンプレートの選択
![grsakura.png](https://qiita-image-store.s3.amazonaws.com/0/76959/af2f38db-c138-bffa-a8bb-a76224164788.png)

```gr-sketch.cpp
#include <Arduino.h>
#include <Wire.h>
#include "ESP8266.h"
#include "Milkcocoa.h"
#include "Client_ESP8266.h"

#define ESP_Serial      Serial1

/************************* WiFi Access Point *********************************/
#define WLAN_SSID       "XXXXXXXX"
#define WLAN_PASS       "XXXXXXXX"

///////////////////////////////////////////////////////////////////////////////////
// Milkcocoa
/************************* Your Milkcocoa Setup *********************************/
#define MILKCOCOA_APP_ID      "XXXXXXXXX"
#define MILKCOCOA_DATASTORE   "XXXXXXXXX"

/************* Milkcocoa Setup (you don't need to change this!) ******************/
#define MILKCOCOA_SERVERPORT  1883

/************ Global State (you don't need to change this!) ******************/
// 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;

#ifndef MILKCOCOA_API_KEY
Milkcocoa milkcocoa = Milkcocoa(&wifi, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID);
#else
Milkcocoa *milkcocoa = Milkcocoa::createWithApiKey(&wifi, MQTT_SERVER, MILKCOCOA_SERVERPORT, MILKCOCOA_APP_ID, MQTT_CLIENTID, MILKCOCOA_API_KEY, MILKCOCOA_API_SECRET);
#endif

void onpush(DataElement *pelem);
void onsend(DataElement *pelem);
void sendData(int value);

//blip
int	oldBlipResult;
int	blipResult;

/*****************************
 * setup
 ****************************/
void setup()
{
    Serial.begin(115200);
    Serial.println("GR-SAKURA start");

    // For confirmation of MIKAN operation
    pinMode(PIN_LED0,OUTPUT);
    pinMode(PIN_LED1,OUTPUT);
    pinMode(PIN_LED2,OUTPUT);
    pinMode(PIN_LED3,OUTPUT);

    digitalWrite(PIN_LED0, LOW);

	//Wire
  	Wire.begin();
  	
  	blipResult = -1;
  	oldBlipResult = -1;

    Serial.println("wifi start");
    wifi.begin(ESP_Serial, 115200);

  	//Wifi
    if (wifi.setOprToStation()) {
        Serial.print("to station ok\r\n");
    } else {
        Serial.print("to station err\r\n");
    }

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

    if (wifi.disableMUX()) {
        Serial.print("single ok\r\n");
    } else {
        Serial.print("single err\r\n");
    }

  	//Milkcocoa
#ifndef MILKCOCOA_API_KEY
//	if(milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush)){
  	if(milkcocoa.on(MILKCOCOA_DATASTORE, "send", onsend)){
#else
//	if(milkcocoa->on(MILKCOCOA_DATASTORE, "push", onpush)){
  	if(milkcocoa->on(MILKCOCOA_DATASTORE, "send", onsend)){
#endif
      Serial.println("milkcocoa on sucesss");
  	}
  	else {
      Serial.println("milkcocoa on failure");
  	}


  	Serial.println("setup end");
}


/*****************************
 * loop
 ****************************/
void loop()
{
#ifndef MILKCOCOA_API_KEY
  	milkcocoa.loop();
#else
  	milkcocoa->loop();
#endif

  	Wire.requestFrom(8, 1);// request 1 bytes from Slave ID #8

  	while (Wire.available()) 
  	{
    	blipResult = Wire.read();
    	Serial.println(blipResult);
    	
    	if (oldBlipResult != blipResult)
    	{
    		oldBlipResult = blipResult;
    		if ( blipResult > 0)
    		{
    			sendData(blipResult);
    		}
    	}
  	}
}


/*****************************
 * sendData
 * value : 送信データ
 ****************************/
void sendData(int value)
{
	DataElement elem = DataElement();
	elem.setValue("v", value);
#ifndef MILKCOCOA_API_KEY
//  milkcocoa.push(MILKCOCOA_DATASTORE, &elem);
	milkcocoa.send(MILKCOCOA_DATASTORE, &elem);
#else
//  milkcocoa->push(MILKCOCOA_DATASTORE, &elem);
	milkcocoa->send(MILKCOCOA_DATASTORE, &elem);
#endif

  	Serial.print("send ");
  	Serial.println(value);
}


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

void onsend(DataElement *pelem) 
{
	Serial.print("onsend : ");
	Serial.println(pelem->getFloat("v"));
}
```

###HTML(Milkcocoa)
MILKCOCOA APP IDと、USER DATASTOREは、gr-sketch.cppで使用した値と同じものに書き換えてください。

```test.htm
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Blip2 IoT Test</title>
</head>
<body>
    <script src="https://cdn.mlkcca.com/v0.6.0/milkcocoa.js"></script>
    <script>
        var milkcocoa = new MilkCocoa('MILKCOCOA APP ID.mlkcca.com');
        var dataStore = milkcocoa.dataStore("USER DATASTORE NAME");

        dataStore.on("send", function (d)
        {
            console.log('ret=%d', d.value.v);
        });
    </script>
</body>
</html>
```

#実行
Ardino,GR-Sakuraへプログラムを書き込み後、配線しUSBを接続します。
Blip2はUSB2本使用するため、合計4本のUSBケーブルを使います。
マイコンの起動が確認出来たら、test.htmファイルをブラウザで開くと、結果の値をコンソールで確認できます。
値の意味は下記になります。
1：No Occupancy
2：Occupancy
3：No meta data

#今後
・BLIP2から取得している情報は、Occupancyのみのため、BLOB情報を取得する。
　BLOB情報には、物体検出の矩形情報が含んでいます。

・ArduinoとGR-Sakuraの２つのマイコンを使用しているのを、GR-Sakuraのみにする。
　マイコンの処理スペックが異なるためか、Arduinoのプログラムを、そのままGR-Sakuraに移植した場合、SPIの同期がとれなかったため、暫定策として、2台のマイコンを使用しています。

#参考
・ADZS-BF707-BLIP2
http://www.analog.com/jp/design-center/evaluation-hardware-and-software/evaluation-boards-kits/adzs-bf707-blip2.html
・GR-Sakura
http://gadget.renesas.com/ja/product/sakura.html