[CODE, TIDY] make use of RTC user memory + restructure code

This commit is contained in:
Jannik Beyerstedt 2018-08-08 00:11:22 +02:00
parent a9fe58bb47
commit 139b14b403
5 changed files with 359 additions and 126 deletions

27
.clang-format Normal file
View file

@ -0,0 +1,27 @@
# C/C++ Coding Style for generic Projects
# UseTab: (VS Code current setting)
BasedOnStyle: LLVM
Language: Cpp
IndentWidth: 2
ColumnLimit: 110
BreakBeforeBraces: Attach
PointerAlignment: Left
AccessModifierOffset: -2
#IndentPPDirectives: AfterHash
Cpp11BracedListStyle: true
AlignConsecutiveDeclarations: true
AlignConsecutiveAssignments: true
AlignEscapedNewlines: Left
AlignTrailingComments: true
IndentWrappedFunctionNames: false
AllowShortFunctionsOnASingleLine: Inline
SpacesBeforeTrailingComments: 2
FixNamespaceComments: true
ReflowComments: false
SortIncludes: false
SortUsingDeclarations: false

32
.editorconfig Normal file
View file

@ -0,0 +1,32 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.c, *.cpp, *.h]
trim_trailing_whitespace = true
[*.py]
indent_style = space
indent_size = 4
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2
[*.json]
indent_size = 2
[Makefile, *.mk]
indent_size = 4
indent_style = tab

View file

