この記事はCPS Lab Advent Calendarの9日目の記事です。
CPS Labo Advent Calendar 2018 - Adventar[CPS Labo Advent Calendar 2018 - Adventar]
8日目の記事はこちら
LabMart(のシステム)を作った話 - unotoviveのメモ
*記事を書いている時手元にハードがなかったので後日写真を載せます
##何でこれを書いたか
- 公式のマニュアルにHTTPPOSTの例がtwitterを利用した事例はあったものの,jsonを送る事例がかなり見つけにくいところにあった.
- 公式マニュアルのjson形式での送る説明が分かりずらい.
- 使ってる人がそんなにいなく,ネットに記事が転がってない.(マニュアルが優秀だからかもしれない)
##目標
センサデータをjson形式に変えて,httppostするところまでやっていきたいと思います.
今回は利用しませんが3gimのarduinolibraryです.参考までに https://github.com/openwireless/3gim
###最終的な外観
外での実験中での写真しかなくてすみません.
##使用機材
- ESP32 or Arduino
- ジャンパー線
- ブレッドボード
- 3GIM v2.2
- soracom社のsimカード
##モジュール図
esp32---3GIM(simカードはsoracom sim)
#準備
- ボーレートの設定(任意)
- simカードの設定
- 3Gネットワーク経由でデータを取得してみる.
##配線など
下記接続を元に配線.ブレボあたりでいいと思う.参考リンクにマニュアルがあるのでそこに詳しく記載されているのでそちらをみてください.
*画像は公式より
Arduino/ESP32 | 3GIM |
---|---|
GND | GND |
3.3V | VCC(+) |
3.3v | IOREF |
RX(4)/RX(16) | TX |
TX(5)/TX(17) | RX |
GPIO7 | POWER_ON |
###ボーレートの設定(任意)
*この作業は Arduinoで行いました.ESP32でもできるかもしれないけどなぜか出来なかった.
それではやっていき,下記のプログラムをArduinoかesp32に書き込みます.
#include <SoftwareSerial.h>
SoftwareSerial iemSerial(4, 5);
const unsigned long baudrate = 9600;
void setup() {
Serial.begin(baudrate);
iemSerial.begin(baudrate);
pinMode(7,OUTPUT);
digitalWrite(7,HIGH);
digitalWrite(7,LOW);
Serial.println("Ready.");
}
void loop() {
if (iemSerial.available() > 0) {
char c = iemSerial.read();
Serial.print(c);
}
if (Serial.available() > 0) {
char c = Serial.read();
Serial.print(c); // Echo back
iemSerial.print(c);
}
}
HardwareSerial Serial3g(1);
const unsigned long baudrate = 9600;
void setup() {
Serial.begin(baudrate);
Serial3g.begin(baudrate, SERIAL_8N1, 16, 17);
pinMode(7, OUTPUT);
digitalWrite(7, HIGH); delay(5);
digitalWrite(7, LOW); //3GIMシールド電源ON
Serial.println("Ready.");
}
void loop() {
if (Serial3g.available() > 0) {
char c = Serial3g.read();
Serial.print(c);
}
if (Serial.available() > 0) {
char c = Serial.read();
Serial.print(c); // Echo back
Serial3g.print(c);
}
}
このプログラムはシリアル通信で3gimに送ったものをPC側のシリアル通信に表示したり,逆に3gimがarduinoにシリアル通信でコールバックしたものを,PCに表示するプログラムです.
これを使ってボーレート設定を行っていきます.
プログラムを書き込んだらシリアルモニタボーレート9600を開きます.
プログラムを走らせてから15秒程度たつと,3gimの緑色のLEDが点滅し
Welcome to 3GIM(v2.2)
とシリアルモニタに表示されるはずです.
表示されない場合は
- 配線(特にUARTのTxとRxの接続)が間違っている
- 不具合で初期ボーレートが変わっている.もしくは誰かにボーレートを変えられた
- 電源の供給が足りていない
の線を当たってみてください.
公式マニュアルにも記載されていますが,一度ボーレートを変更してしまうと,ボーレート一つ一つ探していかないといけないので,大変です.
Welcome to 3GIM(v2.2)
が表示されたら.
$YB 任意のボーレート
を入力するとレスポンスが帰ってきて,3GIMのボーレートが変更されます.
###simカードの設定
はじめ,3GIMを触っててRSSIなどが取れるのにGETやPOSTが出来ない問題に当たりました.原因としてはsimカードの設定を行っていなかったからです.それではやっていき.
プログラムは先ほどと同じものを使用します.
シリアルモニタに
$PS
と入力するとデフォルトのsimカードのプロファイルをみることがみることが出来ます.
僕がsoracom simを利用した時は
$PS=OK "iijmio.jp","mio@iij","iij"
と帰ってきました.このままではsoracom simは使えないので設定を変更します.
$PS "soracom.io" "sora" "sora"
と,入力します.
$PS=OK "soracom.io" "sora" "sora"
となればOKです.
一応
$PS
ともう一度入力して,先ほどのレスポンスが帰って来れば成功です.
###3Gネットワーク経由でデータを取得してみる
プログラムは先ほどと同じものを使用します.
参考画像
$WG http://tabrain.jp/demo/httpGET_test.txt
と入力して
Tabrain Web site Complete access from 3GIM
が帰って来れば成功です
3G通信が出来ています.出来ていない場合はもう一度 simカードのプロファイルの方を見直してください
#ようやく本番(post)
ArduinoとESP32の違いはsoftwareserialかhardwareserialかの違いだけです
この解説が分かりずらいんじゃ...
これを元にしました
ここでは温湿度を測って送ってますが,温湿度センサーがなくても問題ないです.
#include <SoftwareSerial.h>
SoftwareSerial iemSerial(4, 5);
// リスト②:温度センサ値を 3 分ごとに m2x クラウドにアップ
const unsigned long baudrate = 9600;
#define LIMITTIME 35000 // ms (3G module start time)
String URL = "ポスト先URL";//最後のスペースは必要
//String HEADER = "\"Host: api-m2x.att.com$r$nX-M2X-KEY: x-m2x-key$r$nContent-Type:application/json$r$n\"";
String HEADER = "\"Content-Type:application/json$r$n\"";
void setup() {
while (!Serial);
Serial.begin(baudrate);
Serial.println(">Ready. Initilaizing...");
while ( !_3Gsetup() ) {
Serial.println(" Connect Error ... Stop");
while (1);
}
Serial.println("Connected");
}
void loop () {
unsigned long tim = millis();
//-------------------- TEMP -----------------
float temp = analogRead(A1)*0.322-60.0;
Serial.println("TEMP = " + String(temp));
String body1 = "\"{\$\"values\$\" : {\$\"TEMP\$\" : [{ \$\"timestamp\$\" : \$\"";
String body2 = "\$\" , \$\"value\$\" : \$\"" + String(temp) +"\$\"}]}}\" ";
if(_3G_WP("$WP " + URL + body1+ datetime()+ body2 + HEADER)){
Serial.println(Serial1.readStringUntil('\n')); }
else Serial.println("Data Update false...");
while(millis()-tim<120000); //waiting
}
// =================== datetime ==============
// Get Date & Time (3GIM command)
// return --> string "2015-12-23T01:23:45%2B09:00"
// 2015-09-20T14:05:12.345Z\$\" , \$\"value\$\" : \$\"32.0\$\"}]}}\" ";
// + "2015-09-02T11:59:00%2B09:00&csv=TEMP01,2015-09-02T10:00:00%2B09:00LIGHT01,24.2"
String datetime() {
String dtime;
do {
SendDataSerial.println("$YT");
while (!SendDataSerial.available());
dtime = SendDataSerial.readStringUntil('\n');
Serial.println(dtime);
delay(1000);
} while ( !(dtime.indexOf("201") > 0) );
dtime.replace(" ", "T"); dtime.replace("/", "-");
return (dtime.substring(7) + "$+09:00");
}
//============== 3G setup =================
boolean _3Gsetup() {
Serial.println("start _3Gsetup()");
pinMode(7, OUTPUT);
digitalWrite(7, HIGH); delay(100);
digitalWrite(7, LOW); // 3G shield --> digitalWrite(7,HIGH);
//------- 3G module begin & connect ---------
String str;
unsigned long tim = millis();
do {
while (!SendDataSerial.available());
str = SendDataSerial.readStringUntil('\n');
} while (!(str.indexOf("3GIM") > 0) && (millis() - tim) < LIMITTIME);
if ( millis() - tim >= LIMITTIME) {
return false;
} else return true;
}
//============== $WP command =================
boolean _3G_WP(String command) {
delay(10);
Serial.println(command); // debug
delay(10);
SendDataSerial.println(command);
String rstr;
unsigned long tim = millis(); // time set(ms)
do {
while (!SendDataSerial.available());
rstr = SendDataSerial.readStringUntil('\n');
Serial.println(rstr); //debug print....
} while (!(rstr.indexOf("$WP=") == 0) && (millis() - tim) < LIMITTIME); // $WP return check
return (rstr.indexOf("$WP=") == 0);
}
HardwareSerial SendDataSerial(1);
// リスト②:温度センサ値を 3 分ごとに m2x クラウドにアップ
const unsigned long baudrate = 9600;
#define LIMITTIME 35000 // ms (3G module start time)
String URL = "ポスト先URL";//最後のスペースは必要
//String HEADER = "\"Host: api-m2x.att.com$r$nX-M2X-KEY: x-m2x-key$r$nContent-Type:application/json$r$n\"";
String HEADER = "\"Content-Type:application/json$r$n\"";
void setup() {
while (!Serial);
Serial.begin(baudrate);
Serial.println(">Ready. Initilaizing...");
while ( !_3Gsetup() ) {
Serial.println(" Connect Error ... Stop");
while (1);
}
Serial.println("Connected");
}
void loop () {
unsigned long tim = millis();
//-------------------- TEMP -----------------
float temp = analogRead(A1)*0.322-60.0;
Serial.println("TEMP = " + String(temp));
String body1 = "\"{\$\"values\$\" : {\$\"TEMP\$\" : [{ \$\"timestamp\$\" : \$\"";
String body2 = "\$\" , \$\"value\$\" : \$\"" + String(temp) +"\$\"}]}}\"";
if(_3G_WP("$WP" + " " + URL + body1+ datetime()+ body2+ " " + HEADER)){
Serial.println(Serial1.readStringUntil('\n')); }
else Serial.println("Data Update false...");
while(millis()-tim<120000); //waiting
}
// =================== datetime ==============
// Get Date & Time (3GIM command)
// return --> string "2015-12-23T01:23:45%2B09:00"
// 2015-09-20T14:05:12.345Z\$\" , \$\"value\$\" : \$\"32.0\$\"}]}}\" ";
// + "2015-09-02T11:59:00%2B09:00&csv=TEMP01,2015-09-02T10:00:00%2B09:00LIGHT01,24.2"
String datetime() {
String dtime;
do {
SendDataSerial.println("$YT");
while (!SendDataSerial.available());
dtime = SendDataSerial.readStringUntil('\n');
Serial.println(dtime);
delay(1000);
} while ( !(dtime.indexOf("201") > 0) );
dtime.replace(" ", "T"); dtime.replace("/", "-");
return (dtime.substring(7) + "$+09:00");
}
//============== 3G setup =================
boolean _3Gsetup() {
Serial.println("start _3Gsetup()");
pinMode(7, OUTPUT);
digitalWrite(7, HIGH); delay(100);
digitalWrite(7, LOW);
SendDataSerial.begin(9600, SERIAL_8N1, 16, 17);
//------- 3G module begin & connect ---------
String str;
unsigned long tim = millis();
do {
while (!SendDataSerial.available());
str = SendDataSerial.readStringUntil('\n');
} while (!(str.indexOf("3GIM") > 0) && (millis() - tim) < LIMITTIME);
if ( millis() - tim >= LIMITTIME) {
return false;
} else return true;
}
//============== $WP command =================
boolean _3G_WP(String command) {
delay(10);
Serial.println(command); // debug
delay(10);
SendDataSerial.println(command);
String rstr;
unsigned long tim = millis(); // time set(ms)
do {
while (!SendDataSerial.available());
rstr = SendDataSerial.readStringUntil('\n');
Serial.println(rstr); //debug print....
} while (!(rstr.indexOf("$WP=") == 0) && (millis() - tim) < LIMITTIME); // $WP return check
return (rstr.indexOf("$WP=") == 0);
}
##解説
###関数
- _3G_setup
- 3GIMをsetupする関数,と言っても3GIMからのシリアル通信を受け取って,一定時間接続がないとタイムアウトする
- _3G_WP
- ポストする関数.送信するコマンド文字列を渡してあげる
- datetime
- $YTコマンドで時間をとってくる
##データ形式(重要)
3GIMではjsonの書き方と,ヘッダーファイルが少し異なります.
###jsonの記述
"\"{\$\"values\$\" : {\$\"TEMP\$\" : [{ \$\"timestamp\$\" : \$\""20190101"\$\" , \$\"value\$\" : \$\"" + String(temp) +"\$\"}]}}\";
プログラムから抜粋するとこんな感じになってます.意味わからん ;;
もう少し小さくみてみる
"\"{\$\"value\$\" : \$\"hoge\$\"}\";
こんな感じ,冒頭の 「 "\」はエスケープシーケンス,ここで特殊なのがキーと値の前に「$」をつけます
プログラムにするときはエスケープシーケンスが必要なので「/$」,またjson形式はダブルクォーテーションで囲わないといけないので「/"」でくくるとこんな感じになります.
試してないのですがシリアルモニタから$WPコマンドで飛ばす時は
$WP "URL" "{$"value$" : $"hoge$"}" "HEADER"
になるんじゃないかなと思います
HEADERの記述
こんな感じ
"\"Host:api-m2x.att.com$r$nX-M2X-KEY: x-m2x-key$r$nContent-Type:application/json$r$n\"";
まぁわからない;;;
細かくしてみてみる
"\"Content-Type:application/json$r$n\"";
分かりやすい,3GIMのHEADERは最後に「$r$n」を後ろにひっつけます.二つ以上のHEADERは普通にくっつければ問題ないようです.
#まとめ
今回は3GIMのセットアップからHTTPPOSTまで行いました.
- simカードの設定を行うこと
- ボーレートは一度変更してわからなくなったら1つ1つ探さないといけない.
- jsonをhttppostする時は jsonのキー,値には前後に「$」を,HEADERには「$r$n」をつける
###参考リンク
株式会社タブレイン
3GIM V2.2R01manual
Part4 Arduino and 3GIM
start [3GIM Wiki]