Arduino M0 Pro でマルチタスクプログラミング : NCES IoT Base Shield : Milkcocoa

#はじめに

この記事では，TOPPERS/R2CAにより，NCES IoT Base Shield に接続したセンサ(Ultrasonic Ranger)の値をMilkcocoaにsendしたり，Milkcocoa経由で受け取ったデータによりサーボを駆動する方法について説明します．

NCES IoT Base Shiledについては以下の記事を参照して下さい．

- [Arduino M0 Pro でマルチタスクプログラミング : NCES IoT Base Shield : Wifi/SD機能](http://qiita.com/exshonda/items/48b30a165e4c6845e900)
- [Arduino M0 Pro でマルチタスクプログラミング : NCES IoT Base Shield : GROVE機能](http://qiita.com/exshonda/items/4b8b9525d7bfaca8a6ad)


# 必要なハードウェア

- [Arduino M0 Pro](http://www.arduino.org/products/boards/4-arduino-boards/arduino-m0-pro) 
- NCES IoT Base Shield
- [Grove Servo](http://www.seeedstudio.com/wiki/Grove_-_Servo)
- [Grove - Ultrasonic Ranger](http://www.seeedstudio.com/wiki/Grove_-_Ultrasonic_Ranger)
- Windows PC
 - Windows7とWindows10で動作を確認しています．
- USB(microB)ケーブル
 - ボードには付属していないので用意して下さい．

#サーバー側の設定

[Arduino M0 Pro でマルチタスクプログラミング : Milkcocoaへの接続](http://qiita.com/exshonda/items/5f84f50c15bcbbb178cd)を参考にサーバー側の設定をして下さい．

##ハードウェアセットアップ

M0にNCES IoT Base Shield を取り付けて，さらにGrove ServoをD2に，とGrove - Ultrasonic RangerをD3に接続します．

<img width="400" alt="aFullSizeRender.jpg.jpg" src=https://qiita-image-store.s3.amazonaws.com/0/106870/73e75c70-1c50-712f-1948-eff16c108437.jpeg>

##ソフトウェアセットアップ

プログラムは以下にあります．

- examples\Milkcocoa_NCESIoT

コードは次のようになっています．STA_SSID/STA_PASSWORDは使用する環境に合わせて定義して下さい．MILKCOCOA_APP_ID/MILKCOCOA_DATASTOREも同様にサーバーの設定に合わせて定義します．

タスク数は2個で，片方のタスク(loop()を実行するメインタスク)で，Ultora Sonicの値を取得してRangeInCentimetersのキーに設定してpush()で送信します．

もう片方のタスク(loop1()を実行するtask1)は，milkcocoa.loop(1)でデータが来ているかチェックして，データが来ていれば，onpush()が実行されます．onpush()は，SERVOのキーが送信された場合は整数値に変換して，0から180の範囲ならサーボを指定された角度で駆動します．

```examples\Milkcocoa_NCESIoT\r2ca_app.h
#define R2CA_NUM_TASK 1

```

```examples\Milkcocoa_NCESIoT\r2ca_app.cpp
#include "r2ca.h"
#include "ESP8266.h"
#include "Milkcocoa.h"
#include "Client_ESP8266.h"

#include "../examples_gdef.h"

#define MILKCOCOA_SERVERPORT  1883

ESP8266Client wifi_client;

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

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

#include <Servo.h> 
Servo myservo; 

#include "Ultrasonic.h"
Ultrasonic ultrasonic(3);

extern void onpush(DataElement *elem);

bool setup_done = false;

void setup()
{
    int ret;
    
    Serial.begin(115200);
    Serial.print("Milkcocoa NCES IoT demo");

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

    ret = WiFi.begin(Serial5, 115200);

    if(ret == 1) {
        Serial.print("Cannot communicate with ESP8266.");
        while(1);        
    } else if(ret == 2) {
        Serial.println("FW Version mismatch.");
        Serial.print("FW Version:");
        Serial.println(WiFi.getVersion().c_str());
        Serial.print("Supported FW Version:");
        Serial.println(ESP8266_SUPPORT_VERSION);
        while(1);
    } else {
        Serial.print("begin ok\r\n");
    }

    Serial.print("FW Version:");
    Serial.println(WiFi.getVersion().c_str());
    
    if (WiFi.setOprToStation()) {
        Serial.print("to station ok\r\n");
    } else {
        Serial.print("to station err\r\n");
    }
    
    if (WiFi.joinAP(STA_SSID, STA_PASSWORD)) {
        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.stopServer()) {
        Serial.print("Stop server ok\r\n");
    } else {
        Serial.print("Stop server err\r\n");
    }        
    
    if (WiFi.disableMUX()) {
        Serial.print("single ok\r\n");
    } else {
        Serial.print("single err\r\n");
    }
    
    if (milkcocoa.on(MILKCOCOA_DATASTORE, "push", onpush)){
        Serial.println("milkcocoa on sucesss");   
    }
    else {
        Serial.println("milkcocoa on failure");   
    }

    myservo.attach(2);
    
    setup_done = true;
    Serial.println("setup end\r\n");
}

void loop() {
    int8_t ret;
    int8_t push_ret;
    
    long RangeInCentimeters;
    RangeInCentimeters = ultrasonic.MeasureInCentimeters();
    Serial.print(RangeInCentimeters);//0~400cm
    Serial.println(" cm");
    
    DataElement elem = DataElement();
    elem.setValue("RangeInCentimeters", (int)RangeInCentimeters);
    
    do {
        push_ret = milkcocoa.push(MILKCOCOA_DATASTORE, &elem);
        if (push_ret != 0) {
            Serial.println("Milkcocoa.push() error.");
            Serial.println(milkcocoa.pushErrorString(push_ret));
            Serial.println(push_ret);
            Serial.println("Retrying MQTT push in 5 seconds...");
            delay(5000);
        }        
    }while(push_ret != 0);
    
    Serial.println("Push success.");
    delay(2000);
    
    if (Serial.available() > 0) {
        Serial.read();
        Serial.print("Pause  : Input any char to continue.\n\r");
        while(!(Serial.available() > 0));
        Serial.print("Resume.\n\r");
        Serial.read();
    }    
}

void onpush(DataElement *pelem) {
    char *data;
    int  pos;
    if(!pelem->getString("SERVO", &data)) {
        return;
    };
    
    pos = atoi(data);

    if ((pos >=  0) && (pos <= 180))  {
        myservo.write(pos);
        Serial.print("Ser Servo : ");
        Serial.println(pos);
    }
}

void loop1() {
    int8_t ret;
    while((ret = milkcocoa.loop(1)) != 0) {
        Serial.println("Milkcocoa.loop() connection error.");
        Serial.println(milkcocoa.connectErrorString(ret));
        Serial.println(ret);
        Serial.println("Retrying MQTT connection in 5 seconds...");
        delay(5000);
    }
    delay(100);
}
```

実行後，データストアにUltrasonic Rangerの値がストアされていることが確認できます．また，[Milkcocoa Tester](http://milkcocoa-tester.netlify.com/)により，SERVOのキーに値を指定して送信することにより，サーボが動作することを確認できます．

#おわりに
この記事では，TOPPERS/R2CAによる NCES IoT Base Shield とMilkcocoaの連携方法について説明しました．マルチタスクが使えるので，Milkcocoaへの送信と受信を独立して実施できるので，タイミングが異なる場合も容易にプログラミング可能です．