Commit 4866e2e8c9fccedd0fb2ca3a925bc71e740499ee

Authored by Jeroen88
1 parent 8e5a31ce

Add example to show the secondary response for each know OpenTherm::READ_DATA_ID…

…, including explanation of flags, using the correct data type and showing the unit (if any) for boilers and HVACs
examples/OpenTherm_Show_DATA-IDs/OpenTherm_Show_DATA-IDs.ino 0 → 100644
  1 +#include <EasyOpenTherm.h>
  2 +
  3 +// GPIO pin used to read data from the boiler or HVAC. Must support interrupts
  4 +#define OT_RX_PIN (34)
  5 +// GPIO pin used to send data to the boiier or HVAC. Must not be a 'read only' GPIO
  6 +#define OT_TX_PIN (17)
  7 +
  8 +
  9 +
  10 +// primaryFlags is used to tell the secondary device (boiler) what available services (Central heating, cooling, domestic hot water) it wants to make use of
  11 +// The meaning of each bit is defined in enum class OpenTherm::STATUS_FLAGS
  12 +uint8_t requestServices() {
  13 + Serial.println("Request secondary services using status command:");
  14 + uint8_t primaryFlags = 0;
  15 +
  16 + Serial.println("+ Enable Domestic Hot Water (DHW)");
  17 + primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_DHW_ENABLE);
  18 +
  19 + Serial.println("+ Enable Central Heating (CH)");
  20 + primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_CH_ENABLE);
  21 +
  22 + Serial.println("+ Enable cooling");
  23 + primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_COOLING_ENABLE);
  24 +
  25 + Serial.println("+ Enable Outside Temperature Compensation");
  26 + primaryFlags |= uint8_t(OpenTherm::STATUS_FLAGS::PRIMARY_OTC_ENABLE);
  27 +
  28 + return primaryFlags;
  29 +}
  30 +
  31 +
  32 +// The statusFlags returned by the boiler tell us what the status is. Each bit in statusFlags has a meaning defined in OpenTherm::STATUS_FLAGS
  33 +void showSecondaryStatus(uint8_t statusFlags) {
  34 + Serial.printf("Status flags is 0x%02x\n", statusFlags);
  35 +
  36 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_FAULT_INDICATION)) Serial.println("> FAULT NOTIFICATION");
  37 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_CH_MODE)) Serial.println("> Central Heating (CH) mode");
  38 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_DHW_MODE)) Serial.println("> Domestc Hot Water (DHW) mode");
  39 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_FLAME_STATUS)) Serial.println("> Flame is on"); else Serial.println("> Flame is off");
  40 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_COOLING_STATUS)) Serial.println("> Cooling");
  41 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_CH2_MODE)) Serial.println("> Second Central Heating system (CH2) is active");
  42 + if(statusFlags & uint8_t(OpenTherm::STATUS_FLAGS::SECONDARY_DIAGNOSTIC_IND)) Serial.println("> DIAGNOSTICS INDICATION");
  43 +}
  44 +
  45 +
  46 +// primaryFlags is used to tell the secondary device (HVAC) what available services (Ventilation, bypass postion, bypass mode, free ventilation mode) it wants to make use of
  47 +// The meaning of each bit is defined in enum class OpenTherm::HVAC_STATUS_FLAGS
  48 +uint8_t HVACRequestServices() {
  49 + Serial.println("Request secondary services using status command:");
  50 + uint8_t primaryFlags = 0;
  51 +
  52 + Serial.println("+ Enable Ventilation");
  53 + primaryFlags |= uint8_t(OpenTherm::HVAC_STATUS_FLAGS::PRIMARY_VENTILATION_ENABLE);
  54 +
  55 + // Please check which of these should be enabled
  56 + //Serial.println("+ Enable bypass postion");
  57 + //primaryFlags |= uint8_t(OpenTherm::HVAC_STATUS_FLAGS::PRIMARY_BYPASS_POSTION);
  58 +
  59 + // Please check which of these should be enabled
  60 + //Serial.println("+ Enable bypass mode");
  61 + //primaryFlags |= uint8_t(OpenTherm::HVAC_STATUS_FLAGS::PRIMARY_BYPASS_MODE);
  62 +
  63 + // Please check which of these should be enabled
  64 + //Serial.println("+ Enable free ventilation mode");
  65 + //primaryFlags |= uint8_t(OpenTherm::HVAC_STATUS_FLAGS::PRIMARY_FREE_VENTILATION_MODE);
  66 +
  67 + return primaryFlags;
  68 +}
  69 +
  70 +
  71 +// The statusFlags returned by the HVAC tell us what the status is. Each bit in statusFlags has a meaning defined in OpenTherm::HVAC_STATUS_FLAGS
  72 +void HVACShowSecondaryStatus(uint8_t statusFlags) {
  73 + Serial.printf("Status flags is 0x%02x\n", statusFlags);
  74 +
  75 + if(statusFlags & uint8_t(OpenTherm::HVAC_STATUS_FLAGS::SECONDARY_FAULT_INDICATION)) Serial.println("> FAULT NOTIFICATION");
  76 + if(statusFlags & uint8_t(OpenTherm::HVAC_STATUS_FLAGS::SECONDARY_VENTILATION_MODE)) Serial.println("> Ventilation mode");
  77 + if(statusFlags & uint8_t(OpenTherm::HVAC_STATUS_FLAGS::SECONDARY_BYPASS_STATUS)) Serial.println("> Bypass status");
  78 + if(statusFlags & uint8_t(OpenTherm::HVAC_STATUS_FLAGS::SECONDARY_BYPASS_AUTOMATIC_STATUS)) Serial.println("> Bypass automatisc status");
  79 + if(statusFlags & uint8_t(OpenTherm::HVAC_STATUS_FLAGS::SECONDARY_FREE_VENTILATION_MODE)) Serial.println("> Free ventilation maode");
  80 + if(statusFlags & uint8_t(OpenTherm::HVAC_STATUS_FLAGS::SECONDARY_DIAGNOSTIC_IND)) Serial.println("> DIAGNOSTICS INDICATION");
  81 +}
  82 +
  83 +
  84 +void setup() {
  85 + // Create a static OpenTherm instance called 'thermostat' (i.e primary and boiler is secondary) with OT_RX_PIN to receive data from boiler and OT_TX_PIN to send data to boiler
  86 +// Only one OpenTherm object may be created!
  87 + static OpenTherm thermostat(OT_RX_PIN, OT_TX_PIN);
  88 +
  89 + Serial.begin(115200);
  90 + delay(5000); // Give Serial monitor in Arduino IDE 2.0.1 some time for a board with native USB support
  91 + Serial.println("\n\nStarted");
  92 +
  93 + // primaryFlags is used to tell the secondary device (boiler) what available services (central heating, cooling, domestic hot water) it wants to make use of
  94 + // Each service is a bit in the primaryFlags. The right bits are set by calling requestServices();
  95 + uint8_t primaryFlags = requestServices();
  96 +
  97 + // Send primaryFlags to the boiler to request services. The boiler returns it's status in statusFlags. Each bit has a meaning which is displayed by calling showSecondaryStatus();
  98 + Serial.println("\nRequest services from the boiler and check it's status...");
  99 + uint8_t statusFlags;
  100 +
  101 + bool success = thermostat.status(primaryFlags, statusFlags);
  102 +
  103 + if(success) { // It is mandatory for the boiler to support it's status
  104 + showSecondaryStatus(statusFlags);
  105 + } else {
  106 + Serial.println("Failed to get status, is your boiler connected, the OpenTherm Controller Board correctly wired and the GPIO's defined correctly?");
  107 + }
  108 +
  109 + if(success) {
  110 + Serial.println("\nChecking each read DATA-ID. This may take some time, especially if the boiler does not respond to a DATA-ID because then the thermostat waits for a timeout of about a second for each of such a DATA-ID.");
  111 +
  112 + uint16_t uintValue;
  113 + int16_t sintValue;
  114 + float floatValue;
  115 + uint8_t MSBValue, LSBValue;
  116 +
  117 +
  118 + if(thermostat.read(OpenTherm::READ_DATA_ID::FAULT_FLAGS, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  119 + Serial.println("Fault flags:");
  120 + if(MSBValue == 0) {
  121 + Serial.println("> No faults");
  122 + } else {
  123 + if(MSBValue & uint8_t(OpenTherm::FAULT_FLAGS::SERVICE_REQUEST)) Serial.println("> Service request");
  124 + if(MSBValue & uint8_t(OpenTherm::FAULT_FLAGS::LOCKOUT_RESET)) Serial.print("> Lockout reset");
  125 + if(MSBValue & uint8_t(OpenTherm::FAULT_FLAGS::LOW_WATER_PRESS)) Serial.print("> Low water pressure");
  126 + if(MSBValue & uint8_t(OpenTherm::FAULT_FLAGS::GAS_FLAME)) Serial.print("> Gas flame fault");
  127 + if(MSBValue & uint8_t(OpenTherm::FAULT_FLAGS::AIR_PRESS)) Serial.print("> Air pressure fault");
  128 + if(MSBValue & uint8_t(OpenTherm::FAULT_FLAGS::WATER_OVER_TEMP)) Serial.print("> Water over temperature fault");
  129 + uint8_t knownFlags = uint8_t(OpenTherm::FAULT_FLAGS::SERVICE_REQUEST) | uint8_t(OpenTherm::FAULT_FLAGS::LOCKOUT_RESET) | uint8_t(OpenTherm::FAULT_FLAGS::LOW_WATER_PRESS) | uint8_t(OpenTherm::FAULT_FLAGS::GAS_FLAME) | uint8_t(OpenTherm::FAULT_FLAGS::AIR_PRESS) | uint8_t(OpenTherm::FAULT_FLAGS::WATER_OVER_TEMP);
  130 + if(MSBValue & ~knownFlags) Serial.printf("> Remaining unknown flags 0x%02x", MSBValue & ~knownFlags);
  131 + }
  132 + Serial.printf("> OEM specific fault code is 0x%02x\n", LSBValue);
  133 + }
  134 +
  135 +
  136 + if(thermostat.read(OpenTherm::READ_DATA_ID::OEM_DIAGNOSTIC, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  137 + Serial.printf("OEM diagnostic value: 0x%04x\n", uintValue);
  138 + }
  139 +
  140 +
  141 + if(thermostat.read(OpenTherm::READ_DATA_ID::SECONDARY_CONFIGURATION, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  142 + Serial.println("Secondary configuration:");
  143 + if(MSBValue == 0) {
  144 + Serial.print("> none;");
  145 + } else {
  146 + if(MSBValue & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW_PRESENT)) Serial.println("> Domestic Hot Water (DHW) present"); else Serial.println("> Domestic Hot Water (DHW) NOT present");
  147 + if(MSBValue & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_CONTROL_TYPE)) Serial.println("> Control type on/off"); else Serial.println("> Control type modulating");
  148 + if(MSBValue & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_COOLING)) Serial.println("> Cooling supported"); else Serial.println("> Cooling NOT supported");
  149 + if(MSBValue & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW)) Serial.println("> Domestic Hot Water (DHW) storage tank"); else Serial.println("> Domestic Hot Water (DHW) instantaneous or not-specified");
  150 + if(MSBValue & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_LOW_OFF_PUMP_CTRL)) Serial.println("> Primary low-off & pump control function NOT allowed"); else Serial.println("> Primary low-off & pump control function allowed");
  151 + if(MSBValue & uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_CH2_PRESENT)) Serial.println("> 2nd Central heating present"); else Serial.println("> 2nd Central heating NOT present");
  152 + uint8_t knownFlags = uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW_PRESENT) | uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_CONTROL_TYPE) | uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_COOLING) | uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_DHW) | uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_LOW_OFF_PUMP_CTRL) | uint8_t(OpenTherm::CONFIGURATION_FLAGS::SECONDARY_CH2_PRESENT);
  153 + if(MSBValue & ~knownFlags) Serial.printf("> Remaining unknown flags 0x%02x\n", MSBValue & ~knownFlags);
  154 + }
  155 + Serial.printf("> Secondary Member ID is %u (0x%02x)\n", LSBValue, LSBValue);
  156 + } else {
  157 + Serial.println("Secondary configuration mandatory but not supported");
  158 + }
  159 +
  160 +
  161 + if(thermostat.read(OpenTherm::READ_DATA_ID::OPENTHERM_VERSION_SECONDARY, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  162 + Serial.printf("Secondary OpenTherm Version: %.02f\n", floatValue);
  163 + }
  164 +
  165 +
  166 + if(thermostat.read(OpenTherm::READ_DATA_ID::SECONDARY_PRODUCT_VERSION, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  167 + Serial.printf("Secondary Product Version: %u, %u\n", MSBValue, LSBValue);
  168 + }
  169 +
  170 +
  171 + if(thermostat.read(OpenTherm::READ_DATA_ID::RELATIVE_MODULATION_LEVEL, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  172 + Serial.printf("Relative Modulation level: %.02f%\n", floatValue);
  173 + } else {
  174 + Serial.println("Relative Modulation level mandatory but not supported");
  175 + }
  176 +
  177 +
  178 + if(thermostat.read(OpenTherm::READ_DATA_ID::CH_WATER_PRESSURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  179 + Serial.printf("Central heating water pressure: %.02f bar\n", floatValue);
  180 + }
  181 +
  182 +
  183 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_FLOW_RATE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  184 + Serial.printf("Domestic Hot Water (DHW) flow rate: %.02f l/min\n", floatValue);
  185 + }
  186 +
  187 +
  188 + if(thermostat.read(OpenTherm::READ_DATA_ID::DAY_TIME, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  189 + Serial.print("Day of week: ");
  190 + switch((MSBValue & 0b11100000) >> 5) {
  191 + case 0: Serial.print("not available"); break;
  192 + case 1: Serial.print("Monday"); break;
  193 + case 2: Serial.print("Tuesday"); break;
  194 + case 3: Serial.print("Wednesday"); break;
  195 + case 4: Serial.print("Thursday"); break;
  196 + case 5: Serial.print("Friday"); break;
  197 + case 6: Serial.print("Saturday"); break;
  198 + case 7: Serial.print("Sunday"); break;
  199 + }
  200 + Serial.printf(", time: %02u:%02u\n", MSBValue & 0b00011111, LSBValue);
  201 + }
  202 +
  203 +
  204 + if(thermostat.read(OpenTherm::READ_DATA_ID::DATE, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  205 + Serial.print("Date: ");
  206 + switch(MSBValue) {
  207 + case 1: Serial.print("January"); break;
  208 + case 2: Serial.print("February"); break;
  209 + case 3: Serial.print("March"); break;
  210 + case 4: Serial.print("April"); break;
  211 + case 5: Serial.print("May"); break;
  212 + case 6: Serial.print("June"); break;
  213 + case 7: Serial.print("July"); break;
  214 + case 8: Serial.print("August"); break;
  215 + case 9: Serial.print("September"); break;
  216 + case 10: Serial.print("October"); break;
  217 + case 11: Serial.print("November"); break;
  218 + case 12: Serial.print("December"); break;
  219 + }
  220 + Serial.printf(", %02u\n", LSBValue);
  221 + }
  222 +
  223 +
  224 + if(thermostat.read(OpenTherm::READ_DATA_ID::YEAR, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  225 + Serial.printf("Year: %04u\n", uintValue);
  226 + }
  227 +
  228 +
  229 + if(thermostat.read(OpenTherm::READ_DATA_ID::BOILER_WATER_TEMP, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  230 + Serial.printf("Boiler water temperature (from boiler): %.02f °C\n", floatValue);
  231 + }
  232 +
  233 +
  234 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  235 + Serial.printf("Domestic Hot Water (DHW) temperature: %.02f °C\n", floatValue);
  236 + }
  237 +
  238 +
  239 + if(thermostat.read(OpenTherm::READ_DATA_ID::OUTSIDE_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  240 + Serial.printf("Outside temperature: %.02f °C\n", floatValue);
  241 + }
  242 +
  243 +
  244 + if(thermostat.read(OpenTherm::READ_DATA_ID::RETURN_WATER_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  245 + Serial.printf("Return water temperature (to boiler): %.02f °C\n", floatValue);
  246 + }
  247 +
  248 +
  249 + if(thermostat.read(OpenTherm::READ_DATA_ID::SOLAR_STORE_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  250 + Serial.printf("Solar storage temperature (to boiler): %.02f °C\n", floatValue);
  251 + }
  252 +
  253 +
  254 + if(thermostat.read(OpenTherm::READ_DATA_ID::SOLAR_COLLECTOR_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  255 + Serial.printf("Solar collector temperature (to boiler): %.02f °C\n", floatValue);
  256 + }
  257 +
  258 +
  259 + if(thermostat.read(OpenTherm::READ_DATA_ID::FLOW_TEMPERATURE_CH2, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  260 + Serial.printf("2nd Central Heating Boiler water temperature (from boiler) (to boiler): %.02f °C\n", floatValue);
  261 + }
  262 +
  263 +
  264 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW2_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  265 + Serial.printf("2nd Central Heating Domestic Hot Water (DHW) temperature: %.02f °C\n", floatValue);
  266 + }
  267 +
  268 +
  269 + if(thermostat.read(OpenTherm::READ_DATA_ID::EXHAUST_TEMPERATURE, sintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  270 + Serial.printf("Exhaust temperature: %d °C\n", sintValue);
  271 + }
  272 +
  273 +
  274 + if(thermostat.read(OpenTherm::READ_DATA_ID::BURNER_STARTS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  275 + if(uintValue == 0xffff) {
  276 + Serial.println("Burner starts: unavailable");
  277 + } else {
  278 + Serial.printf("Burner starts: %u\n", uintValue);
  279 + }
  280 + }
  281 +
  282 +
  283 + if(thermostat.read(OpenTherm::READ_DATA_ID::CH_PUMP_STARTS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  284 + if(uintValue == 0xffff) {
  285 + Serial.println("Central Heating pump starts: unavailable");
  286 + } else {
  287 + Serial.printf("Central Heating pump starts: %u\n", uintValue);
  288 + }
  289 + }
  290 +
  291 +
  292 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_PUMP_VALVE_STARTS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  293 + if(uintValue == 0xffff) {
  294 + Serial.println("Domestic Hot water (DHW) pump/valve starts: unavailable");
  295 + } else {
  296 + Serial.printf("Domestic Hot water (DHW) pump/valve starts: %u\n", uintValue);
  297 + }
  298 + }
  299 +
  300 +
  301 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_BURNER_STARTS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  302 + if(uintValue == 0xffff) {
  303 + Serial.println("Burner starts in Domestic Hot water (DHW) mode: unavailable");
  304 + } else {
  305 + Serial.printf("Burner starts in Domestic Hot water (DHW) mode: %u\n", uintValue);
  306 + }
  307 + }
  308 +
  309 +
  310 + if(thermostat.read(OpenTherm::READ_DATA_ID::BURNER_OPERATION_HOURS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  311 + Serial.printf("Burner operating hours: %u hours\n", uintValue);
  312 + }
  313 +
  314 +
  315 + if(thermostat.read(OpenTherm::READ_DATA_ID::CH_PUMP_OPERATION_HOURS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  316 + Serial.printf("Central Heating pump operating hours: %u hours\n", uintValue);
  317 + }
  318 +
  319 +
  320 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_PUMP_VALVE_OPERATION_HOURS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  321 + Serial.printf("Domestic Hot Water (DHW) pump has been running or DHW valve has been opened for: %u hours\n", uintValue);
  322 + }
  323 +
  324 +
  325 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_BURNER_OPERATION_HOURS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  326 + Serial.printf("Domestic Hot Water (DHW) burner operating hours: %u hours\n", uintValue);
  327 + }
  328 +
  329 +
  330 + if(thermostat.read(OpenTherm::READ_DATA_ID::REMOTE_PARAMETER, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  331 + Serial.println("Remote parameters:");
  332 + if(MSBValue == 0) {
  333 + Serial.println("> transfer enable: none");
  334 + } else {
  335 + if(MSBValue & uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::TRANSFER_ENABLE_DHW_SETPOINT)) Serial.println("> Domestic Hot Water (DHW) setpoint transfer: enabled"); else Serial.println("> Domestic Hot Water (DHW) setpoint transfer: disabled");
  336 + if(MSBValue & uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::TRANSFER_ENABLE_MAX_CH_SETPOINT)) Serial.println("> Max Central Heating (CH) setpoint transfer: enabled"); else Serial.println("> Max Central Heating (CH) setpoint transfer: disabled");
  337 + uint8_t knownFlags = uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::TRANSFER_ENABLE_DHW_SETPOINT) | uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::TRANSFER_ENABLE_MAX_CH_SETPOINT);
  338 + if(MSBValue & ~knownFlags) Serial.printf(" Remaining unknown flags 0x%02x;", MSBValue & ~knownFlags);
  339 + }
  340 + if(LSBValue == 0) {
  341 + Serial.println("> read/write: none");
  342 + } else {
  343 + if(LSBValue & uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::READ_WRITE_DHW_SETPOINT)) Serial.println("> Domestic Hot Water (DHW) setpoint: read/write"); else Serial.println("> Domestic Hot Water (DHW) setpoint: read-only");
  344 + if(LSBValue & uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::READ_WRITE_MAX_CH_SETPOINT)) Serial.println("> Max Central Heating (CH) setpoint: read/write"); else Serial.println("> Max Central Heating (CH) setpoint transfer: read-only");
  345 + uint8_t knownFlags = uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::READ_WRITE_DHW_SETPOINT) | uint8_t(OpenTherm::REMOTE_PARAMETER_FLAGS::READ_WRITE_MAX_CH_SETPOINT);
  346 + if(LSBValue & ~knownFlags) Serial.printf(" Remaining unknown flags 0x%02x;", LSBValue & ~knownFlags);
  347 + }
  348 + }
  349 +
  350 +
  351 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_SETPOINT_BOUNDS, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  352 + Serial.printf("Domestic Hot Water (DHW) setpoint bounds between %u and %u °C\n", LSBValue, MSBValue);
  353 + }
  354 +
  355 +
  356 + if(thermostat.read(OpenTherm::READ_DATA_ID::CH_SETPOINT_BOUNDS, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  357 + Serial.printf("Central Heating (CH) setpoint bounds between %u and %u °C\n", LSBValue, MSBValue);
  358 + }
  359 +
  360 +
  361 + if(thermostat.read(OpenTherm::READ_DATA_ID::OTC_CURVE_BOUNDS, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  362 + Serial.printf("Outside Temperature Compensation (OTC) curve bounds between %u and %u\n", LSBValue, MSBValue);
  363 + }
  364 +
  365 +
  366 + if(thermostat.read(OpenTherm::READ_DATA_ID::DHW_SETPOINT, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  367 + Serial.printf("Domestic Hot Water (DHW) temperature setpoint (remote parameter 1): %.02f °C\n", floatValue);
  368 + }
  369 +
  370 +
  371 + if(thermostat.read(OpenTherm::READ_DATA_ID::MAX_CH_WATER_SETPOINT, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  372 + Serial.printf("Maximum allowable Central Heating (CH) water temperature setpoint (remote parameter 2): %.02f °C\n", floatValue);
  373 + }
  374 +
  375 +
  376 + if(thermostat.read(OpenTherm::READ_DATA_ID::OTC_CURVE_RATIO, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  377 + Serial.printf("Outside Temperature Compensation (OTC) curve ratio (remote parameter 3): %.02f °C\n", floatValue);
  378 + }
  379 +
  380 +
  381 + if(thermostat.read(OpenTherm::READ_DATA_ID::NUMBER_OF_TSPS, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  382 + Serial.printf("%d Transparent Secondary Parameters:\n", MSBValue);
  383 + for(uint8_t index = 0; index < MSBValue; index++) { // Not tested, might as well be from 1 up and until MSBValue
  384 + if(thermostat.readWrite(OpenTherm::READ_WRITE_DATA_ID::TSP_COMMAND, index, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  385 + Serial.printf("Command %u, value %u\n", index, LSBValue);
  386 + }
  387 + }
  388 + }
  389 +
  390 +
  391 + if(thermostat.read(OpenTherm::READ_DATA_ID::FAULT_BUFFER_SIZE, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  392 + Serial.printf("%d Fault buffer entries:\n", MSBValue);
  393 + for(uint8_t index = 0; index < MSBValue; index++) { // Not tested, might as well be from 1 up and until MSBValue
  394 + if(thermostat.readWrite(OpenTherm::READ_WRITE_DATA_ID::FAULT_BUFFER_DATA, index, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  395 + Serial.printf("Fault buffer entry %u has value %u\n", index, LSBValue);
  396 + }
  397 + }
  398 + }
  399 +
  400 +
  401 + if(thermostat.read(OpenTherm::READ_DATA_ID::MAX_BOILER_CAPACITY_MIN_MOD_LEV, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  402 + Serial.printf("Maximum boiler power: %u kW; Minimum modulation level as percentage of maximum power: %u%\n", LSBValue, MSBValue);
  403 + }
  404 +
  405 +
  406 + if(thermostat.read(OpenTherm::READ_DATA_ID::MAX_BOILER_CAPACITY_MIN_MOD_LEV, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  407 + if(floatValue == 0.0) { // Sepcification says this is a float; I would expect a uint16_t or two uint8_t's
  408 + Serial.println("NO room setpoint override by remote");
  409 + } else {
  410 + Serial.println("Room setpoint override by remote");
  411 + }
  412 + }
  413 +
  414 +
  415 + if(thermostat.read(OpenTherm::READ_DATA_ID::BOILER_HEAT_EXCHANGER_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  416 + Serial.printf("Boiler heat exchanger temperature: %.02f °C\n", floatValue);
  417 + }
  418 +
  419 +
  420 + if(thermostat.read(OpenTherm::READ_DATA_ID::BOILER_FAN_SPEED_SETPOINT_VALUE, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  421 + Serial.printf("Boiler fan speed setpoint: %u rpm, actual: %u rpm\n", MSBValue, LSBValue); // Not tested, I expect rpm x100
  422 + }
  423 +
  424 +
  425 + if(thermostat.read(OpenTherm::READ_DATA_ID::ELECTRICAL_CURRENT_BURNER_FLAME, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  426 + Serial.printf("Electrical current through burner flame %u μA\n", uintValue); // Not tested, data type not sure, might as well be a f8.8
  427 + }
  428 +
  429 +
  430 + if(thermostat.read(OpenTherm::READ_DATA_ID::UNSUCCESSFUL_BURNER_STARTS, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  431 + if(uintValue == 0xffff) {
  432 + Serial.println("Number of unsuccessful burner starts: unavailable");
  433 + } else {
  434 + Serial.printf("Number of unsuccessful burner starts: %u\n", uintValue);
  435 + }
  436 + }
  437 +
  438 +
  439 + if(thermostat.read(OpenTherm::READ_DATA_ID::FLAME_SIGNAL_TOO_LOW, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  440 + if(uintValue == 0xffff) {
  441 + Serial.println("Number of times flame signal was too low: unavailable");
  442 + } else {
  443 + Serial.printf("Number of times flame signal was too low: %u\n", uintValue);
  444 + }
  445 + }
  446 +
  447 + }
  448 +
  449 +
  450 + // HVAC specific DATA-IDs, NOT TESTED, aQ similar requests as for the boiler are made
  451 + // primaryFlags is used to tell the secondary device (HVAC) what available services (Ventilation, bypass postion, bypass mode, free ventilation mode) it wants to make use of
  452 + // Each service is a bit in the primaryFlags. The right bits are set by calling requestServices();
  453 + primaryFlags = HVACRequestServices();
  454 +
  455 + // Send primaryFlags to the HVAC to request services. The HVAC returns it's status in statusFlags. Each bit has a meaning which is displayed by calling HVACShowSecondaryStatus();
  456 + Serial.println("\nRequest services from the HVAC and check it's status...");
  457 +
  458 + success = thermostat.status(primaryFlags, statusFlags);
  459 +
  460 + if(success) { // Most likely It is mandatory for the HVAC to support it's status
  461 + HVACShowSecondaryStatus(statusFlags);
  462 + } else {
  463 + Serial.println("Failed to get status, is your HVAC connected, the OpenTherm Controller Board correctly wired and the GPIO's defined correctly?");
  464 + }
  465 +
  466 + if(success) {
  467 + Serial.println("\nChecking each read DATA-ID. This may take some time, especially if the HVAC does not respond to a DATA-ID because then the thermostat waits for a timeout of about a second for each of such a DATA-ID.");
  468 +
  469 + uint16_t uintValue;
  470 + int16_t sintValue;
  471 + float floatValue;
  472 + uint8_t MSBValue, LSBValue;
  473 +
  474 +
  475 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_RELATIVE_VENT_SETPOINT, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  476 + Serial.printf("HVAC relative ventilation setpoint: %.02f%\n", floatValue);
  477 + }
  478 +
  479 +
  480 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_FAULT_FLAGS, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  481 + Serial.println("HVAC fault flags:");
  482 + if(MSBValue == 0) {
  483 + Serial.println("> No faults;");
  484 + } else {
  485 + uint8_t mask = 0b00000001;
  486 + for(size_t index = 0; index < 8; index++) {
  487 + if(MSBValue & mask) Serial.printf("> HVAC flag bit %u set\n", index);
  488 + mask <<= 1;
  489 + }
  490 + }
  491 + Serial.printf("> OEM specific fault code is 0x%02x\n", LSBValue);
  492 + }
  493 +
  494 +
  495 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_OEM_DIAGNOSTIC_CODE, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  496 + Serial.printf("HVAC OEM diagnostic value: 0x%04x\n", uintValue);
  497 + }
  498 +
  499 +
  500 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_SECONDARY_CONFIGURATION, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  501 + Serial.println("Secondary configuration:");
  502 + if(MSBValue == 0) {
  503 + Serial.print("> none;");
  504 + } else {
  505 + if(MSBValue & uint8_t(OpenTherm::HVAC_CONFIGURATION_FLAGS::SECONDARY_SYSTEM_TYPE)) Serial.println("> HVAC system type set"); else Serial.println("> HVAC system type cleared");
  506 + if(MSBValue & uint8_t(OpenTherm::HVAC_CONFIGURATION_FLAGS::SECONDARY_BYPASS)) Serial.println("> Bypass enabled"); else Serial.println("> Bypass disabled");
  507 + if(MSBValue & uint8_t(OpenTherm::HVAC_CONFIGURATION_FLAGS::SECONDARY_SPEED_CONTROL)) Serial.println("> Speed control enabled"); else Serial.println("> Speed control disabled");
  508 + uint8_t knownFlags = uint8_t(OpenTherm::HVAC_CONFIGURATION_FLAGS::SECONDARY_SYSTEM_TYPE) | uint8_t(OpenTherm::HVAC_CONFIGURATION_FLAGS::SECONDARY_BYPASS) | uint8_t(OpenTherm::HVAC_CONFIGURATION_FLAGS::SECONDARY_SPEED_CONTROL);
  509 + if(MSBValue & ~knownFlags) Serial.printf("> HVAC remaining unknown flags 0x%02x\n", MSBValue & ~knownFlags);
  510 + }
  511 + Serial.printf("> HVAC secondary Member ID is %u (0x%02x)\n", LSBValue, LSBValue);
  512 + } else {
  513 + Serial.println("HVAC secondary configuration (supposed to be) mandatory but not supported");
  514 + }
  515 +
  516 +
  517 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_OPENTHERM_VERSION_SECONDARY, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  518 + Serial.printf("HVAC secondary OpenTherm Version: %.02f\n", floatValue);
  519 + }
  520 +
  521 +
  522 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_SECONDARY_PRODUCT_VERSION, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  523 + Serial.printf("HVAC secondary Product Version: %u, %u\n", MSBValue, LSBValue);
  524 + }
  525 +
  526 +
  527 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_RELATIVE_VENTILATION, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  528 + Serial.printf("HVAC relative ventilation: %.02f%\n", floatValue);
  529 + }
  530 +
  531 +
  532 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_RELATIVE_HUMIDITY_EXHAUST, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  533 + Serial.printf("HVAC relative humidity exhaust air: %.02f%\n", floatValue);
  534 + }
  535 +
  536 +
  537 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_CO2_LEVEL_EXHAUST_AIR, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  538 + Serial.printf("HVAC CO2 level exhaust air: %u ppm\n", uintValue); // Might as well be a f8.8
  539 + }
  540 +
  541 +
  542 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_SUPPLY_INLET_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  543 + Serial.printf("HVAC supply inlet temperature: %.02f °C\n", floatValue);
  544 + }
  545 +
  546 +
  547 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_SUPPLY_OUTLET_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  548 + Serial.printf("HVAC supply outlet temperature: %.02f °C\n", floatValue);
  549 + }
  550 +
  551 +
  552 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_EXHAUST_INLET_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  553 + Serial.printf("HVAC exhaust inlet temperature: %.02f °C\n", floatValue);
  554 + }
  555 +
  556 +
  557 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_EXHAUST_OUTLET_TEMPERATURE, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  558 + Serial.printf("HVAC exhaust outlet temperature: %.02f °C\n", floatValue);
  559 + }
  560 +
  561 +
  562 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_EXHAUST_FAN_SPEED, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  563 + Serial.printf("HVAC exhaust fan speed: %u rpm\n", uintValue); // Might as well be a f8.8
  564 + }
  565 +
  566 +
  567 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_SUPPLY_FAN_SPEED, uintValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  568 + Serial.printf("HVAC supply fan speed: %u rpm\n", uintValue); // Might as well be a f8.8
  569 + }
  570 +
  571 +
  572 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_NOMINAL_RELATIVE_VENTILATION, floatValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  573 + Serial.printf("HVAC nominal relative ventilation: %.02f%\n", floatValue);
  574 + }
  575 +
  576 +
  577 + // Skipped:
  578 + // HVAC_NUMBER_OF_TSPS = 88, // u8, u8? Number of transparent-secondary-parameter supported by the secondary device, -Reserved-
  579 + // HVAC_TSP_COMMAND = 89, // u8, u8 Index number of following TSP, Value of the referenced TSP
  580 + // HVAC_FAULT_BUFFER_SIZE = 90, // u8, u8? The size of the fault history buffer
  581 + // HVAC_FAULT_BUFFER_DATA = 91, // u8, u8 Index number of Fault Buffer entry, Value of the referenced Fault Buffer entry
  582 + // HVAC_OPERATING_MODE = 99, // ? Operating mode HC1, HC2 / Operating mode DHW
  583 +
  584 +
  585 + if(thermostat.read(OpenTherm::READ_DATA_ID::HVAC_RF_STRENGTH_BATTERY_LEVEL, MSBValue, LSBValue) && thermostat.error() == OpenTherm::ERROR_CODES::OK) {
  586 + Serial.printf("HVAC RF strength: %u (unit?) and battery level: %u (%?)\n", MSBValue, LSBValue);
  587 + }
  588 + }
  589 +}
  590 +
  591 +void loop() {
  592 + // put your main code here, to run repeatedly:
  593 +
  594 +}
... ...