From 41f77f688cf0ed9133f5d29248d99bc125c65291 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Thu, 4 Jun 2020 11:36:58 +0200 Subject: [PATCH] Add command ``SetOption94 0/1`` Add command ``SetOption94 0/1`` to select MAX31855 or MAX6675 thermocouple support (#8616) --- RELEASENOTES.md | 2 + tasmota/CHANGELOG.md | 1 + tasmota/my_user_config.h | 10 +- tasmota/xsns_39_max31855.ino | 229 ++++++++++++++++++----------------- 4 files changed, 127 insertions(+), 115 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 2453fd5d4..5e4429dde 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -61,6 +61,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Fix escape of non-JSON received serial data (#8329) - Add command ``Rule0`` to change global rule parameters - Add command ``Time 4`` to display timestamp using milliseconds (#8537) +- Add command ``SetOption94 0/1`` to select MAX31855 or MAX6675 thermocouple support (#8616) - Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491) - Add support for unique MQTTClient (and inherited fallback topic) by full Mac address using ``mqttclient DVES_%12X`` (#8300) - Add more functionality to ``Switchmode`` 11 and 12 (#8450) @@ -69,6 +70,7 @@ The following binary downloads have been compiled with ESP8266/Arduino library c - Add support for VEML7700 Ambient light intensity Sensor by device111 (#8432) - Add Three Phase Export Active Energy to SDM630 driver - Add Zigbee options to ``ZbSend`` to write and report attributes +- Add Zigbee auto-responder for common attributes - Add ``CpuFrequency`` to ``status 2`` - Add ``FlashFrequency`` to ``status 4`` - Add support for up to two BH1750 sensors controlled by commands ``BH1750Resolution`` and ``BH1750MTime`` (#8139) diff --git a/tasmota/CHANGELOG.md b/tasmota/CHANGELOG.md index e12680d36..54e1a5f63 100644 --- a/tasmota/CHANGELOG.md +++ b/tasmota/CHANGELOG.md @@ -7,6 +7,7 @@ - Change Adafruit_SGP30 library from v1.0.3 to v1.2.0 (#8519) - Fix escape of non-JSON received serial data (#8329) - Add command ``Time 4`` to display timestamp using milliseconds (#8537) +- Add command ``SetOption94 0/1`` to select MAX31855 or MAX6675 thermocouple support (#8616) - Add commands ``LedPwmOn 0..255``, ``LedPwmOff 0..255`` and ``LedPwmMode1 0/1`` to control led brightness by George (#8491) - Add Three Phase Export Active Energy to SDM630 driver - Add wildcard pattern ``?`` for JSON matching in rules diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 0d968b1f8..050c2e390 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -612,12 +612,12 @@ // -- Low level interface devices ----------------- #define USE_DHT // Add support for DHT11, AM2301 (DHT21, DHT22, AM2302, AM2321) and SI7021 Temperature and Humidity sensor (1k6 code) -//#define USE_MAX31855 // Add support for MAX31855 K-Type thermocouple sensor using softSPI +//#define USE_MAX31855 // Add support for MAX31855/MAX6675 K-Type thermocouple sensor using softSPI //#define USE_MAX31865 // Add support for MAX31865 RTD sensors using softSPI - #define MAX31865_PTD_WIRES 2 // PTDs come in several flavors. Pick yours - #define MAX31865_PTD_RES 100 // Nominal PTD resistance at 0°C (100Ω for a PT100, 1000Ω for a PT1000, YMMV!) - #define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000) - #define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD + #define MAX31865_PTD_WIRES 2 // PTDs come in several flavors. Pick yours + #define MAX31865_PTD_RES 100 // Nominal PTD resistance at 0°C (100Ω for a PT100, 1000Ω for a PT1000, YMMV!) + #define MAX31865_REF_RES 430 // Reference resistor (Usually 430Ω for a PT100, 4300Ω for a PT1000) + #define MAX31865_PTD_BIAS 0 // To calibrate your not-so-good PTD // -- IR Remote features - all protocols from IRremoteESP8266 -------------------------- // IR Full Protocols mode is activated through platform.io only. diff --git a/tasmota/xsns_39_max31855.ino b/tasmota/xsns_39_max31855.ino index e80f879c5..fcdb69094 100644 --- a/tasmota/xsns_39_max31855.ino +++ b/tasmota/xsns_39_max31855.ino @@ -18,20 +18,27 @@ */ #ifdef USE_MAX31855 +/*********************************************************************************************\ + * MAX31855 and MAX6675 - Thermocouple + * + * SetOption94 0 - MAX31855 + * SetOption94 1 - MAX6675 +\*********************************************************************************************/ #define XSNS_39 39 -bool initialized = false; +const char kMax31855Types[] PROGMEM = "MAX31855|MAX6675"; -struct MAX31855_ResultStruct{ - uint8_t ErrorCode; // Error Codes: 0 = No Error / 1 = TC open circuit / 2 = TC short to GND / 4 = TC short to VCC - float ProbeTemperature; // Measured temperature of the 'hot' TC junction (probe temp) - float ReferenceTemperature; // Measured temperature of the 'cold' TC junction (reference temp) +bool max31855_initialized = false; + +struct MAX31855_ResultStruct { + uint8_t ErrorCode; // Error Codes: 0 = No Error / 1 = TC open circuit / 2 = TC short to GND / 4 = TC short to VCC + float ProbeTemperature; // Measured temperature of the 'hot' TC junction (probe temp) + float ReferenceTemperature; // Measured temperature of the 'cold' TC junction (reference temp) } MAX31855_Result; -void MAX31855_Init(void){ - if(initialized) - return; +void MAX31855_Init(void) { + if (PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)) { // Set GPIO modes for SW-SPI pinMode(Pin(GPIO_MAX31855CS), OUTPUT); @@ -42,120 +49,122 @@ void MAX31855_Init(void){ digitalWrite(Pin(GPIO_MAX31855CS), HIGH); digitalWrite(Pin(GPIO_MAX31855CLK), LOW); - initialized = true; -} - -/* -* MAX31855_GetResult(void) -* Acquires the raw data via SPI, checks for MAX31855 errors and fills result structure -*/ -void MAX31855_GetResult(void){ - // Controlled via SetOption94 - if (Settings.flag4.max6675) { - int32_t RawData = MAX31855_ShiftIn(16); - int32_t temp = (RawData >> 3) & ((1 << 12) - 1); - - /* Occasionally the sensor returns 0xfff, consider it an error */ - if (temp == ((1 << 12) - 1)) - return; - - MAX31855_Result.ErrorCode = 0; - MAX31855_Result.ReferenceTemperature = NAN; - MAX31855_Result.ProbeTemperature = ConvertTemp(0.25 * temp); - return; - } - int32_t RawData = MAX31855_ShiftIn(32); - uint8_t probeerror = RawData & 0x7; - - MAX31855_Result.ErrorCode = probeerror; - MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); - if(probeerror) - MAX31855_Result.ProbeTemperature = NAN; // Return NaN if MAX31855 reports an error - else - MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); -} - - -/* -* MAX31855_GetProbeTemperature(int32_t RawData) -* Decodes and returns the temperature of TCs 'hot' junction from RawData -*/ -float MAX31855_GetProbeTemperature(int32_t RawData){ - if(RawData & 0x80000000) - RawData = (RawData >> 18) | 0xFFFFC000; // Negative value - Drop lower 18 bits and extend to negative number - else - RawData >>= 18; // Positiv value - Drop lower 18 bits - - float result = (RawData * 0.25); // MAX31855 LSB resolution is 0.25°C for probe temperature - - return ConvertTemp(result); // Check if we have to convert to Fahrenheit -} - -/* -* MAX31855_GetReferenceTemperature(int32_t RawData) -* Decodes and returns the temperature of TCs 'cold' junction from RawData -*/ -float MAX31855_GetReferenceTemperature(int32_t RawData){ - if(RawData & 0x8000) - RawData = (RawData >> 4) | 0xFFFFF000; // Negative value - Drop lower 4 bits and extend to negative number - else - RawData = (RawData >> 4) & 0x00000FFF; // Positiv value - Drop lower 4 bits and mask out remaining bits (probe temp, error bit, etc.) - - float result = (RawData * 0.0625); // MAX31855 LSB resolution is 0.0625°C for reference temperature - - return ConvertTemp(result); // Check if we have to convert to Fahrenheit + max31855_initialized = true; + } } /* * MAX31855_ShiftIn(uint8_t Length) * Communicates with MAX31855 via SW-SPI and returns the raw data read from the chip */ -int32_t MAX31855_ShiftIn(uint8_t Length){ - int32_t dataIn = 0; +int32_t MAX31855_ShiftIn(uint8_t Length) { + int32_t dataIn = 0; - digitalWrite(Pin(GPIO_MAX31855CS), LOW); // CS = LOW -> Start SPI communication - delayMicroseconds(1); // CS fall to output enable = max. 100ns + digitalWrite(Pin(GPIO_MAX31855CS), LOW); // CS = LOW -> Start SPI communication + delayMicroseconds(1); // CS fall to output enable = max. 100ns - for (uint32_t i = 0; i < Length; i++) - { - digitalWrite(Pin(GPIO_MAX31855CLK), LOW); - delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns - dataIn <<= 1; - if(digitalRead(Pin(GPIO_MAX31855DO))) - dataIn |= 1; - digitalWrite(Pin(GPIO_MAX31855CLK), HIGH); - delayMicroseconds(1); // CLK pulse width high = min. 100ns - } - - digitalWrite(Pin(GPIO_MAX31855CS), HIGH); // CS = HIGH -> End SPI communication + for (uint32_t i = 0; i < Length; i++) { digitalWrite(Pin(GPIO_MAX31855CLK), LOW); - return dataIn; + delayMicroseconds(1); // CLK pulse width low = min. 100ns / CLK fall to output valid = max. 40ns + dataIn <<= 1; + if (digitalRead(Pin(GPIO_MAX31855DO))) { + dataIn |= 1; + } + digitalWrite(Pin(GPIO_MAX31855CLK), HIGH); + delayMicroseconds(1); // CLK pulse width high = min. 100ns + } + + digitalWrite(Pin(GPIO_MAX31855CS), HIGH); // CS = HIGH -> End SPI communication + digitalWrite(Pin(GPIO_MAX31855CLK), LOW); + return dataIn; } -void MAX31855_Show(bool Json){ - char probetemp[33]; - char referencetemp[33]; - dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); - dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); +/* +* MAX31855_GetProbeTemperature(int32_t RawData) +* Decodes and returns the temperature of TCs 'hot' junction from RawData +*/ +float MAX31855_GetProbeTemperature(int32_t RawData) { + if (RawData & 0x80000000) { + RawData = (RawData >> 18) | 0xFFFFC000; // Negative value - Drop lower 18 bits and extend to negative number + } else { + RawData >>= 18; // Positiv value - Drop lower 18 bits + } + float result = (RawData * 0.25); // MAX31855 LSB resolution is 0.25°C for probe temperature - if(Json){ - ResponseAppend_P(PSTR(",\"MAX31855\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ - probetemp, referencetemp, MAX31855_Result.ErrorCode); + return ConvertTemp(result); // Check if we have to convert to Fahrenheit +} + +/* +* MAX31855_GetReferenceTemperature(int32_t RawData) +* Decodes and returns the temperature of TCs 'cold' junction from RawData +*/ +float MAX31855_GetReferenceTemperature(int32_t RawData) { + if (RawData & 0x8000) { + RawData = (RawData >> 4) | 0xFFFFF000; // Negative value - Drop lower 4 bits and extend to negative number + } else { + RawData = (RawData >> 4) & 0x00000FFF; // Positiv value - Drop lower 4 bits and mask out remaining bits (probe temp, error bit, etc.) + } + float result = (RawData * 0.0625); // MAX31855 LSB resolution is 0.0625°C for reference temperature + + return ConvertTemp(result); // Check if we have to convert to Fahrenheit +} + +/* +* MAX31855_GetResult(void) +* Acquires the raw data via SPI, checks for MAX31855 errors and fills result structure +*/ +void MAX31855_GetResult(void) { + if (Settings.flag4.max6675) { // SetOption94 - Implement simpler MAX6675 protocol instead of MAX31855 + int32_t RawData = MAX31855_ShiftIn(16); + int32_t temp = (RawData >> 3) & ((1 << 12) - 1); + + /* Occasionally the sensor returns 0xfff, consider it an error */ + if (temp == ((1 << 12) - 1)) { return; } + + MAX31855_Result.ErrorCode = 0; + MAX31855_Result.ReferenceTemperature = NAN; + MAX31855_Result.ProbeTemperature = ConvertTemp(0.25 * temp); + } else { + int32_t RawData = MAX31855_ShiftIn(32); + uint8_t probeerror = RawData & 0x7; + + MAX31855_Result.ErrorCode = probeerror; + MAX31855_Result.ReferenceTemperature = MAX31855_GetReferenceTemperature(RawData); + if (probeerror) { + MAX31855_Result.ProbeTemperature = NAN; // Return NaN if MAX31855 reports an error + } else { + MAX31855_Result.ProbeTemperature = MAX31855_GetProbeTemperature(RawData); + } + } +} + +void MAX31855_Show(bool Json) { + char probetemp[33]; + char referencetemp[33]; + dtostrfd(MAX31855_Result.ProbeTemperature, Settings.flag2.temperature_resolution, probetemp); + dtostrfd(MAX31855_Result.ReferenceTemperature, Settings.flag2.temperature_resolution, referencetemp); + + char sensor_name[10]; + GetTextIndexed(sensor_name, sizeof(sensor_name), Settings.flag4.max6675, kMax31855Types); + + if (Json) { + ResponseAppend_P(PSTR(",\"%s\":{\"" D_JSON_PROBETEMPERATURE "\":%s,\"" D_JSON_REFERENCETEMPERATURE "\":%s,\"" D_JSON_ERROR "\":%d}"), \ + sensor_name, probetemp, referencetemp, MAX31855_Result.ErrorCode); #ifdef USE_DOMOTICZ - if (0 == tele_period) { - DomoticzSensor(DZ_TEMP, probetemp); - } + if (0 == tele_period) { + DomoticzSensor(DZ_TEMP, probetemp); + } #endif // USE_DOMOTICZ #ifdef USE_KNX - if (0 == tele_period) { - KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); - } -#endif // USE_KNX - } else { -#ifdef USE_WEBSERVER - WSContentSend_PD(HTTP_SNS_TEMP, "MAX31855", probetemp, TempUnit()); -#endif // USE_WEBSERVER + if (0 == tele_period) { + KnxSensor(KNX_TEMPERATURE, MAX31855_Result.ProbeTemperature); } +#endif // USE_KNX +#ifdef USE_WEBSERVER + } else { + WSContentSend_PD(HTTP_SNS_TEMP, sensor_name, probetemp, TempUnit()); +#endif // USE_WEBSERVER + } } /*********************************************************************************************\ @@ -165,12 +174,12 @@ void MAX31855_Show(bool Json){ bool Xsns39(uint8_t function) { bool result = false; - if(PinUsed(GPIO_MAX31855CS) && PinUsed(GPIO_MAX31855CLK) && PinUsed(GPIO_MAX31855DO)){ + if (FUNC_INIT == function) { + MAX31855_Init(); + } + else if (max31855_initialized) { switch (function) { - case FUNC_INIT: - MAX31855_Init(); - break; case FUNC_EVERY_SECOND: MAX31855_GetResult(); break;