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