znanev / ATC_MiThermometer

Custom firmware for the Xiaomi Thermometer MHO-C401 and Telink Flasher via USB to Serial converter
2 stars 0 forks source link

Energy usage while updating display #7

Closed michapr closed 3 years ago

michapr commented 3 years ago

Original firmware while updating display: c401_partial_update (take y-value/10 for mA)

Custom firmware with cpu_stall_wakeup_by_timer0() c401_cust_fw_2 (one time unit = 500ms)

This part comes up only while updating display with new value, any idea what can be different here? (100ms <-> 1.5sec)

znanev commented 3 years ago

Ok, now I clearly see the difference and have no explanation - it seems on the second graph that the CPU is busy waiting while active (and it should be in deep sleep instead).

Just to be 100% sure that you've got the latest binary, can you please get it from the latest release:

https://github.com/znanev/ATC_MiThermometer/releases/download/0.4.0/ATC_Thermometer.bin

Cheers!

michapr commented 3 years ago

Here is the picture from this FW: c401_cust_fw_3 (one unit=500ms)

znanev commented 3 years ago

Hm, that's really strange, indeed.

Is this measurement on startup, or is it some of the cyclical updates?

michapr commented 3 years ago

It is one of the cyclic updates, you see the advertising peaks every 2 seconds. During startup there is a longer interval.

znanev commented 3 years ago

I'll have to make some captures with a logic analyser from the original and then the modified firmware, but can't promise I'll find time for this during the week.

How about the startup sequence? Does the modified firmware consume much more energy than the original one?

michapr commented 3 years ago

I have not checked it, because there is the "ATC sequence" with MAC. This take a long time same energy (if I remember right) - but this is only once, that's why have not checked in detail.

I have already tried to move the POWER_ON / POWER_OFF to other places, but no difference. Do you remember about the IO_BUSY_N waiting times after each transmit operation?

michapr commented 3 years ago

Of course not sure (do not know this CPU in detail) - but maybe the suspend mode is not the right one here in this case?

http://wiki.telink-semi.cn/tools_and_sdk/Driver/doc/kite/html/group___g_p7.html http://wiki.telink-semi.cn/tools_and_sdk/Driver/doc/kite/html/md__project_1__table_of__content_10__t_s_i__p_m__features.html

The Deepsleep with SRAM Retention Mode seems to save more energy and can be used with timer for wakeup. As far as I understand it is used for the sleep between the advertising intervals too (if I'm not wrong)

znanev commented 3 years ago

Of course not sure (do not know this CPU in detail) ...

Neither do I - to be honest the documentation for this CPU is slacking compared to "big brands" CPUs...

This function looks promising - cpu_sleep_wakeup_32k_rc():

http://wiki.telink-semi.cn/tools_and_sdk/Driver/doc/kite/html/pm_8h.html#af8f6984e656c60eae0d02c45a573e1a9

I'll have to experiment with it, but as I said previously, my main interest was to make the EPD work .. at all :) My knowledge about this particular MCU is close to zero. But nevertheless I'm curious enough to try to at least "emulate" the original firmware regarding the EPD driving, I'm just not confident enough in my ability to delve deeper into this particular platform.

atc1441 commented 3 years ago

I wouldnt call myself a guru on that as well, my way would also be try and error, need to get my hands on a low power meassuring method!

Another approach is to make a state mashine and use an interrupt on the busy pin. So going to sleep on update and let the epd wake the mcu.

znanev commented 3 years ago

@michapr Can you please try measuring the energy with the latest release:

https://github.com/znanev/ATC_MiThermometer/releases/download/0.5.0/ATC_Thermometer.bin

I changed the IDLE sleep with DEEP SLEEP this time, I'm curious to see if there are any measurable changes to the energy consumed.

@atc1441 This was exactly my train of thought too - instead of waiting to de-assert BUSY_N signal, create a pin interrupt handler for it... for which handling of course a state machine will be needed. Any volunteers for that ;)

atc1441 commented 3 years ago

