Skip to content

Commit

Permalink
过载保护
Browse files Browse the repository at this point in the history
  • Loading branch information
qlwz committed Feb 19, 2020
1 parent 14807e9 commit 89c232b
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 33 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [x] MQTT服务器连接控制
- [x] 通过MQTT连入Home Assistant
- [x] 电压/电流/功率/视在功率/无功功率/功率因数/用电量统计(不做任何精度保证)
- [x] 过载保护


## 拆机接线及烧录固件相关
Expand Down
19 changes: 13 additions & 6 deletions include/CSE7766.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
#define CSE_PREF 1000
#define CSE_UREF 100

#define USE_ENERGY_MARGIN_DETECTION // Add support for Energy Margin detection (+1k6 code)
#define USE_ENERGY_POWER_LIMIT // Add additional support for Energy Power Limit detection (+1k2 code
#define ENERGY_WATCHDOG 4 // Allow up to 4 seconds before deciding no valid data present

const uint16_t MAX_POWER_HOLD = 10; // Time in SECONDS to allow max agreed power
const uint16_t MAX_POWER_WINDOW = 30; // Time in SECONDS to disable allow max agreed power
const uint8_t MAX_POWER_RETRY = 5; // Retry count allowing agreed power limit overflow

typedef struct _CSE
{
long voltage_cycle = 0;
Expand All @@ -36,17 +38,22 @@ typedef struct _ENERGY
float reactive_power = NAN; // 123.1 VAr
float power_factor = NAN; // 0.12

float daily = 0; // 123.123 kWh
float total = 0; // 12345.12345 kWh total energy
float daily = 0; // 123.123 kWh
float total = 0; // 12345.12345 kWh total energy

unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily
unsigned long kWhtoday_delta = 0; // 1212312345 Wh 10^-5 (deca micro Watt hours) - Overflows to Energy.kWhtoday (HLW and CSE only)
unsigned long kWhtoday; // 12312312 Wh * 10^-2 (deca milli Watt hours) - 5764 = 0.05764 kWh = 0.058 kWh = Energy.daily

uint8_t data_valid = 0;

uint16_t power_history[3] = {0};
uint8_t power_steady_counter = 8; // Allow for power on stabilization
bool power_delta = false;

uint16_t mplh_counter = 0;
uint16_t mplw_counter = 0;
uint8_t mplr_counter = 0;
uint8_t mplv_counter = 0;
} ENERGY;

const uint32_t HLW_PREF_PULSE = 12530; // was 4975us = 201Hz = 1000W
Expand Down
8 changes: 5 additions & 3 deletions include/DC1.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class DC1 : public Module
void energyUpdate();
void energyUpdateToday();
void energyMarginCheck();
void energyMaxPower();
void energyShow(bool isMqtt);

String powerTopic;
Expand All @@ -74,7 +75,7 @@ class DC1 : public Module
// 按键
uint8_t buttonDebounceTime = 50;
uint16_t buttonLongPressTime = 2000; // 2000 = 2s
bool buttonTiming[4] = {false, false, false, false};
uint8_t buttonTiming = 0;
unsigned long buttonTimingStart[4] = {0, 0, 0, 0};
uint8_t buttonAction[4] = {0, 0, 0, 0}; // 0 = 没有要执行的动作, 1 = 执行短按动作, 2 = 执行长按动作
void checkButton(uint8_t ch);
Expand All @@ -90,13 +91,14 @@ class DC1 : public Module

public:
DC1ConfigMessage config;
bool lastState[4] = {false, false, false, false};
uint8_t lastState = 0;
uint8_t lastState2 = 0;
uint8_t channels = 0;

void init();
String getModuleName() { return F("dc1"); }
String getModuleCNName() { return F("DC1插线板"); }
String getModuleVersion() { return F("2020.02.17.2000"); }
String getModuleVersion() { return F("2020.02.19.1500"); }
String getModuleAuthor() { return F("情留メ蚊子"); }
bool moduleLed();

Expand Down
7 changes: 4 additions & 3 deletions include/DC1Config.pb.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.3.9.4 at Sun Feb 16 09:37:42 2020. */
/* Generated by nanopb-0.3.9.4 at Wed Feb 19 10:49:10 2020. */

