From 41e50d48946017ad22753f31dd88d7f4ab454dd4 Mon Sep 17 00:00:00 2001 From: doudar Date: Mon, 6 Nov 2023 17:59:00 -0600 Subject: [PATCH 1/6] Added a custom integrated implementation of WifiManager. - Updated builtin.h html color scheme. - When pages are served, BLE server will slow it's roll. - Added a custom integrated implementation of WifiManager. - Removed several BLE scan hard stops in favor of a single loop which waits for the scan to gracefully end. - Fixed bug where WiFi.getMode() wasn't returning the correct mode and caused slow/no wifi connections in AP mode. - Repurposed LogComData. Will now not transmit any logging info to WiFi when it's turned off. - Reworked web pages to start to implement more built in functions. - Removed style.css (Internal to the program now). --- CHANGELOG.md | 8 + data/bluetoothscanner.html | 28 -- data/btsimulator.html | 29 -- data/develop.html | 31 -- data/hrtowatts.html | 28 -- data/index.html | 34 +-- data/list.json | 2 +- data/settings.html | 23 -- data/shift.html | 25 -- data/status.html | 22 -- data/streamfit.html | 9 - data/style.css | 237 --------------- include/BLE_Common.h | 2 +- include/Builtin_Pages.h | 570 ++++++++++++++++++++++++++++++------ include/HTTP_Server_Basic.h | 16 +- include/WebsocketAppender.h | 1 + include/settings.h | 4 +- platformio.ini | 4 +- src/BLE_Client.cpp | 37 ++- src/BLE_Common.cpp | 22 +- src/HTTP_Server_Basic.cpp | 333 +++++++++++++++++++-- src/Main.cpp | 24 +- src/SensorCollector.cpp | 9 +- src/WebsocketAppender.cpp | 3 + 24 files changed, 873 insertions(+), 628 deletions(-) delete mode 100644 data/style.css diff --git a/CHANGELOG.md b/CHANGELOG.md index e45108b4..7b8d033b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added +- Added a custom integrated implementation of WifiManager. ### Changed - Updated communications overview picture. - Updated kit purchasing links. +- Updated builtin.h html color scheme. +- When pages are served, BLE server will slow it's roll. +- Removed several BLE scan hard stops in favor of a single loop which waits for the scan to gracefully end. +- Fixed bug where WiFi.getMode() wasn't returning the correct mode and caused slow/no wifi connections in AP mode. +- Repurposed LogComData. Will now not transmit any logging info to WiFi when it's turned off. +- Reworked web pages to start to implement more built in functions. +- Removed style.css (Internal to the program now). ### Hardware - Wire diameter reduced from 7.2mm to 6.0mm on the window passthrough to accommodate the latest batch of cables. diff --git a/data/bluetoothscanner.html b/data/bluetoothscanner.html index 5c92e7ab..ac68b6c0 100644 --- a/data/bluetoothscanner.html +++ b/data/bluetoothscanner.html @@ -1,17 +1,3 @@ - - - - - - SmartSpin2K Bluetooth Scanner - - - -
http://github.com/doudar/SmartSpin2k @@ -219,20 +205,6 @@

xhttp.send(); } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; - - //Delay loading css to not swamp webserver - window.addEventListener('load', function () { - setTimeout(loadCss, 100); - }, false); - \ No newline at end of file diff --git a/data/btsimulator.html b/data/btsimulator.html index 0cabde73..19f593ea 100644 --- a/data/btsimulator.html +++ b/data/btsimulator.html @@ -1,17 +1,3 @@ - - - - - - - SmartSpin2K Web Server - - -

@@ -277,21 +263,6 @@

ERG Target Watts

xhttp.send(); } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; - - //Delay loading css to not swamp webserver - window.addEventListener('load', function () { - setTimeout(loadCss, 100); - setTimeout(requestConfigValues, 500); - }, false); - let updateWattsTimer = null; function autoUpdateWattsClick() { if (updateWattsTimer != null) { diff --git a/data/develop.html b/data/develop.html index 6fe06ccc..6b8ccce5 100644 --- a/data/develop.html +++ b/data/develop.html @@ -1,16 +1,3 @@ - - - - - - - SmartSpin2K Web Server - -
http://github.com/doudar/SmartSpin2k @@ -27,22 +14,4 @@

- - \ No newline at end of file diff --git a/data/hrtowatts.html b/data/hrtowatts.html index 2e034e1f..48b2dd42 100644 --- a/data/hrtowatts.html +++ b/data/hrtowatts.html @@ -1,17 +1,3 @@ - - - - - - - SmartSpin2K Web Server - - -
http://github.com/doudar/SmartSpin2k @@ -102,20 +88,6 @@

xhttp.send(); } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; - - //Delay loading css to not swamp webserver - window.addEventListener('load', function () { - setTimeout(loadCss, 100); - }, false); - function updateSlider(element, valueElement) { var sliderValue = element.value; valueElement.innerHTML = sliderValue; diff --git a/data/index.html b/data/index.html index e305f588..7d010d4b 100644 --- a/data/index.html +++ b/data/index.html @@ -1,24 +1,12 @@ - - - - - - - SmartSpin2K Web Server - - -
http://github.com/doudar/SmartSpin2k +
http://github.com/doudar/SmartSpin2k

SmartSpin2k

Web Shifter

Heartrate to Watts Setup

-

Settings

-

Bluetooth Scanner

+

WiFi Setup

+

Bluetooth Setup

+

Advanced Settings

Developer Tools

SS2K Help

@@ -48,20 +36,6 @@

xhttp.send(); } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; - - //Delay loading css to not swamp webserver - window.addEventListener('load', function () { - setTimeout(loadCss, 100); - }, false); - \ No newline at end of file diff --git a/data/list.json b/data/list.json index 03ed5c9c..91faa239 100644 --- a/data/list.json +++ b/data/list.json @@ -1 +1 @@ -["bluetoothscanner.html", "btsimulator.html", "favicon.ico", "hrtowatts.html", "index.html", "settings.html", "shift.html", "status.html", "style.css", "streamfit.html", "develop.html"] \ No newline at end of file +["bluetoothscanner.html", "btsimulator.html", "favicon.ico", "hrtowatts.html", "index.html", "settings.html", "shift.html", "status.html", "streamfit.html", "develop.html"] \ No newline at end of file diff --git a/data/settings.html b/data/settings.html index 9a89abee..16f414b0 100644 --- a/data/settings.html +++ b/data/settings.html @@ -1,16 +1,3 @@ - - - - - - SmartSpin2K Web Server - - -
http://github.com/doudar/SmartSpin2k @@ -350,18 +337,8 @@

xhttp.send(); } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; - //Delay loading css to not swamp webserver window.addEventListener('load', function () { - setTimeout(loadCss, 100); startConfigUpdate(); }, false); diff --git a/data/shift.html b/data/shift.html index a44f0307..640cc96f 100644 --- a/data/shift.html +++ b/data/shift.html @@ -1,17 +1,3 @@ - - - - - - - SmartSpin2K Web Server - - -

@@ -44,7 +30,6 @@

Shift

