diff --git a/hackair-v2-advanced.ino b/hackair-v2-advanced.ino index b0fd89a..ebf6b89 100644 --- a/hackair-v2-advanced.ino +++ b/hackair-v2-advanced.ino @@ -12,6 +12,9 @@ * in the Public Domain. */ +#undef ADAFRUIT_MQTT_SUPPORT // Change "#undef" to "#define" to enable MQTT support +#undef INFLUXDB_SUPPORT // Change "#undef" to "#define" to enable InfluxDB support + #include #include // Adafruit's DHT sensor library https://github.com/adafruit/DHT-sensor-library #include // Local DNS Server used for redirecting all requests to the configuration portal @@ -22,34 +25,37 @@ #include // Variant of WiFiClient with TLS support (from ESP82266 core wifi) #include // https://github.com/tzapu/WiFiManager #include // https://github.com/hackair-project/hackAir-Arduino -#include "Adafruit_MQTT.h" // Adafruit.io MQTT library -#include "Adafruit_MQTT_Client.h" // Adafruit.io MQTT library +#ifdef ADAFRUIT_MQTT_SUPPORT +# include "Adafruit_MQTT.h" // Adafruit.io MQTT library +# include "Adafruit_MQTT_Client.h" // Adafruit.io MQTT library +#endif // ADAFRUIT_MQTT_SUPPORT #include // https://github.com/bblanchon/ArduinoJson -#include // InfluxDB support +#ifdef INFLUXDB_SUPPORT +# include // InfluxDB support +#endif // INFLUXDB_SUPPORT // Configuration -#define HOSTNAME "hackair" // hostname to use for MDNS under the .local extension ( hackair.local ) +#define HOSTNAME "hackair" // hostname to use for MDNS under the .local extension ( hackair.local ) #define DEBUG "0" // set this to 1 to stop sending data to the hackAIR platform -#define ADAFRUIT_IO_ENABLE "0" // set this to 1 to enable Adafruit.io sending -#define INFLUXDB_ENABLE "0" // set this to 1 to enable InfluxDB support +#ifdef ADAFRUIT_MQTT_SUPPORT // Adafruit MQTT - -#define AIO_SERVER "io.adafruit.com" -#define AIO_SERVERPORT 8883 -#define AIO_USERNAME "AIO_USERNAME" -#define AIO_KEY "AIO_KEY" -#define AIO_PM25 "PM25FEED" -#define AIO_PM10 "PM10FEED" - -// InfluxDB support - -#define INFLUXDB_HOST "" -#define INFLUXDB_PORT "8086" -#define INFLUXDB_DATABASE "aq" -#define INFLUXDB_USER "" -#define INFLUXDB_PASS "" +# define AIO_SERVER "io.adafruit.com" +# define AIO_SERVERPORT 8883 +# define AIO_USERNAME "AIO_USERNAME" +# define AIO_KEY "AIO_KEY" +# define AIO_PM25 "PM25FEED" +# define AIO_PM10 "PM10FEED" +#endif // ADAFRUIT_MQTT_SUPPORT + +#ifdef INFLUXDB_SUPPORT +# define INFLUXDB_HOST "" +# define INFLUXDB_PORT "8086" +# define INFLUXDB_DATABASE "aq" +# define INFLUXDB_USER "" +# define INFLUXDB_PASS "" +#endif // INFLUXDB_SUPPORT // No more configuration below this line @@ -57,11 +63,11 @@ char hackair_api_token[44]; // hackAIR API token to be collected via WiFiManager char sensebox_id[40]; // openSenseMap senseBox ID char osem_token[80]; // openSenseMap senseBox access token -//flag for saving data +// flag for saving data bool shouldSaveConfig = false; -//callback notifying us of the need to save config -void saveConfigCallback () { +// callback notifying us of the need to save config +void saveConfigCallback() { Serial.println("Should save config"); shouldSaveConfig = true; } @@ -81,10 +87,12 @@ ADC_MODE(ADC_VCC); // Create a secure client for sending data using HTTPs WiFiClientSecure client; +#ifdef ADAFRUIT_MQTT_SUPPORT // create the objects for Adafruit IO Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_KEY); Adafruit_MQTT_Publish pm25_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME AIO_PM25); Adafruit_MQTT_Publish pm10_feed = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME AIO_PM10); +#endif // ADAFRUIT_MQTT_SUPPORT // Struct for storing sensor data struct hackAirData data; @@ -92,16 +100,13 @@ unsigned long previous_millis = 0; void setup() { // Open serial communications - Serial.begin(9600); Serial.println("\nHackAIR v2 sensor"); - if ( DEBUG == "1" ) { - + if (DEBUG == "1") { Serial.println("Debug mode on, not sending data to hackAIR"); - } - + Serial.println("Ready"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); @@ -109,11 +114,10 @@ void setup() { pinMode(BUILTIN_LED, OUTPUT); // read config from filesystem - - if (SPIFFS.begin()) { + if (SPIFFS.begin()) { Serial.println("mounted file system"); if (SPIFFS.exists("/config.json")) { - //file exists, reading and loading + // file exists, reading and loading Serial.println("reading config file"); File configFile = SPIFFS.open("/config.json", "r"); if (configFile) { @@ -124,21 +128,20 @@ void setup() { configFile.readBytes(buf.get(), size); DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.parseObject(buf.get()); + JsonObject &json = jsonBuffer.parseObject(buf.get()); json.prettyPrintTo(Serial); if (json.success()) { Serial.println("\nparsed json"); - strcpy(hackair_api_token, json["hackair_api_token"]); + strcpy(hackair_api_token, json["hackair_api_token"]); - const char* my_osem_token = json["osem_token"]; + const char *my_osem_token = json["osem_token"]; strcpy(osem_token, my_osem_token); Serial.println(osem_token); - const char* my_sensebox_id = json["sensebox_id"]; + const char *my_sensebox_id = json["sensebox_id"]; strcpy(sensebox_id, my_sensebox_id); Serial.println(sensebox_id); - } else { Serial.println("failed to load json config"); } @@ -147,7 +150,7 @@ void setup() { } else { Serial.println("failed to mount FS"); } - + // Initialize the PM sensor sensor.begin(); sensor.enablePowerControl(); @@ -181,20 +184,20 @@ void setup() { delay(10000); } - //read updated parameters + // read updated parameters strcpy(hackair_api_token, custom_hackair_api_token.getValue()); strcpy(sensebox_id, custom_sensebox_id.getValue()); strcpy(osem_token, custom_osem_token.getValue()); - //save the custom parameters to FS + // save the custom parameters to FS if (shouldSaveConfig) { Serial.println("saving config"); DynamicJsonBuffer jsonBuffer; - JsonObject& json = jsonBuffer.createObject(); + JsonObject &json = jsonBuffer.createObject(); json["hackair_api_token"] = hackair_api_token; json["sensebox_id"] = sensebox_id; json["osem_token"] = osem_token; - + File configFile = SPIFFS.open("/config.json", "w"); if (!configFile) { Serial.println("failed to open config file for writing"); @@ -203,9 +206,9 @@ void setup() { json.printTo(Serial); json.printTo(configFile); configFile.close(); - //end save + // end save } - + // check if we have connected to the WiFi Serial.println("Network connected"); Serial.println("Local IP address: "); @@ -213,14 +216,12 @@ void setup() { Serial.println("Default Gateway: "); Serial.print(WiFi.gatewayIP()); MDNS.begin(HOSTNAME); - } void loop() { - int chip_id = ESP.getChipId(); float vdd = ESP.getVcc() / 500.0; - + while (WiFi.status() != WL_CONNECTED) { WiFi.hostname("hackair"); delay(500); @@ -232,58 +233,49 @@ void loop() { float env_temp = dht.readTemperature(); float env_hum = dht.readHumidity(); - + int error = 0; Serial.println("Measuring..."); -// Measure data + // Measure data sensor.clearData(data); sensor.readAverageData(data, 60); // 60 averages if (data.error != H_ERROR_SENSOR) { - // Compensate for humidity - float humidity = dht.readHumidity(); - if (isnan(humidity)) { - data.error |= H_ERROR_HUMIDITY; - } else { - sensor.humidityCompensation(data, humidity); - } + // Compensate for humidity + float humidity = dht.readHumidity(); + if (isnan(humidity)) { + data.error |= H_ERROR_HUMIDITY; + } else { + sensor.humidityCompensation(data, humidity); + } } // construct the JSON to send to the hackAIR platform String dataJson = "{\"reading\":{\"PM2.5_AirPollutantValue\":\""; - dataJson += data.pm25; - dataJson += "\",\"PM10_AirPollutantValue\":\""; - dataJson += data.pm10; - dataJson += "\"},\"battery\":\""; - dataJson += vdd; - dataJson += "\",\"tamper\":\""; - dataJson += "0"; - dataJson += "\",\"error\":\""; - dataJson += data.error; - dataJson += "\"}"; - - - - if ( DEBUG != "1" ) { - + dataJson += data.pm25; + dataJson += "\",\"PM10_AirPollutantValue\":\""; + dataJson += data.pm10; + dataJson += "\"},\"battery\":\""; + dataJson += vdd; + dataJson += "\",\"tamper\":\""; + dataJson += "0"; + dataJson += "\",\"error\":\""; + dataJson += data.error; + dataJson += "\"}"; + + if (DEBUG != "1") { // send data to network - if ( ADAFRUIT_IO_ENABLE == "1" ) { - - MQTT_connect(); - pm25_feed.publish(data.pm25); - pm10_feed.publish(data.pm10); - - } +#ifdef ADAFRUIT_MQTT_SUPPORT + MQTT_connect(); + pm25_feed.publish(data.pm25); + pm10_feed.publish(data.pm10); +#endif // ADAFRUIT_MQTT_SUPPORT - - // Send the data to the hackAIR server - Serial.println("Sending data to hackAIR platform..."); - if (client.connect("api.hackair.eu", 443)) { Serial.println("Connected to api.hackair.eu"); client.print("POST /sensors/arduino/measurements HTTP/1.1\r\n"); @@ -300,66 +292,56 @@ void loop() { client.println(dataJson); Serial.println(dataJson); delay(500); - - while (client.available()) { - - char c = client.read(); - - Serial.print(c); - } - client.stop(); - } else { - - Serial.println("Unable to send data to hackAIR platform\n"); - } - delay(1000); - - // Send data to openSenseMap - if ( sensebox_id != "" && osem_token != "") { - // Send the data to the openSenseMap server - Serial.println("Sending data to openSenseMap platform..."); - Serial.println(sensebox_id); - Serial.println("OSEM:\n"); - Serial.println(osem_token); - Serial.println("\nEND\n"); - - if (client.connect("api.opensensemap.org", 443)) { - Serial.println("Connected to api.opensensemap.org"); - client.print("POST /boxes/"); - client.print(sensebox_id); - client.print("/data?hackair=true HTTP/1.1\r\n"); - client.print("Host: api.opensensemap.org\r\n"); - client.print("Connection: close\r\n"); - client.print("Authorization: "); - client.println(osem_token); - client.print("Cache-Control: no-cache\r\n"); - client.print("Content-Type: application/json\r\n"); - client.print("Content-Length: "); - client.println(dataJson.length() + 2); - client.println(""); - client.println(dataJson); - Serial.println(dataJson); - delay(500); while (client.available()) { char c = client.read(); - Serial.print(c); } + client.stop(); + } else { + Serial.println("Unable to send data to hackAIR platform\n"); } - client.stop(); delay(1000); + + // Send data to openSenseMap + if (sensebox_id != "" && osem_token != "") { + // Send the data to the openSenseMap server + Serial.println("Sending data to openSenseMap platform..."); + Serial.println(sensebox_id); + Serial.println("OSEM:\n"); + Serial.println(osem_token); + Serial.println("\nEND\n"); + + if (client.connect("api.opensensemap.org", 443)) { + Serial.println("Connected to api.opensensemap.org"); + client.print("POST /boxes/"); + client.print(sensebox_id); + client.print("/data?hackair=true HTTP/1.1\r\n"); + client.print("Host: api.opensensemap.org\r\n"); + client.print("Connection: close\r\n"); + client.print("Authorization: "); + client.println(osem_token); + client.print("Cache-Control: no-cache\r\n"); + client.print("Content-Type: application/json\r\n"); + client.print("Content-Length: "); + client.println(dataJson.length() + 2); + client.println(""); + client.println(dataJson); + Serial.println(dataJson); + delay(500); + + while (client.available()) { + char c = client.read(); + Serial.print(c); + } + } + client.stop(); + delay(1000); } else { - Serial.println("Unable to send data to openSenseMap platform\n"); - } - - } else { - // DEBUG is on, output values to serial but don't send to network - Serial.println("DEBUG"); Serial.println(DEBUG); Serial.print("hackair API token: "); @@ -372,41 +354,35 @@ void loop() { Serial.println(env_hum); Serial.println(dataJson); // write sensor values to serial for debug - if ( INFLUXDB_ENABLE == "1" ) { +#ifdef INFLUXDB_SUPPORT + // connect to influxdb + Influxdb influx(INFLUXDB_HOST); // port defaults to 8086 + influx.setDbAuth(INFLUXDB_DATABASE, INFLUXDB_USER, INFLUXDB_PASS); // with authentication - // connect to influxdb + String influx_chip_id = String(chip_id); - Influxdb influx(INFLUXDB_HOST); // port defaults to 8086 - influx.setDbAuth(INFLUXDB_DATABASE, INFLUXDB_USER, INFLUXDB_PASS); // with authentication + // create a measurement object + InfluxData measurement("airquality"); + measurement.addTag("device", influx_chip_id); + measurement.addTag("sensor", "sds11"); + measurement.addValue("pm10", data.pm10); + measurement.addValue("pm25", data.pm25); + measurement.addValue("temperature", env_temp); + measurement.addValue("humidity", env_hum); - String influx_chip_id = String(chip_id); - - // create a measurement object - InfluxData measurement ("airquality"); - measurement.addTag("device", influx_chip_id); - measurement.addTag("sensor", "sds11"); - measurement.addValue("pm10", data.pm10); - measurement.addValue("pm25", data.pm25); - measurement.addValue("temperature", env_temp); - measurement.addValue("humidity", env_hum); + // write it into db + influx.write(measurement); - // write it into db - influx.write(measurement); - - client.println("Writing to InfluxDB"); + client.println("Writing to InfluxDB"); +#endif // INFLUXDB_SUPPORT + } - } - - } - // Turn off sensor and go to sleep sensor.turnOff(); unsigned long current_millis = millis(); - while (current_millis < - (previous_millis + (minutes_time_interval * 60 * 1000))) { + while (current_millis < (previous_millis + (minutes_time_interval * 60 * 1000))) { delay(10000); current_millis = millis(); - } previous_millis = current_millis; sensor.turnOn(); @@ -414,6 +390,7 @@ void loop() { // define functions +#ifdef ADAFRUIT_MQTT_SUPPORT void MQTT_connect() { int8_t ret; @@ -426,17 +403,18 @@ void MQTT_connect() { uint8_t retries = 3; while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected - Serial.println(mqtt.connectErrorString(ret)); - Serial.println(ret); - Serial.println("Retrying MQTT connection in 5 seconds..."); - mqtt.disconnect(); - delay(5000); // wait 5 seconds - retries--; - if (retries == 0) { - // basically die and wait for WDT to reset me - while (1); - } + Serial.println(mqtt.connectErrorString(ret)); + Serial.println(ret); + Serial.println("Retrying MQTT connection in 5 seconds..."); + mqtt.disconnect(); + delay(5000); // wait 5 seconds + retries--; + if (retries == 0) { + // basically die and wait for WDT to reset me + while (true) ; + } } Serial.println("MQTT Connected!"); } +#endif // ADAFRUIT_MQTT_SUPPORT