Skip to content

Commit

Permalink
yasolr_start_pzem()
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieucarbou committed Dec 29, 2024
1 parent ad894e8 commit fc013a6
Show file tree
Hide file tree
Showing 16 changed files with 275 additions and 200 deletions.
13 changes: 7 additions & 6 deletions include/YaSolR.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,12 @@ extern Mycila::Task mqttPublishStaticTask;
extern Mycila::Task mqttPublishTask;

// PZEM
extern Mycila::PZEM pzemO1;
extern Mycila::PZEM pzemO2;
extern Mycila::Task pzemO1PairingTask;
extern Mycila::Task pzemO2PairingTask;
extern Mycila::Task pzemTask;
extern Mycila::TaskManager pzemTaskManager;
extern Mycila::PZEM* pzemO1;
extern Mycila::PZEM* pzemO2;
extern Mycila::Task* pzemO1PairingTask;
extern Mycila::Task* pzemO2PairingTask;
extern Mycila::Task* pzemTask;
extern Mycila::TaskManager* pzemTaskManager;

extern Mycila::Dimmer dimmerO1;
extern Mycila::Dimmer dimmerO2;
Expand Down Expand Up @@ -162,6 +162,7 @@ extern void yasolr_start_jsy_remote_listener();
extern void yasolr_start_jsy();
extern void yasolr_start_lights();
extern void yasolr_start_logging();
extern void yasolr_start_pzem();
extern void yasolr_start_rest_api();
extern void yasolr_start_website();
extern void yasolr_start_zcd();
Expand Down
32 changes: 21 additions & 11 deletions lib/MycilaRouter/MycilaRouterOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ void Mycila::RouterOutput::toJson(const JsonObject& root, float gridVoltage) con
Metrics dimmerMetrics;
getOutputMetrics(dimmerMetrics, gridVoltage);
toJson(root["metrics"].to<JsonObject>(), dimmerMetrics);

JsonObject local = root["source"]["local"].to<JsonObject>();
if (_localMetrics.isPresent()) {
local["enabled"] = true;
local["time"] = _localMetrics.getLastUpdateTime();
toJson(local, _localMetrics.get());
} else {
local["enabled"] = false;
}
}

