農業IoTでサーバー連携に挑戦!ESP32でWebhook 解決編
この記事について
農業IoTシリーズの続編です。
前回までに ESP32からサーバーへWebhookを投げる実装をしていましたが、どうしてもサーバーが受け取ってくれずに悩んでいました。
今回はその原因が「たった2バイトのズレ」だったことを突き止めて、無事に HTTP 200 OK を返せるようになった顛末をまとめています。
さらにデバッグに使ったサーバー側のテストスクリプトも紹介するので、同じようにハマった方の参考になるかもしれません。

]
🎀 ほんと、バグの正体って“そんなこと!?”ってオチが多いんだよね〜
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から送られてきた meta と data を受け取り、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 までお気軽にどうぞ。
🎀 技術的な質問やフィードバックも大歓迎!気になったらお問い合わせフォームからぜひどうぞ〜
🔗 関連リンク
しゃちらぼの最新情報や開発の様子は、こちらでも発信しています:
- 🌐しゃちらぼ公式サイト
- 🐦 X(旧Twitter):@shachi_lab
- 📗 Qiita:@shachi-lab
- 🐙 GitHub:@shachi-lab
- 📸 Instagram:@shachi_lab
ほんとは「しゃちらぼ(Shachi-lab)」なんだけど、見つけてくれてありがとう🐬




コメント