ネットワーク上のESP32を自動検索する (M-SEARCH)
皆さんこんにちは、Rcatです。
皆さんESP32を使った工作でwi-fi経由で通信する時どうしてますか?
普通に考えると、ブラウザにIPアドレスを入力して通信する、ソケットなどを使っている場合の専用のプログラムとIPアドレスでやはり通信すると思います。
さて、IPアドレスどうやって取得してますか?
大抵のサンプルを見るとシリアルにプリントをしてますね。というか普通そうです。
さて、ここで問題になるのが工作に組み込んだ後、それができるかどうかというところです。
別にIPアドレスを固定してしまえばまあそれでいいんですけど、そういった操作をせずに、自分でIPアドレスを確認しなくても通信することができないか考えてみました。
本記事は情報はすべて無料です
概要
さて、この記事では少なくともwi-fiに接続したESP32とどうやって通信するのかを理解しているていで進めていきます。
具体的にはネットワークに接続された機器には、IPアドレスが割り当てられており、そのアドレスを使って相手を特定して通信するといった内容が理解しているという前提です。
で、結論から言うとIPアドレスが分からないと通信できませんが、マルチキャストという方法があります。
これを使って特定の機器に呼びかけて返事をしてもらう仕組みがあり、それがM-SEARCHです。
これを使用するにあたりネットワークを監視しましたが、どうやらGoogleの画面にキャスト機能もこれを使ってるみたい?です。
確かにChromecastをwi-fiに接続すれば、どの機器からも簡単に画面がキャストできるって、よく考えたらどうやって検出してるの?って話ですよね。
というわけでこの機能をESP32で使用して自動検出をしてみましょう
M-SEARCHって何?
さて、まずはM-SEARCHとは何かから解説していきましょう。
例えるなら飛行機で具合で悪い人が出た時によくある「お客様の中にお医者様はいらっしゃいますか」ってやつですね。
この方法は対象のネットワーク全体呼びかけを行い、それに該当する機器のみが応答することによって成り立っています。
例えば、以下のサービスはネットワーク内のルーターを検出する時に使います。
"urn:schemas-upnp-org:service:WANIPConnection:1"
何をする時に使うかというと、自動でポートを開放する時に使うんですね。
実際にパケットを投げてみた結果が以下
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=120
ST: urn:schemas-upnp-org:service:WANIPConnection:1
USN: uuid:e4c797e0-82ce-4f84-b56d-085d9d77c8cd::urn:schemas-upnp-org:service:WANIPConnection:1
EXT:
SERVER: TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8
LOCATION: http://192.168.0.1:1900/rootDesc.xml
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 1
BOOTID.UPNP.ORG: 1
CONFIGID.UPNP.ORG: 1337
まぁこんな感じでルーターから応答の文字列が返ってくるわけですね。
注目すべきはLOCATIONの部分です。ここURLが書いてありますよね。
つまり、この部分に自分のIPアドレスを載せることができるのです。
目指すべき形
さて、前置きがなくなってしまいましたが、本題へ行きましょう。
最終的に以下のような呼びかけと、応答をすることを目的とします。
>>> resp = m.Search("urn:Rcat999:service:ESPSearch:1")
>>> print(resp)
HTTP/1.1 200 OK
Location: 192.168.0.136
ST: urn:Rcat999:service:ESPSearch:1
サービス名
今回使用するサービス名には以下を使用します。urn:Rcat999:service:ESPSearch-cam:1
urn
ベンダーごとに各固有の値。私の自作サービスなので、Rcat999にしますservice
サービス名です。ESP32を探すのでそれっぽい名前にします。1
バージョン番号らしいです。とりあえず1にしとくか、なくてもいいと思います。
内容
HTTP/1.1 200 OK
どんな応答でも必ず最初にあるヘッダーなので入れておきます。Location: アドレス
この辺からは自由に改変していきます。
今求めているのはIPアドレスなので、それだけを入れます。ST
これも多分なくていいとは思いますが、一応を入れておきます。
Python(呼びかけ側)
さて、まずは呼びかける方から作っていきます。
といっても実は数年前にベースはできているので、それをちょこっと最新の知識に基づいて修正するだけです。
最初にルーターに対して呼びかけができたのも、実はすでにあるからだったりします。
追記:せっかくなのでリファクタリングしました。Python側は下記で最新版を紹介しています
というわけで以下がソースになります。
ポイント
送信先
アドレスは"239.255.255.250"でポートは1900です。内容
ここにM-Searchの形式で呼びかけ用のデータを記述していきます。
私も詳しいわけではないので、全て分かるわけではありませんが、この固定メッセージのうち、変更できるのはサービス名の部分だけだと考えています。
'M-SEARCH * HTTP/1.1',
f'MX: {Timeout}', #タイムアウト
'HOST: 239.255.255.250:1900',
'MAN: "ssdp:discover"',
f'ST: {ServiceName}' #サービス名
今回はこのサービス名の部分を引数に応じて変更できるようなプログラムにしました。
改行
M-Searchのメッセージは全てcrlf改行のようです。
この辺間違えると特にC言語の処理の方で面倒なので気をつけてください。
また、メッセージの最後は2連続で改行が入ることになっています。データの送信
後は調べると色々出てきますが、マルチキャストに対して先ほどのヘッダーデータを送信します応答の受け取り
上記のソースが最適なのかはわかりませんが、1秒待ってからデータを受信しています。
このプログラムでは、受信したデータをテキストに変換しても戻り値として返しています。
実際に使ってみる
とりあえず使ってみましょう。
モジュールをインポートして、クラスをインスタンスします。
作ったM-SearchオブジェクトのSearchメゾットを使って呼びかけを行います。
とりあえずルーターを呼んでみます。
Python 3.8.1 (tags/v3.8.1:1b293b6, Dec 18 2019, 23:11:46) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import M_search as ms
>>> MS = ms.MSearch_Rcat()
>>> resp = MS.Search("urn:schemas-upnp-org:service:WANIPConnection:1")
>>> print(resp)
HTTP/1.1 200 OK
CACHE-CONTROL: max-age=120
ST: urn:schemas-upnp-org:service:WANIPConnection:1
USN: uuid:e4c797e0-82ce-4f84-b56d-085d9d77c8cd::urn:schemas-upnp-org:service:WANIPConnection:1
EXT:
SERVER: TP-Link/TP-LINK UPnP/1.1 MiniUPnPd/1.8
LOCATION: http://192.168.0.1:1900/rootDesc.xml
OPT: "http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: 1
BOOTID.UPNP.ORG: 1
CONFIGID.UPNP.ORG: 1337
ちゃんと応答が来ましたね。ちなみに、ルーターのサービス名は稀に違うこともあるので、機種によっては反応しなかったりするようです。
ESP32(サーバー側)
さて、今度は呼びかけられるサーバーの方を作っていきます。
こちらも数年前にPythonでは実装していましたが、ESP32用に実装するのは初めてとなります。
というわけで以下がソースになります。
ポイント
まず、これをやるためにはwi-fiudpのインクルードが必要です
上記では先頭のタブで宣言しているので書いてありません。
#include <WiFiUdp.h>
最初に実行するのはマルチキャストを受信するための待ち受け準備です。
この関数をSetup(Wi-Fi接続後)に呼び出してください。
次にループの部分ですが、やっていることはすごく単純です
とにかくいろんなデバイスからのパケットが流れてくるので、できる限り軽く処理できるようにしています
というわけで、複数のIFの関門が待ち受けています。
まず最初は、先頭の7文字がM-Searchかどうか。
次にSTが含まれるかどうか
含まれていた場合、その後ろに改行があるかどうか確認して切り出す
最後に切り出したサービス名が自分のサービス名と同じかどうか確認
ここで自分と同じだった場合、自分が呼ばれているとして返事をします。
返事の方法は最初に定めた通りのフォーマットで返事を行います。ここまでが一連の流れとなります。
実際に使ってみる
というわけでこの機能を組み込んだ。ESP32をwi-fiに接続して呼び出してみましょう
呼び出しには先ほど作成したPythonを使います。
このように応答が返ってきました。
これでESP32のIPアドレスは"192.168.0.30"であることが分かります。
ちなみに複数台が接続している場合の応答はこのようになります
全体の応答の中から、IPアドレスだけを正規表現で抜き出したりすれば、複数のESP32の検出なんかもできそうですね。
そもそも工作に応じてサービス名を変えるというのが一番確実ではありますが。
まとめ
いかがでしたか?
今回のESP32のように工作に組み込んだ後、パソコンと接続できない状態でのIPアドレスの取得方法について考えてみました。
これをやる前は事前にアドレスを固定したり、ディスプレイに表示させたりしていましたが、もうそういったことはしなくてもよさそうです。
これは今後の工作のスタンダードになりそうです。
配布情報
今回の機能は以下のテンプレートに機能追加する形で実装しています
本機能のソースコードは以下から配布されています
なお、入手には別途有料区間のキーワード及び権限Lv1が必要です。
権限については利用規約を、キーワードについては購入いただければ開放されます。
ここから先は
¥ 300
情報が役に立ったと思えば、僅かでも投げ銭していただけるとありがたいです。