LoginSignup
16
15

More than 3 years have passed since last update.

M5Stack BasicでWiFi GeolocationをMap表示する

Last updated at Posted at 2020-04-27

M5Stack Basicを使って、WiFiスキャンで得られるMACアドレス情報を元にGoogle Map上に現在位置をプロットするサンプルを作ってみました。
IMG_4891.jpg

WiFiスキャンでAPリストを取得する

まずは、WiFi networksをスキャンし、周辺のアクセスポイントを取得します。WiFi.hをインクルードしたうえでWiFi.scanNetworks();でWiFiネットワークをスキャンします。戻り値として見つかったWiFiネットワークの数を返します。その後、スキャンされたネットワーク数でループし、channel(i)でチャネル、RSSI(i)で受信レベル、SSID(i)でSSIDを取得できますが、ここでは、MACアドレスを取得したいので、WiFi.BSSIDstr(i)を使います。

wifiscan.c
int n = WiFi.scanNetworks();  //ネットワークをスキャンして数を取得
for (int i = 0; i < n; i++) {
  String bssid = WiFi.BSSIDstr(i);  //BSSID(MACアドレス)を文字列で取得
}

Google Geolocation APIで位置情報を取得

得られたMACアドレスのリストを元に、Google Geolocation APIで位置情報(緯度経度)を取得します。
位置情報の取得は、下記のURLにPOSTすることになります。
https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_API_KEY
POST時に先ほどのMACアドレスのリストをJSONで渡すことになります。例えば、このようなJSONです。

reqest_geolocation.json
{
  "considerIp":"false",
  "wifiAccessPoints":[
    {"macAddress":"AA:AA:AA:AA:AA:AA"},
    {"macAddress":"BB:BB:BB:BB:BB:BB"},
    {"macAddress":"CC:CC:CC:CC:CC:CC"}
  ]
}

ArduinoでJSONを取り扱うにはArduinoJsonが便利です。

ArduinoJsonライブラリのインストール

ArduinoJsonライブラリを使用するためには、Arduino IDEのライブラリマネージャでAruinoJsonをインストールしておく必要があります。
image.png

JSONシリアライズ

ArduinoJsonでは、メモリ消費を極力抑えるために生成するJSONのサイズを指定しておく必要があります。上記のように3つのMACアドレスをJSONコードにする場合に必要なサイズを調べる必要がありますが、ArduinoJson Assistantを使いましょう。下図のように、Input欄に想定するJSONコードを入力すると、右側のMemory pool sizeにメモリ確保の記載例が出てきます。
image.png
今回の場合、3つの配列がそれぞれ1つずつの要素を持ち(wifiAccessPoints)、considerIpとあわせて2つの要素になるため、JSON_ARRAY_SIZE(3) + 3*JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2)分の確保が必要になります。更に各要素の文字数を想定し、追加で121バイトを確保しておく必要があります。
下記にJSONシリアライズのサンプルを記載します。(あまり多くのMACアドレスを送るとコスト的にも無駄な場合もありますので、MAX_APで指定しておきます)

jsonserialize.c
const int MAX_AP = 3
int n = WiFi.scanNetworks();
const size_t capacity = JSON_ARRAY_SIZE(MAX_AP) + (MAX_AP)*JSON_OBJECT_SIZE(1) + JSON_OBJECT_SIZE(2) + 34 + (MAX_AP * 29);
DynamicJsonDocument doc(capacity);

doc["considerIp"] = "false";
JsonArray wifiAccessPoints = doc.createNestedArray("wifiAccessPoints");
for (int i = 0; i < n; i++)  {
  String bssid = WiFi.BSSIDstr(i);
  JsonObject wifiAP = wifiAccessPoints.createNestedObject();
  wifiAP["macAddress"] = WiFi.BSSIDstr(i);
  if (i + 1 == MAX_AP) break;
}
serializeJson(doc, json);

Google Geolocation APIにPOST

リクエストJSONが出来上がれば、Google Geolocation APIにPOSTします。
HTTPClientを使ったサンプルは以下の通りです。json_requestは先ほどシリアライズしたJSONコードです。

google_geolocation.c
HTTPClient http;
http.begin("https://www.googleapis.com/geolocation/v1/geolocate?key=YOUR_API_KEY");
int status_code = http.POST(json_request);
if (status_code == 200) {
  String json_response = http.getString();
  const size_t capacity = 2*JSON_OBJECT_SIZE(2) + 30;
  DynamicJsonDocument doc(capacity);
  deserializeJson(doc, json_response);
  float lat = doc["location"]["lat"];
  float lng = doc["location"]["lng"];
  float accuracy = doc["accuracy"];
}
http.end();

ここで、またしても、ArduinoJsonを使って、Google Geolocation APIから帰ってきたJSONをデシリアライズしています。
これで、緯度経度(と精度)が取得できました。

Google Static Mapで現在位置をプロットする

最後にGoogle Map上に位置をプロットすることになりますが、M5Stackの画面上に静的画像として描画したいので、Map Static APIを使います。このAPIは下記URLに必要なパラメータを渡せば、jpeg等の画像ストリームを取得できます。https://maps.googleapis.com/maps/api/staticmap?[parameters]&key=YOUR_API_KEY
指定可能なパラメータは、下記のようなものがあります。(参考)

パラメータ 説明
center 地図の中心座標。「緯度,経度」または「住所」「建物名」なども指定可能です。 35.5000,136.5000、六本木ヒルズ
zoom マップのズームレベルを決定します。1~22を指定できます。 15
size 地図画像のサイズ(幅×高さ)を決定します。M5Stackのディスプレイを考えると320x240が最適です。 320x240
format 画像の形式を決定します。gif、jpg、png を指定できます。 jpg
maptype 地図のタイプを決定します。roadmap, satellite, hybrid, terrain を指定できます。 roadmap

Static Mapの取得に成功すれば、getStreamPtr()で画像ストリームを読み込んでいき、最後にM5.Lcd.drawJpg(...)で画面描画をすることとなります。
以下にサンプルを示します。

drawMap.c
HTTPClient http;
http.begin(url); //urlはパラメータ込みのGoogle Static APIへのURL
int status_code = http.GET();
if (status_code == 200) {
  int len = http.getSize();
  if (len > 0) {
    WiFiClient * stream = http.getStreamPtr();
    // read all data from server
    uint8_t* p = buff;
    int l = len;
    while (http.connected() && (l > 0 || len == -1)) {
      // get available data size
      size_t size = stream->available();
      if (size) {
        int s = ((size > sizeof(buff)) ? sizeof(buff) : size);
        int c = stream->readBytes(p, s);
        p += c;
        if (l > 0) {
          l -= c;
        }
      }
    }
  }
  M5.Lcd.drawJpg(buff, len);
}
http.end();

M5StackのdrawJpg関数については、こちらを参考にしてください。

サンプルコード

今回の全サンプルコードはこちら(GitHub)上にあげておきます。Google Static Map用のAPI KeyやWiFi接続用のSSID、キーはご自身の環境にあわせてください。
image.png

16
15
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15