ESP32をBluetoothキーボードやマウスにする

Arduino Leonardo等は、USBキーボードやマウスとして振舞わせ、PC等を操作するのに使うことができます。
同じくArduino IDEをプログラミング環境として使えるESP32ではUSBキーボード機能は付いていません。しかし、代わりにBluetooth LEを利用してキーボードとして振舞わせることができるはず。ということで、やってみました。

セットアップ

今回はM5StickCを使いましたが、ESP32ならどのボードでも同様です。

M5StickC ESP32 PICOミニIoT開発ボードフィンガーコンピューターカラーLCD付き

Arduino IDEでESP32を使用するためのセットアップ手順は、下記サイトなどを参考にしてください。
esp32_setup – スイッチサイエンス

BLE HID (Human Interface Device)のためのクラスはESP32のツールキットにすでに含まれていますが、キーボードやマウスとして振る舞うのに必要なセットアップをクラスにまとめたライブラリを作ってくれている方がいるので、これを利用すると簡単です。

  • キーボード

github.com

  • マウス

github.com

このライブラリをダウンロードして、スケッチを保存している Arduino フォルダの下の libraries 内に入れて IDE を起動すれば利用できるようになります。


ライブラリの使い方は、ファイル>スケッチ例>(ライブラリ名) 以下のサンプルを見れば一目瞭然なほど簡単です。
例えば以下のスケッチは、5秒ごとに Hello world とタイプし、エンターキーを押すものです。

#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

void setup() {
  Serial.begin(115200);
  bleKeyboard.begin();
}

void loop() {
  if (bleKeyboard.isConnected()) {
    bleKeyboard.print("Hello world");

    bleKeyboard.write(KEY_RETURN);
  }
  delay(5000);
}

これをESPに書き込んで起動して、BLEに対応したPCやスマートフォン等からBluetoothバイス一覧を開くと、 「ESP32 BLE Keyboard」というデバイスが見えます。これに接続してテキストエディタ等に切り替えると、5秒ごとに入力されるのが確認できます。
PCだけでなく、iPhoneでも動作を確認できました。

マウスでも同様です。ただし、こちらはiPhoneでは動作が確認できませんでした(Assosiative Touchを有効にしてみたりしたのですが、ボタンもカーソル移動も反応せず…)。

追記

コメントで教えていただきました。多少の修正で接続できるようになるとのことです。
参考:
IoS 13 compatibility? · Issue #3 · T-vK/ESP32-BLE-Mouse · GitHub
ESP32でiOSにも繋がるBLE Mouse - Qiita


このままだと勝手に動くだけですが、せっかくM5StickCを使っているので、カウントダウンを表示したりボタン操作で一時停止などができるようにしてみました。
下記サンプルではCtrlキーを5秒ごとに押しています。(これだけだとあまり意味はないですが、ゲーム等と組み合わせたり、使い方は工夫次第ということで。)

続きを読む

ESP32で64x32 LEDマトリクスパネルを制御

ESP32を使って、Amazonで安価に売られている64x32ピクセルのLEDマトリクスに色々表示できるようにしてみました。

f:id:NeoCat:20180502031918j:plain

 
ESP32 + 64x32 RGB LED Matrix 

使ったもの

ESP32-DevKitC

P3 RGBピクセルパネルHDビデオディスプレイ64×32ドットマトリックスSMD LEDディスプレイモジュール192×96mm 

 f:id:NeoCat:20180502205034p:plain

あとは適宜ブレッドボードなど。 

配線

続きを読む

赤外線アレイセンサ AMG8833 (Grid-EYE) をWebSocketで見てみる

赤外線アレイセンサ AMG8833、いわゆるGrid-EYEを使ってみました。

www.switch-science.com

 

上記の説明の通り、60度の四角錐の測定エリア(2次元)を8x8に分割した64個のピクセルについて、0〜80℃の温度が得られます。時間分解能は10fpsです。

これを、ESP8266 (ESP-WROOM-02) のI2Cで読み出してみました。上記は3.3Vで動作するモジュールになっているので、電源を含め、4本(Vcc, GND, SCL, SDA)の線で繋ぐだけで動作します。

