272 lines
11 KiB
C++
272 lines
11 KiB
C++
#include <Arduino.h>
|
|
#include <macros.h>
|
|
#include <Component.h>
|
|
#include <enums.h>
|
|
#include "Logger.h"
|
|
#include "./PHApp.h"
|
|
#include <ESPmDNS.h>
|
|
#include <LittleFS.h>
|
|
#include <ArduinoJson.h>
|
|
|
|
#include "./config.h"
|
|
#include "./config_adv.h"
|
|
#include "./config-modbus.h"
|
|
#include "./features.h"
|
|
|
|
#ifdef ENABLE_PROCESS_PROFILE
|
|
#include "profiles/PlotBase.h"
|
|
#include "profiles/SignalPlot.h"
|
|
#include "profiles/TemperatureProfile.h"
|
|
#endif
|
|
|
|
#include <modbus/ModbusTCP.h>
|
|
#include <modbus/ModbusTypes.h>
|
|
|
|
|
|
short PHApp::loadNetworkSettings() {
|
|
Log.infoln(F("PHApp::loadNetworkSettings() - Attempting to load network configuration from LittleFS..."));
|
|
if (!LittleFS.begin(true)) { // Ensure LittleFS is mounted (true formats if necessary)
|
|
Log.errorln(F("PHApp::loadNetworkSettings() - Failed to mount LittleFS. Cannot load network configuration."));
|
|
wifiSettings.print(); // Print defaults before returning
|
|
return E_FATAL; // Use E_FATAL for critical FS failure
|
|
}
|
|
|
|
File configFile = LittleFS.open(NETWORK_CONFIG_FILENAME, "r");
|
|
if (!configFile) {
|
|
Log.warningln(F("PHApp::loadNetworkSettings() - Failed to open network config file: %s. Using default settings."), NETWORK_CONFIG_FILENAME);
|
|
LittleFS.end(); // Close LittleFS
|
|
wifiSettings.print(); // Print defaults before returning
|
|
return E_NOT_FOUND; // Indicates file wasn't found, defaults will be used.
|
|
}
|
|
|
|
Log.infoln(F("PHApp::loadNetworkSettings() - Opened network config file: %s"), NETWORK_CONFIG_FILENAME);
|
|
|
|
JsonDocument doc; // Using JsonDocument for automatic memory management
|
|
|
|
DeserializationError error = deserializeJson(doc, configFile);
|
|
configFile.close(); // Close the file as soon as possible
|
|
|
|
if (error) {
|
|
Log.errorln(F("PHApp::loadNetworkSettings() - Failed to parse network config JSON: %s. Using default settings."), error.c_str());
|
|
LittleFS.end(); // Close LittleFS
|
|
wifiSettings.print(); // Print defaults before returning
|
|
return E_INVALID_PARAMETER; // Indicates a parsing error, defaults will be used.
|
|
}
|
|
|
|
JsonObject root = doc.as<JsonObject>();
|
|
if (root.isNull()) {
|
|
Log.errorln(F("PHApp::loadNetworkSettings() - Network config JSON root is not an object. Using default settings."));
|
|
LittleFS.end();
|
|
wifiSettings.print(); // Print defaults before returning
|
|
return E_INVALID_PARAMETER;
|
|
}
|
|
|
|
Log.infoln(F("PHApp::loadNetworkSettings() - Successfully parsed network config file. Applying settings..."));
|
|
short loadResult = wifiSettings.loadSettings(root); // Call the existing method in WiFiNetworkSettings
|
|
|
|
LittleFS.end(); // Ensure LittleFS is closed after operations
|
|
|
|
if (loadResult == E_OK) {
|
|
Log.infoln(F("PHApp::loadNetworkSettings() - Network settings loaded successfully from %s."), NETWORK_CONFIG_FILENAME);
|
|
} else {
|
|
Log.warningln(F("PHApp::loadNetworkSettings() - Issues applying parsed network settings. Some defaults may still be in use."));
|
|
}
|
|
wifiSettings.print(); // Print settings after attempting to load them
|
|
return loadResult;
|
|
}
|
|
|
|
short PHApp::saveNetworkSettings(JsonObject& doc) {
|
|
Log.infoln(F("PHApp::saveNetworkSettings() - Attempting to save network configuration to LittleFS..."));
|
|
|
|
if (!LittleFS.begin(true)) { // Ensure LittleFS is mounted
|
|
Log.errorln(F("PHApp::saveNetworkSettings() - Failed to mount LittleFS. Cannot save network configuration."));
|
|
return E_FATAL; // Or a more specific LittleFS error
|
|
}
|
|
|
|
File configFile = LittleFS.open(NETWORK_CONFIG_FILENAME, "w"); // Open for writing, creates if not exists, truncates if exists
|
|
if (!configFile) {
|
|
Log.errorln(F("PHApp::saveNetworkSettings() - Failed to open network config file '%s' for writing."), NETWORK_CONFIG_FILENAME);
|
|
LittleFS.end(); // Close LittleFS
|
|
return E_FATAL; // Replaced E_FS_ERROR with E_FATAL
|
|
}
|
|
|
|
Log.infoln(F("PHApp::saveNetworkSettings() - Opened/created network config file: %s for writing."), NETWORK_CONFIG_FILENAME);
|
|
|
|
size_t bytesWritten = serializeJson(doc, configFile);
|
|
configFile.close(); // Close the file as soon as possible
|
|
|
|
if (bytesWritten > 0) {
|
|
Log.infoln(F("PHApp::saveNetworkSettings() - Successfully wrote %d bytes to %s."), bytesWritten, NETWORK_CONFIG_FILENAME);
|
|
} else {
|
|
Log.errorln(F("PHApp::saveNetworkSettings() - Failed to serialize JSON to file or wrote 0 bytes to %s."), NETWORK_CONFIG_FILENAME);
|
|
LittleFS.end(); // Close LittleFS
|
|
// Attempt to remove the (potentially empty or corrupted) file if serialization failed.
|
|
if (LittleFS.exists(NETWORK_CONFIG_FILENAME)) {
|
|
LittleFS.remove(NETWORK_CONFIG_FILENAME);
|
|
}
|
|
return E_INVALID_PARAMETER; // Or a more specific serialization error
|
|
}
|
|
|
|
LittleFS.end(); // Ensure LittleFS is closed after operations
|
|
Log.infoln(F("PHApp::saveNetworkSettings() - Network settings saved successfully to %s."), NETWORK_CONFIG_FILENAME);
|
|
// Optionally, after saving, you might want to immediately reload and apply these settings:
|
|
// loadNetworkSettings();
|
|
// Or, signal that a restart is needed for settings to take full effect if they are only read at boot.
|
|
return E_OK;
|
|
}
|
|
|
|
short PHApp::setupNetwork()
|
|
{
|
|
loadNetworkSettings(); // Load settings from LittleFS first
|
|
bool sta_connected = false;
|
|
bool ap_started = false;
|
|
|
|
#if defined(ENABLE_AP_STA)
|
|
WiFi.mode(WIFI_AP_STA);
|
|
Log.infoln("Setting up AP_STA with SSID: %s", wifiSettings.ap_ssid.c_str());
|
|
if (!WiFi.softAPConfig(wifiSettings.ap_config_ip, wifiSettings.ap_config_gateway, wifiSettings.ap_config_subnet))
|
|
{
|
|
Log.errorln("AP Failed to configure");
|
|
}
|
|
else
|
|
{
|
|
if (!WiFi.softAP(wifiSettings.ap_ssid.c_str(), wifiSettings.ap_password.c_str()))
|
|
{
|
|
Log.errorln("AP Failed to start");
|
|
}
|
|
else
|
|
{
|
|
Log.infoln("AP IP address: %s", WiFi.softAPIP().toString().c_str());
|
|
ap_started = true;
|
|
}
|
|
}
|
|
|
|
// Configure Station (STA) part
|
|
Log.infoln("Configuring STA for AP_STA mode...");
|
|
|
|
if (!WiFi.config(wifiSettings.sta_local_IP, wifiSettings.sta_gateway, wifiSettings.sta_subnet, wifiSettings.sta_primary_dns, wifiSettings.sta_secondary_dns))
|
|
{
|
|
Log.errorln("STA (for AP_STA) Failed to configure");
|
|
}
|
|
WiFi.begin(wifiSettings.sta_ssid.c_str(), wifiSettings.sta_password.c_str());
|
|
Log.infoln("Attempting to connect to STA WiFi: %s", wifiSettings.sta_ssid.c_str());
|
|
|
|
int connect_timeout_ms = 30000;
|
|
unsigned long start_time = millis();
|
|
while (WiFi.status() != WL_CONNECTED && (millis() - start_time < connect_timeout_ms))
|
|
{
|
|
delay(100);
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED)
|
|
{
|
|
Log.infoln("STA IP address (AP_STA mode): %s", WiFi.localIP().toString().c_str());
|
|
sta_connected = true;
|
|
}
|
|
else
|
|
{
|
|
Log.warningln("STA (for AP_STA) connection failed or timed out. AP is still active.");
|
|
}
|
|
|
|
#elif defined(ENABLE_WIFI) // STA mode only
|
|
Log.infoln("Configuring WiFi in STA mode...");
|
|
if (!WiFi.config(wifiSettings.sta_local_IP, wifiSettings.sta_gateway, wifiSettings.sta_subnet, wifiSettings.sta_primary_dns, wifiSettings.sta_secondary_dns))
|
|
{
|
|
Log.errorln("STA Failed to configure");
|
|
}
|
|
WiFi.begin(wifiSettings.sta_ssid.c_str(), wifiSettings.sta_password.c_str());
|
|
int connect_timeout_ms = 30000;
|
|
unsigned long start_time = millis();
|
|
while (WiFi.status() != WL_CONNECTED && (millis() - start_time < connect_timeout_ms))
|
|
{
|
|
delay(100);
|
|
}
|
|
if (WiFi.status() == WL_CONNECTED)
|
|
{
|
|
Log.infoln("IP address: %s", WiFi.localIP().toString().c_str());
|
|
sta_connected = true;
|
|
}
|
|
else
|
|
{
|
|
Log.errorln("WiFi connection timed out!");
|
|
// return E_WIFI_CONNECTION_FAILED; // Keep network setup going if AP might work or for mDNS on AP
|
|
}
|
|
#endif
|
|
|
|
// Initialize mDNS
|
|
// It should be started if either STA is connected or AP is successfully started.
|
|
if (sta_connected || ap_started) {
|
|
const char* mdns_hostname = "polymech-cassandra";
|
|
if (MDNS.begin(mdns_hostname)) {
|
|
Log.infoln("mDNS responder started. Hostname: %s", mdns_hostname);
|
|
MDNS.addService("http", "tcp", 80);
|
|
Log.infoln("mDNS service _http._tcp.local on port 80 advertised.");
|
|
Log.infoln("Access the web server at: http://%s.local", mdns_hostname);
|
|
} else {
|
|
Log.errorln("Error starting mDNS responder!");
|
|
}
|
|
} else {
|
|
Log.warningln("Neither STA connected nor AP started. mDNS will not be initialized.");
|
|
}
|
|
|
|
#ifdef ENABLE_MODBUS_TCP
|
|
setNetCapability(OBJECT_NET_CAPS::E_NCAPS_MODBUS);
|
|
setupModbus();
|
|
#else
|
|
modbusManager = nullptr;
|
|
#endif
|
|
|
|
#if defined(ENABLE_WEBSERVER) && defined(ENABLE_MODBUS_TCP)
|
|
|
|
if (modbusManager) // Check Modbus dependency first
|
|
{
|
|
IPAddress webserverIP = IPAddress(0,0,0,0);
|
|
bool canStartWebServer = false;
|
|
|
|
#if defined(ENABLE_AP_STA)
|
|
webserverIP = WiFi.softAPIP(); // IP of the AP interface
|
|
if (webserverIP && webserverIP != IPAddress(0,0,0,0)) {
|
|
Log.infoln("AP_STA mode: Web server will use AP IP: %s", webserverIP.toString().c_str());
|
|
canStartWebServer = true;
|
|
} else {
|
|
Log.errorln("AP_STA mode: Soft AP IP is invalid or not yet available. Cannot determine IP for web server on AP.");
|
|
}
|
|
// Log STA IP for informational purposes if connected
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
Log.infoln("AP_STA mode: STA interface is also connected with IP: %s", WiFi.localIP().toString().c_str());
|
|
Log.infoln(" External clients (on STA network) might try http://%s", WiFi.localIP().toString().c_str());
|
|
}
|
|
#elif defined(ENABLE_WIFI) // STA mode only
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
webserverIP = WiFi.localIP();
|
|
Log.infoln("STA mode: Web server will use STA IP: %s", webserverIP.toString().c_str());
|
|
canStartWebServer = true;
|
|
} else {
|
|
Log.errorln("STA mode: WiFi not connected. Cannot start web server.");
|
|
}
|
|
#else
|
|
// This case should not be hit if ENABLE_WEBSERVER implies one of the WiFi modes for IP-based server.
|
|
Log.warningln("WebServer enabled, but no WiFi mode (AP_STA or STA) is configured to provide an IP address.");
|
|
#endif
|
|
if (canStartWebServer) {
|
|
webServer = new RESTServer(webserverIP, 80, modbusManager, this);
|
|
components.push_back(webServer);
|
|
Log.infoln("RESTServer initialized.");
|
|
Log.infoln("Clients connected to the ESP32 (e.g., via AP) should try accessing the server at: http://%s", webserverIP.toString().c_str());
|
|
} else {
|
|
Log.errorln("Cannot initialize RESTServer: No suitable IP address available from current WiFi configuration.");
|
|
webServer = nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Log.errorln("Cannot initialize RESTServer: ModbusTCP is null! Ensure Modbus is setup first.");
|
|
webServer = nullptr;
|
|
return E_DEPENDENCY_NOT_MET;
|
|
}
|
|
#elif defined(ENABLE_WEBSERVER) && !defined(ENABLE_MODBUS_TCP)
|
|
Log.warningln("WebServer enabled but Modbus TCP is not. RESTServer initialization might be incomplete.");
|
|
webServer = nullptr; // Keep it null if it relies on ModbusTCP
|
|
#endif
|
|
return E_OK;
|
|
} |