お世話になっております。
M5StackでWi-Fi経由でkintoneにデータをアップロードするコードを書き実行しました。
M5Stackを起動しアップロードを始めるとなんどトライしても約132秒後に勝手に再起動してしまいます。
原因がまったくわからないのでハードの問題かソフトの問題かわかりません。
コードを記しますので原因や解決法についてアドバイスを頂ければ助かります。
#include <M5Stack.h> #include <TinyGPS++.h> #include <HTTPClient.h> #include <M5StackUpdater.h> #include "utility/MPU9250.h" HardwareSerial GPS_s(2); MPU9250 IMU; HTTPClient http; TinyGPSPlus gps; //各種初期セット int signalin = 35; //A0pin読み取り設定 int signalVal = 0; //アナログ値格納用 float sigV = 0; //信号電圧 int batv = 0; //バッテリー電圧 int Timeout = 0; //Wi-Fi接続タイムアウト float sigLine = 1; //ADC閾値初期セット値 int DelayTime = 0; //ADCノイズディレイタイム初期セット値 unsigned long RunTime = 0;//端末稼働時間 //kintone const int appId = アプリID; //GPS int STAS = 0; int HDOP = 0; float ALT = 0; float SPD = 0; float LAT = 0; float LNG = 0; void setup() { M5.begin(); M5.Power.begin(); Wire.begin(); GPS_s.begin(9600); IMU.initMPU9250(); if (digitalRead(BUTTON_A_PIN) == 0) { Serial.println("Will Load menu binary"); updateFromFS(SD); ESP.restart(); } // Wi-Fi 接続待ち WiFi.begin(); // Wi-Fi接続 while (WiFi.status() != WL_CONNECTED) { delay(100); M5.Lcd.printf("."); Timeout = millis(); if (Timeout >= 10000) { M5.Lcd.printf("wifiTimeout"); delay(1500); M5.Lcd.printf("Reboot now"); delay(1500); ESP.restart(); } } M5.Lcd.println("\nwifi connect ok"); delay(1000); pinMode(signalin, INPUT); M5.Lcd.drawJpgFile(SD, "/jpg/GMS.jpg"); delay(3000); M5.Lcd.drawJpgFile(SD, "/jpg/miniGMS.jpg"); } void loop() { M5.update(); signalVal = analogRead(signalin); sigV = signalVal * 3.3 / 4095; batv = M5.Power.getBatteryLevel(); RunTime = millis()/1000; IMU.readAccelData(IMU.accelCount); // 加速度の生データーを取得する IMU.getAres(); // スケール値を取得する // x/y/z軸の加速度値を計算する IMU.ax = (float)IMU.accelCount[0] * IMU.aRes - IMU.accelBias[0]; IMU.ay = (float)IMU.accelCount[1] * IMU.aRes - IMU.accelBias[1]; IMU.az = (float)IMU.accelCount[2] * IMU.aRes - IMU.accelBias[2]; while (GPS_s.available() > 0) { if (gps.encode(GPS_s.read())){ STAS = gps.satellites.value(); HDOP = gps.hdop.value(); ALT = gps.altitude.meters(); SPD = gps.speed.kmph(); LAT = gps.location.lat(); LNG = gps.location.lng(); break; } } //LCD M5.Lcd.fillRect(0, 0, 265, 240, 0xFFFF); M5.Lcd.setCursor(1, 1); //文字の位置 M5.Lcd.setTextSize(1);//文字の大きさ M5.Lcd.setTextColor(BLACK); M5.Lcd.print("ADC:"); M5.Lcd.print(sigV); M5.Lcd.print("V"); M5.Lcd.print(" IB:"); M5.Lcd.print(batv); M5.Lcd.println("%"); M5.Lcd.print("ax:"); M5.Lcd.print(IMU.ax); M5.Lcd.println("mG"); M5.Lcd.print("ay:"); M5.Lcd.print(IMU.ay); M5.Lcd.println("mG"); M5.Lcd.print("az:"); M5.Lcd.print(IMU.az); M5.Lcd.println("mG"); M5.Lcd.print("satellites:"); M5.Lcd.print(gps.satellites.value()); // Number of satellites in use (u32) M5.Lcd.print(" hdop:"); M5.Lcd.println(gps.hdop.value()); // Horizontal Dim. of Precision (100ths-i32) M5.Lcd.print("month:"); M5.Lcd.print(gps.date.month()); M5.Lcd.print(" day:"); M5.Lcd.println(gps.date.day()); M5.Lcd.print("Time/"); M5.Lcd.print(gps.time.hour()); M5.Lcd.print(":"); M5.Lcd.print(gps.time.minute()); M5.Lcd.print(":"); M5.Lcd.print(gps.time.second()); M5.Lcd.print(":"); M5.Lcd.println(gps.time.centisecond()); M5.Lcd.print("ALT:"); M5.Lcd.print(gps.altitude.meters()); M5.Lcd.print(" SPD:"); M5.Lcd.print(gps.speed.kmph()); M5.Lcd.println("km/h"); M5.Lcd.print("LAT="); M5.Lcd.println(gps.location.lat(), 6); M5.Lcd.print("lng="); M5.Lcd.println(gps.location.lng(), 6); //kintone接続&アップロード const int appId = アプリID; char jsonFormat[] = "{\"app\":\"%d \",\"record\": {\"kadou\":{\"value\":\"%d\"},\"IB\":{\"value\":\"%d\"},\"ADC\":{\"value\":\"%3.2f\"},\"AX\":{\"value\": \"%3.2f\"},\"AY\":{\"value\":\"%3.2f\"},\"AZ\":{\"value\":\"%3.2f\"},\"S\":{\"value\":\"%d\"},\"hdop\":{\"value\":\"%d\"},\"ALT\":{\"value\":\"%5.1f\"},\"SPD\":{\"value\":\"%4.1f\"},\"Lat\":{\"value\":\"%9.6f\"},\"Lon\":{\"value\":\"%9.6f\"}}}"; char json[sizeof jsonFormat + (8 * 11) -1]; sprintf(json, jsonFormat, appId, RunTime, batv, sigV, IMU.ax, IMU.ay, IMU.az, STAS, HDOP, ALT, SPD, LAT, LNG); int httpResponseCode = 0; http.begin("https://サブドメイン名.cybozu.com/k/v1/record.json"); http.addHeader(F("X-Cybozu-API-Token"), F("APIキー")); http.addHeader(F("Content-type"), F("application/json")); httpResponseCode = http.POST(json); M5.Lcd.printf("httpResponseCode = %d \n", httpResponseCode); M5.Lcd.println(http.getString()); delay(1000); }
よろしくお願いいたします。
http.POST(json); でhttpsでPOSTをしていますが、httpsで送信できていないか、レスポンスが返ってこないか、レスポンスは返ってきているけど期待のレスポンスになっていなくて停まっているのかもしれません。そこから確認されてみてはいかがでしょう。
ご回答ありがとうございます。
正常にデータがアップロードされている上、アップロード中はhttpResponseCodeで1が返ってきているので問題なく通信されているかと思っています。
ほかに確認方法があればご教示いただけますか?
> httpResponseCodeで1
1で正しいのでしょうか? ESP32のHTTPClientであれば、ここはHTTPステータスの200とか返ってきそうなところですが。http.POSTをしなければ再起動しないのであれば、明らかにhttp.POST(の使い方)が原因です。あとはsprintfを使わずに、固定値で送った場合にどうなるか試してみてはいかがでしょうか。sprintf(の使い方)の問題かどうかが切り分けられます。既に回答をいただいているので、以降はコメントは控えます。
とりあえず、 httpResponseCode = http.POST(json); の行をコメントにしたらどう変わるか、ぐらいは見てもバチは当たらないと思う。
>dodox86様
ご回答ありがとうございます。ステータスコードは200が返ってきました。
ただ再起動が発生してしまうことに変わりなし,,です。
>thkana様
コメントアウトしたところ、再起動発生しなくなりました!
また、アップロード間隔を1sから2sに変更したところ、稼働時間も倍になりましたがやはり再起動は発生します。。。
なにが原因なのでしょうか。。。
それでわかるのは、やっぱりhttpClient周りが怪しいということ。
ところで、前の質問 https://teratail.com/questions/232747 のendの件はどうだったんでしょう?
>ところで、前の質問 https://teratail.com/questions/232747 のendの件はどうだったんでしょう?
endを記述しても変わらず,,,でした。
http.POSTを何回もやっている内に、トータルで130秒くらいで再起動している、と言うことでしょうか。であれば、httpのオブジェクトを使いまわしているために、中で何か致命的なメモリーリークなどが起きているのかもしれません。HTTPClient httpをグローバル変数として宣言するのではなく、loop()関数内のローカルで試してみては。
あと、httpオブジェクトを使いまわしているのであれば、 http.addHeader(F("X-Cybozu-API-Token"), F("APIキー")); などとloop()ごとに実行してはいけないのでは。(中で適切に処理されているかもしれませんが)これだとどんどん追加されてしまうと思います。
dodox86さんご回答ありがとうございます。
http.addHeader(F("X-Cybozu-API-Token"), F("APIキー"));という部分はloopの外で一度だけ処理すればloopでその都度実行する必要はない、ということでしょうか?
Pythonでお試しサーバ立てて実験してみたのですが、httpの接続を無造作に
http.begin("http://192.168.10.3:8000");
としていたら高頻度(数回に1回)でリブートがかかりました。Python側でHTTP0.9と出るのが気になってHTTPClient.hを覗くと、
#ifdef HTTPCLIENT_1_1_COMPATIBLE
なんていう条件コンパイルもあったりしてするので、なにかの拍子でHTTP0.9になったりするのかも知れません。HTTP0.9にはPOSTがないのでいろいろ起こるとか?
ちなみに、
http.begin("192.168.0.3",8000);
としたらHTTP1.1になって、しばらく走らせてもリブートはかかりませんでした。何かその辺書き方を変えてみたら、例えば
bool begin(String host, uint16_t port, String uri = "/");
だったらどうなるか、というのは試せそうです。
詳しい説明と検証ありがとうございます。
こちらでも検証を進めてみます!
dodox86さん
>HTTPClient httpをグローバル変数として宣言するのではなく、loop()関数内のローカルで試してみては。
まさにこれが原因でした!
なぜこのようなことが起きるのか不思議ですが、ほかの皆様から頂いた情報をもとに検証をしてみたいと思います!
> なぜこのようなことが起きるのか不思議ですが、
ローカルで宣言すると、HTTPClientクラスのオブジェクトはloop()が実行されるたびに生成(初期化)し、消滅します。
http.addHeader()(<とは限りませんが)などの呼び出しでメモリ使用が累積することもなくなる為でしょう。N回目で必ずNG(再起動かかる)なら、再現性が極めて高いと言えて、その可能性が大きいです。HTTPClientのソースファイル自体を読んでみることをおすすめします。
例えばお使いのM5StackのHTTPClientのソースが以下なのであれば
https://github.com/espressif/arduino-esp32/blob/master/libraries/HTTPClient/src/
HTTPClient::addHeader は HTTPClientクラス内のメンバー変数String _headers;に、addHeaderを呼び出すごとに足し込んでいくようなコードになっています。最初の呼び出しであれば引数に「初めてのヘッダー追加」とのようにフラグを指定する必要がありそうです。
void addHeader(const String& name, const String& value, bool first = false, bool replace = true);
これが原因であれば、HTTPClientをグローバルに宣言していて、それを使いまわす元のコードでは問題(メモリーを使い切る)があることになります。あとはご自分で確認してみてください。
なるほどですね。
グローバルですとオブジェクトがその都度呼び出されて蓄積されていって最終的にメモリがオーバーフローするということでしょうか?
C++のお話になります。HTTPClientのオブジェクト(インスタンス)が生き続けていて、loop()を呼び出すたびにaddHeaderなり他のメンバー関数を呼び出し、その中でメモリ使用量が増えるような操作になったら、どうなるでしょうか。グローバル変数ではなく、loop()内のスコープで生成(construct)、消滅(destruct)するなら、使用するメモリは累積しませんよね。
回答1件
あなたの回答
tips
プレビュー