#ifndef PB_DC1CONFIG_PB_H_INCLUDED
#define PB_DC1CONFIG_PB_H_INCLUDED
Expand Down Expand Up @@ -32,14 +32,15 @@ typedef struct _DC1ConfigMessage {
uint64_t energy_kWhtotal;
uint16_t energy_kWhdoy;
uint32_t energy_kWhtotal_time;
uint16_t energy_max_power;
/* @@protoc_insertion_point(struct:DC1ConfigMessage) */
} DC1ConfigMessage;

/* Struct field encoding specification for nanopb */
extern const pb_field_t DC1ConfigMessage_fields[17];
extern const pb_field_t DC1ConfigMessage_fields[18];

/* Maximum encoded size of messages (where known) */
#define DC1ConfigMessage_size 106
#define DC1ConfigMessage_size 113

/* Message IDs (where set with "msgid" option) */
#ifdef PB_MSGID
Expand Down
3 changes: 3 additions & 0 deletions lib/esp_framework/src/Framework.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,15 @@ void Framework::loop()
{
yield();
Led::loop();
yield();
Mqtt::loop();
yield();
module->loop();
yield();
Wifi::loop();
yield();
Http::loop();
yield();
Rtc::loop();
}
}
1 change: 1 addition & 0 deletions nanopb/DC1Config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ message DC1ConfigMessage {
uint32 energy_kWhtotal = 27 [(nanopb).int_size = IS_64];
uint32 energy_kWhdoy = 28 [(nanopb).int_size = IS_16];
uint32 energy_kWhtotal_time = 29 [(nanopb).int_size = IS_32];
uint32 energy_max_power = 30 [(nanopb).int_size = IS_16];

}
126 changes: 107 additions & 19 deletions src/DC1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ void DC1::init()
switchRelay(ch, config.power_on_state == 1, false); // 开关通电时闭合
}
// 总开关关时跳过其他
if (ch == 0 && !lastState[0] && config.sub_kinkage != 0)
if (ch == 0 && !bitRead(lastState, 0) && config.sub_kinkage != 0)
{
break;
}
Expand Down Expand Up @@ -113,6 +113,7 @@ void DC1::resetConfig()
config.sub_kinkage = 2;
config.energy_power_delta = 10;
config.report_interval = 60;
config.energy_max_power = 2300;
}

