LoginSignup
3
4

More than 5 years have passed since last update.

IoT ハッカソン デバイスプログラム手順 〜 Wio LTE M1/NB1(BG96) SoftBank Edition 〜

Posted at

はじめに

本コンテンツは、Wio LTE M1/NB1(BG96) (以下Wio LTE)を用いた、IoT ハッカソンで使用する技術ハンズオンの資料です。
ネットワーク環境は参加者が自由選択できますので、参考としてご活用ください。

概要

本ページでは温湿度センサーから取得した温度値をHTTPを介して転送する例を示します。

お断り

本ハッカソンで提供する Wio LTE M1/NB1(BG96) は非売品のサンプルとなりますので、取扱いにご注意ください。

準備

Seeed JPの開発環境のセットアップページより、環境の準備を進めてください。

動作確認用フロー作成

enebularから、動作確認用のフローを作成していきます。

プロジェクト作成

Projectsを選択し、Create Projectを選択します。
Projects___enebular.png

続いて、ダイアログにプロジェクト名を入力し、Submitボタンを選択します。
プロジェクト名には、何を入力しても構いません。

Projects___enebular-3.png

フロー作成

作成したプロジェクトのASSETSから、Flowsを選択します。

niigata-hackathon___enebular.png

Flowのリストから+ボタンを選択して、フローを作成していきます。

Flows___enebular.png

ダイアログが表示されたら、最上段のname欄にフロー名を入力します。
ここでは、"wio-debug"としています。
入力が終わったら、"Continue"ボタンを選択してください。
Flows___enebular-2.png

続いて、Editボタンを選択して、フローを編集していきます。
wio-debug___enebular.png

フロー編集

以下の図のようなフローを作成していきます。
enebular_Flow_Editor-2.png

ブラウザ左側にあるノード一覧から以下のノードを選択して、フローを編集するエリアにドラッグ & ドロップしてください。
配置したら、httpノードからdebughttps responseノードへ接続します。
- 入力 - http
- 出力 - debug
- 出力 - http response

次に、エントリーポイントURLを設定していきます。
httpノードをダブルクリックして、以下設定値を入力します。
入力が完了したら完了ボタンを選択します。

項目 設定値
メソッド POST
URL /test
名前 <任意 >

enebular_Flow_Editor-2.png

ここではURLを"/test"としていますが、開発時に各自設定を変更してください。
また、URLの設定項目はデバイスのプログラムに設定が必要になるため、控えておいてください。

次に、応答コードを設定します。
http responseノードをダブルクリックして、以下設定値を入力します。
入力が完了したら完了ボタンを選択します。

項目 設定値
名前 <任意>
状態コード 200

Banners_and_Alerts_と_enebular_Flow_Editor.png

編集が完了したら、デプロイを選択します。
enebular_Flow_Editor-3.png

続いて、iアイコンにマウスカーソルを載せて転送先URLを控えてください。
転送先URL

以上で、デバッグ用のフロー作成が完了です。

センサー接続

以下のように、温湿度センサーをD38ポートに接続します。
また、アンテナが接続されていることを確認してください。

温湿度センサー接続

続いて、接続が完了したらUSBケーブルでPCとWio LTEを接続してください。

サンプルプログラム

Arduino IDEを開き、以下のサンプルコードwiolte_bg96_temperature.inoを貼り付けてください。

wiolte_bg96_temperature.ino
#include <WioCellLibforArduino.h>
#include <stdio.h>

// LTEプロバイダ 接続情報
#define APN      "<APN>"
#define USERNAME "<ユーザー名>"
#define PASSWORD "<パスワード>"

// データ送信先選択
#define TARGET_HOST       "<転送先URL>"
#define TARGET_URL        "<エントリーポイントURL>"
constexpr const char *ENDPOINT = TARGET_HOST TARGET_URL;

constexpr uint8_t SENSOR_PIN = WIO_D38; // センサー接続ピン

// ===================================================
// グローバル変数
// ===================================================
WioCellular g_wio;

// ===================================================
// 送信データ定義
// ===================================================
struct SendPacket
{
  float temp;

  // 送信データをJSON文字列へ変換
  String JsonStringify() const
  {
    String result("");
    char buf[32];

    sprintf(buf, "%.1f", this->temp);
    result += "{\"tmpr_c\":";
    result += buf;
    result += "}";

    return result;
  }
};

