Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions SmartFuseBox/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,7 @@ constexpr uint8_t ConfigLinkedRelayCount = 2;

struct SystemConfig {
int8_t timezoneOffset;
bool rebootOnSave; // if true, device reboots after C0 persists config
uint8_t reserved1[3];
uint8_t reserved1[4];
int8_t reserved2[4];
} __attribute__((packed));

Expand Down
32 changes: 31 additions & 1 deletion SmartFuseBox/ConfigCommandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "ConfigCommandHandler.h"
#include "ConfigController.h"
#include "SystemFunctions.h"
#include "MessageBus.h"

#if defined(CARD_CONFIG_LOADER)
#include "SdCardConfigLoader.h"
Expand All @@ -35,7 +36,8 @@ ConfigCommandHandler::ConfigCommandHandler(
ConfigController* configController)
:
_wifiController(wifiController),
_configController(configController)
_configController(configController),
_messageBus(nullptr)
#if defined(CARD_CONFIG_LOADER)
, _sdCardConfigLoader(nullptr)
#endif
Expand All @@ -45,6 +47,11 @@ ConfigCommandHandler::ConfigCommandHandler(
#endif
{}

void ConfigCommandHandler::setMessageBus(MessageBus* messageBus)
{
_messageBus = messageBus;
}

#if defined(MQTT_SUPPORT)
void ConfigCommandHandler::setMqttConfigHandler(MQTTConfigCommandHandler* mqttConfigHandler)
{
Expand All @@ -65,6 +72,29 @@ bool ConfigCommandHandler::handleCommand(SerialCommandManager* sender, const cha
if (SystemFunctions::commandMatches(command, ConfigSaveSettings))
{
result = _configController->save();

if (result == ConfigResult::Success)
{
const char* rebootStr = getParamValue(params, paramCount, "reboot");
if (rebootStr && SystemFunctions::parseBooleanValue(rebootStr))
{
if (SystemFunctions::canReboot() && _messageBus != nullptr)
{
char rebootVal[] = "pending";
StringKeyValue rebootParam = makeParam("reboot", rebootVal);
sendAckOk(sender, command, &rebootParam);
_messageBus->publish<RebootRequested>();
return true;
Comment thread
k3ldar marked this conversation as resolved.
}
else
{
char rebootVal[] = "required";
Comment thread
k3ldar marked this conversation as resolved.
StringKeyValue rebootParam = makeParam("reboot", rebootVal);
sendAckOk(sender, command, &rebootParam);
return true;
}
}
}
}
else if (SystemFunctions::commandMatches(command, ConfigGetSettings))
{
Expand Down
4 changes: 4 additions & 0 deletions SmartFuseBox/ConfigCommandHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

// Forward declarations
class ConfigController;
class MessageBus;

#if defined(CARD_CONFIG_LOADER)
class SdCardConfigLoader;
Expand All @@ -40,6 +41,7 @@ class ConfigCommandHandler : public virtual BaseCommandHandler, public BaseConfi
private:
IWifiController* _wifiController;
ConfigController* _configController;
MessageBus* _messageBus;

#if defined(CARD_CONFIG_LOADER)
SdCardConfigLoader* _sdCardConfigLoader;
Expand All @@ -55,6 +57,8 @@ class ConfigCommandHandler : public virtual BaseCommandHandler, public BaseConfi
IWifiController* wifiController,
ConfigController* configController);

void setMessageBus(MessageBus* messageBus);

#if defined(CARD_CONFIG_LOADER)
void setSdCardConfigLoader(SdCardConfigLoader* sdCardConfigLoader);
#endif
Expand Down
3 changes: 1 addition & 2 deletions SmartFuseBox/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ void ConfigManager::migrateV4toV5()
memset(_cfg.auth.reserved, 0x00, sizeof(_cfg.auth.reserved));

_cfg.version = ConfigVersion5;
_cfg.system.rebootOnSave = false;
memset(_cfg.system.reserved1, 0x00, sizeof(_cfg.system.reserved1));
_cfg.sdCard.csPin = PinDisabled;
}

Expand Down Expand Up @@ -316,7 +316,6 @@ void ConfigManager::resetToDefaults()
strncpy(_cfg.location.homePort, "Unknown", ConfigHomePortLength - 1);
_cfg.location.homePort[ConfigHomePortLength - 1] = '\0';
_cfg.system.timezoneOffset = 0; // UTC
_cfg.system.rebootOnSave = false;

_cfg.network.bluetoothEnabled = false;

Expand Down
90 changes: 77 additions & 13 deletions SmartFuseBox/Dht11SensorHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "WarningManager.h"
#include "WarningType.h"
#include "BaseSensor.h"
#include "Environment.h"

constexpr uint64_t TempHumidityCheckMs = 2500;

Expand All @@ -48,8 +49,14 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
#if defined(MQTT_SUPPORT)
char _slugTemp[32];
char _slugHumidity[32];
char _slugDewPoint[32];
char _slugComfort[32];
char _slugCondensation[40];
char _nameTemp[48];
char _nameHumidity[48];
char _nameDewPoint[48];
char _nameComfort[48];
char _nameCondensation[48];
#endif

protected:
Expand Down Expand Up @@ -232,8 +239,14 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
#if defined(MQTT_SUPPORT)
snprintf(_slugTemp, sizeof(_slugTemp), "%s_temperature", _safeSlug);
snprintf(_slugHumidity, sizeof(_slugHumidity), "%s_humidity", _safeSlug);
snprintf(_slugDewPoint, sizeof(_slugDewPoint), "%s_dew_point", _safeSlug);
snprintf(_slugComfort, sizeof(_slugComfort), "%s_comfort", _safeSlug);
snprintf(_slugCondensation, sizeof(_slugCondensation), "%s_condensation_risk", _safeSlug);
snprintf(_nameTemp, sizeof(_nameTemp), "%s Temperature", _name);
snprintf(_nameHumidity, sizeof(_nameHumidity), "%s Humidity", _name);
snprintf(_nameDewPoint, sizeof(_nameDewPoint), "%s Dew Point", _name);
snprintf(_nameComfort, sizeof(_nameComfort), "%s Comfort", _name);
snprintf(_nameCondensation, sizeof(_nameCondensation), "%s Condensation Risk", _name);
#endif
}

Expand All @@ -254,8 +267,25 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
dtostrf(_temperatureOffset, 1, 1, celsiusOffset);
dtostrf(_humidityOffset, 1, 1, humidityOffset);

int written = snprintf(buffer, size, "\"name\":\"%s\",\"SensorPin\":%u,\"temperature\":%s,\"tempOffset\":%s,\"humidity\":%s,\"humOffset\":%s",
_name, _sensorPin, celsius, celsiusOffset, humidity, humidityOffset);
double dewPt = Environment::dewPoint(_celsius, _humidity);
char dewPointStr[8];
dtostrf(dewPt, 1, 1, dewPointStr);

char comfortBuf[24];
strncpy_P(comfortBuf, Environment::getComfortDescription(_celsius, _humidity, dewPt), sizeof(comfortBuf));
comfortBuf[sizeof(comfortBuf) - 1] = '\0';

CondensationRisk risk = Environment::condensationRisk(_celsius, dewPt, false);
char riskBuf[8];
strncpy_P(riskBuf, Environment::getCondensationRiskLabel(risk), sizeof(riskBuf));
riskBuf[sizeof(riskBuf) - 1] = '\0';

char safeName[64];
SystemFunctions::sanitizeJsonString(_name, safeName, sizeof(safeName));

int written = snprintf(buffer, size,
"\"name\":\"%s\",\"SensorPin\":%u,\"temperature\":%s,\"tempOffset\":%s,\"humidity\":%s,\"humOffset\":%s,\"dew_point\":%s,\"comfort\":\"%s\",\"condensation_risk\":\"%s\"",
safeName, _sensorPin, celsius, celsiusOffset, humidity, humidityOffset, dewPointStr, comfortBuf, riskBuf);

if (written < 0 || (size_t)written >= size)
{
Expand All @@ -282,28 +312,62 @@ class Dht11SensorHandler : public BaseSensor, public BroadcastLoggerSupport
#if defined(MQTT_SUPPORT)
uint8_t getMqttChannelCount() const override
{
return 2;
return 5;
}

MqttSensorChannel getMqttChannel(uint8_t channelIndex) const override
{
if (channelIndex == 0)
switch (channelIndex)
{
return { _nameTemp, _slugTemp, "temperature", "temperature", "\xc2\xb0""C", false };
case 0:
return { _nameTemp, _slugTemp, "temperature", "temperature", "\xc2\xb0""C", false };
case 1:
return { _nameHumidity, _slugHumidity, "humidity", "humidity", "%", false };
case 2:
return { _nameDewPoint, _slugDewPoint, "humidity", "temperature", "\xc2\xb0""C", false };
case 3:
return { _nameComfort, _slugComfort, "humidity", nullptr, nullptr, false };
case 4:
return { _nameCondensation, _slugCondensation, "humidity", nullptr, nullptr, false };
default:
return { _nameHumidity, _slugHumidity, "humidity", "humidity", "%", false };
}

return { _nameHumidity, _slugHumidity, "humidity", "humidity", "%", false };
}

void getMqttValue(uint8_t channelIndex, char* buffer, size_t size) const override
{
if (channelIndex == 0)
{
dtostrf(_celsius, 1, 1, buffer);
}
else
switch (channelIndex)
{
snprintf(buffer, size, "%u", static_cast<uint8_t>(_humidity));
case 0:
dtostrf(_celsius, 1, 1, buffer);
break;
case 1:
snprintf(buffer, size, "%u", static_cast<uint8_t>(_humidity));
break;
case 2:
{
double dewPt = Environment::dewPoint(_celsius, _humidity);
dtostrf(dewPt, 1, 1, buffer);
break;
}
case 3:
{
double dewPt = Environment::dewPoint(_celsius, _humidity);
strncpy_P(buffer, Environment::getComfortDescription(_celsius, _humidity, dewPt), size);
buffer[size - 1] = '\0';
break;
}
case 4:
{
double dewPt = Environment::dewPoint(_celsius, _humidity);
CondensationRisk risk = Environment::condensationRisk(_celsius, dewPt, false);
strncpy_P(buffer, Environment::getCondensationRiskLabel(risk), size);
buffer[size - 1] = '\0';
break;
}
default:
if (size > 0) buffer[0] = '\0';
break;
}
}
#endif
Expand Down
51 changes: 51 additions & 0 deletions SmartFuseBox/Environment.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ constexpr char comfortHumid[] PROGMEM = "Humid";
constexpr char comfortDry[] PROGMEM = "Dry";
constexpr char comfortFair[] PROGMEM = "Fair";

constexpr char condensationLow[] PROGMEM = "Low";
constexpr char condensationWatch[] PROGMEM = "Watch";
constexpr char condensationHigh[] PROGMEM = "High";

enum class CondensationRisk
{
Low,
Expand Down Expand Up @@ -62,4 +66,51 @@ class Environment
if (delta <= 2.0) return CondensationRisk::Watch;
return CondensationRisk::Low;
}

/**
* @brief Return a PROGMEM comfort description string based on temperature, humidity and dew point.
*
* @param tempC Air temperature in degrees Celsius
* @param humidity Relative humidity (0-100 %)
* @param dewPoint Pre-calculated dew point in degrees Celsius
* @return PROGMEM string pointer — copy with strncpy_P before use
*/
static const char* getComfortDescription(double tempC, double humidity, double dewPoint)
{
if (tempC < 10 && humidity > 70)
return comfortColdDamp;

if (tempC < 12)
return comfortCold;

if (tempC > 26 && humidity > 65)
return comfortHotHumid;

if (dewPoint > 18)
return comfortHumid;

if (tempC >= 18 && tempC <= 24 && humidity < 65)
return comfortComfortable;

return comfortFair;
}

/**
* @brief Return a PROGMEM label string for a CondensationRisk value.
*
* @param risk Condensation risk level
* @return PROGMEM string pointer — copy with strncpy_P before use
*/
static const char* getCondensationRiskLabel(CondensationRisk risk)
{
switch (risk)
{
case CondensationRisk::High:
return condensationHigh;
case CondensationRisk::Watch:
return condensationWatch;
default:
return condensationLow;
}
}
};
5 changes: 5 additions & 0 deletions SmartFuseBox/MessageBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,11 @@ struct MqttMessageReceived
using Callback = std::function<void(const char* topic, const char* payload)>;
};

