|
本帖最后由 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)。
[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中就会出现存储大小。
在当前情况下,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]
|
|