Comparison of Sleep Currents for XIAO ESP32C6, S3, and C3

First of all, I’d like to thank msfujino for all his detective work with the power consumption. I’ve read a lot of your threads, and it’s been super helpful.

I have a question because of a difference in power consumption I’ve noticed between making the same project on xiao-c6 vs xiao-s3.

Here’s my project:

  • 2x xiao-c6
  • 2x xiao-s3
  • Powered via 18650 in dumb holder, directly soldered to bat+, bat-
  • Battery power being read via 220k ohm resistor voltage divider to ADC
  • code runs every 20 minutes, then sleeps
  • code reads temperature from max31855, wifi signal and power from ADC
  • code then deep sleeps after values are sent
  • code is esphome
  • c6 is using external antennae instead of onboard. See on_boot on c6 yaml.

I suspect perhaps it’s the initialization of external antennae initialization, which is only done on the c6. It could be something else. I have no idea. Curious if deep sleep current could get tested with C6 using external wifi.

I’ve noticed that my s3’s are doing much better in battery life, and I don’t think this can simply be explained by deep sleep current, since it’s so small.

HomeAssistant battery usage graphs
I can’t upload so here’s a link
www.imgur [DOT] com/a/XoEoZDd


  • Purple is an s3 with max31855
  • Brown is an c6 with max31855
  • Orange is an c6 but with PT100 & max31865

You’ll notice the battery on the c6’s goes down way faster than the s3. I’ve noticed this with both of my s3’s.

The purple s3 on the graph, has been running since Jan 24th (graph only shows it since the 29th).

Here are my esphome yaml’s

ESP32-C6

substitutions:
  name: esp32c6-thermocouple
  run_duration_time: 60s
  sleep_duration_time: 20min
  sensor_update_time: 5s
  mac_address: 41a314
  ip_address: 192.168.1.156
## Better Deep Sleep
# https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/
# https://tatham.blog/2021/02/06/esphome-batteries-deep-sleep-and-over-the-air-updates/
# https://webbinaro.dev/blog/battery-powered-esp-sensors-esphome/
# https://community.home-assistant.io/t/esphome-api-vs-mqtt-for-deep-sleep-in-2022/440934
# https://community.home-assistant.io/t/loosing-my-mind-esphome-deep-sleep-and-mqtt-wont-work-together/298787
## LIGHT SLEEP
# https://github.com/esphome/feature-requests/issues/141
# https://gist.github.com/nagisa/8d3af3ff8bfd9563d673bb458dfd491d
esphome:
  name: ${name}
  name_add_mac_suffix: true
  # Change from Internal WIFI Antennae to External
  on_boot:
    - priority: 700.0
      then:
        lambda: |-
          gpio_set_direction(GPIO_NUM_3, GPIO_MODE_OUTPUT);
          gpio_set_level(GPIO_NUM_3, 0);
          vTaskDelay(pdMS_TO_TICKS(100));
          gpio_set_direction(GPIO_NUM_14, GPIO_MODE_OUTPUT);
          gpio_set_level(GPIO_NUM_14, 1);
    - priority: -300
      then:
        - logger.log: 'START - Consider Deep Sleep'
        - script.execute: consider_deep_sleep
        - logger.log: 'FINISH - Consider Deep Sleep'

esp32:
  board: esp32-c6-devkitc-1
  variant: esp32c6
  framework:
    type: esp-idf
    version: "5.3.1"
    platform_version: 6.9.0

## https://github.com/esphome/esphome/pull/7942
external_components:
  - source: github://pr#7942
    refresh: 1d
    components:
      - adc
  - source: github://pr#7988
    refresh: 1d
    components:
      - mqtt
  - source:
      type: local
      path: my_components
    components: [ max31855 ]

# Wi-Fi configuration with WPA3
wifi:
  power_save_mode: LIGHT # default
  fast_connect: true
  networks:
    - ssid: "ASUS"
      password: "XXXXXXXX"
  manual_ip:
    static_ip: ${ip_address}
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.2
  ap:
    ap_timeout: 1min # default

