Connect to WLAN and get the time via NTP
This commit is contained in:
parent
1197e083f4
commit
1045e85cfb
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,6 @@
|
|||
.pio
|
||||
.cache
|
||||
compile_commands.json
|
||||
|
||||
data/etc/*
|
||||
!data/etc/.*
|
||||
|
|
0
data/etc/.keep
Normal file
0
data/etc/.keep
Normal file
74
data/htdocs/index.html
Normal file
74
data/htdocs/index.html
Normal file
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>BME680-Wetterstation</title>
|
||||
<script type="text/javascript" src="utils.js"></script>
|
||||
<script type="text/javascript">
|
||||
function updateWeatherTable(bme680data)
|
||||
{
|
||||
elements = ['temperature', 'humidity', 'pressure', 'gas_resistance'];
|
||||
|
||||
|
||||
elements.forEach(function(item, index) {
|
||||
elem = document.getElementById(item);
|
||||
val = bme680data[item].value;
|
||||
if(item == "gas_resistance") {
|
||||
val /= 1000.0;
|
||||
}
|
||||
elem.textContent = val.toFixed(2);
|
||||
});
|
||||
}
|
||||
|
||||
function setup()
|
||||
{
|
||||
getBME680Data(updateWeatherTable);
|
||||
setInterval(function() { getBME680Data(updateWeatherTable) }, 60000);
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
th, td {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
td.value {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onLoad="setup()">
|
||||
<h1>BME680-Wetterstation</h1>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Beschreibung</th>
|
||||
<th>Messwert</th>
|
||||
<th>Einheit</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Temperatur</td>
|
||||
<td class="value" id="temperature">-,--</td>
|
||||
<td>°C</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Luftfeuchtigkeit</td>
|
||||
<td class="value" id="humidity">-,--</td>
|
||||
<td>%rH</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Luftdruck</td>
|
||||
<td class="value" id="pressure">----,--</td>
|
||||
<td>hPa</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Gaswiderstand</td>
|
||||
<td class="value" id="gas_resistance">-,--</td>
|
||||
<td>kΩ</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
25
data/htdocs/utils.js
Normal file
25
data/htdocs/utils.js
Normal file
|
@ -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);
|
||||
}
|
36
include/Config.h
Normal file
36
include/Config.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class Config
|
||||
{
|
||||
public:
|
||||
struct WLAN
|
||||
{
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
};
|
||||
|
||||
typedef std::vector<WLAN> 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;
|
||||
};
|
|
@ -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
|
||||
|
|
44
src/Config.cpp
Normal file
44
src/Config.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
#include <LittleFS.h>
|
||||
|
||||
#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();
|
||||
}
|
161
src/main.cpp
161
src/main.cpp
|
@ -1,20 +1,92 @@
|
|||
#include <Arduino.h>
|
||||
#include <Adafruit_SCD30.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <NTPClient.h>
|
||||
|
||||
#include <LittleFS.h>
|
||||
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Fonts/FreeSans9pt7b.h>
|
||||
#include <Fonts/FreeSans12pt7b.h>
|
||||
#include <Fonts/FreeSansBold12pt7b.h>
|
||||
|
||||
#include "Config.h"
|
||||
|
||||
#include <epaper.h>
|
||||
|
||||
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<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_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<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_CLASS)> *display)
|
||||
static void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(GxEPD2_DRIVER_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_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(G
|
|||
epaper_plot(150, 5, 150, 65, &ts_scd30_co2, 900);
|
||||
|
||||
// second line: temperature
|
||||
y = 75 + FreeSans12pt7b.yAdvance;
|
||||
y = 75 + FreeSans12pt7b.yAdvance*3/4;
|
||||
display->setCursor(0, y);
|
||||
|
||||
display->setFont(&FreeSans12pt7b);
|
||||
|
@ -144,7 +277,7 @@ void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(G
|
|||
epaper_plot(150, 80, 150, 65, &ts_scd30_temperature, 900);
|
||||
|
||||
// second line: Humidity
|
||||
y = 150 + FreeSans12pt7b.yAdvance;
|
||||
y = 150 + FreeSans12pt7b.yAdvance*3/4;
|
||||
display->setCursor(0, y);
|
||||
|
||||
display->setCursor(0, y);
|
||||
|
@ -166,19 +299,27 @@ void draw_epaper_callback(GxEPD2_DISPLAY_CLASS<GxEPD2_DRIVER_CLASS, MAX_HEIGHT(G
|
|||
|
||||
void loop(void)
|
||||
{
|
||||
static bool calibrationDone = true; // change to false to recalibrate at REF_CO2_PPM
|
||||
static bool first_scd30_readout = true;
|
||||
static uint32_t lastEPaperRefresh = 0;
|
||||
uint32_t now = millis();
|
||||
|
||||
wiFiMulti.run(); // maintain the WiFi connection
|
||||
timeClient.update(); // keep time in sync
|
||||
|
||||
if(scd30.dataReady()) {
|
||||
if (!scd30.read()) {
|
||||
Serial.println("scd30: Error reading sensor data");
|
||||
return;
|
||||
}
|
||||
|
||||
timeseries_append(&ts_scd30_co2, scd30.CO2);
|
||||
timeseries_append(&ts_scd30_temperature, scd30.temperature);
|
||||
timeseries_append(&ts_scd30_humidity, scd30.relative_humidity);
|
||||
// skip the first readout because the CO2 reading is then invalid
|
||||
if(!first_scd30_readout) {
|
||||
timeseries_append(&ts_scd30_co2, scd30.CO2);
|
||||
timeseries_append(&ts_scd30_temperature, scd30.temperature);
|
||||
timeseries_append(&ts_scd30_humidity, scd30.relative_humidity);
|
||||
} else {
|
||||
first_scd30_readout = false;
|
||||
}
|
||||
|
||||
Serial.print("CO2: ");
|
||||
Serial.print(scd30.CO2, 3);
|
||||
|
@ -196,6 +337,7 @@ void loop(void)
|
|||
epaper_draw_and_hibernate(draw_epaper_callback, false);
|
||||
}
|
||||
|
||||
/*
|
||||
if(!calibrationDone && now >= 300000) {
|
||||
uint16_t calValue = scd30.getForcedCalibrationReference();
|
||||
|
||||
|
@ -208,6 +350,7 @@ void loop(void)
|
|||
|
||||
calibrationDone = true;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
delay(1000);
|
||||
|
|
Loading…
Reference in a new issue