multiple bus ids - #329

This commit is contained in:
Paul
2020-02-18 16:03:54 +01:00
parent 0712ab5152
commit b4e1d35d86
6 changed files with 192 additions and 86 deletions

View File

@@ -79,7 +79,9 @@ typedef struct {
uint8_t dallas_gpio; // pin for attaching external dallas temperature sensors uint8_t dallas_gpio; // pin for attaching external dallas temperature sensors
bool dallas_parasite; // on/off is using parasite bool dallas_parasite; // on/off is using parasite
uint8_t tx_mode; // TX mode 1,2 or 3 uint8_t tx_mode; // TX mode 1,2 or 3
uint8_t bus_id; // BUS ID, defaults to 0x0B for the service key
uint8_t master_thermostat; // Product ID of master thermostat to use, 0 for automatic uint8_t master_thermostat; // Product ID of master thermostat to use, 0 for automatic
char * known_devices; // list of known deviceIDs for quick boot
} _EMSESP_Settings; } _EMSESP_Settings;
typedef struct { typedef struct {
@@ -101,6 +103,7 @@ static const command_t project_cmds[] PROGMEM = {
{true, "shower_alert <on | off>", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"}, {true, "shower_alert <on | off>", "stop hot water to send 3 cold burst warnings after max shower time is exceeded"},
{true, "publish_time <seconds>", "set frequency for publishing data to MQTT (-1=off, 0=automatic)"}, {true, "publish_time <seconds>", "set frequency for publishing data to MQTT (-1=off, 0=automatic)"},
{true, "tx_mode <n>", "changes Tx logic. 1=EMS generic, 2=EMS+, 3=HT3"}, {true, "tx_mode <n>", "changes Tx logic. 1=EMS generic, 2=EMS+, 3=HT3"},
{true, "bus_id <ID>", "EMS-ESP's deviceID. 0B=Service Key (default), 0D=Modem, 0A=Hand terminal, 0F=Time module, 12=Error module"},
{true, "master_thermostat [product id]", "set default thermostat to use. No argument lists options"}, {true, "master_thermostat [product id]", "set default thermostat to use. No argument lists options"},
{false, "info", "show current values deciphered from the EMS messages"}, {false, "info", "show current values deciphered from the EMS messages"},
@@ -112,9 +115,8 @@ static const command_t project_cmds[] PROGMEM = {
{false, "publish", "publish all values to MQTT"}, {false, "publish", "publish all values to MQTT"},
{false, "refresh", "fetch values from the EMS devices"}, {false, "refresh", "fetch values from the EMS devices"},
{false, "devices", "list detected EMS devices"}, {false, "devices [scan [deep]]", "list, ask Master or perform deep scan of EMS devices"},
{false, "queue", "show current Tx queue"}, {false, "queue", "show current Tx queue"},
{false, "autodetect [scan]", "scan for EMS devices and external sensors. scan uses brute-force"},
{false, "send XX ...", "send raw telegram data to EMS bus (XX are hex values)"}, {false, "send XX ...", "send raw telegram data to EMS bus (XX are hex values)"},
{false, "thermostat read <type ID>", "send read request to the thermostat for heating circuit hc 1-4"}, {false, "thermostat read <type ID>", "send read request to the thermostat for heating circuit hc 1-4"},
{false, "thermostat temp [hc] <degrees>", "set current thermostat temperature"}, {false, "thermostat temp [hc] <degrees>", "set current thermostat temperature"},
@@ -259,6 +261,9 @@ void showInfo() {
if (ems_getBusConnected()) { if (ems_getBusConnected()) {
myDebug_P(PSTR(" Bus is connected, protocol: %s"), (ems_isHT3() ? "HT3" : "Buderus")); myDebug_P(PSTR(" Bus is connected, protocol: %s"), (ems_isHT3() ? "HT3" : "Buderus"));
myDebug_P(PSTR(" Rx: # successful read requests=%d, # CRC errors=%d"), EMS_Sys_Status.emsRxPgks, EMS_Sys_Status.emxCrcErr); myDebug_P(PSTR(" Rx: # successful read requests=%d, # CRC errors=%d"), EMS_Sys_Status.emsRxPgks, EMS_Sys_Status.emxCrcErr);
if (strlen(EMSESP_Settings.known_devices) > 0) {
myDebug_P(PSTR(" Saved known device IDs: %s"), EMSESP_Settings.known_devices);
}
if (ems_getTxCapable()) { if (ems_getTxCapable()) {
char valuestr[8] = {0}; // for formatting floats char valuestr[8] = {0}; // for formatting floats
@@ -550,7 +555,7 @@ void scanDallas() {
char buffer[128]; char buffer[128];
char buffer2[128]; char buffer2[128];
for (uint8_t i = 0; i < EMSESP_Settings.dallas_sensors; i++) { for (uint8_t i = 0; i < EMSESP_Settings.dallas_sensors; i++) {
myDebug_P(PSTR("External temperature sensor type: %s id: %s found"), ds18.getDeviceType(buffer, i), ds18.getDeviceID(buffer2, i)); myDebug_P(PSTR("External temperature sensor found, type: %s id: %s"), ds18.getDeviceType(buffer, i), ds18.getDeviceID(buffer2, i));
} }
} }
} }
@@ -567,21 +572,28 @@ void publishSensorValues() {
} }
// each payload per sensor is 30 bytes so calculate if we have enough space // each payload per sensor is 30 bytes so calculate if we have enough space
if ((EMSESP_Settings.dallas_sensors * 30) > DS18_MQTT_PAYLOAD_MAXSIZE) { if ((EMSESP_Settings.dallas_sensors * 50) > DS18_MQTT_PAYLOAD_MAXSIZE) {
myDebug("Error: too many Dallas sensors for MQTT payload"); myDebug("Error: too many Dallas sensors for MQTT payload");
} }
StaticJsonDocument<DS18_MQTT_PAYLOAD_MAXSIZE> doc; StaticJsonDocument<DS18_MQTT_PAYLOAD_MAXSIZE> doc;
JsonObject sensors = doc.to<JsonObject>(); JsonObject sensors = doc.to<JsonObject>();
bool hasdata = false; bool hasdata = false;
char buffer[128] = {0}; char buffer[128] = {0}; // temp string buffer
// see if the sensor values have changed, if so send it on // see if the sensor values have changed, if so send it on
for (uint8_t i = 0; i < EMSESP_Settings.dallas_sensors; i++) { for (uint8_t i = 0; i < EMSESP_Settings.dallas_sensors; i++) {
float sensorValue = ds18.getValue(i); float sensorValue = ds18.getValue(i);
if (sensorValue != DS18_DISCONNECTED) { if (sensorValue != DS18_DISCONNECTED) {
sensors[ds18.getDeviceID(buffer, i)] = sensorValue; hasdata = true;
hasdata = true; // create a nested object
// https://github.com/proddy/EMS-ESP/issues/327
char sensorID[10]; // sensor{1-n}
strlcpy(sensorID, PAYLOAD_EXTERNAL_SENSOR_NUM, sizeof(sensorID));
strlcat(sensorID, _int_to_char(buffer, i + 1), sizeof(sensorID));
JsonObject dataSensor = sensors.createNestedObject(sensorID);
dataSensor[PAYLOAD_EXTERNAL_SENSOR_ID] = ds18.getDeviceID(buffer, i);
dataSensor[PAYLOAD_EXTERNAL_SENSOR_TEMP] = sensorValue;
} }
} }
@@ -800,47 +812,45 @@ void publishEMSValues(bool force) {
JsonObject rootMixing = doc.to<JsonObject>(); JsonObject rootMixing = doc.to<JsonObject>();
for (uint8_t hc_v = 1; hc_v <= EMS_MIXING_MAXHC; hc_v++) { for (uint8_t hc_v = 1; hc_v <= EMS_MIXING_MAXHC; hc_v++) {
_EMS_MixingModule_HC * mixing = &EMS_MixingModule.hc[hc_v - 1]; _EMS_MixingModule_HC * mixingHC = &EMS_MixingModule.hc[hc_v - 1];
// only send if we have an active Heating Circuit with real data // only send if we have an active Heating Circuit with real data
if (mixing->active) { if (mixingHC->active) {
// build new json object
char hc[10]; // hc{1-4} char hc[10]; // hc{1-4}
strlcpy(hc, MIXING_HC, sizeof(hc)); strlcpy(hc, MIXING_HC, sizeof(hc));
strlcat(hc, _int_to_char(s, mixing->hc), sizeof(hc)); strlcat(hc, _int_to_char(s, mixingHC->hc), sizeof(hc));
JsonObject dataMixing = rootMixing.createNestedObject(hc); JsonObject dataMixingHC = rootMixing.createNestedObject(hc);
if (mixing->flowTemp < EMS_VALUE_USHORT_NOTSET) if (mixingHC->flowTemp < EMS_VALUE_USHORT_NOTSET)
dataMixing["flowTemp"] = (float)mixing->flowTemp / 10; dataMixingHC["flowTemp"] = (float)mixingHC->flowTemp / 10;
if (mixing->flowSetTemp != EMS_VALUE_INT_NOTSET) if (mixingHC->flowSetTemp != EMS_VALUE_INT_NOTSET)
dataMixing["setflowTemp"] = mixing->flowSetTemp; dataMixingHC["setflowTemp"] = mixingHC->flowSetTemp;
if (mixing->pumpMod != EMS_VALUE_INT_NOTSET) if (mixingHC->pumpMod != EMS_VALUE_INT_NOTSET)
dataMixing["pumpMod"] = mixing->pumpMod; dataMixingHC["pumpMod"] = mixingHC->pumpMod;
if (mixing->valveStatus != EMS_VALUE_INT_NOTSET) if (mixingHC->valveStatus != EMS_VALUE_INT_NOTSET)
dataMixing["valveStatus"] = mixing->valveStatus; dataMixingHC["valveStatus"] = mixingHC->valveStatus;
} }
} }
for (uint8_t wwc_v = 1; wwc_v <= EMS_MIXING_MAXWWC; wwc_v++) { for (uint8_t wwc_v = 1; wwc_v <= EMS_MIXING_MAXWWC; wwc_v++) {
_EMS_MixingModule_WWC * mixing = &EMS_MixingModule.wwc[wwc_v - 1]; _EMS_MixingModule_WWC * mixingWWC = &EMS_MixingModule.wwc[wwc_v - 1];
// only send if we have an active Warm water Circuit with real data // only send if we have an active Warm water Circuit with real data
if (mixing->active) { if (mixingWWC->active) {
// build new json object
char wwc[10]; // wwc{1-2} char wwc[10]; // wwc{1-2}
strlcpy(wwc, MIXING_WWC, sizeof(wwc)); strlcpy(wwc, MIXING_WWC, sizeof(wwc));
strlcat(wwc, _int_to_char(s, mixing->wwc), sizeof(wwc)); strlcat(wwc, _int_to_char(s, mixingWWC->wwc), sizeof(wwc));
JsonObject dataMixing = rootMixing.createNestedObject(wwc); JsonObject dataMixing = rootMixing.createNestedObject(wwc);
if (mixing->flowTemp < EMS_VALUE_USHORT_NOTSET) if (mixingWWC->flowTemp < EMS_VALUE_USHORT_NOTSET)
dataMixing["wwTemp"] = (float)mixing->flowTemp / 10; dataMixing["wwTemp"] = (float)mixingWWC->flowTemp / 10;
if (mixing->pumpMod != EMS_VALUE_INT_NOTSET) if (mixingWWC->pumpMod != EMS_VALUE_INT_NOTSET)
dataMixing["pumpStatus"] = mixing->pumpMod; dataMixing["pumpStatus"] = mixingWWC->pumpMod;
if (mixing->tempStatus != EMS_VALUE_INT_NOTSET) if (mixingWWC->tempStatus != EMS_VALUE_INT_NOTSET)
dataMixing["tempStatus"] = mixing->tempStatus; dataMixing["tempStatus"] = mixingWWC->tempStatus;
} }
} }
data[0] = '\0'; // reset data for next package data[0] = '\0'; // reset data for next package
serializeJson(doc, data, sizeof(data)); serializeJson(doc, data, sizeof(data));
myDebugLog("Publishing mixing device data via MQTT"); myDebugLog("Publishing mixing data via MQTT");
myESP.mqttPublish(TOPIC_MIXING_DATA, data); myESP.mqttPublish(TOPIC_MIXING_DATA, data);
ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_MIXING); // unset flag ems_Device_remove_flags(EMS_DEVICE_UPDATE_FLAG_MIXING); // unset flag
} }
@@ -1039,6 +1049,11 @@ bool LoadSaveCallback(MYESP_FSACTION_t action, JsonObject settings) {
EMSESP_Settings.master_thermostat = settings["master_thermostat"] | 0; // default to 0 (none) EMSESP_Settings.master_thermostat = settings["master_thermostat"] | 0; // default to 0 (none)
ems_setMasterThermostat(EMSESP_Settings.master_thermostat); ems_setMasterThermostat(EMSESP_Settings.master_thermostat);
EMSESP_Settings.bus_id = settings["bus_id"] | EMS_BUSID_DEFAULT; // default to 0x0B (Service Key)
ems_setEMSbusid(EMSESP_Settings.bus_id);
EMSESP_Settings.known_devices = strdup(settings["known_devices"] | "");
return true; return true;
} }
@@ -1052,7 +1067,9 @@ bool LoadSaveCallback(MYESP_FSACTION_t action, JsonObject settings) {
settings["shower_alert"] = EMSESP_Settings.shower_alert; settings["shower_alert"] = EMSESP_Settings.shower_alert;
settings["publish_time"] = EMSESP_Settings.publish_time; settings["publish_time"] = EMSESP_Settings.publish_time;
settings["tx_mode"] = EMSESP_Settings.tx_mode; settings["tx_mode"] = EMSESP_Settings.tx_mode;
settings["bus_id"] = EMSESP_Settings.bus_id;
settings["master_thermostat"] = EMSESP_Settings.master_thermostat; settings["master_thermostat"] = EMSESP_Settings.master_thermostat;
settings["known_devices"] = EMSESP_Settings.known_devices;
return true; return true;
} }
@@ -1171,6 +1188,18 @@ bool SetListCallback(MYESP_FSACTION_t action, uint8_t wc, const char * setting,
} }
} }
// bus_id
if ((strcmp(setting, "bus_id") == 0) && (wc == 2)) {
uint8_t id = strtoul(value, 0, 16);
if ((id == 0x0B) || (id == 0x0D) || (id == 0x0A) || (id == 0x0F) || (id == 0x12)) {
EMSESP_Settings.bus_id = id;
ems_setEMSbusid(id);
ok = true;
} else {
myDebug_P(PSTR("Error. Usage: set bus_id <ID>, with ID=0B, 0D, 0A, 0F or 12"));
}
}
// master_thermostat // master_thermostat
if (strcmp(setting, "master_thermostat") == 0) { if (strcmp(setting, "master_thermostat") == 0) {
if (wc == 1) { if (wc == 1) {
@@ -1206,6 +1235,7 @@ bool SetListCallback(MYESP_FSACTION_t action, uint8_t wc, const char * setting,
myDebug_P(PSTR(" dallas_gpio=%d"), EMSESP_Settings.dallas_gpio); myDebug_P(PSTR(" dallas_gpio=%d"), EMSESP_Settings.dallas_gpio);
myDebug_P(PSTR(" dallas_parasite=%s"), EMSESP_Settings.dallas_parasite ? "on" : "off"); myDebug_P(PSTR(" dallas_parasite=%s"), EMSESP_Settings.dallas_parasite ? "on" : "off");
myDebug_P(PSTR(" tx_mode=%d"), EMSESP_Settings.tx_mode); myDebug_P(PSTR(" tx_mode=%d"), EMSESP_Settings.tx_mode);
myDebug_P(PSTR(" bus_id=0x%02X"), EMSESP_Settings.bus_id);
myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Settings.listen_mode ? "on" : "off"); myDebug_P(PSTR(" listen_mode=%s"), EMSESP_Settings.listen_mode ? "on" : "off");
myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Settings.shower_timer ? "on" : "off"); myDebug_P(PSTR(" shower_timer=%s"), EMSESP_Settings.shower_timer ? "on" : "off");
myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Settings.shower_alert ? "on" : "off"); myDebug_P(PSTR(" shower_alert=%s"), EMSESP_Settings.shower_alert ? "on" : "off");
@@ -1274,6 +1304,27 @@ void TelnetCallback(uint8_t event) {
} }
} }
// get the list of know devices, as a string, and save them to the config file
void saveEMSDevices() {
if (Devices.empty()) {
return;
}
char s[100];
char buffer[16] = {0};
s[0] = '\0';
for (std::list<_Detected_Device>::iterator it = Devices.begin(); it != Devices.end(); ++it) {
strlcat(s, _hextoa(it->device_id, buffer), sizeof(s));
strlcat(s, " ", sizeof(s));
}
strlcpy(EMSESP_Settings.known_devices, s, sizeof(s));
myDebug("The device IDs %s%s%swill be automatically scanned when EMS-ESP boots up.", COLOR_BOLD_ON, EMSESP_Settings.known_devices, COLOR_BOLD_OFF);
myESP.saveSettings();
}
// extra commands options for telnet debug window // extra commands options for telnet debug window
// wc is the word count, i.e. number of arguments. Everything is in lower case. // wc is the word count, i.e. number of arguments. Everything is in lower case.
void TelnetCommandCallback(uint8_t wc, const char * commandLine) { void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
@@ -1297,32 +1348,46 @@ void TelnetCommandCallback(uint8_t wc, const char * commandLine) {
} }
if (strcmp(first_cmd, "devices") == 0) { if (strcmp(first_cmd, "devices") == 0) {
ems_printDevices(); if (wc == 1) {
ok = true; // print
ems_printDevices();
return;
}
// wc = 2 or more. check for "scan"
char * second_cmd = _readWord();
if (strcmp(second_cmd, "scan") == 0) {
if (wc == 2) {
// just scan use UBA 0x07 telegram
myDebug_P(PSTR("Requesting EMS bus master for its device list and scanning for external sensors..."));
scanDallas();
ems_clearDeviceList();
ems_doReadCommand(EMS_TYPE_UBADevices, EMS_Boiler.device_id);
return;
}
// wc is 3 or more. check for additional "force" argument
char * third_cmd = _readWord();
if (strcmp(third_cmd, "deep") == 0) {
myDebug_P(PSTR("Started deep scan of EMS bus for our known devices. This can take up to 10 seconds..."));
ems_clearDeviceList();
ems_scanDevices();
return;
}
} else if (strcmp(second_cmd, "save") == 0) {
saveEMSDevices();
return;
}
ok = false; // unknown command
} }
if (strcmp(first_cmd, "queue") == 0) { if (strcmp(first_cmd, "queue") == 0) {
ems_printTxQueue(); ems_printTxQueue();
ok = true; ok = true;
} }
if (strcmp(first_cmd, "autodetect") == 0) {
if (wc == 2) {
char * second_cmd = _readWord();
if (strcmp(second_cmd, "scan") == 0) {
ems_clearDeviceList();
ems_scanDevices();
ok = true;
}
} else {
myDebug("Scanning for new EMS devices and attached external sensors...");
scanDallas();
ems_clearDeviceList();
ems_doReadCommand(EMS_TYPE_UBADevices, EMS_Boiler.device_id);
ok = true;
}
}
// logging // logging
if ((strcmp(first_cmd, "log") == 0) && (wc >= 2)) { if ((strcmp(first_cmd, "log") == 0) && (wc >= 2)) {
char * second_cmd = _readWord(); char * second_cmd = _readWord();
@@ -1995,7 +2060,9 @@ void initEMSESP() {
EMSESP_Settings.led_gpio = EMSESP_LED_GPIO; EMSESP_Settings.led_gpio = EMSESP_LED_GPIO;
EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO; EMSESP_Settings.dallas_gpio = EMSESP_DALLAS_GPIO;
EMSESP_Settings.tx_mode = EMS_TXMODE_DEFAULT; // default tx mode EMSESP_Settings.tx_mode = EMS_TXMODE_DEFAULT; // default tx mode
EMSESP_Settings.bus_id = EMS_BUSID_DEFAULT; // Service Key is default
EMSESP_Settings.master_thermostat = 0; EMSESP_Settings.master_thermostat = 0;
EMSESP_Settings.known_devices = nullptr;
// shower settings // shower settings
EMSESP_Shower.timerStart = 0; EMSESP_Shower.timerStart = 0;
@@ -2067,6 +2134,38 @@ void showerCheck() {
} }
} }
// this is called when we've first established an EMS connection
// go send out some spies to figure out what is on the bus and who's driving it
void startupEMSscan() {
if (EMSESP_Settings.listen_mode) {
return;
}
// First scan anything we may have saved as known devices from a "devices save" command
if (strlen(EMSESP_Settings.known_devices) > 0) {
char * p;
char * temp = strdup(EMSESP_Settings.known_devices); // because strlok is destructive, make a copy
char value[10] = {0};
// get first value
if ((p = strtok(temp, " "))) { // delimiter
strlcpy(value, p, sizeof(value));
uint8_t val = (uint8_t)strtol(value, 0, 16);
ems_doReadCommand(EMS_TYPE_Version, val);
}
// and iterate until end
while (p != 0) {
if ((p = strtok(nullptr, " "))) {
strlcpy(value, p, sizeof(value));
uint8_t val = (uint8_t)strtol(value, 0, 16);
ems_doReadCommand(EMS_TYPE_Version, val);
}
}
}
// ask the Boiler to show us it's attached devices in case we missed anything
ems_discoverModels();
}
// //
// SETUP // SETUP
// //
@@ -2102,12 +2201,10 @@ void setup() {
myESP.setUseSerial(false); myESP.setUseSerial(false);
emsuart_init(); // start EMS bus transmissions emsuart_init(); // start EMS bus transmissions
myDebug_P(PSTR("[UART] Rx/Tx connection established")); myDebug_P(PSTR("[UART] Rx/Tx connection established"));
if (!EMSESP_Settings.listen_mode) { startupEMSscan();
// go and find the boiler and thermostat types, if not in listen mode
ems_discoverModels();
}
} }
// enable regular checks to fetch data and publish using Tx (unless listen_mode is enabled) // enable regular checks to fetch data and publish using Tx (unless listen_mode is enabled)
if (!EMSESP_Settings.listen_mode) { if (!EMSESP_Settings.listen_mode) {
regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS regularUpdatesTimer.attach(REGULARUPDATES_TIME, do_regularUpdates); // regular reads from the EMS

View File

@@ -95,7 +95,9 @@ void ems_init() {
EMS_Sys_Status.emsPollFrequency = 0; EMS_Sys_Status.emsPollFrequency = 0;
EMS_Sys_Status.txRetryCount = 0; EMS_Sys_Status.txRetryCount = 0;
EMS_Sys_Status.emsIDMask = 0x00; EMS_Sys_Status.emsIDMask = 0x00;
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME; EMS_Sys_Status.emsTxMode = EMS_TXMODE_DEFAULT;
EMS_Sys_Status.emsbusid = EMS_BUSID_DEFAULT;
EMS_Sys_Status.emsPollAck[0] = EMS_BUSID_DEFAULT;
// thermostat // thermostat
strlcpy(EMS_Thermostat.datetime, "?", sizeof(EMS_Thermostat.datetime)); strlcpy(EMS_Thermostat.datetime, "?", sizeof(EMS_Thermostat.datetime));
@@ -442,6 +444,11 @@ void ems_setTxMode(uint8_t mode) {
EMS_Sys_Status.emsTxMode = mode; EMS_Sys_Status.emsTxMode = mode;
} }
void ems_setEMSbusid(uint8_t id) {
EMS_Sys_Status.emsbusid = id;
EMS_Sys_Status.emsPollAck[0] = id;
}
void ems_setMasterThermostat(uint8_t product_id) { void ems_setMasterThermostat(uint8_t product_id) {
EMS_Sys_Status.emsMasterThermostat = product_id; EMS_Sys_Status.emsMasterThermostat = product_id;
} }
@@ -577,7 +584,7 @@ void _ems_sendTelegram() {
} }
// create the header // create the header
EMS_TxTelegram.data[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask; // src EMS_TxTelegram.data[0] = EMS_Sys_Status.emsbusid ^ EMS_Sys_Status.emsIDMask; // src
// dest // dest
if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) { if (EMS_TxTelegram.action == EMS_TX_TELEGRAM_WRITE) {
@@ -752,7 +759,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
if ((length >= 5) && (telegram[length - 1] == _crcCalculator(telegram, length))) { if ((length >= 5) && (telegram[length - 1] == _crcCalculator(telegram, length))) {
EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE; EMS_Sys_Status.emsTxStatus = EMS_TX_STATUS_IDLE;
EMS_Sys_Status.emsIDMask = telegram[0] & 0x80; EMS_Sys_Status.emsIDMask = telegram[0] & 0x80;
EMS_Sys_Status.emsPollAck[0] = EMS_ID_ME ^ EMS_Sys_Status.emsIDMask; EMS_Sys_Status.emsPollAck[0] = EMS_Sys_Status.emsbusid ^ EMS_Sys_Status.emsIDMask;
} else } else
return; // ignore the whole telegram Rx Telegram while in DETECT mode return; // ignore the whole telegram Rx Telegram while in DETECT mode
} }
@@ -779,7 +786,7 @@ void ems_parseTelegram(uint8_t * telegram, uint8_t length) {
uint8_t value = telegram[0]; // 1st byte of data package uint8_t value = telegram[0]; // 1st byte of data package
// check first for a Poll for us // check first for a Poll for us
if ((value ^ 0x80 ^ EMS_Sys_Status.emsIDMask) == EMS_ID_ME) { if ((value ^ 0x80 ^ EMS_Sys_Status.emsIDMask) == EMS_Sys_Status.emsbusid) {
static uint32_t _last_emsPollFrequency = 0; static uint32_t _last_emsPollFrequency = 0;
uint32_t timenow_microsecs = micros(); uint32_t timenow_microsecs = micros();
EMS_Sys_Status.emsPollFrequency = (timenow_microsecs - _last_emsPollFrequency); EMS_Sys_Status.emsPollFrequency = (timenow_microsecs - _last_emsPollFrequency);
@@ -919,7 +926,7 @@ void _printMessage(_EMS_RxTelegram * EMS_RxTelegram) {
(void)ems_getDeviceTypeDescription(dest, type_s); (void)ems_getDeviceTypeDescription(dest, type_s);
strlcat(output_str, type_s, sizeof(output_str)); strlcat(output_str, type_s, sizeof(output_str));
if (dest == EMS_ID_ME) { if (dest == EMS_Sys_Status.emsbusid) {
strlcpy(color_s, COLOR_YELLOW, sizeof(color_s)); // me strlcpy(color_s, COLOR_YELLOW, sizeof(color_s)); // me
} else if (dest == EMS_ID_NONE) { } else if (dest == EMS_ID_NONE) {
strlcpy(color_s, COLOR_GREEN, sizeof(color_s)); // broadcast strlcpy(color_s, COLOR_GREEN, sizeof(color_s)); // broadcast
@@ -1753,7 +1760,7 @@ bool _addDevice(_EMS_DEVICE_TYPE device_type, uint8_t product_id, uint8_t device
/** /**
* type 0x07 - shows us the connected EMS devices * type 0x07 - shows us the connected EMS devices
* e.g. 08 00 07 00 0B 80 00 00 00 00 00 00 00 00 00 00 00 (CRC=47) #data=13 * e.g. 08 00 07 00 0B 80 00 00 00 00 00 00 00 00 00 00 00 (CRC=47) #data=13
* Junkers is 15 (I think) * Junkers has 15 bytes of data
*/ */
void _process_UBADevices(_EMS_RxTelegram * EMS_RxTelegram) { void _process_UBADevices(_EMS_RxTelegram * EMS_RxTelegram) {
if (EMS_RxTelegram->data_length > EMS_SYS_DEVICEMAP_LENGTH) { if (EMS_RxTelegram->data_length > EMS_SYS_DEVICEMAP_LENGTH) {
@@ -1774,8 +1781,8 @@ void _process_UBADevices(_EMS_RxTelegram * EMS_RxTelegram) {
for (uint8_t bit = 0; bit < 8; bit++) { for (uint8_t bit = 0; bit < 8; bit++) {
if ((byte & 0x01) && ((saved_byte & 0x01) == 0)) { if ((byte & 0x01) && ((saved_byte & 0x01) == 0)) {
uint8_t device_id = ((data_byte + 1) * 8) + bit; uint8_t device_id = ((data_byte + 1) * 8) + bit;
if (device_id != EMS_ID_ME) { if (device_id != EMS_Sys_Status.emsbusid) {
// myDebug("[EMS] Detected new EMS Device with ID 0x%02X", device_id); myDebug("[EMS] Detected new EMS Device with ID 0x%02X. Fetching version information...", device_id);
if (!ems_getTxDisabled()) { if (!ems_getTxDisabled()) {
ems_doReadCommand(EMS_TYPE_Version, device_id); // get version, but ignore ourselves ems_doReadCommand(EMS_TYPE_Version, device_id); // get version, but ignore ourselves
} }
@@ -1921,7 +1928,7 @@ void _process_Version(_EMS_RxTelegram * EMS_RxTelegram) {
* Figure out the boiler and thermostat types * Figure out the boiler and thermostat types
*/ */
void ems_discoverModels() { void ems_discoverModels() {
//myDebug_P(PSTR("Starting auto discover of EMS devices...")); // ask Boiler for it's known devices
ems_doReadCommand(EMS_TYPE_UBADevices, EMS_ID_BOILER); ems_doReadCommand(EMS_TYPE_UBADevices, EMS_ID_BOILER);
} }
@@ -2107,7 +2114,7 @@ bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer) {
// check for the fixed device IDs we already know about, like 0x00 for broadcast, 0x0B for me, 0x08 for Boiler // check for the fixed device IDs we already know about, like 0x00 for broadcast, 0x0B for me, 0x08 for Boiler
if (device_id == EMS_ID_BOILER) { if (device_id == EMS_ID_BOILER) {
device_type = EMS_DEVICE_TYPE_BOILER; device_type = EMS_DEVICE_TYPE_BOILER;
} else if (device_id == EMS_ID_ME) { } else if (device_id == EMS_Sys_Status.emsbusid) {
device_type = EMS_DEVICE_TYPE_SERVICEKEY; device_type = EMS_DEVICE_TYPE_SERVICEKEY;
} else if (device_id == EMS_ID_NONE) { } else if (device_id == EMS_ID_NONE) {
device_type = EMS_DEVICE_TYPE_NONE; device_type = EMS_DEVICE_TYPE_NONE;
@@ -2225,7 +2232,7 @@ void ems_printDevices() {
for (uint8_t bit = 0; bit < 8; bit++) { for (uint8_t bit = 0; bit < 8; bit++) {
if (byte & 0x01) { if (byte & 0x01) {
uint8_t device_id = ((data_byte + 1) * 8) + bit; uint8_t device_id = ((data_byte + 1) * 8) + bit;
if (device_id != EMS_ID_ME) { if (device_id != EMS_Sys_Status.emsbusid) {
strlcat(s, " 0x", sizeof(s)); strlcat(s, " 0x", sizeof(s));
strlcat(s, _hextoa(device_id, buffer), sizeof(s)); strlcat(s, _hextoa(device_id, buffer), sizeof(s));
} }
@@ -2236,7 +2243,6 @@ void ems_printDevices() {
} }
strlcat(s, COLOR_BOLD_OFF, sizeof(s)); strlcat(s, COLOR_BOLD_OFF, sizeof(s));
myDebug_P(PSTR("")); // newline
myDebug(s); myDebug(s);
// print out the ones we recognized // print out the ones we recognized
@@ -2312,7 +2318,7 @@ void ems_sendRawTelegram(char * telegram) {
strlcpy(value, p, sizeof(value)); strlcpy(value, p, sizeof(value));
EMS_TxTelegram.data[0] = (uint8_t)strtol(value, 0, 16); EMS_TxTelegram.data[0] = (uint8_t)strtol(value, 0, 16);
} }
// and interate until end // and iterate until end
while (p != 0) { while (p != 0) {
if ((p = strtok(nullptr, " ,"))) { if ((p = strtok(nullptr, " ,"))) {
strlcpy(value, p, sizeof(value)); strlcpy(value, p, sizeof(value));
@@ -2785,10 +2791,10 @@ void ems_setWarmTapWaterActivated(bool activated) {
EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type; EMS_TxTelegram.comparisonPostRead = EMS_TxTelegram.type;
// create header // create header
EMS_TxTelegram.data[0] = EMS_ID_ME; // src EMS_TxTelegram.data[0] = EMS_Sys_Status.emsbusid; // src
EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest EMS_TxTelegram.data[1] = EMS_TxTelegram.dest; // dest
EMS_TxTelegram.data[2] = EMS_TxTelegram.type; // type EMS_TxTelegram.data[2] = EMS_TxTelegram.type; // type
EMS_TxTelegram.data[3] = EMS_TxTelegram.offset; // offset EMS_TxTelegram.data[3] = EMS_TxTelegram.offset; // offset
// we use the special test mode 0x1D for this. Setting the first data to 5A puts the system into test mode and // we use the special test mode 0x1D for this. Setting the first data to 5A puts the system into test mode and
// a setting of 0x00 puts it back into normal operating mode // a setting of 0x00 puts it back into normal operating mode
@@ -2823,11 +2829,11 @@ void ems_startupTelegrams() {
char s[20] = {0}; char s[20] = {0};
// Write type 0x1D to get out of function test mode // Write type 0x1D to get out of function test mode
snprintf(s, sizeof(s), "%02X %02X 1D 00 00", EMS_ID_ME, EMS_Boiler.device_id); snprintf(s, sizeof(s), "%02X %02X 1D 00 00", EMS_Sys_Status.emsbusid, EMS_Boiler.device_id);
ems_sendRawTelegram(s); ems_sendRawTelegram(s);
// Read type 0x01 // Read type 0x01
snprintf(s, sizeof(s), "%02X %02X 01 00 1B", EMS_ID_ME, EMS_Boiler.device_id | 0x80); snprintf(s, sizeof(s), "%02X %02X 01 00 1B", EMS_Sys_Status.emsbusid, EMS_Boiler.device_id | 0x80);
ems_sendRawTelegram(s); ems_sendRawTelegram(s);
} }
@@ -2862,7 +2868,7 @@ void ems_testTelegram(uint8_t test_num) {
telegram[0] = (uint8_t)strtol(value, 0, 16); telegram[0] = (uint8_t)strtol(value, 0, 16);
} }
// and interate until end // and iterate until end
while (p != 0) { while (p != 0) {
if ((p = strtok(nullptr, " ,"))) { if ((p = strtok(nullptr, " ,"))) {
strlcpy(value, p, sizeof(value)); strlcpy(value, p, sizeof(value));
@@ -3011,7 +3017,7 @@ void _ems_processTelegram(_EMS_RxTelegram * EMS_RxTelegram) {
// we're only interested in broadcast messages (dest is 0x00) or ones for us (dest is 0x0B) // we're only interested in broadcast messages (dest is 0x00) or ones for us (dest is 0x0B)
uint8_t dest = EMS_RxTelegram->dest; uint8_t dest = EMS_RxTelegram->dest;
if ((dest != EMS_ID_NONE) && (dest != EMS_ID_ME)) { if ((dest != EMS_ID_NONE) && (dest != EMS_Sys_Status.emsbusid)) {
return; return;
} }
@@ -3048,7 +3054,7 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram) {
uint8_t * telegram = EMS_RxTelegram->telegram; uint8_t * telegram = EMS_RxTelegram->telegram;
// if its an echo of ourselves from the master UBA, ignore. This should never happen mind you // if its an echo of ourselves from the master UBA, ignore. This should never happen mind you
if (EMS_RxTelegram->src == EMS_ID_ME) { if (EMS_RxTelegram->src == EMS_Sys_Status.emsbusid) {
if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_JABBER) if (EMS_Sys_Status.emsLogging == EMS_SYS_LOGGING_JABBER)
_debugPrintTelegram("echo: ", EMS_RxTelegram, COLOR_WHITE); _debugPrintTelegram("echo: ", EMS_RxTelegram, COLOR_WHITE);
return; return;
@@ -3066,7 +3072,7 @@ void _processType(_EMS_RxTelegram * EMS_RxTelegram) {
// at this point we can assume TxStatus was EMS_TX_STATUS_WAIT as we just sent a read or validate telegram // at this point we can assume TxStatus was EMS_TX_STATUS_WAIT as we just sent a read or validate telegram
// for READ or VALIDATE the dest (telegram[1]) is always us, so check for this // for READ or VALIDATE the dest (telegram[1]) is always us, so check for this
// and if not we probably didn't get any response so remove the last Tx from the queue and process the telegram anyway // and if not we probably didn't get any response so remove the last Tx from the queue and process the telegram anyway
if ((telegram[1] & 0x7F) != EMS_ID_ME) { if ((telegram[1] & 0x7F) != EMS_Sys_Status.emsbusid) {
_removeTxQueue(); _removeTxQueue();
_ems_processTelegram(EMS_RxTelegram); _ems_processTelegram(EMS_RxTelegram);
return; return;
@@ -3215,8 +3221,6 @@ void ems_doReadCommand(uint16_t type, uint8_t dest) {
* Find the versions of our connected devices * Find the versions of our connected devices
*/ */
void ems_scanDevices() { void ems_scanDevices() {
myDebug_P(PSTR("Started scanning the EMS bus for known devices. This can take up to 10 seconds..."));
std::list<uint8_t> Device_Ids; // create a new list std::list<uint8_t> Device_Ids; // create a new list
Device_Ids.push_back(EMS_ID_BOILER); // UBAMaster/Boilers - 0x08 Device_Ids.push_back(EMS_ID_BOILER); // UBAMaster/Boilers - 0x08

View File

@@ -12,6 +12,9 @@
#include <Arduino.h> #include <Arduino.h>
#include <list> // std::list #include <list> // std::list
// EMS bus IDs
#define EMS_BUSID_DEFAULT 0x0B // Default 0x0B (Service Key)
// EMS tx_mode types // EMS tx_mode types
#define EMS_TXMODE_DEFAULT 1 // Default (was previously known as tx_mode 2 in v1.8.x) #define EMS_TXMODE_DEFAULT 1 // Default (was previously known as tx_mode 2 in v1.8.x)
#define EMS_TXMODE_EMSPLUS 2 // EMS+ #define EMS_TXMODE_EMSPLUS 2 // EMS+
@@ -147,6 +150,7 @@ typedef struct {
uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80 uint8_t emsIDMask; // Buderus: 0x00, Junkers: 0x80
uint8_t emsPollAck[1]; // acknowledge buffer for Poll uint8_t emsPollAck[1]; // acknowledge buffer for Poll
uint8_t emsTxMode; // Tx mode 1, 2 or 3 uint8_t emsTxMode; // Tx mode 1, 2 or 3
uint8_t emsbusid; // EMS bus ID, default 0x0B for Service Key
uint8_t emsMasterThermostat; // product ID for the default thermostat to use uint8_t emsMasterThermostat; // product ID for the default thermostat to use
char emsDeviceMap[EMS_SYS_DEVICEMAP_LENGTH]; // contents of 0x07 telegram with bitmasks for all active EMS devices char emsDeviceMap[EMS_SYS_DEVICEMAP_LENGTH]; // contents of 0x07 telegram with bitmasks for all active EMS devices
} _EMS_Sys_Status; } _EMS_Sys_Status;
@@ -235,7 +239,7 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
{EMS_DEVICE_TYPE_UNKNOWN, EMS_MODELTYPE_UNKNOWN_STRING}, // the first, at index 0 is reserved for "unknown" {EMS_DEVICE_TYPE_UNKNOWN, EMS_MODELTYPE_UNKNOWN_STRING}, // the first, at index 0 is reserved for "unknown"
{EMS_DEVICE_TYPE_NONE, "All"}, {EMS_DEVICE_TYPE_NONE, "All"},
{EMS_DEVICE_TYPE_SERVICEKEY, "Me"}, {EMS_DEVICE_TYPE_SERVICEKEY, "Me"},
{EMS_DEVICE_TYPE_BOILER, "UBAMaster"}, {EMS_DEVICE_TYPE_BOILER, "Boiler"},
{EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"}, {EMS_DEVICE_TYPE_THERMOSTAT, "Thermostat"},
{EMS_DEVICE_TYPE_MIXING, "Mixing Module"}, {EMS_DEVICE_TYPE_MIXING, "Mixing Module"},
{EMS_DEVICE_TYPE_SOLAR, "Solar Module"}, {EMS_DEVICE_TYPE_SOLAR, "Solar Module"},
@@ -243,7 +247,7 @@ const _EMS_Device_Types EMS_Devices_Types[] = {
{EMS_DEVICE_TYPE_GATEWAY, "Gateway"}, {EMS_DEVICE_TYPE_GATEWAY, "Gateway"},
{EMS_DEVICE_TYPE_SWITCH, "Switching Module"}, {EMS_DEVICE_TYPE_SWITCH, "Switching Module"},
{EMS_DEVICE_TYPE_CONTROLLER, "Controller"}, {EMS_DEVICE_TYPE_CONTROLLER, "Controller"},
{EMS_DEVICE_TYPE_CONNECT, "Connect"} {EMS_DEVICE_TYPE_CONNECT, "Connect Module"}
}; };
@@ -462,6 +466,7 @@ void ems_setWarmWaterModeComfort(uint8_t comfort);
void ems_setModels(); void ems_setModels();
void ems_setTxDisabled(bool b); void ems_setTxDisabled(bool b);
void ems_setTxMode(uint8_t mode); void ems_setTxMode(uint8_t mode);
void ems_setEMSbusid(uint8_t id);
void ems_setMasterThermostat(uint8_t product_id); void ems_setMasterThermostat(uint8_t product_id);
char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false); char * ems_getDeviceDescription(_EMS_DEVICE_TYPE device_type, char * buffer, bool name_only = false);
bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer); bool ems_getDeviceTypeDescription(uint8_t device_id, char * buffer);

View File

@@ -12,7 +12,6 @@
#include "ems.h" #include "ems.h"
// Fixed EMS Device IDs // Fixed EMS Device IDs
#define EMS_ID_ME 0x0B // our device, hardcoded as the "Service Key"
#define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08 #define EMS_ID_BOILER 0x08 // all UBA Boilers have 0x08
/* /*

View File

@@ -1 +1 @@
#define APP_VERSION "1.9.5b34" #define APP_VERSION "1.9.5b35"

View File

@@ -98,7 +98,8 @@ var custom_configfile = {
"shower_timer": true, "shower_timer": true,
"shower_alert": false, "shower_alert": false,
"publish_time": 10, "publish_time": 10,
"tx_mode": 1 "tx_mode": 1,
"bus_id": 11
} }
}; };