一時、Amazon Dash Buttonをハックして、任意のIoTボタンに改造するのが流行りました。Amazon Dash Buttonのボタンプッシュの検知はPCやRaspberryPiでもできるのですが、それをESP8266で行う方法メモです。
Amazon Dash Button ハックの仕組み
Amazon Dash Button ハックの仕組みについておさらいしておきます。 仕組みについては Amazon Dash ButtonをただのIoTボタンとして使う に詳しく書いてあります。
改めて説明すると、Amazon Dash Buttonは、ボタンが押されると、電源が入り、WiFiに接続し、インターネットを介してAmazonのサーバーにアクセスし、実際の注文を行います。
しかし、注文に絡むデバイスのため、本当にハックするのは難しく、改造等は行わないで、Amazon Dash ButtonがWiFiに接続したのを検知して、ボタンがプッシュされたとみなす、間接的なハックを行います。
ボタンを押すとAmazon Dash ButtonがWiFiに接続するようにするには、Amazon Dash ButtonにWiFiのアクセスポイント情報が登録されている必要があります。
そして、その登録のために、注文商品選択直前までAmazon Dash Buttonのセットアップを進め、そこでセットアップを中断しておきます。
そこまでセットアップが進んでいると、WiFiアクセスポイントの登録は完了していて、以後は、ボタンを押すとWiFiアクセスポイントに接続するようになります。
WiFi接続検知の方法
WiFi接続検知の方法は2つあります。
1つ目
Amazon Dash ButtonはWiFiへ接続の際、自分のMACアドレスをブロードキャストします。その仕組を利用して、ボタン検出する側の機器がWiFiにブロードキャストされるMACアドレスをウォッチして検出する方法です。
2つ目
ボタン検出する側の機器自らがWiFiのアクセスポイントとなり、Amazon Dash Buttonに登録アクセスポイントして登録する方法です。そして、Amazon Dash Buttonから、自らのアクセスポイントへのコネクションを検出します。
手順がシンプルなので、多くのハックは1つ目の方法を用いています。
1つ目の場合、WiFiアクセスポイントが可動している場合、Amazon Dash Buttonが実際にAmazonに接続されてしまいます。Amazon Dash Buttonのセットアップが完了していないので、それによって何か注文が発生することは無いのですが、接続する度に、AmazonからAmazon Dash Buttonのセットアップが完了していない旨の通知が届きます。
インターネットには接続せず、ESP8266とAmazon Dash Button間の閉じた環境のみで通信を行わせたかったので、ここでは2つ目の方法を用いました。
手順
ESP8266をアクセスポイントとして登録
ESP8266はWiFiを受けて、WiFi発信することができるので、仕様上はWiFiからWiFiへの中継ポイント、つまりNATになることができます。
しかし、ESP8266でNAT(NAPT)でWi-Fiを中継する に記載されているように、ESP8266のライブラリではその機能をOFFにされているため、一旦ライブラリを修正してその機能をONにしてライブラリを再コンパイルし、そのライブラリを使ってNATを実装し可動する必要があります。
そして、ESP8266をNATとして可動させ、Amazon Dash ButtonをそのNATを介してセットアップして、ESP8266をAmazon Dash Buttonに登録する必要があります。
幸い、A full functional WiFi Repeater にESP8266をNATにするソースとバイナリがありますので、今回はそのバイナリをインストールして使用しました。
NATインストール
詳細は A full functional WiFi Repeater の「Building and Flashing」に記載されています。
- ダウンロード
A full functional WiFi Repeater で[Clone or download]-[Download ZIP]して解凍 - ソフトインストール
https://www.espressif.com/en/support/download/other-tools からバイナリ書き込みツール「Flash Download Tools」をダウンロードしてインストール - バイナリロード
解凍したファイルの「firmware」にバイナリ「0x00000.bin」「0x10000.bin」があるのでそれを「Flash Download Tools」を使って書き込みます。
(製品によってESP8266のFLASH SIZEが異なるので、使っているESP8266に合わせます)
NATセットアップ
- ESP8266セットアップ
起動するとNATになっているので 詳細は A full functional WiFi Repeater の「Basic Web Config Interface」に従って、ブラウザでアクセスして設定します。
(ここではデフォルトの設定を用いました) - Amazon Dash Buttonセットアップ
上記で設定したESP8266のアクセスポイントにAmazon Dash Buttonがアクセスするようにして、Amazon Dash Buttonのハックにあるのと同様の、商品選択直前までのセットアップを行います。
ボタン検知プログラム
ESP8266のアクセスポイント(SSIDとパスワード)がAmazon Dash Buttonに登録されましたので、後はボタン検知のESP8266のプログラムを書いて転送し実行するだけです。
サンプルプログラムは下記になります。
- Amazon Dash ButtonがESP8266のアクセスポイントに接続すると、
onStationConnected()
コールバック関数が呼ばれますので、これをもってボタンプッシュ検知としています - DNSサーバーは必須ではないのですが、あった方がAmazon Dash Buttonがリトライをあきらめるのが早い(次のボタン検知可能までの間隔が短くなる)ようなので入れてあります
#include <ESP8266WiFi.h> #include <DNSServer.h> const char *ssid = "xxxx"; const char *password = "xxxxxxxxxx"; const byte dns_port = 53; IPAddress apIP(192, 168, 4, 1); IPAddress subnet(255, 255, 255, 0); DNSServer dnsServer; WiFiEventHandler stationConnectedHandler; byte bConnection = 0; int push_count= 0; int send_frame = 0; const int max_send_frame = 200000; byte pin_a = 16; byte pin_b = 14; void setup() { delay(1000); Serial.begin(115200); Serial.println(); Serial.println("Configuring access point..."); WiFi.softAPConfig(apIP, apIP, subnet); WiFi.softAP(ssid, password); stationConnectedHandler = WiFi.onSoftAPModeStationConnected(&onStationConnected); IPAddress myIP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(myIP); dnsServer.start(dns_port, "*", apIP); pinMode(pin_a, OUTPUT); pinMode(pin_b, OUTPUT); digitalWrite(pin_a, LOW); digitalWrite(pin_b, LOW); } void onStationConnected(const WiFiEventSoftAPModeStationConnected& evt) { bConnection = 1; send_frame = 0; ++push_count; Serial.print("Station connected: "); Serial.print(macToString(evt.mac) + " -> "); Serial.println(push_count); digitalWrite(pin_a, HIGH); digitalWrite(pin_b, HIGH); Serial.println("pin HIGH"); } void pinOut(){ if(bConnection){ if(send_frame<max_send_frame){ ++send_frame; } else { bConnection = 0; digitalWrite(pin_a, LOW); digitalWrite(pin_b, LOW); Serial.println("pin LOW"); } } } void loop() { dnsServer.processNextRequest(); pinOut(); } String macToString(const unsigned char* mac) { char buf[20]; snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); return String(buf); }
感想等
当初、ESP8266とAmazon Dash Buttonの両方がWiFiアクセスポイントに接続しないと、ESP8266からAmazon Dash ButtonのMACアドレスは取得できないと思っていたのですが、WiFiアクセスポイントが無くても取得できるんですね。
それを知らずに、ESP8266とAmazon Dash Buttonが直接する方法を模索して、今回のやり方になりました。
ESP8266のサンプルプログラムには、HTTPクライアントやアクセスポイントがあるので、直接通信はすぐに出来そうかと思い、自分で実装しようとしてみたのですが中々うまくいかず、NATソフトを使ってようやく解決しました。結構回り道してしまいましたが、過程で色々勉強になったので良しとします…。