農業IoTでサーバー連携に挑戦!ESP32でWebhook 解決編

ろらたんがESP32を手に持ち、Webhook連携の解決を紹介する「しゃちらぼブログ」のアイキャッチ画像 ESP32

農業IoTでサーバー連携に挑戦!ESP32でWebhook 解決編

この記事について

農業IoTシリーズの続編です。
前回までに ESP32からサーバーへWebhookを投げる実装をしていましたが、どうしてもサーバーが受け取ってくれずに悩んでいました。

今回はその原因が「たった2バイトのズレ」だったことを突き止めて、無事に HTTP 200 OK を返せるようになった顛末をまとめています。
さらにデバッグに使ったサーバー側のテストスクリプトも紹介するので、同じようにハマった方の参考になるかもしれません。

ろらたんがESP32をもってPCを操作してる様子
]

🎀 ほんと、バグの正体って“そんなこと!?”ってオチが多いんだよね〜

Webhook がとおった

Webhookで、HTTPの応答が200で返るようになった!

追加の解析で原因が判明しました。
esp_http_client_open() に渡す Content-Length と、実際に write した合計バイト数がズレていた ことが原因でした。

特に、multipart の境界やヘッダ末尾に入る CRLF(\r\n)を実際より多めに見積もっていた ため、サーバーは「まだ残りが来るはず」と待ち続けて、最終的にタイムアウトになっていたようです。
存在しないCRLFの2byte分を余分に計算に含めていたっぽい。

厳密に計算し直し、宣言した Content-Length = 実際に write したバイト数 をピッタリ合わせたところ、HTTP 200 が返って安定動作するようになりました🎉
分かってみれば何てことないんだけど、しばらく悩んでました。

🎀 たった数バイトのズレでもサーバーは正直なんだね〜。ピタッと合うと一気に機嫌が良くなるよ!

HTTPステータスコード

サーバーから返ってくる 200 とか 400 は「HTTPステータスコード」と呼ばれます。
ざっくり言うと「サーバーがどう受け取ったか」の合図です。

  • 200 OK
    正常にリクエストを受け取って処理できたよ、という成功の合図。
  • 400 Bad Request
    リクエストの内容が間違ってる/不足しているなどで処理できないときのエラー。
  • 500 Internal Server Error
    サーバー側で処理中にエラーが起きたときの合図。

今回のテストスクリプト(下の章で解説)では、meta が無い場合に 400 を返すようにしてあります。
逆に正しく送れていれば 200 が返ってきて「OK!」と分かる、という仕組みです。

🎀 数字だけ見ると冷たいけど、サーバーの気持ちを翻訳すると“それエラーだよ”って教えてくれてるんだよね〜


Content-Lengthってなんだ?

HTTPの世界では「サーバーに送るデータの合計バイト数」を正確に申告する必要があります。
この数字がズレると、サーバーは「まだ続きが来るはず」と待ち続け、結果タイムアウト。
逆に多すぎれば「データ途中で終わった」とエラーになります。

🎀 サーバーはウソが嫌いなんだね。数字が1バイトでも違うと『納得いかない!』って拗ねちゃうみたい〜

実際、今回のケースでは「存在しないCRLF 2バイト」が余計に数えられていました。
人間的には「まぁ2バイトくらい誤差でしょ」って思うけど、コンピュータは違うんですよね。


AIのコードは良く確かめよう

さらに別件でハマったのがここ。
AIの指示に従って、esp_http_client_write の代わりに「全バイト数を送信し切るヘルパー関数」を提案され、それを使ってみたら送信エラーに…。

よーーくコードを見直したら、このヘルパー関数の戻り値は bool で、成功なら true、失敗なら false
一方、esp_http_client_write の戻り値は「送信したバイト数」。
結果、true = 1なので、送信に成功しても1byteしか送信しなかったっていうことになります。
AIに「置き換え可能」と言われたまま使ったせいで、常に“エラー”判定になってしまったというオチでした。
「AIの提示コードをそのまま信用しない」って頭では分かってたけど、うっかりやっちゃいました😅

🎀 AIのコードは“きっかけ”にするくらいがちょうどいいんだよね〜!


サーバー側のスクリプト

テストスクリプト

今回の原因を突き止めるとき、サーバー側では簡単な挙動で確認しました。
テスト用に置いたのは、たった28行のシンプルなPHPスクリプト。
ESP32から送られてきた metadata を受け取り、JSONファイルとして uploads/ ディレクトリに保存します。

※あくまで検証用サンプルです。本番運用では認証やセキュリティ対策を必ず追加してください。

<?php
header('Content-Type: application/json; charset=UTF-8');