Would really love to do it, but it will take the evenings of about 1 week to be perfect and i can not spend that time right now. As the complete integration of the EPD would be on the list then as well. Also data logging and persistense storage^^ oh an 2 point callibration. Sorry for OT

znanev commented 3 years ago

@atc1441 I was actually joking for the volunteering part ;)

No worries, I'm in the same boat - not much spare time right now, just satisfying my insatiable curiosity ...

atc1441 commented 3 years ago

Even if its a joke it's still true :D some day someone will do it

michapr commented 3 years ago

@znanev

@michapr Can you please try measuring the energy with the latest release: https://github.com/znanev/ATC_MiThermometer/releases/download/0.5.0/ATC_Thermometer.bin

No visible difference,... Will test more later today.

znanev commented 3 years ago

@michapr

No visible difference,... Will test more later today.

I think that I have a theory about what might be happening!

The original firmware broadcasts temperature and humidity once in about 10 minutes and battery level once in an hour. So the energy you measure on every update interval is just the one consumed by the sensor and the EPD, and does not include the BLE broadcast! In contrast, the ATC firmware is advertising measurements about every 2 seconds. So while you are measuring the EPD updates, it might just happen that the measurement also includes the radio transmission of the BLE advertisement packets! @atc1441 - can probably double-check this to be 100% sure.

So, if you want to test the energy consumption of both firmwares in a more comparable manner, I think that we should build a version with BLE advertisement disabled, or at least with a big enough interval so you can really measure just the EPD update energy consumption.

Surely, I may be totally wrong.. but at least this might be a good start !

atc1441 commented 3 years ago

You are wrong on that. BLE is always broadcasting its name, in adidtion to the name it is "Advertising" the sensor data. It makes not difference if Advertising is enabled or not. Ble is sending every 3 seconds or so an name and advertising package even on stock firmware

atc1441 commented 3 years ago

It could still be that ble is active while meassuring tho

znanev commented 3 years ago

Fair enough - that's why I wanted to fact-check my theory with you.

What I can do later this week is capture (again!) the EPD communication from the original firmware and from the modified one. Then compare the overall timings...

michapr commented 3 years ago

I have compared the refresh on display only - was waiting while display was changed and captured the picture - in both cases...

@znanev : Is for C401 still P14 still the SWS pin? I have to flash my sensor... wrong software experiment ;) And - what is the Reset pin there, do you know it?

Thanks

znanev commented 3 years ago

Yep, it's the P14 pad. And don't forget to connect a 1.5k resistor between TX and RX of your USB-to-Serial adapter.

As for the reset - it is not brought to a pad - you have to use pin 18 on the chip directly, if you must.

Or just use ATCtelink.py directly - I flash mine with it all the time. But the newest one from @atc1441's repo doesn't work anymore for me - so be sure to use older version (before today), or use the one from this fork.

michapr commented 3 years ago

Thanks, reflashed... ;)

Want to add some useful information about original firmware... I was thinking about the EPD ready time - this should be visible on original sensor too... And it is. You see the advertising peaks every 2 seconds about - and the lower power usage for partial update.

So the time for update after measurement is also about 2 seconds (so should be) - but the used energy is smaller. c401_partial_update_long (one time unit: 500ms)

Maybe the update procedure is other? As sample not all elements will be updated - only the different?

Here the update procedure in long: c401_partial_update_long2 (one time unit 200ms)

BTW: Even the advertising is within the EPD update ;)

znanev commented 3 years ago

Maybe the update procedure is other? As sample not all elements will be updated - only the different?

Surely my implementation is quite different than the one used in the original firmware. The only way to tell this is to de-compile the original code, but it is a tedious task and I couldn't configure the tools needed to do that. The other option was reverse-engineering, which I did. Based on my observations and findings, having sniffed the communication traffic between the CPU and the EPD, I'm quite confident that the communication part is quite close to the original. At least this is what I saw when re-captured said communication with logic analyser.

The original firmware performs partial update most of the time - which inevitably takes about 2 seconds every time. This is still quite a gain, because full update takes more than 4 seconds. But this is the nature of EPD displays - they need time to energise the white and black particles in order to display / clear segments.

