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
+
+
+ Beschreibung |
+ Messwert |
+ Einheit |
+
+
+ Temperatur |
+ -,-- |
+ °C |
+
+
+ Luftfeuchtigkeit |
+ -,-- |
+ %rH |
+
+
+ Luftdruck |
+ ----,-- |
+ hPa |
+
+
+ Gaswiderstand |
+ -,-- |
+ kΩ |
+
+
+
+
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);