Arduino
milkcocoa
ESP8266
GR-SAKURA
BLIP2

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

More than 1 year has passed since last update.

目的

APSのプレゼント企画で入手したADZS-BF707-BLIP2を使用し、IoTに欠かせないセンサーアプリケーションの実験をします。カメラの画像を解析し、駐車場内に車がいるかいないかの物体検出をマイコン上で行い、クラウドにデータをアップします。
complete.jpg

使用するパーツ

ADZS-BF707-BLIP2

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

Arduino

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

GR-Sakura

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

ESPWroom2(ESP8266)

ATコマンドでWifi接続するために使用します。

準備

ADZS-BF707-BLIP2を使って物体検出をためしてみる

BLIP2単体で、物体検出の能力を試してみます。
サイトから、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サンプルプログラム内のファームウェアを使用してください。
ファームウェアの書き換えは、BLIP2にICE1000を接続後、ADVisionSensorController.exeを起動し、
firmware upgradeタブから、ファームアップデートを選んでください。
尚、事前にCrossCoreEmbeddedStudioのVersionのバージョン「2.1.0」をインストールしておいてください。
Version2.1.0以外の場合は、GUIフォルダのConfigFilesフォルダ内の「GeneralConfiguration.xml」ファイル、2.1.0を対応バージョンに書き換えてください。

接続

pincomplete.png
line.jpg

プログラム

Arduino

ArduinoIDEを使用してプログラムを作成します。

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

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

テンプレートの選択
grsakura.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の2つのマイコンを使用しているのを、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