WiFiScanner

Project Description
After building the RSSI Scanner for Muenchen.freifunk.net I was urged to add a logging and GPS tracking to it.
This the smallest Wifi Scanner I have used up today.
Used Hardware
- Adafruit Feather ESP8266 HUZZAH
- Adafruit Adalogger FeatherWing SD & RTC
- Adafruit NeoPixel FeatherWing
- DCRobot GPS Module
- 380mAh LiPo
- Cables
- Breadboard

I used almost all GPIO of the ESP8266 (only unused is the ADC). The used GPIOs are:
- GPIO 4 & 5 Used for the RTC for I2C Wire SDA and SCL
- GPIO 0 & 16 Used for the software serial to talk to the GPS Module
- GPIO 12, 13, 14 & 15 Used for the SPI connection to the SD Card reader
- GPIO 2 Used to drive the NeoPixel array
You have to break the default connection on the Adafruit NeoPixel FeatherWing and solder the correct connection to enable GPIO 2 connection.
The micro SD card needs to be formatted as a FAT16. There are some tweaks to make this happen.
Add a CR1220 battery to feed the RTC, while system is unpowered.

The Sourcecode:
/* RSSI Mapper V1.0 Copyright 2016, -mat- filid brandy, brandy@linuxpinguin.de See /project/wifiscanner As I use a Adafruit ESP8266 Huzzah Feather this is the uses PIN usage For the GPS I use: GPIO #00 = SoftwareSerial RX GPIO #16 = SoftwareSerial TX For the Adafruit NeoPixel FeatherWing you will need to cut the default and enable it for use of Pin 2 GPIO #02 = stripPin For the Adafruit Datalogger FeatherWing we will use the following Pins: SD will use: GPIO #15 = ChipSelect GPIO #14 = SPI SCK GPIO #13 = SPI MOSI GPIO #12 = SPI MISO For the RT Clock on the same Adafruit Datalogger FeatherWing we will use the following Pins: GPIO #05 = I2C SCL GPIO #04 = I2C SDA And then there are no more GPIOs left for the ESP 8266 (okay only the ADC analog one) */ #define DEBUG false // GPS #include <SoftwareSerial.h> #include <TinyGPS.h> #define ssRX 0 #define ssTX 16 #define GPSresolution 4 // NeoPixel #include <Adafruit_NeoPixel.h> #define stripPin 2 #define stripSize 32 // SD #include <SPI.h> #include <SD.h> #define chipSelect 15 // WiFi #include "ESP8266WiFi.h" #include <WiFiClient.h> //#include <ESP8266WebServer.h> //#include <ESP8266mDNS.h> // Real Time Clock #include <Wire.h> #include "RTClib.h" #define SDA 4 #define SCL 5 /* Init the hardware related variables */ // The multitasking looping support via timeslots // [0] = search for new ssids // [1] = find out the RSSI for a known BSSID // [2] = glowing orb effect // uint32_t cTick; // current Tick in millis() uint32_t pTick[3]; // previous Tick in millis() uint32_t dTick[3]; // difference between current and previous Tick bool fTick[3]; // first time // GPS TinyGPS gps; SoftwareSerial ss(ssRX, ssTX); // NeoPixel Adafruit_NeoPixel strip = Adafruit_NeoPixel( stripSize, stripPin, NEO_GRB + NEO_KHZ800); // WiFi String searchForSSID1 = "muenchen.freifunk.net"; String searchForSSID2 = "mesh.ffmuc"; // SD Sd2Card card; SdVolume volume; SdFile root; boolean SDworks = false; // Realtime Clock RTC_PCF8523 rtc; char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; // WatchDog // Use a watchdog timer to catch issue inside the WiFi module // This sometime triggers, when scanning for new networks and none are in the neighboorhood // The WiFi subsystem in the chip hangs, the watchdog catches this and reset the whole system #include <Ticker.h> Ticker secondTick; int watchdogCount = 0; void ISRwatchdog() { watchdogCount++; if (watchdogCount > 5) { Serial.println(); Serial.println("watchdog triggered reset!"); ESP.reset(); } } /* Handling GPS -------------------------------------------------------------------- */ namespace myGPS { typedef struct { bool newGPSData; bool debug; bool debugComm; unsigned long chars; unsigned short sentences; unsigned short failed; float lat; float lon; unsigned long age; } GPSData; static void debug(GPSData& gpsd) { gpsd.debug = !gpsd.debug; } static void debugComm(GPSData& gpsd) { gpsd.debugComm = !gpsd.debugComm; } static String print_date() { DateTime now = rtc.now(); int year; byte month, day, hour, minute, second, hsec; unsigned long age; String ret; gps.crack_datetime(&year, &month, &day, &hour, &minute, &second, &hsec, &age ); if (TinyGPS::GPS_INVALID_AGE != age) { rtc.adjust(DateTime(year, month, day, hour, minute, second)); if (DEBUG) Serial.println("Adjusted time from GPS"); } char sz[32]; sprintf(sz, "%02d/%02d/%02d %02d:%02d:%02d", now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second()); ret = sz; return ret; } static void handleGPS(GPSData& gpsd) { gpsd.newGPSData = false; // Every 1 second = 1000 milliseconds for (unsigned long start = millis(); millis() - start < 1000; ) { while (ss.available()) { char c = ss.read(); if (gpsd.debugComm) Serial.write(c); if (gps.encode(c)) gpsd.newGPSData = true; } } if (gpsd.newGPSData) { gps.f_get_position( &gpsd.lat, &gpsd.lon, &gpsd.age); if (gpsd.debug) { Serial.print("\n LAT="); Serial.print(gpsd.lat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : gpsd.lat, 6); Serial.print(" LON="); Serial.print(gpsd.lon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : gpsd.lon, 6); Serial.print(" SAT="); Serial.print(gps.satellites() == TinyGPS::GPS_INVALID_SATELLITES ? 0 : gps.satellites()); Serial.print(" PREC="); Serial.print(gps.hdop() == TinyGPS::GPS_INVALID_HDOP ? 0 : gps.hdop()); Serial.print(" Date="); Serial.print(print_date()); } strip.setPixelColor(0, (0x0F << 8) + 0x07); } else { strip.setPixelColor(0, (0x07 << 16) + 0x07); } strip.show(); gps.stats( &gpsd.chars, &gpsd.sentences, &gpsd.failed); if (0 == gpsd.chars) { Serial.println("\n\n** No characters received from GPS: check wiring **\n"); } if (gpsd.debugComm) { Serial.print("\n CHARS="); Serial.print(gpsd.chars); Serial.print(" SENTENCES="); Serial.print(gpsd.sentences); Serial.print(" CSUM ERR="); Serial.println(gpsd.failed); } } } myGPS::GPSData myGPSData; /* Wifi */ String encryptionTypes(int which) { switch (which) { case ENC_TYPE_WEP: return "WEP"; break; case ENC_TYPE_TKIP: return "WPA/TKIP"; break; case ENC_TYPE_CCMP: return "WPA2/CCMP"; break; case ENC_TYPE_NONE: return "None"; break; case ENC_TYPE_AUTO: return "Auto"; break; default: return "Unknown"; break; } } String bssidToString(uint8_t *thisBSSID) { String result; for (int i = 0; i < 6; i++ ) { char r[5]; sprintf(r, "%X:", thisBSSID[i]); result = result + r; } return result.substring(1, result.length() - 1); } void displayNodes(int ffmucmesh, int ffmucnodes, int opennodes, int nodes) { allColor(0); int maxAPs = strip.numPixels() - 1; maxAPs = ( maxAPs < nodes) ? nodes : maxAPs; maxAPs = ( maxAPs <= 0 ) ? strip.numPixels() - 1 : maxAPs; for (int i = 0; i < map(nodes, 0, maxAPs, 0, strip.numPixels() - 1); i++) { strip.setPixelColor(i + 1, 0x0F << 8); } for (int i = 0; i < map(opennodes, 0, maxAPs, 0, strip.numPixels() - 1); i++) { strip.setPixelColor(i + 1, 0x0F << 16); } for (int i = 0; i < map(ffmucnodes + ffmucmesh, 0, maxAPs, 0, strip.numPixels() - 1); i++) { strip.setPixelColor(i + 1, 0x0F); } for (int i = 0; i < map(ffmucmesh, 0, maxAPs, 0, strip.numPixels() - 1); i++) { strip.setPixelColor(i + 1, 0x02); } strip.show(); } /* NeoPixel */ void allColor(uint32_t c) { for (int i = 0; i < strip.numPixels(); i++) { strip.setPixelColor(i, c); } strip.show(); } /* SETUP -------------------------------------------------------------------------- */ void setup() { Serial.begin(9600); // GPS ss.begin(9600); // NeoPixel strip.begin(); strip.show(); // Setup the WiFi module WiFi.mode(WIFI_STA); WiFi.disconnect(); dTick[0] = 60 * 1000; // Every 60 seconds fTick[0] = HIGH; // SD card setup Serial.print("\nInitializing SD card..."); if (!card.init(SPI_HALF_SPEED, chipSelect)) { SDworks = false; Serial.println("initialization failed. Things to check:"); Serial.println("* is a card inserted?"); Serial.println("* is your wiring correct?"); Serial.println("* did you change the chipSelect pin to match your shield or module?"); return; } else { SDworks = true; Serial.println("Wiring is correct and a card is present."); // print the type of card Serial.print("\nCard type: "); switch (card.type()) { case SD_CARD_TYPE_SD1: Serial.println("SD1"); break; case SD_CARD_TYPE_SD2: Serial.println("SD2"); break; case SD_CARD_TYPE_SDHC: Serial.println("SDHC"); break; default: Serial.println("Unknown"); } // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32 if (!volume.init(card)) { Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card"); SDworks = false; return; } // print the type and size of the first FAT-type volume uint32_t volumesize; Serial.print("\nVolume type is FAT"); Serial.println(volume.fatType(), DEC); Serial.println(); volumesize = volume.blocksPerCluster(); // clusters are collections of blocks volumesize *= volume.clusterCount(); // we'll have a lot of clusters volumesize *= 512; // SD card blocks are always 512 bytes Serial.print("Volume size (bytes): "); Serial.println(volumesize); Serial.print("Volume size (Kbytes): "); volumesize /= 1024; Serial.println(volumesize); Serial.print("Volume size (Mbytes): "); volumesize /= 1024; Serial.println(volumesize); SD.begin(chipSelect); } // Real Time Clock if (! rtc.begin()) { Serial.println("Couldn't find RTC"); while (1); } if (! rtc.initialized()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); } // Attach the interrupt 1 to the watchdog secondTick.attach(1, ISRwatchdog); Serial.println("Setup done"); allColor(0); } /* WORK -------------------------------------------------------------------------- */ void loop() { watchdogCount = 0; // Feed the watchdog yield(); // Give the WiFi subsystem time to do its things cTick = millis(); File dataFile; // GPS using namespace myGPS; myGPS::handleGPS(myGPSData); if (DEBUG) myGPS::debug(myGPSData); if (DEBUG) myGPS::debugComm(myGPSData); // WiFi if ( ((cTick - pTick[0]) >= dTick[0] || HIGH == fTick[0])) { pTick[0] = cTick; fTick[0] = LOW; int networks = WiFi.scanNetworks(); yield(); if ( 0 == networks) { Serial.println("No networks found"); } else { Serial.println(""); int kinds[4]; kinds[0] = 0; kinds[1] = 0; kinds[2] = 0; kinds[3] = 0; for (int network = 0; network < networks; network++) { String ssid_scan; int32_t rssi_scan; uint8_t sec_scan; uint8_t* BSSID_scan; int32_t chan_scan; bool hidden_scan; WiFi.getNetworkInfo(network, ssid_scan, sec_scan, rssi_scan, BSSID_scan, chan_scan, hidden_scan); Serial.print("\""); Serial.print(ssid_scan); Serial.print("\","); Serial.print("\""); Serial.print(bssidToString(BSSID_scan)); Serial.print("\","); Serial.print(""); Serial.print(rssi_scan); Serial.print(","); Serial.print(""); Serial.print(chan_scan); Serial.print(","); Serial.print("\""); Serial.print(encryptionTypes(sec_scan)); Serial.print("\","); Serial.print(""); Serial.print(myGPSData.lat, GPSresolution); Serial.print(","); Serial.print(""); Serial.print(myGPSData.lon, GPSresolution); Serial.print(","); Serial.print("\""); Serial.print(myGPS::print_date()); Serial.print("\""); Serial.println(""); if (SDworks) dataFile = SD.open("ssids.txt", FILE_WRITE); if (dataFile) { dataFile.print("\""); dataFile.print(ssid_scan); dataFile.print("\","); dataFile.print("\""); dataFile.print(bssidToString(BSSID_scan)); dataFile.print("\","); dataFile.print(""); dataFile.print(rssi_scan); dataFile.print(","); dataFile.print(""); dataFile.print(chan_scan); dataFile.print(","); dataFile.print("\""); dataFile.print(encryptionTypes(sec_scan)); dataFile.print("\","); dataFile.print(""); dataFile.print(myGPSData.lat, GPSresolution); dataFile.print(","); dataFile.print(""); dataFile.print(myGPSData.lon, GPSresolution); dataFile.print(","); dataFile.print("\""); dataFile.print(myGPS::print_date()); dataFile.print("\""); dataFile.println(""); dataFile.close(); } else { Serial.println("error opening ssids.txt"); SDworks = false; } if (ENC_TYPE_NONE == sec_scan) kinds[0]++; else kinds[1]++; if (searchForSSID1.equals(ssid_scan)) kinds[2]++; if (searchForSSID2.equals(ssid_scan)) kinds[3]++; } displayNodes(kinds[3], kinds[2], kinds[0], networks); Serial.print("\t\""); Serial.print(networks); Serial.print(" networks ("); Serial.print(kinds[0]); Serial.print(" open ("); Serial.print(kinds[2] + kinds[3]); Serial.print(" ffmuc), "); Serial.print(kinds[1]); Serial.println(" closed)\""); } } }