ESP8266の場合、ArduinoWireライブラリはデフォルトでSDAにGPIO4、SCLにGPIO5を使用するため、この通りにしておくとスムーズです。(これ以外のピンでもピン番号さえ指定すれば動作します。)

 ESP8266はこちらのブレークアウトボードを使いました。

ESP-WROOM-02-搭載mikroBUS-R-対応ブレークアウトボード-Ver-2

 

基盤は以前別の目的でFusionPCBで作ったものを流用しています。GPIO13にはステータス確認用のLEDがついています。

f:id:NeoCat:20180128185609j:plain

 

AMG8833が取得した10fpsの結果をリアルタイムに手軽に可視化して見るため、WebSocketでブラウザにデータを流して表示してみることにします。ESP8266上にWebサーバ/WebSocketサーバを乗せて、そこにWebブラウザで接続すれば見られるようにしましょう。

 

AMG8833に対応したArduino用のライブラリはいくつか公開されていますが、今回はAdafruitが公開しているものを使いました。

Arduino IDEの「スケッチ → ライブラリをインクルード → ライブラリを管理…」をメニューから選び、Adafruit AMG88xx Libraryを検索してインストールします。ついでにWebSockets というライブラリも入れておきます。これは名前の通りWebSocketのサーバ/クライアント実装です。

 

あとは記事末尾のプログラムをArduino IDEで書き込みます。

AMG8833に関連するのは以下の部分です。0x68はこのセンサのI2Cアドレスです(0x68と0x69を基盤上のジャンパで選択可能。Adafruitのライブラリではデフォルトが0x69になっていました。)

#include <Wire.h>
#include <Adafruit_AMG88xx.h> Adafruit_AMG88xx amg; // setup amg.begin(0x68); // loop float pixels[AMG88xx_PIXEL_ARRAY_SIZE]; amg.readPixels(pixels);

ESP8266を起動すると、指定したWi-Fiに接続し(接続中は1秒間隔でLEDが点滅し、接続成功すると3秒間隔の点滅になります)、Webサーバが起動します。シリアル出力またはArduino IDEの「ツール → シリアルボート」でIPアドレスを確認し、そこにWebブラウザで「 http://192.168.10.xx/ 」のように接続すると、以下のように8x8のグリッドにリアルタイムの出力値が表示されます(なお自分の方に向けたときに鏡像になるよう、左右反転して表示させています)。グリッド内の各数値は℃、色は0℃〜30℃に色相を適当に割り当てています(JavaScriptの bgcolor 関数)。

 

近距離なら人の形が識別できそう。 

f:id:NeoCat:20180128210415p:plain

 

センサから2mくらい離れたところで手を振ってみたところ。10fpsだと結構動きもわかります。

f:id:NeoCat:20180128205653g:plain

 

 

暖房をつけている状態で部屋全体が入るくらいの距離にしてみると、天井近くの方が床付近よりも2〜3℃くらい暖かいことがわかったりします。なおここで見ているのは空気の温度ではなく、壁の温度だと思います。 

f:id:NeoCat:20180128210420p:plain

 

プログラム

 

gist.github.com

Qiのパケットを解析してみる

iPhoneにも 8 / X でQiが搭載されました。

Qiでは受電機器から充電器に対して通信を行うことで、充電の開始/停止や電力の調整を行いながら動作しています。これにより異常があれば即時充電を止めるようになっているわけですね。

通信速度は2kbpsで、受電側の負荷を変化させることで充電側に電力の変化として通信データを伝える後方散乱変調という仕組みが使われています。詳しくは以下のデータシートなどが参考になります。

https://toshiba.semicon-storage.com/info/docget.jsp?did=14812&prodName=TC7763WBG

 

実際にどんな通信がなされているか気になったので、この通信パケットをキャプチャしてみました。

回路

続きを読む

ESP8266でNAT(NAPT)でWi-Fiを中継する

ESP8266(ESP-WROOM-02)を最近よく使っています。

ESP8266を使ったセンサを、Wi-Fiアクセスポイントから遠い、直接電波の届きにくい場所に置きたかったため、ESP8266をもう一つ使ってWi-Fiの到達範囲を拡張する中継機にした(といってもブリッジではなくNAPTルータ)という話です。