xhr.send(); } - function startUpdate() { //Update values on specified interval loading late because this tiny webserver hates frequent requests if (updateTimer === undefined) { @@ -72,18 +57,8 @@

Shift

xhttp.send(); } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; - //Delay loading css to not swamp webserver window.addEventListener('load', function () { - setTimeout(loadCss, 100); setTimeout(requestConfigValues, 500); startUpdate(); }, false); diff --git a/data/status.html b/data/status.html index 48ba3b2c..28f9b849 100644 --- a/data/status.html +++ b/data/status.html @@ -1,16 +1,3 @@ - - - - - - SmartSpin2K Web Server - - -
http://github.com/doudar/SmartSpin2k @@ -193,18 +180,9 @@

} } - //define function to load css - var loadCss = function () { - var cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'style.css'; - var head = document.getElementsByTagName('head')[0]; - head.parentNode.insertBefore(cssLink, head); - }; //Delay loading css to not swamp webserver window.addEventListener('load', function () { - setTimeout(loadCss, 100); requestConfigValues(); setupLogging(); setTimeout(requestRuntimeValues(), 200); diff --git a/data/streamfit.html b/data/streamfit.html index 8a8a554e..46c7dc95 100644 --- a/data/streamfit.html +++ b/data/streamfit.html @@ -1,12 +1,3 @@ - - - - - - - SmartSpin2K Web Server - -
http://github.com/doudar/SmartSpin2k diff --git a/data/style.css b/data/style.css deleted file mode 100644 index d5e8b2a6..00000000 --- a/data/style.css +++ /dev/null @@ -1,237 +0,0 @@ -html { - font-family: sans-serif; - display: inline-block; - margin: 5px auto; - text-align: center; - background-color: #03245c; - line-height: 1em; -} - -label { - font-size: medium; -} - -div { - font-size: medium; -} - -a { - color: #000000; -} -a:visited { - color: #000000; -} -h1 { - color: #03245c; - padding: 0.5rem; - line-height: 1em; -} -h2 { - color: #000000; - font-size: 1.5rem; - font-weight: bold; -} -p { - font-size: 1rem; -} -.button { - display: inline-block; - background-color: #2a9df4; - border: line; - border-radius: 4px; - color: #d0efff; - padding: 10px 40px; - text-decoration: none; - font-size: 20px; - margin: 0px; - cursor: pointer; -} -.button2 { - background-color: #f44336; - padding: 10px 35px; -} -.switch { - position: relative; - display: inline-block; - width: 80px; - height: 40px; -} -.switch input { - display: none; -} -.slider { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - transition: 0.4s; - background-color: #03254c; - border-radius: 34px; -} -.slider:before { - position: absolute; - content: ""; - height: 37px; - width: 37px; - left: 2px; - bottom: 2px; - background-color: #d0efff; - -webkit-transition: 0.4s; - transition: 0.4s; - border-radius: 68px; -} -input:checked + .slider { - transition: 0.4s; - background-color: #2a9df4; -} -input:checked + .slider:before { - -webkit-transform: translateX(38px); - -ms-transform: translateX(38px); - transform: translateX(38px); -} -.slider2 { - -webkit-appearance: none; - margin: 5px; - width: 270px; - height: 20px; - background: #d0efff; - /*outline:8px ridge rgba(170,50,220, .6); - border-radius: 2rem;*/ - outline: none; - -webkit-transition: 0.2s; - transition: opacity 0.2s; -} -.slider2::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 30px; - height: 30px; - background: #03254c; - cursor: pointer; -} -.slider2::-moz-range-thumb { - width: 30px; - height: 30px; - background: #1167b1; - cursor: pointer; -} - -table.center { - margin-left: auto; - margin-right: auto; -} - -.tooltip { - position: relative; - display: inline-block; - border-bottom: 1px dotted #03254c; -} - -.tooltip .tooltiptext { - visibility: hidden; - width: 120px; - background-color: #03254c; - color: #d0efff; - text-align: center; - border-radius: 6px; - padding: 5px 0; - - /* Position the tooltip */ - position: absolute; - z-index: 1; -} - -.tooltip:hover .tooltiptext { - visibility: visible; -} - -.watermark { - display: inline; - position: fixed; - top: 0px; - left: 0px; - transform: translate(calc(50vw - 200px), calc(50vh - 170px)) rotate(45deg); - transition: 0.4s ease-in-out; - opacity: 0.7; - z-index: 99; - color: grey; - font-size: 7rem; -} - -.watermark:hidden { - transition: visibility 0s 2s, opacity 2s linear; -} - -.shiftButton { - -webkit-appearance: none; - -webkit-text-stroke: 2px rgba(104, 104, 104, 0.412); - appearance: auto; - width: 16%; - height: 6rem; - background: #03254c; - color: white; - cursor: pointer; - font-weight: bold; - font-size: calc(1vw + 1vh); -} - -.shiftBox { - background: #2a9df4; - color: white; - font-weight: bold; - font-size: calc(1vw + 1vh); - width: 4%; - text-align: center; -} - -body { - display: block; - margin: 0 auto; - background-color: #1167b1; - opacity: 1; - transition: 0.5s ease-in-out; - height: 100%; - width: 100%; -} - -fieldset { - border: 10px solid; - border-color: #1e3252; - box-sizing: border-box; - grid-area: 1 / 1; - padding: 5px; - margin: 0 auto; - z-index: -1; -} - -.confirmation-dialog { - display: flex; - align-items: center; - justify-content: center; - position: fixed; - top: 0; - left: 0; - height: 100%; - width: 100%; - background-color: rgba(201, 201, 201, 0.7); - z-index: 100; -} - -.confirmation-dialog > .confirmation-panel { - background-color: #03245c; - color: white; - padding: 20px; - border-radius: 10px; -} - -.confirmation-panel > .confirmation-buttongroup { - padding-top: 10px; -} - -.confirmation-buttongroup > input[type="button"] { - width: 75px; - height: 40px; - font-size: 1em; - border-radius: 10px; -} diff --git a/include/BLE_Common.h b/include/BLE_Common.h index 5096fd01..142e0fc2 100644 --- a/include/BLE_Common.h +++ b/include/BLE_Common.h @@ -175,7 +175,7 @@ class SpinBLEClient { boolean connectedCT = false; boolean connectedRemote = false; boolean doScan = false; - bool dontBlockScan = true; + // bool dontBlockScan = true; bool intentionalDisconnect = false; int noReadingIn = 0; int cscCumulativeCrankRev = 0; diff --git a/include/Builtin_Pages.h b/include/Builtin_Pages.h index 52ef5bef..6d869950 100644 --- a/include/Builtin_Pages.h +++ b/include/Builtin_Pages.h @@ -12,92 +12,492 @@ #include -String OTAStyle = ""; +const char HTTP_STYLE[] = + ""; /* Login page */ -String OTALoginIndex = "
" - "

ESP32 Login

" - " " - " " - "
" - "" + - OTAStyle; - -String noIndexHTML = "" - "" - "" - "

The webserver files
need to be updated.

" - "Please enter the credentials for your network
or upload a new " - "filesystem image using the
link below." - "" - " SmartSpin2K Web Server" - "" - "" - "
" - "

" - "" - "" - "" - "" - "
" - "
" - "" - "
" - "

Update " - "Firmware

" - "" - " " + - OTAStyle; +const char OTALOGININDEX[] = + "
" + "

SmartSpin2k Login

" + "Visit Github for the Username and Password" + " " + " " + "
" + ""; + +const char HTTP_TITLE[] = "SmartSpin2K Web Server"; + +const char NOINDEXHTML[] = + "

The webserver files
need to be updated.

" + "Please enter the credentials for your network
or upload a new " + "filesystem image using the
link below." + "
" + "" + "
"; + +const char GITHUBLINK[] = "http://github.com/doudar/SmartSpin2k"; /* Server Index Page */ -String OTAServerIndex = "" - "
" - "
SmartSpin2k OTA
" - "
" - "" - "" - "
" - "
" - "
0%
" - "
" - "
Valid files are firmware.bin or littlefs.bin
" - "
" - "" - ""; +String OTAServerIndex = + "" + "
" + "
SmartSpin2k OTA
" + "
" + "" + "" + "
" + "
" + "
0%
" + "
" + "
Valid files are firmware.bin or littlefs.bin
" + "
" + "" + ""; + +const char WM_LANGUAGE[] = "en-US"; // i18n lang code + +const char HTTP_HEAD_START[] = + "" + "" + "" + "" + "" + "{v}"; + +const char HTTP_SCRIPT[] = + ""; // @todo add button states, disable on click , show ack , spinner etc + +const char HTTP_HEAD_END[] = + "
http://github.com/doudar/SmartSpin2k

Main Index

{1}
"; // {c} = _bodyclass + +const char HTTP_ROOT_MAIN[] = "

{t}

{v}

"; + +const char* const HTTP_PORTAL_MENU[] = { + "

\n", // MENU_WIFI + "

\n", // MENU_WIFINOSCAN + "

\n", // MENU_INFO + "

\n", // MENU_PARAM + "

\n", // MENU_CLOSE + "

\n", // MENU_RESTART + "

\n", // MENU_EXIT + "

\n", // MENU_ERASE + "

\n", // MENU_UPDATE + "

" // MENU_SEP +}; + +// const char HTTP_PORTAL_OPTIONS[] = strcat(HTTP_PORTAL_MENU[0] , HTTP_PORTAL_MENU[3] , HTTP_PORTAL_MENU[7]); +const char HTTP_PORTAL_OPTIONS[] = ""; +const char HTTP_ITEM_QI[] = ""; // rssi icons +const char HTTP_ITEM_QP[] = "
{r}%
"; // rssi percentage {h} = hidden showperc pref +const char HTTP_ITEM[] = "
{v}{qi}{qp}
"; // {q} = HTTP_ITEM_QI, {r} = HTTP_ITEM_QP +// const char HTTP_ITEM[] = "
{v} {R} {r}% {q} {e}
"; // test all tokens + +const char HTTP_FORM_START[] = "
"; +const char HTTP_FORM_WIFI[] = + "
Show Password"; +const char HTTP_FORM_WIFI_END[] = ""; +const char HTTP_FORM_STATIC_HEAD[] = "

