diff --git a/TelnetPrint.h b/TelnetPrint.h index 6c2ea53..a6f1779 100644 --- a/TelnetPrint.h +++ b/TelnetPrint.h @@ -8,7 +8,7 @@ # pragma once #include -#define MAX_TELNET_CLIENTS 3 +#define MAX_TELNET_CLIENTS 3 // more class TelnetPrint : public Print { protected: @@ -47,7 +47,7 @@ class TelnetPrint : public Print { } } - // TODO: find more efficient method than sending byte by byte + // TODO: find more efficient method than sending byte by byte (!) virtual size_t write(uint8_t s) { for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ if (clients[i] && clients[i].connected()){ @@ -58,13 +58,13 @@ class TelnetPrint : public Print { } // Is only called for Strings, not char arrays - virtual size_t write(const char *buffer, size_t size) { + /*virtual size_t write(const char *buffer, size_t size) { for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){ if (clients[i] && clients[i].connected()){ clients[i].write(buffer, size); } } return size; - } + }*/ }; diff --git a/api.h b/api.h index 2a2eaef..bee246e 100644 --- a/api.h +++ b/api.h @@ -9,7 +9,6 @@ class OsemApi { public: bool postMeasurement(String measurement, String sensorID) { - //telnet.print("Connecting to API.. "); if (!client.connect(API_ENDPOINT, 443)) { return false; } diff --git a/config.h b/config.h index 142e70a..bab67fc 100644 --- a/config.h +++ b/config.h @@ -1,28 +1,35 @@ #pragma once +/* GENERAL */ +// interval of the measurements in ms. 0 for "as fast as possible" +#define MEASUREMENT_INTERVAL 10000 +// pull this pin down to disable measurements (eg for quickly bulk uploading stored measurements) +#define PIN_MEASURE_MODE D14 +// pull this pin down to disable uploads (eg for higher measurement rates when no realtime data is required) +#define PIN_UPLOAD_MODE D15 +// should be higher than the number of sensors to avoid storage overflowing. tradeoff with the measurement interval +#define MAX_UPLOADS_PER_CYCLE 4 + /* WiFi (ESP8266) */ +#define WIFI_SSID "Elmo" +#define WIFI_PASS "XXXXXX" -#define WIFI_SSID "FRITZ!Box 7490" -#define WIFI_PASS "XXXXXX" -/* GPS reciever (uBloc NEO-7M) */ -#define GPS_RX_PIN 4 -#define GPS_TX_PIN 3 +/* GPS reciever (uBloc NEO-7M) connected to hardware serial (SoftwareSerial does not work well!!) */ #define GPS_BAUD 9600 -#define GPS_INTERVAL 1000 // update interval of the gps device +#define GPS_INTERVAL 1000 // update interval of the gps device in ms /* API (openSenseMap) */ #define API_ENDPOINT "api.osem.vo1d.space" // SHA1 of the API SSL cert #define API_FINGERPRINT "A2 38 74 C7 B0 71 07 D4 2A 1C A5 6D 0D 05 3E 0A 90 68 A5 CB" -#define API_KEY_LENGTH 24 +#define API_KEY_LENGTH 24 // length of the keys #define API_KEY "XXXXXXXXXXX" #define ID_BOX "57c7f3291421551100bf13c8" #define ID_SENSOR_WIFI_APS "57c7f3291421551100bf13ca" #define ID_SENSOR_WIFI_NET "57cdd4ce1421551100bf17c5" #define ID_SENSOR_WIFI_OPEN "57cdd4ce1421551100bf17c6" -#define MEASUREMENT_INTERVAL 20000 diff --git a/mobile-sensebox.ino b/mobile-sensebox.ino index 3f7a44c..4b0daa0 100644 --- a/mobile-sensebox.ino +++ b/mobile-sensebox.ino @@ -21,24 +21,80 @@ bool storeMeasurement(float lat, float lng, float value, const char* timeStamp, m.value = value; strcpy(m.timeStamp, timeStamp); strcpy(m.sensorID, sensorID); - return storage.add(m); } -bool uploadMeasurements(const uint16_t maxUploads = 5) { - uint16_t uploadCount = 0; - String measure; - // only upload limited measures per cycle, to avoid long gaps in measurements - while (storage.size() && uploadCount++ < maxUploads) { - measure = storage.pop(); - DEBUG_OUT << "Uploading measurement for " << measure; - if(!api.postMeasurement(measure.substring(API_KEY_LENGTH + 1), measure.substring(0, API_KEY_LENGTH))) { - DEBUG_OUT << "upload failed!" << EOL; - return false; +void measure(WifiState& wifiState) { + char dateString[20]; + + // measure WiFi + wifiState = wifi.scan(WIFI_SSID); + + // update gps position + // TODO: how to handle lost GPS fix? + // TODO: check if is data is valid? + if(!gps.updateLocation() || !gps.updateTime()) { + DEBUG_OUT << "GPS timed out" << EOL; + digitalWrite(BUILTIN_LED, LOW); + } else { + digitalWrite(BUILTIN_LED, HIGH); + } + TinyGPSLocation loc = gps.getLocation(); + gps.getISODate(dateString); + + // print state + DEBUG_OUT << "homeAvailable: " << wifiState.homeAvailable << EOL; + DEBUG_OUT << "numAPs: " << wifiState.numAccessPoints << EOL; + DEBUG_OUT << "numNetworks: " << wifiState.numNetworks << EOL; + DEBUG_OUT << "numUnencrypted: " << wifiState.numUnencrypted << EOL; + DEBUG_OUT.print("lat: "); + DEBUG_OUT.print(loc.lat(), 6); + DEBUG_OUT.print(" lng: "); + DEBUG_OUT.println(loc.lng(), 6); + DEBUG_OUT << dateString << EOL; + + // IDEA: write location update to separate file? + + // write measurements to file + if (!storeMeasurement(loc.lat(), loc.lng(), wifiState.numAccessPoints, dateString, ID_SENSOR_WIFI_APS)) + DEBUG_OUT << "measurement (wifi aps) store failed!" << EOL; + + if (!storeMeasurement(loc.lat(), loc.lng(), wifiState.numNetworks, dateString, ID_SENSOR_WIFI_NET)) + DEBUG_OUT << "measurement (wifi nets) store failed!" << EOL; + + if (!storeMeasurement(loc.lat(), loc.lng(), wifiState.numUnencrypted, dateString, ID_SENSOR_WIFI_OPEN)) + DEBUG_OUT << "measurement (wifi open) store failed!" << EOL; + + DEBUG_OUT << EOL; +} + +void upload(WifiState& wifiState) { + size_t numMeasures = storage.size(); + DEBUG_OUT << numMeasures << " measurements stored." << EOL; + if (!numMeasures) return; + + // in case we are not measuring, scan manually to detect the home network + if (digitalRead(PIN_MEASURE_MODE) == LOW) + wifiState = wifi.scan(WIFI_SSID); + + // connect to wifi, if available & not connected yet + // once we are connected, upload (max) 5 stored measurements & free the storage + if (wifi.isConnected() || (wifiState.homeAvailable && wifi.connect(WIFI_SSID, WIFI_PASS)) ) { + uint16_t uploadCount = 0; + String measure; + // only upload limited measures per cycle, to avoid long gaps in measurements + while (storage.size() && uploadCount++ < MAX_UPLOADS_PER_CYCLE) { + measure = storage.pop(); + DEBUG_OUT << "Uploading measurement for " << measure; + if (api.postMeasurement(measure.substring(API_KEY_LENGTH + 1), measure.substring(0, API_KEY_LENGTH))) + DEBUG_OUT << "success!" << EOL; + else + DEBUG_OUT << "upload failed!" << EOL; } - DEBUG_OUT << "success!" << EOL; + } else { + DEBUG_OUT << "wifi connection to " << WIFI_SSID << " failed" << EOL; } - return true; + DEBUG_OUT << EOL; } // delay for a given duration (ms), rollover-safe implementation @@ -54,13 +110,18 @@ void adaptiveDelay(unsigned long ms, unsigned long offset = 0) { unsigned long now = millis(); unsigned long elapsed = now - start + offset; - //DEBUG_OUT << elapsed << EOL; if (elapsed >= ms) return; } } + /* MAIN ENTRY POINTS */ void setup() { + pinMode(BUILTIN_LED, OUTPUT); // status indicator LED: on = no GPS fix + digitalWrite(BUILTIN_LED, LOW); + pinMode(PIN_MEASURE_MODE, INPUT_PULLUP); // switch for measurements (pull it down to disable) + pinMode(PIN_UPLOAD_MODE, INPUT_PULLUP); // switch for API uploads (pull it down to disable) + size_t bytesFree = storage.begin(); gps.begin(); wifi.begin(); @@ -68,89 +129,41 @@ void setup() { // DEBUG: just for connection to telnet printer wifi.connect(WIFI_SSID, WIFI_PASS); DEBUG_OUT.begin(); - delay(5000); + delay(3000); telnet.pollClients(); // wait until we got a first fix from GPS, and thus an initial time - DEBUG_OUT.print("Getting GPS fix.."); - while (!gps.updateLocation()) { DEBUG_OUT.print("."); } - DEBUG_OUT.println(" done! "); - - DEBUG_OUT.println("WiFi MAC WiFi IP"); - DEBUG_OUT.print(WiFi.macAddress()); - DEBUG_OUT.print(" "); - DEBUG_OUT.println(WiFi.localIP()); - - DEBUG_OUT.print("SPIFF bytes free: "); - DEBUG_OUT.println(bytesFree); - - DEBUG_OUT.println("Setup done!\n"); + DEBUG_OUT << "Getting GPS fix.."; + while (!gps.updateLocation()) { DEBUG_OUT << "."; } + DEBUG_OUT << " done!" << EOL; + digitalWrite(BUILTIN_LED, HIGH); + + DEBUG_OUT << "Setup done!" << EOL; + DEBUG_OUT << "WiFi MAC WiFi IP" << EOL; + DEBUG_OUT << WiFi.macAddress() << " " << WiFi.localIP() << EOL; + DEBUG_OUT << "SPIFF bytes free: " << bytesFree << EOL << EOL; } unsigned long cycleStart; -TinyGPSLocation loc; -char dateString[20]; +WifiState wifiState; // global, as both measure and upload need the state void loop() { cycleStart = millis(); - //gps.pollGPS(); - //telnet.pollClients(); - - // measure WiFi - WifiState wifiState = wifi.scan(WIFI_SSID); - - // measure GPS - if(!gps.updateLocation()) DEBUG_OUT.println("GPS timed out (location)"); - if(!gps.updateTime()) DEBUG_OUT.println("GPS timed out (time)"); - loc = gps.getLocation(); - gps.getISODate(dateString); - - // print state - DEBUG_OUT << "homeAvailable: " << wifiState.homeAvailable << EOL; - DEBUG_OUT << "numAPs: " << wifiState.numAccessPoints << EOL; - DEBUG_OUT << "numNetworks: " << wifiState.numNetworks << EOL; - DEBUG_OUT << "numUnencrypted: " << wifiState.numUnencrypted << EOL; - DEBUG_OUT.print("lat: "); - DEBUG_OUT.print(loc.lat(), 6); - DEBUG_OUT.print(" lng: "); - DEBUG_OUT.println(loc.lng(), 6); - DEBUG_OUT << dateString << EOL; - - // TODO. write location update to file + if (digitalRead(PIN_MEASURE_MODE) == HIGH) + measure(wifiState); - // write measurements to file - if (storeMeasurement(loc.lat(), loc.lng(), wifiState.numAccessPoints, dateString, ID_SENSOR_WIFI_APS)) { - DEBUG_OUT.print("measurement (wifi aps) stored! storage size: "); - } else { - DEBUG_OUT.print("measurement (wifi aps) store failed! storage size: "); - } - DEBUG_OUT.println(storage.size()); - if (storeMeasurement(loc.lat(), loc.lng(), wifiState.numNetworks, dateString, ID_SENSOR_WIFI_NET)) { - DEBUG_OUT.print("measurement (wifi nets) stored! storage size: "); - } else { - DEBUG_OUT.print("measurement (wifi nets) store failed! storage size: "); - } - DEBUG_OUT.println(storage.size()); - if (storeMeasurement(loc.lat(), loc.lng(), wifiState.numUnencrypted, dateString, ID_SENSOR_WIFI_OPEN)) { - DEBUG_OUT.print("measurement (wifi open) stored! storage size: "); - } else { - DEBUG_OUT.print("measurement (wifi open) store failed! storage size: "); - } - DEBUG_OUT.println(storage.size()); + if (digitalRead(PIN_UPLOAD_MODE) == HIGH) + upload(wifiState); - // connect to wifi, if available & not connected yet - // once we are connected, upload (max) 4 stored measurements & free the storage - if (wifi.isConnected() || (wifiState.homeAvailable && wifi.connect(WIFI_SSID, WIFI_PASS)) ) { - uploadMeasurements(4); + if (digitalRead(PIN_MEASURE_MODE) == HIGH) { + // run the measurements in a fixed interval, using an adaptive delay + // IDEA: dont use time but distance interval? -> TinyGPSPlus::distanceBetween() + adaptiveDelay(MEASUREMENT_INTERVAL, millis() - cycleStart); + } else { + // run as fast as possible when not measuring. + // adaptiveDelay has to be called anyway, as some polling functions are run within + adaptiveDelay(0); } - - DEBUG_OUT << EOL; - - // the measurement & upload cycle takes ~4 seconds, so lets we wait measure - // every 10secs in total - // run the loop every 10secs. using an adaptive delay - // IDEA: dont use time but distance interval? -> TinyGPSPlus::distanceBetween() - adaptiveDelay(MEASUREMENT_INTERVAL, millis() - cycleStart); }