Merge pull request #1212 from justcallmekoko/develop

Develop
This commit is contained in:
Just Call Me Koko
2026-04-15 09:37:20 -04:00
committed by GitHub
4 changed files with 480 additions and 442 deletions
+7 -32
View File
@@ -2256,8 +2256,6 @@ void WiFiScan::StartScan(uint8_t scan_mode, uint16_t color) {
#endif
RunProbeScan(scan_mode, color);
}
else if (scan_mode == WIFI_SCAN_STATION_WAR_DRIVE)
RunProbeScan(scan_mode, color);
else if (scan_mode == WIFI_SCAN_EVIL_PORTAL)
RunEvilPortal(scan_mode, color);
else if (scan_mode == WIFI_SCAN_EAPOL)
@@ -2554,7 +2552,6 @@ void WiFiScan::StopScan(uint8_t scan_mode) {
(currentScanMode == WIFI_SCAN_SAE_COMMIT) ||
(currentScanMode == WIFI_SCAN_AP) ||
(currentScanMode == WIFI_SCAN_WAR_DRIVE) ||
(currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE) ||
(currentScanMode == WIFI_SCAN_EVIL_PORTAL) ||
(currentScanMode == WIFI_SCAN_RAW_CAPTURE) ||
(currentScanMode == WIFI_SCAN_STATION) ||
@@ -2646,10 +2643,8 @@ void WiFiScan::StopScan(uint8_t scan_mode) {
}
// Close POI file if wardrive was active
if ((currentScanMode == WIFI_SCAN_WAR_DRIVE) ||
(currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE)) {
if (currentScanMode == WIFI_SCAN_WAR_DRIVE)
this->closePoiFile();
}
if ((currentScanMode == BT_SCAN_ALL) ||
@@ -5134,8 +5129,7 @@ void WiFiScan::RunBeaconScan(uint8_t scan_mode, uint16_t color) {
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
startLog(F("wardrive"));
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
buffer_obj.append(header_line);
buffer_obj.append(this->header_line);
this->openPoiFile();
} else {
return;
@@ -5320,19 +5314,6 @@ void WiFiScan::RunProbeScan(uint8_t scan_mode, uint16_t color) {
startPcap(F("flock"));
else if (scan_mode == WIFI_SCAN_DETECT_FOLLOW)
startPcap(F("mac_track"));
else if (scan_mode == WIFI_SCAN_STATION_WAR_DRIVE) {
#ifdef HAS_GPS
if (gps_obj.getGpsModuleStatus()) {
startLog(F("station_wardrive"));
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
buffer_obj.append(header_line);
} else {
return;
}
#else
return;
#endif
}
this->setLEDMode(MODE_SNIFF);
@@ -5344,8 +5325,6 @@ void WiFiScan::RunProbeScan(uint8_t scan_mode, uint16_t color) {
display_obj.tft.drawCentreString(text_table4[40],TFT_WIDTH / 2,16,2);
else if (scan_mode == WIFI_SCAN_DETECT_FOLLOW)
display_obj.tft.drawCentreString("MAC Monitor",TFT_WIDTH / 2,16,2);
else if (scan_mode == WIFI_SCAN_STATION_WAR_DRIVE)
display_obj.tft.drawCentreString("Station Wardrive",TFT_WIDTH / 2,16,2);
else {
display_obj.tft.drawCentreString("Flock Sniff",TFT_WIDTH / 2,16,2);
}
@@ -5356,7 +5335,6 @@ void WiFiScan::RunProbeScan(uint8_t scan_mode, uint16_t color) {
#endif
display_obj.tft.setTextColor(TFT_GREEN, TFT_BLACK);
if ((scan_mode != WIFI_SCAN_DETECT_FOLLOW) &&
(scan_mode != WIFI_SCAN_STATION_WAR_DRIVE) &&
(scan_mode != BT_SCAN_FLOCK)) {
display_obj.tftDrawChannelScaleButtons(set_channel, false);
display_obj.tftDrawExitScaleButtons(false);
@@ -5472,8 +5450,7 @@ void WiFiScan::RunBluetoothScan(uint8_t scan_mode, uint16_t color) {
this->startWardriverWiFi();
this->wifi_initialized = true;
}
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
buffer_obj.append(header_line);
buffer_obj.append(this->header_line);
}
#endif
@@ -6199,14 +6176,14 @@ String WiFiScan::processPwnagotchiBeacon(const uint8_t* frame, int length) {
bool deauth = doc["policy"]["deauth"];
int uptime = doc["uptime"];
Serial.print(F("Pwnagotchi Name: "));
Serial.print(F("Name: "));
Serial.println(name);
Serial.print(F("Pwnd Totals: "));
Serial.print(F("Pwnd #: "));
Serial.println(pwnd_tot);
#ifdef HAS_SCREEN
display_obj.display_buffer->add(String("Pwnagotchi: ") + name + ", ");
display_obj.display_buffer->add(String("Name: ") + name + ", ");
display_obj.display_buffer->add(" Pwnd: " + String(pwnd_tot) + ", ");
display_obj.display_buffer->add(" Uptime: " + String(uptime) + ", ");
if (deauth)
@@ -6217,7 +6194,7 @@ String WiFiScan::processPwnagotchiBeacon(const uint8_t* frame, int length) {
display_obj.display_buffer->add(String(" Ver: ") + ver + " ");
#endif
return String("Pwnagotchi: ") + name + ", \nPwnd: " + String(pwnd_tot) + ", \nVer: " + ver;
return String("Name: ") + name + ", \nPwnd: " + String(pwnd_tot) + ", \nVer: " + ver;
}
else
return "";
@@ -7135,7 +7112,6 @@ void WiFiScan::beaconSnifferCallback(void* buf, wifi_promiscuous_pkt_type_t type
String essid = "";
if ((wifi_scan_obj.currentScanMode == WIFI_SCAN_PROBE) ||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE) ||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_AP) ||
(wifi_scan_obj.currentScanMode == WIFI_SCAN_PWN)) {
if (type == WIFI_PKT_MGMT) {
@@ -9982,7 +9958,6 @@ void WiFiScan::main(uint32_t currentTime)
(currentScanMode == WIFI_SCAN_PINESCAN) ||
(currentScanMode == WIFI_SCAN_MULTISSID) ||
(currentScanMode == WIFI_SCAN_DEAUTH) ||
(currentScanMode == WIFI_SCAN_STATION_WAR_DRIVE) ||
(currentScanMode == WIFI_SCAN_ALL))
{
if (currentTime - initTime >= this->channel_hop_delay * HOP_DELAY) {
+2
View File
@@ -682,6 +682,8 @@ class WiFiScan
static MacEntry mac_entries[mac_history_len_half];
static uint8_t mac_entry_state[mac_history_len_half];
String header_line = "WigleWifi-1.4,appRelease=" + (String)MARAUDER_VERSION + ",model=ESP32 Marauder,release=" + (String)MARAUDER_VERSION + ",device=ESP32 Marauder,display=SPI TFT,board=ESP32 Marauder,brand=JustCallMeKoko\nMAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type\n";
uint8_t dual_band_channels[DUAL_BAND_CHANNELS] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 32, 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173, 177};
uint8_t dual_band_channel_index = 0;
+456 -410
View File
@@ -1,410 +1,456 @@
#include "settings.h"
String Settings::getSettingsString() {
return this->json_settings_string;
}
bool Settings::begin() {
if(!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)){
return false;
}
File settingsFile;
//SPIFFS.remove("/settings.json"); // NEED TO REMOVE THIS LINE
if (SPIFFS.exists("/settings.json")) {
settingsFile = SPIFFS.open("/settings.json", FILE_READ);
if (!settingsFile) {
settingsFile.close();
if (this->createDefaultSettings(SPIFFS))
return true;
else
return false;
}
}
else {
if (this->createDefaultSettings(SPIFFS))
return true;
else
return false;
}
String json_string;
DynamicJsonDocument jsonBuffer(JSON_SETTING_SIZE);
DeserializationError error = deserializeJson(jsonBuffer, settingsFile);
if (error) {
Serial.print("Could not parse json during setup: ");
Serial.println(error.f_str());
}
serializeJson(jsonBuffer, json_string);
this->json_settings_string = json_string;
return true;
}
template <typename T>
T Settings::loadSetting(String key) {}
// Get type int settings
template<>
int Settings::loadSetting<int>(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"];
}
return 0;
}
// Get type string settings
template<>
String Settings::loadSetting<String>(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"];
}
Serial.println("Did not find setting named " + (String)key + ". Creating...");
if (this->createDefaultSettings(SPIFFS, true, json["Settings"].size(), "String", key))
return "";
return "";
}
// Get type bool settings
template<>
bool Settings::loadSetting<bool>(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("Could not parse json to load");
}
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"];
}
Serial.println("Did not find setting named " + (String)key + ". Creating...");
if (this->createDefaultSettings(SPIFFS, true, json["Settings"].size(), "bool", key))
return true;
return false;
}
//Get type uint8_t settings
template<>
uint8_t Settings::loadSetting<uint8_t>(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"];
}
return 0;
}
template <typename T>
T Settings::saveSetting(String key, bool value) {}
template<>
bool Settings::saveSetting<bool>(String key, bool value) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
String settings_string;
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key) {
json["Settings"][i]["value"] = value;
Serial.println("Saving setting...");
File settingsFile = SPIFFS.open("/settings.json", FILE_WRITE);
if (!settingsFile) {
Serial.println(F("Failed to create settings file"));
return false;
}
if (serializeJson(json, settingsFile) == 0) {
Serial.println(F("Failed to write to file"));
}
if (serializeJson(json, settings_string) == 0) {
Serial.println(F("Failed to write to string"));
}
// Close the file
settingsFile.close();
this->json_settings_string = settings_string;
this->printJsonSettings(settings_string);
return true;
}
}
return false;
}
template <typename T>
T Settings::saveSetting(String key, String value) {}
template<>
bool Settings::saveSetting<bool>(String key, String value) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
String settings_string;
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key) {
json["Settings"][i]["value"] = value;
File settingsFile = SPIFFS.open("/settings.json", FILE_WRITE);
if (!settingsFile) {
return false;
}
if (serializeJson(json, settingsFile) == 0) {
Serial.println(F("Failed to write to file"));
}
if (serializeJson(json, settings_string) == 0) {
Serial.println(F("Failed to write to string"));
}
// Close the file
settingsFile.close();
this->json_settings_string = settings_string;
this->printJsonSettings(settings_string);
return true;
}
}
return false;
}
bool Settings::toggleSetting(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key) {
if (json["Settings"][i]["value"]) {
saveSetting<bool>(key, false);
return false;
}
else {
saveSetting<bool>(key, true);
return true;
}
return false;
}
}
}
String Settings::setting_index_to_name(int i) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
return json["Settings"][i]["name"];
}
int Settings::getNumberSettings() {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("\nCould not parse json");
}
return json["Settings"].size();
}
String Settings::getSettingType(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
DeserializationError error = deserializeJson(json, this->json_settings_string);
if (error) {
Serial.print("\nCould not parse json: ");
Serial.println(error.f_str());
}
for (int i = 0; i < json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["type"];
}
return "";
}
void Settings::printJsonSettings(String json_string) {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, json_string)) {
Serial.println("\nCould not parse json");
}
for (int i = 0; i < json["Settings"].size(); i++) {
Serial.println("Name: " + json["Settings"][i]["name"].as<String>());
Serial.println("Type: " + json["Settings"][i]["type"].as<String>());
Serial.println("Value: " + json["Settings"][i]["value"].as<String>() + "\n");
}
}
bool Settings::createDefaultSettings(fs::FS &fs, bool spec, uint8_t index, String typeStr, String name) {
Serial.println(F("Creating default settings file: settings.json"));
File settingsFile = fs.open("/settings.json", FILE_WRITE);
if (!settingsFile) {
Serial.println(F("Failed to create settings file"));
return false;
}
String settings_string;
if (!spec) {
DynamicJsonDocument jsonBuffer(JSON_SETTING_SIZE);
jsonBuffer["Settings"][0]["name"] = "ForcePMKID";
jsonBuffer["Settings"][0]["type"] = "bool";
jsonBuffer["Settings"][0]["value"] = false;
jsonBuffer["Settings"][0]["range"]["min"] = false;
jsonBuffer["Settings"][0]["range"]["max"] = true;
jsonBuffer["Settings"][1]["name"] = "ForceProbe";
jsonBuffer["Settings"][1]["type"] = "bool";
jsonBuffer["Settings"][1]["value"] = false;
jsonBuffer["Settings"][1]["range"]["min"] = false;
jsonBuffer["Settings"][1]["range"]["max"] = true;
jsonBuffer["Settings"][2]["name"] = "SavePCAP";
jsonBuffer["Settings"][2]["type"] = "bool";
jsonBuffer["Settings"][2]["value"] = true;
jsonBuffer["Settings"][2]["range"]["min"] = false;
jsonBuffer["Settings"][2]["range"]["max"] = true;
jsonBuffer["Settings"][3]["name"] = "EnableLED";
jsonBuffer["Settings"][3]["type"] = "bool";
jsonBuffer["Settings"][3]["value"] = true;
jsonBuffer["Settings"][3]["range"]["min"] = false;
jsonBuffer["Settings"][3]["range"]["max"] = true;
jsonBuffer["Settings"][4]["name"] = "EPDeauth";
jsonBuffer["Settings"][4]["type"] = "bool";
jsonBuffer["Settings"][4]["value"] = false;
jsonBuffer["Settings"][4]["range"]["min"] = false;
jsonBuffer["Settings"][4]["range"]["max"] = true;
jsonBuffer["Settings"][5]["name"] = "ChanHop";
jsonBuffer["Settings"][5]["type"] = "bool";
jsonBuffer["Settings"][5]["value"] = false;
jsonBuffer["Settings"][5]["range"]["min"] = false;
jsonBuffer["Settings"][5]["range"]["max"] = true;
jsonBuffer["Settings"][6]["name"] = "ClientSSID";
jsonBuffer["Settings"][6]["type"] = "String";
jsonBuffer["Settings"][6]["value"] = "";
jsonBuffer["Settings"][6]["range"]["min"] = "";
jsonBuffer["Settings"][6]["range"]["max"] = "";
jsonBuffer["Settings"][7]["name"] = "ClientPW";
jsonBuffer["Settings"][7]["type"] = "String";
jsonBuffer["Settings"][7]["value"] = "";
jsonBuffer["Settings"][7]["range"]["min"] = "";
jsonBuffer["Settings"][7]["range"]["max"] = "";
//jsonBuffer.printTo(settingsFile);
if (serializeJson(jsonBuffer, settingsFile) == 0) {
}
if (serializeJson(jsonBuffer, settings_string) == 0) {
}
}
else {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
Serial.println("Could not parse json to create new setting");
return false;
}
Serial.println("Creating " + typeStr + " setting...");
if (typeStr == "bool") {
json["Settings"][index]["name"] = name;
json["Settings"][index]["type"] = typeStr;
json["Settings"][index]["value"] = true;
json["Settings"][index]["range"]["min"] = false;
json["Settings"][index]["range"]["max"] = true;
if (serializeJson(json, settings_string) == 0) {
}
if (serializeJson(json, settingsFile) == 0) {
}
}
else if (typeStr == "String") {
json["Settings"][index]["name"] = name;
json["Settings"][index]["type"] = typeStr;
json["Settings"][index]["value"] = "";
json["Settings"][index]["range"]["min"] = "";
json["Settings"][index]["range"]["max"] = "";
if (serializeJson(json, settings_string) == 0) {
}
if (serializeJson(json, settingsFile) == 0) {
}
}
}
// Close the file
settingsFile.close();
this->json_settings_string = settings_string;
this->printJsonSettings(settings_string);
return true;
}
#include "settings.h"
// ---------------------------------------------------------------------------
// _buildCache — called once after json_settings_string is loaded/updated.
// Parses the JSON exactly once and fills every field of _cache.
// All loadSetting<T>() reads hit the cache; no heap is allocated on read.
// ---------------------------------------------------------------------------
void Settings::_buildCache() {
DynamicJsonDocument json(JSON_SETTING_SIZE);
if (deserializeJson(json, this->json_settings_string)) {
Serial.println(F("_buildCache: could not parse json"));
return;
}
for (int i = 0; i < (int)json["Settings"].size(); i++) {
String name = json["Settings"][i]["name"].as<String>();
if (name == "ForcePMKID")
_cache.ForcePMKID = json["Settings"][i]["value"].as<bool>();
else if (name == "ForceProbe")
_cache.ForceProbe = json["Settings"][i]["value"].as<bool>();
else if (name == "SavePCAP")
_cache.SavePCAP = json["Settings"][i]["value"].as<bool>();
else if (name == "EnableLED")
_cache.EnableLED = json["Settings"][i]["value"].as<bool>();
else if (name == "EPDeauth")
_cache.EPDeauth = json["Settings"][i]["value"].as<bool>();
else if (name == "ChanHop")
_cache.ChanHop = json["Settings"][i]["value"].as<bool>();
else if (name == "ClientSSID")
_cache.ClientSSID = json["Settings"][i]["value"].as<String>();
else if (name == "ClientPW")
_cache.ClientPW = json["Settings"][i]["value"].as<String>();
}
}
// ---------------------------------------------------------------------------
String Settings::getSettingsString() { return this->json_settings_string; }
bool Settings::begin() {
if (!SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED)) {
return false;
}
File settingsFile;
// SPIFFS.remove("/settings.json"); // NEED TO REMOVE THIS LINE
if (SPIFFS.exists("/settings.json")) {
settingsFile = SPIFFS.open("/settings.json", FILE_READ);
if (!settingsFile) {
settingsFile.close();
if (this->createDefaultSettings(SPIFFS))
return true;
else
return false;
}
} else {
if (this->createDefaultSettings(SPIFFS))
return true;
else
return false;
}
String json_string;
DynamicJsonDocument jsonBuffer(JSON_SETTING_SIZE);
DeserializationError error = deserializeJson(jsonBuffer, settingsFile);
if (error)
Serial.println(error.f_str());
serializeJson(jsonBuffer, json_string);
this->json_settings_string = json_string;
// Populate the flat cache from the freshly loaded JSON.
this->_buildCache();
return true;
}
// ---------------------------------------------------------------------------
// loadSetting<T> — O(1), zero heap, reads straight from the cache struct.
// ---------------------------------------------------------------------------
template <typename T> T Settings::loadSetting(String key) {}
// Get type int settings
template <> int Settings::loadSetting<int>(String key) {
// No int settings are defined currently; fall back to parsing if ever added.
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"];
}
return 0;
}
// Get type String settings — read from cache, no JSON parse
template <> String Settings::loadSetting<String>(String key) {
if (key == "ClientSSID")
return _cache.ClientSSID;
if (key == "ClientPW")
return _cache.ClientPW;
// Unknown String key: fall back to JSON so the setting can be auto-created.
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"].as<String>();
}
//Serial.println("Did not find setting named " + (String)key + ". Creating...");
if (this->createDefaultSettings(SPIFFS, true, json["Settings"].size(),
"String", key))
return "";
return "";
}
// Get type bool settings — read from cache, no JSON parse
template <> bool Settings::loadSetting<bool>(String key) {
if (key == "ForcePMKID")
return _cache.ForcePMKID;
if (key == "ForceProbe")
return _cache.ForceProbe;
if (key == "SavePCAP")
return _cache.SavePCAP;
if (key == "EnableLED")
return _cache.EnableLED;
if (key == "EPDeauth")
return _cache.EPDeauth;
if (key == "ChanHop")
return _cache.ChanHop;
// Unknown bool key: fall back to JSON so the setting can be auto-created.
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"].as<bool>();
}
//Serial.println("Did not find setting named " + (String)key + ". Creating...");
if (this->createDefaultSettings(SPIFFS, true, json["Settings"].size(), "bool",
key))
return true;
return false;
}
// Get type uint8_t settings — read from cache, no JSON parse
template <> uint8_t Settings::loadSetting<uint8_t>(String key) {
// uint8_t settings reuse bool cache fields where applicable.
if (key == "ForcePMKID")
return (uint8_t)_cache.ForcePMKID;
if (key == "ForceProbe")
return (uint8_t)_cache.ForceProbe;
if (key == "SavePCAP")
return (uint8_t)_cache.SavePCAP;
if (key == "EnableLED")
return (uint8_t)_cache.EnableLED;
if (key == "EPDeauth")
return (uint8_t)_cache.EPDeauth;
if (key == "ChanHop")
return (uint8_t)_cache.ChanHop;
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["value"].as<uint8_t>();
}
return 0;
}
// ---------------------------------------------------------------------------
// saveSetting — writes SPIFFS, updates json_settings_string, rebuilds cache.
// ---------------------------------------------------------------------------
template <typename T> T Settings::saveSetting(String key, bool value) {}
template <> bool Settings::saveSetting<bool>(String key, bool value) {
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
String settings_string;
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key) {
json["Settings"][i]["value"] = value;
File settingsFile = SPIFFS.open("/settings.json", FILE_WRITE);
if (!settingsFile) {
return false;
}
serializeJson(json, settingsFile);
serializeJson(json, settings_string);
settingsFile.close();
this->json_settings_string = settings_string;
// Keep the cache in sync — no re-parse needed, just update the field.
if (key == "ForcePMKID")
_cache.ForcePMKID = value;
else if (key == "ForceProbe")
_cache.ForceProbe = value;
else if (key == "SavePCAP")
_cache.SavePCAP = value;
else if (key == "EnableLED")
_cache.EnableLED = value;
else if (key == "EPDeauth")
_cache.EPDeauth = value;
else if (key == "ChanHop")
_cache.ChanHop = value;
this->printJsonSettings(settings_string);
return true;
}
}
return false;
}
template <typename T> T Settings::saveSetting(String key, String value) {}
template <> bool Settings::saveSetting<bool>(String key, String value) {
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
String settings_string;
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key) {
json["Settings"][i]["value"] = value;
//Serial.println("Saving setting...");
File settingsFile = SPIFFS.open("/settings.json", FILE_WRITE);
if (!settingsFile)
return false;
serializeJson(json, settingsFile);
serializeJson(json, settings_string);
settingsFile.close();
this->json_settings_string = settings_string;
// Keep the cache in sync for String fields.
if (key == "ClientSSID")
_cache.ClientSSID = value;
else if (key == "ClientPW")
_cache.ClientPW = value;
this->printJsonSettings(settings_string);
return true;
}
}
return false;
}
// ---------------------------------------------------------------------------
// toggleSetting — reads current bool value from cache (fast), then delegates
// to saveSetting which will update the cache again.
// ---------------------------------------------------------------------------
bool Settings::toggleSetting(String key) {
// Use the cached value to decide direction — avoids an extra JSON parse.
bool current = this->loadSetting<bool>(key);
if (current) {
saveSetting<bool>(key, false);
//Serial.println("Setting value to false");
return false;
} else {
saveSetting<bool>(key, true);
//Serial.println("Setting value to true");
return true;
}
}
// ---------------------------------------------------------------------------
// Rarely-called utility functions — keep JSON parsing, they are not hot paths.
// ---------------------------------------------------------------------------
String Settings::setting_index_to_name(int i) {
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
return json["Settings"][i]["name"];
}
int Settings::getNumberSettings() {
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
return json["Settings"].size();
}
String Settings::getSettingType(String key) {
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, this->json_settings_string);
for (int i = 0; i < (int)json["Settings"].size(); i++) {
if (json["Settings"][i]["name"].as<String>() == key)
return json["Settings"][i]["type"];
}
return "";
}
void Settings::printJsonSettings(String json_string) {
DynamicJsonDocument json(JSON_SETTING_SIZE);
deserializeJson(json, json_string);
Serial.println("Settings\n----------------------------------------------");
for (int i = 0; i < (int)json["Settings"].size(); i++) {
Serial.println("Name: " + json["Settings"][i]["name"].as<String>());
Serial.println("Type: " + json["Settings"][i]["type"].as<String>());
Serial.println("Value: " + json["Settings"][i]["value"].as<String>() + "\n");
}
}
// ---------------------------------------------------------------------------
// createDefaultSettings — sets json_settings_string then rebuilds the cache.
// ---------------------------------------------------------------------------
bool Settings::createDefaultSettings(fs::FS &fs, bool spec, uint8_t index, String typeStr, String name) {
File settingsFile = fs.open("/settings.json", FILE_WRITE);
if (!settingsFile) {
//Serial.println(F("Failed to create settings file"));
return false;
}
String settings_string;
if (!spec) {
DynamicJsonDocument jsonBuffer(JSON_SETTING_SIZE);
jsonBuffer["Settings"][0]["name"] = "ForcePMKID";
jsonBuffer["Settings"][0]["type"] = "bool";
jsonBuffer["Settings"][0]["value"] = false;
jsonBuffer["Settings"][0]["range"]["min"] = false;
jsonBuffer["Settings"][0]["range"]["max"] = true;
jsonBuffer["Settings"][1]["name"] = "ForceProbe";
jsonBuffer["Settings"][1]["type"] = "bool";
jsonBuffer["Settings"][1]["value"] = false;
jsonBuffer["Settings"][1]["range"]["min"] = false;
jsonBuffer["Settings"][1]["range"]["max"] = true;
jsonBuffer["Settings"][2]["name"] = "SavePCAP";
jsonBuffer["Settings"][2]["type"] = "bool";
jsonBuffer["Settings"][2]["value"] = true;
jsonBuffer["Settings"][2]["range"]["min"] = false;
jsonBuffer["Settings"][2]["range"]["max"] = true;
jsonBuffer["Settings"][3]["name"] = "EnableLED";
jsonBuffer["Settings"][3]["type"] = "bool";
jsonBuffer["Settings"][3]["value"] = true;
jsonBuffer["Settings"][3]["range"]["min"] = false;
jsonBuffer["Settings"][3]["range"]["max"] = true;
jsonBuffer["Settings"][4]["name"] = "EPDeauth";
jsonBuffer["Settings"][4]["type"] = "bool";
jsonBuffer["Settings"][4]["value"] = false;
jsonBuffer["Settings"][4]["range"]["min"] = false;
jsonBuffer["Settings"][4]["range"]["max"] = true;
jsonBuffer["Settings"][5]["name"] = "ChanHop";
jsonBuffer["Settings"][5]["type"] = "bool";
jsonBuffer["Settings"][5]["value"] = false;
jsonBuffer["Settings"][5]["range"]["min"] = false;
jsonBuffer["Settings"][5]["range"]["max"] = true;
jsonBuffer["Settings"][6]["name"] = "ClientSSID";
jsonBuffer["Settings"][6]["type"] = "String";
jsonBuffer["Settings"][6]["value"] = "";
jsonBuffer["Settings"][6]["range"]["min"] = "";
jsonBuffer["Settings"][6]["range"]["max"] = "";
jsonBuffer["Settings"][7]["name"] = "ClientPW";
jsonBuffer["Settings"][7]["type"] = "String";
jsonBuffer["Settings"][7]["value"] = "";
jsonBuffer["Settings"][7]["range"]["min"] = "";
jsonBuffer["Settings"][7]["range"]["max"] = "";
//jsonBuffer.printTo(settingsFile);
if (serializeJson(jsonBuffer, settingsFile) == 0) {
}
if (serializeJson(jsonBuffer, settings_string) == 0) {
}
}
else {
DynamicJsonDocument json(JSON_SETTING_SIZE); // ArduinoJson v6
if (deserializeJson(json, this->json_settings_string)) {
return false;
}
if (typeStr == "bool") {
json["Settings"][index]["name"] = name;
json["Settings"][index]["type"] = typeStr;
json["Settings"][index]["value"] = true;
json["Settings"][index]["range"]["min"] = false;
json["Settings"][index]["range"]["max"] = true;
if (serializeJson(json, settings_string) == 0) {
}
if (serializeJson(json, settingsFile) == 0) {
}
}
else if (typeStr == "String") {
json["Settings"][index]["name"] = name;
json["Settings"][index]["type"] = typeStr;
json["Settings"][index]["value"] = "";
json["Settings"][index]["range"]["min"] = "";
json["Settings"][index]["range"]["max"] = "";
if (serializeJson(json, settings_string) == 0) {
}
if (serializeJson(json, settingsFile) == 0) {
}
}
}
// Close the file
settingsFile.close();
this->json_settings_string = settings_string;
// Rebuild cache from the newly written settings.
this->_buildCache();
this->printJsonSettings(settings_string);
return true;
}
+15
View File
@@ -22,6 +22,21 @@ class Settings {
private:
String json_settings_string;
// Flat cache populated once at begin() and kept in sync by saveSetting().
// All loadSetting<T>() reads hit this struct — zero heap, zero JSON parse.
struct SettingsCache {
bool ForcePMKID = false;
bool ForceProbe = false;
bool SavePCAP = true;
bool EnableLED = true;
bool EPDeauth = false;
bool ChanHop = false;
String ClientSSID = "";
String ClientPW = "";
} _cache;
void _buildCache(); // parse json_settings_string -> _cache
public:
bool begin();