"; +const char HTTP_FORM_END[] = "

"; +const char HTTP_FORM_LABEL[] = ""; +const char HTTP_FORM_PARAM_HEAD[] = "

"; +const char HTTP_FORM_PARAM[] = "
\n"; // do not remove newline! + +const char HTTP_SCAN_LINK[] = "
"; +const char HTTP_REBOOT_LINK[] = "
"; + +const char HTTP_SAVED[] = "
Saving Credentials
Trying to connect ESP to network.
If it fails reconnect to AP to try again
"; +const char HTTP_PARAMSAVED[] = "
Saved
"; +const char HTTP_END[] = "
"; +const char HTTP_ERASEBTN[] = "
"; +const char HTTP_UPDATEBTN[] = "
"; +const char HTTP_BACKBTN[] = "

"; + +const char HTTP_STATUS_ON[] = "
Connected to {v}
with IP {i}
"; +const char HTTP_STATUS_OFF[] = "
Not connected to {v}{r}
"; // {c=class} {v=ssid} {r=status_off} +const char HTTP_STATUS_OFFPW[] = "
Authentication failure"; // STATION_WRONG_PASSWORD, no eps32 +const char HTTP_STATUS_OFFNOAP[] = "
AP not found"; // WL_NO_SSID_AVAIL +const char HTTP_STATUS_OFFFAIL[] = "
Could not connect"; // WL_CONNECT_FAILED +const char HTTP_STATUS_NONE[] = "
No AP set
"; +const char HTTP_BR[] = "
"; + +const char HTTP_UPDATE_FAIL[] = "
Update failed!
Reboot device and try again
"; +const char HTTP_UPDATE_SUCCESS[] = "
Update successful.
Device rebooting now...
"; + +const char HTTP_INFO_esphead[] = "

esp32


"; +const char HTTP_INFO_chiprev[] = "
Chip rev
{1}
"; +const char HTTP_INFO_lastreset[] = "
Last reset reason
CPU0: {1}
CPU1: {2}
"; +const char HTTP_INFO_aphost[] = "
Access point hostname
{1}
"; +const char HTTP_INFO_psrsize[] = "
PSRAM Size
{1} bytes
"; +const char HTTP_INFO_temp[] = "
Temperature
{1} C° / {2} F°
"; +const char HTTP_INFO_hall[] = "
Hall
{1}
"; + +const char HTTP_INFO_memsmeter[] = "
"; +const char HTTP_INFO_memsketch[] = "
Memory - Sketch size
Used / Total bytes
{1} / {2}"; +const char HTTP_INFO_freeheap[] = "
Memory - Free heap
{1} bytes available
"; +const char HTTP_INFO_wifihead[] = "

WiFi


"; +const char HTTP_INFO_uptime[] = "
Uptime
{1} mins {2} secs
"; +const char HTTP_INFO_chipid[] = "
Chip ID
{1}
"; +const char HTTP_INFO_idesize[] = "
Flash size
{1} bytes
"; +const char HTTP_INFO_sdkver[] = "
SDK version
{1}
"; +const char HTTP_INFO_cpufreq[] = "
CPU frequency
{1}MHz
"; +const char HTTP_INFO_apip[] = "
Access point IP
{1}
"; +const char HTTP_INFO_apmac[] = "
Access point MAC
{1}
"; +const char HTTP_INFO_apssid[] = "
Access point SSID
{1}
"; +const char HTTP_INFO_apbssid[] = "
BSSID
{1}
"; +const char HTTP_INFO_stassid[] = "
Station SSID
{1}
"; +const char HTTP_INFO_staip[] = "
Station IP
{1}
"; +const char HTTP_INFO_stagw[] = "
Station gateway
{1}
"; +const char HTTP_INFO_stasub[] = "
Station subnet
{1}
"; +const char HTTP_INFO_dnss[] = "
DNS Server
{1}
"; +const char HTTP_INFO_host[] = "
Hostname
{1}
"; +const char HTTP_INFO_stamac[] = "
Station MAC
{1}
"; +const char HTTP_INFO_conx[] = "
Connected
{1}
"; +const char HTTP_INFO_autoconx[] = "
Autoconnect
{1}
"; + +const char HTTP_INFO_aboutarduino[] = "
Arduino
{1}
"; +const char HTTP_INFO_aboutsdk[] = "
ESP-SDK/IDF
{1}
"; +const char HTTP_INFO_aboutdate[] = "
Build date
{1}
"; + +const char WIFI_PAGE_TITLE[] = "

WiFi Setup

