diff --git a/.prettierrc b/.prettierrc
index 9510464..97b367e 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,5 +1,5 @@
{
- "semi": false,
+ "semi": true,
"singleQuote": true,
"quoteProps": "as-needed",
"jsxSingleQuote": true,
diff --git a/README.md b/README.md
index 9e21750..3a61023 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,9 @@ A template for developing NOT[^1]-devices using micro controllers like the
ESP8266 or ESP32 (support pending) using [PlatformIO](https://platformio.org/)
and the [Arduino environment](https://github.com/arduino/ArduinoCore-API).
+The WebGUI is tested on `Android Browser 2.3.5` (yikes!) and should work with
+other outdated browsers, so you can use your old devices to control your NOT!
+
[^1]:
Network Of Things (a.k.a. IOT / marketing buzzword for network / internet
enabled devices)
diff --git a/TODO.md b/TODO.md
index e909923..0a56f5a 100644
--- a/TODO.md
+++ b/TODO.md
@@ -4,14 +4,15 @@
## Planned
-- [ ] load WiFi configuration from filesystem
+- [x] load WiFi STA configuration from filesystem
+- [x] HTTPS
+- [ ] load WiFi AP configuration from filesystem
- [ ] edit WiFi configuration from browser
-- [ ] HTTPS
- [ ] API & Console configuration
- [ ] IPv4 & IPv6 configuration, DNS, captive portal
- [ ] physical access validation for reset / recovery
- [ ] MQTT / HomeAssistant integration
-- [ ] ESP32 support
+- [x] ESP32 support
- [ ] SPA frontend, live
- [ ] Change peripheral / pin configuration
- [ ] SD card logging
diff --git a/boards/esp32c3-mini.json b/boards/esp32c3-mini.json
index b1155f9..c744416 100644
--- a/boards/esp32c3-mini.json
+++ b/boards/esp32c3-mini.json
@@ -1,35 +1,30 @@
{
- "build": {
- "arduino":{
- "ldscript": "esp32c3_out.ld"
+ "build": {
+ "arduino": {
+ "ldscript": "esp32c3_out.ld"
+ },
+ "core": "esp32",
+ "extra_flags": "-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1",
+ "f_cpu": "160000000L",
+ "f_flash": "80000000L",
+ "flash_mode": "qio",
+ "mcu": "esp32c3",
+ "variant": "esp32c3-mini",
+ "variants_dir": "variants"
},
- "core": "esp32",
- "extra_flags": "-DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1",
- "f_cpu": "160000000L",
- "f_flash": "80000000L",
- "flash_mode": "qio",
- "mcu": "esp32c3",
- "variant": "esp32c3-mini",
- "variants_dir": "variants"
- },
- "connectivity": [
- "wifi"
- ],
- "debug": {
- "openocd_target": "esp32c3.cfg"
- },
- "frameworks": [
- "arduino",
- "espidf"
- ],
- "name": "Espressif ESP32-C3-MINI",
- "upload": {
- "flash_size": "4MB",
- "maximum_ram_size": 327680,
- "maximum_size": 4194304,
- "require_upload_port": true,
- "speed": 460800
- },
- "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html",
- "vendor": "Espressif"
+ "connectivity": ["wifi"],
+ "debug": {
+ "openocd_target": "esp32c3.cfg"
+ },
+ "frameworks": ["arduino", "espidf"],
+ "name": "Espressif ESP32-C3 Mini",
+ "upload": {
+ "flash_size": "4MB",
+ "maximum_ram_size": 327680,
+ "maximum_size": 4194304,
+ "require_upload_port": true,
+ "speed": 460800
+ },
+ "url": "https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/hw-reference/esp32c3/user-guide-devkitm-1.html",
+ "vendor": "Espressif"
}
diff --git a/data/web/add.html b/data/web/add.html
new file mode 100644
index 0000000..153a0d0
--- /dev/null
+++ b/data/web/add.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+ Tiny NOT Add WiFi
+
+
+
+ WiFi configuration
+
+
+
+
+
+
+
diff --git a/data/web/app.js b/data/web/app.js
new file mode 100644
index 0000000..7d1af05
--- /dev/null
+++ b/data/web/app.js
@@ -0,0 +1,51 @@
+var cssPropertySupported = (function () {
+ var mem = {}; // save test results for efficient future calls with same parameters
+
+ return function cssPropertySupported(prop, values) {
+ if (mem[prop + values]) return mem[prop + values];
+
+ var element = document.createElement('p'),
+ index = values.length,
+ value,
+ result = false;
+
+ try {
+ while (index--) {
+ value = values[index];
+ element.style.setProperty(prop, value);
+
+ if (element.style.getPropertyValue(prop) === value) {
+ result = true;
+ break;
+ }
+ }
+ } catch (pError) {}
+
+ mem[prop + values] = result;
+ return result;
+ };
+})();
+
+if (!cssPropertySupported('display', ['flex'])) {
+ fillViewport();
+}
+
+function fillViewport() {
+ var vh = window.innerHeight * 0.01;
+
+ var main = document.querySelector('main');
+ var footer = document.querySelector('footer');
+ main.style.minHeight = window.innerHeight - footer.offsetHeight * 2 + 'px';
+
+ window.addEventListener('resize', function () {
+ main.style.minHeight =
+ window.innerHeight - footer.offsetHeight * 2 + 'px';
+ });
+
+ // also hide early android browser's zoom controls
+ var meta = document.createElement('meta');
+ meta.name = 'viewport';
+ meta.content =
+ 'width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no';
+ document.getElementsByTagName('head')[0].appendChild(meta);
+}
diff --git a/data/web/index.html b/data/web/index.html
new file mode 100644
index 0000000..45f1546
--- /dev/null
+++ b/data/web/index.html
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+ Tiny NOT setup
+
+
+
+ Start
+
+
+ Status
+
+
+ Field |
+ Value |
+
+
+
+ Uptime |
+
+
+ |
+
+
+
+
+
+
+ Stored
+
+
+ -
+ stored SSID - stored BSSID - RSSI
+
+
+
+
+
+
+
+ Scan results
+
+
+
+ - SSID - BSSID - RSSI
+
+
+
+
+
+
+
+
+
+
diff --git a/data/web/logo.png b/data/web/logo.png
new file mode 100644
index 0000000..628655f
Binary files /dev/null and b/data/web/logo.png differ
diff --git a/data/web/style.css b/data/web/style.css
new file mode 100644
index 0000000..df84f76
--- /dev/null
+++ b/data/web/style.css
@@ -0,0 +1,116 @@
+* {
+ box-sizing: border-box;
+ margin: 0;
+ font-family: Arial, Helvetica, sans-serif;
+ color: white;
+}
+
+html {
+ /* min-height: 100%; */
+ background-color: #232323;
+}
+
+h1,
+h2 {
+ text-align: center;
+ margin-top: 0.5rem;
+ margin-bottom: 1rem;
+}
+
+[data-symbol]::before {
+ content: attr(data-symbol);
+ padding-right: 0.5rem;
+}
+
+input[type='submit'],
+a {
+ background-color: #233c54;
+}
+
+input[type='submit']:hover,
+input[type='submit']:focus-visible,
+a:hover,
+a:focus-visible {
+ background-color: #61a8ea;
+}
+
+input,
+a {
+ padding: 0.25rem;
+ border-radius: 4px;
+ display: inline-block;
+}
+
+ol {
+ /* list-style-position: inside; */
+ display: inline-block;
+ text-align: left;
+}
+
+table {
+ display: inline-table;
+}
+
+section {
+ margin-bottom: 1rem;
+}
+
+.list-actions {
+ display: inline-block;
+}
+
+.section-actions {
+ margin: 0.5rem 0;
+}
+
+input {
+ border: 1px solid grey;
+ background-color: rgb(40, 40, 40);
+}
+
+body {
+ min-height: 100vh;
+ min-height: 100dvh;
+ display: flex;
+ flex-direction: column;
+}
+
+main {
+ flex-grow: 1;
+ padding-bottom: 40px;
+ display: block;
+}
+
+input {
+ display: block;
+ margin: 0.5rem auto;
+}
+
+input[required] {
+ border-color: red;
+}
+
+nav,
+.align-center {
+ text-align: center;
+}
+
+form {
+ background-color: #181818;
+ border-radius: 8px;
+ padding: 1rem;
+ margin: auto;
+ width: fit-content;
+ display: inline-block;
+}
+
+form div {
+ position: relative;
+}
+
+footer {
+ background-color: #233c54;
+ text-align: center;
+ padding: 1rem 0;
+ border-radius: 8px 8px 0 0;
+}
diff --git a/lib/WiFiManager/src/WiFiManager.cpp b/lib/WiFiManager/src/WiFiManager.cpp
index 3147525..12f1508 100644
--- a/lib/WiFiManager/src/WiFiManager.cpp
+++ b/lib/WiFiManager/src/WiFiManager.cpp
@@ -10,19 +10,23 @@
#endif
#ifdef ESP32
-#include
#include
#include
+#include
WiFiMulti wifiMulti;
+WiFiEventCb disconnectedEventHandler;
+typedef wifi_ap_record_t bss_info;
+int scanned_ap_num;
#elif defined(ESP8266)
#include
ESP8266WiFiMulti wifiMulti;
+WiFiEventHandler disconnectedEventHandler;
+typedef nullptr_t WiFiEventInfo_t;
#endif
-WiFiEventHandler disconnectedEventHandler;
std::vector *connection_queue;
JsonDocument doc;
-const uint8 *bssid;
+const uint8_t *bssid;
// keep track of current connection
uint currentConfig;
@@ -38,7 +42,7 @@ int sortByRSSI(const void *a, const void *b) {
bool matches_ssid(const std::vector &networks, JsonVariant config) {
for (auto &&network : networks) {
- if (memcmp((char *)network->ssid, config["ssid"].as().c_str(), network->ssid_len) == 0) {
+ if (memcmp((char *)network->ssid, config["ssid"].as().c_str(), strlen((const char *)network->ssid)) == 0) {
return true;
}
}
@@ -60,11 +64,23 @@ bool matches_bssid(const std::vector &networks, JsonVariant co
return false;
}
-void disconnectHandler(const WiFiEventStationModeDisconnected &event = {}) {
+void startRecoveryAP() {
+ inRecovery = true;
+ WiFi.disconnect();
+ WiFi.softAP("ESP reached recovery state", NULL);
+}
+
+void disconnectHandler(WiFiEvent_t event, WiFiEventInfo_t info) {
if (inRecovery) {
return;
}
+ if (connection_queue->size() == 0) {
+ Log.noticeln("No configurations found to try. Starting recovery sequence");
+ startRecoveryAP();
+ return;
+ }
+
Log.noticeln("In disc-handler");
JsonVariant config = connection_queue->at(currentConfig);
if (currentTry < config["retries"]) {
@@ -73,9 +89,7 @@ void disconnectHandler(const WiFiEventStationModeDisconnected &event = {}) {
} else {
if (currentConfig + 1 == connection_queue->size()) {
Log.noticeln("Station disconnected. No more configurations to try. Starting recovery sequence");
- inRecovery = true;
- WiFi.disconnect();
- WiFi.softAP("ESP reached recovery state", "tempPW12345");
+ startRecoveryAP();
return;
} else {
currentTry = 0;
@@ -102,13 +116,22 @@ void disconnectHandler(const WiFiEventStationModeDisconnected &event = {}) {
tryStartMillis = millis();
if (!config["hostname"].isNull()) {
Log.noticeln("Set hostname %s", config["hostname"].as().c_str());
+#ifdef ESP32
+ WiFi.mode(WIFI_MODE_NULL);
+#endif
WiFi.setHostname(config["hostname"].as().c_str());
}
- WiFi.begin(config["ssid"].as(), psk, 0);
- Log.noticeln("Set Wifi to ssid: '%s' psk: '%s' bssid: %s", WiFi.SSID().c_str(), WiFi.psk().c_str(), WiFi.BSSIDstr());
+ WiFi.begin(config["ssid"].as(), psk, 0, bssid);
+ Log.noticeln("Set Wifi to ssid: '%s' psk: '%s' bssid: %s", WiFi.SSID().c_str(), WiFi.psk().c_str(), WiFi.BSSIDstr().c_str());
}
+#ifdef ESP8266
+void disconnectHandler_ESP8266(const WiFiEventStationModeDisconnected &event = {}) {
+ disconnectHandler(WiFiEvent_t::WIFI_EVENT_ANY, nullptr);
+}
+#endif
+
void getScanResults(int count) {
Log.noticeln("Scan done. Found %i radios", count);
@@ -116,10 +139,14 @@ void getScanResults(int count) {
std::vector networks;
networks.resize(count);
- uint8 max_ssid_length = 0;
+ size_t max_ssid_length = 0;
for (int i = 0; i < count; i++) {
+#ifdef ESP32
+ networks.at(i) = (bss_info *)WiFiScanClass::getScanInfoByIndex(i);
+#elif defined(ESP8266)
networks.at(i) = WiFi.getScanInfoByIndex(i);
- max_ssid_length = max(networks.at(i)->ssid_len, max_ssid_length);
+#endif
+ max_ssid_length = max(strlen((const char *)networks.at(i)->ssid), max_ssid_length);
}
std::sort(networks.begin(), networks.end(), sortByRSSI);
@@ -129,7 +156,14 @@ void getScanResults(int count) {
for (auto &network : networks) {
bssid = network->bssid;
snprintf_P(bssid_char, sizeof(bssid_char), "%02X:%02X:%02X:%02X:%02X:%02X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
- snprintf_P(buffer, sizeof(buffer), "SSID: %-*s, RSSI: %i, BSSID: %s, hidden: %i", max_ssid_length, network->ssid, network->rssi, bssid_char, network->is_hidden);
+#ifdef ESP32
+ const bool ssid_is_hidden = strlen((const char *)network->ssid) == 0;
+ const uint8_t channel = network->primary;
+#elif defined(ESP8266)
+ const bool ssid_is_hidden = network->is_hidden;
+ const uint8_t channel = network->channel;
+#endif
+ snprintf_P(buffer, sizeof(buffer), "SSID: %-*s, RSSI: %i, BSSID: %s, hidden: %i, channel: %i", max_ssid_length, network->ssid, network->rssi, bssid_char, ssid_is_hidden, channel);
Log.noticeln("%s", buffer);
}
@@ -145,7 +179,17 @@ void getScanResults(int count) {
Log.noticeln("Adding %s to queue", config["name"].as().c_str());
}
}
- disconnectHandler(); // start connecting to configurations
+
+#ifdef ESP32
+ disconnectHandler(WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_START, arduino_event_info_t{}); // start connecting to configurations
+#endif
+#ifdef ESP8266
+ disconnectHandler(WiFiEvent_t::WIFI_EVENT_ANY, nullptr); // start connecting to configurations
+#endif
+}
+
+void getScanResultsESP32(WiFiEvent_t event, WiFiEventInfo_t info) {
+ getScanResults(WiFi.scanComplete()); // assume scan always completed since we are in scan complete CB
}
wl_status_t WiFiManager::tick(unsigned long update_interval = 100) {
@@ -198,15 +242,19 @@ bool WiFiManager::loadFromJson(File file) {
setupAP = doc["SetupAP"].as();
for (JsonVariant i : doc["WiFiSta"].as()) {
- Serial.println(i["name"].as() + i["ssid"].as() + i["psk"].as());
Log.noticeln("Name: %s", i["name"].as().c_str());
Log.noticeln("SSID: %s", i["ssid"].as().c_str());
Log.noticeln("PSK: %s", i["psk"].as().c_str());
}
- disconnectedEventHandler = WiFi.onStationModeDisconnected(disconnectHandler);
-
+#ifdef ESP32
+ WiFi.onEvent(disconnectHandler, WiFiEvent_t::ARDUINO_EVENT_WIFI_STA_DISCONNECTED);
+ WiFi.onEvent(getScanResultsESP32, WiFiEvent_t::ARDUINO_EVENT_WIFI_SCAN_DONE);
+ WiFi.scanNetworks(true, true);
+#elif defined(ESP8266)
+ disconnectedEventHandler = WiFi.onStationModeDisconnected(disconnectHandler_ESP8266);
WiFi.scanNetworksAsync(getScanResults, true);
+#endif
loadConnections();
@@ -216,7 +264,7 @@ bool WiFiManager::loadFromJson(File file) {
bool WiFiManager::loadConnections() {
WiFi.persistent(false);
- WiFi.mode(WIFI_AP_STA);
+ WiFi.mode(WIFI_STA);
IPAddress local_ip, gateway, subnet;
local_ip.fromString("10.0.0.1");
@@ -225,8 +273,9 @@ bool WiFiManager::loadConnections() {
uint8_t newMACAddress[WL_MAC_ADDR_LENGTH] = {0x32, 0xAE, 0xA4, 0x07, 0x0D, 0x66};
- const bool ap = true;
+ const bool ap = false;
+ // TODO: can't set AP channel, can't scan AP channel -> set ap, then start async scan seems to work
if (ap) {
if (setApMac(newMACAddress))
@@ -243,7 +292,11 @@ bool WiFiManager::loadConnections() {
}
bool WiFiManager::setApMac(uint8_t mac[WL_MAC_ADDR_LENGTH]) {
+#ifdef ESP32
+ esp_wifi_set_mac(WIFI_IF_AP, &mac[0]);
+#elif defined(ESP8266)
wifi_set_macaddr(SOFTAP_IF, &mac[0]);
+#endif
char charMacAddress[(sizeof(*mac) * 3)];
snprintf_P(charMacAddress, sizeof(charMacAddress), "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
diff --git a/lib/WiFiManager/src/WiFiManager.h b/lib/WiFiManager/src/WiFiManager.h
index c3a7c6b..7c0409c 100644
--- a/lib/WiFiManager/src/WiFiManager.h
+++ b/lib/WiFiManager/src/WiFiManager.h
@@ -1,15 +1,22 @@
#ifdef ESP32
-#include
+#include
#include
-#include
#elif defined(ESP8266)
#include
#endif
+#ifndef WL_IPV4_LENGTH
+#define WL_IPV4_LENGTH 4
+#endif
+
#ifndef WL_IPV6_LENGTH
#define WL_IPV6_LENGTH 16
#endif
+#ifndef WL_MAC_ADDR_LENGTH
+#define WL_MAC_ADDR_LENGTH 6
+#endif
+
struct WiFiConnection {
char *ssid;
char *bssid;
diff --git a/platformio.ini b/platformio.ini
index 31b146b..4431208 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -15,6 +15,13 @@ default_envs = nodemcuv2
build_flags = '-D BUILD_VERSION="0.0.1"' -D DEBUG_ESP_PORT=Serial -D NDEBUG
extra_scripts =
./extra_scripts/reset_board.py
+lib_deps = miguel5612/MQUnifiedsensor @ ^3.0.0
+ adafruit/DHT sensor library @ ^1.4.6
+ dawidchyrzynski/home-assistant-integration @ ^2.1.0
+ arduinogetstarted/ezLED @ ^1.0.1
+ https://github.com/gilex-dev/rc-switch.git#SKL-W1B
+ thijse/ArduinoLog @ ^1.1.1
+ bblanchon/ArduinoJson @ ^7.4.2
[env:nodemcuv2]
platform = espressif8266
@@ -28,6 +35,28 @@ build_flags = ${common_env_data.build_flags} -D SERIAL_BAUD_RATE=${this.monitor_
extra_scripts = ${common_env_data.extra_scripts}
lib_compat_mode = strict
lib_ldf_mode = chain
-lib_deps =
- thijse/ArduinoLog @ ^1.1.1
- bblanchon/ArduinoJson @ ^7.4.2
+lib_deps = ${common_env_data.lib_deps}
+
+[env:nodemcuv2_ota]
+extends = env:nodemcuv2
+upload_protocol = espota
+[env:esp32-dev]
+upload_port = /dev/ttyUSB0
+framework = arduino
+platform = espressif32
+board = esp32dev
+upload_speed = 921600
+monitor_speed = 115200
+monitor_filters = esp32_exception_decoder
+board_build.filesystem = littlefs
+build_flags = ${common_env_data.build_flags} -D SERIAL_BAUD_RATE=${this.monitor_speed} -D BUILD_DEBUG=false
+extra_scripts = ${common_env_data.extra_scripts}
+lib_compat_mode = strict
+lib_ldf_mode = chain
+lib_deps = ${common_env_data.lib_deps}
+ hoeken/PsychicHttp @ ^1.2.1
+
+[env:esp32c3-mini]
+platform = espressif32
+extends = env:esp32dev
+board = esp32c3-mini
diff --git a/src/main.cpp b/src/main.cpp
index 57b1b4a..4ef216a 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,6 +5,32 @@
#include
#include
+static const char serverCert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+-----END CERTIFICATE-----
+)EOF";
+
+static const char serverKey[] PROGMEM = R"EOF(
+-----BEGIN RSA PRIVATE KEY-----
+-----END RSA PRIVATE KEY-----
+)EOF";
+
+#ifdef ESP32
+#include
+#include
+#include
+#ifdef PSY_ENABLE_SSL
+PsychicHttpsServer server;
+#else
+PsychicHttpServer server;
+#endif
+
+#elif defined(ESP8266)
+#include
+BearSSL::ESP8266WebServerSecure server(443);
+BearSSL::ServerSessions serverCache(5);
+#endif
+
#ifndef SERIAL_BAUD_RATE
#define SERIAL_BAUD_RATE 115200
#endif
@@ -16,6 +42,25 @@ WiFiManager manager;
unsigned long currentMillis, oldMillis;
void setup() {
+ WiFi.persistent(false);
+#ifdef ESP8266
+ server.getServer().setRSACert(new BearSSL::X509List(serverCert), new BearSSL::PrivateKey(serverKey));
+
+ // Cache SSL sessions to accelerate the TLS handshake.
+ server.getServer().setCache(&serverCache);
+
+ server.on("/", []() {
+ server.send(200, "text/plain", "Hello from esp8266 over HTTPS!");
+ });
+ server.begin();
+#elif ESP32
+ WiFi.mode(WIFI_MODE_STA); // required by PsychicHttp
+ server.listen(443, serverCert, serverKey);
+ server.on("/", [](PsychicRequest *request) {
+ return request->reply(200, "text/plain", "Hello from esp32 over HTTPS!");
+ });
+#endif
+
Serial.begin(SERIAL_BAUD_RATE);
Log.setPrefix(printPrefix);
Log.setSuffix(printSuffix);
@@ -42,7 +87,6 @@ void setup() {
}
void loop() {
-
currentMillis = millis();
if (currentMillis - oldMillis >= 1000) {
@@ -52,5 +96,10 @@ void loop() {
if (manager.tick(1000) == WL_CONNECTED) {
// regularOperationStuff();
}
+
+#ifdef ESP8266
+ esp_yield();
+ server.handleClient();
+#endif
// importantStuff();
}
diff --git a/src/main.h b/src/main.h
index e69de29..5367fac 100644
--- a/src/main.h
+++ b/src/main.h
@@ -0,0 +1,3 @@
+#ifndef WL_MAC_ADDR_LENGTH
+#define WL_MAC_ADDR_LENGTH 6
+#endif