diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d1680cc58ac..2e2ac2f6b7b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file. - HX711 optional calibration precision option on command ``Sensor34 2 `` where `` is 1 to 20 (#13983) - Matter support for Zigbee Occupancy and Light 0/1/2 (OnOff / Dimmer / White Color Temperature) (#22110) - KNX additional KnxTx functions and define KNX_USE_DPT9 (#22071) +- Support for I2C M5Unit (Mini)Scales using HX711 driver ### Breaking Changed diff --git a/I2CDEVICES.md b/I2CDEVICES.md index 53167f88fe43..3ef72f1cac29 100644 --- a/I2CDEVICES.md +++ b/I2CDEVICES.md @@ -126,4 +126,6 @@ Index | Define | Driver | Device | Address(es) | Bus2 | Descrip 86 | USE_AMSX915 | xsns_114 | AMS6915 | 0x28 | | Pressure (absolute/differential) and temperature sensor 87 | USE_SPL06_007 | xsns_25 | SPL06-007 | 0x76 | | Pressure and temperature sensor 88 | USE_QMP6988 | xsns_28 | QMP6988 | 0x56, 0x70 | Yes | Pressure and temperature sensor + 89 | USE_HX711_M5SCALES | xsns_34 | M5SCALES | 0x26 | Yes | M5Unit (Mini)Scales(HX711 STM32) U177 + NOTE: Bus2 supported on ESP32 only. diff --git a/RELEASENOTES.md b/RELEASENOTES.md index e86bd9d68f2b..0af15f1bf667 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -123,6 +123,7 @@ The latter links can be used for OTA upgrades too like ``OtaUrl https://ota.tasm ### Added - Command ``SetOption69 1`` to enable Serial Bridge inverted Receive [#22000](https://github.com/arendst/Tasmota/issues/22000) - HX711 optional calibration precision option on command ``Sensor34 2 `` where `` is 1 to 20 [#13983](https://github.com/arendst/Tasmota/issues/13983) +- Support for I2C M5Unit (Mini)Scales using HX711 driver - Energy command ``PowerSet 60,230`` to calibrate both Current and Power with known resistive load of 60W at 230V using calibrated Voltage - Energy command ``CurrentSet 60,230`` to calibrate both Power and Current with known resistive load of 60W at 230V using calibrated Voltage - Energy Log level 4 message when (Calculated) Apparent Power is less than Active Power indicating wrong calibration [#20653](https://github.com/arendst/Tasmota/issues/20653) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 27e66f01725a..4b5e7fdf11ac 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -1043,6 +1043,7 @@ // #define TM1638_MAX_KEYS 8 // Add support for 8 keys // #define TM1638_MAX_LEDS 8 // Add support for 8 leds //#define USE_HX711 // Add support for HX711 load cell (+1k5 code) +// #define USE_HX711_M5SCALES // [I2cDriver89] Enable support for M5Unit (Mini)Scales (I2C address 0x26) (+0k4 code) // #define USE_HX711_GUI // Add optional web GUI to HX711 as scale (+1k8 code) // #define HX711_CAL_PRECISION 1 // When HX711 calibration is to course, raise this value diff --git a/tasmota/tasmota_xsns_sensor/xsns_34_hx711.ino b/tasmota/tasmota_xsns_sensor/xsns_34_hx711.ino index c039507a2b72..782d8a291496 100644 --- a/tasmota/tasmota_xsns_sensor/xsns_34_hx711.ino +++ b/tasmota/tasmota_xsns_sensor/xsns_34_hx711.ino @@ -27,12 +27,17 @@ * - Execute command Sensor34 1 * * To calibrate the scale perform the following tasks: - * - Set reference weight once using command Sensor34 3 + * - (Optional) Set reference weight once using command Sensor34 3 * - Remove any weight from the scale - * - Execute command Sensor34 2 and follow messages shown + * - Execute command Sensor34 2 and follow messages shown + * ------------------------------------------------------------------------------------------- + * I2C M5Unit (Mini)Scales(HX711 STM32) supported + * + * I2C Address: 0x26 \*********************************************************************************************/ #define XSNS_34 34 +#define XI2C_89 89 // See I2CDEVICES.md #ifndef HX_MAX_WEIGHT #define HX_MAX_WEIGHT 20000 // Default max weight in gram @@ -47,7 +52,6 @@ #define HX711_CAL_PRECISION 1 // When calibration is to course, raise this value to max 20 (otherwise uint32_t overflow) #endif - #define HX_TIMEOUT 120 // A reading at default 10Hz (pin RATE to Gnd on HX711) can take up to 100 milliseconds #define HX_SAMPLES 10 // Number of samples for average calculation #define HX_CAL_TIMEOUT 15 // Calibration step window in number of seconds @@ -97,7 +101,55 @@ Hx_t* Hx = nullptr; /*********************************************************************************************/ +#if defined(USE_I2C) && defined(USE_HX711_M5SCALES) +// M5Unit scales (STM32) need at least 800mS delay before ready after power on +// hence HxInit() is called 2 seconds after power on using FUNC_EVERY_SECOND + +#ifndef HX_SCALES_ADDR +#define HX_SCALES_ADDR 0x26 // M5Unit (Mini)Scales(HX711 STM32) default I2C address +#endif +#ifndef HX_SCALES_RAW_ADC +#define HX_SCALES_RAW_ADC 0x00 // M5Unit MiniScales raw ADC register (U177) +//#define HX_SCALES_RAW_ADC 0x10 // M5Unit Scales raw ADC register (U108) +#endif +#define HX_SCALES_I2C_ADDR 0xFF // M5Unit (Mini)Scales get I2C address register + +bool HxM5Found(void) { + uint8_t data = 0; + if (!I2cReadBuffer(HX_SCALES_ADDR, HX_SCALES_I2C_ADDR, &data, 1, Hx->pin_dout)) { + return (HX_SCALES_ADDR == data); // Verify I2C address with stored register + } + return false; +} + +long HxM5ReadRaw(void) { + long rawADC = -1; + uint8_t data[4] = { 0 }; + if (!I2cReadBuffer(HX_SCALES_ADDR, HX_SCALES_RAW_ADC, data, 4, Hx->pin_dout)) { +#if (HX_SCALES_RAW_ADC == 0x00) // M5Unit MiniScales (U177) + long rawADC1 = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); +#else // M5Unit Scales (U108) + long rawADC1 = data[0]; + rawADC1 = (rawADC1 << 8) | data[1]; + rawADC1 = (rawADC1 << 8) | data[2]; + rawADC1 = (rawADC1 << 8) | data[3]; +#endif // M5Unit Scales (U108) + rawADC = 0x01FFFFFF - rawADC1; // Additional weight on miniscale decreases ADC value + } + return rawADC; +} +#endif // USE_I2C && USE_HX711_M5SCALES + +/*********************************************************************************************/ + bool HxIsReady(uint16_t timeout) { + +#if defined(USE_I2C) && defined(USE_HX711_M5SCALES) + if (Hx->pin_dout == Hx->pin_sck) { + return HxM5Found(); + } +#endif // USE_I2C && USE_HX711_M5SCALES + // A reading can take up to 100 mS or 600mS after power on uint32_t start = millis(); while ((digitalRead(Hx->pin_dout) == HIGH) && (millis() - start < timeout)) { @@ -107,6 +159,13 @@ bool HxIsReady(uint16_t timeout) { } long HxRead(void) { + +#if defined(USE_I2C) && defined(USE_HX711_M5SCALES) + if (Hx->pin_dout == Hx->pin_sck) { + return HxM5ReadRaw(); + } +#endif // USE_I2C && USE_HX711_M5SCALES + if (!HxIsReady(HX_TIMEOUT)) { return -1; } uint8_t data[3] = { 0 }; @@ -317,21 +376,44 @@ long HxWeight(void) { } void HxInit(void) { - if (PinUsed(GPIO_HX711_DAT) && PinUsed(GPIO_HX711_SCK)) { + uint32_t hx711_config = (PinUsed(GPIO_HX711_DAT) && PinUsed(GPIO_HX711_SCK)) ? 1 : 0; + +#if defined(USE_I2C) && defined(USE_HX711_M5SCALES) + uint32_t bus; + if (!hx711_config && I2cEnabled(XI2C_89)) { + for (bus = 0; bus < 2; bus++) { + if (!I2cSetDevice(HX_SCALES_ADDR, bus)) { continue; } + I2cSetActiveFound(HX_SCALES_ADDR, "M5Unit Scales", bus); + hx711_config = 2; + break; + } + } +#endif // USE_I2C && USE_HX711_M5SCALES + + if (hx711_config > 0) { Hx = (Hx_t*)calloc(sizeof(Hx_t), 1); // Need calloc to reset registers to 0/false if (nullptr == Hx) { return; } // Hx->calibrate_step = HX_CAL_END; // HX_CAL_END = 0 - Hx->pin_sck = Pin(GPIO_HX711_SCK); - Hx->pin_dout = Pin(GPIO_HX711_DAT); - pinMode(Hx->pin_sck, OUTPUT); - pinMode(Hx->pin_dout, INPUT); - digitalWrite(Hx->pin_sck, LOW); +#if defined(USE_I2C) && defined(USE_HX711_M5SCALES) + if (2 == hx711_config) { + Hx->pin_sck = bus; // If both are equal use M5Scale instead of HX711 + Hx->pin_dout = bus; + } else { +#endif // USE_I2C && USE_HX711_M5SCALES + Hx->pin_sck = Pin(GPIO_HX711_SCK); + Hx->pin_dout = Pin(GPIO_HX711_DAT); + pinMode(Hx->pin_sck, OUTPUT); + pinMode(Hx->pin_dout, INPUT); + digitalWrite(Hx->pin_sck, LOW); +#if defined(USE_I2C) && defined(USE_HX711_M5SCALES) + } +#endif // USE_I2C && USE_HX711_M5SCALES SetWeightDelta(); - if (HxIsReady(8 * HX_TIMEOUT)) { // Can take 600 milliseconds after power on + if (HxIsReady(8 * HX_TIMEOUT)) { // Could take 600 milliseconds after power on if (!Settings->weight_max) { Settings->weight_max = HX_MAX_WEIGHT / 1000; } if (!Settings->weight_precision) { Settings->weight_precision = HX711_CAL_PRECISION; } if (!Settings->weight_calibration) { Settings->weight_calibration = HX_SCALE * Settings->weight_precision; } @@ -606,8 +688,10 @@ void HandleHxAction(void) { bool Xsns34(uint32_t function) { bool result = false; - if (FUNC_INIT == function) { - HxInit(); + if (FUNC_EVERY_SECOND == function) { + if (2 == TasmotaGlobal.uptime) { // Fix power on init + HxInit(); + } } else if (Hx) { switch (function) {