# Enable logging
logger:
  level: DEBUG
  logs:
    mqtt.component: DEBUG
    mqtt.client: DEBUG

# Enable OTA updates
ota:
  platform: esphome
  on_end:
    then:
      - logger.log: "Finished OTA Update. Removing ota_mode"
      - mqtt.publish:
          topic: ${name}-${mac_address}/ota_mode
          payload: 'OFF'
          retain: true
      - globals.set:
          id: ota_mode
          value: "false"
      - delay: 5s

mqtt:
  broker: 192.168.1.2
  port: 1883
  discovery_unique_id_generator: "mac"
  discovery_object_id_generator: "device_name"
  birth_message:
  will_message:
  on_message:
    - topic: ${name}-${mac_address}/ota_mode
      payload: 'ON'
      then:
        - logger.log: "OTA Mode - ON"
        - globals.set:
            id: ota_mode
            value: "true"
        - deep_sleep.prevent: deep_sleep_1
    - topic: ${name}-${mac_address}/ota_mode
      payload: 'OFF'
      then:
        - logger.log: "OTA Mode - OFF"
        - globals.set:
            id: ota_mode
            value: "false"
        - deep_sleep.allow: deep_sleep_1
  
deep_sleep:
  id: deep_sleep_1
  run_duration: ${run_duration_time}
  sleep_duration: ${sleep_duration_time}

switch:
  - platform: shutdown
    id: switch_shutdown
    name: "ESP Shutdown"

# max31855
spi:
  id: spi_thermo
  clk_pin: GPIO19   # SCK pin
  miso_pin: GPIO20  # MISO pin

globals:
  - id: updates
    type: int
    restore_value: no
    initial_value: '0'
  - id: ota_mode
    type: bool
    restore_value: yes
    initial_value: "false"

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    id: wifi_signalzzz
    update_interval: never
    on_value:
      - logger.log: 'UPDATED Wifi Signalzzz'
      - lambda: |-
          id(updates)++;
    
  - platform: max31855
    name: "K Thermocouple Temperature"
    id: k_thermocouple
    cs_pin: GPIO17
    spi_id: spi_thermo
    accuracy_decimals: 1
    update_interval: never
    on_value:
      - logger.log: 'UPDATED Thermocouple'
      - lambda: |-
          id(updates)++;
    reference_temperature:
      name: "Reference Temp"
      id: ref_temp
      on_value:
        - logger.log: 'UPDATED RefTemp'
        - lambda: |-
            id(updates)++;
  - platform: adc
    name: "Battery Voltage"
    id: adc_vcc
    attenuation: 12db
    pin: GPIO0
    accuracy_decimals: 2
    samples: 16 # should be 16, default 1
    update_interval: never
    filters:
      - multiply: 2.0
    on_value:
      - logger.log: 'UPDATED Battery Voltage'
      - lambda: |-
          id(updates)++;

## https://webbinaro.dev/blog/battery-powered-esp-sensors-esphome/
script:
  - id: consider_deep_sleep
    mode: queued
    then:
      - logger.log: 'Doing UPDATES'
      - lambda: |-
          id(updates) = 0;
          id(k_thermocouple).update();
          id(adc_vcc).update();
          id(wifi_signalzzz).update();
      - logger.log: 'Waiting for UPDATES'
      - wait_until:
          lambda: |-
            return (id(updates) >= 4);
      - logger.log: 'DONE Waiting for UPDATES'
      - logger.log:
          format: 'Batt: %3.2f, wifi: %3.2f, temp: %3.2f, ref_temp: %3.2f'
          args: [ id(adc_vcc).state, id(wifi_signalzzz).state, id(k_thermocouple).state, id(ref_temp).state ]
      - delay: 200ms
      - if:
          ## It appears that id(vcc).state doesn't go through the multiplyer. 
          condition:
            # On USB power is 3.7/2 1.34
            lambda: 'return ((id(adc_vcc).state) < 3.0 && id(adc_vcc).state > 2.0);'
          then:
            - logger.log: 
                level: ERROR
                format: 'Going to sleep. VOLTS under 3 %3.2f'
                args: [ id(adc_vcc).state ]
            - lambda: |-
                id(switch_shutdown).turn_on();
          else:
            - logger.log: 
                format: 'Going to sleep for X amount of time. Prevent? %d'
                args: [ id(ota_mode) ]
            - lambda: |-
                id(deep_sleep_1).begin_sleep(false);