// ===================================================
// 初期化処理
// ===================================================
void setup()
{
  SerialUSB.begin(115200);
  delay(200);

  SerialUSB.println("start: I/O Initialize.");
  g_wio.Init();

  SerialUSB.println("start: Power ON.");
  g_wio.PowerSupplyCellular(true);

  // LTE NB1を明示的に指定
  SerialUSB.println("### ACCESS_TECHNOLOGY_LTE_NB   ");
  g_wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_NB1);

  SerialUSB.println("start: Module Initialize.");
  if(!g_wio.TurnOnOrReset()){
    SerialUSB.println("error:  Module init failed.");
    return;
  }

  // プロバイダ経由してMobile Network接続
  // OSI参照モデルのL2(データリンク層)における認証
  SerialUSB.println("### SELECT_NETWORK_MODE_MANUAL ");
  g_wio.SetSelectNetwork(WioCellular::SELECT_NETWORK_MODE_MANUAL, "44020");

  SerialUSB.println("start: APN activate.");
  if(!g_wio.Activate(APN, USERNAME, PASSWORD, 30000)){
    SerialUSB.println("error: APN activate failed.");
  }

  g_wio.PowerSupplyGrove(true);
  delay(500);
  TemperatureAndHumidityBegin(SENSOR_PIN);

  SerialUSB.println("setup OK.");

  SerialUSB.print("target host: ");
  SerialUSB.println(TARGET_HOST);
}

// ===================================================
// 定期処理
// ===================================================
void loop()
{
  float temp;
  float humi;

  const bool sensorValueOk = TemperatureAndHumidityRead(&temp, &humi);
  if (sensorValueOk) {
    SendPacket data;
    data.temp = temp;
    SerialUSB.print("temp:"); SerialUSB.println(data.temp);
    sendData(data);
  }
  else {
    SerialUSB.println("TEMP SENSOR ERROR!");    
  }

  delay(5000);
}

// ===================================================
// データ送信処理
// ===================================================
void sendData(const SendPacket &packet)
{
  String data = packet.JsonStringify();
  SerialUSB.print("Send:");
  SerialUSB.print(data);
  SerialUSB.println("");

  WioCellularHttpHeader header;
  header["Content-Type"] = "application/json";

  int status;
  if (g_wio.HttpPost(ENDPOINT, data.c_str(), &status, header)){
    SerialUSB.print("Response Code:");
    SerialUSB.println(status);
  }
  else {
    SerialUSB.println("Failed POST.");    
  }
}

////////////////////////////////////////////////////////////////////////////////////////
//
// 以下、温湿度センサーのスケッチ例を引用
// [ファイル] -> [Wio cell lib for Arduino] -> [grove] -> [grove-temperature-and-humidity-sensor]
// 
////////////////////////////////////////////////////////////////////////////////////////

int TemperatureAndHumidityPin;

void TemperatureAndHumidityBegin(int pin) {
  TemperatureAndHumidityPin = pin;
  DHT11Init(TemperatureAndHumidityPin);
}

bool TemperatureAndHumidityRead(float* temperature, float* humidity) {
  byte data[5];

  DHT11Start(TemperatureAndHumidityPin);
  for (int i = 0; i < 5; i++) data[i] = DHT11ReadByte(TemperatureAndHumidityPin);
  DHT11Finish(TemperatureAndHumidityPin);

  if(!DHT11Check(data, sizeof (data))) return false;
  if (data[1] >= 10) return false;
  if (data[3] >= 10) return false;

  *humidity = (float)data[0] + (float)data[1] / 10.0f;
  *temperature = (float)data[2] + (float)data[3] / 10.0f;

  return true;
}

////////////////////////////////////////////////////////////////////////////////////////
//

void DHT11Init(int pin) {
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT);
}

void DHT11Start(int pin) {
  // Host the start of signal
  digitalWrite(pin, LOW);
  delay(18);

  // Pulled up to wait for
  pinMode(pin, INPUT);
  while (!digitalRead(pin)) ;

  // Response signal
  while (digitalRead(pin)) ;

  // Pulled ready to output
  while (!digitalRead(pin)) ;
}

byte DHT11ReadByte(int pin) {
  byte data = 0;

  for (int i = 0; i < 8; i++) {
    while (digitalRead(pin)) ;

    while (!digitalRead(pin)) ;
    unsigned long start = micros();

    while (digitalRead(pin)) ;
    unsigned long finish = micros();

    if ((unsigned long)(finish - start) > 50) data |= 1 << (7 - i);
  }

  return data;
}

void DHT11Finish(int pin) {
  // Releases the bus
  while (!digitalRead(pin)) ;
  digitalWrite(pin, HIGH);
  pinMode(pin, OUTPUT);
}

bool DHT11Check(const byte* data, int dataSize) {
  if (dataSize != 5) return false;

  byte sum = 0;
  for (int i = 0; i < dataSize - 1; i++) {
    sum += data[i];
  }

  return data[dataSize - 1] == sum;
}

////////////////////////////////////////////////////////////////////////////////////////

URL設定

貼り付けたコードを以下の転送先URLエントリーポイントURLをenebularのフロー作成で控えた値に置き換えてください。

#define TARGET_HOST       "<転送先URL>"

