diff --git a/.gitignore b/.gitignore index e7c7fed..c60601b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ .pio .cache compile_commands.json + +data/etc/* +!data/etc/.* diff --git a/data/etc/.keep b/data/etc/.keep new file mode 100644 index 0000000..e69de29 diff --git a/data/htdocs/index.html b/data/htdocs/index.html new file mode 100644 index 0000000..b979561 --- /dev/null +++ b/data/htdocs/index.html @@ -0,0 +1,74 @@ + + + + + BME680-Wetterstation + + + + + +

BME680-Wetterstation

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
BeschreibungMesswertEinheit
Temperatur-,--°C
Luftfeuchtigkeit-,--%rH
Luftdruck----,--hPa
Gaswiderstand-,--
+ + diff --git a/data/htdocs/utils.js b/data/htdocs/utils.js new file mode 100644 index 0000000..1d1fab1 --- /dev/null +++ b/data/htdocs/utils.js @@ -0,0 +1,25 @@ +function callURL(url) +{ + var xmlhttp = new XMLHttpRequest(); + xmlhttp.open("GET", url, true); + xmlhttp.send(); +} + +function getjson(url, handler_func) +{ + var xmlhttp = new XMLHttpRequest(); + xmlhttp.onreadystatechange = function () { + if(this.readyState == 4 && this.status == 200) { + var data = JSON.parse(this.responseText); + handler_func(data); + } + } + + xmlhttp.open("GET", url, true); + xmlhttp.send(); +} + +function getBME680Data(handler_func) +{ + return getjson("/bme680.json", handler_func); +} diff --git a/include/Config.h b/include/Config.h new file mode 100644 index 0000000..78f5f09 --- /dev/null +++ b/include/Config.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include + +class Config +{ + public: + struct WLAN + { + std::string ssid; + std::string password; + }; + + typedef std::vector WLANList; + + static Config &instance() + { + static Config theConfig; + return theConfig; + } + + void load(void); + + const WLANList& getWLANList(void) { return m_wlans; } + + const std::string& getCRPassword(void) { return m_crPassword; } + const std::string& getCRSalt(void) { return m_crSalt; } + + private: + Config(); + + WLANList m_wlans; + std::string m_crPassword; + std::string m_crSalt; +}; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 0ddd1b4..8d53347 100644 --- a/platformio.ini +++ b/platformio.ini @@ -21,7 +21,9 @@ lib_deps = adafruit/Adafruit SCD30@^1.0.11 SPI Wire + NTPClient #esp32_https_server build_flags = -std=c++11 +board_build.filesystem = littlefs diff --git a/src/Config.cpp b/src/Config.cpp new file mode 100644 index 0000000..84d2ec7 --- /dev/null +++ b/src/Config.cpp @@ -0,0 +1,44 @@ +#include + +#include "Config.h" + +Config::Config() +{} + +void Config::load(void) +{ + // load WLANs + File wlanFile = LittleFS.open("/etc/wlan", "r"); + + while(wlanFile.available()) { + String ssid = wlanFile.readStringUntil('\n'); + + if(!wlanFile.available()) { + Serial.println("/etc/wlan terminated early. Last entry ignored."); + break; + } + + String passwd = wlanFile.readStringUntil('\n'); + + m_wlans.emplace_back(WLAN{ssid.c_str(), passwd.c_str()}); + } + + wlanFile.close(); + + // load Challenge-Response data + File authFile = LittleFS.open("/etc/auth", "r"); + + if(authFile.available()) { + String passwd = authFile.readStringUntil('\n'); + m_crPassword = passwd.c_str(); + + if(!authFile.available()) { + m_crSalt = ""; + } else { + String salt = authFile.readStringUntil('\n'); + m_crSalt = salt.c_str(); + } + } + + authFile.close(); +} diff --git a/src/main.cpp b/src/main.cpp index 64649c8..6db08b1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,20 +1,92 @@ #include #include +#include +#include +#include + +#include + #include #include #include #include +#include "Config.h" + #include Adafruit_SCD30 scd30; static timeseries_t ts_scd30_co2, ts_scd30_temperature, ts_scd30_humidity; +static WiFiMulti wiFiMulti; +static bool wiFiConnectedToStation; + +static WiFiUDP ntpUDP; + +NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); + #define REF_ALTITUDE 320 // meters #define REF_CO2_PPM 425 // clean air according to DWD at 2024-06-17 +void wifi_setup(void) +{ + // Connect the WiFi network (or start an AP if that doesn't work) + for (auto &net : Config::instance().getWLANList()) + { + Serial.print(F("Adding network ")); + Serial.println(net.ssid.c_str()); + wiFiMulti.addAP(net.ssid.c_str(), net.password.c_str()); + } + + Serial.println(F("Trying to connect for 5 Minutes...")); + + WiFi.setHostname("SensorCube"); + WiFi.mode(WIFI_STA); + //WiFi.setSleepMode(WIFI_MODEM_SLEEP); + + wiFiConnectedToStation = true; // assume the best + + bool led_on = true; + for(size_t tries = 0; tries < 3000; tries++) + { + if(wiFiMulti.run() == WL_CONNECTED) { + Serial.println(F("")); + Serial.println(F("WiFi connected")); + Serial.println(F("IP address: ")); + Serial.println(WiFi.localIP()); + break; + } + + led_on = !led_on; + //digitalWrite(LED_BUILTIN, led_on); + delay(100); + + Serial.print(F(".")); + } + + if(WiFi.status() != WL_CONNECTED) { + Serial.println(F("Connection failed, setting up access point...")); + + wiFiConnectedToStation = false; + + IPAddress apIP(192, 168, 42, 1); + + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); + WiFi.softAP("sensorcube", "sensorcube"); + WiFi.enableAP(true); + + //digitalWrite(LED_BUILTIN, false); // LED ON (active low) + } else { + //digitalWrite(LED_BUILTIN, true); // LED OFF (active low) + } + + timeClient.begin(); + timeClient.update(); +} + void initSCD30(void) { timeseries_init(&ts_scd30_co2, 0, 15, "ppm", "%.1f"); @@ -71,25 +143,86 @@ void initSCD30(void) } +static void draw_epaper_initial_callback(GxEPD2_DISPLAY_CLASS *display) +{ + display->setRotation(1); + display->setTextColor(GxEPD_BLACK); + + int16_t y = FreeSansBold12pt7b.yAdvance*3/4; + display->setFont(&FreeSansBold12pt7b); + display->setCursor(0, y); + display->print("SensorCube"); + + y += 2*FreeSansBold12pt7b.yAdvance; + display->setCursor(0, y); + + display->setFont(&FreeSans12pt7b); + display->print("Connected to WLAN: "); + + y += FreeSans12pt7b.yAdvance; + display->setCursor(0, y); + + display->setFont(&FreeSansBold12pt7b); + display->print(WiFi.SSID()); + + y += FreeSansBold12pt7b.yAdvance; + display->setCursor(0, y); + + display->setFont(&FreeSans12pt7b); + display->print("IPv4: "); + + y += FreeSans12pt7b.yAdvance; + display->setCursor(0, y); + + display->setFont(&FreeSansBold12pt7b); + display->print(WiFi.localIP()); + + y += 2*FreeSansBold12pt7b.yAdvance; + display->setCursor(0, y); + + display->setFont(&FreeSans12pt7b); + display->print("Initial time: "); + + y += FreeSans12pt7b.yAdvance; + display->setCursor(0, y); + + display->setFont(&FreeSansBold12pt7b); + display->print(timeClient.getFormattedTime()); + + y += FreeSansBold12pt7b.yAdvance; +} + + void setup(void) { Serial.begin(115200); Serial.println("Hello World!"); + if(!LittleFS.begin()) { + Serial.println(F("LittleFS setup failed!")); + while(1) delay(1); + } + + Config::instance().load(); + delay(3000); + wifi_setup(); + initSCD30(); epaper_init(); - epaper_test(); + + epaper_draw_and_hibernate(draw_epaper_initial_callback, false); + } -void draw_epaper_callback(GxEPD2_DISPLAY_CLASS *display) +static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS *display) { display->setRotation(1); display->setTextColor(GxEPD_BLACK); - int16_t y = FreeSans12pt7b.yAdvance; + int16_t y = FreeSans12pt7b.yAdvance*3/4; // first line: CO₂ display->setCursor(0, y); @@ -115,7 +248,7 @@ void draw_epaper_callback(GxEPD2_DISPLAY_CLASSsetCursor(0, y); display->setFont(&FreeSans12pt7b); @@ -144,7 +277,7 @@ void draw_epaper_callback(GxEPD2_DISPLAY_CLASSsetCursor(0, y); display->setCursor(0, y); @@ -166,19 +299,27 @@ void draw_epaper_callback(GxEPD2_DISPLAY_CLASS= 300000) { uint16_t calValue = scd30.getForcedCalibrationReference(); @@ -208,6 +350,7 @@ void loop(void) calibrationDone = true; } + */ } delay(1000);