diff --git a/api.h b/api.h index e807141..2a2eaef 100644 --- a/api.h +++ b/api.h @@ -11,12 +11,10 @@ class OsemApi { bool postMeasurement(String measurement, String sensorID) { //telnet.print("Connecting to API.. "); if (!client.connect(API_ENDPOINT, 443)) { - //Serial.println("connection failed"); return false; } if (!client.verify(API_FINGERPRINT, API_ENDPOINT)) { - //Serial.println("certificate doesn't match"); return false; } @@ -29,12 +27,10 @@ class OsemApi { client << measurement; // read response - while (client.connected()) { - String line = client.readStringUntil('\n'); - if (line == "\r") break; - } - Serial << "API-Server response: " << client.readString() << EOL; - + if (!client.connected()) return false; + String line = client.readStringUntil('\r'); + if (line != "HTTP/1.1 201 Created") return false; + return true; } }; diff --git a/config.h b/config.h index f9461a4..03a4491 100644 --- a/config.h +++ b/config.h @@ -20,3 +20,5 @@ #define API_KEY "XXXXXXXXXXX" #define ID_BOX "57c7f3291421551100bf13c8" #define ID_SENSOR_WIFI "57c7f3291421551100bf13ca" + +#define MEASUREMENT_INTERVAL 10000 diff --git a/gps.h b/gps.h index c414142..e57070f 100644 --- a/gps.h +++ b/gps.h @@ -66,14 +66,11 @@ class Gps { } /** - * return an iso8601 formatted datestring with the current time + * fill a char[20] with an iso8601 formatted date from now */ - static char* getISODate() { - // TODO: check why we need to allocate that much storage for a 20 character string for Serial printing?? - char result[100] = { 0 }; - sprintf(result, "%04d-%02d-%02d-T%02d:%02d:%02dZ", + void getISODate(char* result) { + sprintf(result, "%04d-%02d-%02dT%02d:%02d:%02dZ", year(), month(), day(), hour(), minute(), second()); - return result; } }; diff --git a/mobile-sensebox.ino b/mobile-sensebox.ino index 0b49e61..87f7b0b 100644 --- a/mobile-sensebox.ino +++ b/mobile-sensebox.ino @@ -4,34 +4,16 @@ #include "api.h" #include "storage.h" #include "TelnetPrint.h" +#include "streampipe.h" -#define DEBUG_OUT Serial -//#define DEBUG_OUT telnet +#define DEBUG_OUT telnet // debug output is available via telnet! -//TelnetPrint telnet = TelnetPrint(); Storage storage = Storage(); Wifi wifi = Wifi(); +TelnetPrint telnet = TelnetPrint(); OsemApi api = OsemApi(); Gps gps = Gps(); -/* UTILS */ -void printState(WifiState wifiState) { - DEBUG_OUT.print("homeAvailable: "); - DEBUG_OUT.println(wifiState.homeAvailable); - DEBUG_OUT.print("numNetworks: "); - DEBUG_OUT.println(wifiState.numNetworks); - DEBUG_OUT.print("numUnencrypted: "); - DEBUG_OUT.println(wifiState.numUnencrypted); - - DEBUG_OUT.print("lat: "); - //DEBUG_OUT.print(gps.getLocation().lat(), 6); - DEBUG_OUT.print(" lng: "); - //DEBUG_OUT.println(gps.getLocation().lng(), 6); - - DEBUG_OUT.println(gps.getISODate()); - DEBUG_OUT.println(""); -} - bool storeMeasurement(float lat, float lng, float value, const char* timeStamp, const char* sensorID) { Measurement m; m.lat = lat; @@ -43,26 +25,57 @@ bool storeMeasurement(float lat, float lng, float value, const char* timeStamp, 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; + } + DEBUG_OUT << "success!" << EOL; + } + return true; +} + +// delay for a given duration (ms), rollover-safe implementation +// offset may be a duration which has been "used up" before, so the delay is adaptive, +// meaning that the interval of a adaptiveDelay() call is constant +void adaptiveDelay(unsigned long ms, unsigned long offset = 0) { + unsigned long start = millis(); + for (;;) { + // for some reason, operations have to be performed in this loop for + // proper operation, so we just do the polling to be done anyway + gps.pollGPS(); + telnet.pollClients(); + + unsigned long now = millis(); + unsigned long elapsed = now - start + offset; + //DEBUG_OUT << elapsed << EOL; + if (elapsed >= ms) return; + } +} /* MAIN ENTRY POINTS */ void setup() { - DEBUG_OUT.begin(115200); - size_t bytesFree = storage.begin(); - //gps.begin(); + gps.begin(); wifi.begin(); + // DEBUG: just for connection to telnet printer wifi.connect(WIFI_SSID, WIFI_PASS); - - //delay(5000); // DEBUG oportunity to connect to network logger + DEBUG_OUT.begin(); + delay(5000); + telnet.pollClients(); // wait until we got a first fix from GPS, and thus an initial time - /*DEBUG_OUT.print("Getting GPS fix.."); + DEBUG_OUT.print("Getting GPS fix.."); while (!gps.updateLocation()) { DEBUG_OUT.print("."); } - DEBUG_OUT.print(" done! ");*/ - DEBUG_OUT.println(gps.getISODate()); + DEBUG_OUT.println(" done! "); - DEBUG_OUT.println("Setup done!\n"); DEBUG_OUT.println("WiFi MAC WiFi IP"); DEBUG_OUT.print(WiFi.macAddress()); DEBUG_OUT.print(" "); @@ -71,38 +84,58 @@ void setup() { DEBUG_OUT.print("SPIFF bytes free: "); DEBUG_OUT.println(bytesFree); - digitalWrite(D9, HIGH); // DEBUG: integrated led? doesnt work + DEBUG_OUT.println("Setup done!\n"); } +unsigned long cycleStart; +TinyGPSLocation loc; +char dateString[20]; + void loop() { - //pollGPS(); - //DEBUG_OUT.pollClients(); + cycleStart = millis(); + + //gps.pollGPS(); + //telnet.pollClients(); + + // measure WiFi WifiState wifiState = wifi.scan(WIFI_SSID); - char* dateString = gps.getISODate(); - // TODO: take other measurements (average them?) -/* + // 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 << "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; // write measurements to file - if (storeMeasurement(51.25345345, 7.89, wifiState.numNetworks, "2016-08-31T00:03:10Z", ID_SENSOR_WIFI)) { - DEBUG_OUT.print("measurement stored! storage size: "); + if (storeMeasurement(loc.lat(), loc.lng(), wifiState.numNetworks, dateString, ID_SENSOR_WIFI)) { + DEBUG_OUT.print("measurement (wifi) stored! storage size: "); } else { - DEBUG_OUT.print("measurement store failed! storage size: "); + DEBUG_OUT.print("measurement (wifi) store failed! storage size: "); } DEBUG_OUT.println(storage.size()); - // TODO: connect to wifi, if available & not connected yet - - // then upload local data, remove from storage - while (storage.size()) { - String measure = storage.pop(); - api.postMeasurement(measure.substring(API_KEY_LENGTH + 1), measure.substring(0, API_KEY_LENGTH)); + // connect to wifi, if available & not connected yet + // once we are connected, upload (max) 3 stored measurements & free the storage + if (wifi.isConnected() || (wifiState.homeAvailable && wifi.connect(WIFI_SSID, WIFI_PASS)) ) { + uploadMeasurements(3); } + + DEBUG_OUT << EOL; - printState(wifiState); - delay(5000); + // 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); } diff --git a/wifi.h b/wifi.h index 37fcd8b..8db0aaf 100644 --- a/wifi.h +++ b/wifi.h @@ -50,5 +50,9 @@ class Wifi { //telnet.println(" timeout"); return false; } + + bool isConnected() { + return (WiFi.status() == WL_CONNECTED); + } };