├── ESP8266_GeoLocate_1.ino ├── ESP8266_GeoLocate_2.ino └── README.md /ESP8266_GeoLocate_1.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ESP8266_GeoLocate_1 3 | 4 | GeoLocation using IP Address 5 | 6 | This is a copy of the Adafruit CC3000 Solution at 7 | https://github.com/adafruit/Adafruit_CC3000_Library/blob/master/examples/GeoLocation/GeoLocation.ino 8 | by Limor Fried, Kevin Townsend and Phil Burgess 9 | 10 | presented as a contrast to the alternative 11 | ESP8266_GeoLocate_2 using WAP 'sniffing' 12 | 13 | m0xpd 14 | 15 | August2016 16 | 17 | For further information: 18 | http://m0xpd.blogspot.com/2016/08/esp8266-geolocation.html 19 | ======================================================= 20 | */ 21 | 22 | #include 23 | #include 24 | // variables for location service... 25 | char 26 | country[20], 27 | region[20], 28 | city[20], 29 | name[13], // Temp space for name:value parsing 30 | value[64]; // Temp space for name:value parsing 31 | float 32 | longitude, latitude; 33 | char server[] = "freegeoip.net"; // (using DNS) 34 | 35 | char ssid[] = ; // your network SSID (name) 36 | char pass[] = ; // your network password 37 | 38 | 39 | // Initialize the Ethernet client library 40 | // with the IP address and port of the server 41 | // that you want to connect to (port 80 is default for HTTP): 42 | WiFiClient client; 43 | 44 | 45 | void setup() { 46 | // put your setup code here, to run once: 47 | uint32_t ip = 0L; 48 | DynamicJsonBuffer jsonBuffer; 49 | Serial.begin(115200); 50 | Serial.println("Start"); 51 | // We start by connecting to a WiFi network 52 | Serial.print("Connecting to "); 53 | Serial.println(ssid); 54 | WiFi.begin(ssid, pass); 55 | while (WiFi.status() != WL_CONNECTED) { 56 | delay(500); 57 | Serial.print("."); 58 | } 59 | Serial.println(""); 60 | Serial.println("WiFi connected"); 61 | Serial.println("IP address: "); 62 | ip=WiFi.localIP(); 63 | Serial.println(ip); 64 | Serial.println("\nStarting connection to server..."); 65 | // if you get a connection, report back via serial: 66 | if (client.connect(server, 80)) { 67 | Serial.println("connected to server"); 68 | // Make a HTTP request: 69 | client.println("GET /json/ HTTP/1.1\r\nHost: freegeoip.net\r\n\r\n"); 70 | Serial.print("\r\nReading response..."); 71 | country[0] = region[0] = city[0] = 0; // Clear data 72 | jsonParse(0, 0); 73 | client.println("Connection: close"); 74 | Serial.println(F("OK")); 75 | Serial.println(F("\nDisconnecting")); 76 | client.stop(); 77 | } 78 | Serial.print(F("\r\nRESULTS:\r\n Country: ")); 79 | Serial.println(country); 80 | Serial.print(F(" Region: ")); 81 | Serial.println(region); 82 | Serial.print(F(" City: ")); 83 | Serial.println(city); 84 | Serial.print(F(" Longitude: ")); 85 | Serial.println(longitude); 86 | Serial.print(F(" Latitude: ")); 87 | Serial.println(latitude); 88 | 89 | } 90 | 91 | void loop() { 92 | // put your main code here, to run repeatedly: 93 | 94 | } 95 | 96 | boolean jsonParse(int depth, byte endChar) { 97 | int c, i; 98 | boolean readName = true; 99 | for(;;) { 100 | while(isspace(c = timedRead())); // Scan past whitespace 101 | if(c < 0) return false; // Timeout 102 | if(c == endChar) return true; // EOD 103 | 104 | if(c == '{') { // Object follows 105 | if(!jsonParse(depth + 1, '}')) return false; 106 | if(!depth) return true; // End of file 107 | } else if(c == '[') { // Array follows 108 | if(!jsonParse(depth + 1,']')) return false; 109 | } else if((c == '"') || (c == '\'')) { // String follows 110 | if(readName) { // Name-reading mode 111 | if(!readString(name, sizeof(name)-1, c)) return false; 112 | } else { // Value-reading mode 113 | if(!readString(value, sizeof(value)-1, c)) return false; 114 | // Process name and value strings: 115 | if (!strcasecmp(name, "country_name")) { 116 | strncpy(country, value, sizeof(country)-1); 117 | } else if(!strcasecmp(name, "region_name")) { 118 | strncpy(region, value, sizeof(region)-1); 119 | } else if(!strcasecmp(name, "city")) { 120 | strncpy(city, value, sizeof(city)-1); 121 | } 122 | } 123 | } else if(c == ':') { // Separator between name:value 124 | readName = false; // Now in value-reading mode 125 | value[0] = 0; // Clear existing value data 126 | } else if(c == ',') { // Separator between name/value pairs 127 | readName = true; // Now in name-reading mode 128 | name[0] = 0; // Clear existing name data 129 | } else { 130 | // Else true/false/null or a number follows. 131 | value[0] = c; 132 | if(!strcasecmp(name, "longitude")) { 133 | if(!readString(value+1, sizeof(value)-1, ',')) return false; 134 | longitude = atof(value); 135 | } else if(!strcasecmp(name, "latitude")) { 136 | if(!readString(value+1, sizeof(value)-1, ',')) return false; 137 | latitude = atof(value); 138 | } 139 | readName = true; // Now in name-reading mode 140 | name[0] = 0; // Clear existing name data 141 | } 142 | } 143 | } 144 | 145 | // Read from client stream with a 5 second timeout. Although an 146 | // essentially identical method already exists in the Stream() class, 147 | // it's declared private there...so this is a local copy. 148 | int timedRead(void) { 149 | unsigned long start = millis(); 150 | while((!client.available()) && ((millis() - start) < 5000L)); 151 | return client.read(); // -1 on timeout 152 | } 153 | 154 | // Read string from client stream into destination buffer, up to a maximum 155 | // requested length. Buffer should be at least 1 byte larger than this to 156 | // accommodate NUL terminator. Opening quote is assumed already read, 157 | // closing quote will be discarded, and stream will be positioned 158 | // immediately following the closing quote (regardless whether max length 159 | // is reached -- excess chars are discarded). Returns true on success 160 | // (including zero-length string), false on timeout/read error. 161 | boolean readString(char *dest, int maxLen, char quote) { 162 | int c, len = 0; 163 | 164 | while((c = timedRead()) != quote) { // Read until closing quote 165 | if(c == '\\') { // Escaped char follows 166 | c = timedRead(); // Read it 167 | // Certain escaped values are for cursor control -- 168 | // there might be more suitable printer codes for each. 169 | if (c == 'b') c = '\b'; // Backspace 170 | else if(c == 'f') c = '\f'; // Form feed 171 | else if(c == 'n') c = '\n'; // Newline 172 | else if(c == 'r') c = '\r'; // Carriage return 173 | else if(c == 't') c = '\t'; // Tab 174 | else if(c == 'u') c = unidecode(4); 175 | else if(c == 'U') c = unidecode(8); 176 | // else c is unaltered -- an escaped char such as \ or " 177 | } // else c is a normal unescaped char 178 | 179 | if(c < 0) return false; // Timeout 180 | 181 | // In order to properly position the client stream at the end of 182 | // the string, characters are read to the end quote, even if the max 183 | // string length is reached...the extra chars are simply discarded. 184 | if(len < maxLen) dest[len++] = c; 185 | } 186 | 187 | dest[len] = 0; 188 | return true; // Success (even if empty string) 189 | } 190 | 191 | // Read a given number of hexadecimal characters from client stream, 192 | // representing a Unicode symbol. Return -1 on error, else return nearest 193 | // equivalent glyph in printer's charset. (See notes below -- for now, 194 | // always returns '-' or -1.) 195 | int unidecode(byte len) { 196 | int c, v, result = 0; 197 | while(len--) { 198 | if((c = timedRead()) < 0) return -1; // Stream timeout 199 | if ((c >= '0') && (c <= '9')) v = c - '0'; 200 | else if((c >= 'A') && (c <= 'F')) v = 10 + c - 'A'; 201 | else if((c >= 'a') && (c <= 'f')) v = 10 + c - 'a'; 202 | else return '-'; // garbage 203 | result = (result << 4) | v; 204 | } 205 | 206 | return '?'; 207 | } 208 | -------------------------------------------------------------------------------- /ESP8266_GeoLocate_2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ESP8266_GeoLocate_2 3 | 4 | GeoLocation using local Wireless Access Points 5 | 6 | m0xpd 7 | 8 | August 2016 9 | 10 | For further information: 11 | http://m0xpd.blogspot.com/2016/08/esp8266-geolocation.html 12 | ======================================================= 13 | */ 14 | 15 | #include 16 | #include 17 | #include "ESP8266WiFi.h" 18 | 19 | char myssid[] = ; // your network SSID (name) 20 | char mypass[] = ; // your network password 21 | 22 | 23 | //Credentials for Google GeoLocation API... 24 | //const char* Host = "www.googleapis.com"; 25 | //String thisPage = "/geolocation/v1/geolocate?key="; 26 | //String key = ; 27 | 28 | //Credentials for Mozilla GeoLocation API... 29 | //const char* Host = "location.services.mozilla.com"; 30 | //String thisPage = "/v1/geolocate?key="; 31 | //String key = ; 32 | 33 | int status = WL_IDLE_STATUS; 34 | String jsonString = "{\n"; 35 | 36 | double latitude = 0.0; 37 | double longitude = 0.0; 38 | 39 | int more_text = 0; // set to 1 for more debug output 40 | 41 | void setup() { 42 | Serial.begin(115200); 43 | Serial.println("Start"); 44 | // Set WiFi to station mode and disconnect from an AP if it was previously connected 45 | WiFi.mode(WIFI_STA); 46 | WiFi.disconnect(); 47 | delay(100); 48 | Serial.println("Setup done"); 49 | } 50 | 51 | void loop() { 52 | char bssid[6]; 53 | DynamicJsonBuffer jsonBuffer; 54 | Serial.println("scan start"); 55 | // WiFi.scanNetworks will return the number of networks found 56 | int n = WiFi.scanNetworks(); 57 | Serial.println("scan done"); 58 | if (n == 0) 59 | Serial.println("no networks found"); 60 | else 61 | { 62 | Serial.print(n); 63 | Serial.println(" networks found..."); 64 | 65 | if(more_text){ 66 | // Print out the formatted json... 67 | Serial.println("{"); 68 | Serial.println("\"homeMobileCountryCode\": 234,"); // this is a real UK MCC 69 | Serial.println("\"homeMobileNetworkCode\": 27,"); // and a real UK MNC 70 | Serial.println("\"radioType\": \"gsm\","); // for gsm 71 | Serial.println("\"carrier\": \"Vodafone\","); // associated with Vodafone 72 | //Serial.println("\"cellTowers\": ["); // I'm not reporting any cell towers 73 | //Serial.println("],"); 74 | Serial.println("\"wifiAccessPoints\": ["); 75 | for (int i = 0; i < n; ++i) 76 | { 77 | Serial.println("{"); 78 | Serial.print("\"macAddress\" : \""); 79 | Serial.print(WiFi.BSSIDstr(i)); 80 | Serial.println("\","); 81 | Serial.print("\"signalStrength\": "); 82 | Serial.println(WiFi.RSSI(i)); 83 | if(i"); 143 | Serial.println(" "); 144 | if (client.connect(Host, 443)) { 145 | Serial.println("Connected"); 146 | client.println("POST " + thisPage + key + " HTTP/1.1"); 147 | client.println("Host: "+ (String)Host); 148 | client.println("Connection: close"); 149 | client.println("Content-Type: application/json"); 150 | client.println("User-Agent: Arduino/1.0"); 151 | client.print("Content-Length: "); 152 | client.println(jsonString.length()); 153 | client.println(); 154 | client.print(jsonString); 155 | delay(500); 156 | } 157 | 158 | //Read and parse all the lines of the reply from server 159 | while (client.available()) { 160 | String line = client.readStringUntil('\r'); 161 | if(more_text){ 162 | Serial.print(line); 163 | } 164 | JsonObject& root = jsonBuffer.parseObject(line); 165 | if(root.success()){ 166 | latitude = root["location"]["lat"]; 167 | longitude = root["location"]["lng"]; 168 | } 169 | } 170 | 171 | Serial.println("closing connection"); 172 | Serial.println(); 173 | client.stop(); 174 | 175 | Serial.print("Latitude = "); 176 | Serial.println(latitude,6); 177 | Serial.print("Longitude = "); 178 | Serial.println(longitude,6); 179 | 180 | // Wait for ever... 181 | while(1){ 182 | delay(1000); 183 | } 184 | 185 | } 186 | 187 | 188 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266-GeoLocation 2 | This demonstrates methods to GeoLocate using the ESP8266 device. 3 | 4 | They are presented as sketches for the Arduino IDE. 5 | 6 | The first method, ESP8266_GeoLocation_1, obtains a location estimate from the IP address using the freegeoip service (freegeoip.net), 7 | using code copied almost verbatim from the Adafruit CC3000 soution. 8 | 9 | The second method, ESP_GeoLocation_2, obtains a location estimate from the results of a scan of nearby Wireless Access Points, 10 | using GeoLocation APIs such as those offered by Google or Mozilla. 11 | 12 | It includes an explicit example of how to build the json string which is required to submit the WAP scan result to these services. 13 | 14 | For more information and a description of my motivating application in amateur radio, 15 | see http://m0xpd.blogspot.com/2016/08/esp8266-geolocation.html 16 | --------------------------------------------------------------------------------