@ -1,143 +1,237 @@
/* /*
ESP8266 Environmental si7021s Node * ESP8266 Environmental Sensor Node with SI7021 (Temp + Hum)
* (to be build with Arduino or the supplied Makefile and makeEspArduino)
copyright: Jannik Beyerstedt | http://jannikbeyerstedt.de | code@jannikbeyerstedt.de *
license: http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 License * 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 <ESP8266WiFi.h>
#include <WiFiClient.h> #include <WiFiClient.h>
#include <Wire.h>
#include "Adafruit_Si7021.h"
extern "C" {
#include "user_interface.h"
}
#define DEBUG /* enable Serial debugging */ #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 TEST /* use test config and database */
#define PRINT_INFO /* enable Serial debugging */
// #define PRINT_DEBUG /* enable additional debug output */
#define HUZZAH 1 /* Adafruit Feather HUZZAH */ /* CONFIGURATION OPTIONS */
#define LOLIN 2 /* LoLin NodeMCU */ #ifndef NODENAME
#define ESP07S 3 /* standalone ESP07S */ // #define NODENAME "env-huzzah-01" /* IMPORTANT: CHANGE FOR EACH DEVICE */
#define BOARD 1 /* choose from above */ #define NODENAME "env-esp07-01" /* IMPORTANT: CHANGE FOR EACH DEVICE */
#define NODENAME "env-huzzah-01" /* IMPORTANT: CHANGE FOR EACH DEVICE */
//#define NODENAME "env-esp07-01" /* IMPORTANT: CHANGE FOR EACH DEVICE */
/* SETTINGS */
#ifdef TEST
String loggerName = "esp-test";
unsigned int sendInterval_sec = 180; /* 180 sec (3 min) */
#else
String loggerName = NODENAME;
unsigned int sendInterval_sec = 1800; /* 1800 sec (30 min) */
#endif #endif
//IPAddress ip(192, 168, 50, 5); /* IMPORTANT: CHANGE FOR EACH DEVICE */
IPAddress gate(192, 168, 50, 1);
IPAddress subn(255, 255, 255, 0);
float tempOffset = -1.0;
float humiOffset = -10.0;
const char* host = "ursaminor.fra80"; #ifndef BOARD
#ifdef TEST #define BOARD 0 /* IMPORTANT: select one of the BOARD_* types */
String serviceUri = "/write?db=test";
#else
String serviceUri = "/write?db=sensors";
#endif #endif
const int httpPort = 8086;
#if BOARD == LOLIN #define DB_HOSTNAME "ursaminor.fra80"
#define DB_PASSWD "c2Vuc29yczpTZW5zb3JzLXcuaW5mbHV4QGhvbWU" // BasicAuth String
#if BOARD == BOARD_LOLIN
#define BATT_FULL 2812 #define BATT_FULL 2812
#define BATT_CUTOFF 2280 #define BATT_CUTOFF 2280
#elif BOARD == HUZZAH #elif BOARD == BOARD_HUZZAH
#define BATT_FULL 3100 #define BATT_FULL 3100
#define BATT_CUTOFF 2450 /* 2500=3,0V; 3080=4,0V (Battery Voltage!!!) */ #define BATT_CUTOFF 2450 /* 2500=3,0V; 3080=4,0V (Battery Voltage!!!) */
#elif BOARD == ESP07S #elif BOARD == BOARD_ESP07S
#define BATT_FULL 3250 #define BATT_FULL 3600
#define BATT_CUTOFF 2450 /* 2450=2,7V; 3200=3,5V (VCC Voltage) */ #define BATT_CUTOFF 2450 /* 2450=2,7V; 3200=3,5V (VCC Voltage) */
#else
#error "unsupported board chosen"
#endif
/* SETTINGS */
float tempOffset = -1.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 = 1; /* 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 #endif
/* GLOBAL VARIABLES */ /* GLOBAL VARIABLES */
const unsigned int wifiConnectTimeout = 30; /* num of 500ms intervals */ 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(); Adafruit_Si7021 si7021 = Adafruit_Si7021();
float temp; float temp = 0.0;
float humi; float humi = 0.0;
ADC_MODE(ADC_VCC); // set the ADC to read VCC uint16_t battLevel = 0;
float battPerc = 0.0;
#ifdef DEBUG RTC_DATA_ATTR uint8_t sampleCnt = 0; // for average value
#define PRINT(x) Serial.print(x) RTC_DATA_ATTR float tempAccu = 0.0; // for average value
#define PRINTLN(x) Serial.println(x) RTC_DATA_ATTR float humiAccu = 0.0; // for average value
// 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 #else
#define PRINT(x) #define INFO_PRINT(x)
#define PRINTLN(x) #define INFO_PRINTLN(x)
#endif #endif
bool wifiConnect(void) { #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; unsigned int cycles = 0;
PRINTLN("INFO Connecting to WiFi"); WiFi.forceSleepWake();
INFO_PRINT("[INFO ] Connecting to WiFi");
WiFi.mode(WIFI_STA); WiFi.mode(WIFI_STA);
WiFi.begin("WLAN-Bey-IoT", "IoT-Fra80"); WiFi.begin(wlan_ssid, wlan_passwd);
//WiFi.config(ip, gate, subn); // use a static ip for faster connect
while (WiFi.status() != WL_CONNECTED) { while (WiFi.status() != WL_CONNECTED) {
delay(500); delay(wlanConnectCheckInterval);
PRINT("."); // log level: DEBUG INFO_PRINT(".");
cycles++; cycles++;
if (cycles > wifiConnectTimeout) { if (cycles > wlanConnectTimeout / wlanConnectCheckInterval) {
PRINTLN(" FAILED"); INFO_PRINTLN(" FAILED");
return false; return false;
} }
} }
PRINT(" ok, IP: "); // log level: DEBUG INFO_PRINT(" ok, IP: ");
PRINTLN(WiFi.localIP()); INFO_PRINTLN(WiFi.localIP());
return true; 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() { void setup() {
#ifdef DEBUG #ifdef PRINT_INFO
Serial.begin(115200); Serial.begin(115200);
delay(100); delay(100);
Serial.println(""); Serial.println("");
Serial.print("Node ");
Serial.print("[INFO ] Node ");
Serial.print(loggerName); Serial.print(loggerName);
Serial.print(", BoardType: "); Serial.print(", BoardType: ");
Serial.println(BOARD); Serial.println(BOARD);
#endif #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.mode(WIFI_OFF);
WiFi.forceSleepBegin(); WiFi.forceSleepBegin();
si7021.begin(); // setup the si7021 sensor
pinMode(0, OUTPUT); if (false == si7021.begin()) {
INFO_PRINTLN("[ERROR] SI7021 not set up properly");
/* GET VALUE SAMPLES */
humi = si7021.readHumidity() + humiOffset;
temp = si7021.readTemperature() + tempOffset;
#ifdef DEBUG
Serial.print("> Humidity: ");
Serial.print(humi, 2);
Serial.print("\tTemperature: ");
Serial.println(temp, 2);
#endif
/* WRITE VALUES TO DATABASE */
// turn wifi on again
PRINTLN("DEBUG turning wifi on again");
WiFi.forceSleepWake();
if (!wifiConnect()) {
return;
} }
/* GET VALUE SAMPLES */
// measure environmental data from si7021
humi = si7021.readHumidity() + humiOffset;
temp = si7021.readTemperature() + tempOffset;
// measure battery level
uint16_t battLevel = ESP.getVcc(); uint16_t battLevel = ESP.getVcc();
float battPerc = 100.0 * (battLevel - BATT_CUTOFF) / (BATT_FULL - BATT_CUTOFF); float battPerc = 100.0 * (battLevel - BATT_CUTOFF) / (BATT_FULL - BATT_CUTOFF);
Serial.print("Bat: ");
Serial.print(battLevel); #ifdef PRINT_INFO
Serial.print(", "); Serial.printf("[INFO ] > Humidity: %5.2f Temperature: %5.2f", humi, temp);
Serial.print(battPerc); Serial.printf(" Battery Bat: %5.1f/100 (raw: %4d)\n", battPerc, battLevel);
Serial.println("%"); #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 // send data to influxdb
String influxDataLine = "weather,sensor=" + loggerName + " temp=" + String(temp, 2); String influxDataLine = "weather,sensor=" + loggerName + " temp=" + String(temp, 2);
@ -145,16 +239,16 @@ void setup() {
influxDataLine += ",batRaw=" + String(battLevel); influxDataLine += ",batRaw=" + String(battLevel);
influxDataLine += ",bat=" + String(battPerc); influxDataLine += ",bat=" + String(battPerc);
PRINT("INFO connecting to "); INFO_PRINT("[INFO ] connecting to ");
PRINT(host); INFO_PRINT(host);
WiFiClient client; WiFiClient client;
if (client.connect(host, httpPort)) { if (client.connect(host, httpPort)) {
PRINTLN(" > success"); // log level: INFO INFO_PRINTLN(" > success");
String httpRequest = "POST " + serviceUri + " HTTP/1.1\r\n" + "Host: " + host + ":" + httpPort + "\r\n" + String httpRequest = "POST " + serviceUri + " HTTP/1.1\r\n" + "Host: " + host + ":" + httpPort +
"Content-Length: " + influxDataLine.length() + "\r\n" + "\r\n" + "Content-Length: " + influxDataLine.length() + "\r\n" +
"Authorization: Basic c2Vuc29yczpTZW5zb3JzLXcuaW5mbHV4QGhvbWU=\r\n" + "Authorization: Basic " + DB_PASSWD + "=\r\n" + "Connection: close\r\n" +
"Connection: close\r\n" + "\r\n" + influxDataLine + "\r\n"; "\r\n" + influxDataLine + "\r\n";
client.print(httpRequest); client.print(httpRequest);
delay(100); delay(100);
@ -162,8 +256,15 @@ void setup() {
String line = client.readStringUntil('\n'); String line = client.readStringUntil('\n');
if (line.indexOf("HTTP") != -1) { if (line.indexOf("HTTP") != -1) {
if (line.indexOf("204") == -1) { // expect a HTTP 204 status code if (line.indexOf("204") == -1) { // expect a HTTP 204 status code
PRINTLN("ERROR: invalid http status code"); INFO_PRINTLN("[ERROR] invalid http status code");
PRINTLN(line); INFO_PRINTLN(line);
} else { // successful database submission
// reset average after successful database submission
sampleCnt = 0;
tempAccu = 0;
humiAccu = 0;
wlanConnectFailCnt = 0;
} }
break; break;
} else { } else {
@ -172,22 +273,42 @@ void setup() {
} }
} else { } else {
PRINTLN(" > FAILED"); INFO_PRINTLN(" > FAILED");
} }
client.stop(); client.stop();
/* SAVE EVEN MORE ENERGY, WAIT IN DEEP SLEEP */ WiFi.disconnect(true);
PRINTLN("DEBUG going to deep sleep now"); // continue with deep sleep at end of main function
ESP.deepSleep(sendInterval_sec * 1000000, WAKE_RF_DEFAULT);
delay(100); } 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\n", sampleCnt, sampleAvgNum);
// 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() { void loop() {
/* WIFI CONNECT FAILED, WAIT 5 MINUTES IN DEEP SLEEP */ /* this should never be reached!!! */
PRINTLN("DEBUG going to deep sleep now"); INFO_PRINTLN("[ERROR] the arduino loop should never be reached!!!");
if (sendInterval_sec > (5 * 60)) { doDeepSleep(sampleInterval_sec);
ESP.deepSleep(5 * 60 * 1000000, WAKE_RF_DEFAULT);
} else {
ESP.deepSleep(sendInterval_sec * 1000000, WAKE_RF_DEFAULT);
}
delay(100);
} }

26
rtc-helpers.hpp Normal file
View file

@ -0,0 +1,26 @@
/*
* ESP8266 RTC Memory Access library
*
* copyright: Jannik Beyerstedt | http://jannikbeyerstedt.de | code@jannikbeyerstedt.de
* license: http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 License
*/
#ifndef RTC_HELPERS_H
#define RTC_HELPERS_H
#include <Arduino.h>
#define RTC_DATA_ATTR // only for copy-paste compatibility with ESP32 Code
typedef struct {
uint32_t magic;
uint8_t sampleCnt;
uint8_t wlanConnectFailCnt;
float tempAccu;
float humiAccu;
} rtcStore_t __attribute__((aligned(4)));
bool rtcMemoryLoad(rtcStore_t* rtcStore_ptr);
bool rtcMemoryStore(rtcStore_t* rtcStore_ptr);
#endif

27
src/rtc-helpers.cpp Normal file
View file

@ -0,0 +1,27 @@
/*
* ESP8266 RTC Memory Access library
*
* copyright: Jannik Beyerstedt | http://jannikbeyerstedt.de | code@jannikbeyerstedt.de
* license: http://www.gnu.org/licenses/gpl-3.0.txt GPLv3 License
*/
#include "rtc-helpers.hpp"
#include <ESP.h>
#define RTC_MEMCHECK_MAGIC 0xAABBCCDD // cheap input validation by using a magic constant
bool rtcMemoryLoad(rtcStore_t* rtcStore_ptr) {
if (ESP.rtcUserMemoryRead(0, (uint32_t*)rtcStore_ptr, sizeof(*rtcStore_ptr))) {
return (RTC_MEMCHECK_MAGIC == rtcStore_ptr->magic);
}
return false;
}
bool rtcMemoryStore(rtcStore_t* rtcStore_ptr) {
rtcStore_ptr->magic = RTC_MEMCHECK_MAGIC;
if (ESP.rtcUserMemoryWrite(0, (uint32_t*)rtcStore_ptr, sizeof(*rtcStore_ptr))) {
return true;
}
return false;
}