#はじめに
こんにちは。ひつじです。
すっかり寒い季節になってきましたね。
前回、AlibabaCloudPlatformの記事を書いたのですが今回は実際にIoTデバイスからセンシングデータを受信してみたいと思います。
本記事の目的としては「ArduinoとSoracomAir」を通じてAlibabaCloudへの接続が成功したことのシェア」となります。
#<前回記事>AlibabaCloud IoT Platform設定方法
https://qiita.com/sheep29/items/e99dc9e50d7a44321520
#マイコンについて
マイコンって何だ!?とお思いの方もいらっしゃると思いますが、今回使うのは以下のマイコンモジュールです。
Wioと書いて「ワイオ」と読みます。
Seeed社が開発しているマイコンモジュールです。Grove コネクターと STM32F4 マイコン、LTE モジュールが搭載されています。
#Wioについて
今、手元には2つのWioモジュールがあります。
Cat.1(カテゴリー1)
NB-IoTと比較し帯域が多く利用でき、通話モードも備えている。
SIM:Soracom Air
NB-IoT(NBは、Narrow Bandの略。)
Cat.1より使用電流が少なく、モデムも低価格。帯域幅が狭いので低容量通信に長けている。
SIM:SoftBank
このWioにはArduinoという開発環境が搭載されており、C#に似たArduino言語でプログラムを記述、実行することができます。
基板にはgroveコネクタが6個ほど付いているのがわかると思います。
ここに、センサーをアタッチするイメージです。
Groveコネクタを使えばセンサーをカチッとはめるだけで簡単に使えて便利です!
こんな感じ。
上記の画像はGPSセンサーをアタッチします。
なんかプラモデルみたいで楽しいですよね。
今回の最終構成ですが、このモジュールにてセンサーの情報を取得し、MQTTでAlibabaCloudのIoTプラットフォームへ転送を行います。その後、TableStoreと呼ばれるNoSQLにストアして、可視化ツール(DATAV)で可視化していきたいと思います。
[前置き]
記事が長くなっちゃったので、まずは、ArduinoとIoTPlatFormへの接続を投稿します
#Arduinoについて
Arduinoは使うマイコンモジュールによってボード、必要なライブラリが異なるので注意が必要です。
以下のリンクを参考すれば、ちゃんとWioの環境にあったArduinoが準備できるで安心してください。
###Arduinoのインストール
Arduinoのインストールは以下のサイトから実施できます。
###ボードの設定
今回はWioのボードをインストールする必要がありますが、手順は全て以下のリンクに記載されております。
https://seeedjp.github.io/Wiki/Wio_LTE_for_Arduino/Home-ja.html
###今回使うマイコン
今回はSoracomスターターキットについてきているCat.1を利用していきたいとおもいます。こちらのスターターキットにはSoracomAirというSIMが付いています。今回はSoracomAirを用いた回線でAlibabaCloudに接続したいと思います。
以下、後々使う接続情報です。
項目 | パラメータ |
---|---|
APN | soracom.io |
USERNAME | sora |
PASSWORD | sora |
#IoTPlatformの設定
前回のブログに記述した内容を参考にIotPlatformの設定を行います。
設定後のパラメータは以下となります。
項目 | パラメータ |
---|---|
ホスト名 | a63V16tah0l.iot-as-mqtt.ap-northeast-1.aliyuncs.com |
ポート | 1883 |
クライアントID | sheep29|securemode=3,signmethod=hmacsha1 |
トピック | /a63V16tah0l/devsheep/user/topicsheep |
ユーザ | devsheep&a63V16tah0l |
パスワード | 217629783CFE62B50ECCB2B56CAAFC4C465E7D66 |
###PubSubclient library注意点
PubSubClient.hはKeepAliveは15秒がデフォルトですが AlibabaCloudではKeepAliveを30秒に設定する必要があります。
ご自身の端末にインストールしたライブラリを編集してください。
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 30
#endif
###Arduinoを操作してみる
では、実際にArduinoでのスケッチをしていきたいと思います。
WioデバイスのuptimeをAlibabaCloudに送ってみたいと思います。
尚、Arduinoのスケッチはスケッチ例から作成すると便利です。
ファイル -> スケッチ例 -> Wio LTE for Arduino -> mqtt -> mqttclient
#include <WioLTEforArduino.h>
#include <WioLTEClient.h>
#include <PubSubClient.h>
#include <stdio.h>
#define APN "soracom.io"
#define USERNAME "sora"
#define PASSWORD "sora"
#define MQTT_SERVER_HOST "a63V16tah0l.iot-as-mqtt.ap-northeast-1.aliyuncs.com"
#define MQTT_SERVER_PORT (1883)
#define ID "sheep29|securemode=3,signmethod=hmacsha1|"
#define OUT_TOPIC "/a63V16tah0l/devsheep/user/topicsheep"
#define MQUSER "devsheep&a63V16tah0l"
#define MQPASS "217629783CFE62B50ECCB2B56CAAFC4C465E7D66"
#define INTERVAL (60000)
WioLTE Wio;
WioLTEClient WioClient(&Wio);
PubSubClient MqttClient;
void callback(char* topic, byte* payload, unsigned int length) {
SerialUSB.print("Subscribe:");
for (int i = 0; i < length; i++) SerialUSB.print((char)payload[i]);
SerialUSB.println("");
}
void setup() {
delay(200);
SerialUSB.println("");
SerialUSB.println("--- START ---------------------------------------------------");
SerialUSB.println("### I/O Initialize.");
Wio.Init();
SerialUSB.println("### Power supply ON.");
Wio.PowerSupplyLTE(true);
delay(500);
SerialUSB.println("### Turn on or reset.");
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### ERROR! ###");
return;
}
SerialUSB.println("### Connecting to \""APN"\".");
if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
SerialUSB.println("### ERROR! ###");
return;
}
SerialUSB.println("### Connecting to MQTT server \""MQTT_SERVER_HOST"\"");
MqttClient.setServer(MQTT_SERVER_HOST, MQTT_SERVER_PORT);
MqttClient.setCallback(callback);
MqttClient.setClient(WioClient);
if (!MqttClient.connect(ID,MQUSER,MQPASS)) {
SerialUSB.println("### ERROR! ###");
return;
}
MqttClient.subscribe(IN_TOPIC);
SerialUSB.println("### Setup completed.");
}
//INTERVALでUPTIME送信
void loop() {
char data[100];
sprintf(data, "{\"uptime\":%lu}", millis() / 1000);
SerialUSB.print("Publish:");
SerialUSB.print(data);
SerialUSB.println("");
MqttClient.publish(OUT_TOPIC, data);
err:
unsigned long next = millis();
while (millis() < next + INTERVAL)
{
MqttClient.loop();
}
}
##スケッチと確認
IoTPlatformの設定とスケッチの作成が完了したら
マイコンボード(Wio)への書き込みを実施しましょう。
実行後はArduino上のコンソールから出力結果を確認できるので、出力結果を確認してみましょう。
ツール -> シリアルモニタ
上記の例では「soracom.io」と「AlibabaCloud」への接続がうまくできたことを表しています。
#AlibabaCloudコンソールでPublishデータの確認
次にAlibabaCloudのIoT Platformの画面にて出力結果を確認してみましょう。
AlibabaCloudコンソールにログインして、IoT Platformのアイコンをクリックします。
監視と運用 -> デバイスログ -> デバイス動作分析
ステータス200で返ってきていることが確認できますね。
では、WioからPublishしたテキストデータも確認しましょう。
監視と運用 -> デバイスログ -> アップストリーム分析 -> MessageID
これでWioデバイスからSoracomAirを使ってAlibabaCloudのIotPlatformとの接続を確認できました。
#GPSセンサーからデータを送る
GPSセンサーで緯度経度の情報を送付してみたいと思います。
先ほどと同じ要領で下記のようなコードをスケッチします。
尚、GPSのサンプルコードもWioのボードにあるので参考にすると簡単にかけます。
#include <WioLTEforArduino.h>
#include <WioLTEClient.h>
#include <PubSubClient.h> // https://github.com/SeeedJP/pubsubclient
#include <stdio.h>
#define APN "soracom.io"
#define USERNAME "sora"
#define PASSWORD "sora"
#define MQTT_SERVER_HOST "a63V16tah0l.iot-as-mqtt.ap-northeast-1.aliyuncs.com"
#define MQTT_SERVER_PORT (1883)
#define ID "sheep29|securemode=3,signmethod=hmacsha1|"
#define OUT_TOPIC "/a63V16tah0l/devsheep/user/topicsheep"
#define MQUSER "devsheep&a63V16tah0l"
#define MQPASS "217629783CFE62B50ECCB2B56CAAFC4C465E7D66"
#define INTERVAL (60000)
WioLTE Wio;
WioLTEClient WioClient(&Wio);
PubSubClient MqttClient;
String cmds[14] = {"\0"}; // for SplitFunction
String latlong; // 分割された文字列を格納する配列
void callback(char* topic, byte* payload, unsigned int length) {
SerialUSB.print("Subscribe:");
for (int i = 0; i < length; i++) SerialUSB.print((char)payload[i]);
SerialUSB.println("");
}
void setup(){
delay(5000);
SerialUSB.println("");
SerialUSB.println("--- START ---------------------------------------------------");
SerialUSB.println("### I/O Initialize.");
GpsBegin(&Serial);
Wio.Init();
SerialUSB.println("### Power supply ON.");
Wio.PowerSupplyGrove(true);
delay(500);
SerialUSB.println("### Power supply ON.");
Wio.PowerSupplyLTE(true);
delay(500);
SerialUSB.println("### Turn on or reset.");
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### ERROR0! ###");
return;
}
SerialUSB.println("### Connecting to \""APN"\".");
if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
SerialUSB.println("### ERROR1! ###");
return;
}
SerialUSB.println("### Connecting to MQTT server \""MQTT_SERVER_HOST"\"");
MqttClient.setServer(MQTT_SERVER_HOST, MQTT_SERVER_PORT);
MqttClient.setCallback(callback);
MqttClient.setClient(WioClient);
if (!MqttClient.connect(ID,MQUSER,MQPASS)) {
SerialUSB.println("### ERROR2! ###");
return;
}
SerialUSB.println("### Setup completed.");
}
int split(String data, char delimiter, String *dst){
//SerialUSB.println("### Step-S1");
int index = 0;
int arraySize = 15; //GPGGAのフィールド数指定
int datalength = data.length();
dst[index] = "\0";
for (int i = 0; i < datalength; i++) {
char tmp = data.charAt(i);
if ( tmp == delimiter ) {
index++;
dst[index] = "\0";
if ( index > (arraySize - 1)) return -1;
}
else dst[index] += tmp;
}
return (index + 1);
}
void loop()
{
delay(5000);
//SerialUSB.println("### Start");
char sLat[64]="";
char sLong[64]="";
char sLatLong[64]="" ;
const char* data = GpsRead();
//SerialUSB.println("### Step2");
if (data != NULL && strncmp(data, "$GPGGA,", 7) == 0) {
//for debug
//data="$GPGGA,010841.000,3539.9084,N,13945.6998,E,1,4,1.99,165.8,M,3";
SerialUSB.println(data);
int index = split(data, ',', cmds);
for(int i = 0; i < index; i++){
if(i == 2){
//string型のままではsprintfできないのでchar型に変換
char x[50]="\0";
(cmds[i].substring(0,2)+'.'+cmds[i].substring(2,4)+cmds[i].substring(6,9)).toCharArray(x, 50);
sprintf(sLat,"\"lat\": %s",x);
}
if(i == 4){
char y[50]="\0";
(cmds[i].substring(0,3)+'.'+cmds[i].substring(3,5)+cmds[i].substring(6,10)).toCharArray(y, 50);
sprintf(sLong,"\"long\": %s",y);
}
}
sprintf(sLatLong,"{%s,%s}",sLat,sLong);
SerialUSB.println(sLatLong);
MqttClient.publish(OUT_TOPIC, sLatLong);
}
}
#define GPS_OVERFLOW_STRING "OVERFLOW"
HardwareSerial* GpsSerial;
char GpsData[300];
char GpsDataLength;
void GpsBegin(HardwareSerial* serial)
{
GpsSerial = serial;
GpsSerial->begin(9600);
GpsDataLength = 0;
}
const char* GpsRead()
{
while (GpsSerial->available()) {
char data = GpsSerial->read();
if (data == '\r') continue;
if (data == '\n') {
GpsData[GpsDataLength] = '\0';
GpsDataLength = 0;
return GpsData;
}
if (GpsDataLength > sizeof (GpsData) - 1) { // Overflow
GpsDataLength = 0;
return GPS_OVERFLOW_STRING;
}
GpsData[GpsDataLength++] = data;
}
return NULL;
}
では、実行してみます。
IoTPlatformでPublishデータの確認をしてみましょう。
GPS情報が取得できましたね。
latは"latitude"の略で緯度を意味します。
longは"longitude"の略で経度を意味します。
#最後に
本来であれば、このあとルールエンジンを用いてTableStoreと連携し、
DataVで可視化までしたかったのですが別記事として投稿致します!
それではご査収ください〜