void DC1::saveConfig(bool isEverySecond)
Expand All @@ -132,19 +133,19 @@ void DC1::mqttCallback(String topicStr, String str)
{
if (channels >= 1 && topicStr.endsWith("/POWER") || topicStr.endsWith("/POWER1"))
{
switchRelay(0, (str == "ON" ? true : (str == "OFF" ? false : !DC1::lastState[0])));
switchRelay(0, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 0))));
}
else if (channels >= 2 && topicStr.endsWith("/POWER2"))
{
switchRelay(1, (str == "ON" ? true : (str == "OFF" ? false : !DC1::lastState[1])));
switchRelay(1, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 1))));
}
else if (channels >= 3 && topicStr.endsWith("/POWER3"))
{
switchRelay(2, (str == "ON" ? true : (str == "OFF" ? false : !DC1::lastState[2])));
switchRelay(2, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 2))));
}
else if (channels >= 4 && topicStr.endsWith("/POWER4"))
{
switchRelay(3, (str == "ON" ? true : (str == "OFF" ? false : !DC1::lastState[3])));
switchRelay(3, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, 3))));
}
else if (topicStr.endsWith("/clear"))
{
Expand Down Expand Up @@ -248,7 +249,7 @@ String DC1::httpGetStatus(ESP8266WebServer *server)
for (size_t ch = 0; ch < channels; ch++)
{
data += ",\"POWER" + String(ch + 1) + "\":";
data += lastState[ch] ? 1 : 0;
data += bitRead(lastState, ch) ? 1 : 0;
}
energyShow(false);
data += String(tmpData);
Expand All @@ -265,7 +266,7 @@ void DC1::httpHtml(ESP8266WebServer *server)
{
page += F(" <button type='button' style='width:50px' onclick=\"ajaxPost('/dc1_do', 'do=T&c={ch}');\" id='POWER{ch}' ");
page.replace(F("{ch}"), String(ch + 1));
if (lastState[ch])
if (bitRead(lastState, ch))
{
page += F("class='btn-success'>开</button>");
}
Expand Down Expand Up @@ -340,6 +341,9 @@ void DC1::httpHtml(ESP8266WebServer *server)
page += F("<tr><td>功率波动</td><td><input type='number' min='0' max='4000' name='energy_power_delta' required value='{v}'>&nbsp;0关闭,1-100为%,>100是差值(-100)</td></tr>");
page.replace(F("{v}"), String(config.energy_power_delta));

page += F("<tr><td>过载保护</td><td><input type='number' min='0' max='2600' name='energy_max_power' required value='{v}'>&nbsp;W&nbsp;&nbsp;&nbsp;&nbsp;0关闭</td></tr>");
page.replace(F("{v}"), String(config.energy_max_power));

page += F("<tr><td colspan='2'><button type='submit' class='btn-info'>设置</button><br>");
page += F("<button type='button' class='btn-success' style='margin-top:10px' onclick='window.location.href=\"/ha\"'>下载HA配置文件</button><br>");
page += F("<button type='button' class='btn-danger' style='margin-top:10px' onclick=\"javascript:if(confirm('确定要重置用电量?')){ajaxPost('/dc1_setting', 'c=1');}\">重置用电量</button></td></tr>");
Expand All @@ -365,7 +369,7 @@ void DC1::httpDo(ESP8266WebServer *server)
return;
}
String str = server->arg(F("do"));
switchRelay(ch, (str == "ON" ? true : (str == "OFF" ? false : !DC1::lastState[ch])));
switchRelay(ch, (str == "ON" ? true : (str == "OFF" ? false : !bitRead(lastState, ch))));

server->send(200, F("text/html"), "{\"code\":1,\"msg\":\"操作成功\",\"data\":{" + httpGetStatus(server) + "}}");
}
Expand All @@ -385,7 +389,8 @@ void DC1::httpSetting(ESP8266WebServer *server)
config.sub_kinkage = server->arg(F("sub_kinkage")).toInt();

config.report_interval = server->arg(F("report_interval")).toInt();
config.energy_power_delta = server->arg(F("energy_power_delta")).toFloat();
config.energy_power_delta = server->arg(F("energy_power_delta")).toInt();
config.energy_max_power = server->arg(F("energy_max_power")).toInt();

logoLed();

Expand Down Expand Up @@ -457,11 +462,11 @@ void DC1::logoLed()
}
else if (config.logo_led == 2)
{
digitalWrite(LOGO_LED_PIN, lastState[0] ? LOW : HIGH);
digitalWrite(LOGO_LED_PIN, bitRead(lastState, 0) ? LOW : HIGH);
}
else if (config.logo_led == 3)
{
digitalWrite(LOGO_LED_PIN, lastState[0] ? HIGH : LOW);
digitalWrite(LOGO_LED_PIN, bitRead(lastState, 0) ? HIGH : LOW);
}
}

Expand All @@ -475,7 +480,7 @@ void DC1::switchRelay(uint8_t ch, bool isOn, bool isSave)

