ATC_MiThermometer
ATC_MiThermometer copied to clipboard
Power consumption when measuring temperature and humidity.
Consumption optimization expected?
The total power consumption is reduced when CPU CLK to 16 MHz. The power consumption for battery measurement is the short measurement cycle. The number of voltage measurements can be increased - these will be small percentages of the total consumption. The main energy consumption that can be reduced is the temperature and humidity sensor reading cycle.
Original firmware (XiaomiLYWSD03MMC):
Firmware ATC_Thermometer.bin:
Test variant (GPIO Wake Up):
Awesome, I guess the 3 peaks are while advertising?
Awesome, I guess the 3 peaks are while advertising?
yes. In the original firmware, the amplitude is less - lower the RF TX level
Module GY-169 (INA169) 100 ohm shunt + oscilloscope
Original firmware (XiaomiLYWSD03MMC):
@pvvx Could you provide the code for the sensor.c module (? only here ? ) where you have used the GPIO wakeup?
Thanks!
ATC_Thermometr differs in consumption 5..6 times (3.6 mA / 0.6 mA) when measuring temperature. One ATC_Thermometr measurement is equal to 5 measurements Original XiaomiLYWSD03MMC.
Could you provide the code for the sensor.c module (? only here ? ) where you have used the GPIO wakeup?
If there was a working code, I would have posted it long ago. It has an unresolved problem...
After sending a command to the sensor for measurement, it is necessary to send the start + sensor address to the bus. https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHTC3_Datasheet.pdf SCL to low. Chip goes into deep sleep -> I2C controller reset. On return (wake up), initialization of the I2C controller causes failure of subsequent read data from the sensor.
#include <stdint.h>
#include "tl_common.h"
#include "drivers.h"
#include "vendor/common/user_config.h"
#include "app_config.h"
#include "drivers/8258/gpio_8258.h"
#include "drivers/8258/pm.h"
#include "stack/ble/ll/ll_pm.h"
#include "i2c.h"
#include "sensor.h"
#define GPIO_WAKEUP_SENS GPIO_PC3 // GPIO_PC2 or GPIO_PC3 ?
int32_t temp;
uint32_t humi;
wakeup_callback_t wakeup_cb; // typedef void (*wakeup_callback_t)(void);
//const uint8_t sens_wakeup[] = {0x35,0x17};
//const uint8_t sens_sleep[] = {0xB0,0x98};
//const uint8_t sens_reset[] = {0x80,0x5D};
void send_sensor(uint16_t cmd) {
if((reg_clk_en0 & FLD_CLK0_I2C_EN)==0)
init_i2c();
reg_i2c_id = 0xE0;
reg_i2c_adr_dat = cmd;
reg_i2c_ctrl = FLD_I2C_CMD_START | FLD_I2C_CMD_ID | FLD_I2C_CMD_ADDR | FLD_I2C_CMD_DO | FLD_I2C_CMD_STOP;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
}
void init_sensor(){
send_sensor(0x1735); // send_i2c(0xE0,(uint8_t *) sens_wakeup, sizeof(sens_wakeup));
sleep_us(240);
send_sensor(0x5d80); //send_i2c(0xE0,(uint8_t *)sens_reset, sizeof(sens_reset));
sleep_us(240);
send_sensor(0x98b0); //send_i2c(0xE0,(uint8_t *)sens_sleep, sizeof(sens_sleep));
}
_attribute_ram_code_ void read_sensor_start(void) {
send_sensor(0x1735); // send_i2c(0xE0,(uint8_t *) sens_wakeup, sizeof(sens_wakeup));
sleep_us(240);
reg_i2c_mode &= ~FLD_I2C_HOLD_MASTER;// Enable clock stretching for Sensor
reg_i2c_id = 0xE0;
reg_i2c_adr_dat = 0xA27C;
reg_i2c_ctrl = FLD_I2C_CMD_START | FLD_I2C_CMD_ID | FLD_I2C_CMD_ADDR | FLD_I2C_CMD_DO;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
reg_i2c_id = 0xE0 | FLD_I2C_WRITE_READ_BIT;
reg_i2c_ctrl = FLD_I2C_CMD_ID | FLD_I2C_CMD_START;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
}
_attribute_ram_code_ void read_sensor_cb(void) {
int16_t _temp;
uint16_t _humi;
wakeup_cb = NULL;
cpu_set_gpio_wakeup(GPIO_WAKEUP_SENS, Level_High, 0); // pad high wakeup deepsleep
init_i2c();
reg_i2c_id = 0xE0 | FLD_I2C_WRITE_READ_BIT;
// int i = 10;
// do {
reg_i2c_ctrl = FLD_I2C_CMD_ID | FLD_I2C_CMD_START;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
// } while((reg_i2c_status & FLD_I2C_NAK) && i--);
reg_i2c_ctrl = FLD_I2C_CMD_DI | FLD_I2C_CMD_READ_ID;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
_temp = reg_i2c_di << 8;
reg_i2c_ctrl = FLD_I2C_CMD_DI | FLD_I2C_CMD_READ_ID;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
_temp |= reg_i2c_di;
reg_i2c_ctrl = FLD_I2C_CMD_DI | FLD_I2C_CMD_READ_ID;
temp = 1750*_temp - 450;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
(void)reg_i2c_di;
reg_i2c_ctrl = FLD_I2C_CMD_DI | FLD_I2C_CMD_READ_ID;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
_humi = reg_i2c_di << 8;
reg_i2c_ctrl = FLD_I2C_CMD_DI | FLD_I2C_CMD_READ_ID;
_humi |= reg_i2c_di;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
reg_i2c_ctrl = reg_i2c_ctrl = FLD_I2C_CMD_DI | FLD_I2C_CMD_READ_ID | FLD_I2C_CMD_ACK;
humi = 100*_humi;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
(void)reg_i2c_di;
reg_i2c_ctrl = FLD_I2C_CMD_STOP;
while(reg_i2c_status & FLD_I2C_CMD_BUSY);
// sleep_us(240);
send_sensor(0x98b0); //send_i2c(0xE0,(uint8_t *)sens_sleep, sizeof(sens_sleep));
}
_attribute_ram_code_ void read_sensor_deep_sleep(void) {
read_sensor_start();
gpio_setup_up_down_resistor(GPIO_PC2, PM_PIN_PULLUP_1M);
gpio_setup_up_down_resistor(GPIO_PC3, PM_PIN_PULLUP_1M);
wakeup_cb = read_sensor_cb;
}
_attribute_ram_code_ void read_sensor_go_sleep(void) {
if(((bls_pm_getSystemWakeupTick() - clock_time())) > 30 * CLOCK_16M_SYS_TIMER_CLK_1MS) {
cpu_set_gpio_wakeup(GPIO_WAKEUP_SENS, Level_High, 1); // pad high wakeup deepsleep
bls_pm_setWakeupSource(PM_WAKEUP_PAD); //gpio pad wakeup suspend/deepsleep
} else {
WaitMs(11);
read_sensor_cb();
}
}
Other ATC_Thermometr code changes:
typedef void (*wakeup_callback_t)(void);
_attribute_ram_code_ void user_init_deepRetn(void){//after sleep this will get executed
....
if(pm_is_deepPadWakeup()){
if (wakeup_cb) {
wakeup_cb();
temp += temp_offset;
humi += humi_offset;
if((temp-last_temp > temp_alarm_point)||(last_temp-temp > temp_alarm_point)||(humi-last_humi > humi_alarm_point)||(last_humi-humi > humi_alarm_point)){// instant advertise on to much sensor difference
set_adv_data(temp, humi, battery_level, battery_mv);
}
last_temp = temp;
last_humi = humi;
}
}
}
_attribute_ram_code_ int app_suspend_enter(void) {
if (ota_is_working) {
bls_pm_setSuspendMask(SUSPEND_DISABLE);
bls_pm_setManualLatency(0);
return 0;
} else {
if (wakeup_cb) read_sensor_go_sleep();
bls_pm_setSuspendMask(
SUSPEND_ADV | DEEPSLEEP_RETENTION_ADV | SUSPEND_CONN
| DEEPSLEEP_RETENTION_CONN);
return 1;
}
}
void main_loop(){
blt_sdk_main_loop();
....
if (!wakeup_cb)
read_sensor_deep_sleep();
....
app_suspend_enter();
}
Is timer used in the original firmware? The current power diagram does not show wake up from GPIO.
Turning on and switching the power supply in chip -> pulse on SCL -> bit 15 Temperature lost.
SCL/SDA - PullUp 1M:
SCL/SDA - PullUp 10K:
Operation is possible only in the "Clock Stretching mode disabled" for the sensor and deep sleep by timer.
Test version with Sensirion SHTC3 humidity sensor working in deep sleep. Timer wake-up.
Source - https://github.com/pvvx/ATC_MiThermometer
Average consumption 17 μA (ATC - 19). Polling of all sensor every 10..11 sec. Adv.interval 1900 ms.
Connection mode is not finalized. Advertising only.
Thank you Victor for looking that deep into it!
Need to buid your Power meter so i can meassure such thinks as well, do you have it documented somewhere?
Will definitely adapt your changes to make the power consumption go down, or do you want to create a PR?
Current consumption in the optimized version: CR2032 - Start 3.314V 200 mAh. Adv.Interval ~1.9 sec (3000*0.625 = 1875 ms) Measured average consumption (1 h, 3.3V): 17.4 uA 200/0.0174/24/30.5 = 15.7025 months
Deep-sleep 6 uA Impulse (Adv): Min 3.5 ms only adv TX/RX, average 5.14 mA Max 17 ms read sensor, average 1.96 mA most often impulse ~4.5 ms
Need to buid your Power meter so i can meassure such thinks as well, do you have it documented somewhere?
For debugging I use:
https://github.com/pvvx/UBIA/tree/master/PowerProfiler
Chrome, USB-COM navigator.serial (experimental-web-platform-features) https://github.com/pvvx/UBIA/blob/master/WebSerialUSB/tst_wso_adcs_GY-169_power_BLE.gif Сan write a USB-COM programmer (com-sws) for Chrome and similar explorer...
Thats a nice idea !
Will definitely adapt your changes to make the power consumption go down, or do you want to create a PR?
I just shared information. I don’t create final custom versions - no time for maintenance.
Power Consumption my new testing (if 2 sec update LCD, measure 10 sec): Advertising Interval 2 sec - 17..18 uA Connection (16,16, 99, 800 -> real interval 2 sec, 3 notify 10 sec) - 15..16 uA
Advertising use RF TX 3 channel, when connected - 1 channel
An additional reduction in consumption for LCD updating:
void update_lcd(){
if(memcmp(&display_cmp_buff, &display_buff, sizeof(display_buff)))
send_to_lcd(display_buff[0],display_buff[1],display_buff[2],display_buff[3],display_buff[4],display_buff[5]);
memcpy(&display_cmp_buff, &display_buff, sizeof(display_buff));
}
@pvvx Can you compare your results with a 10 Ohm shunt? For advertising and measure I get some higher values, maybe because of the voltage drop? (8mA * 100 Ohm = 800mV less voltage on sensor)
Maybe. But for the test, relative values are used - comparison with the original. And less needs to be done. CR2032 also has internal resistance. And operating voltage range device 2.0..3.3V. Lower voltage will result in higher currents (Internal DC-DC).
CPU 16 or 24 MHz ? rf_set_power_level_index(RF_POWER_P3p01dBm)?
Datasheet for Telink BLE SoC TLSR8251:
10 Ohm shunt:
CPU 16 or 24 MHz ? rf_set_power_level_index(RF_POWER_P3p01dBm)?
I have used your coder from your repo (from yesterday)
For deep sleep: there will be additional current for LCD and sensor (deep sleep) - not much but something.
I want only know the real (true) needed energy for measurement, advertising - this are the parts we could modify over the time... ;)
Thanks!
Calculation of the average consumption on the oscilloscope:
Window 24 ms: Average 1.034 mA, Period (adv.impulse) ~2 sec, Deep-Sleep 6 uA
(1.034mA * 24ms + 0.006mA * 2000ms) / 2024ms = 0.0182 mA
Window 24 ms: Average 1.034 mA, Period (adv.impulse) ~2 sec, Deep-Sleep 6 uA (1.034mA * 24ms + 0.006mA * 2000ms) / 2024ms = 0.0182 mA
plus energy of one advertising impulse, right - or is the first part the impulse (I think so,...)?
In the last diagram, this is the maximum duration of an Advertisement with readings of battery voltage and temperature sensor with humidity.
The tail also has:
- I2C SHTV3 sleep command.
- I2C refreshes the LCD.
0.0182mA - Every 2 seconds (each advertisement) the LCD is updated. Every 10 seconds (5 adv) + measurements of all sensors
I have used your coder from your repo (from yesterday)
Repo updates are frequent.
Yesterday it was added:
I make it possible to configure all parameters with reading. And save to Flash - replacing the battery does not change the settings... In the 'connection' mode, each measurement and notify -> less consumption comes out. It is also necessary to embed a GPIO ("reset" mark on the board) trigger for the assigned temperature / нumidity... + loop recording for a month in flash...
It is also necessary to embed a GPIO ("reset" mark on the board) trigger for the assigned temperature / нumidity... + loop recording for a month in flash... What do you mean with this, sorry, didn't understand ;)
How to set different frequency in code (16MHz)? (I have checked your TelinkMiFlasher.html (very nice !) but frequency setting is not there, right?)
Thank you for your work and sharing your knowledge!
"Connection" mode.
In the "connection" mode, a 10 sec cycle consists of 5 periods (2 sec):
The transmission is carried out over one channel and the total consumption is less by several percent.
Due to the shorter duration of the active mode.
How to set different frequency in code (16MHz)?
Select only in source code. app_config.h: #define CLOCK_SYS_CLOCK_HZ 24000000 / 16000000
And don't confuse CLOCK_16M_SYS_TIMER_CLK_xx with CLOCK_SYS_CLOCK_xx for time settings. System timer always from 16 MHz!
The wrong constant + overflow:
if((clock_time()-last_battery_delay) > 5*60000*CLOCK_SYS_CLOCK_1MS){//Read battery delay
https://github.com/atc1441/ATC_MiThermometer/blob/master/ATC_Thermometer/app.c#L79
5 * 60000 * 1000 * 16 = 4800000000 = 0x1_1E1A3000 (!)
I have used your repo again ;) Have you tried the OTA firmware update? I have some problems here (using your flasher page) At first flashed from original FW to your current release - all fine. Then have changed the frequency to 16MHz, tried to update via OTA.
...
12:14:13: Detected custom Firmware
12:14:15: Start DFU
12:14:21: Update error: NotSupportedError: GATT operation failed for unknown reason.
After beginning with upload upload is stopped. After that device is "dead". I must use serial update (python) with new compiled FW, then is all again ok. Tried two times, same result. Any idea? ;)
I have used your repo again ;)
Write there... Reload 'cstartup_825x.S' (there was a previously changed header)
In different devices and SDKs, the maximum advertising interval is no more than 10 seconds. For a longer interval, alternate deep-sleep...
Write there... Reload 'cstartup_825x.S' (there was a previously changed header)
There are no issues enabled in the repo ;)