## Can't use deep_sleep.enter, as it doesn't obey prevent_sleep
#            - deep_sleep.enter:
#                id: deep_sleep_1
#                sleep_duration: ${sleep_duration_time}

ESP32-S3

substitutions:
  name: esp32s3-thermocouple
  run_duration_time: 60s
  sleep_duration_time: 20min
  sensor_update_time: 5s
  mac_address: ed061c
  ip_address: 192.168.1.159
## Better Deep Sleep
# https://ncrmnt.org/2021/12/06/optimizing-esp8266-esphome-for-battery-power-and-making-an-ice-bath-thermometer-as-well/
# https://tatham.blog/2021/02/06/esphome-batteries-deep-sleep-and-over-the-air-updates/
# https://webbinaro.dev/blog/battery-powered-esp-sensors-esphome/
# https://community.home-assistant.io/t/esphome-api-vs-mqtt-for-deep-sleep-in-2022/440934
# https://community.home-assistant.io/t/loosing-my-mind-esphome-deep-sleep-and-mqtt-wont-work-together/298787
## LIGHT SLEEP
# https://github.com/esphome/feature-requests/issues/141
# https://gist.github.com/nagisa/8d3af3ff8bfd9563d673bb458dfd491d
esphome:
  name: ${name}
  name_add_mac_suffix: true
  platformio_options:
    build_flags: -DBOARD_HAS_PSRAM
    board_build.arduino.memory_type: qio_opi
    board_build.f_flash: 80000000L
    board_build.flash_mode: qio 
  on_boot:
    - priority: -300
      then:
        - logger.log: 'START - Consider Deep Sleep'
        - script.execute: consider_deep_sleep
        - logger.log: 'FINISH - Consider Deep Sleep'

esp32:
  board: seeed_xiao_esp32s3
  variant: esp32s3
  framework:
    type: esp-idf
    version: "5.3.1"
    platform_version: 6.9.0

## https://github.com/esphome/esphome/pull/7942
external_components:
  - source: github://pr#7942
    refresh: 1d
    components:
      - adc
  - source: github://pr#7988
    refresh: 1d
    components:
      - mqtt
  - source:
      type: local
      path: my_components
    components: [ max31855 ]

# Wi-Fi configuration with WPA3
wifi:
  power_save_mode: LIGHT # default
  fast_connect: true
  networks:
    - ssid: "ASUS"
      password: "XXXXXXXXXXXX"
  manual_ip:
    static_ip: ${ip_address}
    gateway: 192.168.1.1
    subnet: 255.255.255.0
    dns1: 192.168.1.2
  ap:
    ap_timeout: 1min # default

# Enable logging
logger:
  level: WARN
  logs:
    mqtt.component: WARN
    mqtt.client: WARN

# Enable OTA updates
ota:
  platform: esphome
  on_end:
    then:
      - logger.log: "Finished OTA Update. Removing ota_mode"
      - mqtt.publish:
          topic: ${name}-${mac_address}/ota_mode
          payload: 'OFF'
          retain: true
      - globals.set:
          id: ota_mode
          value: "false"
      - delay: 5s

