Add support for I2C M5Unit (Mini)Scales using HX711 driver

This commit is contained in:
Theo Arends 2024-09-12 12:14:32 +02:00
parent 9d012f64c8
commit 7b8308c36a
5 changed files with 101 additions and 12 deletions

View File

@ -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 <weight in gram> <precision>`` where `<precision>` 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

View File

@ -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.

View File

@ -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 <weight in gram> <precision>`` where `<precision>` 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)

View File

@ -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

View File

@ -27,12 +27,17 @@
* - Execute command Sensor34 1
*
* To calibrate the scale perform the following tasks:
* - Set reference weight once using command Sensor34 3 <reference weight in gram>
* - (Optional) Set reference weight once using command Sensor34 3 <reference weight in gram>
* - Remove any weight from the scale
* - Execute command Sensor34 2 and follow messages shown
* - Execute command Sensor34 2 <reference weight in gram> 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) {