新しいことにはウェルカム

技術 | 電子工作 | ガジェット | ゲーム のメモ書き

ESP8266でAmazon Dash Buttonのボタンプッシュを検知する方法

一時、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」に記載されています。

  1. ダウンロード
    A full functional WiFi Repeater で[Clone or download]-[Download ZIP]して解凍
  2. ソフトインストール
    https://www.espressif.com/en/support/download/other-tools からバイナリ書き込みツール「Flash Download Tools」をダウンロードしてインストール
  3. バイナリロード
    解凍したファイルの「firmware」にバイナリ「0x00000.bin」「0x10000.bin」があるのでそれを「Flash Download Tools」を使って書き込みます。
    (製品によってESP8266のFLASH SIZEが異なるので、使っているESP8266に合わせます)

NATセットアップ

  1. ESP8266セットアップ
    起動するとNATになっているので 詳細は A full functional WiFi Repeater の「Basic Web Config Interface」に従って、ブラウザでアクセスして設定します。
    (ここではデフォルトの設定を用いました)
  2. 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ソフトを使ってようやく解決しました。結構回り道してしまいましたが、過程で色々勉強になったので良しとします…。