I'll try to re-capture the original firmware's communication between the MCU and EPD with logic analyser during the weekend, so we can later compare with the custom implementation.

Meanwhile, if you are feeling adventurous, you can try flashing @atc1441 's original firmware (from his repo, not this fork) in your MHO-C401 device. Surely the display will be blank, but the BLE part should be working normally. Then do the same measurements (wait at least 10 seconds from device start, as this is the time the initializations are done and MAC address is shown). It will be interesting to see what the "baseline" is when later comparing what the firmware driving the EPD display consumes.

michapr commented 3 years ago

C401 with MMC3 firmware (without EPD function, latest from today): Measure temperature: c401_cust_fw-mmc_measure

Advertising interval: c401_cust_fw-mmc_advertising_200ms (one time unit: 200ms)

Advertising detail (typical): c401_cust_fw-mmc_advertising

For all pictures: y-value / 10 = mA

pvvx commented 3 years ago

The ATC_MiThermometer firmware fits completely into SRAM. ATC_MiThermometer uses up to 45 kilobytes (codes + data + bss + stack). The chip has 64 kilobytes of SRAM. The current firmware is just filled with zeros of unused retention SRAM and is 60.7KB in size. Compression is possible in the linker script, discarding unnecessary ones. Using code in SRAM increases performance, which also affects consumption (in active-mode).

However, storing large amounts of SRAM increases the hibernation consumption. Chip SRAM power saving is controlled bit by bit for 8/8/16/16 KB blocks. The software (SDK) has it, but its options are limited in the linker scripts.

The author did not use CPU stops in wait cycles and does not disable unused CPU peripherals. This alone gives a reduction in consumption by more than 3 times.

You can reduce consumption by working on combinations and leaving the previous algorithms up to 5 times.

pvvx commented 3 years ago

Original firmware (first startup) image sleep_us(x ms) -> _attribute_ramcode void pm_WaitMs(unsigned int ms) { cpu_stall_wakeup_by_timer0(msCLOCK_SYS_CLOCK_1MS); } image *(all test power 3.3V)

Turn off the ADC when not in use to measure power = - 0.4 мА

The average value only for the TLSR8251 microcircuit = 0.0130 mA = 13 μA with a simple cycle... : image

pvvx commented 3 years ago

int32k+deep+none: 0.41uA int32k+deep+pad: 0.41uA int32k+deep+tmr: 0.92uA int32k+deepret16k+none: 1.41uA int32k+deepret16k+pad: 1.41uA int32k+deepret16k+tmr: 1.87uA

ext32k+deep+none: 0.43uA ext32k+deep+pad: 0.43uA ext32k+deep+tmr: 2.03uA ext32k+deepret16k+none(tmr should open: 3.00uA) ext32k+deepret16k+pad(tmr should open: 3.00uA) ext32k+deepret16k+tmr: 3.00uA -> https://github.com/Ai-Thinker-Open/Telink_825X_SDK/blob/master/example/8258_driver_test/test_low_power.c#L34

michapr commented 3 years ago

@pvvx 👍 Great work, I have limited knowledge about this processor, and make experiments on sensor is a bit "dangerous" ;) Was happy to be possible two time revive it with possible reflash :)

Can you publish your modified code anywhere ? - I really would like to test and compare it with current results.

Thanks!

znanev commented 3 years ago

@pvvx Thanks for your thorough analysis!

This is way over my knowledge about this particular CPU, but nevertheless your findings will surely serve as a kick start for someone more knowledgeable, who can fine-tune the code to be less power hungry.

pvvx commented 3 years ago

I did not check and did not alter everything. Old Fork with small changes laid out a long time ago. https://github.com/pvvx/ATC_MiThermometer PS: Basically I dig with modules (TB-03F, TB-04 and others), and not with ready-made devices ...

michapr commented 3 years ago

@pvvx - if I understand right you say the main difference in energy consumption is to use the cpu_stall_wakeup_by_timer0(ms*CLOCK_SYS_CLOCK_1MS); ? This is already used in epd.c - the most "problematic" display update procedure.

Your graph (startup) is with EPD (MHO-C401) ? Have you any idea what could be optimized here in this code part?