mqtt:
  broker: 192.168.1.2
  port: 1883
  discovery_unique_id_generator: "mac"
  discovery_object_id_generator: "device_name"
  birth_message:
  will_message:
  on_message:
    - topic: ${name}-${mac_address}/ota_mode
      payload: 'ON'
      then:
        - logger.log: "OTA Mode - ON"
        - globals.set:
            id: ota_mode
            value: "true"
        - deep_sleep.prevent: deep_sleep_1
    - topic: ${name}-${mac_address}/ota_mode
      payload: 'OFF'
      then:
        - logger.log: "OTA Mode - OFF"
        - globals.set:
            id: ota_mode
            value: "false"
        - deep_sleep.allow: deep_sleep_1
  
deep_sleep:
  id: deep_sleep_1
  run_duration: ${run_duration_time}
  sleep_duration: ${sleep_duration_time}

switch:
  - platform: shutdown
    id: switch_shutdown
    name: "ESP Shutdown"

# max31855
spi:
  id: spi_thermo
  clk_pin: GPIO7   # SCK pin
  miso_pin: GPIO8  # MISO pin

globals:
  - id: updates
    type: int
    restore_value: no
    initial_value: '0'
  - id: ota_mode
    type: bool
    restore_value: yes
    initial_value: "false"

sensor:
  - platform: wifi_signal
    name: "WiFi Signal"
    id: wifi_signalzzz
    update_interval: never
    on_value:
      - logger.log: 'UPDATED Wifi Signalzzz'
      - lambda: |-
          id(updates)++;
    
  - platform: max31855
    name: "K Thermocouple Temperature"
    id: k_thermocouple
    cs_pin: GPIO44
    spi_id: spi_thermo
    accuracy_decimals: 1
    update_interval: never
    on_value:
      - logger.log: 'UPDATED Thermocouple'
      - lambda: |-
          id(updates)++;
    reference_temperature:
      name: "Reference Temp"
      id: ref_temp
      on_value:
        - logger.log: 'UPDATED RefTemp'
        - lambda: |-
            id(updates)++;
  - platform: adc
    name: "Battery Voltage"
    id: adc_vcc
    attenuation: 12db
    pin: GPIO1
    accuracy_decimals: 2
    samples: 16 # should be 16, default 1
    update_interval: never
    filters:
      - multiply: 2.0
    on_value:
      - logger.log: 'UPDATED Battery Voltage'
      - lambda: |-
          id(updates)++;

## https://webbinaro.dev/blog/battery-powered-esp-sensors-esphome/
script:
  - id: consider_deep_sleep
    mode: queued
    then:
      - logger.log: 'Doing UPDATES'
      - lambda: |-
          id(updates) = 0;
          id(k_thermocouple).update();
          id(adc_vcc).update();
          id(wifi_signalzzz).update();
      - logger.log: 'Waiting for UPDATES'
      - wait_until:
          lambda: |-
            return (id(updates) >= 4);
      - logger.log: 'DONE Waiting for UPDATES'
      - logger.log:
          format: 'Batt: %3.2f, wifi: %3.2f, temp: %3.2f, ref_temp: %3.2f'
          args: [ id(adc_vcc).state, id(wifi_signalzzz).state, id(k_thermocouple).state, id(ref_temp).state ]
      - delay: 200ms
      - if:
          ## It appears that id(vcc).state doesn't go through the multiplyer. 
          condition:
            # On USB power is 3.7/2 1.34
            lambda: 'return ((id(adc_vcc).state) < 3.0 && id(adc_vcc).state > 2.0);'
          then:
            - logger.log: 
                level: ERROR
                format: 'Going to sleep. VOLTS under 3 %3.2f'
                args: [ id(adc_vcc).state ]
            - lambda: |-
                id(switch_shutdown).turn_on();
          else:
            - logger.log: 
                format: 'Going to sleep for X amount of time. Prevent? %d'
                args: [ id(ota_mode) ]
            - lambda: |-
                id(deep_sleep_1).begin_sleep(false);
## Can't use deep_sleep.enter, as it doesn't obey prevent_sleep
#            - deep_sleep.enter:
#                id: deep_sleep_1
#                sleep_duration: ${sleep_duration_time}

Any insights would be appreciated.