void Mycila::RouterOutput::toJson(const JsonObject& dest, const Metrics& metrics) {
Expand Down Expand Up @@ -288,7 +297,8 @@ void Mycila::RouterOutput::applyAutoBypass() {
void Mycila::RouterOutput::getOutputMetrics(Metrics& metrics, float gridVoltage) const {
metrics.resistance = config.calibratedResistance;
metrics.voltage = gridVoltage;
metrics.energy = _pzem->data.activeEnergy;
if (_localMetrics.isPresent())
metrics.energy = _localMetrics.get().energy;
const float dutyCycle = getDimmerDutyCycleLive();
const float maxPower = metrics.resistance == 0 ? 0 : metrics.voltage * metrics.voltage / metrics.resistance;
metrics.power = dutyCycle * maxPower;
Expand All @@ -300,18 +310,18 @@ void Mycila::RouterOutput::getOutputMetrics(Metrics& metrics, float gridVoltage)
}

bool Mycila::RouterOutput::getOutputMeasurements(Metrics& metrics) const {
if (!_pzem->isConnected())
if (_localMetrics.isAbsent())
return false;
metrics.voltage = _pzem->data.voltage;
metrics.energy = _pzem->data.activeEnergy;
metrics.voltage = _localMetrics.get().voltage;
metrics.energy = _localMetrics.get().energy;
if (getState() == State::OUTPUT_ROUTING) {
metrics.apparentPower = _pzem->data.apparentPower;
metrics.current = _pzem->data.current;
metrics.dimmedVoltage = _pzem->data.dimmedVoltage();
metrics.power = _pzem->data.activePower;
metrics.powerFactor = _pzem->data.powerFactor;
metrics.resistance = _pzem->data.resistance();
metrics.thdi = _pzem->data.thdi();
metrics.apparentPower = _localMetrics.get().apparentPower;
metrics.current = _localMetrics.get().current;
metrics.dimmedVoltage = _localMetrics.get().dimmedVoltage;
metrics.power = _localMetrics.get().power;
metrics.powerFactor = _localMetrics.get().powerFactor;
metrics.resistance = _localMetrics.get().resistance;
metrics.thdi = _localMetrics.get().thdi;
}
return true;
}
Expand Down
30 changes: 17 additions & 13 deletions lib/MycilaRouter/MycilaRouterOutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

#include <MycilaDimmer.h>
#include <MycilaExpiringValue.h>
#include <MycilaPZEM004Tv3.h>
#include <MycilaRelay.h>

#ifdef MYCILA_JSON_SUPPORT
Expand All @@ -32,11 +31,11 @@ namespace Mycila {
};

typedef struct {
float apparentPower = NAN;
float current = NAN;
float dimmedVoltage = NAN;
float energy = NAN;
float power = NAN;
float apparentPower = 0;
float current = 0;
float dimmedVoltage = 0;
float energy = 0;
float power = 0;
float powerFactor = NAN;
float resistance = NAN;
float thdi = NAN;
Expand All @@ -58,11 +57,9 @@ namespace Mycila {

RouterOutput(const char* name,
Dimmer& dimmer,
Relay& relay,
PZEM& pzem) : _name(name),
_dimmer(&dimmer),
_relay(&relay),
_pzem(&pzem) {}
Relay& relay) : _name(name),
_dimmer(&dimmer),
_relay(&relay) {}
// output

State getState() const;
Expand Down Expand Up @@ -102,12 +99,19 @@ namespace Mycila {

// metrics

ExpiringValue<Metrics>& localMetrics() { return _localMetrics; }
const ExpiringValue<Metrics>& localMetrics() const { return _localMetrics; }

// get output theoretical metrics based on the dimmer state and the grid voltage
void getOutputMetrics(Metrics& metrics, float gridVoltage) const;
// get PZEM measurements, and returns false if the PZEM is not connected, true if measurements are available
bool getOutputMeasurements(Metrics& metrics) const;

float getOutputPower() const { return _pzem->isConnected() ? _pzem->data.activePower : 0; }
std::optional<float> getOutputPower() const {
if (_localMetrics.isPresent() && _localMetrics.get().power > 0)
return _localMetrics.get().power;
return std::nullopt;
}

// temperature

Expand All @@ -121,10 +125,10 @@ namespace Mycila {
const char* _name;
Dimmer* _dimmer;
Relay* _relay;
PZEM* _pzem;
bool _autoBypassEnabled = false;
bool _bypassEnabled = false;
ExpiringValue<float> _temperature;
ExpiringValue<Metrics> _localMetrics;

private:
void _setBypass(bool state, bool log = true);
Expand Down
26 changes: 14 additions & 12 deletions src/Website.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,14 +323,14 @@ void YaSolR::Website::begin() {
_relaySwitch(_relay2Switch, routerRelay2);

_output1PZEMSync.onChange([](bool value) {
pzemO1PairingTask.resume();
_output1PZEMSync.setValue(!pzemO1PairingTask.isPaused());
pzemO1PairingTask->resume();
_output1PZEMSync.setValue(!pzemO1PairingTask->isPaused());
dashboard.refresh(_output1PZEMSync);
});

_output2PZEMSync.onChange([](bool value) {
pzemO2PairingTask.resume();
_output2PZEMSync.setValue(!pzemO2PairingTask.isPaused());
pzemO2PairingTask->resume();
_output2PZEMSync.setValue(!pzemO2PairingTask->isPaused());
dashboard.refresh(_output2PZEMSync);
});

Expand Down Expand Up @@ -488,8 +488,10 @@ void YaSolR::Website::begin() {
_energyReset.onPush([]() {
if (jsy)
jsy->resetEnergy();
pzemO1.resetEnergy();
pzemO2.resetEnergy();
if (pzemO1)
pzemO1->resetEnergy();
if (pzemO2)
pzemO2->resetEnergy();
});
_reset.onPush([]() { resetTask.resume(); });
_restart.onPush([]() { restartTask.resume(); });
Expand Down Expand Up @@ -862,7 +864,7 @@ void YaSolR::Website::initCards() {
const bool autoDimmer1Activated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_DIMMER);
const bool autoBypass1Activated = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_BYPASS);
const bool output1TempEnabled = (config.getBool(KEY_ENABLE_OUTPUT1_DS18) && ds18O1 && ds18O1->isEnabled()) || config.isEmpty(KEY_OUTPUT1_TEMPERATURE_MQTT_TOPIC);
const bool pzem1Enabled = config.getBool(KEY_ENABLE_OUTPUT1_PZEM) && pzemO1.isEnabled();
const bool pzem1Enabled = config.getBool(KEY_ENABLE_OUTPUT1_PZEM) && pzemO1 && pzemO1->isEnabled();
const char* output1Days = config.get(KEY_OUTPUT1_DAYS);

_output1DimmerAuto.setValue(autoDimmer1Activated);
Expand Down Expand Up @@ -908,7 +910,7 @@ void YaSolR::Website::initCards() {
const bool autoDimmer2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_DIMMER);
const bool autoBypass2Activated = config.getBool(KEY_ENABLE_OUTPUT2_AUTO_BYPASS);
const bool output2TempEnabled = (config.getBool(KEY_ENABLE_OUTPUT2_DS18) && ds18O2 && ds18O2->isEnabled()) || !config.isEmpty(KEY_OUTPUT2_TEMPERATURE_MQTT_TOPIC);
const bool pzem2Enabled = config.getBool(KEY_ENABLE_OUTPUT2_PZEM) && pzemO2.isEnabled();
const bool pzem2Enabled = config.getBool(KEY_ENABLE_OUTPUT2_PZEM) && pzemO2 && pzemO2->isEnabled();
const char* output2Days = config.get(KEY_OUTPUT2_DAYS);

_output2DimmerAuto.setValue(autoDimmer2Activated);
Expand Down Expand Up @@ -1246,8 +1248,8 @@ void YaSolR::Website::updateCards() {

// Hardware (config)

_output1PZEMSync.setValue(!pzemO1PairingTask.isPaused());
_output2PZEMSync.setValue(!pzemO2PairingTask.isPaused());
_output1PZEMSync.setValue(pzemO1PairingTask && !pzemO1PairingTask->isPaused());
_output2PZEMSync.setValue(pzemO2PairingTask && !pzemO2PairingTask->isPaused());
_resistanceCalibration.setValue(router.isCalibrationRunning());

#ifdef APP_MODEL_PRO
Expand Down Expand Up @@ -1287,10 +1289,10 @@ void YaSolR::Website::updateCards() {
_status(_mqtt, KEY_ENABLE_MQTT, mqtt.isEnabled(), mqtt.isConnected(), mqtt.getLastError() ? mqtt.getLastError() : YASOLR_LBL_113);
_status(_output1Dimmer, KEY_ENABLE_OUTPUT1_DIMMER, dimmerO1.isEnabled(), pulseAnalyzer && pulseAnalyzer->isOnline(), pulseAnalyzer && pulseAnalyzer->isEnabled() ? YASOLR_LBL_110 : YASOLR_LBL_179);
_status(_output1DS18, KEY_ENABLE_OUTPUT1_DS18, ds18O1 && ds18O1->isEnabled(), ds18O1 && ds18O1->getLastTime() > 0, YASOLR_LBL_114);
_status(_output1PZEM, KEY_ENABLE_OUTPUT1_PZEM, pzemO1.isEnabled(), pzemO1.isConnected() && pzemO1.getDeviceAddress() == YASOLR_PZEM_ADDRESS_OUTPUT1, pzemO1.isConnected() ? YASOLR_LBL_180 : YASOLR_LBL_110);
_status(_output1PZEM, KEY_ENABLE_OUTPUT1_PZEM, pzemO1 && pzemO1->isEnabled(), pzemO1 && pzemO1->isConnected() && pzemO1->getDeviceAddress() == YASOLR_PZEM_ADDRESS_OUTPUT1, pzemO1 && pzemO1->isConnected() ? YASOLR_LBL_180 : YASOLR_LBL_110);
_status(_output2Dimmer, KEY_ENABLE_OUTPUT2_DIMMER, dimmerO2.isEnabled(), pulseAnalyzer && pulseAnalyzer->isOnline(), pulseAnalyzer && pulseAnalyzer->isEnabled() ? YASOLR_LBL_110 : YASOLR_LBL_179);
_status(_output2DS18, KEY_ENABLE_OUTPUT2_DS18, ds18O2 && ds18O2->isEnabled(), ds18O2 && ds18O2->getLastTime() > 0, YASOLR_LBL_114);
_status(_output2PZEM, KEY_ENABLE_OUTPUT2_PZEM, pzemO2.isEnabled(), pzemO2.isConnected() && pzemO2.getDeviceAddress() == YASOLR_PZEM_ADDRESS_OUTPUT2, pzemO2.isConnected() ? YASOLR_LBL_180 : YASOLR_LBL_110);
_status(_output2PZEM, KEY_ENABLE_OUTPUT2_PZEM, pzemO2 && pzemO2->isEnabled(), pzemO2 && pzemO2->isConnected() && pzemO2->getDeviceAddress() == YASOLR_PZEM_ADDRESS_OUTPUT2, pzemO2 && pzemO2->isConnected() ? YASOLR_LBL_180 : YASOLR_LBL_110);
_status(_routerDS18, KEY_ENABLE_DS18_SYSTEM, ds18Sys && ds18Sys->isEnabled(), ds18Sys && ds18Sys->getLastTime() > 0, YASOLR_LBL_114);
_status(_zcd, KEY_ENABLE_ZCD, pulseAnalyzer && pulseAnalyzer->isEnabled(), pulseAnalyzer && pulseAnalyzer->isOnline(), YASOLR_LBL_110);
#endif
Expand Down
20 changes: 5 additions & 15 deletions src/fn/yasolr_configure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ void yasolr_configure() {
Mycila::TaskMonitor.addTask("async_tcp"); // AsyncTCP (set stack size with CONFIG_ASYNC_TCP_STACK_SIZE)
Mycila::TaskMonitor.addTask(coreTaskManager.getName()); // YaSolR
Mycila::TaskMonitor.addTask(unsafeTaskManager.getName()); // YaSolR
Mycila::TaskMonitor.addTask(pzemTaskManager.getName()); // YaSolR

// Grid
grid.localMetrics().setExpiration(10000); // local is fast
Expand Down Expand Up @@ -57,6 +56,9 @@ void yasolr_configure() {
// HARDWARE //
//////////////

router.localMetrics().setExpiration(10000); // local is fast
router.remoteMetrics().setExpiration(10000); // remote JSY is fast

// output1
output1.config.calibratedResistance = config.getFloat(KEY_OUTPUT1_RESISTANCE);
output1.config.autoDimmer = config.getBool(KEY_ENABLE_OUTPUT1_AUTO_DIMMER);
Expand All @@ -69,6 +71,7 @@ void yasolr_configure() {
output1.config.weekDays = config.get(KEY_OUTPUT1_DAYS);
output1.config.reservedExcessPowerRatio = config.getFloat(KEY_OUTPUT1_RESERVED_EXCESS) / 100;
output1.temperature().setExpiration(YASOLR_MQTT_MEASUREMENT_EXPIRATION); // local or through mqtt
output1.localMetrics().setExpiration(10000); // local is fast

// output2
output2.config.calibratedResistance = config.getFloat(KEY_OUTPUT2_RESISTANCE);
Expand All @@ -82,6 +85,7 @@ void yasolr_configure() {
output2.config.weekDays = config.get(KEY_OUTPUT2_DAYS);
output2.config.reservedExcessPowerRatio = config.getFloat(KEY_OUTPUT2_RESERVED_EXCESS) / 100;
output2.temperature().setExpiration(YASOLR_MQTT_MEASUREMENT_EXPIRATION); // local or through mqtt
output2.localMetrics().setExpiration(10000); // local is fast

// Home Assistant Discovery
haDiscovery.setDiscoveryTopic(config.getString(KEY_HA_DISCOVERY_TOPIC));
Expand Down Expand Up @@ -112,12 +116,6 @@ void yasolr_configure() {
routerRelay1.setLoad(config.getLong(KEY_RELAY1_LOAD));
routerRelay2.setLoad(config.getLong(KEY_RELAY2_LOAD));

// Electricity: PZEMs
if (config.getBool(KEY_ENABLE_OUTPUT1_PZEM))
pzemO1.begin(YASOLR_PZEM_SERIAL, config.getLong(KEY_PIN_PZEM_RX), config.getLong(KEY_PIN_PZEM_TX), YASOLR_PZEM_ADDRESS_OUTPUT1);
if (config.getBool(KEY_ENABLE_OUTPUT2_PZEM))
pzemO2.begin(YASOLR_PZEM_SERIAL, config.getLong(KEY_PIN_PZEM_RX), config.getLong(KEY_PIN_PZEM_TX), YASOLR_PZEM_ADDRESS_OUTPUT2);

// Dimmers
dimmerO1.setDutyCycleMin(config.getFloat(KEY_OUTPUT1_DIMMER_MIN) / 100);
dimmerO1.setDutyCycleMax(config.getFloat(KEY_OUTPUT1_DIMMER_MAX) / 100);
Expand All @@ -144,9 +142,6 @@ void yasolr_configure() {
mqttPublishStaticTask.setEnabledWhen([]() { return mqtt.isConnected(); });
mqttPublishConfigTask.setEnabledWhen([]() { return mqtt.isConnected(); });

// pzemTaskManager
pzemTask.setEnabledWhen([]() { return (pzemO1.isEnabled() || pzemO2.isEnabled()) && pzemO1PairingTask.isPaused() && pzemO2PairingTask.isPaused(); });

// coreTaskManager
calibrationTask.setManager(coreTaskManager);
networkStartTask.setManager(coreTaskManager);
Expand All @@ -166,11 +161,6 @@ void yasolr_configure() {
mqttPublishConfigTask.setManager(unsafeTaskManager);
mqttPublishStaticTask.setManager(unsafeTaskManager);
mqttPublishTask.setManager(unsafeTaskManager);
pzemO1PairingTask.setManager(unsafeTaskManager);
pzemO2PairingTask.setManager(unsafeTaskManager);

// pzemTaskManager
pzemTask.setManager(pzemTaskManager);

// Router
router.addOutput(output1);
Expand Down
30 changes: 0 additions & 30 deletions src/fn/yasolr_event_listeners.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,34 +90,4 @@ void yasolr_event_listeners() {
logger.info(TAG, "Relay 2 changed to %s", state ? "ON" : "OFF");
mqttPublishTask.requestEarlyRun();
});

pzemO1.setCallback([](const Mycila::PZEM::EventType eventType) {
if (eventType == Mycila::PZEM::EventType::EVT_READ) {
grid.pzemMetrics().update({
.apparentPower = NAN,
.current = NAN,
.energy = NAN,
.energyReturned = NAN,
.frequency = pzemO1.data.frequency,
.power = NAN,
.powerFactor = NAN,
.voltage = pzemO1.data.voltage,
});
}
});

pzemO2.setCallback([](const Mycila::PZEM::EventType eventType) {
if (eventType == Mycila::PZEM::EventType::EVT_READ) {
grid.pzemMetrics().update({
.apparentPower = NAN,
.current = NAN,
.energy = NAN,
.energyReturned = NAN,
.frequency = pzemO2.data.frequency,
.power = NAN,
.powerFactor = NAN,
.voltage = pzemO2.data.voltage,
});
}
});
};
16 changes: 10 additions & 6 deletions src/fn/yasolr_frequency.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ float yasolr_frequency() {
if (frequency > 0)
return frequency;

frequency = round(pzemO1.data.frequency);
if (frequency > 0)
return frequency;
if (pzemO1) {
frequency = round(pzemO1->data.frequency);
if (frequency > 0)
return frequency;
}

frequency = round(pzemO2.data.frequency);
if (frequency > 0)
return frequency;
if (pzemO2) {
frequency = round(pzemO2->data.frequency);
if (frequency > 0)
return frequency;
}

frequency = config.getFloat(KEY_GRID_FREQUENCY);
if (frequency)
Expand Down
4 changes: 2 additions & 2 deletions src/fn/yasolr_start_display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ void yasolr_start_display() {
case DisplayKind::DISPLAY_OUTPUT1: {
display->home.printf("Output 1: %11.11s\n", output1.getStateName());
display->home.printf("Resistance: %4d Ohms\n", static_cast<int>(round(output1.config.calibratedResistance)));
display->home.printf("Dimmer: %3d %% %4d W\n", static_cast<int>(round(output1.getDimmerDutyCycleLive() * 100)), static_cast<int>(round(output1.getOutputPower())));
display->home.printf("Dimmer: %3d %% %4d W\n", static_cast<int>(round(output1.getDimmerDutyCycleLive() * 100)), static_cast<int>(round(output1.getOutputPower().value_or(0))));
if (output1.temperature())
display->home.printf("Temperature: %4.1f ", output1.temperature().get());
else
Expand All @@ -117,7 +117,7 @@ void yasolr_start_display() {
case DisplayKind::DISPLAY_OUTPUT2: {
display->home.printf("Output 2: %11.11s\n", output2.getStateName());
display->home.printf("Resistance: %4d Ohms\n", static_cast<int>(round(output2.config.calibratedResistance)));
display->home.printf("Dimmer: %3d %% %4d W\n", static_cast<int>(round(output2.getDimmerDutyCycleLive() * 100)), static_cast<int>(round(output2.getOutputPower())));
display->home.printf("Dimmer: %3d %% %4d W\n", static_cast<int>(round(output2.getDimmerDutyCycleLive() * 100)), static_cast<int>(round(output2.getOutputPower().value_or(0))));
if (output2.temperature())
display->home.printf("Temperature: %4.1f ", output2.temperature().get());
else
Expand Down
Loading

0 comments on commit fc013a6

Please sign in to comment.