とその前に軽くESP8266について紹介しておくと…(もう知ってる方は読み飛ばしてください)

ESP8266の紹介

ESP8266は無線LANモジュールです。購入した状態では外部のマイコンからATコマンドで制御できます。が、LX106という32bit MCUを搭載しており、このモジュール単体でArduino IDEからプログラミングして動作させることが可能です。この方が圧倒的に便利なので、私自身はATコマンドで使ったことはありません。
しかも安価(単体なら400円台)ですし、ESP-WROOM-02技適も取得していて安心して使え、AVRと比べて高性能(CPUクロック80〜160MHz, 4MBフラッシュ, 36KB RAM)。アナログ入力も1ch搭載していて、センサ・アクチュエータ等を簡単にネットワーク接続できるので非常に便利です。


Arduinoとして利用する方法は下記などが参考になります。
技適済み格安高性能Wi-FiモジュールESP8266をArduinoIDEを使ってIoT開発する為の環境準備を10分でやる方法 - Qiita


なお開発時はモジュール単体よりも、下記のようなブレークアウトボード・開発ボードを購入するのが良いでしょう。特にESPr DeveloperはUSBを接続するだけでいきなり開発が始められ、シリアル変換や書き込みモード設定など気にしなくて済むので非常に楽です(お値段はしますが)。
ESP-WROOM-02ピッチ変換済みモジュール《フル版》 - スイッチサイエンス
ESPr® Developer(ピンソケット実装済) - スイッチサイエンス


ちなみにシリアルでスケッチを書き込むと、ファームウェアが大きいため*1転送に少し時間がかかりますが、Wi-Fi経由で高速にアップロードすることも可能です。
ESP-WROOM-02 + ArduinoOTAでスケッチのWiFi経由アップロード - Qiita


他にもLua等でもプログラミングが可能なファームウェアもあります。
ESP8266(ESP-WROOM-02)自分的まとめ - 半空洞男女関係

Wi-Fiのモード

ESP8266は、自身がWi-FiのアクセスポイントになるWIFI_APモード、既設のWi-Fiアクセスポイントに接続するWIFI_STAモードの両方をサポートしています。
しかしこれ以外にWIFI_AP_STAという、他のアクセスポイントに接続しつつ、同時に自分もアクセスポイントとして他のクライアントからのWi-Fi接続を受け付けるということができます。(ちなみにArduino化した直後のデフォルトではこのモードになっているため、明示的にモード指定していないと ESP_xxxxxx というSSIDが発信されています。APのON/OFFやSSID等の設定はフラッシュに不揮発に保存され、前回の値が電源ONに設定されます。)


モードの切り替えは以下のようにWiFi.mode()を使用します。

const char *ssid = "ParentAP";
const char *password = "********";

const char *ap_ssid = "ESPap";
const char *ap_password = "ESPap_password";
...
  WiFi.mode(WIFI_AP_STA);  // モード設定

  WiFi.softAP(ap_ssid, ap_password);  // APのSSID・パスワード設定
  IPAddress myIP = WiFi.softAPIP();   // APとしてのIPアドレスを取得。デフォルトは 192.168.4.1 ?
  Serial.print("AP IP address: ");
  Serial.println(myIP);

  WiFi.begin(ssid, password);  // 別のAPに接続


子端末になるESP8266では、上記のap_ssidに対してWiFi.begin()で接続することになります。こうすると以下のような接続状態になります。

 親AP(192.168.10.1) <=> (192.168.10.x) ESP8266 (AP: 192.168.4.1) <=> (192.168.4.y)子端末

なお、親と子で別のサブネットのアドレスになっていないと(少なくとも親ネットワーク側からは)通信不能となるのでご注意を。


IPアドレスDHCPで自動的に設定されます(静的に指定もできます)。
また、ESP8266WiFiMultiを使って複数の接続先SSIDを指定しておけば、接続できる方に自動接続するということも可能です。詳しくはWiFiMultiというサンプルスケッチを見てください。

Wi-Fi中継の方法

