基于WiFi定位的谷歌地图显示-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 2159|回复: 0

基于WiFi定位的谷歌地图显示

[复制链接]
发表于 2020-5-29 10:57 | 显示全部楼层 |阅读模式
本帖最后由 vany5921 于 2020-5-29 10:58 编辑

https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_172313_bf3b0ccd-b7bc.jpeg

通过WiFi扫描获取AP列表

首先,扫描WiFi networks,取得周边的访问点。在#include <WiFi.h>的基础上使用WiFi.scanNetworks();扫描WiFi网络。返回值为找到的WiFi网络的数量。然后,以扫描的网络数进行循环,在channel(i)中以信道、RSSI(i)中以接收电平、SSID(i)来取得SSID,但是在这里想要取得MAC地址,WiFi.BSSIDstr使用(i)。
[mw_shl_code=c,true]wifiscan.c
int n = WiFi.scanNetworks();
for (int i = 0; i < n; i++) {
  String bssid = WiFi.BSSIDstr(i);
}[/mw_shl_code]
通过Google Geolocation API获取位置信息
根据获得的MAC地址列表,通过Google Geolocation API获取位置信息(纬度经度)获取位置信息时,请在以下URL中进行POST。
https://www.googleapis.com/geolo ... te?key=YOUR_API_KEY
POST时将之前的MAC地址列表通过JSON发送。比如,像这样的JSON

[mw_shl_code=c,true]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"}
  ]
}[/mw_shl_code]
在Arduino处理JSON时,ArduinoJson很方便。
在ArduinoJson中,为了极力抑制内存消耗,必须预先指定JSON的尺寸。如上所述,将3个MAC地址设为JSON代码时需要调查所需的尺寸,请使用ArduinoJson Assistant。如下图所示,输入Input栏设想的JSON代码后,右侧的Memory pool size中就会出现存储大小。
https___qiita-image-store.s3.ap-northeast-1.amazonaws.com_0_172313_f05505aa-2d50.png
在当前情况下,3个列表各有1个元素(wifiAccess Point s),因为和consierIp一起成为2个元素,所以需要确保JSON ARRAY SIZE(3)+3*JSON OBJECT SIZE(1)+JSON OBJECT SIZE(2)的大小。此外,还需要设想各元素的字符数,追加确保121字节。

[mw_shl_code=c,true]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);[/mw_shl_code]

Google Geolocation API POST
提取JSON完成,将在Google Geolocation API上进行POST,使用HTTPClient的示例如下,json request是刚才格式化的JSON代码。

[mw_shl_code=arduino,true]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();[/mw_shl_code]
现在,再次使用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的屏幕,最适合320x 240
320x240
format
确定图像格式。可以指定gif、jpg、png
jpg
maptype
确定地图类型。可以指定roadmap、satellite、hybrid、terrain
roadmap

