diff --git a/api.h b/api.h index 041181b..e807141 100644 --- a/api.h +++ b/api.h @@ -1,31 +1,40 @@ #pragma once #include #include "config.h" +#include "streampipe.h" class OsemApi { protected: WiFiClientSecure client; public: - bool postMeasurement(String m, String sensorID) { + bool postMeasurement(String measurement, String sensorID) { //telnet.print("Connecting to API.. "); if (!client.connect(API_ENDPOINT, 443)) { - //telnet.println("connection failed"); + //Serial.println("connection failed"); return false; } if (!client.verify(API_FINGERPRINT, API_ENDPOINT)) { - Serial.println("certificate doesn't match"); + //Serial.println("certificate doesn't match"); return false; } - - String url = "/boxes/" + String(ID_BOX) + "/" + sensorID; - // TODO: add actual measurement to post - client.print(String("POST ") + url + " HTTP/1.1\r\n" + - "Host: " + API_ENDPOINT + "\r\n" + - "User-Agent: mobile-sensebox-esp8266\r\n" + - "Connection: close\r\n\r\n"); - + + client << String("POST ") << "/boxes/" << ID_BOX << "/" << sensorID << " HTTP/1.1" << EOL; + client << "Host: " << API_ENDPOINT << EOL; + client << "X-APIKey: " << API_KEY << EOL; + client << "Content-Type: application/json" << EOL; + client << "Connection: close" << EOL; + client << "Content-Length: " << measurement.length() << EOL << EOL; + client << measurement; + + // read response + while (client.connected()) { + String line = client.readStringUntil('\n'); + if (line == "\r") break; + } + Serial << "API-Server response: " << client.readString() << EOL; + return true; } }; diff --git a/config.h b/config.h index 6da7db2..f9461a4 100644 --- a/config.h +++ b/config.h @@ -1,19 +1,22 @@ -/* WiFi (ESP8266) */ #pragma once -static const char* WIFI_SSID = "Elmo"; -static const char* WIFI_PASS = "FreiBier123"; +/* WiFi (ESP8266) */ + + +#define WIFI_SSID "Penaten" +#define WIFI_PASS "XXXXXX" /* GPS reciever (uBloc NEO-7M) */ -static const int GPS_RX_PIN = 4; -static const int GPS_TX_PIN = 3; -static const int GPS_BAUD = 9600; -static const int GPS_INTERVAL = 1000; // update interval of the gps device +#define GPS_RX_PIN 4 +#define GPS_TX_PIN 3 +#define GPS_BAUD 9600 +#define GPS_INTERVAL 1000 // update interval of the gps device /* API (openSenseMap) */ -static const char* API_ENDPOINT = "api.opensensemap.org"; +#define API_ENDPOINT "api.osem.vo1d.space" // SHA1 of the API SSL cert -static const char* API_FINGERPRINT = "0F B0 0C E0 FD 18 C2 0B 07 1C 21 AB A0 FF EF CC 09 62 57 A9"; -static const char* API_KEY = "..."; -static const char* ID_BOX = "57308b2c566b8d3c11114a9f"; -static const char* ID_SENSOR_WIFI = "..."; +#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 "XXXXXXXXXXX" +#define ID_BOX "57c7f3291421551100bf13c8" +#define ID_SENSOR_WIFI "57c7f3291421551100bf13ca" diff --git a/gps.h b/gps.h index 70df806..c414142 100644 --- a/gps.h +++ b/gps.h @@ -3,6 +3,8 @@ #include #include "config.h" +// TODO: refactor updateXXX members to be protected and called by getXXX ? + class Gps { protected: TinyGPSPlus gps; @@ -59,8 +61,12 @@ class Gps { return true; } + TinyGPSLocation& getLocation() { + return gps.location; + } + /** - * return an iso8266 formatted datestring with the current time + * return an iso8601 formatted datestring with the current time */ static char* getISODate() { // TODO: check why we need to allocate that much storage for a 20 character string for Serial printing?? diff --git a/mobile-sensebox.ino b/mobile-sensebox.ino index d4b848d..0b49e61 100644 --- a/mobile-sensebox.ino +++ b/mobile-sensebox.ino @@ -6,6 +6,7 @@ #include "TelnetPrint.h" #define DEBUG_OUT Serial +//#define DEBUG_OUT telnet //TelnetPrint telnet = TelnetPrint(); Storage storage = Storage(); @@ -23,15 +24,15 @@ void printState(WifiState wifiState) { DEBUG_OUT.println(wifiState.numUnencrypted); DEBUG_OUT.print("lat: "); - //DEBUG_OUT.print(gps.location.lat(), 6); + //DEBUG_OUT.print(gps.getLocation().lat(), 6); DEBUG_OUT.print(" lng: "); - //DEBUG_OUT.println(gps.location.lng(), 6); + //DEBUG_OUT.println(gps.getLocation().lng(), 6); DEBUG_OUT.println(gps.getISODate()); DEBUG_OUT.println(""); } -bool storeMeasurement(float lat, float lng, float value, char* timeStamp, char* sensorID) { +bool storeMeasurement(float lat, float lng, float value, const char* timeStamp, const char* sensorID) { Measurement m; m.lat = lat; m.lng = lng; @@ -51,7 +52,7 @@ void setup() { //gps.begin(); wifi.begin(); - //connectWifi(WIFI_SSID, WIFI_PASS); + wifi.connect(WIFI_SSID, WIFI_PASS); //delay(5000); // DEBUG oportunity to connect to network logger @@ -76,7 +77,7 @@ void setup() { void loop() { //pollGPS(); //DEBUG_OUT.pollClients(); - WifiState wifiState = wifi.scanWifi(WIFI_SSID); + WifiState wifiState = wifi.scan(WIFI_SSID); char* dateString = gps.getISODate(); // TODO: take other measurements (average them?) @@ -86,7 +87,7 @@ void loop() { */ // write measurements to file - if (storeMeasurement(51.2, 7.89, wifiState.numNetworks, dateString, "12341234123412341234123412341234")) { + if (storeMeasurement(51.25345345, 7.89, wifiState.numNetworks, "2016-08-31T00:03:10Z", ID_SENSOR_WIFI)) { DEBUG_OUT.print("measurement stored! storage size: "); } else { DEBUG_OUT.print("measurement store failed! storage size: "); @@ -94,17 +95,14 @@ void loop() { DEBUG_OUT.println(storage.size()); // TODO: connect to wifi, if available & not connected yet - // then upload local data, remove from storage - - // DEBUG: recall all previous measurements + + // then upload local data, remove from storage while (storage.size()) { String measure = storage.pop(); - //api.postMeasurement(measure.substring(0, 31), measure.substring(32)); - DEBUG_OUT.println("popped a measurement: "); - DEBUG_OUT.println(measure.substring(0, 31)); // size of sensorID - DEBUG_OUT.println(measure.substring(32)); // skip the newline char + api.postMeasurement(measure.substring(API_KEY_LENGTH + 1), measure.substring(0, API_KEY_LENGTH)); } printState(wifiState); + delay(5000); } diff --git a/storage.h b/storage.h index 32ac695..61a528c 100644 --- a/storage.h +++ b/storage.h @@ -2,45 +2,36 @@ #include #include -#include -#include "api.h" +#include "config.h" +#include "streampipe.h" -#define MEASUREMENT_JSON_SIZE (JSON_OBJECT_SIZE(5)) +#define MEASUREMENT_JSON_SIZE (JSON_OBJECT_SIZE(4)) // 4 properties (sensorID is excluded) struct Measurement { char timeStamp[20]; float lat; float lng; float value; - char sensorID[32]; + char sensorID[API_KEY_LENGTH]; }; class Storage { protected: - // not needed, as we post the data as json string? - /*Measurement deserializeMeasurement(char* s) { - Measurement m; - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.parseObject(s); - m.timeStamp = (const char[sizeof Measurement.timeStamp])jsonBuffer.strdup(root["date"]); - m.lat = root["lat"]; - m.lng = root["lng"]; - m.value = root["value"]; - m.sensorID = root["sensorId"]; - return m; - }*/ + void serializeMeasurement(Measurement& m, Print& f) { + // prepend the sensor ID to the json + f << m.sensorID << EOL; - bool serializeMeasurement(Measurement& m, Print& f) { - f.println(m.sensorID); - StaticJsonBuffer jsonBuffer; - JsonObject& root = jsonBuffer.createObject(); - // TODO: replace temporary data model - root["createdAt"] = m.timeStamp; - root["lat"] = m.lat; - root["lng"] = m.lng; - root["value"] = m.value; - root.printTo(f); - return root.success(); + // convert floats to strings + char val[10], lat[10], lng[10]; + dtostrf(m.value, 5, 6, val); + dtostrf(m.lat, 5, 6, lat); + dtostrf(m.lng, 5, 6, lng); + + f << "{\"value\":" << val + << ",\"createdAt\":\"" << m.timeStamp + << "\",\"lat\":" << lat + << ",\"lng\":" << lng + << "}" << EOL; } public: @@ -60,9 +51,9 @@ class Storage { String fileName = directory + ESP8266TrueRandom.uuidToString(uuid).substring(26); if (File f = SPIFFS.open(fileName, "w") ) { - bool success = serializeMeasurement(m, f); + serializeMeasurement(m, f); f.close(); - return success; + return true; } return false; } diff --git a/streampipe.h b/streampipe.h new file mode 100644 index 0000000..06485d8 --- /dev/null +++ b/streampipe.h @@ -0,0 +1,6 @@ +// add classic C stream pipe operator to arduino C + +#pragma once +#define EOL "\r\n" + +template inline Print &operator <<(Print &obj, T arg) { obj.print(arg); return obj; } diff --git a/wifi.h b/wifi.h index fbc1524..37fcd8b 100644 --- a/wifi.h +++ b/wifi.h @@ -14,7 +14,7 @@ class Wifi { WiFi.mode(WIFI_STA); } - WifiState scanWifi(String homeSSID) { + WifiState scan(String homeSSID) { // TODO: filter duplicate SSIDs! WifiState state; state.homeAvailable = false; @@ -32,7 +32,7 @@ class Wifi { return state; } - bool connectWifi(const char* ssid, const char* pass) { + bool connect(const char* ssid, const char* pass) { static const unsigned int timeout = 10000; // abort after 10 secs unsigned long start = millis();