400エラーが返るんだけど??
こうした悩みを解決する記事を書きたいのですが、分からず苦しんでます…
と記事を書き始めたものの、書いてる途中に解決しました。
WiFiだと簡単に送れる
WiFiだとHTTPクライアントライブラリと連携して簡単にHTTPリクエストが送れます。
URLを指定して、POSTだったらJSON形式のデータを引数にPOST関数に入れれば良いだけです。
JSON形式のデータを作るのもArduinoJSONライブラリで、キー作成してバリュー設定してすぐ作れます。
「M5stack wifi スプシ」とでもググれば、スプレッドシートでのGoogleWEBAPIの作り方も含めてサンプルコードがたくさん見つけられます。
今回SORACOMの3G拡張ボード(携帯回線)を使って送りたいのですが…GSMクライアントでの実装になり…スプレッドシート…ちょっとクセあることが分かってきました。
スプシのWebAPIのURLはHTTPS接続必須
スプシへのアクセスはHTTPSでないといけないらしいです。
HTTPでGoogleのWEBAPIにアクセスするとリダイレクトされてHTTPSにつなぐようで、これはセキュリティ懸念のためとのこと。
WiFiだとWiFiClientSecureがあって、これがHTTPS通信に対応してるんですよね。だから簡単。
400エラーに悩まされる
一方の3G拡張ボード、というかGSMクライアントはHTTP通信にしか対応していない様子…。
ずっと400エラーが返ってきていて、訳わからんかったんです。
400エラーは特定できない何かしらの原因で発生するエラー…つまりどこが問題か分からないのです。
Secureがほしいんだよ、GSMクライアントにもSecureが…。
長く組み込みをやってますが、ネットワークにつなぐのが今回が初めてで、HTTPS通信という考えに至るまで2日要しました。
それまで自分のコードの書き方が悪いのだと思い込んでいて、いくら書き直してもつながらない…。
ソラコムのサンプルにある時刻サーバーからのGETはできているのに、なぜスプシへのPOSTができないのかな、HTTPリクエストの書き方が悪いのかな?
80と443って何?Hostにはドメインを書くの?POSTの後のURLにドメインいらないの?
HTTP/1.1?HTTP1.0?JSONで書いてるからダメなの?
参考)ソラコムのサンプルコード→HTTP でアクセスする
HTTPSRedirect.hライブラリを試す
やっとのことでHTTPリクエストするとリダイレクトされることを知り、今度はHTTPSRedirect.hライブラリが公開されていたので、それを試すもつながらない…
これでつながりました!のブログをたくさん見ました、なのにつながらない…
なぜ俺のはつながらんのや…
よく見たらHTTPSRedirect.hはWiFiクライアントに対してのライブラリやん、俺はGSMクライアントなんだよ、誰か作ってくれてないわけ?(もはや他力本願
あー、ダメだ、どうすればいいんだ…
そう思いつつ、できないことをブログに書いてるときに、ふと…
GSMクライアントにSecureがないって本当?
あれ、WiFiクライアントにはSecureが~、とか言ったもののGSMクライアントにSecureがないってどうして分かったんだっけ?
…そういえばGSMクライアントのヘッダ見てなくない?…え??まさか…
そうしてTinyGsmClient.hを覗くと…
#include "TinyGsmClientUBLOX.h"
typedef TinyGsmUBLOX TinyGsm;
typedef TinyGsmUBLOX::GsmClientUBLOX TinyGsmClient;
typedef TinyGsmUBLOX::GsmClientSecureUBLOX TinyGsmClientSecure; ←!!!
なんと!GSMClientSecureあるじゃん!!
うおー、気づかなかったー…
ということでTinyGsmClientをTinyGsmClientSecureに変更したところ、無事動作しました!
だいぶ回り道しましたが、HTTPリクエストをよく理解できました。
これでWiFi環境に依存しない携帯回線使用の組み込み機器が作れそうです!
サンプルコード
M5stack側
#include <M5Stack.h>
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>
#include <ArduinoJson.h>
TinyGsm modem(Serial2); /* 3G board modem */
TinyGsmClientSecure ctx(modem);
void Init3G(void){
Serial2.begin(115200, SERIAL_8N1, 16, 17);
modem.restart();
String modemInfo = modem.getModemInfo();
M5.Lcd.println(modemInfo);
while (!modem.waitForNetwork()) M5.Lcd.print(".");
M5.Lcd.println(F("Ok"));
modem.gprsConnect("soracom.io", "sora", "sora");
while (!modem.isNetworkConnected()) M5.Lcd.print(".");
IPAddress ipaddr = modem.localIP();
M5.Lcd.print(ipaddr);
}
void loop(){
/* HTTP GET example */
int stat = ctx.connect("script.google.com", 443);
if (stat == 0) return;
// HTTPリクエストの送信
StaticJsonDocument<500> doc;
char pubMessage[256];
// JSONメッセージの作成
JsonArray temperature = doc.createNestedArray("TEST");
temperature.add(12);
ctx.println(F("POST /macros/s/(自分で発行したWEBAPIのURLを書く)/exec HTTP/1.1"));
ctx.println(F("Host: script.google.com"));
ctx.println(F("Content-Type: application/json"));
char content_length_hdr[32];
sprintf(content_length_hdr, "Content-Length: %lu", strlen(pubMessage));
ctx.println(F(content_length_hdr));
ctx.println("");
ctx.print(F(pubMessage));
/* receive response */
while (ctx.connected()) {
String line = ctx.readStringUntil('\n');
M5.Lcd.print(line);
if (line == "\r") {
M5.Lcd.println("headers received.");
break;
}
}
char buf[1 * 1024] = {0};
ctx.readBytes(buf, sizeof(buf)); /* body */
ctx.stop();
M5.Lcd.println(buf);
}
GAS側
function doPost(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('シート1');
var params = JSON.parse(e.postData.getDataAsString());
var temp = params.TEMP;
// データをシートに追加
sheet.insertRows(2,1); // 2行1列目に列を挿入する
sheet.getRange(2, 2).setValue(temp);
// JSON形式のレスポンスを返す
var response = {
message: "Success"
};
return ContentService.createTextOutput(JSON.stringify(response)).setMimeType(ContentService.MimeType.JSON);
}
まとめ
WiFiがなくてもソラコムの3G拡張ボードでSecureな通信ができます
TinyGsmClientSecureを使うのがポイント!
コメント