"; +const char S_y[] = "Yes"; +const char S_n[] = "No"; +const char S_enable[] = "Enabled"; +const char S_disable[] = "Disabled"; +const char S_GET[] = "GET"; +const char S_POST[] = "POST"; +const char S_NA[] = "Unknown"; +const char S_passph[] = "********"; +const char S_titlewifisaved[] = "Credentials saved"; +const char S_titlewifisettings[] = "Settings saved"; +const char S_titlewifi[] = "Config ESP"; +const char S_titleinfo[] = "Info"; +const char S_titleparam[] = "Setup"; +const char S_titleparamsaved[] = "Setup saved"; +const char S_titleexit[] = "Exit"; +const char S_titlereset[] = "Reset"; +const char S_titleerase[] = "Erase"; +const char S_titleclose[] = "Close"; +const char S_options[] = "options"; +const char S_nonetworks[] = "No networks found. Refresh to scan again."; +const char S_staticip[] = "Static IP"; +const char S_staticgw[] = "Static gateway"; +const char S_staticdns[] = "Static DNS"; +const char S_subnet[] = "Subnet"; +const char S_exiting[] = "Exiting"; +const char S_resetting[] = "Module will reset in a few seconds."; +const char S_closing[] = "You can close the page, portal will continue to run"; +const char S_error[] = "An error occured"; +const char S_notfound[] = "File not found\n\n"; +const char S_uri[] = "URI: "; +const char S_method[] = "\nMethod: "; +const char S_args[] = "\nArguments: "; +const char S_parampre[] = "param_"; + +// debug strings +const char D_HR[] = "--------------------"; + +const char S_ssidpre[] = "ESP32"; + +const char WM_VERSION_STR[] = "v2.0.15-rc.1"; + +static const char _wifi_token[] = "wifi"; +static const char _wifinoscan_token[] = "wifinoscan"; +static const char _info_token[] = "info"; +static const char _param_token[] = "param"; +static const char _close_token[] = "close"; +static const char _restart_token[] = "restart"; +static const char _exit_token[] = "exit"; +static const char _erase_token[] = "erase"; +static const char _update_token[] = "update"; +static const char _sep_token[] = "sep"; +static const char _custom_token[] = "custom"; +static PGM_P _menutokens[] = {_wifi_token, _wifinoscan_token, _info_token, _param_token, _close_token, _restart_token, + _exit_token, _erase_token, _update_token, _sep_token, _custom_token}; +const uint8_t _nummenutokens = (sizeof(_menutokens) / sizeof(PGM_P)); + +const char R_root[] = "/"; +const char R_wifi[] = "/wifi"; +const char R_wifinoscan[] = "/0wifi"; +const char R_wifisave[] = "/send_settings"; +const char R_info[] = "/info"; +const char R_param[] = "/param"; +const char R_paramsave[] = "/paramsave"; +const char R_restart[] = "/restart"; +const char R_exit[] = "/exit"; +const char R_close[] = "/close"; +const char R_erase[] = "/erase"; +const char R_status[] = "/status"; +const char R_update[] = "/update"; +const char R_updatedone[] = "/u"; + +// Strings +const char S_ip[] = "ip"; +const char S_gw[] = "gw"; +const char S_sn[] = "sn"; +const char S_dns[] = "dns"; + +// Tokens +//@todo consolidate and reduce +const char T_ss[] = "{"; // token start sentinel +const char T_es[] = "}"; // token end sentinel +const char T_1[] = "{1}"; // @token 1 +const char T_2[] = "{2}"; // @token 2 +const char T_3[] = "{3}"; // @token 2 +const char T_v[] = "{v}"; // @token v +const char T_V[] = "{V}"; // @token v +const char T_I[] = "{I}"; // @token I +const char T_i[] = "{i}"; // @token i +const char T_n[] = "{n}"; // @token n +const char T_p[] = "{p}"; // @token p +const char T_t[] = "{t}"; // @token t +const char T_l[] = "{l}"; // @token l +const char T_c[] = "{c}"; // @token c +const char T_e[] = "{e}"; // @token e +const char T_q[] = "{q}"; // @token q +const char T_r[] = "{r}"; // @token r +const char T_R[] = "{R}"; // @token R +const char T_h[] = "{h}"; // @token h + +// http +const char HTTP_HEAD_CL[] = "Content-Length"; +const char HTTP_HEAD_CT[] = "text/html"; +const char HTTP_HEAD_CT2[] = "text/plain"; +const char HTTP_HEAD_CORS[] = "Access-Control-Allow-Origin"; +const char HTTP_HEAD_CORS_ALLOW_ALL[] = "*"; + +const char* const WIFI_STA_STATUS[]{ + "WL_IDLE_STATUS", // 0 STATION_IDLE + "WL_NO_SSID_AVAIL", // 1 STATION_NO_AP_FOUND + "WL_SCAN_COMPLETED", // 2 + "WL_CONNECTED", // 3 STATION_GOT_IP + "WL_CONNECT_FAILED", // 4 STATION_CONNECT_FAIL, STATION_WRONG_PASSWORD(NI) + "WL_CONNECTION_LOST", // 5 + "WL_DISCONNECTED", // 6 + "WL_STATION_WRONG_PASSWORD" // 7 KLUDGE +}; + +const char* const AUTH_MODE_NAMES[]{"OPEN", "WEP", "WPA_PSK", "WPA2_PSK", "WPA_WPA2_PSK", "WPA2_ENTERPRISE", "MAX"}; + +const char* const WIFI_MODES[] = {"NULL", "STA", "AP", "STA+AP"}; \ No newline at end of file diff --git a/include/HTTP_Server_Basic.h b/include/HTTP_Server_Basic.h index b38628fd..72416382 100644 --- a/include/HTTP_Server_Basic.h +++ b/include/HTTP_Server_Basic.h @@ -13,9 +13,11 @@ class HTTP_Server { private: - public: - bool internetConnection; + String processWiFiHTML(); + public: + bool internetConnection = false; + bool isServing = false; void start(); void stop(); static void handleLittleFSFile(); @@ -23,9 +25,7 @@ class HTTP_Server { static void settingsProcessor(); static void handleHrSlider(); static void FirmwareUpdate(); - static void webClientUpdate(void *pvParameters); - HTTP_Server() { internetConnection = false; } }; @@ -38,9 +38,13 @@ void telegramUpdate(void *pvParameters); #define SEND_TO_TELEGRAM(message) (void)message #endif -// wifi Function void startWifi(); void stopWifi(); - +String getScanItemOut(); +String encryptionTypeStr(uint8_t authmode); +bool WiFi_scanNetworks(); +String getHTTPHead(); +int getRSSIasQuality(int RSSI); +String htmlEntities(String str, bool whitespace = false); extern HTTP_Server httpServer; diff --git a/include/WebsocketAppender.h b/include/WebsocketAppender.h index c893ba56..653302d0 100644 --- a/include/WebsocketAppender.h +++ b/include/WebsocketAppender.h @@ -9,6 +9,7 @@ #include #include "LogAppender.h" +#include "main.h" using namespace websockets; diff --git a/include/settings.h b/include/settings.h index 91bf90bc..0921e909 100644 --- a/include/settings.h +++ b/include/settings.h @@ -204,7 +204,7 @@ #define MAX_RECONNECT_TRIES 3 // loop speed for the SmartSpin2k BLE communications -#define BLE_NOTIFY_DELAY 503 +#define BLE_NOTIFY_DELAY 503 // loop speed for the SmartSpin2k BLE Client reconnect #define BLE_CLIENT_DELAY 101 @@ -258,7 +258,7 @@ #define NORMAL_CAD 90 // Temperature of the ESP32 at which to start reducing the power output of the stepper motor driver. -#define THROTTLE_TEMP 85 +#define THROTTLE_TEMP 100 // Size of the Aux Serial Buffer for Peloton #define AUX_BUF_SIZE 10 diff --git a/platformio.ini b/platformio.ini index cb7f04c9..0fadb375 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,7 +13,7 @@ default_envs = release [esp32doit] lib_ldf_mode = chain lib_compat_mode = strict -platform = espressif32 @ 6.0.1 +platform = espressif32 @ 6.4.0 board = esp32doit-devkit-v1 framework = arduino board_build.partitions = min_spiffs.csv @@ -26,7 +26,7 @@ build_flags = !python build_date_macro.py -D CONFIG_BT_NIMBLE_MAX_CONNECTIONS=6 -D CONFIG_MDNS_STRICT_MODE=1 - -D CORE_DEBUG_LEVEL=1 + -D CORE_DEBUG_LEVEL=2 -D ARDUINO_SERIAL_EVENT_TASK_STACK_SIZE=3500 lib_deps = https://github.com/h2zero/NimBLE-Arduino/archive/refs/tags/1.4.0.zip diff --git a/src/BLE_Client.cpp b/src/BLE_Client.cpp index 9bf82a58..b5ae19de 100644 --- a/src/BLE_Client.cpp +++ b/src/BLE_Client.cpp @@ -58,16 +58,30 @@ static void onNotify(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t } } -// BLE Client loop task +// BLE Client loop task. Manages connections to BLE servers. void bleClientTask(void *pvParameters) { + static unsigned long scanCacheTimer = millis(); for (;;) { - vTaskDelay(BLE_CLIENT_DELAY / portTICK_PERIOD_MS); // Delay a second between loops. + if (httpServer.isServing) { // Scan slower if HTTP Server is working. + vTaskDelay((BLE_NOTIFY_DELAY * 3) / portTICK_PERIOD_MS); + } + vTaskDelay(BLE_CLIENT_DELAY / portTICK_PERIOD_MS); + + NimBLEScan *scan = NimBLEDevice::getScan(); + if (scan) { + while (scan->isScanning()) { // wait for current scan to finish + vTaskDelay(BLE_CLIENT_DELAY / portTICK_PERIOD_MS); + Serial.print("."); + } + } + + if ((millis() - scanCacheTimer) > 1200000) { // Clear the scan cache every 120 seconds + scanCacheTimer = millis(); + SS2K_LOG(BLE_CLIENT_LOG_TAG, "Clearing BLE Scan Cache."); + scan->clearDuplicateCache(); + scan->clearResults(); + } spinBLEClient.checkBLEReconnect(); - // if (spinBLEClient.doScan && (spinBLEClient.scanRetries > 0) && !NimBLEDevice::getScan()->isScanning()) { - // spinBLEClient.scanRetries--; - // SS2K_LOG(BLE_CLIENT_LOG_TAG, "Initiating Scan from Client Task:"); - // spinBLEClient.scanProcess(); - // } #ifdef DEBUG_STACK Serial.printf("BLEClient: %d \n", uxTaskGetStackHighWaterMark(BLEClientTask)); #endif // DEBUG_STACK @@ -75,7 +89,6 @@ void bleClientTask(void *pvParameters) { if (spinBLEClient.myBLEDevices[x].doConnect == true) { if (spinBLEClient.connectToServer()) { SS2K_LOG(BLE_CLIENT_LOG_TAG, "We are now connected to the BLE Server."); - // vTaskDelay(5000 / portTICK_PERIOD_MS); } else { } } @@ -182,7 +195,9 @@ bool SpinBLEClient::connectToServer() { spinBLEClient.resetDevices(pClient); pClient->deleteServices(); pClient->disconnect(); - NimBLEDevice::getScan()->erase(pClient->getPeerAddress()); + if (NimBLEDevice::getScan()) { + NimBLEDevice::getScan()->erase(pClient->getPeerAddress()); + } NimBLEDevice::deleteClient(pClient); } return false; @@ -447,7 +462,7 @@ void SpinBLEClient::scanProcess(int duration) { pBLEScan->setDuplicateFilter(true); pBLEScan->setActiveScan(true); BLEScanResults foundDevices = pBLEScan->start(duration, true); - this->dontBlockScan = false; + // this->dontBlockScan = false; // Load the scan into a Json String int count = foundDevices.getCount(); @@ -733,7 +748,7 @@ void SpinBLEClient::checkBLEReconnect() { if (scan) { if (!NimBLEDevice::getScan()->isScanning()) { spinBLEClient.scanProcess(BLE_RECONNECT_SCAN_DURATION); - //Serial.println("scan"); + // Serial.println("scan"); } } } diff --git a/src/BLE_Common.cpp b/src/BLE_Common.cpp index 1ea4e0e5..41ff2bdc 100644 --- a/src/BLE_Common.cpp +++ b/src/BLE_Common.cpp @@ -21,9 +21,9 @@ TaskHandle_t BLECommunicationTask; void BLECommunications(void *pvParameters) { for (;;) { - if (!spinBLEClient.dontBlockScan) { - NimBLEDevice::getScan()->stop(); // stop routine scans - } + // if (!spinBLEClient.dontBlockScan) { + // NimBLEDevice::getScan()->stop(); // stop routine scans + //} // **********************************Client*************************************** for (size_t x = 0; x < NUM_BLE_DEVICES; x++) { // loop through discovered devices if (spinBLEClient.myBLEDevices[x].connectedClientID != BLE_HS_CONN_HANDLE_NONE) { @@ -31,7 +31,7 @@ void BLECommunications(void *pvParameters) { spinBLEClient.myBLEDevices[x].peerAddress.toString().c_str(), spinBLEClient.myBLEDevices[x].connectedClientID, spinBLEClient.myBLEDevices[x].serviceUUID.toString().c_str(), spinBLEClient.myBLEDevices[x].charUUID.toString().c_str(), spinBLEClient.myBLEDevices[x].isHRM ? "true" : "false", spinBLEClient.myBLEDevices[x].isPM ? "true" : "false", - spinBLEClient.myBLEDevices[x].isCSC? "true" : "false", spinBLEClient.myBLEDevices[x].isCT ? "true" : "false", + spinBLEClient.myBLEDevices[x].isCSC ? "true" : "false", spinBLEClient.myBLEDevices[x].isCT ? "true" : "false", spinBLEClient.myBLEDevices[x].doConnect ? "true" : "false"); if (spinBLEClient.myBLEDevices[x].advertisedDevice) { // is device registered? SpinBLEAdvertisedDevice myAdvertisedDevice = spinBLEClient.myBLEDevices[x]; @@ -44,8 +44,8 @@ void BLECommunications(void *pvParameters) { // Handle BLE HID Remotes if (spinBLEClient.myBLEDevices[x].serviceUUID == HID_SERVICE_UUID) { - spinBLEClient.keepAliveBLE_HID(pClient); //keep alive doesn't seem to help :( - continue; // There is not data that needs to be dequeued for the remote, so got to the next device. + spinBLEClient.keepAliveBLE_HID(pClient); // keep alive doesn't seem to help :( + continue; // There is not data that needs to be dequeued for the remote, so got to the next device. } // Dequeue sensor data we stored during notifications @@ -65,7 +65,7 @@ void BLECommunications(void *pvParameters) { } spinBLEClient.handleBattInfo(pClient, false); - + } else if (!pClient->isConnected()) { // This shouldn't ever be // called... if (pClient->disconnect() == 0) { // 0 is a successful disconnect @@ -82,7 +82,8 @@ void BLECommunications(void *pvParameters) { } // ***********************************SERVER************************************** - if ((spinBLEClient.connectedHRM|| rtConfig.hr.getSimulate()) && !spinBLEClient.connectedPM && !rtConfig.watts.getSimulate() && (rtConfig.hr.getValue() > 0) && userPWC.hr2Pwr) { + if ((spinBLEClient.connectedHRM || rtConfig.hr.getSimulate()) && !spinBLEClient.connectedPM && !rtConfig.watts.getSimulate() && (rtConfig.hr.getValue() > 0) && + userPWC.hr2Pwr) { calculateInstPwrFromHR(); hr2p = true; } else { @@ -96,7 +97,7 @@ void BLECommunications(void *pvParameters) { rtConfig.cad.setValue(0); rtConfig.watts.setValue(0); } - if (!spinBLEClient.connectedHRM&& !rtConfig.hr.getSimulate()) { + if (!spinBLEClient.connectedHRM && !rtConfig.hr.getSimulate()) { rtConfig.hr.setValue(0); } @@ -132,6 +133,9 @@ void BLECommunications(void *pvParameters) { } else { digitalWrite(LED_PIN, HIGH); } + if (httpServer.isServing) { // slow the BLE communications for faster web tasks. + vTaskDelay((BLE_NOTIFY_DELAY) / portTICK_PERIOD_MS); + } vTaskDelay((BLE_NOTIFY_DELAY) / portTICK_PERIOD_MS); #ifdef DEBUG_STACK Serial.printf("BLEComm: %d \n", uxTaskGetStackHighWaterMark(BLECommunicationTask)); diff --git a/src/HTTP_Server_Basic.cpp b/src/HTTP_Server_Basic.cpp index 6f2ccb84..571fa3ef 100644 --- a/src/HTTP_Server_Basic.cpp +++ b/src/HTTP_Server_Basic.cpp @@ -34,6 +34,11 @@ HTTP_Server httpServer; WiFiClientSecure client; WebServer server(80); +// WiFi Scanner +int _numNetworks = 0; +int _minimumQuality = -1; +bool haveScan = false; + #ifdef USE_TELEGRAM #include TaskHandle_t telegramTask; @@ -148,9 +153,7 @@ void HTTP_Server::start() { String response = "Scanning for BLE Devices. Please wait " "15 seconds."; - // spinBLEClient.resetDevices(); - spinBLEClient.dontBlockScan = true; + myIP.toString() + "/bluetoothscanner.html';\",100);"; spinBLEClient.scanProcess(DEFAULT_SCAN_DURATION); server.send(200, "text/html", response); }); @@ -291,7 +294,17 @@ void HTTP_Server::start() { server.on("/login", HTTP_GET, []() { server.sendHeader("Connection", "close"); - server.send(200, "text/html", OTALoginIndex); + String page = FPSTR(OTALOGININDEX); + page += FPSTR(HTTP_STYLE); + server.send(200, "text/html", page); + }); + + server.on("/wifi", []() { + if (!server.arg("refresh").isEmpty()) { + WiFi_scanNetworks(); + } + server.sendHeader("Connection", "close"); + server.send(200, "text/html", httpServer.processWiFiHTML()); }); server.on("/OTAIndex", HTTP_GET, []() { @@ -384,7 +397,7 @@ void HTTP_Server::start() { "webClientUpdate", /* name of task. */ 6000 + (DEBUG_LOG_BUFFER_SIZE * 2), /* Stack size of task Used to be 3000*/ NULL, /* parameter of the task */ - 10, /* priority of the task */ + 4, /* priority of the task */ &webClientTask, /* Task handle to keep track of created task */ 0); /* pin task to core */ @@ -403,14 +416,30 @@ void HTTP_Server::start() { void HTTP_Server::webClientUpdate(void *pvParameters) { static unsigned long mDnsTimer = millis(); // NOLINT: There is no overload in String for uint64_t + bool recheck = false; for (;;) { server.handleClient(); - vTaskDelay(WEBSERVER_DELAY / portTICK_RATE_MS); - if (WiFi.getMode() == WIFI_AP) { + // vTaskDelay(WEBSERVER_DELAY / portTICK_RATE_MS); + if (WiFi.getMode() != WIFI_MODE_STA) { + if (WiFi.softAPgetStationNum()) { // Client Connected to AP + httpServer.isServing = true; + } dnsServer.processNextRequest(); + if (!haveScan) { + WiFi_scanNetworks(); + haveScan = true; + } } // Keep MDNS alive if ((millis() - mDnsTimer) > 30000) { + if (httpServer.isServing) { + if (recheck) { + httpServer.isServing = false; // reset BLE communications slowdown. + recheck = false; + } else { + recheck = true; // check again in 30 seconds. + } + } MDNS.addServiceTxt("http", "_tcp", "lf", String(mDnsTimer)); mDnsTimer = millis(); #ifdef DEBUG_STACK @@ -421,29 +450,265 @@ void HTTP_Server::webClientUpdate(void *pvParameters) { } void HTTP_Server::handleIndexFile() { - String filename = "/index.html"; + httpServer.isServing = true; + String filename = "/index.html"; + server.sendHeader("Connection", "close"); if (LittleFS.exists(filename)) { - File file = LittleFS.open(filename, FILE_READ); - server.streamFile(file, "text/html"); + File file = LittleFS.open(filename, FILE_READ); + String page = ""; + page += FPSTR(HTTP_HEAD_START); + page.replace(FPSTR(T_v), DEVICE_NAME); + page += FPSTR(HTTP_STYLE); + while (file.available()) { + page += char(file.read()); + } + server.send(200, "text/html", page); + // server.streamFile(file, "text/html"); file.close(); SS2K_LOG(HTTP_SERVER_LOG_TAG, "Served %s", filename.c_str()); } else { SS2K_LOG(HTTP_SERVER_LOG_TAG, "%s not found. Sending builtin Index.html", filename.c_str()); - server.send(200, "text/html", noIndexHTML); + server.send(200, "text/html", httpServer.processWiFiHTML()); + } +} + +String HTTP_Server::processWiFiHTML() { + String page = getHTTPHead(); // @token titlewifi + if (!haveScan) { + WiFi_scanNetworks(); // wifiscan, force if arg refresh + } + + page.replace(FPSTR(T_1), reinterpret_cast(WIFI_PAGE_TITLE)); + page += getScanItemOut(); + + String pitem = ""; + + pitem = FPSTR(HTTP_FORM_START); + pitem.replace(FPSTR(T_v), F("send_settings")); // set form action + page += pitem; + + pitem = FPSTR(HTTP_FORM_WIFI); + pitem.replace(FPSTR(T_v), reinterpret_cast(userConfig.getSsid())); + + pitem.replace(FPSTR(T_p), reinterpret_cast(DEFAULT_PASSWORD)); + + page += pitem; + + page += FPSTR(HTTP_BR); + page += FPSTR(HTTP_FORM_WIFI_END); + page += FPSTR(HTTP_FORM_END); + page += FPSTR(HTTP_SCAN_LINK); + if (!LittleFS.exists("/index.html")) { + page += FPSTR(NOINDEXHTML); } + page += FPSTR(HTTP_REBOOT_LINK); + + page += FPSTR(HTTP_END); + + return page; +} + +bool WiFi_scanNetworks() { + // if 0 networks, rescan @note this was a kludge, now disabling to test real cause ( maybe wifi not init etc) + // enable only if preload failed? + if (_numNetworks == 0) { + SS2K_LOG(HTTP_SERVER_LOG_TAG, "NO APs found forcing new scan"); + } + + int8_t res; + + SS2K_LOG(HTTP_SERVER_LOG_TAG, "WiFi Scan SYNC started"); + res = WiFi.scanNetworks(); + + if (res == WIFI_SCAN_FAILED) { + SS2K_LOG(HTTP_SERVER_LOG_TAG, "WiFi Scan failed"); + } else if (res == WIFI_SCAN_RUNNING) { + while (WiFi.scanComplete() == WIFI_SCAN_RUNNING) { + Serial.print("."); + delay(100); + } + _numNetworks = WiFi.scanComplete(); + } else if (res >= 0) { + _numNetworks = res; + haveScan = true; + } + + if (res > 0) { + // Serial.printf("Networks found: %d", _numNetworks); + return true; + } else { + return false; + } +} + +String getHTTPHead() { + String page; + page += FPSTR(HTTP_HEAD_START); + page.replace(FPSTR(T_v), DEVICE_NAME); + page += FPSTR(HTTP_SCRIPT); + page += FPSTR(HTTP_STYLE); + page += FPSTR(HTTP_TITLE); + page += FPSTR(HTTP_HEAD_END); + return page; +} + +String getScanItemOut() { + String page; + + if (!_numNetworks) WiFi_scanNetworks(); // scan in case this gets called before any scans + + int n = _numNetworks; + if (n == 0) { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(F("No networks found")); +#endif + page += FPSTR(S_nonetworks); // @token nonetworks + page += F("