#define TARGET_HOST       "https://ev2-prod-node-red-*****.herokuapp.com"
#define TARGET_URL        "<エントリーポイントURL>"

#define TARGET_URL        "/test"

APN設定

貼り付けたコードの以下のAPN設定情報は、運営技術メンターから入手してください。

// LTEプロバイダ 接続情報
#define APN               "<APN>"
#define USERNAME          "<ユーザー名>"
#define PASSWORD          "<パスワード>"

プログラム書き込み

書き換えが終わったら、Wio LTEにプログラムの書き込みを行います。
wiolte_bg96_temperature___Arduino_1_8_7.png

解説

プログラムは大きく分けて 5 つに別れています。

  1. 初期化(setup)

    LTE 回線への接続、Grove センサの初期化

  2. 定期処理(loop)

    温度線センサーから温度値を読み出し、送信処理(sendData)を呼び出す

  3. 送信処理(sendData)

    温度データを文字列に変換(JsonStringify)して、enebularにHTTP POSTメソッドでデータを転送する

  4. 文字列へ変換(JsonStringify)

    float 型からクラウドでデータ処理しやすい JSON 形式の文字列へ変換

  5. 温湿度センサー値(TemperatureAndHumidityRead)

    温湿度センサのデジタル値を読み取り、アナログ値へ変換
    (※Wio LTEのライブラリに付属するスケッチ例そのもののため、詳細は省略)

初期化(setup)

Wio LTE 本体の処理を呼び出すための変数をグローバル変数で準備します。

wiolte_bg96_temperature.ino抜粋
WioCellular g_wio;

上記の割り当てた内容をsetup関数内で初期化します。

  1. はじめに、Initを呼び出して WioLTE 全体の初期化を行います

    wiolte_bg96_temperature.ino抜粋
    g_wio.Init();
    
  2. 次に LTE の機能を使用するための、PowerSupplyLTEで電源供給を ON し、LTE モジュールの起動操作TurnOnOrResetを呼び出します
    ネットワーク関連の設定(PowerSupplyCellular, SetAccessTechnology) → TurnOnOrReset 順番で呼び出さないと正しく初期化できないので注意してください。

    ここでは、通信方式としてNB-IoTを指定します。

    wiolte_bg96_temperature.ino抜粋
      g_wio.PowerSupplyCellular(true);
    
      // LTE NB1を明示的に指定
      SerialUSB.println("### ACCESS_TECHNOLOGY_LTE_NB   ");
      g_wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_NB1);
    
      SerialUSB.println("start: Module Initialize.");
      if(!g_wio.TurnOnOrReset()){
        SerialUSB.println("error:  Module init failed.");
        return;
      }
    

    また、Cat.M1を指定する場合は、通信方式を呼び出している箇所を以下に書き換えてください。

    wiolte_bg96_temperature.ino抜粋
    g_wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_M1);
    
  3. 今回使用する通信業者のMobile Network Codeを明示的に指定します

    wiolte_bg96_temperature.ino抜粋
      g_wio.SetSelectNetwork(WioCellular::SELECT_NETWORK_MODE_MANUAL, "44020");
    
  4. データ通信を有効にするためActivateでアクティベーションします
    SIM カードが刺さっていなかったり、圏外などで接続できない場合が 3 秒(3000 ミリ秒)で処理を中断するようにしています。
    (何も指定しない場合、120 秒間(2 分)のタイムアウト時間となります)

    wiolte_bg96_temperature.ino抜粋
      if(!g_wio.Activate(APN, USERNAME, PASSWORD, 30000)){
        SerialUSB.println("error: APN activate failed.");
      }
    
  5. Groveポートへの電源供給
    追加でセンサーをつけることを考慮して、GroveコネクターD38以外のポート(D20/A4/A6/I2C/UART)の電源供給をONします

    wiolte_bg96_temperature.ino抜粋
      g_wio.PowerSupplyGrove(true);
      delay(500);
    
  6. 温湿度センサーを初期化
    スケッチ例(ライブラリ付属のサンプルコード)を参考に、温湿度センサー初期化用の関数を呼び出します

    wiolte_bg96_temperature.ino抜粋
      TemperatureAndHumidityBegin(SENSOR_PIN);
    

定期処理(loop)

定期処理では、読み出しと送信処理を呼び出しています。

  1. TemperatureAndHumidityReadで温度センサーから値を読み出し、data.tempに値を格納して、sendData(スケッチ(コード))で準備した送信用の関数呼び出しをします。

    wiolte_bg96_temperature.ino抜粋
      float temp;
      float humi;
    
      const bool sensorValueOk = TemperatureAndHumidityRead(&temp, &humi);
      if (sensorValueOk) {
        SendPacket data;
        data.temp = temp;
        SerialUSB.print("temp:"); SerialUSB.println(data.temp);
        sendData(data);
      }
      else {
        SerialUSB.println("TEMP SENSOR ERROR!");    
      }
    
  2. 最後に 5000 ミリ秒(5 秒)待機して終了します
    なお、時間を短くすることは可能ですが、温度センサーの DHT11 のサンプリング間隔が 2 秒以上の仕様のため、正しい値が取得できなくなってしまいます。

