initial version

esp8266-bme280
noerw 8 years ago
commit 50085550dd

@ -0,0 +1,70 @@
/**
* sets up a telnet server, which can then be used for debug logging purposes
* provides all Print methods.
* begin() must be called in the setup!
* pollClients() must be called in the loop!
* to disable the routines, just skip the begin() call in the setup
*/
# pragma once
#include <ESP8266WiFi.h>
#define MAX_TELNET_CLIENTS 3
class TelnetPrint : public Print {
protected:
WiFiServer server;
WiFiClient clients[MAX_TELNET_CLIENTS];
public:
TelnetPrint(uint16_t port=23) : server(port) {}
void begin() {
server.begin();
server.setNoDelay(true);
}
void pollClients() {
//check clients for data
for(uint16_t i = 0; i < MAX_TELNET_CLIENTS; i++){
if (clients[i] && clients[i].connected()){
while(clients[i].available()) clients[i].read();
}
}
// try to establish pending connections
if (server.hasClient()) {
for(uint8_t i = 0; i < MAX_TELNET_CLIENTS; i++){
//find free/disconnected spot
if (!clients[i] || !clients[i].connected()){
if(clients[i]) clients[i].stop();
clients[i] = server.available();
return;
}
}
//no free/disconnected spot so reject
WiFiClient serverClient = server.available();
serverClient.stop();
}
}
// 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()){
clients[i].write(s);
}
}
return 1;
}
// Is only called for Strings, not char arrays
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;
}
};

27
api.h

@ -0,0 +1,27 @@
#pragma once
#include <WiFiClientSecure.h>
#include "config.h"
WiFiClientSecure api;
bool postMeasurement(String m, String sensorID) {
//telnet.print("Connecting to API.. ");
if (!api.connect(API_ENDPOINT, 443)) {
//telnet.println("connection failed");
return false;
}
if (api.verify(API_FINGERPRINT, API_ENDPOINT)) {
//telnet.println("certificate matches");
} else {
//telnet.println("certificate doesn't match");
return false;
}
String url = "/boxes/" + String(ID_BOX) + "/" + sensorID;
api.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");
return true;
}

@ -0,0 +1,19 @@
/* WiFi (ESP8266) */
#pragma once
static const char* WIFI_SSID = "Elmo";
static const char* WIFI_PASS = "FreiBier123";
/* 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
/* API (openSenseMap) */
static const char* API_ENDPOINT = "api.opensensemap.org";
// 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 = "...";

52
gps.h

@ -0,0 +1,52 @@
#pragma once
#include <TinyGPS++.h>
#include <Time.h>
#include "config.h"
/* GPS */
TinyGPSPlus gps;
// allocate the maximum buffer size, so we can reduce the Serial polling interval to a minimum
//SoftwareSerial gpsSerial(GPS_RX_PIN, GPS_TX_PIN, false, 27*49);
void pollGPS() {
while (Serial.available() > 0)
gps.encode(Serial.read());
}
bool updateLocation() {
// abort if the GPS device does not push valid data within one update cycle
static const unsigned int timeout = 2 * GPS_INTERVAL;
unsigned long start = millis();
do {
pollGPS();
if (millis() - start > timeout) return false;
} while (!gps.location.isUpdated());
return true;
}
bool updateTime() {
// abort if the GPS device does not push valid data within one update cycle
static const unsigned int timeout = 2 * GPS_INTERVAL;
unsigned long start = millis();
do {
pollGPS();
if (millis() - start > timeout) return false;
} while ( !(gps.date.isUpdated() && gps.time.isUpdated()) );
// in case we didnt timeout, resync the local clock
setTime(gps.time.hour(), gps.time.minute(), gps.time.second(),
gps.date.day(), gps.date.month(), gps.date.year());
return true;
}
/* UTILS */
String getISODate() {
char result[16] = { 0 };
sprintf(result, "%04d-%02d-%02d-T%02d:%02d:%02dZ",
year(), month(), day(), hour(), minute(), second());
return (String)result;
}