"); + } else { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(n, F("networks found")); +#endif + // sort networks + int indices[n]; + for (int i = 0; i < n; i++) { + indices[i] = i; + } + + // RSSI SORT + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + if (WiFi.RSSI(indices[j]) > WiFi.RSSI(indices[i])) { + std::swap(indices[i], indices[j]); + } + } + } + + /* test std:sort + std::sort(indices, indices + n, [](const int & a, const int & b) -> bool + { + return WiFi.RSSI(a) > WiFi.RSSI(b); + }); + */ + + // remove duplicates ( must be RSSI sorted ) + String cssid; + for (int i = 0; i < n; i++) { + if (indices[i] == -1) continue; + cssid = WiFi.SSID(indices[i]); + for (int j = i + 1; j < n; j++) { + if (cssid == WiFi.SSID(indices[j])) { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(WM_DEBUG_VERBOSE, F("DUP AP:"), WiFi.SSID(indices[j])); +#endif + indices[j] = -1; // set dup aps to index -1 + } + } + } + + // token precheck, to speed up replacements on large ap lists + String HTTP_ITEM_STR = FPSTR(HTTP_ITEM); + + // toggle icons with percentage + HTTP_ITEM_STR.replace("{qp}", FPSTR(HTTP_ITEM_QP)); + HTTP_ITEM_STR.replace("{qi}", FPSTR(HTTP_ITEM_QI)); + + // set token precheck flags + bool tok_r = HTTP_ITEM_STR.indexOf(FPSTR(T_r)) > 0; + bool tok_R = HTTP_ITEM_STR.indexOf(FPSTR(T_R)) > 0; + bool tok_e = HTTP_ITEM_STR.indexOf(FPSTR(T_e)) > 0; + bool tok_q = HTTP_ITEM_STR.indexOf(FPSTR(T_q)) > 0; + bool tok_i = HTTP_ITEM_STR.indexOf(FPSTR(T_i)) > 0; + + // display networks in page + for (int i = 0; i < n; i++) { + if (indices[i] == -1) continue; // skip dups + +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(WM_DEBUG_VERBOSE, F("AP: "), (String)WiFi.RSSI(indices[i]) + " " + (String)WiFi.SSID(indices[i])); +#endif + + int rssiperc = getRSSIasQuality(WiFi.RSSI(indices[i])); + uint8_t enc_type = WiFi.encryptionType(indices[i]); + + if (_minimumQuality == -1 || _minimumQuality < rssiperc) { + String item = HTTP_ITEM_STR; + if (WiFi.SSID(indices[i]) == "") { + // Serial.println(WiFi.BSSIDstr(indices[i])); + continue; // No idea why I am seeing these, lets just skip them for now + } + item.replace(FPSTR(T_V), htmlEntities(WiFi.SSID(indices[i]))); // ssid no encoding + item.replace(FPSTR(T_v), htmlEntities(WiFi.SSID(indices[i]), true)); // ssid no encoding + if (tok_e) item.replace(FPSTR(T_e), AUTH_MODE_NAMES[enc_type]); + if (tok_r) item.replace(FPSTR(T_r), (String)rssiperc); // rssi percentage 0-100 + if (tok_R) item.replace(FPSTR(T_R), (String)WiFi.RSSI(indices[i])); // rssi db + if (tok_q) item.replace(FPSTR(T_q), (String) int(round(map(rssiperc, 0, 100, 1, 4)))); // quality icon 1-4 + if (tok_i) { + if (enc_type != WIFI_AUTH_OPEN) { + item.replace(FPSTR(T_i), F("l")); + } else { + item.replace(FPSTR(T_i), ""); + } + } + // Serial.println(WiFi.SSID(indices[i])); + page += item; + delay(0); + } else { +#ifdef WM_DEBUG_LEVEL + DEBUG_WM(WM_DEBUG_VERBOSE, F("Skipping , does not meet _minimumQuality")); +#endif + } + } + page += FPSTR(HTTP_BR); + } + + return page; +} + +int getRSSIasQuality(int RSSI) { + int quality = 0; + if (RSSI <= -100) { + quality = 0; + } else if (RSSI >= -50) { + quality = 100; + } else { + quality = 2 * (RSSI + 100); + } + Serial.println(quality); + return quality; +} + +String htmlEntities(String str, bool whitespace) { + str.replace("&", "&"); + str.replace("<", "<"); + str.replace(">", ">"); + str.replace("'", "'"); + if (whitespace) str.replace(" ", " "); + return str; } void HTTP_Server::handleLittleFSFile() { - String filename = server.uri(); - int dotPosition = filename.lastIndexOf("."); - String fileType = filename.substring((dotPosition + 1), filename.length()); + server.sendHeader("Connection", "close"); + httpServer.isServing = true; + String filename = server.uri(); + int dotPosition = filename.lastIndexOf("."); + String fileType = filename.substring((dotPosition + 1), filename.length()); if (LittleFS.exists(filename)) { File file = LittleFS.open(filename, FILE_READ); - if (fileType == "gz") { - fileType = "html"; // no need to change content type as it's done automatically by .streamfile below VV + + if (fileType == "ico") { // send favicon.ico + server.streamFile(file, "image/png"); + } else { + String page = ""; + page += FPSTR(HTTP_HEAD_START); + page.replace(FPSTR(T_v), DEVICE_NAME); + page += FPSTR(HTTP_STYLE); + while (file.available()) { + page += char(file.read()); + } + server.send(200, "text/html", page); } - server.streamFile(file, "text/" + fileType); file.close(); + SS2K_LOG(HTTP_SERVER_LOG_TAG, "Served %s", filename.c_str()); } else if (!LittleFS.exists("/index.html")) { SS2K_LOG(HTTP_SERVER_LOG_TAG, "%s not found and no filesystem. Sending builtin index.html", filename.c_str()); @@ -463,11 +728,17 @@ void HTTP_Server::settingsProcessor() { if (!server.arg("ssid").isEmpty()) { tString = server.arg("ssid"); tString.trim(); + if (tString != userConfig.getSsid()) { + reboot = true; + } userConfig.setSsid(tString); } if (!server.arg("password").isEmpty()) { tString = server.arg("password"); tString.trim(); + if (tString != userConfig.getPassword()) { + reboot = true; + } userConfig.setPassword(tString); } if (!server.arg("deviceName").isEmpty()) { @@ -746,21 +1017,21 @@ void HTTP_Server::FirmwareUpdate() { //////// Update Firmware ///////// SS2K_LOG(HTTP_SERVER_LOG_TAG, "Updating Firmware...Please Wait"); if (((availableVer > currentVer) || updateAnyway) && (userConfig.getAutoUpdate())) { - t_httpUpdate_return ret = httpUpdate.update(client, userConfig.getFirmwareUpdateURL() + String(FW_BINFILE)); - switch (ret) { - case HTTP_UPDATE_FAILED: - SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_FAILED Error %d : %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); - break; - - case HTTP_UPDATE_NO_UPDATES: - SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_NO_UPDATES"); - break; - - case HTTP_UPDATE_OK: - SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_OK"); - break; - } + t_httpUpdate_return ret = httpUpdate.update(client, userConfig.getFirmwareUpdateURL() + String(FW_BINFILE)); + switch (ret) { + case HTTP_UPDATE_FAILED: + SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_FAILED Error %d : %s", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str()); + break; + + case HTTP_UPDATE_NO_UPDATES: + SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_NO_UPDATES"); + break; + + case HTTP_UPDATE_OK: + SS2K_LOG(HTTP_SERVER_LOG_TAG, "HTTP_UPDATE_OK"); + break; } + } } else { // don't update SS2K_LOG(HTTP_SERVER_LOG_TAG, " - Current Version: %s", FIRMWARE_VERSION); } diff --git a/src/Main.cpp b/src/Main.cpp index 8c3268cb..dd0e4a90 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -190,7 +190,7 @@ void SS2K::maintenanceLoop(void *pvParameters) { static int loopCounter = 0; static unsigned long intervalTimer = millis(); static unsigned long intervalTimer2 = millis(); - static bool isScanning = false; + // static bool isScanning = false; while (true) { vTaskDelay(73 / portTICK_RATE_MS); @@ -208,18 +208,18 @@ void SS2K::maintenanceLoop(void *pvParameters) { intervalTimer = millis(); } - if ((millis() - intervalTimer2) > 6007) { - if (NimBLEDevice::getScan()->isScanning()) { // workaround to prevent occasional runaway scans - if (isScanning == true) { - SS2K_LOGW(MAIN_LOG_TAG, "Forcing Scan to stop."); - NimBLEDevice::getScan()->stop(); - isScanning = false; - } else { - isScanning = true; + /* if ((millis() - intervalTimer2) > 6007) { + if (NimBLEDevice::getScan()->isScanning()) { // workaround to prevent occasional runaway scans + if (isScanning == true) { + SS2K_LOGW(MAIN_LOG_TAG, "Forcing Scan to stop."); + NimBLEDevice::getScan()->stop(); + isScanning = false; + } else { + isScanning = true; + } } - } - intervalTimer2 = millis(); - } + intervalTimer2 = millis(); + }*/ if (loopCounter > 10) { ss2k.checkDriverTemperature(); // ss2k.checkBLEReconnect(); diff --git a/src/SensorCollector.cpp b/src/SensorCollector.cpp index 2e53b9e1..e03c82a8 100644 --- a/src/SensorCollector.cpp +++ b/src/SensorCollector.cpp @@ -28,7 +28,7 @@ void collectAndSet(NimBLEUUID charUUID, NimBLEUUID serviceUUID, NimBLEAddress ad if (sensorData->hasHeartRate() && !rtConfig.hr.getSimulate()) { int heartRate = sensorData->getHeartRate(); rtConfig.hr.setValue(heartRate); - spinBLEClient.connectedHRM|= true; + spinBLEClient.connectedHRM |= true; logBufLength += snprintf(logBuf + logBufLength, kLogBufMaxLength - logBufLength, " HR(%d)", heartRate % 1000); } if (sensorData->hasCadence() && !rtConfig.cad.getSimulate()) { @@ -66,11 +66,8 @@ void collectAndSet(NimBLEUUID charUUID, NimBLEUUID serviceUUID, NimBLEAddress ad } } strncat(logBuf + logBufLength, " ]", kLogBufMaxLength - logBufLength); - if (userConfig.getLogComm()) { - SS2K_LOG(BLE_COMMON_LOG_TAG, "%s", logBuf); - } else { - SS2K_LOG(BLE_COMMON_LOG_TAG, "rx %s", sensorData->getId().c_str()); - } + + SS2K_LOG(BLE_COMMON_LOG_TAG, "%s", logBuf); #ifdef USE_TELEGRAM SEND_TO_TELEGRAM(String(logBuf)); #endif diff --git a/src/WebsocketAppender.cpp b/src/WebsocketAppender.cpp index bf715e1d..06c05fee 100644 --- a/src/WebsocketAppender.cpp +++ b/src/WebsocketAppender.cpp @@ -31,6 +31,9 @@ void WebSocketAppender::Loop() { void WebSocketAppender::Log(const char* message) { // Serial.println("Log websocket."); // Serial.printf("%d clients connected.\n", GetClientsCount()); + if(!userConfig.getLogComm()){ + return; + } for (uint8_t index = 0; index < maxClients; index++) { WebsocketsClient* client = _clients[index]; From 435e3c3f14f6e8adaf90a19b26758cf51f425049 Mon Sep 17 00:00:00 2001 From: doudar Date: Tue, 7 Nov 2023 08:21:46 -0600 Subject: [PATCH 2/6] broke out style.css for testing --- data/style.css | 256 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 data/style.css diff --git a/data/style.css b/data/style.css new file mode 100644 index 00000000..57b1384f --- /dev/null +++ b/data/style.css @@ -0,0 +1,256 @@ + \ No newline at end of file From a8f167211071a6b2711c6c02661ef65c522f3675 Mon Sep 17 00:00:00 2001 From: doudar Date: Tue, 7 Nov 2023 08:24:37 -0600 Subject: [PATCH 3/6] cleaned up style.css for evaluation --- data/style.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/data/style.css b/data/style.css index 57b1384f..4d06c2b4 100644 --- a/data/style.css +++ b/data/style.css @@ -225,7 +225,7 @@ fieldset { font-size: 1em; border-radius: 10px; } -/ quality icon + .q{height:16px;margin:0;padding:0 5px;text-align:right;min-width:38px;float:right}.q.q-0:after{background-position-x:0}.q.q-1:after{background-position-x:-16px}.q.q-2:after{background-position-x:-32px}.q.q-3: after{background-position-x:-48px}.q.q-4:after{background-position-x:-64px}.q.l:before{background-position-x:-80px;padding-right:5px}.ql @@ -233,24 +233,24 @@ after{background-position-x:-48px}.q.q-4:after{background-position-x:-64px}.q.l: background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAAAQCAMAAADeZIrLAAAAJFBMVEX/// 8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHJj5lAAAAC3RSTlMAIjN3iJmqu8zd7vF8pzcAAABsSURBVHja7Y1BCsAwCASNSVo3/v+/ BUEiXnIoXkoX5jAQMxTHzK9cVSnvDxwD8bFx8PhZ9q8FmghXBhqA1faxk92PsxvRc2CCCFdhQCbRkLoAQ3q/wWUBqG35ZxtVzW4Ed6LngPyBU2CobdIDQ5oPWI5nCUwAAAAASUVORK5CYII=');} -/ icons @2x media query (32px rescaled + @media (-webkit-min-device-pixel-ratio: 2),(min-resolution: 192dpi){.q:before,.q:after { background-image:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALwAAAAgCAMAAACfM+KhAAAALVBMVEX/// 8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAOrOgAAAADnRSTlMAESIzRGZ3iJmqu8zd7gKjCLQAAACmSURBVHgB7dDBCoMwEEXRmKlVY3L//3NLhyzqIqSUggy8uxnhCR5Mo8xLt+14aZ7wwgsvvPA/ ofv9+44334UXXngvb6XsFhO/VoC2RsSv9J7x8BnYLW+AjT56ud/uePMdb7IP8Bsc/ e7h8Cfk912ghsNXWPpDC4hvN+D1560A1QPORyh84VKLjjdvfPFm++i9EWq0348XXnjhhT+4dIbCW+WjZim9AKk4UZMnnCEuAAAAAElFTkSuQmCC'); background-size: 95px 16px;}} -/ msg callout + .msg{padding:20px;margin:20px 0;border:1px solid #eee;border-left-width:5px;border-left-color:#777}.msg h4{margin-top:0;margin-bottom:5px}.msg.P{border-left-color:#1fa3ec}.msg.P h4{color:#1fa3ec}.msg.D{border-left-color:#dc3630}.msg.D h4{color:#dc3630}.msg.S{border-left-color: #5cb85c}.msg.S h4{color: #5cb85c} -/ list + dt{font-weight:bold}dd{margin:0;padding:0 0 0.5em 0;min-height:12px} td{vertical-align: top;} .h{display:none} button{transition: 0s opacity;transition-delay: 3s;transition-duration: 0s;cursor: pointer} button.D{background-color:#dc3630} button:active{opacity:50% !important;cursor:wait;transition-delay: 0s} -/ link + a{color:#000;font-weight:700;text-decoration:none}a:hover{color:#1fa3ec;text-decoration:underline} \ No newline at end of file From 4af19da08a176ccab07d6c6efd1c4da48420ad2e Mon Sep 17 00:00:00 2001 From: doudar Date: Tue, 7 Nov 2023 08:49:47 -0600 Subject: [PATCH 4/6] Added template html for testing --- data/HTML_testing_template.html | 465 ++++++++++++++++++++++++++++++++ data/style.css | 256 ------------------ 2 files changed, 465 insertions(+), 256 deletions(-) create mode 100644 data/HTML_testing_template.html delete mode 100644 data/style.css diff --git a/data/HTML_testing_template.html b/data/HTML_testing_template.html new file mode 100644 index 00000000..8c455c87 --- /dev/null +++ b/data/HTML_testing_template.html @@ -0,0 +1,465 @@ + + + + + + + SmartSpin2K + + + SmartSpin2K Web Server + + +
+ + http://github.com/doudar/SmartSpin2k + +