if (ch > 0 || (ch == 0 && config.sub_kinkage == 0))
{
if (!lastState[0] && isOn && config.sub_kinkage != 0)
if (!bitRead(lastState, 0) && isOn && config.sub_kinkage != 0)
{
if (config.sub_kinkage == 1 || !isSave)
{
Expand All @@ -491,7 +496,7 @@ void DC1::switchRelay(uint8_t ch, bool isOn, bool isSave)
{
for (size_t ch2 = (config.sub_kinkage == 0 ? 0 : 1); ch2 < channels; ch2++)
{
if (ch2 != ch && lastState[ch2])
if (ch2 != ch && bitRead(lastState, ch2))
{
switchRelay(ch2, false, isSave);
}
Expand All @@ -509,7 +514,8 @@ void DC1::switchRelay(uint8_t ch, bool isOn, bool isSave)
return;
}
}
lastState[ch] = isOn;

bitWrite(lastState, ch, isOn);

Mqtt::publish((powerTopic + (ch + 1)), isOn ? "ON" : "OFF", globalConfig.mqtt.retain);

Expand Down Expand Up @@ -549,9 +555,9 @@ void DC1::checkButton(uint8_t ch)

if (buttonState == 0)
{
if (buttonTiming[ch] == false)
if (!bitRead(buttonTiming, ch))
{
buttonTiming[ch] = true;
bitSet(buttonTiming, ch);
buttonTimingStart[ch] = millis();
}
else
Expand All @@ -568,12 +574,12 @@ void DC1::checkButton(uint8_t ch)
}
else
{
buttonTiming[ch] = false;
bitClear(buttonTiming, ch);
if (buttonAction[ch] != 0)
{
if (buttonAction[ch] == 1) // 执行短按动作
{
switchRelay(ch, !lastState[ch], true);
switchRelay(ch, !bitRead(lastState, ch), true);
}
else if (buttonAction[ch] == 2) // 执行长按动作
{
Expand Down Expand Up @@ -729,6 +735,87 @@ void DC1::energyMarginCheck()
cse7766->Energy.power_delta = false;
reportEnergy();
}
if (config.energy_max_power)
{
DC1::energyMaxPower();
}
}

void DC1::energyMaxPower()
{
if (cse7766->Energy.active_power > config.energy_max_power)
{
if (!cse7766->Energy.mplh_counter)
{
cse7766->Energy.mplh_counter = MAX_POWER_HOLD;
}
else
{
cse7766->Energy.mplh_counter--;
if (!cse7766->Energy.mplh_counter)
{
Debug::AddError(PSTR("MaxPowerReached: %d"), (uint16_t)cse7766->Energy.active_power);
lastState2 = lastState;
for (size_t ch = 0; ch < channels; ch++)
{
if (bitRead(lastState, ch))
{
switchRelay(ch, false, false);
}
}
if (!cse7766->Energy.mplr_counter)
{
cse7766->Energy.mplr_counter = MAX_POWER_RETRY + 1;
}
cse7766->Energy.mplw_counter = MAX_POWER_WINDOW;
cse7766->Energy.mplv_counter = 0;
}
}
}
else if (lastState && (cse7766->Energy.active_power <= config.energy_max_power))
{
cse7766->Energy.mplh_counter = 0;
cse7766->Energy.mplw_counter = 0;

if (cse7766->Energy.mplv_counter++ == 60)
{
cse7766->Energy.mplv_counter = 0;
cse7766->Energy.mplr_counter = 0;
}
}
if (!lastState)
{
if (cse7766->Energy.mplw_counter)
{
cse7766->Energy.mplw_counter--;
}
else
{
if (cse7766->Energy.mplr_counter)
{
cse7766->Energy.mplr_counter--;
if (cse7766->Energy.mplr_counter)
{
Debug::AddError(PSTR("PowerMonitor ON"));
if (lastState2)
{
for (size_t ch = 0; ch < channels; ch++)
{
if (bitRead(lastState2, ch))
{
switchRelay(ch, true, false);
}
}
lastState2 = 0;
}
}
else
{
Debug::AddInfo(PSTR("MaxPowerReachedRetry OFF"));
}
}
}
}
}

void DC1::energyShow(bool isMqtt)
Expand Down Expand Up @@ -808,10 +895,11 @@ void DC1::reportEnergy()
energyShow(true);
Mqtt::publish(Mqtt::getTeleTopic("ENERGY"), tmpData, globalConfig.mqtt.retain);
}

void DC1::reportPower()
{
for (size_t ch = 0; ch < channels; ch++)
{
Mqtt::publish((powerTopic + (ch + 1)), lastState[ch] ? "ON" : "OFF", globalConfig.mqtt.retain);
Mqtt::publish((powerTopic + (ch + 1)), bitRead(lastState, ch) ? "ON" : "OFF", globalConfig.mqtt.retain);
}
}
Loading

0 comments on commit 89c232b

Please sign in to comment.