mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-09 09:19:51 +03:00
56
README.md
56
README.md
@@ -100,7 +100,6 @@ boiler
|
||||
wwonetime <on | off>
|
||||
wwtemp <degrees>
|
||||
read <type ID>
|
||||
temp <degrees>
|
||||
maxpower <%>
|
||||
minpower <%>
|
||||
|
||||
@@ -113,6 +112,61 @@ thermostat
|
||||
```
|
||||
|
||||
----------
|
||||
### **mqtt commands**
|
||||
|
||||
commands can be written as `{"cmd": ,"data": }` or direct.
|
||||
To set thermostat commands depending on heatingcircuits add `"hc": ` or nest the command. Without `"hc":` the first ative heatingcircuit is set.
|
||||
These commands are equivalent:
|
||||
`{"hc":2,"cmd":"daytemp","data":21}` or `{"hc":2,"daytemp":21}`or `{"hc2"{"daytemp":21}}`
|
||||
In direct commands it's possible to combine commands, i.e. `{"hc1":{"daytemp":21,"nighttemp":17},"hc2":{"daytemp":20}}`
|
||||
```
|
||||
*boiler_cmd*
|
||||
comfort <hot, eco, intelligent>
|
||||
flowtemp <degrees>
|
||||
wwtemp <degrees>
|
||||
boilhyston <degrees> (negative value)
|
||||
boilhystoff <degrees> (positive value)
|
||||
burnperiod <minutes>
|
||||
burnminpower <%>
|
||||
burnmaxpower <%>
|
||||
pumpdelay <minutes>
|
||||
|
||||
*thermostat_cmd*
|
||||
--- without hc ---
|
||||
wwmode <off | on | auto>
|
||||
calinttemp <degrees>
|
||||
minexttemp <degrees>
|
||||
building <light | medium | heavy>
|
||||
language <n> (0=de, 1=nl, 2=fr, 3=it) only RC30
|
||||
display <n> (0=int temp, 1= int set, 2=ext. temp, 3=burner, 4=ww, 5=mode, 6=time, 7=date, 8=smoke) only RC30
|
||||
clockoffset <seconds> (only RC30)
|
||||
--- with hc ---
|
||||
mode <auto | night | day | nofrost | heat | eco>
|
||||
temp <degrees>
|
||||
nighttemp <degrees>
|
||||
daytemp <degrees>
|
||||
nofrosttemp <degrees>
|
||||
ecotemp <degrees>
|
||||
heattemp <degrees>
|
||||
summertemp <degrees>
|
||||
designtemp <degrees>
|
||||
offsettemp <degrees>
|
||||
holidaytemp <degrees>
|
||||
remotetemp <degrees>
|
||||
control <0 | 1 | 2>
|
||||
pause <hours>
|
||||
party <hours>
|
||||
holiday <dd.mm.yyyy-dd.mm.yyyy>
|
||||
date <NTP | hh:mm:ss-dd.mm.yyyy-dw-dst>
|
||||
|
||||
*cmd*
|
||||
send <"0B XX XX ..">
|
||||
D0 <0 | 1>
|
||||
D1 <0 | 1>
|
||||
D2 <0 | 1>
|
||||
D3 <0 | 1>
|
||||
|
||||
```
|
||||
|
||||
### **Basic Design Principles**
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ void EMSESPDevicesService::scan_devices(AsyncWebServerRequest * request) {
|
||||
}
|
||||
|
||||
void EMSESPDevicesService::all_devices(AsyncWebServerRequest * request) {
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_STATUS_SIZE);
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_DEVICE_SIZE);
|
||||
JsonObject root = response->getRoot();
|
||||
|
||||
JsonArray devices = root.createNestedArray("devices");
|
||||
@@ -78,7 +78,7 @@ void EMSESPDevicesService::device_data(AsyncWebServerRequest * request, JsonVari
|
||||
if (json.is<JsonObject>()) {
|
||||
uint8_t id = json["id"]; // get id from selected table row
|
||||
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, 1024);
|
||||
AsyncJsonResponse * response = new AsyncJsonResponse(false, MAX_EMSESP_DEVICE_SIZE);
|
||||
#ifndef EMSESP_STANDALONE
|
||||
EMSESP::device_info(id, (JsonObject &)response->getRoot());
|
||||
#endif
|
||||
|
||||
@@ -24,7 +24,8 @@
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <SecurityManager.h>
|
||||
|
||||
#define MAX_EMSESP_STATUS_SIZE 1024
|
||||
// #define MAX_EMSESP_STATUS_SIZE 1024
|
||||
#define MAX_EMSESP_DEVICE_SIZE 1280
|
||||
|
||||
#define EMSESP_DEVICES_SERVICE_PATH "/rest/allDevices"
|
||||
#define SCAN_DEVICES_SERVICE_PATH "/rest/scanDevices"
|
||||
|
||||
@@ -30,7 +30,6 @@ MAKE_PSTR_WORD(intelligent)
|
||||
MAKE_PSTR_WORD(hot)
|
||||
MAKE_PSTR_WORD(maxpower)
|
||||
MAKE_PSTR_WORD(minpower)
|
||||
MAKE_PSTR_WORD(temp)
|
||||
|
||||
MAKE_PSTR(comfort_mandatory, "<hot | eco | intelligent>")
|
||||
|
||||
@@ -106,18 +105,50 @@ void Boiler::boiler_cmd(const char * message) {
|
||||
uint8_t t = doc["wwtemp"];
|
||||
set_warmwater_temp(t);
|
||||
}
|
||||
if (nullptr != doc["boilhyston"]) {
|
||||
int8_t t = doc["boilhyston"];
|
||||
set_hyst_on(t);
|
||||
}
|
||||
if (nullptr != doc["boilhystoff"]) {
|
||||
uint8_t t = doc["boilhystoff"];
|
||||
set_hyst_off(t);
|
||||
}
|
||||
if (nullptr != doc["burnperiod"]) {
|
||||
uint8_t t = doc["burnperiod"];
|
||||
set_burn_period(t);
|
||||
}
|
||||
if (nullptr != doc["burnminpower"]) {
|
||||
uint8_t p = doc["burnminpower"];
|
||||
set_min_power(p);
|
||||
}
|
||||
if (nullptr != doc["burnmaxpower"]) {
|
||||
uint8_t p = doc["burnmaxpower"];
|
||||
set_max_power(p);
|
||||
}
|
||||
if (nullptr != doc["pumpdelay"]) {
|
||||
uint8_t t = doc["pumpdelay"];
|
||||
set_pump_delay(t);
|
||||
}
|
||||
|
||||
if (nullptr != doc["comfort"]) {
|
||||
const char * data = doc["comfort"];
|
||||
if (strcmp((char *)data, "hot") == 0) {
|
||||
set_warmwater_mode(1);
|
||||
} else if (strcmp((char *)data, "eco") == 0) {
|
||||
set_warmwater_mode(2);
|
||||
} else if (strcmp((char *)data, "intelligent") == 0) {
|
||||
set_warmwater_mode(3);
|
||||
}
|
||||
}
|
||||
|
||||
const char * command = doc["cmd"];
|
||||
if (command == nullptr) {
|
||||
if (command == nullptr || doc["data"] == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// boiler ww comfort setting
|
||||
if (strcmp(command, "comfort") == 0) {
|
||||
const char * data = doc["data"];
|
||||
if (data == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (strcmp((char *)data, "hot") == 0) {
|
||||
set_warmwater_mode(1);
|
||||
} else if (strcmp((char *)data, "eco") == 0) {
|
||||
@@ -131,36 +162,45 @@ void Boiler::boiler_cmd(const char * message) {
|
||||
// boiler flowtemp setting
|
||||
if (strcmp(command, "flowtemp") == 0) {
|
||||
uint8_t t = doc["data"];
|
||||
if (t) {
|
||||
set_flow_temp(t);
|
||||
}
|
||||
set_flow_temp(t);
|
||||
return;
|
||||
}
|
||||
|
||||
// boiler temp setting
|
||||
if (strcmp(command, "temp") == 0) {
|
||||
if (strcmp(command, "wwtemp") == 0) {
|
||||
uint8_t t = doc["data"];
|
||||
if (t) {
|
||||
set_temp(t);
|
||||
}
|
||||
set_warmwater_temp(t);
|
||||
return;
|
||||
}
|
||||
|
||||
// boiler max power setting
|
||||
if (strcmp(command, "maxpower") == 0) {
|
||||
if (strcmp(command, "burnmaxpower") == 0) {
|
||||
uint8_t p = doc["data"];
|
||||
if (p) {
|
||||
set_max_power(p);
|
||||
}
|
||||
set_max_power(p);
|
||||
return;
|
||||
}
|
||||
|
||||
// boiler min power setting
|
||||
if (strcmp(command, "minpower") == 0) {
|
||||
if (strcmp(command, "burnminpower") == 0) {
|
||||
uint8_t p = doc["data"];
|
||||
if (p) {
|
||||
set_min_power(p);
|
||||
}
|
||||
set_min_power(p);
|
||||
return;
|
||||
}
|
||||
if (strcmp(command, "boilhyston") == 0) {
|
||||
int8_t t = doc["data"];
|
||||
set_hyst_on(t);
|
||||
return;
|
||||
}
|
||||
if (strcmp(command, "boilhystoff") == 0) {
|
||||
uint8_t t = doc["data"];
|
||||
set_hyst_off(t);
|
||||
return;
|
||||
}
|
||||
if (strcmp(command, "burnperiod") == 0) {
|
||||
uint8_t t = doc["data"];
|
||||
set_burn_period(t);
|
||||
return;
|
||||
}
|
||||
if (strcmp(command, "pumpdelay") == 0) {
|
||||
uint8_t t = doc["data"];
|
||||
set_pump_delay(t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -216,7 +256,7 @@ void Boiler::device_info(JsonArray & root) {
|
||||
|
||||
// publish values via MQTT
|
||||
void Boiler::publish_values() {
|
||||
const size_t capacity = JSON_OBJECT_SIZE(50); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
|
||||
const size_t capacity = JSON_OBJECT_SIZE(56); // must recalculate if more objects addded https://arduinojson.org/v6/assistant/
|
||||
DynamicJsonDocument doc(capacity);
|
||||
|
||||
char s[10]; // for formatting strings
|
||||
@@ -328,7 +368,7 @@ void Boiler::publish_values() {
|
||||
doc["flameCurr"] = (float)(int16_t)flameCurr_ / 10;
|
||||
}
|
||||
if (Helpers::hasValue(heatPmp_, VALUE_BOOL)) {
|
||||
doc["heatPmp"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
|
||||
doc["heatPump"] = Helpers::render_value(s, heatPmp_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (Helpers::hasValue(fanWork_, VALUE_BOOL)) {
|
||||
doc["fanWork"] = Helpers::render_value(s, fanWork_, EMS_VALUE_BOOL);
|
||||
@@ -340,13 +380,37 @@ void Boiler::publish_values() {
|
||||
doc["wWHeat"] = Helpers::render_value(s, wWHeat_, EMS_VALUE_BOOL);
|
||||
}
|
||||
if (Helpers::hasValue(heating_temp_)) {
|
||||
doc["heating_temp"] = heating_temp_;
|
||||
doc["heatingTemp"] = heating_temp_;
|
||||
}
|
||||
if (Helpers::hasValue(pump_mod_max_)) {
|
||||
doc["pump_mod_max"] = pump_mod_max_;
|
||||
doc["pumpModMax"] = pump_mod_max_;
|
||||
}
|
||||
if (Helpers::hasValue(pump_mod_min_)) {
|
||||
doc["pump_mod_min"] = pump_mod_min_;
|
||||
doc["pumpModMin"] = pump_mod_min_;
|
||||
}
|
||||
if (Helpers::hasValue(pumpDelay_)) {
|
||||
doc["pumpDelay"] = pumpDelay_;
|
||||
}
|
||||
if (Helpers::hasValue(burnPeriod_)) {
|
||||
doc["burnMinPeriod"] = burnPeriod_;
|
||||
}
|
||||
if (Helpers::hasValue(burnPowermin_)) {
|
||||
doc["burnMinPower"] = burnPowermin_;
|
||||
}
|
||||
if (Helpers::hasValue(burnPowermax_)) {
|
||||
doc["burnMaxPower"] = burnPowermax_;
|
||||
}
|
||||
if (Helpers::hasValue(boilTemp_on_)) {
|
||||
doc["boilHystOn"] = boilTemp_on_;
|
||||
}
|
||||
if (Helpers::hasValue(boilTemp_off_)) {
|
||||
doc["boilHystOff"] = boilTemp_off_;
|
||||
}
|
||||
if (Helpers::hasValue(setFlowTemp_)) {
|
||||
doc["setFlowTemp"] = setFlowTemp_;
|
||||
}
|
||||
if (Helpers::hasValue(setWWPumpPow_)) {
|
||||
doc["wWSetPumpPower"] = setWWPumpPow_;
|
||||
}
|
||||
if (Helpers::hasValue(wWStarts_)) {
|
||||
doc["wWStarts"] = wWStarts_;
|
||||
@@ -366,17 +430,6 @@ void Boiler::publish_values() {
|
||||
if (Helpers::hasValue(heatWorkMin_)) {
|
||||
doc["heatWorkMin"] = heatWorkMin_;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(temp_)) {
|
||||
doc["heatWorkMin"] = temp_;
|
||||
}
|
||||
if (Helpers::hasValue(maxpower_)) {
|
||||
doc["heatWorkMin"] = maxpower_;
|
||||
}
|
||||
if (Helpers::hasValue(setpointpower_)) {
|
||||
doc["heatWorkMin"] = setpointpower_;
|
||||
}
|
||||
|
||||
if (Helpers::hasValue(serviceCode_)) {
|
||||
doc["serviceCode"] = serviceCodeChar_;
|
||||
doc["serviceCodeNumber"] = serviceCode_;
|
||||
@@ -474,11 +527,17 @@ void Boiler::show_values(uuid::console::Shell & shell) {
|
||||
print_value(shell, 2, F("Heating temperature setting on the boiler"), heating_temp_, F_(degrees));
|
||||
print_value(shell, 2, F("Boiler circuit pump modulation max power"), pump_mod_max_, F_(percent));
|
||||
print_value(shell, 2, F("Boiler circuit pump modulation min power"), pump_mod_min_, F_(percent));
|
||||
print_value(shell, 2, F("Boiler circuit pump delay time"), pumpDelay_, F("min"));
|
||||
print_value(shell, 2, F("Boiler temp hysteresis on"), boilTemp_on_, F_(degrees));
|
||||
print_value(shell, 2, F("Boiler temp hysteresis off"), boilTemp_off_, F_(degrees));
|
||||
print_value(shell, 2, F("Boiler burner min period"), burnPeriod_, F("min"));
|
||||
print_value(shell, 2, F("Boiler burner min power"), burnPowermin_, F_(percent));
|
||||
print_value(shell, 2, F("Boiler burner max power"), burnPowermax_, F_(percent));
|
||||
|
||||
// UBASetPoint - these may differ from the above
|
||||
print_value(shell, 2, F("Boiler temp"), temp_, F_(degrees));
|
||||
print_value(shell, 2, F("Max output power"), maxpower_, F_(percent));
|
||||
print_value(shell, 2, F("Set power"), setpointpower_, F_(percent));
|
||||
print_value(shell, 2, F("Set Flow temperature"), setFlowTemp_, F_(degrees));
|
||||
print_value(shell, 2, F("Boiler burner set power"), setBurnPow_, F_(percent));
|
||||
print_value(shell, 2, F("Warm Water pump set power"), setWWPumpPow_, F_(percent));
|
||||
|
||||
// UBAMonitorSlow
|
||||
if (Helpers::hasValue(extTemp_)) {
|
||||
@@ -591,6 +650,12 @@ void Boiler::process_UBATotalUptime(std::shared_ptr<const Telegram> telegram) {
|
||||
*/
|
||||
void Boiler::process_UBAParameters(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(heating_temp_, 1);
|
||||
telegram->read_value(burnPowermax_,2);
|
||||
telegram->read_value(burnPowermin_,3);
|
||||
telegram->read_value(boilTemp_off_,4);
|
||||
telegram->read_value(boilTemp_on_,5);
|
||||
telegram->read_value(burnPeriod_,6);
|
||||
telegram->read_value(pumpDelay_,8);
|
||||
telegram->read_value(pump_mod_max_, 9);
|
||||
telegram->read_value(pump_mod_min_, 10);
|
||||
}
|
||||
@@ -717,9 +782,9 @@ void Boiler::process_UBAOutdoorTemp(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
// UBASetPoint 0x1A
|
||||
void Boiler::process_UBASetPoints(std::shared_ptr<const Telegram> telegram) {
|
||||
telegram->read_value(temp_, 0); // boiler flow temp
|
||||
telegram->read_value(maxpower_, 1); // max output power in %
|
||||
telegram->read_value(setpointpower_, 14); // ww pump speed/power?
|
||||
telegram->read_value(setFlowTemp_, 0); // boiler set temp from thermostat
|
||||
telegram->read_value(setBurnPow_, 1); // max output power in %
|
||||
telegram->read_value(setWWPumpPow_, 2); // ww pump speed/power?
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
@@ -766,12 +831,6 @@ void Boiler::set_flow_temp(const uint8_t temperature) {
|
||||
write_command(EMS_TYPE_UBASetPoints, 0, temperature);
|
||||
}
|
||||
|
||||
// set heating temp
|
||||
void Boiler::set_temp(const uint8_t temperature) {
|
||||
LOG_INFO(F("Setting boiler temperature to %d C"), temperature);
|
||||
write_command(EMS_TYPE_UBAParameters, 1, temperature);
|
||||
}
|
||||
|
||||
// set min boiler output
|
||||
void Boiler::set_min_power(const uint8_t power) {
|
||||
LOG_INFO(F("Setting boiler min power to "), power);
|
||||
@@ -784,6 +843,30 @@ void Boiler::set_max_power(const uint8_t power) {
|
||||
write_command(EMS_TYPE_UBAParameters, 2, power);
|
||||
}
|
||||
|
||||
// set oiler on hysteresis
|
||||
void Boiler::set_hyst_on(const uint8_t temp) {
|
||||
LOG_INFO(F("Setting boiler hysteresis on to %d C"), temp);
|
||||
write_command(EMS_TYPE_UBAParameters, 5, temp);
|
||||
}
|
||||
|
||||
// set boiler off hysteresis
|
||||
void Boiler::set_hyst_off(const uint8_t temp) {
|
||||
LOG_INFO(F("Setting boiler hysteresis off to %d C"), temp);
|
||||
write_command(EMS_TYPE_UBAParameters, 4, temp);
|
||||
}
|
||||
|
||||
// set min burner period
|
||||
void Boiler::set_burn_period(const uint8_t t) {
|
||||
LOG_INFO(F("Setting burner min. period to %d min"), t);
|
||||
write_command(EMS_TYPE_UBAParameters, 6, t);
|
||||
}
|
||||
|
||||
// set pump delay
|
||||
void Boiler::set_pump_delay(const uint8_t t) {
|
||||
LOG_INFO(F("Setting boiler pump delay to %d min"), t);
|
||||
write_command(EMS_TYPE_UBAParameters, 8, t);
|
||||
}
|
||||
|
||||
// 1=hot, 2=eco, 3=intelligent
|
||||
// note some boilers do not have this setting, than it's done by thermostat
|
||||
// on a RC35 it's by EMSESP::send_write_request(0x37, 0x10, 2, &set, 1, 0); (set is 1,2,3)
|
||||
@@ -888,14 +971,6 @@ void Boiler::console_commands(Shell & shell, unsigned int context) {
|
||||
set_flow_temp(Helpers::atoint(arguments.front().c_str()));
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(temp)},
|
||||
flash_string_vector{F_(degrees_mandatory)},
|
||||
[=](Shell & shell __attribute__((unused)), const std::vector<std::string> & arguments) {
|
||||
set_temp(Helpers::atoint(arguments.front().c_str()));
|
||||
});
|
||||
|
||||
EMSESPShell::commands->add_command(ShellContext::BOILER,
|
||||
CommandFlags::ADMIN,
|
||||
flash_string_vector{F_(maxpower)},
|
||||
|
||||
@@ -121,11 +121,17 @@ class Boiler : public EMSdevice {
|
||||
uint8_t heating_temp_ = EMS_VALUE_UINT_NOTSET; // Heating temperature setting on the boiler
|
||||
uint8_t pump_mod_max_ = EMS_VALUE_UINT_NOTSET; // Boiler circuit pump modulation max. power %
|
||||
uint8_t pump_mod_min_ = EMS_VALUE_UINT_NOTSET; // Boiler circuit pump modulation min. power
|
||||
uint8_t burnPowermin_ = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t burnPowermax_ = EMS_VALUE_UINT_NOTSET;
|
||||
int8_t boilTemp_off_ = EMS_VALUE_INT_NOTSET;
|
||||
int8_t boilTemp_on_ = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t burnPeriod_ = EMS_VALUE_UINT_NOTSET;
|
||||
uint8_t pumpDelay_ = EMS_VALUE_UINT_NOTSET;
|
||||
|
||||
// UBASetPoint
|
||||
uint8_t temp_ = EMS_VALUE_UINT_NOTSET; // boiler flow temp
|
||||
uint8_t maxpower_ = EMS_VALUE_UINT_NOTSET; // max output power in %
|
||||
uint8_t setpointpower_ = EMS_VALUE_UINT_NOTSET; // ww pump speed/power?
|
||||
uint8_t setFlowTemp_ = EMS_VALUE_UINT_NOTSET; // boiler setpoint temp
|
||||
uint8_t setBurnPow_ = EMS_VALUE_UINT_NOTSET; // max output power in %
|
||||
uint8_t setWWPumpPow_ = EMS_VALUE_UINT_NOTSET; // ww pump speed/power?
|
||||
|
||||
// other internal calculated params
|
||||
uint8_t tap_water_active_ = EMS_VALUE_BOOL_NOTSET; // Hot tap water is on/off
|
||||
@@ -161,9 +167,12 @@ class Boiler : public EMSdevice {
|
||||
void set_tapwarmwater_activated(const bool activated);
|
||||
void set_warmwater_onetime(const bool activated);
|
||||
void set_warmwater_circulation(const bool activated);
|
||||
void set_temp(const uint8_t temperature);
|
||||
void set_min_power(const uint8_t power);
|
||||
void set_max_power(const uint8_t power);
|
||||
void set_hyst_on(const uint8_t temp);
|
||||
void set_hyst_off(const uint8_t temp);
|
||||
void set_burn_period(const uint8_t t);
|
||||
void set_pump_delay(const uint8_t t);
|
||||
|
||||
|
||||
// mqtt callbacks
|
||||
|
||||
@@ -266,11 +266,14 @@ void Solar::process_ISM1StatusMessage(std::shared_ptr<const Telegram> telegram)
|
||||
if (Wh != 0xFFFF) {
|
||||
energyLastHour_ = Wh * 10; // set to *10
|
||||
}
|
||||
telegram->read_bitvalue(pump_, 8, 0); // Solar pump on (1) or off (0)
|
||||
telegram->read_value(pumpWorkMin_, 10, 3); // force to 3 bytes
|
||||
telegram->read_bitvalue(collectorOnOff_, 9, 0); // collector shutdown on/off
|
||||
telegram->read_bitvalue(tankHeated_, 9, 2); // tank full
|
||||
}
|
||||
|
||||
/*
|
||||
* Junkers ISM1 Solar Module - type 0x0101 EMS+ for setting values
|
||||
* e.g. 90 30 FF 06 00 01 50
|
||||
*/
|
||||
void Solar::process_ISM1Set(std::shared_ptr<const Telegram> telegram) {
|
||||
|
||||
@@ -207,7 +207,17 @@ void Thermostat::device_info(JsonArray & root) {
|
||||
std::string mode_str(15, '\0');
|
||||
snprintf_P(&mode_str[0], mode_str.capacity() + 1, PSTR("%sMode"), hc_str.c_str());
|
||||
dataElement["name"] = mode_str;
|
||||
dataElement["value"] = mode_tostring(hc->get_mode(flags));
|
||||
std::string modetype_str(20, '\0');
|
||||
if (Helpers::hasValue(hc->summer_mode) && hc->summer_mode) {
|
||||
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - summer"), mode_tostring(hc->get_mode(flags)).c_str());
|
||||
} else if (Helpers::hasValue(hc->holiday_mode) && hc->holiday_mode) {
|
||||
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - holiday"), mode_tostring(hc->get_mode(flags)).c_str());
|
||||
} else if (Helpers::hasValue(hc->mode_type)) {
|
||||
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, PSTR("%s - %s"), mode_tostring(hc->get_mode(flags)).c_str(), mode_tostring(hc->get_mode_type(flags)).c_str());
|
||||
} else {
|
||||
snprintf_P(&modetype_str[0], modetype_str.capacity() + 1, mode_tostring(hc->get_mode(flags)).c_str());
|
||||
}
|
||||
dataElement["value"] = modetype_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -301,7 +311,7 @@ void Thermostat::thermostat_cmd(const char * message) {
|
||||
set_holiday(holiday.c_str(), hc_num);
|
||||
}
|
||||
}
|
||||
|
||||
// commands without heatingcircuit
|
||||
if (nullptr != doc["wwmode"]) {
|
||||
std::string mode = doc["wwmode"];
|
||||
set_ww_mode(mode);
|
||||
@@ -406,10 +416,10 @@ void Thermostat::thermostat_cmd(const char * message) {
|
||||
|
||||
// check for commands like {"hc":2,"cmd":"temp","data":21}
|
||||
const char * command = doc["cmd"];
|
||||
if (command == nullptr) {
|
||||
if (command == nullptr || doc["data"] == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ok, we have command and data
|
||||
if (strcmp(command, "temp") == 0) {
|
||||
float f = doc["data"];
|
||||
if (f) {
|
||||
@@ -595,7 +605,8 @@ void Thermostat::publish_values() {
|
||||
}
|
||||
|
||||
// send this specific data using the thermostat_data topic
|
||||
if ((mqtt_format_ == MQTT_format::SINGLE) || (mqtt_format_ == MQTT_format::HA)) {
|
||||
// if ((mqtt_format_ == MQTT_format::SINGLE) || (mqtt_format_ == MQTT_format::HA)) {
|
||||
if (mqtt_format_ != MQTT_format::NESTED) {
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
rootThermostat = doc.to<JsonObject>(); // clear object
|
||||
}
|
||||
@@ -609,7 +620,8 @@ void Thermostat::publish_values() {
|
||||
|
||||
has_data = true;
|
||||
// if the MQTT format is 'nested' or 'ha' then create the parent object hc<n>
|
||||
if (mqtt_format_ != MQTT_format::SINGLE) {
|
||||
// if (mqtt_format_ != MQTT_format::SINGLE) {
|
||||
if ((mqtt_format_ == MQTT_format::NESTED) || (mqtt_format_ == MQTT_format::HA)) {
|
||||
char hc_name[10]; // hc{1-4}
|
||||
strlcpy(hc_name, "hc", 10);
|
||||
char s[3];
|
||||
@@ -698,7 +710,8 @@ void Thermostat::publish_values() {
|
||||
|
||||
// if format is single, send immediately and clear object for next hc
|
||||
// the topic will have the hc number appended
|
||||
if (mqtt_format_ == MQTT_format::SINGLE) {
|
||||
// if (mqtt_format_ == MQTT_format::SINGLE) {
|
||||
if ((mqtt_format_ == MQTT_format::SINGLE) || (mqtt_format_ == MQTT_format::CUSTOM)) {
|
||||
char topic[30];
|
||||
char s[3];
|
||||
strlcpy(topic, "thermostat_data", 30);
|
||||
@@ -717,10 +730,9 @@ void Thermostat::publish_values() {
|
||||
}
|
||||
|
||||
// if we're using nested json, send all in one go under one topic called thermostat_data
|
||||
// if ((mqtt_format_ == MQTT_format::NESTED) || (mqtt_format_ == MQTT_format::CUSTOM)) {
|
||||
if (mqtt_format_ == MQTT_format::NESTED) {
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
} else if (mqtt_format_ == MQTT_format::CUSTOM) {
|
||||
Mqtt::publish("thermostat_data", doc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,7 +1042,7 @@ void Thermostat::show_values(uuid::console::Shell & shell) {
|
||||
shell.printfln(F(" Display: time"));
|
||||
} else if (ibaMainDisplay_ == 7) {
|
||||
shell.printfln(F(" Display: date"));
|
||||
} else if (ibaMainDisplay_ == 9) {
|
||||
} else if (ibaMainDisplay_ == 8) {
|
||||
shell.printfln(F(" Display: smoke temperature"));
|
||||
}
|
||||
}
|
||||
@@ -1209,7 +1221,7 @@ void Thermostat::process_EasyMonitor(std::shared_ptr<const Telegram> telegram) {
|
||||
void Thermostat::process_IBASettings(std::shared_ptr<const Telegram> telegram) {
|
||||
// 22 - display line on RC35
|
||||
telegram->read_value(ibaMainDisplay_,
|
||||
0); // display on Thermostat: 0 int. temp, 1 int. setpoint, 2 ext. temp., 3 burner temp., 4 ww temp, 5 functioning mode, 6 time, 7 data, 9 smoke temp
|
||||
0); // display on Thermostat: 0 int. temp, 1 int. setpoint, 2 ext. temp., 3 burner temp., 4 ww temp, 5 functioning mode, 6 time, 7 data, 8 smoke temp
|
||||
telegram->read_value(ibaLanguage_, 1); // language on Thermostat: 0 german, 1 dutch, 2 french, 3 italian
|
||||
telegram->read_value(ibaCalIntTemperature_, 2); // offset int. temperature sensor, by * 0.1 Kelvin
|
||||
telegram->read_value(ibaBuildingType_, 6); // building type: 0 = light, 1 = medium, 2 = heavy
|
||||
@@ -1351,11 +1363,16 @@ void Thermostat::process_RCTime(std::shared_ptr<const Telegram> telegram) {
|
||||
if (flags() == EMS_DEVICE_FLAG_EASY) {
|
||||
return; // not supported
|
||||
}
|
||||
|
||||
if (telegram->message_length < 7) {
|
||||
return;
|
||||
}
|
||||
if (telegram->message_data[7] & 0x0C) { // date and time not valid
|
||||
set_datetime("NTP"); // set from NTP
|
||||
return;
|
||||
}
|
||||
if (datetime_.empty()) {
|
||||
datetime_.resize(25, '\0');
|
||||
}
|
||||
|
||||
// render time to HH:MM:SS DD/MM/YYYY
|
||||
// had to create separate buffers because of how printf works
|
||||
char buf1[6];
|
||||
@@ -1510,18 +1527,38 @@ void Thermostat::set_party(const uint8_t hrs, const uint8_t hc_num) {
|
||||
}
|
||||
}
|
||||
|
||||
// set date&time as string hh:mm:ss-dd.mm.yyyy-dw-dst
|
||||
// set date&time as string hh:mm:ss-dd.mm.yyyy-dw-dst or "NTP" for setting to internet-time
|
||||
// dw - day of week (0..6), dst- summertime (0/1)
|
||||
void Thermostat::set_datetime(const char * dt) {
|
||||
uint8_t data[9];
|
||||
data[0] = (dt[16] - '0') * 100 + (dt[17] - '0') * 10 + (dt[18] - '0'); // year
|
||||
data[1] = (dt[12] - '0') * 10 + (dt[13] - '0'); // month
|
||||
data[2] = (dt[0] - '0') * 10 + (dt[1] - '0'); // hour
|
||||
data[3] = (dt[9] - '0') * 10 + (dt[10] - '0'); // day
|
||||
data[4] = (dt[3] - '0') * 10 + (dt[4] - '0'); // min
|
||||
data[5] = (dt[6] - '0') * 10 + (dt[7] - '0'); // sec
|
||||
data[6] = (dt[20] - '0'); // day of week
|
||||
data[7] = (dt[22] - '0'); // summerime
|
||||
if (strcmp(dt,"NTP") == 0) {
|
||||
time_t now = time(nullptr);
|
||||
tm * tm_ = localtime(&now);
|
||||
if (tm_->tm_year < 110) { // no NTP time
|
||||
LOG_WARNING(F("No NTP time. Cannot set RCtime"));
|
||||
return;
|
||||
}
|
||||
data[0] = tm_->tm_year - 100; // Bosch counts from 2000
|
||||
data[1] = tm_->tm_mon;
|
||||
data[2] = tm_->tm_hour;
|
||||
data[3] = tm_->tm_mday;
|
||||
data[4] = tm_->tm_min;
|
||||
data[5] = tm_->tm_sec;
|
||||
data[6] = (tm_->tm_wday + 6) % 7; // Bosch counts from Mo, time from Su
|
||||
data[7] = tm_->tm_isdst + 2; // set DST and flag for ext. clock
|
||||
char time_string[25];
|
||||
strftime(time_string, 25, "%FT%T%z", tm_);
|
||||
LOG_INFO(F("Date and time: %s"), time_string);
|
||||
} else {
|
||||
data[0] = (dt[16] - '0') * 100 + (dt[17] - '0') * 10 + (dt[18] - '0'); // year
|
||||
data[1] = (dt[12] - '0') * 10 + (dt[13] - '0'); // month
|
||||
data[2] = (dt[0] - '0') * 10 + (dt[1] - '0'); // hour
|
||||
data[3] = (dt[9] - '0') * 10 + (dt[10] - '0'); // day
|
||||
data[4] = (dt[3] - '0') * 10 + (dt[4] - '0'); // min
|
||||
data[5] = (dt[6] - '0') * 10 + (dt[7] - '0'); // sec
|
||||
data[6] = (dt[20] - '0'); // day of week
|
||||
data[7] = (dt[22] - '0') + 2; // DST and flag
|
||||
}
|
||||
if ((flags() & 0x0F) == EMS_DEVICE_FLAG_RC35 || (flags() & 0x0F) == EMS_DEVICE_FLAG_RC30_1) {
|
||||
LOG_INFO(F("Setting date and time"));
|
||||
write_command(6, 0, data, 8, 0);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "mqtt.h"
|
||||
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@
|
||||
|
||||
MAKE_PSTR(logger_name, "sensors")
|
||||
|
||||
#ifdef ESP32
|
||||
#define YIELD
|
||||
#else
|
||||
#define YIELD yield()
|
||||
#endif
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
uuid::log::Logger Sensors::logger_{F_(logger_name), uuid::log::Facility::DAEMON};
|
||||
@@ -63,7 +69,7 @@ void Sensors::loop() {
|
||||
if (time_now - last_activity_ >= READ_INTERVAL_MS) {
|
||||
// LOG_DEBUG(F("Read sensor temperature")); // uncomment for debug
|
||||
if (bus_.reset()) {
|
||||
yield();
|
||||
YIELD;
|
||||
bus_.skip();
|
||||
bus_.write(CMD_CONVERT_TEMP);
|
||||
|
||||
@@ -155,17 +161,17 @@ float Sensors::get_temperature_c(const uint8_t addr[]) {
|
||||
LOG_ERROR(F("Bus reset failed before reading scratchpad from %s"), Device(addr).to_string().c_str());
|
||||
return NAN;
|
||||
}
|
||||
yield();
|
||||
YIELD;
|
||||
uint8_t scratchpad[SCRATCHPAD_LEN] = {0};
|
||||
bus_.select(addr);
|
||||
bus_.write(CMD_READ_SCRATCHPAD);
|
||||
bus_.read_bytes(scratchpad, SCRATCHPAD_LEN);
|
||||
yield();
|
||||
YIELD;
|
||||
if (!bus_.reset()) {
|
||||
LOG_ERROR(F("Bus reset failed after reading scratchpad from %s"), Device(addr).to_string().c_str());
|
||||
return NAN;
|
||||
}
|
||||
yield();
|
||||
YIELD;
|
||||
if (bus_.crc8(scratchpad, SCRATCHPAD_LEN - 1) != scratchpad[SCRATCHPAD_LEN - 1]) {
|
||||
LOG_WARNING(F("Invalid scratchpad CRC: %02X%02X%02X%02X%02X%02X%02X%02X%02X from device %s"),
|
||||
scratchpad[0],
|
||||
@@ -202,7 +208,8 @@ float Sensors::get_temperature_c(const uint8_t addr[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
return (float)raw_value / 16;
|
||||
uint32_t raw = (raw_value *625) / 100; // round to 0.01
|
||||
return (float)raw / 100;
|
||||
#else
|
||||
return NAN;
|
||||
#endif
|
||||
|
||||
@@ -34,8 +34,8 @@ static hw_timer_t * timer = NULL;
|
||||
bool drop_next_rx = true;
|
||||
uint8_t tx_mode_ = 0xFF;
|
||||
uint8_t emsTxBuf[EMS_MAXBUFFERSIZE];
|
||||
uint8_t emsTxBufIdx;
|
||||
uint8_t emsTxBufLen;
|
||||
uint8_t emsTxBufIdx = 0;
|
||||
uint8_t emsTxBufLen = 0;
|
||||
uint32_t emsTxWait;
|
||||
|
||||
/*
|
||||
@@ -88,12 +88,15 @@ void IRAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() {
|
||||
portENTER_CRITICAL(&mux);
|
||||
if (emsTxBufIdx < emsTxBufLen) {
|
||||
EMS_UART.fifo.rw_byte = emsTxBuf[emsTxBufIdx];
|
||||
timerAlarmWrite(timer, emsTxWait, true);
|
||||
if (emsTxBufIdx == 1) {
|
||||
timerAlarmWrite(timer, emsTxWait, true);
|
||||
}
|
||||
} else if (emsTxBufIdx == emsTxBufLen) {
|
||||
EMS_UART.conf0.txd_inv = 1;
|
||||
timerAlarmWrite(timer, EMSUART_TX_WAIT_BRK, true);
|
||||
timerAlarmWrite(timer, EMSUART_TX_BRK_TIMER, true);
|
||||
} else if (emsTxBufIdx == emsTxBufLen + 1) {
|
||||
EMS_UART.conf0.txd_inv = 0;
|
||||
emsTxBufLen = 0;
|
||||
timerAlarmDisable(timer);
|
||||
}
|
||||
emsTxBufIdx++;
|
||||
@@ -130,7 +133,7 @@ void EMSuart::start(const uint8_t tx_mode) {
|
||||
xTaskCreate(emsuart_recvTask, "emsuart_recvTask", 2048, NULL, configMAX_PRIORITIES - 1, NULL);
|
||||
|
||||
timer = timerBegin(0, 80, true); // timer prescale to 1 us, countup
|
||||
timerAttachInterrupt(timer, &emsuart_tx_timer_intr_handler, false); // Timer with level interrupt
|
||||
timerAttachInterrupt(timer, &emsuart_tx_timer_intr_handler, true); // Timer with edge interrupt
|
||||
restart();
|
||||
}
|
||||
|
||||
@@ -140,6 +143,9 @@ void EMSuart::start(const uint8_t tx_mode) {
|
||||
void EMSuart::stop() {
|
||||
EMS_UART.int_ena.val = 0; // disable all intr.
|
||||
EMS_UART.conf0.txd_inv = 0; // stop break
|
||||
if (emsTxBufLen > 0) {
|
||||
timerAlarmDisable(timer);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -165,44 +171,11 @@ void EMSuart::restart() {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a 11-bit break by inverting the tx-port
|
||||
*/
|
||||
void EMSuart::tx_brk() {
|
||||
EMS_UART.conf0.txd_inv = 1;
|
||||
delayMicroseconds(EMSUART_TX_WAIT_BRK);
|
||||
EMS_UART.conf0.txd_inv = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a 1-byte poll, ending with a <BRK>
|
||||
*/
|
||||
void EMSuart::send_poll(const uint8_t data) {
|
||||
if (tx_mode_ > 5) { // timer controlled modes
|
||||
emsTxBuf[0] = data;
|
||||
emsTxBufIdx = 0;
|
||||
emsTxBufLen = 1;
|
||||
timerAlarmWrite(timer, emsTxWait, true); // start timer with autoreload
|
||||
timerAlarmEnable(timer); // first interrupt comes immediately
|
||||
} else if (tx_mode_ == EMS_TXMODE_DEFAULT) {
|
||||
volatile uint8_t _usrxc = EMS_UART.status.rxfifo_cnt;
|
||||
uint16_t timeoutcnt = EMSUART_TX_TIMEOUT;
|
||||
EMS_UART.fifo.rw_byte = data;
|
||||
while ((EMS_UART.status.rxfifo_cnt == _usrxc) && (--timeoutcnt > 0)) {
|
||||
delayMicroseconds(EMSUART_TX_BUSY_WAIT);
|
||||
}
|
||||
tx_brk();
|
||||
} else if (tx_mode_ == EMS_TXMODE_EMSPLUS) {
|
||||
EMS_UART.fifo.rw_byte = data;
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||
tx_brk();
|
||||
} else if (tx_mode_ == EMS_TXMODE_HT3) {
|
||||
EMS_UART.fifo.rw_byte = data;
|
||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||
tx_brk();
|
||||
} else {
|
||||
EMS_UART.fifo.rw_byte = data;
|
||||
}
|
||||
transmit(&data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -221,7 +194,7 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
}
|
||||
emsTxBufIdx = 0;
|
||||
emsTxBufLen = len;
|
||||
if (tx_mode_ > 100) {
|
||||
if (tx_mode_ > 100 && len > 1) {
|
||||
timerAlarmWrite(timer, EMSUART_TX_WAIT_REPLY, true);
|
||||
} else {
|
||||
timerAlarmWrite(timer, emsTxWait, true); // start with autoreload
|
||||
@@ -242,7 +215,9 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
EMS_UART.fifo.rw_byte = buf[i];
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||
}
|
||||
tx_brk();
|
||||
EMS_UART.conf0.txd_inv = 1; // send <brk>
|
||||
delayMicroseconds(EMSUART_TX_BRK_PLUS);
|
||||
EMS_UART.conf0.txd_inv = 0;
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -251,11 +226,13 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
EMS_UART.fifo.rw_byte = buf[i];
|
||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||
}
|
||||
tx_brk();
|
||||
EMS_UART.conf0.txd_inv = 1; // send <brk>
|
||||
delayMicroseconds(EMSUART_TX_BRK_HT3);
|
||||
EMS_UART.conf0.txd_inv = 0;
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
// mode 1
|
||||
// mode 1: wait for echo after each byte
|
||||
// flush fifos -- not supported in ESP32 uart #2!
|
||||
// EMS_UART.conf0.rxfifo_rst = 1;
|
||||
// EMS_UART.conf0.txfifo_rst = 1;
|
||||
@@ -267,7 +244,9 @@ uint16_t EMSuart::transmit(const uint8_t * buf, const uint8_t len) {
|
||||
delayMicroseconds(EMSUART_TX_BUSY_WAIT); // burn CPU cycles...
|
||||
}
|
||||
}
|
||||
tx_brk();
|
||||
EMS_UART.conf0.txd_inv = 1;
|
||||
delayMicroseconds(EMSUART_TX_BRK_EMS);
|
||||
EMS_UART.conf0.txd_inv = 0;
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,20 +46,25 @@
|
||||
#define EMS_TXMODE_NEW 4 // for michael's testing
|
||||
|
||||
// LEGACY
|
||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||
#define EMSUART_TX_WAIT_BRK (EMSUART_TX_BIT_TIME * 10) // 10 bt
|
||||
#define EMSUART_TX_WAIT_REPLY 100000 // delay 100ms after first byte
|
||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||
|
||||
// Timer controlled modes
|
||||
#define EMSUART_TX_BRK_TIMER (EMSUART_TX_BIT_TIME * 10 + 28) // 10.25 bit times
|
||||
#define EMSUART_TX_WAIT_REPLY 100000 // delay 100ms after first byte
|
||||
|
||||
// EMS 1.0
|
||||
#define EMSUART_TX_BUSY_WAIT (EMSUART_TX_BIT_TIME / 8) // 13
|
||||
#define EMSUART_TX_TIMEOUT (32 * EMSUART_TX_BIT_TIME / EMSUART_TX_BUSY_WAIT) // 256
|
||||
#define EMSUART_TX_TIMEOUT (20 * EMSUART_TX_BIT_TIME / EMSUART_TX_BUSY_WAIT)
|
||||
#define EMSUART_TX_BRK_EMS (EMSUART_TX_BIT_TIME * 10)
|
||||
|
||||
// HT3/Junkers - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) plus 7 bit delay. The -8 is for lag compensation.
|
||||
// since we use a faster processor the lag is negligible
|
||||
#define EMSUART_TX_WAIT_HT3 (EMSUART_TX_BIT_TIME * 17) // 1768
|
||||
#define EMSUART_TX_BRK_HT3 (EMSUART_TX_BIT_TIME * 11)
|
||||
|
||||
// EMS+ - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) and delay of another Bytetime.
|
||||
#define EMSUART_TX_WAIT_PLUS (EMSUART_TX_BIT_TIME * 20) // 2080
|
||||
#define EMSUART_TX_BRK_PLUS (EMSUART_TX_BIT_TIME * 11)
|
||||
|
||||
|
||||
// customize the GPIO pins for RX and TX here
|
||||
@@ -91,7 +96,6 @@ class EMSuart {
|
||||
static void emsuart_recvTask(void * para);
|
||||
static void IRAM_ATTR emsuart_rx_intr_handler(void * para);
|
||||
static void IRAM_ATTR emsuart_tx_timer_intr_handler();
|
||||
static void tx_brk();
|
||||
};
|
||||
|
||||
} // namespace emsesp
|
||||
|
||||
@@ -95,10 +95,10 @@ void ICACHE_RAM_ATTR EMSuart::emsuart_tx_timer_intr_handler() {
|
||||
timer1_write(emsTxWait);
|
||||
} else if (emsTxBufIdx == emsTxBufLen) {
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // set <BRK>
|
||||
timer1_write(EMSUART_TX_WAIT_BRK * 5);
|
||||
timer1_write(EMSUART_TX_BRK_TIMER);
|
||||
} else {
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK); // reset <BRK>
|
||||
sending_ = false;
|
||||
sending_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,11 +114,6 @@ void ICACHE_FLASH_ATTR EMSuart::emsuart_flush_fifos() {
|
||||
* init UART0 driver
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
|
||||
if (tx_mode_ > 100) {
|
||||
emsTxWait = 5 * EMSUART_TX_BIT_TIME * (tx_mode - 90);
|
||||
} else {
|
||||
emsTxWait = 5 * EMSUART_TX_BIT_TIME * (tx_mode + 10); // bittimes wait to next bytes
|
||||
}
|
||||
if (tx_mode_ != 0xFF) { // it's a restart no need to configure uart
|
||||
tx_mode_ = tx_mode;
|
||||
restart();
|
||||
@@ -133,7 +128,6 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
|
||||
}
|
||||
pEMSRxBuf = paEMSRxBuf[0]; // reset EMS Rx Buffer
|
||||
|
||||
ETS_UART_INTR_DISABLE();
|
||||
ETS_UART_INTR_ATTACH(nullptr, nullptr);
|
||||
|
||||
// pin settings
|
||||
@@ -153,22 +147,14 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
|
||||
// UCFFT = RX FIFO Full Threshold (7 bit) = want this to be 31 for 32 bytes of buffer (default was 127)
|
||||
// see https://www.espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf
|
||||
//
|
||||
// change: we set UCFFT to 1 to get an immediate indicator about incoming traffic.
|
||||
// Otherwise, we're only noticed by UCTOT or RxBRK!
|
||||
// change: don't care, we do not use these interrupts
|
||||
USC1(EMSUART_UART) = 0; // reset config first
|
||||
USC1(EMSUART_UART) = 0; // reset config
|
||||
// USC1(EMSUART_UART) = (0x7F << UCFFT) | (0x01 << UCTOT) | (1 << UCTOE); // enable interupts
|
||||
|
||||
// set interrupts for triggers
|
||||
USIC(EMSUART_UART) = 0xFFFF; // clear all interupts
|
||||
USIE(EMSUART_UART) = 0; // disable all interrupts
|
||||
|
||||
// enable rx break, fifo full and timeout.
|
||||
// but not frame error UIFR (because they are too frequent) or overflow UIOF because our buffer is only max 32 bytes
|
||||
// change: we don't care about Rx Timeout - it may lead to wrong readouts
|
||||
// change:we don't care about Fifo full and read only on break-detect
|
||||
USIE(EMSUART_UART) = (1 << UIBD) | (0 << UIFF) | (0 << UITO);
|
||||
|
||||
// set up interrupt callbacks for Rx
|
||||
system_os_task(emsuart_recvTask, EMSUART_recvTaskPrio, recvTaskQueue, EMSUART_recvTaskQueueLen);
|
||||
|
||||
@@ -182,10 +168,9 @@ void ICACHE_FLASH_ATTR EMSuart::start(uint8_t tx_mode) {
|
||||
drop_next_rx = true;
|
||||
|
||||
// for sending with large delay in EMS+ mode we use a timer interrupt
|
||||
timer1_attachInterrupt(emsuart_tx_timer_intr_handler); // Add ISR Function
|
||||
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE); // 5 MHz timer
|
||||
ETS_UART_INTR_ENABLE();
|
||||
USIE(EMSUART_UART) = (1 << UIBD);
|
||||
timer1_attachInterrupt(emsuart_tx_timer_intr_handler);
|
||||
|
||||
restart();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -204,67 +189,25 @@ void ICACHE_FLASH_ATTR EMSuart::stop() {
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR EMSuart::restart() {
|
||||
if (USIR(EMSUART_UART) & ((1 << UIBD))) {
|
||||
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the BREAK detect interrupt
|
||||
USIC(EMSUART_UART) = (1 << UIBD); // INT clear the <brk> detect interrupt
|
||||
drop_next_rx = true;
|
||||
}
|
||||
if (tx_mode_ > 100) {
|
||||
emsTxWait = 5 * EMSUART_TX_BIT_TIME * (tx_mode_ - 90);
|
||||
} else {
|
||||
emsTxWait = 5 * EMSUART_TX_BIT_TIME * (tx_mode_ + 10); // bittimes wait to next bytes
|
||||
}
|
||||
emsTxBufIdx = 0;
|
||||
emsTxBufLen = 0;
|
||||
timer1_enable(TIM_DIV16, TIM_EDGE, TIM_SINGLE);
|
||||
USIE(EMSUART_UART) = (1 << UIBD);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a BRK signal
|
||||
* Which is a 11-bit set of zero's (11 cycles)
|
||||
*/
|
||||
void ICACHE_FLASH_ATTR EMSuart::tx_brk() {
|
||||
// make sure Tx FIFO is empty
|
||||
while (((USS(EMSUART_UART) >> USTXC) & 0xFF)) {
|
||||
}
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // set bit
|
||||
// also for EMS+ there is no need to wait longer, we are finished and can free the bus.
|
||||
delayMicroseconds(EMSUART_TX_WAIT_BRK); // 1144
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK); // clear BRK bit
|
||||
USIE(EMSUART_UART) = (1 << UIBD); // enable <brk> interrupt
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a 1-byte poll, ending with a <BRK>
|
||||
* It's a bit dirty. there is no special wait logic per tx_mode type, fifo flushes or error checking
|
||||
*/
|
||||
void EMSuart::send_poll(uint8_t data) {
|
||||
// reset tx-brk, just in case it is accidentally set
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
sending_ = true;
|
||||
|
||||
if (tx_mode_ >= 5) { // timer controlled modes
|
||||
emsTxBuf[0] = data;
|
||||
emsTxBufIdx = 0;
|
||||
emsTxBufLen = 1;
|
||||
timer1_write(emsTxWait);
|
||||
} else if (tx_mode_ == EMS_TXMODE_NEW) { // hardware controlled modes
|
||||
USF(EMSUART_UART) = data;
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK);
|
||||
} else if (tx_mode_ == EMS_TXMODE_HT3) {
|
||||
USF(EMSUART_UART) = data;
|
||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||
tx_brk(); // send <BRK>
|
||||
sending_ = false;
|
||||
} else if (tx_mode_ == EMS_TXMODE_EMSPLUS) {
|
||||
USF(EMSUART_UART) = data;
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS);
|
||||
tx_brk(); // send <BRK>
|
||||
sending_ = false;
|
||||
} else {
|
||||
// tx_mode 1
|
||||
volatile uint8_t _usrxc = (USS(EMSUART_UART) >> USRXC) & 0xFF;
|
||||
USF(EMSUART_UART) = data;
|
||||
uint16_t timeoutcnt = EMSUART_TX_TIMEOUT;
|
||||
while ((((USS(EMSUART_UART) >> USRXC) & 0xFF) == _usrxc) && (--timeoutcnt > 0)) {
|
||||
delayMicroseconds(EMSUART_TX_BUSY_WAIT); // burn CPU cycles...
|
||||
}
|
||||
tx_brk(); // send <BRK>
|
||||
sending_ = false;
|
||||
}
|
||||
void ICACHE_FLASH_ATTR EMSuart::send_poll(uint8_t data) {
|
||||
transmit(&data, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -276,20 +219,18 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
if (len == 0 || len >= EMS_MAXBUFFERSIZE) {
|
||||
return EMS_TX_STATUS_ERR; // nothing or to much to send
|
||||
}
|
||||
// reset tx-brk, just in case it is accidentally set
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
sending_ = true;
|
||||
|
||||
// timer controlled modes with extra delay
|
||||
if (tx_mode_ >= 5) {
|
||||
sending_ = true;
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
emsTxBuf[i] = buf[i];
|
||||
}
|
||||
USF(EMSUART_UART) = buf[0]; // send first byte
|
||||
emsTxBufIdx = 0;
|
||||
emsTxBufLen = len;
|
||||
if (tx_mode_ > 100) {
|
||||
timer1_write(EMSUART_TX_WAIT_REPLY);
|
||||
if (tx_mode_ > 100 && len > 1) {
|
||||
timer1_write(EMSUART_TX_WAIT_REPLY); // large delay after first byte
|
||||
} else {
|
||||
timer1_write(emsTxWait);
|
||||
}
|
||||
@@ -301,7 +242,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
for (uint8_t i = 0; i < len; i++) {
|
||||
USF(EMSUART_UART) = buf[i];
|
||||
}
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // send <BRK> at the end, clear by interrupt
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -311,8 +252,9 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
USF(EMSUART_UART) = buf[i];
|
||||
delayMicroseconds(EMSUART_TX_WAIT_PLUS); // 2070
|
||||
}
|
||||
tx_brk(); // send <BRK>
|
||||
sending_ = false;
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // set break
|
||||
delayMicroseconds(EMSUART_TX_BRK_PLUS);
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -326,8 +268,9 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
// wait until bits are sent on wire
|
||||
delayMicroseconds(EMSUART_TX_WAIT_HT3);
|
||||
}
|
||||
tx_brk(); // send <BRK>
|
||||
sending_ = false;
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // set break bit
|
||||
delayMicroseconds(EMSUART_TX_BRK_HT3);
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
return EMS_TX_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -356,9 +299,7 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
*
|
||||
*/
|
||||
|
||||
// disable rx interrupt
|
||||
// clear Rx status register, resetting the Rx FIFO and flush it
|
||||
// ETS_UART_INTR_DISABLE();
|
||||
emsuart_flush_fifos();
|
||||
|
||||
// send the bytes along the serial line
|
||||
@@ -371,18 +312,9 @@ uint16_t ICACHE_FLASH_ATTR EMSuart::transmit(uint8_t * buf, uint8_t len) {
|
||||
delayMicroseconds(EMSUART_TX_BUSY_WAIT); // burn CPU cycles...
|
||||
}
|
||||
}
|
||||
|
||||
// we got the whole telegram in the Rx buffer
|
||||
// on Rx-BRK (bus collision), we simply enable Rx and leave it
|
||||
// otherwise we send the final Tx-BRK
|
||||
// worst case, we'll see an additional Rx-BRK...
|
||||
// neither bus collision nor timeout - send terminating BRK signal
|
||||
if (!(USIS(EMSUART_UART) & (1 << UIBD))) {
|
||||
// no bus collision - send terminating BRK signal
|
||||
tx_brk();
|
||||
}
|
||||
// ETS_UART_INTR_ENABLE(); // open up the FIFO again to start receiving
|
||||
sending_ = false;
|
||||
USC0(EMSUART_UART) |= (1 << UCBRK); // snd break
|
||||
delayMicroseconds(EMSUART_TX_BRK_EMS);
|
||||
USC0(EMSUART_UART) &= ~(1 << UCBRK);
|
||||
return EMS_TX_STATUS_OK; // send the Tx ok status back
|
||||
}
|
||||
|
||||
|
||||
@@ -40,22 +40,26 @@
|
||||
#define EMS_TXMODE_NEW 4 // for michael's testing
|
||||
|
||||
// LEGACY
|
||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||
#define EMSUART_TX_WAIT_BRK (EMSUART_TX_BIT_TIME * 10)
|
||||
#define EMSUART_TX_WAIT_REPLY 500000 // delay 100ms after first byte
|
||||
#define EMSUART_TX_BIT_TIME 104 // bit time @9600 baud
|
||||
|
||||
// TIMER modes
|
||||
#define EMSUART_TX_BRK_TIMER (EMSUART_TX_BIT_TIME * 52) // > 10 bittimes for timer modes
|
||||
#define EMSUART_TX_WAIT_REPLY 500000 // delay 100ms after first byte
|
||||
|
||||
// EMS 1.0
|
||||
#define EMSUART_TX_BUSY_WAIT (EMSUART_TX_BIT_TIME / 8) // 13
|
||||
// #define EMSUART_TX_TIMEOUT (22 * EMSUART_TX_BIT_TIME / EMSUART_TX_BUSY_WAIT) // 176
|
||||
// #define EMSUART_TX_TIMEOUT (32 * 8) // 256 for tx_mode 1 - see https://github.com/proddy/EMS-ESP/issues/398#issuecomment-645886277
|
||||
#define EMSUART_TX_TIMEOUT (220 * 8) // 1760 as in v1.9 (180 ms)
|
||||
#define EMSUART_TX_BRK_EMS (EMSUART_TX_BIT_TIME * 10)
|
||||
|
||||
// HT3/Junkers - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) plus 7 bit delay. The -8 is for lag compensation.
|
||||
// since we use a faster processor the lag is negligible
|
||||
#define EMSUART_TX_WAIT_HT3 (EMSUART_TX_BIT_TIME * 17) // 1768
|
||||
#define EMSUART_TX_BRK_HT3 (EMSUART_TX_BIT_TIME * 11)
|
||||
|
||||
// EMS+ - Time to send one Byte (8 Bits, 1 Start Bit, 1 Stop Bit) and delay of another Bytetime.
|
||||
#define EMSUART_TX_WAIT_PLUS (EMSUART_TX_BIT_TIME * 20) // 2080
|
||||
#define EMSUART_TX_BRK_PLUS (EMSUART_TX_BIT_TIME * 11)
|
||||
|
||||
namespace emsesp {
|
||||
|
||||
@@ -85,7 +89,6 @@ class EMSuart {
|
||||
static void ICACHE_RAM_ATTR emsuart_rx_intr_handler(void * para);
|
||||
static void ICACHE_FLASH_ATTR emsuart_recvTask(os_event_t * events);
|
||||
static void ICACHE_FLASH_ATTR emsuart_flush_fifos();
|
||||
static void ICACHE_FLASH_ATTR tx_brk();
|
||||
static void ICACHE_RAM_ATTR emsuart_tx_timer_intr_handler();
|
||||
static bool sending_;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user