pvvx commented 3 years ago

The chip, when turned on, consumes a lot when it comes out of sleep mode. The delay before GPIO initialization is very important until they are all initialized. + 3 or more mA.

pvvx commented 3 years ago

Almost all peripherals (i2c, UART, etc.) work without a processor. The processor can sleep... Standby power consumption 1.42..1.45 mA.

The activation time from deep sleep depends on the size of the ".data" segment. It is better that it be zero. More detailed in the Chinese documentation: https://yadi.sk/i/FsMuapzANxT78Q

pvvx commented 3 years ago

Disable the fattest consumer - flash cache (ictag) has not yet been possible. It might not be possible to make it work only from SRAM. Until there was time...

michapr commented 3 years ago

The current main problem for this sensor usage is the e-ink display procedure.

As you can see in the picture at the top - the procedure take about 1.5 seconds and 3mA. Time is needed for the display - there is no way... But the current is too high, the original firmware take some more time but less current for same thing: https://github.com/znanev/ATC_MiThermometer/issues/7#issuecomment-738892658 (take y-value/10 for mA) It looks like the CPU sleep loop is not working effective (in "send_sequence()" while waiting for the IO_BUSY_N ), but not sure of course.

Maybe you have any hint for us?

znanev commented 3 years ago

The average value only for the TLSR8251 microcircuit = 0.0130 mA = 13 μA with a simple cycle... : image

@pvvx Are you sure that the consumption shown every ~2 seconds is indeed due to the EPD refresh? From what I see on the logic analyser, the display is not refreshed on uniform intervals, but only when there is a change to be shown (temperature / humidity / symbols). I'll post captures later.

In order to be sure that the display updates as frequently as possible, what I do at least, is breath out onto the sensor, then quickly waving a piece of cardboard to cool it down, then breath out again etc. @pvvx - if you can do this an re-capture the energy consumption, that will be great.

michapr commented 3 years ago

@znanev - as far as I understand this is for MMC03 sensor - it's about the chip power usage only.

I have modified your code a bit - updating EPD only if temp/hum changed - it save much battery as I can see. Have set advertising interval to 5 seconds. Battery measurement to large interval (some hours).

And at second sensor have optimized the EPD update only if temp. change is >0.1 degree and hum. change >2% - it is for my case ok and will save battery in the meantime, while we have not optimized the EPD update function.

In any case this is better as to connect every 5 minutes to the sensor and request from there the values...

znanev commented 3 years ago

I'm posting some captures from the logic analyser, made with the original firmware of MHO-C401.

  1. Start-up sequence on power on.

MHO-C401_PowerOn_Init.logicdata

image

Some observations:

Up until 1st second there is some glitching from inserting the USB cable of the power supply, connected to the device (A1).

  1. Some partial updates within one minute

MHO_C401_PartialUpdates_within_one_minute.logicdata

image

Some observations:

I captured 60 seconds of data while the device was running its "normal" cycle - i.e. displaying current temperature and humidity values. As those were relatively stable with the period of one minute, there were just two display updates within this period.

As you can see on the graph, there is a "silent" period of about 37 seconds, and no communication is happening between the CPU and the display during this interval. So the original firmware indeed updated the display only if there are any changes, not on regular intervals.

Later on I'll post similar captures, but with the device running the modified firmware.

znanev commented 3 years ago

Following up with captures from the modified (ATC) firmware, latest code (as of Release 0.6.0).

  1. Start-up sequence on power on.

MHO-C401_ATC_PowerOn_Init.logicdata

image

Some observations:

The full initialisation sequence takes 6.68 seconds (A1 to A2). This is a bit faster than the original firmware (7.33), but I've set quite aggressive timings for the clock signal and these seem to work ok.

Following command DISPLAY_REFRESH (0x12), signal BUSY_N is low for 1.57 seconds (B1 to B2 and C1 to C2). During this initialisation sequence, each DISPLAY_REFRESH command performs full update (taking 1.57 seconds). This is exactly the same as in the original firmware and can be expected (i.e. this is the time waiting for the display to de-assert the BUSY_N signal).

  1. Some partial updates within one minute

