基于WiFi定位的谷歌地图显示
本帖最后由 vany5921 于 2020-5-29 10:58 编辑通过WiFi扫描获取AP列表
首先,扫描WiFi networks,取得周边的访问点。在#include <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);
}
通过Google Geolocation API获取位置信息
根据获得的MAC地址列表,通过Google Geolocation API获取位置信息(纬度经度)获取位置信息时,请在以下URL中进行POST。
https://www.googleapis.com/geolo ... te?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中,为了极力抑制内存消耗,必须预先指定JSON的尺寸。如上所述,将3个MAC地址设为JSON代码时需要调查所需的尺寸,请使用ArduinoJson Assistant。如下图所示,输入Input栏设想的JSON代码后,右侧的Memory pool size中就会出现存储大小。
在当前情况下,3个列表各有1个元素(wifiAccess Point s),因为和consierIp一起成为2个元素,所以需要确保JSON ARRAY SIZE(3)+3*JSON OBJECT SIZE(1)+JSON OBJECT SIZE(2)的大小。此外,还需要设想各元素的字符数,追加确保121字节。
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代码。
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?&key=YOUR_API_KEY
可指定的参数有以下几种:
参数说明示例
center地图中心坐标也可以指定纬度、经度或地址、建筑名等 35.5000,136.5000
zoom确定映射的缩放级别。可指定1~2215
size确定地图图像的大小(宽度×高度)。考虑到M5Stack的屏幕,最适合320x 240320x240
format确定图像格式。可以指定gif、jpg、pngjpg
maptype 确定地图类型。可以指定roadmap、satellite、hybrid、terrainroadmap
通过http.getStreamPtr()取得画面,通过M5.Lcd.drawjpg()绘制图像
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();
完整示例代码
#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 = { 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;
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;
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(" size: %d\n", len);
if (len > 0) {
WiFiClient * stream = http.getStreamPtr();
Serial.printf(" 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(" read: %d\n", c);
if (l > 0) {
l -= c;
}
}
}
}
Serial.println("");
Serial.println(" connection closed.");
M5.Lcd.drawJpg(buff, len, 0, 20);
} else {
Serial.println(" Failed");
}
http.end();
}
页:
[1]