improvements to rendering floats

This commit is contained in:
proddy
2019-03-24 11:54:49 +01:00
parent 599171202c
commit c7c07eb1c4
6 changed files with 177 additions and 150 deletions

View File

@@ -182,6 +182,7 @@ int16_t DS18::getRawValue(unsigned char index) {
}
// return real value as a double
// The raw temperature data is in units of sixteenths of a degree, so the value must be divided by 16 in order to convert it to degrees.
double DS18::getValue(unsigned char index) {
double value = (float)getRawValue(index) / 16.0;
return value;

View File

@@ -161,56 +161,42 @@ char * _bool_to_char(char * s, uint8_t value) {
// convert short (two bytes) to text value
// negative values are assumed stored as 1-compliment (https://medium.com/@LeeJulija/how-integers-are-stored-in-memory-using-twos-complement-5ba04d61a56c)
char * _short_to_char(char * s, int16_t value, uint8_t div = 10) {
char * _short_to_char(char * s, int16_t value, uint8_t decimals = 1) {
// remove errors on invalid values
if (abs(value) >= EMS_VALUE_SHORT_NOTSET) {
strlcpy(s, "?", sizeof(s));
return (s);
}
if (div != 0) {
char s2[5] = {0};
// check for negative values
if (value < 0) {
strlcpy(s, "-", 2);
strlcat(s, itoa(abs(value) / div, s2, 10), 5);
} else {
strlcpy(s, itoa(value / div, s2, 10), 5);
}
strlcat(s, ".", sizeof(s));
strlcat(s, itoa(abs(value) % div, s2, 10), 5);
} else {
if (decimals == 0) {
itoa(value, s, 10);
return (s);
}
// floating point
char s2[5] = {0};
// check for negative values
if (value < 0) {
strlcpy(s, "-", 2);
value = abs(value);
}
strlcpy(s, itoa(value / (decimals * 10), s2, 10), 5);
strlcat(s, ".", sizeof(s));
strlcat(s, itoa(value % (decimals * 10), s2, 10), 5);
return s;
}
// convert int (single byte) to text value
char * _int_to_char(char * s, uint8_t value, uint8_t div = 0) {
if (value == EMS_VALUE_INT_NOTSET) {
strlcpy(s, "?", sizeof(s));
} else {
if (div != 0) {
char s2[5] = {0};
strlcpy(s, itoa(value / div, s2, 10), 5);
strlcat(s, ".", sizeof(s));
strlcat(s, itoa(value % div, s2, 10), 5);
} else {
itoa(value, s, 10);
}
}
return s;
}
// takes an int value (1 byte), converts to a fraction
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div = 0) {
// takes a short value (2 bytes), converts to a fraction
// most values stored a s short are either *10 or *100
void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t decimals = 1) {
char buffer[200] = {0};
char s[20] = {0};
strlcpy(buffer, " ", sizeof(buffer));
strlcat(buffer, prefix, sizeof(buffer));
strlcat(buffer, ": ", sizeof(buffer));
strlcat(buffer, _int_to_char(s, value, div), sizeof(buffer));
strlcat(buffer, _short_to_char(s, value, decimals), sizeof(buffer));
if (postfix != NULL) {
strlcat(buffer, " ", sizeof(buffer));
@@ -220,15 +206,49 @@ void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, u
myDebug(buffer);
}
// takes a short value (2 bytes), converts to a fraction
void _renderShortValue(const char * prefix, const char * postfix, int16_t value, uint8_t div = 10) {
// convert int (single byte) to text value
char * _int_to_char(char * s, uint8_t value, uint8_t div = 1) {
if (value == EMS_VALUE_INT_NOTSET) {
strlcpy(s, "?", sizeof(s));
return (s);
}
char s2[5] = {0};
switch (div) {
case 1:
itoa(value, s, 10);
break;
case 2:
strlcpy(s, itoa(value >> 1, s2, 10), 5);
strlcat(s, ".", sizeof(s));
strlcat(s, ((value & 0x01) ? "5" : "0"), 5);
break;
case 10:
strlcpy(s, itoa(value / 10, s2, 10), 5);
strlcat(s, ".", sizeof(s));
strlcat(s, itoa(value % 10, s2, 10), 5);
break;
default:
itoa(value, s, 10);
break;
}
return s;
}
// takes an int value (1 byte), converts to a fraction
void _renderIntValue(const char * prefix, const char * postfix, uint8_t value, uint8_t div = 1) {
char buffer[200] = {0};
char s[20] = {0};
strlcpy(buffer, " ", sizeof(buffer));
strlcat(buffer, prefix, sizeof(buffer));
strlcat(buffer, ": ", sizeof(buffer));
strlcat(buffer, _short_to_char(s, value, div), sizeof(buffer));
strlcat(buffer, _int_to_char(s, value, div), sizeof(buffer));
if (postfix != NULL) {
strlcat(buffer, " ", sizeof(buffer));
@@ -422,11 +442,16 @@ void showInfo() {
if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) {
// for easy temps are * 100
// also we don't have the time or mode
_renderShortValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 100);
_renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 100);
} else {
_renderShortValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 2);
_renderShortValue("Set room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 10);
_renderShortValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 10);
} else {
// because we store in 2 bytes short, when converting to a single byte we'll loose the negative value if its unset
if ((EMS_Thermostat.setpoint_roomTemp <= 0) || (EMS_Thermostat.curr_roomTemp <= 0)) {
EMS_Thermostat.setpoint_roomTemp = EMS_VALUE_INT_NOTSET;
EMS_Thermostat.curr_roomTemp = EMS_VALUE_INT_NOTSET;
}
_renderIntValue("Setpoint room temperature", "C", EMS_Thermostat.setpoint_roomTemp, 2); // convert to a single byte * 2
_renderIntValue("Current room temperature", "C", EMS_Thermostat.curr_roomTemp, 10); // is *10
myDebug(" Thermostat time is %02d:%02d:%02d %d/%d/%d",
EMS_Thermostat.hour,
@@ -451,12 +476,12 @@ void showInfo() {
// Dallas
if (EMSESP_Status.dallas_sensors != 0) {
char s[80] = {0};
//char s[80] = {0};
char buffer[128] = {0};
char valuestr[8] = {0}; // for formatting temp
myDebug("%sExternal temperature sensors:%s", COLOR_BOLD_ON, COLOR_BOLD_OFF);
for (uint8_t i = 0; i < EMSESP_Status.dallas_sensors; i++) {
snprintf(s, sizeof(s), "Sensor #%d %s", i + 1, ds18.getDeviceString(buffer, i));
_renderShortValue(s, "C", ds18.getRawValue(i), 16); // divide by 16
myDebug(" Sensor #%d %s: %s C", i + 1, ds18.getDeviceString(buffer, i), _float_to_char(valuestr, ds18.getValue(i) ));
}
myDebug(""); // newline
}
@@ -524,9 +549,8 @@ void publishValues(bool force) {
rootBoiler["wWComfort"] = "Intelligent";
}
rootBoiler["wWCurTmp"] = _short_to_char(s, EMS_Boiler.wWCurTmp);
snprintf(s, sizeof(s), "%i.%i", EMS_Boiler.wWCurFlow / 10, EMS_Boiler.wWCurFlow % 10);
rootBoiler["wWCurFlow"] = s;
rootBoiler["wWCurTmp"] = _short_to_char(s, EMS_Boiler.wWCurTmp);
rootBoiler["wWCurFlow"] = _int_to_char(s, EMS_Boiler.wWCurFlow, 10);
rootBoiler["wWHeat"] = _bool_to_char(s, EMS_Boiler.wWHeat);
rootBoiler["curFlowTemp"] = _short_to_char(s, EMS_Boiler.curFlowTemp);
rootBoiler["retTemp"] = _short_to_char(s, EMS_Boiler.retTemp);
@@ -571,7 +595,7 @@ void publishValues(bool force) {
// handle the thermostat values separately
if (ems_getThermostatEnabled()) {
// only send thermostat values if we actually have them
if ((EMS_Thermostat.curr_roomTemp == 0) || (EMS_Thermostat.setpoint_roomTemp == 0))
if ((EMS_Thermostat.curr_roomTemp <= 0) || (EMS_Thermostat.setpoint_roomTemp <= 0))
return;
// build new json object
@@ -579,11 +603,11 @@ void publishValues(bool force) {
JsonObject rootThermostat = doc.to<JsonObject>();
if ((ems_getThermostatModel() == EMS_MODEL_EASY) || (ems_getThermostatModel() == EMS_MODEL_BOSCHEASY)) {
rootThermostat[THERMOSTAT_CURRTEMP] = _short_to_char(s, EMS_Thermostat.curr_roomTemp, 100);
rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 100);
} else {
rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 10);
rootThermostat[THERMOSTAT_CURRTEMP] = _short_to_char(s, EMS_Thermostat.curr_roomTemp, 10);
rootThermostat[THERMOSTAT_SELTEMP] = _short_to_char(s, EMS_Thermostat.setpoint_roomTemp, 2);
} else {
rootThermostat[THERMOSTAT_SELTEMP] = _int_to_char(s, EMS_Thermostat.setpoint_roomTemp, 2);
rootThermostat[THERMOSTAT_CURRTEMP] = _int_to_char(s, EMS_Thermostat.curr_roomTemp, 10);
}
// RC20 has different mode settings
@@ -701,6 +725,61 @@ char * _readWord() {
return word;
}
// publish external dallas sensor temperature values to MQTT
void do_publishSensorValues() {
if (EMSESP_Status.dallas_sensors != 0) {
publishSensorValues();
}
}
// call PublishValues without forcing, so using CRC to see if we really need to publish
void do_publishValues() {
// don't publish if we're not connected to the EMS bus
if ((ems_getBusConnected()) && (!myESP.getUseSerial()) && myESP.isMQTTConnected()) {
publishValues(false);
}
}
// callback to light up the LED, called via Ticker every second
// fast way is to use WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << EMSESP_Status.led_gpio)); // 4 is on, 8 is off
void do_ledcheck() {
if (EMSESP_Status.led) {
if (ems_getBusConnected()) {
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
} else {
int state = digitalRead(EMSESP_Status.led_gpio);
digitalWrite(EMSESP_Status.led_gpio, !state);
}
}
}
// Thermostat scan
void do_scanThermostat() {
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
myDebug("> Scanning thermostat message type #0x%02X...", scanThermostat_count);
ems_doReadCommand(scanThermostat_count, EMS_Thermostat.type_id);
scanThermostat_count++;
}
}
// do a system health check every now and then to see if we all connections
void do_systemCheck() {
if ((!ems_getBusConnected()) && (!myESP.getUseSerial())) {
myDebug("Error! Unable to read from EMS bus. Retrying in %d seconds...", SYSTEMCHECK_TIME);
}
}
// force calls to get data from EMS for the types that aren't sent as broadcasts
// only if we have a EMS connection
void do_regularUpdates() {
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
myDebugLog("Calling scheduled data refresh from EMS devices...");
ems_getThermostatValues();
ems_getBoilerValues();
ems_getOtherValues();
}
}
// initiate a force scan by sending type read requests from 0 to FF to the thermostat
// used to analyze responses for debugging
void startThermostatScan(uint8_t start) {
@@ -713,6 +792,27 @@ void startThermostatScan(uint8_t start) {
scanThermostat.attach(SCANTHERMOSTAT_TIME, do_scanThermostat);
}
// turn back on the hot water for the shower
void _showerColdShotStop() {
if (EMSESP_Shower.doingColdShot) {
myDebugLog("[Shower] finished shot of cold. hot water back on");
ems_setWarmTapWaterActivated(true);
EMSESP_Shower.doingColdShot = false;
showerColdShotStopTimer.detach(); // disable the timer
}
}
// turn off hot water to send a shot of cold
void _showerColdShotStart() {
if (EMSESP_Status.shower_alert) {
myDebugLog("[Shower] doing a shot of cold water");
ems_setWarmTapWaterActivated(false);
EMSESP_Shower.doingColdShot = true;
// start the timer for n seconds which will reset the water back to hot
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
}
}
// callback for loading/saving settings to the file system (SPIFFS)
bool FSCallback(MYESP_FSACTION action, const JsonObject json) {
bool recreate_config = true;
@@ -1235,82 +1335,6 @@ void initEMSESP() {
EMSESP_Shower.doingColdShot = false;
}
// publish external dallas sensor temperature values to MQTT
void do_publishSensorValues() {
if (EMSESP_Status.dallas_sensors != 0) {
publishSensorValues();
}
}
// call PublishValues without forcing, so using CRC to see if we really need to publish
void do_publishValues() {
// don't publish if we're not connected to the EMS bus
if ((ems_getBusConnected()) && (!myESP.getUseSerial()) && myESP.isMQTTConnected()) {
publishValues(false);
}
}
// callback to light up the LED, called via Ticker every second
// fast way is to use WRITE_PERI_REG(PERIPHS_GPIO_BASEADDR + (state ? 4 : 8), (1 << EMSESP_Status.led_gpio)); // 4 is on, 8 is off
void do_ledcheck() {
if (EMSESP_Status.led) {
if (ems_getBusConnected()) {
digitalWrite(EMSESP_Status.led_gpio, (EMSESP_Status.led_gpio == LED_BUILTIN) ? LOW : HIGH); // light on. For onboard LED high=off
} else {
int state = digitalRead(EMSESP_Status.led_gpio);
digitalWrite(EMSESP_Status.led_gpio, !state);
}
}
}
// Thermostat scan
void do_scanThermostat() {
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
myDebug("> Scanning thermostat message type #0x%02X...", scanThermostat_count);
ems_doReadCommand(scanThermostat_count, EMS_Thermostat.type_id);
scanThermostat_count++;
}
}
// do a system health check every now and then to see if we all connections
void do_systemCheck() {
if ((!ems_getBusConnected()) && (!myESP.getUseSerial())) {
myDebug("Error! Unable to read from EMS bus. Retrying in %d seconds...", SYSTEMCHECK_TIME);
}
}
// force calls to get data from EMS for the types that aren't sent as broadcasts
// only if we have a EMS connection
void do_regularUpdates() {
if ((ems_getBusConnected()) && (!myESP.getUseSerial())) {
myDebugLog("Calling scheduled data refresh from EMS devices...");
ems_getThermostatValues();
ems_getBoilerValues();
ems_getOtherValues();
}
}
// turn off hot water to send a shot of cold
void _showerColdShotStart() {
if (EMSESP_Status.shower_alert) {
myDebugLog("[Shower] doing a shot of cold water");
ems_setWarmTapWaterActivated(false);
EMSESP_Shower.doingColdShot = true;
// start the timer for n seconds which will reset the water back to hot
showerColdShotStopTimer.attach(SHOWER_COLDSHOT_DURATION, _showerColdShotStop);
}
}
// turn back on the hot water for the shower
void _showerColdShotStop() {
if (EMSESP_Shower.doingColdShot) {
myDebugLog("[Shower] finished shot of cold. hot water back on");
ems_setWarmTapWaterActivated(true);
EMSESP_Shower.doingColdShot = false;
showerColdShotStopTimer.detach(); // disable the timer
}
}
/*
* Shower Logic
*/

View File

@@ -14,11 +14,6 @@
#include <MyESP.h>
#include <list> // std::list
#define _toByte(i) (data[i])
#define _toShort(i) ((data[i] << 8) + data[i + 1])
#define _toLong(i) ((data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]))
#define _bitRead(i, bit) (((data[i]) >> (bit)) & 0x01)
// myESP for logging to telnet and serial
#define myDebug(...) myESP.myDebug(__VA_ARGS__)
@@ -26,7 +21,15 @@ _EMS_Sys_Status EMS_Sys_Status; // EMS Status
CircularBuffer<_EMS_TxTelegram, EMS_TX_TELEGRAM_QUEUE_MAX> EMS_TxQueue; // FIFO queue for Tx send buffer
// callbacks per type
//
// process callbacks per type
//
// macros used in the _process* functions
#define _toByte(i) (data[i])
#define _toShort(i) ((data[i] << 8) + data[i + 1])
#define _toLong(i) ((data[i] << 16) + (data[i + 1] << 8) + (data[i + 2]))
#define _bitRead(i, bit) (((data[i]) >> (bit)) & 0x01)
// generic
void _process_Version(uint8_t src, uint8_t * data, uint8_t length);
@@ -1191,7 +1194,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
if (typeFound) {
// its a boiler
myDebug("Boiler found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
myDebug("Boiler found. Model %s with TypeID 0x%02X, ProductID %d, Version %s",
Boiler_Types[i].model_string,
Boiler_Types[i].type_id,
product_id,
@@ -1200,7 +1203,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
// if its a boiler set it
// it will take the first one found in the list
if ((EMS_Boiler.type_id == EMS_ID_NONE) || (EMS_Boiler.type_id == Boiler_Types[i].type_id)) {
myDebug("* Setting Boiler type to Model %s, TypeID 0x%02X, Product ID %d, Version %s",
myDebug("* Setting Boiler type to Model %s, TypeID 0x%02X, ProductID %d, Version %s",
Boiler_Types[i].model_string,
Boiler_Types[i].type_id,
product_id,
@@ -1230,7 +1233,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
if (typeFound) {
// its a known thermostat
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Thermostat found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
myDebug("Thermostat found. Model %s with TypeID 0x%02X, ProductID %d, Version %s",
Thermostat_Types[i].model_string,
Thermostat_Types[i].type_id,
product_id,
@@ -1240,7 +1243,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
// if we don't have a thermostat set, use this one
if ((EMS_Thermostat.type_id == EMS_ID_NONE) || (EMS_Thermostat.model_id == EMS_MODEL_NONE)
|| (EMS_Thermostat.type_id == Thermostat_Types[i].type_id)) {
myDebug("* Setting Thermostat type to Model %s, TypeID 0x%02X, Product ID %d, Version %s",
myDebug("* Setting Thermostat type to Model %s, TypeID 0x%02X, ProductID %d, Version %s",
Thermostat_Types[i].model_string,
Thermostat_Types[i].type_id,
product_id,
@@ -1272,7 +1275,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
}
if (typeFound) {
myDebug("Device found. Model %s with TypeID 0x%02X, Product ID %d, Version %s",
myDebug("Device found. Model %s with TypeID 0x%02X, ProductID %d, Version %s",
Other_Types[i].model_string,
Other_Types[i].type_id,
product_id,
@@ -1289,7 +1292,7 @@ void _process_Version(uint8_t src, uint8_t * data, uint8_t length) {
return;
} else {
myDebug("Unrecognized device found. TypeID 0x%02X, Product ID %d, Version %s", src, product_id, version);
myDebug("Unrecognized device found. TypeID 0x%02X, ProductID %d, Version %s", src, product_id, version);
}
}
@@ -1339,7 +1342,7 @@ void _ems_setThermostatModel(uint8_t thermostat_modelid) {
// set the thermostat
if (EMS_Sys_Status.emsLogging >= EMS_SYS_LOGGING_BASIC) {
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, Product ID %d",
myDebug("Setting Thermostat. Model %s with TypeID 0x%02X, ProductID %d",
thermostat_type->model_string,
thermostat_type->type_id,
thermostat_type->product_id);
@@ -1485,7 +1488,7 @@ char * ems_getThermostatDescription(char * buffer) {
strlcat(buffer, _hextoa(EMS_Thermostat.type_id, tmp), size);
}
strlcat(buffer, " (Product ID:", size);
strlcat(buffer, " (ProductID:", size);
strlcat(buffer, itoa(EMS_Thermostat.product_id, tmp, 10), size);
strlcat(buffer, " Version:", size);
strlcat(buffer, EMS_Thermostat.version, size);
@@ -1522,7 +1525,7 @@ char * ems_getBoilerDescription(char * buffer) {
strlcat(buffer, _hextoa(EMS_Boiler.type_id, tmp), size);
}
strlcat(buffer, " (Product ID:", size);
strlcat(buffer, " (ProductID:", size);
strlcat(buffer, itoa(EMS_Boiler.product_id, tmp, 10), size);
strlcat(buffer, " Version:", size);
strlcat(buffer, EMS_Boiler.version, size);
@@ -1574,12 +1577,12 @@ void ems_printAllTypes() {
myDebug("\nThese %d boiler type devices are in the library:", _Boiler_Types_max);
for (i = 0; i < _Boiler_Types_max; i++) {
myDebug(" %s, type ID:0x%02X Product ID:%d", Boiler_Types[i].model_string, Boiler_Types[i].type_id, Boiler_Types[i].product_id);
myDebug(" %s, type ID:0x%02X ProductID:%d", Boiler_Types[i].model_string, Boiler_Types[i].type_id, Boiler_Types[i].product_id);
}
myDebug("\nThese %d EMS devices are in the library:", _Other_Types_max);
for (i = 0; i < _Other_Types_max; i++) {
myDebug(" %s, type ID:0x%02X Product ID:%d", Other_Types[i].model_string, Other_Types[i].type_id, Other_Types[i].product_id);
myDebug(" %s, type ID:0x%02X ProductID:%d", Other_Types[i].model_string, Other_Types[i].type_id, Other_Types[i].product_id);
}
myDebug("\nThese telegram type IDs are recognized for the selected boiler:");
@@ -1591,7 +1594,7 @@ void ems_printAllTypes() {
myDebug("\nThese %d thermostats models are supported:", _Thermostat_Types_max);
for (i = 0; i < _Thermostat_Types_max; i++) {
myDebug(" %s, type ID:0x%02X Product ID:%d Read/Write support:%c%c",
myDebug(" %s, type ID:0x%02X ProductID:%d Read/Write support:%c%c",
Thermostat_Types[i].model_string,
Thermostat_Types[i].type_id,
Thermostat_Types[i].product_id,
@@ -1940,7 +1943,7 @@ void ems_setWarmTapWaterActivated(bool activated) {
}
/*
* Start up sequence for UBA Master
* Start up sequence for UBA Master, hopefully to initialize a handshake
* Still experimental
*/
void ems_startupTelegrams() {

View File

@@ -6,5 +6,5 @@
#pragma once
#define APP_NAME "EMS-ESP"
#define APP_VERSION "1.6.0b5"
#define APP_VERSION "1.6.0b6"
#define APP_HOSTNAME "ems-esp"