esp8266-sensornode/esp8266-sensornode.ino

333 lines
10 KiB
C++

/*
* ESP8266 Environmental Sensor Node with SI7021 (Temp + Hum)
* (to be build with Arduino or the supplied Makefile and makeEspArduino)
*
* copyright: Jannik Beyerstedt | https://jannikbeyerstedt.de | code@jannikbeyerstedt.de
* license: http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 License
*/
#include <Wire.h>
#include <Adafruit_Si7021.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include "rtc-helpers.hpp"
rtcStore_t rtcStore;
#define BOARD_HUZZAH 1 /* Adafruit Feather HUZZAH */
#define BOARD_LOLIN 2 /* LoLin NodeMCU */
#define BOARD_ESP07S 3 /* standalone ESP07S */
/* DEBUG SETTINGS */
#define TEST /* use test config and database */
#define PRINT_INFO /* enable Serial debugging */
// #define PRINT_DEBUG /* enable additional debug output */
/* CONFIGURATION OPTIONS */
#ifndef NODENAME
// #define NODENAME "env-huzzah-01" /* IMPORTANT: CHANGE FOR EACH DEVICE */
#define NODENAME "env-TODO" /* IMPORTANT: CHANGE FOR EACH DEVICE */
#endif
#ifndef BOARD
#define BOARD 0 /* IMPORTANT: select one of the BOARD_* types */
#endif
#define DB_HOSTNAME "ursaminor.fra80"
#define DB_PASSWD "c2Vuc29yczpTZW5zb3JzLXcuaW5mbHV4QGhvbWU" // BasicAuth String
#if BOARD == BOARD_HUZZAH
#define BATT_FULL 3400
#define BATT_CUTOFF 2450 /* 2500=3,0V; 3080=4,0V (Battery Voltage!!!) */
#elif BOARD == BOARD_ESP07S
#define BATT_FULL 3600
#define BATT_CUTOFF 2450 /* 2450=2,7V; 3200=3,5V (VCC Voltage) */
#elif BOARD == BOARD_LOLIN
#define BATT_FULL 2812
#define BATT_CUTOFF 2280
#else
#error "unsupported board chosen"
#endif
#if BOARD == BOARD_ESP07S
String loggerId = "esp07-"; // UID = this prefix + last 3 octets of the MAC address
#endif
#if BOARD != BOARD_ESP07S
String loggerId = "esp12-"; // UID = this prefix + last 3 octets of the MAC address
#endif
/* SETTINGS */
float tempOffset = +0.0;
float humiOffset = +0.0;
const char* host = "ursaminor.fra80";
const int httpPort = 8086;
const char* wlan_ssid = "WLAN-Bey-IoT";
const char* wlan_passwd = "IoT-Fra80";
#ifndef TEST
String loggerName = NODENAME;
const uint16_t sendInterval_sec = 1800; /* 1800 sec (30 min) */
const uint8_t sampleAvgNum = 6; /* 6 (every 5 minutes) */
const uint16_t sampleInterval_sec = sendInterval_sec / sampleAvgNum;
String serviceUri = "/write?db=sensors";
#else
String loggerName = "esp-test";
const uint16_t sendInterval_sec = 180; /* 180 sec (3 min) */
const uint8_t sampleAvgNum = 6; /* 3 (every 1 minute) */
const uint16_t sampleInterval_sec = sendInterval_sec / sampleAvgNum;
String serviceUri = "/write?db=test";
#endif
/* GLOBAL VARIABLES */
const uint16_t wlanConnectCheckInterval = 250; /* milli seconds: poll wifi.state after wifi.begin() */
const uint16_t wlanConnectTimeout = 15000; /* milli seconds */
RTC_DATA_ATTR uint8_t wlanConnectFailCnt = 0;
Adafruit_Si7021 si7021 = Adafruit_Si7021();
float temp = 0.0;
float humi = 0.0;
uint16_t battLevel = 0;
float battPerc = 0.0;
RTC_DATA_ATTR uint8_t sampleCnt = 0; // for average value
RTC_DATA_ATTR float tempAccu = 0.0; // for average value
RTC_DATA_ATTR float humiAccu = 0.0; // for average value
uint8_t chipid[6] = {0}; // WiFi MAC address
// setup the ADC for VCC measurement
ADC_MODE(ADC_VCC);
#ifdef PRINT_INFO
#define INFO_PRINT(x) Serial.print(x)
#define INFO_PRINTLN(x) Serial.println(x)
#else
#define INFO_PRINT(x)
#define INFO_PRINTLN(x)
#endif
#if defined(PRINT_INFO) && defined(PRINT_DEBUG)
#define DEBUG_PRINT(x) Serial.print(x)
#define DEBUG_PRINTLN(x) Serial.println(x)
#else
#define DEBUG_PRINT(x)
#define DEBUG_PRINTLN(x)
#endif
bool wlanConnect(void) {
unsigned int cycles = 0;
WiFi.forceSleepWake();
INFO_PRINT("[INFO ] Connecting to WiFi");
WiFi.mode(WIFI_STA);
WiFi.begin(wlan_ssid, wlan_passwd);
while (WiFi.status() != WL_CONNECTED) {
delay(wlanConnectCheckInterval);
INFO_PRINT(".");
cycles++;
if (cycles > wlanConnectTimeout / wlanConnectCheckInterval) {
INFO_PRINTLN(" FAILED");
return false;
}
}
INFO_PRINT(" ok, IP: ");
INFO_PRINTLN(WiFi.localIP());
return true;
}
void doDeepSleep(uint16_t durationSec) {
// store data to RTC user memory
DEBUG_PRINTLN("[DEBUG] Persisting some values to RTC memory");
rtcStore.sampleCnt = sampleCnt;
rtcStore.wlanConnectFailCnt = wlanConnectFailCnt;
rtcStore.humiAccu = humiAccu;
rtcStore.tempAccu = tempAccu;
#ifdef PRINT_DEBUG
Serial.printf("[DEBUG] sample %3d wlanFail %3d HumiAccu %6.2f TempAccu %6.2f\n", rtcStore.sampleCnt,
rtcStore.wlanConnectFailCnt, rtcStore.humiAccu, rtcStore.tempAccu);
#endif
rtcMemoryStore(&rtcStore);
#ifdef PRINT_DEBUG
Serial.printf("[DEBUG] Going to deep sleep for %d seconds\n", durationSec);
#endif
#ifdef PRINT_INFO
delay(100); // time to send the messages via Serial
#endif
ESP.deepSleep(durationSec * 1000000);
}
void setupRtcStore() {
DEBUG_PRINTLN("[DEBUG] Loading persisted values from RTC memory");
if (true == rtcMemoryLoad(&rtcStore)) {
DEBUG_PRINTLN("[DEBUG] loaded data is valid");
sampleCnt = rtcStore.sampleCnt;
wlanConnectFailCnt = rtcStore.wlanConnectFailCnt;
humiAccu = rtcStore.humiAccu;
tempAccu = rtcStore.tempAccu;
} else {
INFO_PRINTLN("[ERROR] Invalid data loaded from RTC memory -> clearing values");
sampleCnt = 0;
wlanConnectFailCnt = 0;
humiAccu = 0.0;
tempAccu = 0.0;
}
#ifdef PRINT_DEBUG
Serial.printf("[DEBUG] sample %3d wlanFail %3d HumiAccu %6.2f TempAccu %6.2f\n", rtcStore.sampleCnt,
rtcStore.wlanConnectFailCnt, rtcStore.humiAccu, rtcStore.tempAccu);
#endif
}
void setup() {
// set logger node unique ID with MAC address
char macIdString[6] = {0};
WiFi.macAddress(chipid);
sprintf(macIdString, "%02x%02x%02x", chipid[3], chipid[4], chipid[5]);
loggerId += macIdString;
#ifdef PRINT_INFO
Serial.begin(115200);
delay(100);
Serial.println("");
Serial.print("[INFO ] Node ");
Serial.print(loggerId);
Serial.print(", Name: ");
Serial.print(loggerName);
Serial.print(", BoardType: ");
Serial.println(BOARD);
#endif
// load data stored to RTC user memory
setupRtcStore();
// setup the wakeup pin
pinMode(0, OUTPUT);
// turn WiFi off for now
WiFi.mode(WIFI_OFF);
WiFi.forceSleepBegin();
// setup the si7021 sensor
if (false == si7021.begin()) {
INFO_PRINTLN("[ERROR] SI7021 not set up properly");
}
/* GET VALUE SAMPLES */
// measure environmental data from si7021
humi = si7021.readHumidity() + humiOffset;
temp = si7021.readTemperature() + tempOffset;
// measure battery level
uint16_t battLevel = ESP.getVcc();
float battPerc = 100.0 * (battLevel - BATT_CUTOFF) / (BATT_FULL - BATT_CUTOFF);
#ifdef PRINT_INFO
Serial.printf("[INFO ] > Humidity: %5.2f Temperature: %5.2f", humi, temp);
Serial.printf(" Battery Bat: %5.1f/100 (raw: %4d)\n", battPerc, battLevel);
#endif
/* ---------- MAIN LOGIC START ---------- */
// accumulate and average $sampleAvgNum samples before sending data
sampleCnt++;
tempAccu += temp;
humiAccu += humi;
// calculate average and send data, if $sampleAvgNum are reached
if (sampleCnt >= sampleAvgNum) {
INFO_PRINTLN("[INFO ] Enough samples collected -> send average to database");
temp = tempAccu / (float)sampleCnt;
humi = humiAccu / (float)sampleCnt;
#ifdef PRINT_INFO
Serial.printf("[INFO ] < Humidity: %5.2f Temperature: %5.2f\n", humi, temp);
#endif
// turn wifi on again
DEBUG_PRINTLN("[DEBUG] Turning wifi on again and connecting");
if (wlanConnect()) {
// send data to influxdb
String influxDataLine = "weather,id=" + loggerId + ",sensor=" + loggerName + " temp=" + String(temp, 2);
influxDataLine += ",hum=" + String(humi, 2);
influxDataLine += ",batRaw=" + String(battLevel);
influxDataLine += ",bat=" + String(battPerc);
INFO_PRINT("[INFO ] connecting to ");
INFO_PRINT(host);
WiFiClient client;
if (client.connect(host, httpPort)) {
INFO_PRINTLN(" > success");
String httpRequest = "POST " + serviceUri + " HTTP/1.1\r\n" + "Host: " + host + ":" + httpPort +
"\r\n" + "Content-Length: " + influxDataLine.length() + "\r\n" +
"Authorization: Basic " + DB_PASSWD + "=\r\n" + "Connection: close\r\n" +
"\r\n" + influxDataLine + "\r\n";
client.print(httpRequest);
delay(100);
while (client.available()) {
String line = client.readStringUntil('\n');
if (line.indexOf("HTTP") != -1) {
if (line.indexOf("204") == -1) { // expect a HTTP 204 status code
INFO_PRINTLN("[ERROR] invalid http status code");
INFO_PRINTLN(line);
} else { // successful database submission
// reset average after successful database submission
sampleCnt = 0;
tempAccu = 0;
humiAccu = 0;
wlanConnectFailCnt = 0;
}
break;
} else {
;
}
}
} else {
INFO_PRINTLN(" > FAILED");
}
client.stop();
WiFi.disconnect(true);
// continue with deep sleep at end of main function
} else { // ELSE: !wlanConnect
/* WIFI CONNECT FAILED, WAIT MAX 5 MINUTES IN DEEP SLEEP */
wlanConnectFailCnt++;
#ifdef PRINT_INFO
Serial.printf("[INFO ] WiFi connect failed %d time(s) -> wait %03d s and try again\n",
wlanConnectFailCnt, sampleInterval_sec);
#endif
if (sampleInterval_sec > (5 * 60)) {
doDeepSleep(5 * 60);
} else {
doDeepSleep(sampleInterval_sec);
}
} // ENDIF: wlanConnect
} else { // ELSE: sampleCnt < sampleAvgNum
#ifdef PRINT_INFO
Serial.printf("[INFO ] %d of %d samples collected -> sleep %4d s\n", sampleCnt, sampleAvgNum,
sampleInterval_sec);
// continue with deep sleep at end of main function
#endif
} // ENDIF: sampleCnt
/* ---------- MAIN LOGIC END ---------- */
/* SAVE ENERGY BY WAITING IN DEEP SLEEP */
doDeepSleep(sampleInterval_sec);
}
void loop() {
/* this should never be reached!!! */
INFO_PRINTLN("[ERROR] the arduino loop should never be reached!!!");
doDeepSleep(sampleInterval_sec);
}