struct RebootRequested
{
using Callback = std::function<void()>;
};

class MessageBus {
private:
template<typename Event>
Expand Down
2 changes: 1 addition & 1 deletion SmartFuseBox/PageVhfRadio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ void PageVhfRadio::onEnterPage()
char wrappedBuffer[134];
SystemFunctions::wrapTextAtWordBoundary(callSignBuf, wrappedBuffer, sizeof(wrappedBuffer), 30);

sendText(ControlCallSign, callSignBuf);
sendText(ControlCallSign, wrappedBuffer);
}

void PageVhfRadio::begin()
Expand Down
4 changes: 3 additions & 1 deletion SmartFuseBox/SensorNetworkHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,11 @@ void SensorNetworkHandler::formatStatusJson(char* buffer, size_t size)
}

// Write sensor entry (removed duplicate and fixed format)
char safeName[64];
SystemFunctions::sanitizeJsonString(sensor->getSensorName(), safeName, sizeof(safeName));
int n = snprintf(buffer + written, size - written,
"\"%s\":{\"uid\":%d,\"idType\":%d,\"type\":%d,%s}",
sensor->getSensorName(),
safeName,
sensor->getUid(),
static_cast<uint8_t>(sensor->getSensorIdType()),
static_cast<uint8_t>(sensor->getSensorType()),
Expand Down
4 changes: 2 additions & 2 deletions SmartFuseBox/SmartFuseBox.vcxproj

Large diffs are not rendered by default.

13 changes: 13 additions & 0 deletions SmartFuseBox/SmartFuseBoxApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ SmartFuseBoxApp::SmartFuseBoxApp(SerialCommandManager* commandMgrComputer)
, _schedulerCommandHandler(&_relayController)
, _serialHandlerCount(0)
, _scheduleController(&_relayController, &_messageBus)
, _pendingRebootTime(0)

#if defined(LED_MANAGER)
, _ledManager(&_messageBus)
Expand Down Expand Up @@ -261,6 +262,13 @@ void SmartFuseBoxApp::setup(RemoteSensor** remoteSensors, uint8_t remoteSensorCo

// Link config sync manager to handlers so they can coordinate config synchronization
_ackHandler.setConfigController(&_configController);
_configHandler.setMessageBus(&_messageBus);

// Subscribe to deferred reboot requests
_messageBus.subscribe<RebootRequested>([this]()
{
_pendingRebootTime = SystemFunctions::millis64() + 500;
});

#if defined(MQTT_SUPPORT)
// Link MQTT config handler and controller
Expand Down Expand Up @@ -422,6 +430,11 @@ void SmartFuseBoxApp::loop()

SystemCpuMonitor::update();

if (_pendingRebootTime != 0 && now >= _pendingRebootTime)
{
SystemFunctions::reboot();
}

delay(DefaultDelay);
}

Expand Down
1 change: 1 addition & 0 deletions SmartFuseBox/SmartFuseBoxApp.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ class SmartFuseBoxApp
ISerialCommandHandler* _serialHandlers[MaxSerialHandlerCount];
uint8_t _serialHandlerCount;
ScheduleController _scheduleController;
uint64_t _pendingRebootTime;

#if defined(LED_MANAGER)
LedMatrixManager _ledManager;
Expand Down
2 changes: 1 addition & 1 deletion SmartFuseBox/SunCalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,5 +124,5 @@ SunTimes SunCalculator::calculateSunTimes(float lat, float lon, uint16_t year, u
float UT_rise = fmod(T_rise - lngHour + 24, 24);
float UT_set = fmod(T_set - lngHour + 24, 24);

return { UT_rise + tzOffset, UT_set + tzOffset, true };
return { fmodf(UT_rise + tzOffset + 24.0f, 24.0f), fmodf(UT_set + tzOffset + 24.0f, 24.0f), true };
}
Loading
Loading