電波の橋 プログラム作成挑戦記 (UART-WiFiブリッジ)
初級者が、プログラムの作成に挑戦する記事の第四段です。
今回は、Arduinoによるプログラミングに挑戦しました。題材は、無線LANによるUARTのカプセル化です。
プログラムの目的
今回の目的は、シリアル接続の無線化です。具体的な対象としては、自作プロキシ(作成記事参照)が稼働するマシンへのPCからのシリアル接続を考えています。まずは、その構成と課題を説明します。
現在の構成
自作プロキシは、シングルボードコンピュータ(SBC)上で作動しています。そのSBCにはUARTの端子が設けられ、その端子にはオーディオケーブルの一端が着脱自在に接続されます。また、そのケーブルの他端は、着脱自在にUSB-シリアル変換器に接続されます。
USB-シリアル変換器は、UART信号をUSB信号に変換するものであり、USBケーブルを介して、PCに接続されます。
PCでは、変換器がUSBシリアルデバイス(/dev/ttyUSB0)として認識されます。
その端末デバイス(ttyUSB0)を、minicomなどの端末ソフトウェアで開くことで、SBCとPCがシリアル接続されます。
課題
このシリアル接続を、普段から設けているわけではありません。SBCに不具合があったときだけ臨時に敷設しています。
そのため、ケーブル類の準備が毎回必要になり面倒くさいです。特にUSBケーブルは5mぐらいあってとても煩わしいです。
この課題を解決するために、イ、ロの二つの案を考えてみました。
解決案イ
UART-無線ブリッジを二つ用意して、それらにより経路の途中を無線化します。無線の方式には、Zigbee、Bluetoothや無線LANが考えられます。
このイ案の欠点は、ブリッジが二つ必要になるので、費用も二倍になることです。
解決案ロ
この案では、イ案におけるPC側のブリッジをソフトウェアで置換えます。
具体的には、無線方式に無線LANを選び、その無線LANにより、ブリッジからアクセスポイント(AP)までを繋ぎます。さらに、有線LANにより、APからPCまでを繋ぎます。
PCにはシリアル端末の代わりに擬似端末(pty)を用意し、その擬似端末と有線LANをソフトウェアにより中継します。
以上により、PC側のブリッジが無線LAN-擬似端末ブリッジに置き換えられます。
ロ案の欠点は、UART-無線LANブリッジのプログラムと、無線LAN-擬似端末ブリッジのプログラムとで、二つのプログラムが必要になることです。
どちらの案を採るか
考えるまでもなく、金が掛からないロ案を採用しました。
以下に、今回作成したUART-無線LANブリッジと、無線LAN-擬似端末ブリッジを説明します。
UART-無線LANブリッジ
UART-無線LANブリッジは、ハードウェアをなすマイコンと、そのマイコンで作動するソフトウェア(つまり自作プログラム)とからなります。
マイコン
マイコンは、格安WiFiマイコンのESP8266です。ほぼ同じ値段で上位のESP32もあるのですが、消費電力が低くそうなESP8266にしました。
ですが、ESP32はAESの専用命令を持っているので、無線LAN(WPA2)のことを考えると、ESP32のシングルコアが一番良かったのかもしれません。エジソンプラザ(横浜の賽格電子市場)には売ってませんが...
開発環境
開発環境は、Arduinoという初心者向けのマイコン開発環境です。Arduinoでは、C++を使って開発をします。
プログラムの簡単な説明
#include "Indicator.h"
#include "Tunnel.h"
#include "Uart.h"
#include "Wlan.h"
static Uart uart (115200);
static const char SSID[] = "diss";
static const char PASSPH[] = "esarhpssapterces";
static IPAddress addr (10, 0, 0, 1);
static Wlan wlan (SSID, PASSPH, addr);
static Tunnel wlanToUart (uart, wlan);
static Tunnel uartToWlan (wlan, uart);
static Indicator indL2 (14);
static Indicator indL3 (4);
void setup () {
indL2.turnOff ();
indL3.turnOff ();
}
void loop () {
wlan.begin (indL2, indL3);
wlanToUart.clear ();
uartToWlan.clear ();
while (wlan.isConnected ()) {
yield (); // !! DO NOT REMOVE !! Dirty trick to prevent reset by WDT.
wlanToUart.transmit (128);
uartToWlan.transmit (128);
}
}
Indicatorは、ブリッジの状態を表示するためのクラスです。実装では、LEDを制御して作動中、停止中および待機中の三つの状態を表示します。
Uartは、ArduinoのSerialクラスのラッパーであり、Wlanは、WiFiクラスのラッパーです。両方ともMyStreamという仮想クラスのサブクラスになっています。
struct MyStream {
virtual size_t read (uint8_t *, size_t) = 0;
virtual size_t write (uint8_t *, size_t) = 0;
virtual void clear () = 0;
};
MyStreamは、UartとWlanのインターフェイスを規定するための仮想クラスです。(おそらく、MyStreamの定義には、structではなくclassを使うべきでしょう。C++には、エラーにはならないが使うべきではないとされる構文がアホほどあって本当に頭と効率と気持ちが悪いです。)
Tunnelは、所謂一方向パイプです。Tunnelは、送信元のMyStreamから読み取ったデータを、送信先のMyStreamへ転送します。ちなみに、転送にはCPUを使っています。Arduinoなので仕方がないのですが、本来ならDMAで処理したいところです。
無線LAN-擬似端末ブリッジ
使用言語
使用言語は、C言語です。今回こそはGoを使うつもりでした。しかし、残念なことに、標準ライブラリに擬似端末関数が見当たらなかったので、渋々諦めました。本当に残念です。いつもの言い訳ではありません。
擬似端末
端末は、Unixが誕生したときからある大昔のデバイスです。擬似端末は、その端末をソフトウェアで模した端末デバイスであり、こちらも歴史は古いです。そのため、非常に洗練されたインターフェースがOSにより提供されています。
よって、擬似端末のコードは、五行もあれば書けてしまいます。残りの部分についても、TCP/IPのクライアントとデータ転送というよくある処理なので簡単に書けます。コードは簡単に書けますが、記事は書けません。
まとめ
今回は、ソフトウェアとハードウェアの両方の作業となりました。ソフトウェアは、特に問題はなかったのですが、ハードウェアはESP8266のENピンに少し手こずりました。やはり、電子工作をするならオシロスコープと安定化電源ぐらいは欲しいですね。
これを機に、作成記事の題材を電子工作にまで広げることも考えられます。ただし、部品代が掛かるのが難点です。
それでも、世に溢れる「蛇口をひねったら水が出てきた!(そりゃそう設計されてるからそうなるでしょ...)」みたいな電子工作ブログに仲間入りするのも、時流に乗れてる感じがして良いかもしれません。
今後の予定
画像プログラムの記事にするか、電子工作系プログラムの記事にするかで迷っています。
今回の反応が「メンチカツ」ぐらいあれば、電子工作の記事を続けてもいいのですが、なければさっさと見切りをつけて画像プログラムに切り替えたいと思います。