1 | #define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // 360 minutes - 4 times a day
|
2 |
|
3 | #include <WiFi.h>
|
4 | #include <ESPmDNS.h>
|
5 | #include <WiFiUdp.h>
|
6 | #include <ArduinoOTA.h>
|
7 | #include <EEPROM.h>
|
8 | #include "bsec.h"
|
9 | /* Configure the BSEC library with information about the sensor
|
10 | 18v/33v = Voltage at Vdd. 1.8V or 3.3V
|
11 | 3s/300s = BSEC operating mode, BSEC_SAMPLE_RATE_LP or BSEC_SAMPLE_RATE_ULP
|
12 | 4d/28d = Operating age of the sensor in days
|
13 | generic_18v_3s_4d
|
14 | generic_18v_3s_28d
|
15 | generic_18v_300s_4d
|
16 | generic_18v_300s_28d
|
17 | generic_33v_3s_4d
|
18 | generic_33v_3s_28d
|
19 | generic_33v_300s_4d
|
20 | generic_33v_300s_28d
|
21 | */
|
22 | const uint8_t bsec_config_iaq[] = {
|
23 | #include "config/generic_33v_3s_4d/bsec_iaq.txt"
|
24 | };
|
25 |
|
26 |
|
27 | // Helper functions declarations
|
28 | void checkIaqSensorStatus(void);
|
29 | void errLeds(void);
|
30 | void loadState(void);
|
31 | void updateState(void);
|
32 |
|
33 |
|
34 | // Create an object of the class Bsec
|
35 | Bsec iaqSensor;
|
36 | uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE] = {0};
|
37 | uint16_t stateUpdateCounter = 0;
|
38 |
|
39 | String output;
|
40 |
|
41 | const char* ssid = "xxx";
|
42 | const char* password = "xxxxx";
|
43 |
|
44 | uint8_t i;
|
45 | bool ConnectionEstablished; // Flag for successfully handled connection
|
46 |
|
47 | #define MAX_TELNET_CLIENTS 2
|
48 |
|
49 | WiFiServer TelnetServer(23);
|
50 | WiFiClient TelnetClient[MAX_TELNET_CLIENTS];
|
51 |
|
52 | void setup()
|
53 | {
|
54 | Serial.begin(115200);
|
55 | Serial.println("Over The Air and Telnet Example");
|
56 |
|
57 | Serial.printf("Sketch size: %u\n", ESP.getSketchSize());
|
58 | Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace());
|
59 |
|
60 | WiFi.mode(WIFI_STA);
|
61 | WiFi.begin(ssid, password);
|
62 | Wire.begin();
|
63 | iaqSensor.begin(BME680_I2C_ADDR_SECONDARY, Wire);
|
64 | output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix);
|
65 | Serial.println(output);
|
66 | checkIaqSensorStatus();
|
67 |
|
68 | iaqSensor.setConfig(bsec_config_iaq);
|
69 | checkIaqSensorStatus();
|
70 |
|
71 | loadState();
|
72 |
|
73 |
|
74 | bsec_virtual_sensor_t sensorList[10] = {
|
75 | BSEC_OUTPUT_RAW_TEMPERATURE,
|
76 | BSEC_OUTPUT_RAW_PRESSURE,
|
77 | BSEC_OUTPUT_RAW_HUMIDITY,
|
78 | BSEC_OUTPUT_RAW_GAS,
|
79 | BSEC_OUTPUT_IAQ,
|
80 | BSEC_OUTPUT_STATIC_IAQ,
|
81 | BSEC_OUTPUT_CO2_EQUIVALENT,
|
82 | BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
|
83 | BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
|
84 | BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
|
85 | };
|
86 |
|
87 | iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
|
88 | checkIaqSensorStatus();
|
89 |
|
90 | // ... Give ESP 10 seconds to connect to station.
|
91 | unsigned long startTime = millis();
|
92 | Serial.print("Waiting for wireless connection ");
|
93 | while (WiFi.status() != WL_CONNECTED && millis() - startTime < 10000)
|
94 | {
|
95 | delay(200);
|
96 | Serial.print(".");
|
97 | }
|
98 | Serial.println();
|
99 |
|
100 | while (WiFi.status() != WL_CONNECTED)
|
101 | {
|
102 | Serial.println("Connection Failed! Rebooting...");
|
103 | delay(3000);
|
104 | ESP.restart();
|
105 | }
|
106 |
|
107 | Serial.print("IP address: ");
|
108 | Serial.println(WiFi.localIP());
|
109 |
|
110 | Serial.println("Starting Telnet server");
|
111 | TelnetServer.begin();
|
112 | TelnetServer.setNoDelay(true);
|
113 |
|
114 | pinMode(LED_BUILTIN, OUTPUT); // initialize onboard LED as output
|
115 |
|
116 | // OTA
|
117 |
|
118 | // Port defaults to 8266
|
119 | // ArduinoOTA.setPort(8266);
|
120 |
|
121 | // Hostname defaults to esp8266-[ChipID]
|
122 | // ArduinoOTA.setHostname("myesp8266");
|
123 |
|
124 | // No authentication by default
|
125 | ArduinoOTA.setPassword((const char *)"1234");
|
126 |
|
127 | ArduinoOTA.onStart([]() {
|
128 | Serial.println("Start");
|
129 | });
|
130 |
|
131 | ArduinoOTA.onEnd([]() {
|
132 | Serial.println("\nEnd");
|
133 | });
|
134 |
|
135 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
136 | Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
|
137 | });
|
138 | ArduinoOTA.onError([](ota_error_t error) {
|
139 | Serial.printf("Error[%u]: ", error);
|
140 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
|
141 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
|
142 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
|
143 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
|
144 | else if (error == OTA_END_ERROR) Serial.println("End Failed");
|
145 | });
|
146 |
|
147 | ArduinoOTA.begin();
|
148 | }
|
149 |
|
150 | void loop() {
|
151 | //ArduinoOTA.handle(); // Wait for OTA connection
|
152 | blinkLED(); // Blink LED
|
153 | Telnet(); // Handle telnet connections
|
154 | unsigned long time_trigger = millis();
|
155 | if (iaqSensor.run()) { // If new data is available
|
156 | output = "time=" +String(time_trigger);
|
157 | output += ", rawTemperature=" + String(iaqSensor.rawTemperature);
|
158 | output += ", pressure=" + String(iaqSensor.pressure);
|
159 | output += ", rawHumidity=" + String(iaqSensor.rawHumidity);
|
160 | output += ", gasResistance=" + String(iaqSensor.gasResistance);
|
161 | output += ", iaq=" + String(iaqSensor.iaq);
|
162 | output += ", iaqAccuracy=" + String(iaqSensor.iaqAccuracy);
|
163 | output += ", temperature=" + String(iaqSensor.temperature);
|
164 | output += ", humidity=" + String(iaqSensor.humidity);
|
165 | output += ", staticIaq=" + String(iaqSensor.staticIaq);
|
166 | output += ", co2Equivalent=" + String(iaqSensor.co2Equivalent);
|
167 | output += ", breathVocEquivalent=" + String(iaqSensor.breathVocEquivalent);
|
168 | TelnetMsg(output);
|
169 | } else {
|
170 | checkIaqSensorStatus();
|
171 | }
|
172 | delay(30000);
|
173 | }
|
174 |
|
175 | void TelnetMsg(String text)
|
176 | {
|
177 | for(i = 0; i < MAX_TELNET_CLIENTS; i++)
|
178 | {
|
179 | if (TelnetClient[i] || TelnetClient[i].connected())
|
180 | {
|
181 | TelnetClient[i].println(text);
|
182 | }
|
183 | }
|
184 | delay(10); // to avoid strange characters left in buffer
|
185 | }
|
186 |
|
187 | void Telnet()
|
188 | {
|
189 | // Cleanup disconnected session
|
190 | for(i = 0; i < MAX_TELNET_CLIENTS; i++)
|
191 | {
|
192 | if (TelnetClient[i] && !TelnetClient[i].connected())
|
193 | {
|
194 | Serial.print("Client disconnected ... terminate session "); Serial.println(i+1);
|
195 | TelnetClient[i].stop();
|
196 | }
|
197 | }
|
198 |
|
199 | // Check new client connections
|
200 | if (TelnetServer.hasClient())
|
201 | {
|
202 | ConnectionEstablished = false; // Set to false
|
203 |
|
204 | for(i = 0; i < MAX_TELNET_CLIENTS; i++)
|
205 | {
|
206 | // Serial.print("Checking telnet session "); Serial.println(i+1);
|
207 |
|
208 | // find free socket
|
209 | if (!TelnetClient[i])
|
210 | {
|
211 | TelnetClient[i] = TelnetServer.available();
|
212 |
|
213 | Serial.print("New Telnet client connected to session "); Serial.println(i+1);
|
214 |
|
215 | TelnetClient[i].flush(); // clear input buffer, else you get strange characters
|
216 | TelnetClient[i].println("Welcome!");
|
217 |
|
218 | TelnetClient[i].print("Millis since start: ");
|
219 | TelnetClient[i].println(millis());
|
220 |
|
221 | TelnetClient[i].print("Free Heap RAM: ");
|
222 | TelnetClient[i].println(ESP.getFreeHeap());
|
223 |
|
224 | TelnetClient[i].println("----------------------------------------------------------------");
|
225 |
|
226 | ConnectionEstablished = true;
|
227 |
|
228 | break;
|
229 | }
|
230 | else
|
231 | {
|
232 | // Serial.println("Session is in use");
|
233 | }
|
234 | }
|
235 |
|
236 | if (ConnectionEstablished == false)
|
237 | {
|
238 | Serial.println("No free sessions ... drop connection");
|
239 | TelnetServer.available().stop();
|
240 | // TelnetMsg("An other user cannot connect ... MAX_TELNET_CLIENTS limit is reached!");
|
241 | }
|
242 | }
|
243 |
|
244 | for(i = 0; i < MAX_TELNET_CLIENTS; i++)
|
245 | {
|
246 | if (TelnetClient[i] && TelnetClient[i].connected())
|
247 | {
|
248 | if(TelnetClient[i].available())
|
249 | {
|
250 | //get data from the telnet client
|
251 | while(TelnetClient[i].available())
|
252 | {
|
253 | Serial.write(TelnetClient[i].read());
|
254 | }
|
255 | }
|
256 | }
|
257 | }
|
258 | }
|
259 |
|
260 | ////////////////////////////////////////////////////////////////////////////////////////
|
261 | // Blink function with telnet output
|
262 |
|
263 | const long interval = 2000;
|
264 | int ledState = LOW;
|
265 | unsigned long previousMillis = 0;
|
266 |
|
267 | void blinkLED()
|
268 | {
|
269 | unsigned long currentMillis = millis();
|
270 |
|
271 | // if enough millis have elapsed
|
272 | if (currentMillis - previousMillis >= interval)
|
273 | {
|
274 | previousMillis = currentMillis;
|
275 |
|
276 | // toggle the LED
|
277 | ledState = !ledState;
|
278 | digitalWrite(LED_BUILTIN, ledState);
|
279 |
|
280 | String ledStateMsg = "LED State = ";
|
281 | ledStateMsg += ledState;
|
282 | TelnetMsg(ledStateMsg);
|
283 | }
|
284 | }
|
285 | // Helper function definitions
|
286 | void checkIaqSensorStatus(void)
|
287 | {
|
288 | if (iaqSensor.status != BSEC_OK) {
|
289 | if (iaqSensor.status < BSEC_OK) {
|
290 | output = "BSEC error code : " + String(iaqSensor.status);
|
291 | Serial.println(output);
|
292 | for (;;)
|
293 | errLeds(); /* Halt in case of failure */
|
294 | } else {
|
295 | output = "BSEC warning code : " + String(iaqSensor.status);
|
296 | Serial.println(output);
|
297 | }
|
298 | }
|
299 |
|
300 | if (iaqSensor.bme680Status != BME680_OK) {
|
301 | if (iaqSensor.bme680Status < BME680_OK) {
|
302 | output = "BME680 error code : " + String(iaqSensor.bme680Status);
|
303 | Serial.println(output);
|
304 | for (;;)
|
305 | errLeds(); /* Halt in case of failure */
|
306 | } else {
|
307 | output = "BME680 warning code : " + String(iaqSensor.bme680Status);
|
308 | Serial.println(output);
|
309 | }
|
310 | }
|
311 | iaqSensor.status = BSEC_OK;
|
312 | }
|
313 |
|
314 | void errLeds(void)
|
315 | {
|
316 | pinMode(LED_BUILTIN, OUTPUT);
|
317 | digitalWrite(LED_BUILTIN, HIGH);
|
318 | delay(100);
|
319 | digitalWrite(LED_BUILTIN, LOW);
|
320 | delay(100);
|
321 | }
|
322 |
|
323 | void loadState(void)
|
324 | {
|
325 | if (EEPROM.read(0) == BSEC_MAX_STATE_BLOB_SIZE) {
|
326 | // Existing state in EEPROM
|
327 | Serial.println("Reading state from EEPROM");
|
328 |
|
329 | for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) {
|
330 | bsecState[i] = EEPROM.read(i + 1);
|
331 | Serial.println(bsecState[i], HEX);
|
332 | }
|
333 |
|
334 | iaqSensor.setState(bsecState);
|
335 | checkIaqSensorStatus();
|
336 | } else {
|
337 | // Erase the EEPROM with zeroes
|
338 | Serial.println("Erasing EEPROM");
|
339 |
|
340 | for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE + 1; i++)
|
341 | EEPROM.write(i, 0);
|
342 |
|
343 | EEPROM.commit();
|
344 | }
|
345 | }
|
346 |
|
347 | void updateState(void)
|
348 | {
|
349 | bool update = false;
|
350 | /* Set a trigger to save the state. Here, the state is saved every STATE_SAVE_PERIOD with the first state being saved once the algorithm achieves full calibration, i.e. iaqAccuracy = 3 */
|
351 | if (stateUpdateCounter == 0) {
|
352 | if (iaqSensor.iaqAccuracy >= 3) {
|
353 | update = true;
|
354 | stateUpdateCounter++;
|
355 | }
|
356 | } else {
|
357 | /* Update every STATE_SAVE_PERIOD milliseconds */
|
358 | if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) {
|
359 | update = true;
|
360 | stateUpdateCounter++;
|
361 | }
|
362 | }
|
363 |
|
364 | if (update) {
|
365 | iaqSensor.getState(bsecState);
|
366 | checkIaqSensorStatus();
|
367 |
|
368 | Serial.println("Writing state to EEPROM");
|
369 |
|
370 | for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE ; i++) {
|
371 | EEPROM.write(i + 1, bsecState[i]);
|
372 | Serial.println(bsecState[i], HEX);
|
373 | }
|
374 |
|
375 | EEPROM.write(0, BSEC_MAX_STATE_BLOB_SIZE);
|
376 | EEPROM.commit();
|
377 | }
|
378 | }
|