♻️ Handle merging

This commit is contained in:
Rune Harlyk
2026-01-31 16:11:20 +01:00
committed by Rune Harlyk
parent aca8ee6de5
commit bd984309f1
19 changed files with 224 additions and 391 deletions
-1
View File
@@ -1,7 +1,6 @@
#pragma once
#include <wifi/wifi_idf.h>
#include <ArduinoJson.h>
#include <esp_http_server.h>
#include "platform_shared/message.pb.h"
+10 -23
View File
@@ -1,7 +1,6 @@
#pragma once
#include <esp_http_server.h>
#include <ArduinoJson.h>
#include <esp_littlefs.h>
#include <esp_vfs.h>
#include <dirent.h>
@@ -12,40 +11,28 @@
#define MOUNT_POINT "/littlefs"
#define FS_CONFIG_DIRECTORY "/config"
#define DEVICE_CONFIG_FILE "/config/peripheral.json"
#define CAMERA_SETTINGS_FILE "/config/cameraSettings.pb"
#define AP_SETTINGS_FILE "/config/apSettings.pb"
#define MDNS_SETTINGS_FILE "/config/mdnsSettings.pb"
#define WIFI_SETTINGS_FILE "/config/wifiSettings.pb"
#define PERIPHERAL_SETTINGS_FILE "/config/peripheralSettings.pb"
#define SERVO_SETTINGS_FILE "/config/servoSettings.pb"
namespace FileSystem {
void listFilesProto(const std::string &directory, api_FileEntry *entry);
std::string listFiles(const std::string &directory, bool isRoot = true);
bool deleteFile(const char *filename);
bool editFile(const char *filename, const uint8_t *content, size_t size);
#define AP_SETTINGS_FILE MOUNT_POINT "/config/apSettings.json"
#define CAMERA_SETTINGS_FILE MOUNT_POINT "/config/cameraSettings.json"
#define FS_CONFIG_DIRECTORY MOUNT_POINT "/config"
#define DEVICE_CONFIG_FILE MOUNT_POINT "/config/peripheral.json"
#define WIFI_SETTINGS_FILE MOUNT_POINT "/config/wifiSettings.json"
#define SERVO_SETTINGS_FILE MOUNT_POINT "/config/servoSettings.json"
#define MDNS_SETTINGS_FILE MOUNT_POINT "/config/mdnsSettings.json"
#define DEVICE_CONFIG_FILE MOUNT_POINT "/config/peripheral.pb"
#define CAMERA_SETTINGS_FILE MOUNT_POINT "/config/cameraSettings.pb"
#define AP_SETTINGS_FILE MOUNT_POINT "/config/apSettings.pb"
#define MDNS_SETTINGS_FILE MOUNT_POINT "/config/mdnsSettings.pb"
#define WIFI_SETTINGS_FILE MOUNT_POINT "/config/wifiSettings.pb"
#define PERIPHERAL_SETTINGS_FILE MOUNT_POINT "/config/peripheralSettings.pb"
#define SERVO_SETTINGS_FILE MOUNT_POINT "/config/servoSettings.pb"
namespace FileSystem {
bool init();
void listFilesProto(const std::string &directory, api_FileEntry *entry);
std::string listFiles(const std::string &directory, bool isRoot = true);
bool deleteFile(const char *filename);
bool editFile(const char *filename, const uint8_t *content, size_t size);
bool editFile(const char *filename, const char *content);
bool fileExists(const char *filename);
std::string readFile(const char *filename);
bool writeFile(const char *filename, const char *content);
bool writeFile(const char *filename, const uint8_t *content, size_t size);
bool mkdirRecursive(const char *path);
esp_err_t getFilesProto(httpd_req_t *request);
+9 -10
View File
@@ -1,7 +1,6 @@
#pragma once
#include <esp_http_server.h>
#include <ArduinoJson.h>
#include <mdns.h>
#include <template/stateful_service.h>
#include <template/stateful_proto_endpoint.h>
@@ -10,15 +9,6 @@
#include <utils/timing.h>
class MDNSService : public StatefulService<MDNSSettings> {
private:
FSPersistencePB<MDNSSettings> _persistence;
bool _started {false};
void reconfigureMDNS();
void startMDNS();
void stopMDNS();
void addServices();
public:
MDNSService();
~MDNSService();
@@ -29,4 +19,13 @@ class MDNSService : public StatefulService<MDNSSettings> {
esp_err_t queryServices(httpd_req_t *request, api_Request *protoReq);
StatefulProtoEndpoint<MDNSSettings, api_MDNSSettings> protoEndpoint;
private:
FSPersistencePB<MDNSSettings> _persistence;
bool _started {false};
void reconfigureMDNS();
void startMDNS();
void stopMDNS();
void addServices();
};
+2 -5
View File
@@ -2,7 +2,6 @@
#include <wifi/wifi_idf.h>
#include <wifi/dns_server.h>
#include <ArduinoJson.h>
#include <template/state_result.h>
#include <platform_shared/api.pb.h>
#include <cstring>
@@ -77,11 +76,9 @@ inline APSettings APSettings_defaults() {
return settings;
}
inline void APSettings_read(const APSettings &settings, APSettings &proto) {
proto = settings;
}
inline void APSettings_read(const APSettings &settings, APSettings &proto) { proto = settings; }
inline StateUpdateResult APSettings_update(const APSettings &proto, APSettings &settings) {
settings = proto;
return StateUpdateResult::CHANGED;
}
}
@@ -7,10 +7,10 @@
* I2C software connection
*/
#ifndef SDA_PIN
#define SDA_PIN SDA
#define SDA_PIN 21
#endif
#ifndef SCL_PIN
#define SCL_PIN SCL
#define SCL_PIN 22
#endif
#ifndef I2C_FREQUENCY
#define I2C_FREQUENCY 1000000UL
@@ -35,7 +35,8 @@ inline void PeripheralsConfiguration_read(const PeripheralsConfiguration& settin
proto = settings;
}
inline StateUpdateResult PeripheralsConfiguration_update(const PeripheralsConfiguration& proto, PeripheralsConfiguration& settings) {
inline StateUpdateResult PeripheralsConfiguration_update(const PeripheralsConfiguration& proto,
PeripheralsConfiguration& settings) {
settings = proto;
return StateUpdateResult::CHANGED;
}
+1 -4
View File
@@ -1,7 +1,6 @@
#pragma once
#include <wifi/wifi_idf.h>
#include <ArduinoJson.h>
#include <template/state_result.h>
#include <platform_shared/api.pb.h>
#include <cstring>
@@ -50,9 +49,7 @@ inline WiFiSettings WiFiSettings_defaults() {
return settings;
}
inline void WiFiSettings_read(const WiFiSettings &settings, WiFiSettings &proto) {
proto = settings;
}
inline void WiFiSettings_read(const WiFiSettings &settings, WiFiSettings &proto) { proto = settings; }
inline StateUpdateResult WiFiSettings_update(const WiFiSettings &proto, WiFiSettings &settings) {
settings = proto;
-1
View File
@@ -20,7 +20,6 @@ esp_err_t handleSleep(httpd_req_t *request);
void reset();
void restart();
void sleep();
void status(JsonObject &root);
void getAnalytics(socket_message_AnalyticsData &analytics);
void getStaticSystemInformation(socket_message_StaticSystemInformation &info);
@@ -1,29 +1,24 @@
#pragma once
#include <FS.h>
#include <template/stateful_service.h>
#include <template/state_result.h>
#include <filesystem.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include <cstdio>
#include <sys/stat.h>
#include <esp_log.h>
static const char *TAG_PERSISTENCE = "FSPersistencePB";
/**
* Protobuf-based filesystem persistence for StatefulService.
*
* @tparam T The state type (should be a nanopb-generated struct like api_APSettings)
*/
template <class T>
class FSPersistencePB {
public:
// Formats are passed as referenced const (local variable) we want to read from, and a reference (proto) we write to
using ProtoStateReader = std::function<void(const T&, T&)>;
// Formats are passed as referenced const (new object) we read from, and a reference to the local variable we write to
using ProtoStateUpdater = std::function<StateUpdateResult(const T&, T&)>;
using ProtoStateReader = std::function<void(const T &, T &)>;
using ProtoStateUpdater = std::function<StateUpdateResult(const T &, T &)>;
FSPersistencePB(ProtoStateReader stateReader, ProtoStateUpdater stateUpdater,
StatefulService<T> *statefulService, const char *filePath,
const pb_msgdesc_t *msgDescriptor, size_t maxSize,
const T &defaultState)
FSPersistencePB(ProtoStateReader stateReader, ProtoStateUpdater stateUpdater, StatefulService<T> *statefulService,
const char *filePath, const pb_msgdesc_t *msgDescriptor, size_t maxSize, const T &defaultState)
: _stateReader(stateReader),
_stateUpdater(stateUpdater),
_statefulService(statefulService),
@@ -36,17 +31,19 @@ class FSPersistencePB {
}
void readFromFS() {
File file = _fs->open(_filePath, "r");
FILE *file = fopen(_filePath, "rb");
if (file) {
size_t fileSize = file.size();
fseek(file, 0, SEEK_END);
size_t fileSize = ftell(file);
fseek(file, 0, SEEK_SET);
if (fileSize > 0 && fileSize <= _maxSize) {
uint8_t *buffer = new uint8_t[fileSize];
size_t bytesRead = file.read(buffer, fileSize);
file.close();
size_t bytesRead = fread(buffer, 1, fileSize, file);
fclose(file);
if (bytesRead == fileSize) {
// Allocate on heap to avoid stack overflow with large proto messages
T *protoMsg = new T();
*protoMsg = {};
pb_istream_t stream = pb_istream_from_buffer(buffer, bytesRead);
@@ -62,7 +59,7 @@ class FSPersistencePB {
}
delete[] buffer;
} else {
file.close();
fclose(file);
}
}
@@ -74,7 +71,6 @@ class FSPersistencePB {
uint8_t *buffer = new uint8_t[_maxSize];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, _maxSize);
// Allocate on heap to avoid stack overflow with large proto messages
T *protoMsg = new T();
*protoMsg = {};
_statefulService->read([this, protoMsg](const T &state) { _stateReader(state, *protoMsg); });
@@ -89,14 +85,15 @@ class FSPersistencePB {
mkdirs();
File file = _fs->open(_filePath, "w");
FILE *file = fopen(_filePath, "wb");
if (!file) {
ESP_LOGE(TAG_PERSISTENCE, "Failed to open file for writing: %s", _filePath);
delete[] buffer;
return false;
}
size_t written = file.write(buffer, stream.bytes_written);
file.close();
size_t written = fwrite(buffer, 1, stream.bytes_written, file);
fclose(file);
delete[] buffer;
return written == stream.bytes_written;
@@ -111,8 +108,7 @@ class FSPersistencePB {
void enableUpdateHandler() {
if (!_updateHandlerId) {
_updateHandlerId = _statefulService->addUpdateHandler(
[&](const std::string &originId) { writeToFS(); });
_updateHandlerId = _statefulService->addUpdateHandler([&](const std::string &originId) { writeToFS(); });
}
}
@@ -120,7 +116,6 @@ class FSPersistencePB {
ProtoStateReader _stateReader;
ProtoStateUpdater _stateUpdater;
StatefulService<T> *_statefulService;
FS *_fs{&ESP_FS};
const char *_filePath;
const pb_msgdesc_t *_msgDescriptor;
size_t _maxSize;
@@ -132,13 +127,15 @@ class FSPersistencePB {
size_t index = 0;
while ((index = path.find('/', index + 1)) != std::string::npos) {
std::string segment = path.substr(0, index);
if (!_fs->exists(segment.c_str())) _fs->mkdir(segment.c_str());
struct stat st;
if (stat(segment.c_str(), &st) != 0) {
FileSystem::mkdirRecursive(segment.c_str());
}
}
}
protected:
void applyDefaults() {
_statefulService->updateWithoutPropagation(
[this](T &state) { return _stateUpdater(_defaultState, state); });
_statefulService->updateWithoutPropagation([this](T &state) { return _stateUpdater(_defaultState, state); });
}
};
+5 -28
View File
@@ -1,7 +1,5 @@
#pragma once
#include <ArduinoJson.h>
#include <list>
#include <functional>
#include <freertos/FreeRTOS.h>
@@ -10,19 +8,13 @@
#include <template/state_result.h>
template <typename T>
using JsonStateUpdater = std::function<StateUpdateResult(JsonVariant &root, T &settings)>;
template <typename T>
using JsonStateReader = std::function<void(T &settings, JsonVariant &root)>;
using HandlerId = size_t;
using StateUpdateCallback = std::function<void(const std::string &originId)>;
using StateHookCallback = std::function<void(const std::string &originId, StateUpdateResult &result)>;
class HandlerBase {
protected:
static inline HandlerId nextId_ = 1; // Start from 1, 0 is invalid
static inline HandlerId nextId_ = 1;
HandlerId id_;
bool allowRemove_;
@@ -98,31 +90,16 @@ class StatefulService {
return result;
}
StateUpdateResult update(JsonVariant &jsonObject, JsonStateUpdater<T> stateUpdater, const std::string &originId) {
lock();
StateUpdateResult result = stateUpdater(jsonObject, state_);
unlock();
notifyStateChange(originId, result);
return result;
}
StateUpdateResult updateWithoutPropagation(JsonVariant &jsonObject, JsonStateUpdater<T> stateUpdater) {
lock();
StateUpdateResult result = stateUpdater(jsonObject, state_);
unlock();
return result;
}
void read(std::function<void(T &)> stateReader) {
lock();
stateReader(state_);
unlock();
}
void read(JsonVariant &jsonObject, JsonStateReader<T> stateReader) {
lock();
stateReader(state_, jsonObject);
unlock();
void read(std::function<void(const T &)> stateReader) const {
const_cast<StatefulService *>(this)->lock();
stateReader(state_);
const_cast<StatefulService *>(this)->unlock();
}
void callUpdateHandlers(const std::string &originId) {
+17 -19
View File
@@ -17,25 +17,6 @@
#define IP_EVENT_STA_GOT_IP_IDF 1000
class WiFiService : public StatefulService<WiFiSettings> {
private:
static void getNetworks(JsonObject &root);
static void getNetworkStatus(JsonObject &root);
void onStationModeDisconnected(int32_t event, void *event_data);
void onStationModeStop(int32_t event, void *event_data);
static void onStationModeGotIP(int32_t event, void *event_data);
FSPersistencePB<WiFiSettings> _persistence;
void reconfigureWiFiConnection();
void manageSTA();
void connectToWiFi();
void configureNetwork(WiFiNetwork &network);
unsigned long _lastConnectionAttempt;
bool _stopping;
constexpr static uint16_t reconnectDelay {10000};
public:
WiFiService();
~WiFiService();
@@ -52,4 +33,21 @@ class WiFiService : public StatefulService<WiFiSettings> {
static esp_err_t getNetworkStatus(httpd_req_t *request);
StatefulProtoEndpoint<WiFiSettings, api_WifiSettings> protoEndpoint;
private:
void onStationModeDisconnected(int32_t event, void *event_data);
void onStationModeStop(int32_t event, void *event_data);
static void onStationModeGotIP(int32_t event, void *event_data);
FSPersistencePB<WiFiSettings> _persistence;
void reconfigureWiFiConnection();
void manageSTA();
void connectToWiFi();
void configureNetwork(WiFiNetwork &network);
unsigned long _lastConnectionAttempt;
bool _stopping;
constexpr static uint16_t reconnectDelay {10000};
};
+2 -2
View File
@@ -1,7 +1,7 @@
# HTTP Server WebSocket support
CONFIG_HTTPD_WS_SUPPORT=y
CONFIG_HTTPD_MAX_REQ_HDR_LEN=1024
CONFIG_HTTPD_MAX_URI_LEN=512
CONFIG_HTTPD_MAX_REQ_HDR_LEN=2048
CONFIG_HTTPD_MAX_URI_LEN=1024
# mDNS
CONFIG_MDNS_MAX_SERVICES=10
+9 -27
View File
@@ -4,14 +4,10 @@
static const char *TAG = "APService";
APService::APService()
: protoEndpoint(APSettings_read, APSettings_update, this,
API_REQUEST_EXTRACTOR(ap_settings, api_APSettings),
: protoEndpoint(APSettings_read, APSettings_update, this, API_REQUEST_EXTRACTOR(ap_settings, api_APSettings),
API_RESPONSE_ASSIGNER(ap_settings, api_APSettings)),
_persistence(APSettings_read, APSettings_update, this,
AP_SETTINGS_FILE, api_APSettings_fields, api_APSettings_size,
APSettings_defaults()) {
: endpoint(APSettings::read, APSettings::update, this),
_persistence(APSettings::read, APSettings::update, this, AP_SETTINGS_FILE),
_persistence(APSettings_read, APSettings_update, this, AP_SETTINGS_FILE, api_APSettings_fields,
api_APSettings_size, APSettings_defaults()),
_dnsServer(nullptr),
_lastManaged(0),
_reconfigureAp(false),
@@ -26,22 +22,8 @@ APService::~APService() {
}
}
void APService::begin() {
_persistence.readFromFS();
}
void APService::begin() { _persistence.readFromFS(); }
void APService::status(JsonObject &root) {
root["status"] = getAPNetworkStatus();
root["ip_address"] = (uint32_t)(WiFi.softAPIP());
root["mac_address"] = WiFi.softAPmacAddress().c_str();
root["station_num"] = WiFi.softAPgetStationNum();
}
APNetworkStatus APService::getAPNetworkStatus() {
wifi_mode_t currentWiFiMode = WiFi.getMode();
bool apActive = currentWiFiMode == WIFI_MODE_AP || currentWiFiMode == WIFI_MODE_APSTA;
if (apActive && state().provisionMode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) {
return APNetworkStatus::LINGERING;
esp_err_t APService::getStatusProto(httpd_req_t *request) {
api_Response res = api_Response_init_zero;
res.status_code = 200;
@@ -53,15 +35,15 @@ esp_err_t APService::getStatusProto(httpd_req_t *request) {
void APService::statusProto(api_APStatus &proto) {
proto.status = getAPNetworkStatus();
proto.ip_address = static_cast<uint32_t>(WiFi.softAPIP());
String mac = WiFi.softAPmacAddress();
std::string mac = WiFi.softAPmacAddress();
strncpy(proto.mac_address, mac.c_str(), sizeof(proto.mac_address) - 1);
proto.mac_address[sizeof(proto.mac_address) - 1] = '\0';
proto.station_num = WiFi.softAPgetStationNum();
}
APNetworkStatus APService::getAPNetworkStatus() {
WiFiMode_t currentWiFiMode = WiFi.getMode();
bool apActive = currentWiFiMode == WIFI_AP || currentWiFiMode == WIFI_AP_STA;
wifi_mode_t currentWiFiMode = WiFi.getMode();
bool apActive = currentWiFiMode == WIFI_MODE_AP || currentWiFiMode == WIFI_MODE_APSTA;
if (apActive && state().provision_mode != AP_MODE_ALWAYS && WiFi.status() == WL_CONNECTED) {
return LINGERING;
}
@@ -88,8 +70,8 @@ void APService::loop() {
void APService::manageAP() {
wifi_mode_t currentWiFiMode = WiFi.getMode();
if (state().provisionMode == AP_MODE_ALWAYS ||
(state().provisionMode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED) || _recoveryMode) {
if (state().provision_mode == AP_MODE_ALWAYS ||
(state().provision_mode == AP_MODE_DISCONNECTED && WiFi.status() != WL_CONNECTED) || _recoveryMode) {
if (_reconfigureAp || currentWiFiMode == WIFI_MODE_NULL || currentWiFiMode == WIFI_MODE_STA) {
startAP();
}
+1 -1
View File
@@ -278,7 +278,7 @@ esp_err_t WebServer::wsSendAll(const uint8_t* data, size_t len) {
}
esp_err_t WebServer::sendError(httpd_req_t* req, int status, const char* message) {
return send(req, status, (uint8_t*) message, strlen(message));
return send(req, status, (uint8_t*)message, strlen(message));
}
esp_err_t WebServer::sendOk(httpd_req_t* req) { return send(req, 200, nullptr, 0); }
+70 -71
View File
@@ -4,13 +4,14 @@
#include <cstring>
#include "utils/string_utils.hpp"
#include <esp_log.h>
#include <pb_encode.h>
#include <pb_decode.h>
static const char *TAG = "FileSystem";
namespace FileSystem {
// Storage for dynamically allocated FileEntry arrays
static std::vector<api_FileEntry*> allocatedEntries;
static std::vector<api_FileEntry *> allocatedEntries;
static void freeAllocatedEntries() {
for (auto ptr : allocatedEntries) {
@@ -19,67 +20,81 @@ static void freeAllocatedEntries() {
allocatedEntries.clear();
}
void listFilesProto(const std::string &directory, api_FileEntry *entry) {
File root = ESP_FS.open(directory.find("/") == 0 ? directory.c_str() : ("/" + directory).c_str());
if (!root.isDirectory()) {
static void listFilesProtoRecursive(const std::string &directory, api_FileEntry *entry) {
DIR *dir = opendir(directory.c_str());
if (!dir) {
entry->children_count = 0;
entry->children = nullptr;
return;
}
// First pass: count children
std::vector<File> files;
File file = root.openNextFile();
while (file) {
files.push_back(file);
file = root.openNextFile();
}
std::vector<std::string> names;
std::vector<bool> isDirs;
std::vector<size_t> sizes;
if (files.empty()) {
struct dirent *d;
while ((d = readdir(dir)) != nullptr) {
if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) continue;
std::string fullPath = directory + "/" + d->d_name;
struct stat st;
if (stat(fullPath.c_str(), &st) == 0) {
names.push_back(d->d_name);
isDirs.push_back(S_ISDIR(st.st_mode));
sizes.push_back(st.st_size);
}
}
closedir(dir);
if (names.empty()) {
entry->children_count = 0;
entry->children = nullptr;
return;
}
// Allocate children array
entry->children_count = files.size();
entry->children = new api_FileEntry[files.size()];
entry->children_count = names.size();
entry->children = new api_FileEntry[names.size()];
allocatedEntries.push_back(entry->children);
// Fill children
for (size_t i = 0; i < files.size(); i++) {
for (size_t i = 0; i < names.size(); i++) {
api_FileEntry &child = entry->children[i];
memset(&child, 0, sizeof(child));
std::string name = std::string(files[i].name());
strncpy(child.name, name.c_str(), sizeof(child.name) - 1);
strncpy(child.name, names[i].c_str(), sizeof(child.name) - 1);
child.name[sizeof(child.name) - 1] = '\0';
child.is_directory = files[i].isDirectory();
child.is_directory = isDirs[i];
if (child.is_directory) {
listFilesProto(name, &child);
std::string childPath = directory + "/" + names[i];
listFilesProtoRecursive(childPath, &child);
} else {
child.size = files[i].size();
child.size = sizes[i];
child.children_count = 0;
child.children = nullptr;
}
}
}
void listFilesProto(const std::string &directory, api_FileEntry *entry) {
std::string path = directory;
if (path.empty() || path[0] != '/') {
path = "/" + directory;
}
std::string fullPath = std::string(MOUNT_POINT) + path;
listFilesProtoRecursive(fullPath, entry);
}
esp_err_t getFilesProto(httpd_req_t *request) {
freeAllocatedEntries(); // Clean up any previous allocations
freeAllocatedEntries();
api_Response res = api_Response_init_zero;
res.status_code = 200;
res.which_payload = api_Response_file_list_tag;
// Create root entry
api_FileEntry rootEntry = api_FileEntry_init_zero;
strncpy(rootEntry.name, "root", sizeof(rootEntry.name) - 1);
rootEntry.is_directory = true;
listFilesProto("/", &rootEntry);
listFilesProtoRecursive(MOUNT_POINT, &rootEntry);
// Allocate entries array for FileList
res.payload.file_list.entries_count = 1;
res.payload.file_list.entries = new api_FileEntry[1];
allocatedEntries.push_back(res.payload.file_list.entries);
@@ -87,8 +102,10 @@ esp_err_t getFilesProto(httpd_req_t *request) {
esp_err_t result = WebServer::send(request, 200, res, api_Response_fields);
freeAllocatedEntries(); // Clean up after sending
freeAllocatedEntries();
return result;
}
bool init() {
esp_vfs_littlefs_conf_t conf = {
.base_path = MOUNT_POINT,
@@ -157,6 +174,19 @@ bool writeFile(const char *filename, const char *content) {
return written == len;
}
bool writeFile(const char *filename, const uint8_t *content, size_t size) {
FILE *f = fopen(filename, "wb");
if (!f) {
ESP_LOGE(TAG, "Failed to open file for writing: %s", filename);
return false;
}
size_t written = fwrite(content, 1, size, f);
fclose(f);
return written == size;
}
bool mkdirRecursive(const char *path) {
char tmp[256];
char *p = nullptr;
@@ -211,8 +241,7 @@ esp_err_t getConfigFile(httpd_req_t *request) {
if (content.empty()) {
return WebServer::sendError(request, 500, "Failed to read file");
}
String content = file.readString();
file.close();
if (ends_with(path, ".pb")) {
httpd_resp_set_type(request, "application/x-protobuf");
} else if (ends_with(path, ".json")) {
@@ -224,48 +253,30 @@ esp_err_t getConfigFile(httpd_req_t *request) {
}
esp_err_t handleDelete(httpd_req_t *request, const api_FileDeleteRequest &req) {
ESP_LOGI(TAG, "Deleting file: %s", req.path);
std::string fullPath = std::string(MOUNT_POINT) + req.path;
ESP_LOGI(TAG, "Deleting file: %s", fullPath.c_str());
api_Response res = api_Response_init_zero;
if (deleteFile(req.path)) {
if (deleteFile(fullPath.c_str())) {
res.status_code = 200;
res.which_payload = api_Response_empty_message_tag;
return WebServer::send(request, 200, res, api_Response_fields);
} else {
return WebServer::sendError(request, 500, "Delete failed");
httpd_resp_set_type(request, "application/json");
return httpd_resp_send(request, content.c_str(), content.length());
}
esp_err_t handleDelete(httpd_req_t *request, JsonVariant &json) {
if (json.is<JsonObject>()) {
const char *filename = json["file"].as<const char *>();
std::string fullPath = std::string(MOUNT_POINT) + filename;
ESP_LOGI(TAG, "Deleting file: %s", fullPath.c_str());
return deleteFile(fullPath.c_str()) ? WebServer::sendOk(request)
: WebServer::sendError(request, 500, "Delete failed");
}
}
esp_err_t handleEdit(httpd_req_t *request, const api_FileEditRequest &req) {
ESP_LOGI(TAG, "Editing file: %s", req.path);
std::string fullPath = std::string(MOUNT_POINT) + req.path;
ESP_LOGI(TAG, "Editing file: %s", fullPath.c_str());
api_Response res = api_Response_init_zero;
if (editFile(req.path, req.content->bytes, req.content->size)) {
if (editFile(fullPath.c_str(), req.content->bytes, req.content->size)) {
res.status_code = 200;
res.which_payload = api_Response_empty_message_tag;
return WebServer::send(request, 200, res, api_Response_fields);
} else {
return WebServer::sendError(request, 500, "Edit failed");
esp_err_t handleEdit(httpd_req_t *request, JsonVariant &json) {
if (json.is<JsonObject>()) {
const char *filename = json["file"].as<const char *>();
const char *content = json["content"].as<const char *>();
std::string fullPath = std::string(MOUNT_POINT) + filename;
ESP_LOGI(TAG, "Editing file: %s", fullPath.c_str());
return editFile(fullPath.c_str(), content) ? WebServer::sendOk(request)
: WebServer::sendError(request, 500, "Edit failed");
}
}
@@ -310,34 +321,22 @@ std::string listFiles(const std::string &directory, bool isRoot) {
return output;
}
bool editFile(const char *filename, const uint8_t *content, size_t size) {
File file = ESP_FS.open(filename, FILE_WRITE);
if (!file) return false;
bool editFile(const char *filename, const uint8_t *content, size_t size) { return writeFile(filename, content, size); }
file.write(content, size);
file.close();
return true;
}
bool editFile(const char *filename, const char *content) { return writeFile(filename, content); }
esp_err_t mkdir(httpd_req_t *request, const api_FileMkdirRequest &req) {
ESP_LOGI(TAG, "Creating directory: %s", req.path);
std::string fullPath = std::string(MOUNT_POINT) + req.path;
ESP_LOGI(TAG, "Creating directory: %s", fullPath.c_str());
api_Response res = api_Response_init_zero;
if (ESP_FS.mkdir(req.path)) {
if (mkdirRecursive(fullPath.c_str())) {
res.status_code = 200;
res.which_payload = api_Response_empty_message_tag;
return WebServer::send(request, 200, res, api_Response_fields);
} else {
return WebServer::sendError(request, 500, "mkdir failed");
}
bool editFile(const char *filename, const char *content) { return writeFile(filename, content); }
esp_err_t mkdir(httpd_req_t *request, JsonVariant &json) {
const char *path = json["path"].as<const char *>();
std::string fullPath = std::string(MOUNT_POINT) + path;
ESP_LOGI(TAG, "Creating directory: %s", fullPath.c_str());
return mkdirRecursive(fullPath.c_str()) ? WebServer::sendOk(request)
: WebServer::sendError(request, 500, "mkdir failed");
}
} // namespace FileSystem
+19 -21
View File
@@ -76,35 +76,32 @@ void setupServer() {
server.on("/api/wifi/networks", HTTP_GET, [&](httpd_req_t *request) { return wifiService.getNetworks(request); });
server.on("/api/wifi/sta/status", HTTP_GET,
[&](httpd_req_t *request) { return wifiService.getNetworkStatus(request); });
server.on("/api/ap/status", HTTP_GET, [&](httpd_req_t *request) { return apService.getStatusProto(request); });
server.on("/api/ap/settings", HTTP_GET,
[&](httpd_req_t *request) { return apService.protoEndpoint.getState(request); });
server.on("/api/ap/settings", HTTP_POST,
[&](httpd_req_t *request, api_Request *protoReq) {
return apService.protoEndpoint.handleStateUpdate(request, protoReq);
});
[&](httpd_req_t *request) { return apService.protoEndpoint.getState(request); });
server.on("/api/ap/settings", HTTP_POST, [&](httpd_req_t *request, api_Request *protoReq) {
return apService.protoEndpoint.handleStateUpdate(request, protoReq);
});
server.on("/api/peripherals/settings", HTTP_GET,
[&](httpd_req_t *request) { return peripherals.protoEndpoint.getState(request); });
server.on("/api/peripherals/settings", HTTP_POST,
[&](httpd_req_t *request, api_Request *protoReq) {
return peripherals.protoEndpoint.handleStateUpdate(request, protoReq);
});
server.on("/api/peripherals/settings", HTTP_POST, [&](httpd_req_t *request, api_Request *protoReq) {
return peripherals.protoEndpoint.handleStateUpdate(request, protoReq);
});
#if FT_ENABLED(USE_MDNS)
server.on("/api/mdns/settings", HTTP_GET, [&](httpd_req_t *request) { return mdnsService.protoEndpoint.getState(request); });
server.on("/api/mdns/settings", HTTP_POST,
[&](httpd_req_t *request, api_Request *protoReq) {
return mdnsService.protoEndpoint.handleStateUpdate(request, protoReq);
});
server.on("/api/mdns/settings", HTTP_GET,
[&](httpd_req_t *request) { return mdnsService.protoEndpoint.getState(request); });
server.on("/api/mdns/settings", HTTP_POST, [&](httpd_req_t *request, api_Request *protoReq) {
return mdnsService.protoEndpoint.handleStateUpdate(request, protoReq);
});
server.on("/api/mdns/status", HTTP_GET, [&](httpd_req_t *request) { return mdnsService.getStatus(request); });
server.on("/api/mdns/query", HTTP_POST,
[&](httpd_req_t *request, api_Request *protoReq) {
return mdnsService.queryServices(request, protoReq);
});
server.on("/api/mdns/query", HTTP_POST, [&](httpd_req_t *request, api_Request *protoReq) {
return mdnsService.queryServices(request, protoReq);
});
#endif
server.on("/api/config/*", HTTP_GET, [](httpd_req_t *request) { return FileSystem::getConfigFile(request); });
server.on("/api/files", HTTP_GET, [&](httpd_req_t *request) { return FileSystem::getFilesProto(request); });
STAITC_PROTO_POST_ENDPOINT(server, "/api/files/delete", file_delete_request, FileSystem::handleDelete);
@@ -277,6 +274,7 @@ void IRAM_ATTR SpotControlLoopEntry(void *) {
void IRAM_ATTR serviceLoopEntry(void *) {
ESP_LOGI("main", "Service task starting");
WiFi.init();
wifiService.begin();
mdns_init();
mdns_hostname_set(APP_NAME);
+25 -70
View File
@@ -8,9 +8,8 @@ MDNSService::MDNSService()
: protoEndpoint(MDNSSettings_read, MDNSSettings_update, this,
API_REQUEST_EXTRACTOR(mdns_settings, api_MDNSSettings),
API_RESPONSE_ASSIGNER(mdns_settings, api_MDNSSettings)),
_persistence(MDNSSettings_read, MDNSSettings_update, this,
MDNS_SETTINGS_FILE, api_MDNSSettings_fields, api_MDNSSettings_size,
MDNSSettings_defaults()) {
_persistence(MDNSSettings_read, MDNSSettings_update, this, MDNS_SETTINGS_FILE, api_MDNSSettings_fields,
api_MDNSSettings_size, MDNSSettings_defaults()) {
addUpdateHandler([&](const std::string &originId) { reconfigureMDNS(); }, false);
}
@@ -35,14 +34,6 @@ void MDNSService::reconfigureMDNS() {
void MDNSService::startMDNS() {
ESP_LOGV(TAG, "Starting MDNS with hostname: %s", state().hostname);
if (MDNS.begin(state().hostname)) {
_started = true;
MDNS.setInstanceName(state().instance);
addServices();
ESP_LOGI(TAG, "MDNS started successfully with hostname: %s", state().hostname);
} else {
esp_err_t err = mdns_init();
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize MDNS: %s", esp_err_to_name(err));
@@ -50,7 +41,7 @@ void MDNSService::startMDNS() {
return;
}
err = mdns_hostname_set(state().hostname.c_str());
err = mdns_hostname_set(state().hostname);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to set MDNS hostname: %s", esp_err_to_name(err));
mdns_free();
@@ -58,7 +49,7 @@ void MDNSService::startMDNS() {
return;
}
err = mdns_instance_name_set(state().instance.c_str());
err = mdns_instance_name_set(state().instance);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to set MDNS instance name: %s", esp_err_to_name(err));
}
@@ -66,7 +57,7 @@ void MDNSService::startMDNS() {
_started = true;
addServices();
ESP_LOGI(TAG, "MDNS started successfully with hostname: %s", state().hostname.c_str());
ESP_LOGI(TAG, "MDNS started successfully with hostname: %s", state().hostname);
}
void MDNSService::stopMDNS() {
@@ -78,11 +69,15 @@ void MDNSService::stopMDNS() {
void MDNSService::addServices() {
for (size_t i = 0; i < state().services_count; i++) {
const auto &service = state().services[i];
MDNS.addService(service.service, service.protocol, service.port);
esp_err_t err = mdns_service_add(nullptr, service.service, service.protocol, service.port, nullptr, 0);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to add service %s: %s", service.service, esp_err_to_name(err));
continue;
}
for (size_t j = 0; j < service.txt_records_count; j++) {
const auto &txt = service.txt_records[j];
MDNS.addServiceTxt(service.service, service.protocol, txt.key, txt.value);
mdns_service_txt_item_set(service.service, service.protocol, txt.key, txt.value);
}
}
@@ -90,25 +85,7 @@ void MDNSService::addServices() {
const auto &txt = state().global_txt_records[i];
for (size_t j = 0; j < state().services_count; j++) {
const auto &service = state().services[j];
MDNS.addServiceTxt(service.service, service.protocol, txt.key, txt.value);
for (const auto &service : state().services) {
esp_err_t err =
mdns_service_add(nullptr, service.service.c_str(), service.protocol.c_str(), service.port, nullptr, 0);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Failed to add service %s: %s", service.service.c_str(), esp_err_to_name(err));
continue;
}
for (const auto &txt : service.txtRecords) {
mdns_service_txt_item_set(service.service.c_str(), service.protocol.c_str(), txt.key.c_str(),
txt.value.c_str());
}
}
for (const auto &txt : state().globalTxtRecords) {
for (const auto &service : state().services) {
mdns_service_txt_item_set(service.service.c_str(), service.protocol.c_str(), txt.key.c_str(),
txt.value.c_str());
mdns_service_txt_item_set(service.service, service.protocol, txt.key, txt.value);
}
}
}
@@ -143,68 +120,46 @@ esp_err_t MDNSService::queryServices(httpd_req_t *request, api_Request *protoReq
const api_MDNSQueryRequest &queryReq = protoReq->payload.mdns_query_request;
ESP_LOGI(TAG, "Querying for service: %s, protocol: %s", queryReq.service, queryReq.protocol);
int n = MDNS.queryService(queryReq.service, queryReq.protocol);
ESP_LOGI(TAG, "Found %d services", n);
mdns_result_t *results = nullptr;
esp_err_t err = mdns_query_ptr(queryReq.service, queryReq.protocol, 3000, 20, &results);
api_Response response = api_Response_init_zero;
response.which_payload = api_Response_mdns_query_response_tag;
api_MDNSQueryResponse &queryResp = response.payload.mdns_query_response;
// Limit to max_count from options file (16)
size_t count = (n > 16) ? 16 : static_cast<size_t>(n);
queryResp.services_count = count;
for (size_t i = 0; i < count; i++) {
strncpy(queryResp.services[i].name, MDNS.hostname(i).c_str(), sizeof(queryResp.services[i].name) - 1);
strncpy(queryResp.services[i].ip, MDNS.IP(i).toString().c_str(), sizeof(queryResp.services[i].ip) - 1);
queryResp.services[i].port = MDNS.port(i);
}
return WebServer::send(request, 200, response, api_Response_fields);
esp_err_t MDNSService::queryServices(httpd_req_t *request, JsonVariant &json) {
std::string service = json["service"].as<std::string>();
std::string proto = json["protocol"].as<std::string>();
JsonDocument doc;
JsonVariant root = doc.to<JsonVariant>();
ESP_LOGI(TAG, "Querying for service: %s, protocol: %s", service.c_str(), proto.c_str());
mdns_result_t *results = nullptr;
esp_err_t err = mdns_query_ptr(service.c_str(), proto.c_str(), 3000, 20, &results);
if (err != ESP_OK) {
ESP_LOGW(TAG, "MDNS query failed: %s", esp_err_to_name(err));
root["services"] = JsonArray();
return WebServer::sendJson(request, 200, doc);
queryResp.services_count = 0;
return WebServer::send(request, 200, response, api_Response_fields);
}
int count = 0;
mdns_result_t *r = results;
while (r) {
while (r && count < 16) {
count++;
r = r->next;
}
ESP_LOGI(TAG, "Found %d services", count);
JsonArray servicesArray = root["services"].to<JsonArray>();
queryResp.services_count = count;
r = results;
while (r) {
JsonVariant serviceObj = servicesArray.add<JsonVariant>();
size_t i = 0;
while (r && i < 16) {
if (r->hostname) {
serviceObj["name"] = r->hostname;
strncpy(queryResp.services[i].name, r->hostname, sizeof(queryResp.services[i].name) - 1);
}
if (r->addr) {
char ip_str[16];
esp_ip4addr_ntoa(&r->addr->addr.u_addr.ip4, ip_str, sizeof(ip_str));
serviceObj["ip"] = ip_str;
strncpy(queryResp.services[i].ip, ip_str, sizeof(queryResp.services[i].ip) - 1);
}
serviceObj["port"] = r->port;
queryResp.services[i].port = r->port;
r = r->next;
i++;
}
mdns_query_results_free(results);
return WebServer::sendJson(request, 200, doc);
return WebServer::send(request, 200, response, api_Response_fields);
}
+6 -2
View File
@@ -8,7 +8,7 @@
#include <esp_sleep.h>
#include <soc/soc.h>
#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
#if CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C6
#include <driver/temperature_sensor.h>
static float temperatureRead() {
@@ -16,7 +16,11 @@ static float temperatureRead() {
static bool initialized = false;
if (!initialized) {
temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(-10, 80);
temperature_sensor_config_t temp_sensor_config = {
.range_min = -10,
.range_max = 80,
.clk_src = TEMPERATURE_SENSOR_CLK_SRC_DEFAULT,
};
if (temperature_sensor_install(&temp_sensor_config, &temp_sensor) == ESP_OK) {
temperature_sensor_enable(temp_sensor);
initialized = true;
+16 -70
View File
@@ -4,15 +4,13 @@
static const char *TAG = "WiFiService";
WiFiService::WiFiService()
: _persistence(WiFiSettings_read, WiFiSettings_update, this, WIFI_SETTINGS_FILE,
api_WifiSettings_fields, api_WifiSettings_size, WiFiSettings_defaults()),
protoEndpoint(WiFiSettings_read, WiFiSettings_update, this,
: protoEndpoint(WiFiSettings_read, WiFiSettings_update, this,
API_REQUEST_EXTRACTOR(wifi_settings, api_WifiSettings),
API_RESPONSE_ASSIGNER(wifi_settings, api_WifiSettings)) {
: _persistence(WiFiSettings::read, WiFiSettings::update, this, WIFI_SETTINGS_FILE),
API_RESPONSE_ASSIGNER(wifi_settings, api_WifiSettings)),
_persistence(WiFiSettings_read, WiFiSettings_update, this, WIFI_SETTINGS_FILE, api_WifiSettings_fields,
api_WifiSettings_size, WiFiSettings_defaults()),
_lastConnectionAttempt(0),
_stopping(false),
endpoint(WiFiSettings::read, WiFiSettings::update, this) {
_stopping(false) {
addUpdateHandler([&](const std::string &originId) { reconfigureWiFiConnection(); }, false);
}
@@ -30,13 +28,10 @@ void WiFiService::begin() {
_persistence.readFromFS();
_lastConnectionAttempt = 0;
if (state().wifi_networks_count == 1) {
configureNetwork(state().wifi_networks[0]);
vTaskDelay(500 / portTICK_PERIOD_MS);
if (!state().wifiSettings.empty()) {
if (state().wifi_networks_count >= 1) {
WiFi.mode(WIFI_MODE_STA);
vTaskDelay(100 / portTICK_PERIOD_MS);
configureNetwork(state().wifiSettings[0]);
configureNetwork(state().wifi_networks[0]);
}
}
@@ -52,7 +47,6 @@ esp_err_t WiFiService::handleScan(httpd_req_t *request) {
WiFi.scanDelete();
WiFi.scanNetworks(true);
}
// Return 202 with empty_message payload (no pointer fields to encode)
api_Response response = api_Response_init_zero;
response.status_code = 202;
response.which_payload = api_Response_empty_message_tag;
@@ -62,7 +56,6 @@ esp_err_t WiFiService::handleScan(httpd_req_t *request) {
esp_err_t WiFiService::getNetworks(httpd_req_t *request) {
int numNetworks = WiFi.scanComplete();
if (numNetworks == -1) {
// Scan in progress - return 202 with empty_message payload
api_Response response = api_Response_init_zero;
response.status_code = 202;
response.which_payload = api_Response_empty_message_tag;
@@ -71,10 +64,8 @@ esp_err_t WiFiService::getNetworks(httpd_req_t *request) {
return handleScan(request);
}
// Limit to 20 networks max
size_t count = (numNetworks > 20) ? 20 : static_cast<size_t>(numNetworks);
// Allocate networks array on stack (pointer type in proto)
api_WifiNetworkScan networks[20] = {};
for (size_t i = 0; i < count; i++) {
@@ -95,15 +86,8 @@ esp_err_t WiFiService::getNetworks(httpd_req_t *request) {
}
void WiFiService::setupMDNS(const char *hostname) {
MDNS.begin(state().hostname);
MDNS.setInstanceName(hostname);
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 80);
MDNS.addServiceTxt("http", "tcp", "Firmware Version", APP_VERSION);
}
mdns_init();
mdns_hostname_set(state().hostname.c_str());
mdns_hostname_set(state().hostname);
mdns_instance_name_set(hostname);
mdns_service_add(nullptr, "_http", "_tcp", 80, nullptr, 0);
mdns_service_add(nullptr, "_ws", "_tcp", 80, nullptr, 0);
@@ -111,19 +95,6 @@ void WiFiService::setupMDNS(const char *hostname) {
mdns_service_txt_set("_http", "_tcp", &txtData, 1);
}
void WiFiService::getNetworks(JsonObject &root) {
JsonArray networks = root["networks"].to<JsonArray>();
int numNetworks = WiFi.scanComplete();
for (int i = 0; i < numNetworks; i++) {
JsonObject network = networks.add<JsonObject>();
network["rssi"] = WiFi.RSSI(i);
network["ssid"] = WiFi.SSID(i).c_str();
network["bssid"] = WiFi.BSSIDstr(i).c_str();
network["channel"] = WiFi.channel(i);
network["encryption_type"] = (uint8_t)WiFi.encryptionType(i);
}
}
esp_err_t WiFiService::getNetworkStatus(httpd_req_t *request) {
api_Response response = api_Response_init_zero;
response.which_payload = api_Response_wifi_status_tag;
@@ -141,15 +112,6 @@ esp_err_t WiFiService::getNetworkStatus(httpd_req_t *request) {
wifiStatus.channel = WiFi.channel();
wifiStatus.subnet_mask = static_cast<uint32_t>(WiFi.subnetMask());
wifiStatus.gateway_ip = static_cast<uint32_t>(WiFi.gatewayIP());
root["local_ip"] = (uint32_t)(WiFi.localIP());
root["mac_address"] = WiFi.macAddress().c_str();
root["rssi"] = WiFi.RSSI();
root["ssid"] = WiFi.SSID().c_str();
root["bssid"] = WiFi.BSSIDstr().c_str();
root["channel"] = WiFi.channel();
root["subnet_mask"] = (uint32_t)(WiFi.subnetMask());
root["gateway_ip"] = (uint32_t)(WiFi.gatewayIP());
IPAddress dnsIP1 = WiFi.dnsIP(0);
IPAddress dnsIP2 = WiFi.dnsIP(1);
if (dnsIP1 != IPAddress(0, 0, 0, 0)) {
@@ -165,8 +127,10 @@ esp_err_t WiFiService::getNetworkStatus(httpd_req_t *request) {
void WiFiService::manageSTA() {
if (WiFi.isConnected() || state().wifi_networks_count == 0) return;
if ((WiFi.getMode() & WIFI_STA) == 0) connectToWiFi();
if (WiFi.isConnected() || state().wifiSettings.empty()) return;
if ((WiFi.getMode() & WIFI_MODE_STA) == 0) {
WiFi.mode(WIFI_MODE_STA);
vTaskDelay(100 / portTICK_PERIOD_MS);
}
connectToWiFi();
}
@@ -193,11 +157,7 @@ void WiFiService::connectToWiFi() {
for (pb_size_t j = 0; j < state().wifi_networks_count; j++) {
WiFiNetwork &network = state().wifi_networks[j];
for (auto &network : state().wifiSettings) {
if (ssid_scan == network.ssid) {
if (rssi_scan >= FACTORY_WIFI_RSSI_THRESHOLD) {
// Network is available
}
if (rssi_scan > bestNetworkDb) {
bestNetworkDb = rssi_scan;
bestNetwork = &network;
@@ -209,10 +169,9 @@ void WiFiService::connectToWiFi() {
if (!state().priority_rssi) {
for (pb_size_t j = 0; j < state().wifi_networks_count; j++) {
WiFiNetwork &network = state().wifi_networks[j];
// Check if this network was found in scan
for (int i = 0; i < scanResult; ++i) {
if (WiFi.SSID(i) == network.ssid) {
ESP_LOGI("WiFiSettingsService", "Connecting to first available network: %s", network.ssid);
ESP_LOGI(TAG, "Connecting to first available network: %s", network.ssid);
configureNetwork(network);
WiFi.scanDelete();
return;
@@ -220,17 +179,7 @@ void WiFiService::connectToWiFi() {
}
}
} else if (bestNetwork) {
ESP_LOGI("WiFiSettingsService", "Connecting to strongest network: %s", bestNetwork->ssid);
if (!state().priorityBySignalStrength) {
for (auto &network : state().wifiSettings) {
if (network.available == true) {
ESP_LOGI(TAG, "Connecting to first available network: %s", network.ssid.c_str());
configureNetwork(network);
break;
}
}
} else if (state().priorityBySignalStrength && bestNetwork) {
ESP_LOGI(TAG, "Connecting to strongest network: %s", bestNetwork->ssid.c_str());
ESP_LOGI(TAG, "Connecting to strongest network: %s", bestNetwork->ssid);
configureNetwork(*bestNetwork);
} else {
ESP_LOGI(TAG, "No known networks found.");
@@ -242,16 +191,13 @@ void WiFiService::connectToWiFi() {
void WiFiService::configureNetwork(WiFiNetwork &network) {
if (network.static_ip_config) {
WiFi.config(IPAddress(network.local_ip), IPAddress(network.gateway_ip),
IPAddress(network.subnet_mask), IPAddress(network.dns_ip_1), IPAddress(network.dns_ip_2));
WiFi.config(IPAddress(network.local_ip), IPAddress(network.gateway_ip), IPAddress(network.subnet_mask),
IPAddress(network.dns_ip_1), IPAddress(network.dns_ip_2));
} else {
WiFi.config(IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0), IPAddress(0, 0, 0, 0));
}
WiFi.setHostname(state().hostname);
WiFi.begin(network.ssid, network.password);
WiFi.setHostname(state().hostname.c_str());
WiFi.begin(network.ssid.c_str(), network.password.c_str());
#if CONFIG_IDF_TARGET_ESP32C3
WiFi.setTxPower(8);
+1 -3
View File
@@ -111,13 +111,11 @@ board_build.filesystem = littlefs
board_build.embed_txtfiles =
board_build.sdkconfig_defaults = esp32/sdkconfig.defaults
lib_deps =
bblanchon/ArduinoJson@^7.0.0
lib_ldf_mode = deep
lib_compat_mode = off
lib_compat_mode = strict
extra_scripts =
pre:esp32/scripts/pre_build.py
pre:esp32/scripts/build_app.py
lib_compat_mode = strict
; debug_tool = esp-builtin
; debug_init_break =
; upload_port = COM[13] # Only use this when upload port is not correctly detected due to multiple COM ports attached.