// // // #include "WifiLocation.h" #include "time.h" const char* googleApisHost = "www.googleapis.com"; const char* googleApiUrl = "/geolocation/v1/geolocate"; // GlobalSign CA certificate valid until 15th december 2021 #if defined ARDUINO_ARCH_ESP32 || defined ARDUINO_ARCH_ESP8266 static const char GlobalSignCA[] PROGMEM = R"EOF( -----BEGIN CERTIFICATE----- MIIESjCCAzKgAwIBAgINAeO0mqGNiqmBJWlQuDANBgkqhkiG9w0BAQsFADBMMSAw HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy MTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg U2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxTzEwggEiMA0GCSqGSIb3DQEBAQUA A4IBDwAwggEKAoIBAQDQGM9F1IvN05zkQO9+tN1pIRvJzzyOTHW5DzEZhD2ePCnv UA0Qk28FgICfKqC9EksC4T2fWBYk/jCfC3R3VZMdS/dN4ZKCEPZRrAzDsiKUDzRr mBBJ5wudgzndIMYcLe/RGGFl5yODIKgjEv/SJH/UL+dEaltN11BmsK+eQmMF++Ac xGNhr59qM/9il71I2dN8FGfcddwuaej4bXhp0LcQBbjxMcI7JP0aM3T4I+DsaxmK FsbjzaTNC9uzpFlgOIg7rR25xoynUxv8vNmkq7zdPGHXkxWY7oG9j+JkRyBABk7X rJfoucBZEqFJJSPk7XA0LKW0Y3z5oz2D0c1tJKwHAgMBAAGjggEzMIIBLzAOBgNV HQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJjR+G4Q68+b7GCfGJAboOt9Cf0rMB8G A1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl BggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp MCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g BDgwNjA0BgZngQwBAgIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y ZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAGoA+Nnn78y6pRjd9XlQWNa7H TgiZ/r3RNGkmUmYHPQq6Scti9PEajvwRT2iWTHQr02fesqOqBY2ETUwgZQ+lltoN FvhsO9tvBCOIazpswWC9aJ9xju4tWDQH8NVU6YZZ/XteDSGU9YzJqPjY8q3MDxrz mqepBCf5o8mw/wJ4a2G6xzUr6Fb6T8McDO22PLRL6u3M4Tzs3A2M1j6bykJYi8wW IRdAvKLWZu/axBVbzYmqmwkm5zLSDW5nIAJbELCQCZwMH56t2Dvqofxs6BBcCFIZ USpxu6x6td0V7SvJCCosirSmIatj/9dSSVDQibet8q/7UK4v4ZUN80atnZz1yg== -----END CERTIFICATE----- )EOF"; #endif String WifiLocation::MACtoString(uint8_t* macAddress) { uint8_t mac[6]; char macStr[18] = { 0 }; #ifdef ARDUINO_ARCH_SAMD sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", macAddress[5], macAddress[4], macAddress[3], macAddress[2], macAddress[1], macAddress[0]); #elif defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 sprintf(macStr, "%02X:%02X:%02X:%02X:%02X:%02X", macAddress[0], macAddress[1], macAddress[2], macAddress[3], macAddress[4], macAddress[5]); #endif return String(macStr); } WifiLocation::WifiLocation(String googleKey) { _googleApiKey = googleKey; } // Function to get a list of surrounding WiFi signals in JSON format to get location via Google Location API String WifiLocation::getSurroundingWiFiJson() { String wifiArray = "[\n"; int8_t numWifi = WiFi.scanNetworks(); if(numWifi > MAX_WIFI_SCAN) { numWifi = MAX_WIFI_SCAN; } #ifdef DEBUG_WIFI_LOCATION Serial.println(String(numWifi) + " WiFi networks found"); #endif // DEBUG_WIFI_LOCATION for (uint8_t i = 0; i < numWifi; i++) {//numWifi; i++) { //Serial.print("WiFi.BSSID(i) = "); //Serial.println((char *)WiFi.BSSID(i)); #ifdef ARDUINO_ARCH_SAMD byte bssid[6]; wifiArray += "{\"macAddress\":\"" + MACtoString(WiFi.BSSID(i,bssid)) + "\","; #elif defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 wifiArray += "{\"macAddress\":\"" + MACtoString(WiFi.BSSID(i)) + "\","; #else #error Only ESP8266 and SAMD platforms are supported #endif wifiArray += "\"signalStrength\":" + String(WiFi.RSSI(i)) + ","; wifiArray += "\"channel\":" + String(WiFi.channel(i)) + "}"; if (i < (numWifi - 1)) { wifiArray += ",\n"; } } #if defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 WiFi.scanDelete(); #endif wifiArray += "]"; #ifdef DEBUG_WIFI_LOCATION Serial.println("WiFi list :\n" + wifiArray); #endif // DEBUG_WIFI_LOCATION return wifiArray; } #if defined ARDUINO_ARCH_ESP32 || defined ARDUINO_ARCH_ESP8266 // Set time via NTP, as required for x.509 validation void setClock () { configTime (3600, 0, "pool.ntp.org", "time.nist.gov"); #ifdef DEBUG_WIFI_LOCATION Serial.print ("Waiting for NTP time sync: "); #endif time_t now = time (nullptr); while (now < 8 * 3600 * 2) { delay (500); #ifdef DEBUG_WIFI_LOCATION Serial.print ("."); #endif now = time (nullptr); } struct tm timeinfo; gmtime_r (&now, &timeinfo); #ifdef DEBUG_WIFI_LOCATION Serial.println (); Serial.print ("Current time: "); Serial.print (asctime (&timeinfo)); #endif } #endif //ARDUINO_ARCH_ESP32 || ARDUINO_ARCH_ESP8266 // Calls Google Location API to get current location using surrounding WiFi signals inf location_t WifiLocation::getGeoFromWiFi() { location_t location; String response = ""; #if defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32 setClock (); #ifdef ARDUINO_ARCH_ESP8266 #if (defined BR_BEARSSL_H__ && not defined USE_CORE_PRE_2_5_0) BearSSL::X509List cert (GlobalSignCA); _client.setTrustAnchors (&cert); #else _client.setCACert (reinterpret_cast(GlobalSignCA), sizeof (GlobalSignCA)); #endif #endif // ARDUINO_ARCH_ESP8266 #endif #ifdef ARDUINO_ARCH_ESP32 _client.setCACert(GlobalSignCA); #endif if (_client.connect(googleApisHost, 443)) { #ifdef DEBUG_WIFI_LOCATION Serial.println("Connected to API endpoint"); #endif // DEBUG_WIFI_LOCATION } else { #ifdef DEBUG_WIFI_LOCATION Serial.println("HTTPS error"); #endif // DEBUG_WIFI_LOCATION return location; } String body = "{\"wifiAccessPoints\":" + getSurroundingWiFiJson() + "}"; #ifdef DEBUG_WIFI_LOCATION Serial.println("requesting URL: " + String(googleApiUrl) + "?key=" + _googleApiKey); #endif // DEBUG_WIFI_LOCATION String request = String("POST ") + String(googleApiUrl); if (_googleApiKey != "") request += "?key=" + _googleApiKey; request += " HTTP/1.1\r\n"; request += "Host: " + String(googleApisHost) + "\r\n"; request += "User-Agent: ESP8266\r\n"; request += "Content-Type:application/json\r\n"; request += "Content-Length:" + String(body.length()) + "\r\n"; request += "Connection: close\r\n\r\n"; request += body; #ifdef DEBUG_WIFI_LOCATION Serial.println("request: \n" + request); #endif // DEBUG_WIFI_LOCATION _client.println(request); #ifdef DEBUG_WIFI_LOCATION Serial.println("request sent"); Serial.print("Free heap: "); Serial.println(ESP.getFreeHeap()); #endif // DEBUG_WIFI_LOCATION int timer = millis(); // Wait for response while (millis() - timer < MAX_CONNECTION_TIMEOUT) { if (_client.available()) break; yield (); } while (_client.available()) { #ifdef DEBUG_WIFI_LOCATION Serial.print("."); #endif // DEBUG_WIFI_LOCATION response += _client.readString(); yield (); } #ifdef DEBUG_WIFI_LOCATION Serial.println(); #endif // DEBUG_WIFI_LOCATION if (response != "") { #ifdef DEBUG_WIFI_LOCATION Serial.println("Got response:"); Serial.println(response); #endif // DEBUG_WIFI_LOCATION int index = response.indexOf("\"lat\":"); if (index != -1) { String tempStr = response.substring(index); //Serial.println(tempStr); tempStr = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); //Serial.println(tempStr); location.lat = tempStr.toFloat(); #ifdef DEBUG_WIFI_LOCATION Serial.println("\nLat: " + String(location.lat, 7)); #endif // DEBUG_WIFI_LOCATION } index = response.indexOf("\"lng\":"); if (index != -1) { String tempStr = response.substring(index); //Serial.println(tempStr); tempStr = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf(",")); //Serial.println(tempStr); location.lon = tempStr.toFloat(); #ifdef DEBUG_WIFI_LOCATION Serial.println("Lon: " + String(location.lon, 7)); #endif // DEBUG_WIFI_LOCATION } index = response.indexOf("\"accuracy\":"); if (index != -1) { String tempStr = response.substring(index); //Serial.println(tempStr); tempStr = tempStr.substring(tempStr.indexOf(":") + 1, tempStr.indexOf("\n")); //Serial.println(tempStr); location.accuracy = tempStr.toFloat(); #ifdef DEBUG_WIFI_LOCATION Serial.println("Accuracy: " + String(location.accuracy) + "\n"); #endif // DEBUG_WIFI_LOCATION } } return location; }