forked from mkraats/hackair-v2-advanced
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhackair-v2-advanced.ino
442 lines (342 loc) · 13 KB
/
hackair-v2-advanced.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
/**
* @file hackair_wemos_2
* This example reads data from a sensor and sends it to the hackAIR platform
* using the Wemos integrated WiFi . This code
* assumes a DHT11 humidity sensor connected to pin D4.
*
* @author LoRAthens Air Quality team
* @author Thanasis Georgiou (Cleanup)
* @author Michiel van der Kraats ( added custom parameter to WiFi manager, mDNS support, Adafruit.IO and cleanup )
*
* This example is part of the hackAIR Arduino Library and is available
* in the Public Domain.
*/
#include <Arduino.h>
#include <DHT.h> // Adafruit's DHT sensor library https://github.com/adafruit/DHT-sensor-library
#include <DNSServer.h> // Local DNS Server used for redirecting all requests to the configuration portal
#include <ESP8266WebServer.h> // Local WebServer used to serve the configuration portal
#include <ESP8266WiFi.h> // ESP8266 Core WiFi Library (you most likely already have this in your sketch)
#include <ESP8266mDNS.h> // ESP8266 MDNS for .local name registration
#include <FS.h> // Arduino filesystem layer
#include <WiFiClientSecure.h> // Variant of WiFiClient with TLS support (from ESP82266 core wifi)
#include <WiFiManager.h> // https://github.com/tzapu/WiFiManager
#include <hackair.h> // https://github.com/hackair-project/hackAir-Arduino
#include "Adafruit_MQTT.h" // Adafruit.io MQTT library
#include "Adafruit_MQTT_Client.h" // Adafruit.io MQTT library
#include <ArduinoJson.h> // https://github.com/bblanchon/ArduinoJson
#include <InfluxDb.h> // InfluxDB support
// Configuration
#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
// 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 ""
// No more configuration below this line
char hackair_api_token[44]; // hackAIR API token to be collected via WiFiManager on first start
char sensebox_id[40]; // openSenseMap senseBox ID
char osem_token[80]; // openSenseMap senseBox access token
//flag for saving data
bool shouldSaveConfig = false;
//callback notifying us of the need to save config
void saveConfigCallback () {
Serial.println("Should save config");
shouldSaveConfig = true;
}
// initialise the PM10/PM25 sensor
hackAIR sensor(SENSOR_SDS011);
// Setup the temperature and humidity sensor (pin D4)
DHT dht(D4, DHT22);
// Measurement interval
const unsigned long minutes_time_interval = 5;
// Setup ADC to measure Vcc (battery voltage)
ADC_MODE(ADC_VCC);
// Create a secure client for sending data using HTTPs
WiFiClientSecure client;
// 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);
// Struct for storing sensor data
struct hackAirData data;
unsigned long previous_millis = 0;
void setup() {
// Open serial communications
Serial.begin(9600);
Serial.println("\nHackAIR v2 sensor");
if ( DEBUG == "1" ) {
Serial.println("Debug mode on, not sending data to hackAIR");
}
Serial.println("Ready");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
pinMode(BUILTIN_LED, OUTPUT);
// read config from filesystem
if (SPIFFS.begin()) {
Serial.println("mounted file system");
if (SPIFFS.exists("/config.json")) {
//file exists, reading and loading
Serial.println("reading config file");
File configFile = SPIFFS.open("/config.json", "r");
if (configFile) {
Serial.println("opened config file");
size_t size = configFile.size();
// Allocate a buffer to store contents of the file.
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonBuffer jsonBuffer;
JsonObject& json = jsonBuffer.parseObject(buf.get());
json.prettyPrintTo(Serial);
if (json.success()) {
Serial.println("\nparsed json");
strcpy(hackair_api_token, json["hackair_api_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"];
strcpy(sensebox_id, my_sensebox_id);
Serial.println(sensebox_id);
} else {
Serial.println("failed to load json config");
}
}
}
} else {
Serial.println("failed to mount FS");
}
// Initialize the PM sensor
sensor.begin();
sensor.enablePowerControl();
sensor.turnOn();
sensor.clearData(data);
// Initialize temperature and humidity sensor
dht.begin();
// Initialize the WiFi connection
WiFiManager wifiManager;
wifiManager.setSaveConfigCallback(saveConfigCallback);
WiFiManagerParameter custom_hackair_api_token("hackair_api_token", "hackAIR Access Key", hackair_api_token, 44);
WiFiManagerParameter custom_sensebox_id("sensebox_id", "openSenseMap senseBox ID", sensebox_id, 40);
WiFiManagerParameter custom_osem_token("osem_token", "senseBox access token", osem_token, 80);
wifiManager.addParameter(&custom_hackair_api_token);
wifiManager.addParameter(&custom_sensebox_id);
wifiManager.addParameter(&custom_osem_token);
// start the sensor once with the following line uncommented to reset previous WiFi settings
// wifiManager.resetSettings();
if (!wifiManager.autoConnect("ESP-wemos")) {
Serial.println("failed to connect, please push reset button and try again");
delay(3000);
ESP.reset();
delay(10000);
}
//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
if (shouldSaveConfig) {
Serial.println("saving config");
DynamicJsonBuffer jsonBuffer;
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");
}
json.printTo(Serial);
json.printTo(configFile);
configFile.close();
//end save
}
// check if we have connected to the WiFi
Serial.println("Network connected");
Serial.println("Local IP address: ");
Serial.print(WiFi.localIP());
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);
Serial.print(".");
}
double pm25 = data.pm25;
double pm10 = data.pm10;
float env_temp = dht.readTemperature();
float env_hum = dht.readHumidity();
int error = 0;
Serial.println("Measuring...");
// 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);
}
}
// 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" ) {
// send data to network
if ( ADAFRUIT_IO_ENABLE == "1" ) {
MQTT_connect();
pm25_feed.publish(data.pm25);
pm10_feed.publish(data.pm10);
}
// 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");
client.print("Host: api.hackair.eu\r\n");
client.print("Connection: close\r\n");
client.print("Authorization: ");
client.println(hackair_api_token);
client.print("Accept: application/vnd.hackair.v1+json\r\n");
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");
}
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: ");
Serial.println(hackair_api_token); // write API token
Serial.print("Chip ID: ");
Serial.println(chip_id);
Serial.print("Temperature: ");
Serial.println(env_temp);
Serial.print("Humidity: ");
Serial.println(env_hum);
Serial.println(dataJson); // write sensor values to serial for debug
if ( INFLUXDB_ENABLE == "1" ) {
// connect to influxdb
Influxdb influx(INFLUXDB_HOST); // port defaults to 8086
influx.setDbAuth(INFLUXDB_DATABASE, INFLUXDB_USER, INFLUXDB_PASS); // with authentication
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);
client.println("Writing to InfluxDB");
}
}
// Turn off sensor and go to sleep
sensor.turnOff();
unsigned long current_millis = millis();
while (current_millis <
(previous_millis + (minutes_time_interval * 60 * 1000))) {
delay(10000);
current_millis = millis();
}
previous_millis = current_millis;
sensor.turnOn();
}
// define functions
void MQTT_connect() {
int8_t ret;
// Stop if already connected.
if (mqtt.connected()) {
return;
}
Serial.print("Connecting to MQTT... ");
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 Connected!");
}