MHO_C401_ATC_PartialUpdates_within_one_minute.logicdata

image

Some observations:

I captured 60 seconds of data while the device was running its "normal" cycle - i.e. displaying current temperature and humidity values. Although those were relatively stable (changed in fact two to three times within that minute), it is visible that the display is refreshed on every wake interval (this is what the code does and can be optimised, as @michapr mentioned above).

Partial update sequence (from POWER_ON - 0x04 until DISPLAY_REFRESH - 0x12) takes about 1.3 seconds (C1 to C2), which is faster compared to the original firmware (1.6 seconds). This is some quirk of the original firmware, which delays a bit the two data send sequences, but from my experiments it doesn't matter, so I left it as is in the driver code.

Following command DISPLAY_REFRESH (0x12), signal BUSY_N is low for 1.25 seconds (A1 to A2 and B1 to B2). This can be expected and the timings exactly matches that of the original firmware (i.e. waiting for de-asserting the BUSY_N signal).

@michapr You can use Saleae Logic to open the captures, if you need to peek into more details about the timings or the decoded SPI protocol (give me a shout if you need another format). I noticed some slight discrepancies in the intermediate re-initialisation of the display, I'll play with those at some point in order to bring my code as close as the original as possible in terms of SPI communication.

In conclusion, there is something else in play that contributes to the excessive energy consumption in the ATC firmware. I have no explanation at present. From my point of view, the SPI communication in the custom firmware closely matches that of the original firmware and this shouldn't be the contributing factor for the excessive energy consumption.

michapr commented 3 years ago

@znanev Thanks a lot for this details, I will check it (have the software in use) What I see is that BUSY_N have in original FW some additional short peaks - looking at our code I thought it should be here too. We are waiting multiple times for the BUSY_N state, right?

Maybe it make sense to set the CPU into longer wait state during the A1/A2 period? Every wakeup cost energy, maybe this also could help.

znanev commented 3 years ago

@michapr Indeed the original firmware probably awaits for BUSY_N after every command. In all fairness, the code I used as base for the EPD driver had waiting for BUSY_N on these places that are in the code now. At the time I didn't have good captures to compare like-for-like the commands, arguments and the BUSY_N and RST_N signals. Now that we have them, we can work through them to enhance the code to match as closely to the original SPI communication as possible. But this will take time...

pvvx commented 3 years ago

@michapr Where the use of Standby power consumption? (1.42..1.45 mA): image No Standby, No ADC off.

pvvx commented 3 years ago

@michapr image Minimal deep-sleep-> test-> deep-sleep cycle for TLSR825x image (500uA / 200us grid step) Example: Recharge deep-sleep (Timer + Retention 16 KiB SRAM): (1.82 mA 0.5 ms + 2.7 mA 0.9 ms) / 1.4 ms

pvvx commented 3 years ago

cpu_sleep_wakeup(DEEPSLEEP_MODE_RET_SRAM_LOW16K , PM_WAKEUP_TIMER, clock_time() + xCLOCK_16M_SYS_TIMER_CLK_1S) The maximum cpu_sleep_wakeup (DEEPSLEEP_MODE_RET_SRAM_LOW16K, PM_WAKEUP_TIMER, clock_time () + 8 CLOCK_16M_SYS_TIMER_CLK_1S) in Telink_825X_SDK is only up to 8 seconds. (8 inclusive) If more than 8 seconds, clock_time () counter and other timers counters will be invalid.

pvvx commented 3 years ago

_PM_WAKEUPPAD wake-up source comes from GPIO module, all GPIOs except 4 MSPI pins (PAx / PBx / PCx / PDx) high and low levels have wake-up function. cpu_set_gpio_wakeup (IO_BUSY_N,Level_High, 1); and goto deep-sleep.

Each output on the LCD will trigger a deep sleep state. Add a set of sleep pauses with RF pauses. The SDK has special routines for this. If the GPIO signal is not active, then there will be no transition to deep-sleep... There are deep sleep exit flags that caused awakening...