if (!isset($_POST['meta'])) {
  http_response_code(400);
  echo json_encode(['ok'=>false, 'reason'=>'no meta']); exit;
}

$meta = json_decode($_POST['meta'], true);
if ($meta === null) $meta = ['meta_raw' => $_POST['meta']];

// data(任意)
$has_file = isset($_FILES['data']) && $_FILES['data']['error'] === UPLOAD_ERR_OK;
$size     = $has_file ? (int)$_FILES['data']['size'] : 0;
$bin      = ($has_file && $size > 0) ? file_get_contents($_FILES['data']['tmp_name']) : null;

$dir = __DIR__ . '/uploads'; if (!is_dir($dir)) mkdir($dir, 0777, true);
$base = $dir . '/' . date('Ymd_His');

// 1ファイルJSONにまとめて保存(dataが無ければ省略)
$out = ['meta' => $meta];
if ($bin !== null) {
  $out['sha256']   = hash('sha256', $bin);
  $out['data_b64'] = base64_encode($bin);
}
file_put_contents($base . '.json', json_encode($out, JSON_UNESCAPED_UNICODE));

echo json_encode(['ok'=>true, 'saved'=>$base.'.json', 'meta_only'=>($bin===null)]);

これを動かしてみると、ESP32から送られたデータがそのままJSON化されて落ちてきます。
逆に Content-Lengthがズレていると、ファイルが出力されなかったり、途中で止まったりする
その挙動を見て「あぁ、やっぱり送信サイズが怪しいな」と確信できました。

🎀 28行でも立派な探偵ツールだね!犯人は“余計な2バイト”だった〜!


別のスクリプト

後から考えたら、以下のようなスクリプトのほうが良かったのかもって思いました。
サーバー側は本業じゃないから、ちょっと手際が悪かったです。

// PHP例
<?php
  $len = $_SERVER['CONTENT_LENGTH'];
  $input = file_get_contents("php://input");
  $recv = strlen($input);

  file_put_contents("debug.log", "Content-Length=$len, Received=$recv\n", FILE_APPEND);
?>

このコードは原因が分かってから作ったので、結局は使ってません。

🎀 これなら“宣言した長さと実際の受信サイズ”がすぐ見比べられるね!


malloc を極力使わない方針に

今回の実装を通してもうひとつ見直したのが メモリの使い方
ESP32のような小メモリ機では、malloc を多用するとヒープ断片化でジワジワ不安定化 → 最悪は再起動という事態になりがちです。

そこで、JSON生成に使っていた cJSONライブラリをやめて、snprintf で直組みする方式に変更しました。

  • staticバッファを用意して、そこに都度組み立てる
  • 大きなデータは分割して esp_http_client_write で逐次送信
  • malloc に依存しないので、ヒープ断片化のリスクを大幅低減

結果として、安定動作がかなり期待できるようになりました🎉

🎀 組み込みでは“malloc禁止令”ってよく聞くけど、やっぱり理にかなってるんだね〜!


農業IoTへの一歩

本来の目的は「農業IoTでセンサーの値をクラウドに集める」こと。
今回はまだ通信まわりの基盤部分ですが、ここが安定しないと実験すら進められません。

実は、業務が忙しくて農業IoTの実験自体はちょっと停滞中…。
でも、通信まわりの改善を積み上げておけば、センサーをつなげたときに一気に進められるはずです。

🎀 IoTって“基盤づくり”が一番地味だけど、一番大事なんだよね〜


まとめ

  • 今回の原因は Content-Length と実際に送ったサイズのズレ(余計なCRLF 2バイト)
  • 宣言と実データをピッタリ合わせることで、HTTP 200 OK が返って安定動作
  • AIのコードは「きっかけ」にして、必ず自分で戻り値や型を確認するのが大事
  • malloc を多用せず、staticバッファ+逐次送信にしたことで安定性も改善
  • サーバー側は簡単なテストスクリプトで挙動を確認できた

ESP32とサーバーのWebhook連携、やっと安定稼働のスタートラインに立てました。

🎀 これで次の実験に進めるね。畑データがクラウドに並ぶ日も近いよ〜!


📪 お問い合わせなど

技術的なご相談やご質問などありましたら、
📩お問い合わせフォーム  
または、
📮info@shachi-lab.com までお気軽にどうぞ。

🎀 技術的な質問やフィードバックも大歓迎!気になったらお問い合わせフォームからぜひどうぞ〜


🔗 関連リンク

しゃちらぼの最新情報や開発の様子は、こちらでも発信しています:

💬「シャチラボ」で検索してきた方も大歓迎!
ほんとは「しゃちらぼ(Shachi-lab)」なんだけど、見つけてくれてありがとう🐬

コメント

タイトルとURLをコピーしました