+ + Main Index + +

+

+ WiFi Setup +

+
+
+ NetwalkerLair + +
86%
+
+
+ danger55-plus + +
34%
+
+
+ Emily + +
18%
+
+
+
+ + +
+ + + + Show Password
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ + diff --git a/data/style.css b/data/style.css deleted file mode 100644 index 4d06c2b4..00000000 --- a/data/style.css +++ /dev/null @@ -1,256 +0,0 @@ - \ No newline at end of file From 3e8479981de6bd9085e2a46d84cf307fc98addf6 Mon Sep 17 00:00:00 2001 From: eMadman Date: Tue, 7 Nov 2023 22:20:08 -0500 Subject: [PATCH 5/6] html updates reformatted wifi list - this should be refactored because I did a lazy solution, creating a table for every line. Added line break for show password fixed text alignment of ssid/password form --- data/HTML_testing_template.html | 49 ++++++++++++++++++++++----------- include/Builtin_Pages.h | 16 +++++------ src/HTTP_Server_Basic.cpp | 3 +- 3 files changed, 43 insertions(+), 25 deletions(-) diff --git a/data/HTML_testing_template.html b/data/HTML_testing_template.html index 8c455c87..6cb26727 100644 --- a/data/HTML_testing_template.html +++ b/data/HTML_testing_template.html @@ -399,7 +399,7 @@ a { color: #000; font-weight: 700; - text-decoration: none + text-decoration: underline } a:hover { @@ -423,29 +423,46 @@

WiFi Setup

-
- NetwalkerLair - -
86%
-
-
+ + + + + + + + +
+ NetwalkerLair + +
86%
+
-
+
Emily
18%
-
+

- - -
- - - + + + + + + + + + +
+ +
+ + + +
+
Show Password


diff --git a/include/Builtin_Pages.h b/include/Builtin_Pages.h index 6d869950..a32d71f7 100644 --- a/include/Builtin_Pages.h +++ b/include/Builtin_Pages.h @@ -18,7 +18,7 @@ const char HTTP_STYLE[] = "label {font-size: medium;}" "div {font-size: medium;}" "a{color: #000000;text-decoration:underline}" - "a:visited {color: #000000;}" + "a:visited {color: #000000;text-decoration:underline}" "h1{color: #03245c;padding: 0.5rem;line-height: 1em;}" "h2{color: #000000;font-size: 1.5rem;font-weight: bold;}" "p{font-size: 1rem;}" @@ -199,7 +199,7 @@ const char HTTP_STYLE[] = "button.D{background-color:#dc3630}" "button:active{opacity:50% !important;cursor:wait;transition-delay: 0s}" // links - "a{color:#000;font-weight:700;text-decoration:none}a:hover{color:#1fa3ec;text-decoration:underline}" + "a{color:#000;font-weight:700;text-decoration:underline}a:hover{color:#1fa3ec;text-decoration:underline}" ""; /* Login page */ @@ -307,16 +307,16 @@ const char* const HTTP_PORTAL_MENU[] = { // const char HTTP_PORTAL_OPTIONS[] = strcat(HTTP_PORTAL_MENU[0] , HTTP_PORTAL_MENU[3] , HTTP_PORTAL_MENU[7]); const char HTTP_PORTAL_OPTIONS[] = ""; -const char HTTP_ITEM_QI[] = ""; // rssi icons -const char HTTP_ITEM_QP[] = "
{r}%
"; // rssi percentage {h} = hidden showperc pref -const char HTTP_ITEM[] = "
{v}{qi}{qp}
"; // {q} = HTTP_ITEM_QI, {r} = HTTP_ITEM_QP +const char HTTP_ITEM_QI[] = ""; // rssi icons +// const char HTTP_ITEM_QP[] = "
{r}%
"; // rssi percentage {h} = hidden showperc pref +const char HTTP_ITEM[] = "
{v}{qi}
"; // {q} = HTTP_ITEM_QI, {r} = HTTP_ITEM_QP // const char HTTP_ITEM[] = "
{v} {R} {r}% {q} {e}
"; // test all tokens const char HTTP_FORM_START[] = ""; const char HTTP_FORM_WIFI[] = - "
Show Password"; + "

Show Password"; const char HTTP_FORM_WIFI_END[] = ""; const char HTTP_FORM_STATIC_HEAD[] = "

"; const char HTTP_FORM_END[] = "

"; diff --git a/src/HTTP_Server_Basic.cpp b/src/HTTP_Server_Basic.cpp index 571fa3ef..884b1574 100644 --- a/src/HTTP_Server_Basic.cpp +++ b/src/HTTP_Server_Basic.cpp @@ -605,11 +605,12 @@ String getScanItemOut() { } } + // token precheck, to speed up replacements on large ap lists String HTTP_ITEM_STR = FPSTR(HTTP_ITEM); // toggle icons with percentage - HTTP_ITEM_STR.replace("{qp}", FPSTR(HTTP_ITEM_QP)); + // HTTP_ITEM_STR.replace("{qp}", FPSTR(HTTP_ITEM_QP)); HTTP_ITEM_STR.replace("{qi}", FPSTR(HTTP_ITEM_QI)); // set token precheck flags From 9f262896f80eccdb0021135aacfaa4c21bac324b Mon Sep 17 00:00:00 2001 From: doudar Date: Tue, 26 Dec 2023 13:36:16 -0600 Subject: [PATCH 6/6] Added popup notification if firmware is incompatible with webserver files. --- data/index.html | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/data/index.html b/data/index.html index 7d010d4b..9f94ba6a 100644 --- a/data/index.html +++ b/data/index.html @@ -1,24 +1,24 @@ -
http://github.com/doudar/SmartSpin2k -

SmartSpin2k

-

-

Web Shifter

-

Heartrate to Watts Setup

-

WiFi Setup

-

Bluetooth Setup

-

Advanced Settings

-

Developer Tools

-

SS2K Help

-

-
+
+ http://github.com/doudar/SmartSpin2k +

SmartSpin2k

+

+

Web Shifter

+

Heartrate to Watts Setup

+

WiFi Setup

+

Bluetooth Setup

+

Advanced Settings

+

Developer Tools

+

SS2K Help

+

+
-

+