送信処理(sendData)

送信処理では、データ変換 → 転送に必要な情報準備 → 送信の順番で処理をします。

  1. バイナリの生値をHTTPで転送するため、JSON形式の文字列に変換します

    wiolte_bg96_temperature.ino抜粋
      String data = packet.JsonStringify();
    
  2. HTTPのコンテンツタイプヘッダにMIMEタイプとしてJSONを指定します

    wiolte_bg96_temperature.ino抜粋
      WioCellularHttpHeader header;
      header["Content-Type"] = "application/json";
    
  3. ENDPOINT指定した転送先にデータを転送します。
    送信データには、c_str()で String 型から char *型のデータを取得して送信します。

    wiolte_bg96_temperature.ino抜粋
      int status;
      if (g_wio.HttpPost(ENDPOINT, data.c_str(), &status, header)){
        SerialUSB.print("Response Code:");
        SerialUSB.println(status);
      }
    

文字列へ変換(JsonStringify)

単精度浮動小数点(float 型)を、sprintf関数を用いて小数点第1位表示(%.1f)になるよう文字の列(char[32])に変換します。
(温度センサーの精度が小数点第1位で取得できるため)

また、最終的に JSON 形式に変換する必要があるため、"{<key>:<value>}"の形式にしていきます。
ここでは仮に 25.51℃ が取れた場合の変換過程を右側にコメントをつけて説明します。

wiolte_bg96_temperature.ino抜粋
    sprintf(buf, "%.1f", this->temp);   // 25.50 → "25.5"    浮動小数点から整数へ変換しbufへ格納
    result += "{\"tmpr_c\":";           // {"tmpr_c":        resultに「{」括弧開きとキー「tmpr_c」を追加
    result += buf;                      // {"tmpr_c":25.5    resultに「25.5」の文字列を追加
    result += "}";                      // {"tmpr_c":25.5}   resultに「}」括弧閉じを追加

プログラムの解説は以上です。

動作確認

デバイス側

Arduino IDEのメニューバーツール - シリアルモニタでシリアルモニタを開きます。
または、ツールバー右側の虫眼鏡アイコンを選択します。

シリアルモニタ表示

続いて、Wio LTE <-> PC間のUART通信のボーレートを設定します。
ここでは、プログラムに合わせて115200 bpsにしています。
シリアルモニタ-2.png

シリアルモニタに以下のように、温度とResponse Codeが200が出力されていれば成功です。

Send:{"tmpr_c":27.5}
Response Code:200

enebular側

データが正しくenebularに転送されていることを確認します。
enebularのフローエディタ右側のタブからデバッグを選択します。

以下の図のように、温度(デバイスが送信した文字列)が表示されていれば成功です。

enebular_Flow_Editor-4.png

その他情報

APIリファレンス

トラブルシュート

D38 ポート以外がうまく機能しない

起動直後は、D38 以外は OFF になるため、有効にする必要があります。
PowerSupplyGroveを setup()関数内で呼び出すことで解決します。

Groveコネクタ電源供給のサンプルコード
WioCellular wio;

void setup()
{
  wio.Init();                 // Wio LTEを初期化
  wio.PowerSupplyGrove(true); // Groveコネクタの電源供給ON
}

データがenebularに転送されない

Arduino IDEのシリアルコンソール出力でResponse Codeが404になっている場合、enebularでホストしているURLとデバイスに設定したURLに相違があります。

Send:{"tmpr_c":26.6}
Response Code:404

デバイス側でデータ転送後、次の処理へ進むのが遅い

enebular側のフローで応答コードを返すフローがない場合、
デバイスはタイムアウトまで待機ます。
一定時間経過後、503と表示されているため、その場合はhttp responseノードを追加してください。

Send:{"tmpr_c":27.4}
Response Code:503

サンプルコード

運営が全チームに用意するセンサーの使い方は、IDEスケッチ例のそれぞれ下記を参照してください。
Arduino IDE ファイルWio cell lib for Arduinogrove

センサ/アクチュエータ スケッチ名
プッシュボタン grove-button
磁気センサ grove-magnetic-switch
ブザー grove-buzzer
温度・湿度センサ grove-temperature-and-humidity-sensor
超音波センサ grove-ultrasonic-ranger
3軸加速度センサ grove-accelerometer
GPS センサ grove-gps

starter-kit


追加センサーの使い方

運営が用意する追加センサーの使い方以下を参照してください。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4