親APとクライアントに同時接続できるということは、Wi-Fi中継機として使えるのでは?と思うわけですが、そのままでは中継機能(IP_FORWARD)は無効化されており、SDKに含まれるIPスタック(lwip)の設定を変更する必要があります。

参考: ESP8266 lwip IP_FORWARD/routing - ESP8266 Developer Zone


これにより、親APのネットワークとESP8266 APのネットワークの間でパケットが転送されるようになります。しかし、これだけでは親APやそのネットワーク内の各端末に「192.168.4.0/24 にアクセスするには 192.168.10.x をルータとして使う」というルーティング情報を設定しないと、通信が行えません(子端末からのパケットはESP8266がデフォルトゲートウェイになるので設定しなくとも親APのネットワークに出て行けますが、その応答パケットが戻っていくためのルーティング情報を設定しないと応答が得られません)。


この設定を不要にする方法として、普通のルータで使われている方式としてNAT(正確にはNAPT)があります。子端末から親ネットワークに出ていく際にソースアドレスをESP8266のもの(192.168.10.x)に書き換えておき、逆にこれに対する応答パケットが来た際には適切に子端末宛てに書き換えるというものです。(戻り先を特定するためにはセッション管理が必要になります。)


なお他にネットワークブリッジとして機能させることも考えられますが、こちらは色々ルーティングに改造を加えないと難しい気がして手を出していません。

NATを実装する

esp8266のSDKのlwipをざっと眺めたところ、NATの機能はないようでしたので、自分で実装してみました。


変更の内容はGitHubに置いてあります。
https://github.com/NeoCat/esp8266-Arduino/commit/4108c8dbced7769c75bcbb9ed880f1d3f178bcbe
上記のバグ修正コミット
https://github.com/NeoCat/esp8266-Arduino/commit/634dfb5f60e902c681c95712bc7455e24773667d


ボードマネージャ等でESP8266のライブラリを導入している場合は、以下のパッチを当てることで対応できます。
https://gist.github.com/NeoCat/da3c141813980edaa256ad351cab3a2c (バグ修正コミットを含む差分)


上記からRaw(またはDownload ZIP)でパッチを適当な場所に保存し、Macであれば端末から

$ cd ~/Library/Arduino15/packages/esp8266
$ patch -p1 < (保存した場所)/0001-Adds-NAPT-and-port-mapping-functionality-to-esp8266-.patch

とすればパッチを適用できます。適用後、lwipを再コンパイルする必要があります。

$ cd hardware/esp8266/2.3.0/tools
$ ln -s ../../../../tools/xtensa-lx106-elf-gcc/1.20.0-* xtensa-lx106-elf
$ cd sdk/lwip/src
$ make && make release  #=> intalled to ../../lib/liblwip_gcc.a


パッチを適用したら、Arduinoのスケッチ内で以下のようにすることでNATを有効化できます。

#define IP_PROTO_TCP     6
#define IP_PROTO_UDP     17
// SDKに追加した関数のプロトタイプ宣言
extern "C" {
  void ip_napt_enable(unsigned long addr, int enable);
  void ip_portmap_add(byte proto, unsigned long maddr, unsigned short mport,
                      unsigned long daddr, unsigned short dport);
  bool ip_portmap_remove(byte proto, unsigned short mport);
}
...
    // WiFi.begin等の後で
    ip_napt_enable(WiFi.softAPIP(), 1);

これ以降、子端末からのTCP/UDP/ICMP(pingのみ)のパケットは親ネットワークにアドレス変更の上で転送され、応答も対応付けてアドレス変換されます。
なおTCP, UDPの送信元ポート番号(1024以上)も重複を避けるために変換対象になります。


親ネットワークから子端末のTCP/UDPサーバにアクセスするためには、ESP8266の特定ポートを子端末にマッピングする必要があります。このためにはスケッチから、

        ip_portmap_add(IP_PROTO_TCP, WiFi.localIP(), 8080,
                                     IPAddress(192,168,4,3), 80);

のように、対象プロトコル(TCPまたはUDP)、ESP8266上のマッピングするポート、転送先となる子端末のIPアドレス、ポートを指定します。
この例では、親ネットワークから 192.168.10.x:8080 にアクセスすると、子端末192.168.4.3の80番ポート(Webサーバ)に接続できます。
ポートマッピングを解除するには、以下のようにします。

        ip_portmap_remove(IP_PROTO_TCP, 8080);