@ -0,0 +1,95 @@
#include "config.h"
#include "gps.h"
#include "wifi.h"
#include "api.h"
#include "storage.h"
#include "TelnetPrint.h"
#define DEBUG_OUT Serial
//TelnetPrint telnet = TelnetPrint();
Storage storage = Storage();
/* UTILS */
void printState(WifiState wifi) {
DEBUG_OUT.print("homeAvailable: ");
DEBUG_OUT.println(wifi.homeAvailable);
DEBUG_OUT.print("numNetworks: ");
DEBUG_OUT.println(wifi.numNetworks);
DEBUG_OUT.print("numUnencrypted: ");
DEBUG_OUT.println(wifi.numUnencrypted);
DEBUG_OUT.print("lat: ");
//DEBUG_OUT.print(gps.location.lat(), 6);
DEBUG_OUT.print(" lng: ");
//DEBUG_OUT.println(gps.location.lng(), 6);
DEBUG_OUT.println(getISODate());
DEBUG_OUT.println("");
}
/* MAIN ENTRY POINTS */
void setup() {
//Serial.begin(9600); // GPS reciever
DEBUG_OUT.begin(115200);
WiFi.mode(WIFI_STA);
connectWifi(WIFI_SSID, WIFI_PASS);
delay(5000);
// wait until we got a first fix from GPS, and thus an initial time
/*DEBUG_OUT.print("Getting GPS fix..");
while (!updateLocation()) { DEBUG_OUT.print("."); }
DEBUG_OUT.print(" done! ");*/
DEBUG_OUT.println(getISODate());
DEBUG_OUT.println("Setup done!\n");
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(storage.begin());
digitalWrite(D9, HIGH);
}
bool firstLoop = true;
void loop() {
//pollGPS();
//DEBUG_OUT.pollClients();
WifiState wifi = scanWifi(WIFI_SSID);
// TODO: take other measurements (average them?)
/*
if(!updateLocation()) DEBUG_OUT.println("GPS timed out (location)");
if(!updateTime()) DEBUG_OUT.println("GPS timed out (time)");
*/
// TODO: write measurements to file
// TODO: connect to wifi, if available & not connected yet
// then upload local data, clean up
printState(wifi);
//delay(20000);
if (firstLoop) {
Measurement testMeasure;
testMeasure.lat = 51.2;
testMeasure.lng = 7.89;
testMeasure.value = 66.6;
strcpy(testMeasure.timeStamp, "2016-08-21T09:07:22Z");
strcpy(testMeasure.sensorID, "123457812345678123456781234567");
if (storage.add(testMeasure)) {
DEBUG_OUT.println("measurement stored! storage size: ");
} else {
DEBUG_OUT.println("measurement store failed! storage size: ");
}
DEBUG_OUT.println(storage.size());
firstLoop = false;
}
}

@ -0,0 +1,104 @@
#pragma once
#include <FS.h>
#include <ESP8266TrueRandom.h>
#include <ArduinoJson.h>
#include "api.h"
#define MEASUREMENT_JSON_SIZE (JSON_OBJECT_SIZE(5))
struct Measurement {
char timeStamp[21];
float lat;
float lng;
float value;
char sensorID[32];
};
// TODO: TEST THIS THING!!
class Storage {
protected:
// not needed, as we post the data as json string?
/*Measurement deserializeMeasurement(char* s) {
Measurement m;
StaticJsonBuffer<MEASUREMENT_JSON_SIZE> 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;
}*/
bool serializeMeasurement(Measurement& m, Print& f) {
StaticJsonBuffer<MEASUREMENT_JSON_SIZE> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
// TODO: fix data model
root["date"] = m.timeStamp;
root["lat"] = m.lat;
root["lng"] = m.lng;
root["value"] = m.value;
root["sensorId"] = m.sensorID;
root.printTo(f);
f.print("\n");
return root.success();
}
public:
Storage() {}
size_t begin() {
SPIFFS.begin();
FSInfo fs;
SPIFFS.info(fs);
return fs.totalBytes - fs.usedBytes;
}
bool add(Measurement& m, const char* directory = "/measurements/") {
byte uuid[16];
ESP8266TrueRandom.uuid(uuid);
// we need to shorten the uuid, as long filenames are not supported it seems..?
String fileName = directory + ESP8266TrueRandom.uuidToString(uuid).substring(26);
if (File f = SPIFFS.open(fileName, "w") ) {
bool success = serializeMeasurement(m, f);
f.close();
return success;
}
return false;
}
String pop(const char* directory = "/measurements/") {
Dir dir = SPIFFS.openDir(directory);
String measurement = "";
if (!dir.next()) return measurement; // abort if storage is empty
String fileName = dir.fileName();
File f = dir.openFile("r");
measurement = f.readStringUntil('\n'); // assumes that the data does not contain any \n!
f.close();
SPIFFS.remove(fileName);
return measurement;
}
/*const Measurement pop(const char* directory = "/measurements/") {
Dir dir = SPIFFS.openDir(directory);
Measurement m;
if (!dir.next()) return m; // abort if storage is empty
String fileName = dir.fileName();
File f = dir.openFile("r");
m = deserializeMeasurement(f);
f.close();
SPIFFS.remove(fileName);
return m;
}*/
uint16_t size(const char* directory = "/measurements/") {
Dir dir = SPIFFS.openDir(directory);
uint16_t i = 0;
while(dir.next()) i++;
return i;
}
};

@ -0,0 +1,47 @@
#pragma once
#include <ESP8266WiFi.h>
#include "config.h"
/* WIFI */
struct WifiState {
bool homeAvailable;
unsigned int numNetworks;
unsigned int numUnencrypted;
};
// TODO: filter duplicate SSIDs!
WifiState scanWifi(String homeSSID) {
WifiState state;
state.homeAvailable = false;
state.numNetworks = WiFi.scanNetworks(false, false);
state.numUnencrypted = 0;
for (unsigned int i = 0; i < state.numNetworks; i++) {
if (WiFi.encryptionType(i) == ENC_TYPE_NONE)
++state.numUnencrypted;
if (WiFi.SSID(i) == homeSSID)
state.homeAvailable = true;
}
return state;
}
bool connectWifi(const char* ssid, const char* pass) {
static const unsigned int timeout = 10000; // abort after 10 secs
unsigned long start = millis();
WiFi.disconnect();
WiFi.begin(ssid, pass);
//telnet.print("Connecting to WiFi.");
while (WiFi.status() != WL_CONNECTED && millis() - start < timeout) {
delay(200);
//telnet.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
//telnet.println("connected!");
return true;
}
//telnet.println(" timeout");
return false;
}
Loading…
Cancel
Save