In BLE, the CPU must sleep and, when wake-up, process small high-speed tasks. Not polling, cycles and etc. Only state machine for wake-up.

For quick monitoring of processes - standby mode, for long standby - deep sleep. What should be applied from the published consumption chart: In standby mode - 1.5 mA, in deep sleep mode 1.4 ms 2.4 mA

The maximum CPU consumption in the ATC_MiThermometer task occurs when polling the supply voltage = 3.x mA. In other cases, up to 2.4 mA. RF directly depends on the TX power.

If these conditions are met, then the number of LCD updates has very little effect on overall consumption. And also the number of temperature polls ... To get correct readings, the number of sensor polls must be increased. PS: I find that 1 poll per second is ideal for home use.

pvvx commented 3 years ago

BLE is always broadcasting its name, in adidtion to the name it is "Advertising" the sensor data. It makes not difference if Advertising is enabled or not. Ble is sending every 3 seconds or so an name and advertising package even on stock firmware

Maximum - 4 sec. With a 3 second interval, there are already a lot of drops on the receiving side. This has most of the external software on different BLE devices. The timer error is increasing. XiaomiLYWSD03MMC does not have a 32768 quartz

pvvx commented 3 years ago

atc1441 > I wouldnt call myself a guru on that as well, my way would also be try and error, need to get my hands on a low power meassuring method! GY-169 INA169, INA199 + oscilloscope. Shunt - ~5..100 Ohm Oscilloscope or ADC with sampling from 50..100 ksps 16 bit. PS: The TSLR825x has a 50 ksps 14 bit ADC, TSLR8258 and USB pins (600 kbytes per second) -> USB-ADC :) TLSR8266 Very low price in JDY-10 module, has ADC and USB... Price below STM32F103T8U6 (Blue Pill)

michapr commented 3 years ago

@pvvx

@michapr Where the use of Standby power consumption? (1.42..1.45 mA):

take the y-value/10 - you will get mA (is witten below the graphics ;) ) - and there is noise above the signal, I know. I use a shunt 10 Ohm, so the voltage is low, and my oscilloscope will show noise without additional external filter. It was not made for a "measurement", but only to get an idea about the energy consumption of the device...

Agree with ADC on/off, I have used the original FW only and tried to optimize the main procedures only, but less inside. As I said, this is not "my CPU" - I use more the Atmel/ESP series and the nRF 5x series for BLE :)

I hope to find time to read more in the TLSR documentation - but it is not easy to make "experiments" on a ready device - better would be to have a breakout board like you use....

pvvx commented 3 years ago

take the y-value/10 - you will get mA (is witten below the graphics ;)

on your graph 3.3mA. ADC off = 3.3-0.4 = 2.9. All peripherals are off = - 0.2 mA CPU standby -> = 1.52 mA. Activate I2C/SPI/UART = up to 0.1 mA Turning on the build in temperature sensor = + 0.44 mA.

image Forgot to turn off the ADC!

pvvx commented 3 years ago

ATC rf_set_power_level_index (RF_POWER_P3p01dBm); TX = < 8 mA

All my measurements at 3.3V. CR2032 and built-in DC-DC will be larger Оn your graph 9 mA (RF TX) The first launch of the chip on the ATC_MiThermometer will take less time. RC 32k, not start quartz 32k

I can measure the current firmware, but does it make sense? I have a lot of LYWSD03MMC but I haven't measured them yet. The check was done on an identical layout. To get only CPU consumption image Checking the original XiaomiLYWSD03MMC showed that it was not possible to use it. Large errors in temperature and humidity. I see no reason to change the sensor in it. It's easier to make another device with a different sensor.

znanev commented 3 years ago

@pvvx Thanks for the heads up about the interrupt waiting function. This is probably the way the original firmware is awaiting the BUSY_N de-assertion.

@michapr Can I please ask you to pull the code from branch test-pin-wakeup, build it and test it on your device? Since I got a negative WAF (Wife Approval Factor) last week, I had to de-solder the debug wires of my device and re-assemble it back and clean-up my workspace :) So I have no means to upload firmware via serial if the last binary turns bad..