なお実装上、1つの宛先(アドレス+ポート)に対しては1つのポートしかマッピングできず、同一の宛先を複数ポートにマップすると動作がおかしくなるのでご注意ください。

制限事項

  • NATテーブルがメモリ(グローバル変数領域)を消費する。エントリ数は512となっていますが、これだと12KB消費します。子端末がせいぜい1,2セッションくらいしか張らないならこんなに要らないので、パッチ中の IP_NAPT_MAX を32等に小さくすれば、メモリを節約できます。(エントリはセッション切断後もタイムアウトするまでしばらく残るので、多めにしないと新たな接続ができなくなります。)逆にPC等をクライアントにしてしまうと、512でギリギリ足りるかどうかといったところです。
  • そんなに速度は出ません。単一TCPセッションのみで測ってみると1MB/s出たら良い方という感じです。センサーデータを送るくらいなら十分すぎますが、PCとかスマートフォンを接続するのには向かないでしょう。
  • TCPセッション管理がやや適当。TCPシーケンス番号を管理していない
  • ICMPパケットのping以外のメッセージに対応していない
  • 1つの宛先(アドレス+ポート)あたり1つのポートマッピングしかできない(NAPTエントリの数を節約するため)
  • AS-ISなので、どこか何かバグってるかも?(言い訳)

*1:ネットワークスタック等のファームウェアも含まれているため

USB直結のスクリーンセーバーブロッカー

USB直結スクリーンセーバーブロッカーを作ったツイートのまとめです。

なおArduino互換のため、Arduino IDEでボードの種類としてArduino Leopardを選択すればプログラムの書き込みが可能でした。

スマートメーターからリアルタイムに消費電力を取得する

少し前からスマートメーターの導入が始まっています。東電は2020年度末までに一般家庭などへの導入を完了させるそう。
http://www.tepco.co.jp/smartmeter/index-j.html


東電の場合、電力メーター情報発信サービス(Bルートサービス)を申し込むと、優先的にスマートメーターに交換してくれます。特に費用はかかりません。スマートメーターが設置されるとでんき家計簿から30分毎の電力使用状況グラフ(0.1kWh単位)を見ることができるようになりますが、加えて上記サービスに申し込むと、スマートメーターと家庭内のHEMS機器との間(これをBルートと言います)で通信を行うことが可能になり、リアルタイムで詳細な電力使用状況(瞬時電力値(W)、瞬時電流値(A)など)が取得できるようになります。


利用にあたってHEMS機器やサービスの導入に費用がかかるのがネックですが、HEMSに使われている無線通信方式(Wi-SUN)やプロトコル(ECHONET Lite)はオープンな規格であり対応する通信モジュールも一般販売されているため、自作の機器でもBルートで通信を行って電力使用状況の取得ができるはずです。


というわけで瞬時電力値の取得に挑戦してみました。


作ったのは、7セグにリアルタイムに瞬時電力(W)が表示されるというもの。シリアルでPCからも値を読み取れます。

まずはBルートサービスに申し込み

自宅の場合、スマートメーターへの交換は申し込んで1週間ほど(週末を指定したので)でやってくれました。要立会い、作業は15分くらい、そのうち5分くらい停電しました。交換後、翌々週くらいにBルート接続用のIDとパスワードが送られてきました。トータル3週間ほど。

使用した機器

その間にWi-SUN関係の通信機器をこちらなどで購入しました。→ https://www.zaikostore.com/zaikostore/itstoreWireless?cid=3#c2

 (DT101EACV101の方が簡単に使えますが、購入できない場合は BP35A7A でもOKと思います。)


BP35A1は電圧が3.3Vで、5Vには対応していませんので、USBシリアルやArduinoも3.3Vに合わせる必要があります(または適切に分圧やレベル変換が必要です)。


シリアル対応7セグは手持ちの古い版なので、真似する場合は適当に別のものに置き換えた方がいいでしょう。(その場合、下記のスケッチの dig というキーワードが出てくる部分の書き換えも必要でしょう。)


