Updates #1

Merged
dan merged 2 commits from updates into main 2026-07-19 02:04:33 +00:00
5 changed files with 263 additions and 24 deletions
Showing only changes of commit 6167432e1b - Show all commits
+9 -7
View File
@@ -4,11 +4,12 @@
// Flag to signal that a new reading is ready to be processed
volatile bool newReadingAvailable = false;
volatile int lastReadFPS = 0;
static NimBLEScan* pScan;
static NimBLEClient* _pClient = nullptr;
static ChronoBLE::ConnectionState currentState;
static const NimBLEAdvertisedDevice* advDevice = nullptr;
static uint32_t scanTimeMs = 5000;
static uint32_t scanTimeMs = 1000; //5000
static uint32_t scanTimeoutMs = 1000;
static NimBLEUUID deviceUUID("0000180a-0000-1000-8000-00805f9b34fb");
static NimBLEUUID serviceUUID("00001623-88EC-688C-644B-3FA706C0BB75");
static NimBLEUUID speedCharacteristicUUID("00001624-88EC-688C-644B-3FA706C0BB75");
@@ -58,7 +59,8 @@ class ScanCallbacks : public NimBLEScanCallbacks {
/** Callback to process the results of the completed scan or restart it */
void onScanEnd(const NimBLEScanResults& results, int reason) override {
//Serial.printf("Scan Ended, reason: %d, device count: %d; Restarting scan\n", reason, results.getCount());
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
delay(scanTimeoutMs);
pScan->start(scanTimeMs, false, false);
}
} scanCallbacks;
@@ -94,11 +96,11 @@ ChronoBLE::ChronoBLE() {
void ChronoBLE::init() {
NimBLEDevice::init("FX-Chrono-Client");
NimBLEDevice::setPower(3); /** 3dbm */
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan = NimBLEDevice::getScan();
pScan->setScanCallbacks(&scanCallbacks, false);
pScan->setInterval(100);
pScan->setWindow(100);
pScan->setActiveScan(true);
pScan->setWindow(99);
pScan->setActiveScan(false);
}
void ChronoBLE::_setState(ConnectionState state){
currentState=state;
@@ -122,7 +124,7 @@ bool ChronoBLE::foundDevice(){if (advDevice!=nullptr){return true;}return false;
void ChronoBLE::startScan(){
Serial.println("starting scan!!!");
_setState(SCANNING);
NimBLEDevice::getScan()->start(scanTimeMs, false, true);
pScan->start(scanTimeMs, false, false);
}
void ChronoBLE::_disconnectDevice(){
_setState(IDLE);
Binary file not shown.
Binary file not shown.
+249 -17
View File
@@ -7,9 +7,37 @@
#include <GunProfile.h>
#include <Update.h>
#include "Display.h"
#include <nvs_flash.h>
static const char *SSID_KEY = "wifi_ssid";
static const char *PASSWORD_KEY = "wifi_password";
static Preferences preferences;
String getWifiSsid() {
String ssid = "";
if (preferences.begin("wifi", true)) {
ssid = preferences.getString(SSID_KEY, "");
preferences.end();
}
return ssid;
}
String getWifiPassword() {
String password = "";
if (preferences.begin("wifi", true)) {
password = preferences.getString(PASSWORD_KEY, "");
preferences.end();
}
return password;
}
void saveWifiCredentials(const String& ssid, const String& password) {
preferences.begin("wifi", false);
preferences.putString(SSID_KEY, ssid);
preferences.putString(PASSWORD_KEY, password);
preferences.end();
}
const char *ssid = "routeguy";
const char *password = "wrangleR621!";
WebServer _server(80);
void (*testModeCallback)();
@@ -132,15 +160,81 @@ void ChronoWebServer::_handleRoot() {
});\
});\
});\
function saveWifi(){\
const ssid = document.getElementById('wifiSsidInput').value;\
const password = document.getElementById('wifiPasswordInput').value;\
if (!ssid) {\
alert('Please enter a WiFi SSID');\
return;\
}\
const formData = new URLSearchParams();\
formData.append('ssid', ssid);\
formData.append('password', password);\
fetch('/wifisave', {\
method: 'POST',\
body: formData\
})\
.then(response => response.text())\
.then(data => {\
console.log(data);\
alert('WiFi settings saved! Device will restart to connect.');\
window.location.href = '/';\
})\
.catch(error => console.error('Error:', error));\
}\
function clearWifi(){\
if(confirm('Clear saved WiFi credentials? Device will start its own AP after reboot.')){\
fetch('/clearwifi', {\
method: 'POST',\
body: new URLSearchParams()\
})\
.then(response => response.text())\
.then(data => {\
console.log(data);\
alert('WiFi credentials cleared. Device will restart.');\
})\
.catch(error => console.error('Error:', error));\
}\
}\
fetch('/version').then(r=>r.json()).then(v=>{document.getElementById('currentVersion').textContent=v.version}).catch(()=>{});\
fetch('/wifi').then(r=>r.text()).then(ssid=>{document.getElementById('wifiSsid').textContent=ssid || 'Not configured'}).catch(()=>{});\
fetch('/wifimode').then(r=>r.text()).then(mode=>{\
const modeEl = document.getElementById('wifiMode');\
if (mode === 'AP') {\
modeEl.textContent = 'Access Point (AP) Mode';\
document.getElementById('wifiFormContainer').style.display = 'block';\
} else if (mode === 'STA') {\
modeEl.textContent = 'Station (STA) Mode - Connected to WiFi';\
document.getElementById('wifiFormContainer').style.display = 'block';\
} else {\
modeEl.textContent = 'Unknown Mode';\
}\
}).catch(()=>{});\
</script>\
<h1>ChronoDisplay Settings</h1>\
<div style='margin-top:50px; position:relative;'>\
<h3>Control Panel</h3>\
<button onclick='exportData()'>Export</button>\
<button onclick='reset()'>Reset</button>\
<button onclick='format()'>Format</button>\
<button onclick='exportData()'>Export Data</button>\
<button onclick='reset()'>Reset Data</button>\
<button onclick='format()'>Delete Profiles</button>\
<button onclick='toggleTestMode()'>Toggle Test Mode</button>\
</div>\
<div style='margin-top:20px; padding: 10px; border: 1px solid #999;'>\
<h3>WiFi Configuration</h3>\
<p style='font-size: 12px; color: #666;'>Mode: <strong id='wifiMode'>Unknown</strong></p>\
<p style='font-size: 12px; color: #666;'>Current WiFi: <strong id='wifiSsid'>Not configured</strong></p>\
<div id='wifiFormContainer' style='margin-top: 10px;'>\
<form id='wifiForm'>\
<label for='wifiSsidInput'>SSID:</label><br/>\
<input type='text' id='wifiSsidInput' style='width: 200px; margin-bottom: 10px;'></input><br/>\
<label for='wifiPasswordInput'>Password:</label><br/>\
<input type='password' id='wifiPasswordInput' style='width: 200px; margin-bottom: 10px;'></input><br/>\
<button type='button' onclick='saveWifi()' style='margin-top: 5px; padding: 8px 16px; background-color: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;'>Save WiFi</button>\
</form>\
<p style='font-size: 12px; color: #666;'>After saving, the device will attempt to connect to the new WiFi network.</p>\
<button type='button' onclick='clearWifi()' style='margin-top: 10px; padding: 8px 16px; background-color: #f44336; color: white; border: none; border-radius: 4px; cursor: pointer;'>Clear WiFi Credentials</button>\
</div>\
</div>\
<div style='margin-top:20px; padding: 10px; border: 1px solid #999;'>\
<h3>OTA Firmware Update</h3>\
<p style='font-size: 12px; color: #666;'>Current Version: <strong id='currentVersion'>Unknown</strong></p>\
@@ -508,21 +602,107 @@ void handleOtaUpdateEnd() {
ESP.restart();
}
}
void ChronoWebServer::init(){
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
void setupSoftAp(const char* ssid, const char* password) {
WiFi.mode(WIFI_AP);
IPAddress local_ip(192, 168, 4, 1);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
WiFi.softAPConfig(local_ip, gateway, subnet);
delay(100);
WiFi.softAP(ssid, password);//, password);
delay(100);
Serial.print("AP started: ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.println(WiFi.softAPIP());
}
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
bool attemptStaConnection(const String& ssid, const String& password, int timeoutMs) {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid.c_str(), password.c_str());
int attempts = 0;
int maxAttempts = timeoutMs / 500;
while (WiFi.status() != WL_CONNECTED && attempts < maxAttempts) {
delay(500);
Serial.print(".");
attempts++;
}
return WiFi.status() == WL_CONNECTED;
}
void ChronoWebServer::init(){
// Initialize NVS flash and erase WiFi config partition
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
Serial.println("NVS partition was truncated, erasing...");
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
// Erase WiFi storage in NVS
nvs_handle_t handle;
err = nvs_open("storage", NVS_READWRITE, &handle);
if (err == ESP_OK) {
nvs_erase_all(handle);
nvs_commit(handle);
nvs_close(handle);
Serial.println("WiFi NVS storage erased");
} else {
Serial.println("No WiFi NVS storage to erase");
}
WiFi.disconnect(true, true);
WiFi.mode(WIFI_OFF);
delay(100);
// Try to load saved WiFi credentials
String savedSsid = getWifiSsid();
String savedPassword = getWifiPassword();
Serial.print("Saved SSID from Preferences: '");
Serial.print(savedSsid);
Serial.println("'");
Serial.print("Saved Password length: ");
Serial.println(savedPassword.length());
bool connected = false;
if (savedSsid.length() > 0) {
Serial.print("Attempting to connect to saved WiFi: ");
Serial.println(savedSsid);
connected = attemptStaConnection(savedSsid, savedPassword, 7000);
if (connected) {
Serial.println("");
Serial.print("Connected to ");
Serial.println(savedSsid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
} else {
Serial.println("");
Serial.println("Failed to connect to saved WiFi - starting AP mode");
WiFi.disconnect(true, true);
WiFi.mode(WIFI_OFF);
delay(100);
}
} else {
Serial.println("No saved WiFi credentials - starting AP mode");
}
if (!connected) {
setupSoftAp("ChronoDisplay", "12345678");
if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}
}
_server.on("/gunprofile/add", _handleAddGunProfile);
@@ -538,9 +718,14 @@ void ChronoWebServer::init(){
_server.on("/format", _handleFormat);
_server.on("/testmode", _handleTestMode);
_server.on("/ota", HTTP_POST, [](){handleOtaUpdateEnd();}, [](){handleOtaUpdate();});
_server.on("/wifi", HTTP_GET, _handleWifi);
_server.on("/wifisave", HTTP_POST, _handleWifiSave);
_server.on("/clearwifi", HTTP_POST, _handleClearWifi);
_server.on("/wifimode", HTTP_GET, _handleWifiMode);
_server.on("/version", HTTP_GET, _handleVersion);
_server.on("/", _handleRoot);
_server.begin();
}
void ChronoWebServer::process(){
@@ -551,3 +736,50 @@ void ChronoWebServer::_handleVersion() {
String versionJson = String("{\"version\":\"") + CHRONO_VERSION + "\",\"major\":" + String(CHRONO_VERSION_MAJOR) + ",\"minor\":" + String(CHRONO_VERSION_MINOR) + ",\"patch\":" + String(CHRONO_VERSION_PATCH) + "}";
_server.send(200, "application/json", versionJson);
}
void ChronoWebServer::_handleWifi() {
String ssid = getWifiSsid();
_server.send(200, "text/plain", ssid);
}
void ChronoWebServer::_handleWifiSave() {
if (_server.method() == HTTP_POST) {
String ssid = "";
String password = "";
if (_server.hasArg("ssid")) {
ssid = _server.arg("ssid");
}
if (_server.hasArg("password")) {
password = _server.arg("password");
}
saveWifiCredentials(ssid, password);
_server.send(200, "text/html", "ok");
delay(1000);
ESP.restart();
}
}
void ChronoWebServer::_handleClearWifi() {
if (_server.method() == HTTP_POST) {
preferences.begin("wifi", false);
preferences.clear();
preferences.end();
_server.send(200, "text/html", "ok");
delay(1000);
ESP.restart();
}
}
void ChronoWebServer::_handleWifiMode() {
String mode;
if (WiFi.getMode() == WIFI_STA) {
mode = "STA";
} else if (WiFi.getMode() == WIFI_AP) {
mode = "AP";
} else if (WiFi.getMode() == WIFI_AP_STA) {
mode = "AP+STA";
} else {
mode = "NONE";
}
_server.send(200, "text/plain", mode);
}
+5
View File
@@ -6,6 +6,7 @@
#include <NetworkClient.h>
#include <ESPmDNS.h>
#include <Update.h>
#include <Preferences.h>
#include <ChronoBLE.h>
#include <GunProfile.h>
#include "ChronoVersion.h"
@@ -36,6 +37,10 @@ class ChronoWebServer {
static void _handleTestMode();
static void _handleOta();
static void _handleVersion();
static void _handleWifi();
static void _handleWifiSave();
static void _handleClearWifi();
static void _handleWifiMode();
};