NOBのArduino日記!

NOBのArduino日記!

趣味は車・バイク・自転車・ラジコン・電子工作です。

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

【ESP32→ルーター→ngrok→colab】https通信!
NAT越えを容易にするトンネリングツール「ngrok」を使う事で、家のWiFiルーター設定を変更する事無く、クライアント「ESP32-WROOM-32E」からサーバー「colab」へのhttps通信がサクッと通ると、この先応用が拡がるかなと思います('▽')/
と言う事で【ESP32→無線LANルーター→有線WAN→ngrokcolab】のシンプルな通信実験をしてみました!(
^^)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)"を書くのが無難
 
イメージ 1 イメージ 3
励みになりますのでよければクリック下さい(^o^)/

↩【NOBのArduino日記!】目次に戻る