【ESP32→ルーター→ngrok→colab】https通信!

と言う事で【ESP32→無線LAN→ルーター→有線WAN→ngrok→colab】のシンプルな通信実験をしてみました!(^^)v
1. ngrokとは
ローカル環境で動いているサーバーやアプリを、インターネットからアクセス可能にするトンネリングツールです。
「ローカルと外部世界をつなぐ魔法のトンネル」 とも呼ばれる便利なツールです。開発中のAPIやWebアプリをすぐに外部公開できるので、Webhook検証やデモ、リモート共有に欠かせない存在になっています。
例えば http://localhost:5000 で動いているアプリを、https://xxxx.ngrok-free.app のような公開URLで外部から利用できるようにします。
仕組み
- 開発PC上で ngrok コマンドを実行
- ngrok クライアントが ngrok サーバーとセキュアなトンネルを確立
- ngrok サーバーが一意の公開URLを発行
- 外部からのアクセスはそのURLを経由してローカルサーバーに転送されます
用途
- Webhookのテスト GitHubやLINE、Slackなど外部サービスからのコールバックをローカルで受け取れる
- デモや共有 開発中のアプリをクライアントやチームに即座に見せられる
- モバイルアプリ開発 スマホアプリからローカルAPIにアクセス可能にする
- リモートデバッグ 外部の人と同じ環境を共有して動作確認できる
特徴
- 数行のコマンドで簡単に公開できる
- デフォルトで HTTPS に対応(セキュア通信)
- 無料プランではランダムURL、有料プランでは固定ドメインやカスタムドメインが利用可能
- HTTP/HTTPS だけでなく TCP/UDP のトンネルもサポート
2. Colabとは
Google が提供する クラウド上の Jupyter Notebook 環境です。
「ブラウザだけで使える無料のPython実行環境」とも呼ばれ、機械学習やデータ分析の学習・実験に広く利用されています。
ローカルに環境構築をしなくても、すぐに Python コードを実行でき、さらに GPU や TPU を無料で利用できるのが大きな魅力です。
仕組み
- ブラウザからアクセスすると、Google のクラウド上に一時的な仮想マシン(VM)が割り当てられる
- ノートブック形式(セル単位)でコードやテキストを記述・実行できる
- Google Drive と連携してノートブックやデータを保存・共有可能
- セッションが終了すると VM はリセットされる(使い捨て環境)
用途
- 機械学習・深層学習の実験(TensorFlow / PyTorch などをすぐ試せる)
- データ分析・可視化(pandas, matplotlib, seaborn などを利用)
- Python学習や教育用教材としての利用
- チームやクライアントとのノートブック共有・共同編集
- 環境構築不要のプロトタイピングや検証
特徴
- 無料で GPU / TPU が利用可能(有料版 Colab Pro / Pro+ ではより高性能・長時間利用可)
- 環境構築不要、ブラウザだけで利用可能
- Google Drive との親和性が高く、保存・共有が容易
- 多くのライブラリがプリインストール済みで、すぐに使える
- 無料版はセッション時間やリソースに制限あり(数時間で切断されることもある)
3. colabのpythonコード
今回作ったプログラムの流れ
- flask-corsライブラリをインストール
- ngrok をダウンロード&セットアップ
- Flask アプリ起動
- ESP32からJSONを受信
- 最新データを返す
- ngrok を HTTPS モードで起動
Flask というWebフレームワークで「/data というURLに対して HTTP の POST リクエストを受け付けます。
HTTP はアプリケーション層のプロトコルで、その下のトランスポート層として TCP/IP を利用しています。
事前準備
ngrokにアクセス → Googleアカウントなどを使いユーザー登録 → ngrokにログイン → Welcome画面の「Run the following command to add your authtoken to the default ngrok.yml configuration file.」下テキストボックス記載の「ngrok config add-authtoken xxxx」の「xxxx」を控えておき、以下「★ご自身のトークンに置き換えてください」の部分に代入します。
# 実行して残っている ngrok プロセスを強制終了する !pkill -9 ngrok # 0. 必要ライブラリをインストール !pip install flask-cors # 1. ngrokプロセスを停止 print("Stopping ngrok processes (using killall)...") get_ipython().system('killall -9 ngrok || echo "No ngrok processes found to kill"') import time, subprocess, requests, os time.sleep(2) # 2. ポート5000を解放 print("\nKilling processes using port 5000...") get_ipython().system('fuser -k 5000/tcp || echo "No process found on port 5000 to kill"') time.sleep(2) # 3. ngrok をダウンロード&セットアップ print("\nDownloading and extracting ngrok...") get_ipython().system('wget -O ngrok-v3.tgz https://bin.equinox.io/c/bNyj1mQVY4c/ngrok-v3-stable-linux-amd64.tgz') get_ipython().system('tar -xvzf ngrok-v3.tgz') # ★ご自身のトークンに置き換えてください get_ipython().system('./ngrok authtoken xxxx') time.sleep(2) # 4. Flask アプリ起動 print("\nStarting Flask application...") from flask import Flask, request, jsonify from threading import Thread from flask_cors import CORS app = Flask(__name__) CORS(app) # CORSを許可 latest_data = {"message": "No data yet"} # ESP32からJSONを受信 @app.route('/data', methods=['POST']) def receive_data(): global latest_data latest_data = request.get_json() print("Received:", latest_data) return "OK" # 最新データを返す @app.route('/latest', methods=['GET']) def get_latest(): return jsonify(latest_data) def run_flask(): app.run(host="0.0.0.0", port=5000, debug=True, use_reloader=False) Thread(target=run_flask).start() time.sleep(5) # 5. ngrok を HTTPS モードで起動 print("\nStarting ngrok tunnel (HTTPS mode) and getting public URL...") ngrok_process = subprocess.Popen( ["/content/ngrok", "http", "5000"], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) time.sleep(3) if ngrok_process.poll() is None: try: tunnels = requests.get("http://localhost:4040/api/tunnels", timeout=30).json()['tunnels'] https_url = next((t['public_url'] for t in tunnels if t['proto'] == 'https'), None) print("\n--- Colab Setup Complete ---") if https_url: print("ESP32 POST URL:", https_url + "/data") # ESP32 用 print("JSON Latest URL:", https_url + "/latest") # JSON確認用 print("----------------------------") except Exception as e: print("Error getting ngrok URL:", e) print("stderr:") print(ngrok_process.stderr.read().decode()) else: print("ngrok failed to start") print(ngrok_process.stderr.read().decode())
colabで上記コードを実行すると以下の様な「…/data」と「…/latest」の2つのURLが出力されますので共に控えておきます。
Starting ngrok tunnel (HTTPS mode) and getting public URL... --- Colab Setup Complete --- ESP32 POST URL: https://xxxx.ngrok-free.dev/data JSON Latest URL: https://xxxx.ngrok-free.dev/latest ----------------------------
4. ESP32(C++)コード
ArduinoIDE(ver2.3.6使用)で「esp32 by Espressif Systems ver」をhttps通信が可能なverに切り替えます。
ArduinoIDEを開き、ツール→ボード→ボードマネージャー→検索テキストボックス→”ESP32”検索→"esp32 by Espressif Systems ver"項目→プルダウンメニュー→"3.0.3"以上を選択→UPDATEクリック
私の環境で実機試験した結果、以下"〇"記載esp32 by Espressif Systems verが「https:」通信可能でした(^^)/
∟Response code: -1 Error: connection refused
∟2.0.11:×
∟2.0.14:×
∟3.0.0:×
∟3.0.2:×
∟Response code: 200 Server response: OK
∟3.0.3:〇
∟3.0.5:〇
∟3.3.1:〇
false: Let's Encrypt ISRG Root X1 証明書(ルートCA) を入手
ngrok の無料ドメイン(.ngrok-free.app / .ngrok-free.dev)は Let's Encrypt の証明書で署名されています。
そのルート CA は ISRG Root X1 です。Let's Encrypt ISRG Root X1 の PEM 形式証明書は公式サイト「https://letsencrypt.org/certs/isrgrootx1.pem」から取得。
このPEM 形式証明書の内容を"そのまま"以下コード中の root_ca()内に貼り付けたら「Response code: 200 Server response: OK」
以下コードをArduinoIDEに貼り付け、コード中「★<-自分の環境に合わせて入力ください」4か所をコメントの通り修正しESP32へ書き込み → シリアルモニタを"115200"で開いておく。
#include <WiFi.h> #include <WiFiClientSecure.h> #include <HTTPClient.h> // WiFi設定 const char* ssid = "your_ssid"; //★<-自分の環境に合わせて入力ください const char* password = "your_pass"; //★<-自分の環境に合わせて入力ください // ngrok の HTTPS エンドポイント const char* serverUrl = "https://your.ngrok-free.dev/data"; //★<-自分の環境に合わせて入力ください void setup() { Serial.begin(115200); WiFi.begin(ssid, password); Serial.print("Connecting to WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi connected"); } void loop() { if (WiFi.status() == WL_CONNECTED) { WiFiClientSecure client; #if false /*true:証明書検証をスキップ 最初の通信出来る事の確認時に使用、その後証明書が通れば不要*/ client.setInsecure(); #else //★<-自分の環境に合わせて入力ください const char* root_ca PROGMEM = R"rawliteral(-----BEGIN CERTIFICATE----- xxxx your_pem xxxx, -----END CERTIFICATE----- )rawliteral"; client.setCACert(root_ca); // ルート証明書を設定 #endif HTTPClient https; if (https.begin(client, serverUrl)) { https.addHeader("Content-Type", "application/json"); https.addHeader("ngrok-skip-browser-warning", "true"); String payload = "{\"message\":\"Hello from ESP32 (no cert)\"}"; int httpResponseCode = https.POST(payload); Serial.printf("Response code: %d\n", httpResponseCode); if (httpResponseCode > 0) { Serial.println("Server response: " + https.getString()); } else { Serial.printf("Error: %s\n", https.errorToString(httpResponseCode).c_str()); } https.end(); } else { Serial.println("Unable to connect to server"); } } delay(5000); // 5秒ごとに送信 } }
5. Send JSON to HTML
任意のhttps宛にテキストを送信するシンプルなウェブアプリを作りました(^^)/
クライアント(ESP32)送信機の代わり、サーバー(colabのpythonコード)側が受信できるかの確認に使用
操作方法
- 上段textbox:4.で控えた「…/data」URLを入力→writeをクリック
- 中段textbox:「{ "message": "Hello" }」等任意の文字列を指定のURLへ送信
- 下段textbox:通信が成功すれば「Response 200」と表示
See the Pen Untitled by NOB-Arduino (@NOB-Arduino) on CodePen.
6. Read JSON from HTML
任意のhttpsからテキストを読み取るシンプルなウェブアプリを作りました(^^)/
クライアント(ESP32や上記アプリ)から、サーバー(colabのpythonコード)が受信した内容の確認に使用
操作方法
- 上段textbox:3.で控えた「…/latest」URLを入力→Startをクリック
- 中段textbox:「{ "message": "Hello" }」等任意の文字列が表示されたらOK
- 下段textbox:通信が成功すれば「Response 200」と表示
See the Pen Receiving JSON from HTML by NOB-Arduino (@NOB-Arduino) on CodePen.
7. まとめ
ESP32を使ったhttps通信は初めてでしたので色々ハマりました( ;∀;)
特にESP32のボードVerについてはCopilotに聞いてもわからず、他含め40時間程ハマってしまいました
!(*'▽')と言う事で備忘録を残しておきます!
ハマりポイント
- ESP32のボードマネージャーVer -> 3.0.3以上でないとhttps通信不可
- Let's Encrypt ISRG Root X1 証明書 -> root_ca変数内に"そのまま"余計なことせずに貼り付ける事!
- colabのpythonコード -> "CORS(app)"を書くのが無難