なおPCで使う場合は、WSR35A1-00 というUSBシリアルとBP35A1を一体化したUSBデバイスも売られているので、これが一番簡単でしょう。


Wi-SUN通信モジュールBP35A1のデータシート等は以下にあります。
http://micro.rohm.com/jp/download_support/wi-sun/

追記

Arduinoではなく、RaspberryPiなどのLinuxボードを使う場合はこちらの記事もみてください。
スマートメーターの瞬時電力や履歴をWebブラウザで見る - Okiraku Programming

事前準備

BP35A1はDT101EACV101を噛ませ、さらに XBeeの載る「C基板」上に乗せました。
C基盤にはArduino Pro MiniのUSBシリアル接続と同配置になるようピンヘッダに配線し、PCと簡単につなげるようになっています。


BP35A1とArduinoは直接シリアル通信で利用できます。ただ、Arduino Pro Miniは一系統しかハードウェアシリアル(UART)を持っていないので、ここにBP35A1を繋いでしまうとPCとシリアル通信ができなくなり、デバッグ等が困難になってしまいます。そこで、BP35A1はArduinoのソフトウェアシリアルを使って接続することにします。*1

ソフトウェアシリアルの欠点として、半二重なので送信と受信が同時にできない、通信速度が高速だと不安定になりやすいという問題があります。BP35A1のデフォルト設定は115200bpsなのですが、3.3V/8MHzのArduinoでは文字が化けたりして正常に動作しませんでした。そこで,事前に通信速度の設定を9600bpsにし、さらに文字間にウェイト400μsを入れるように変更しておきます。


これは、BP35A1をPCにUSBシリアルの3.3V, GND, TX<->RXの4本で接続して、ターミナルソフトを115200bps設定で起動します。
Arduino IDEのシリアルモニターでも改行コードをCRにすれば通信できますので、これが手っ取り早いでしょう。

WUART 43

というコマンドを実行すればシリアル設定はOKです。一度設定すると内蔵のフラッシュに保存され、電源を再投入すると以後は9600bpsでの通信になります。


PCに直接繋いで利用する場合はこのシリアル設定は不要です。また、設定変更後にデフォルトの設定に戻したい時は WUART 00 を実行すればOKです。

配線

こんな感じでブレッドボード上に配線しました。

スケッチ

ArduinoのスケッチはGist上に置いてあります。
https://gist.github.com/NeoCat/72390f357d3ee2ce339d

Bルート用のID・パスワードをスケッチの先頭のID, PASSWORDに設定し、Arduinoに書き込みます。

//#define DEBUG

と書かれた部分のコメントアウトを外すと、詳細な通信状況がシリアルに出力されるようになります。


概ね以下の処理を行っています。

  • setup
    1. 初期設定(バージョン取得、ASCII出力モードへの設定、ID・passwordの設定)
    2. PANのスキャン(スマートメーターとの通信チャンネルやIPv6アドレスなどの取得)
    3. スマートメーターとの通信セッションを確立(ID・passwordによるPANA認証。以降、自動的に暗号化通信になります)
    4. ECHONET Lite (UDP port 3610)を使ってスマートメーターの状態を確認
      • EPC 0x80 (動作状態) = 0x30: ON、 0x31: OFF
      • EPC 0x88 (異常状態) = 0x41: 異常あり、 0x42: 異常なし
  • loop
    1. 3秒ごとにスマートメーターから瞬時電力(EPC 0xe7)を取得,シリアルおよび7セグに表示


ちなみに自宅のスマートメーターは最初なぜか異常状態になっており、セッションの確立は出来るものの、EPC 0x88 = 0x41 (異常あり)を返し、かつ瞬時電力や累積電力なども取得しようとすると"非対応"のエラーを返してきていました。電力会社のスマートメーター推進室ということろに問い合わせてEPC等の状態を伝えたところ、遠隔でスマートメーターの通信機能のログ確認やリブートを行ってくれ、その結果無事に瞬時電力等が取れるようになりました。言ってみるものですね。

参考資料

スケッチ

*1:Arduino LeonardoであればUSB用とハードウェアシリアルが分かれているので簡単だったかも。