通过http.getStreamPtr()取得画面,通过M5.Lcd.drawjpg()绘制图像
[mw_shl_code=c,true]HTTPClient http;
http.begin(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();[/mw_shl_code]

完整示例代码
[mw_shl_code=arduino,true]#include <M5Stack.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>

String key = YOUR_API_KEY;
String host_geolocate = "https://www.googleapis.com/geolocation/v1/geolocate?";
String host_staticmap = "https://maps.googleapis.com/maps/api/staticmap?";
const size_t MAX_AP = 3;   //最大AP数量
float lat, lng;
int accuracy;
uint8_t buff[320 * 220] = { 0 };

void setup() {
  Serial.begin(115200);
  M5.begin(); //M5Stack初始化
  displayStatus("scan SSID");
  
  delay(100);

  connectWiFi();
}

void displayStatus(String state)
{
  M5.Lcd.setTextSize(2);
  M5.Lcd.fillScreen(BLACK); //填充背景
  M5.Lcd.setCursor(0, 0);   //文字显示位置
  M5.Lcd.println(state);
  delay(500);
}

void connectWiFi() {
  WiFi.mode(WIFI_STA);  //切换为STA模式
  WiFi.disconnect();    //Wi-Fi切断

  WiFi.begin("SSID", "pass");
  while (WiFi.status() != WL_CONNECTED) {
    displayStatus("WiFi Connected.");
    displayStatus("Press button A:");
  }
  delay(1000);
}

void loop() {
  M5.update();

  if (M5.BtnA.wasReleased()) {
    clickBtnA();
  }
}

void clickBtnA()
{
  displayStatus("Scanning...");
  String json_request = scanWiFiNetworks();
  displayStatus("Scan finishted.");
  if (json_request.length()) {
    if (getGeolocation(json_request)) {
      char pos[32];
      sprintf(pos, "Lat:%.4f Lng:%.4f", lat, lng);
      displayStatus(pos);
      displayMap();
    } else {
      displayStatus("Failed to obtain location");
    }
  }  
  delay(30000);
}

String scanWiFiNetworks()
{
  String json_request = "";
  
  int n = WiFi.scanNetworks();  //获取扫描的网络数量

  if (n == 0) {
    //没有发现wifi网络
    displayStatus("no networks found");
  } else {
    //发现wifi网络
    displayStatus(" networks found");
    json_request = serializeJsonRequest(n);
  }
  return json_request;
}

String serializeJsonRequest(int cnt_ap)
{
  Serial.print("AP count="); Serial.println(cnt_ap);
  String json;
  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 < cnt_ap; i++)  {
    String bssid = WiFi.BSSIDstr(i);
    Serial.print(i); Serial.print(" BSSID="); Serial.println(bssid);
    JsonObject wifiAP = wifiAccessPoints.createNestedObject();
    wifiAP["macAddress"] = WiFi.BSSIDstr(i);
    if (i + 1 == MAX_AP) break;
  }

  serializeJson(doc, json);
  Serial.println("request->"); Serial.println(json);
  return json;
}

bool getGeolocation(String json_request)
{  
  bool b = false;
  HTTPClient http;
  Serial.println(host_geolocate + "key=" + key);
  http.begin(host_geolocate + "key=" + key);
  int status_code = http.POST(json_request);
  Serial.printf("status_code=%d\r\n", status_code);
  if (status_code == 200) {
    String json_response = http.getString();
    Serial.println("respose->"); Serial.println(json_response);
   
    const size_t capacity = 2*JSON_OBJECT_SIZE(2) + 30;
    DynamicJsonDocument doc(capacity);
    deserializeJson(doc, json_response);
    lat = doc["location"]["lat"];
    lng = doc["location"]["lng"];
    accuracy = doc["accuracy"];

    b = true;
  }
  http.end();
  return b;
}

void displayMap()
{
  char pos[32];
  sprintf(pos, "%f,%f", lat, lng);
  String query = "center=" + String(pos)
                        + "&size=320x220&zoom=15&format=jpg-baseline"
                        + "&markers=size:mid|color:red|label:A|" + String(pos)
                        + "&key=" + key;
  String url = host_staticmap + query;
  HTTPClient http;
  http.begin(url);
  int status_code = http.GET();
  if (status_code == 200) {
    int len = http.getSize();
    Serial.printf("[HTTP] size: %d\n", len);
    if (len > 0) {
      WiFiClient * stream = http.getStreamPtr();
      Serial.printf("[HTTP] strm ptr: %x\n", stream);
      // 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;
          Serial.printf("[HTTP] read: %d\n", c);
          if (l > 0) {
            l -= c;
          }
        }
      }
    }
    Serial.println("");
    Serial.println("[HTTP] connection closed.");
    M5.Lcd.drawJpg(buff, len, 0, 20);
   
  } else {
    Serial.println("[HTTP] Failed");
  }
  http.end();
}[/mw_shl_code]

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 02:37 , Processed in 0.075643 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表