Ct_clamp/adc going crazy after bootloader update

Hi, I have an issue that I can not find the solution to.

I am/was using a CT_Clamp on my ESP32 board to track the power usage of my washing machine. I wanted to add a energy monitor and while changing it said that a new bootloader could be installed if connecting the device with a cable.

I updated the device with a cable (APRIL Release) and since then all the logic that before showed 0A or 0W just went crazy and always show a high A value (between 23A or 30A). So that means the tracking is completely broken.

I have no idea what else to check, I changed the ESP32 device just in case and I could reproduce the exact same issue with the new board. I attached the code.

I though maybe the new CPU frequency was to blame but setting it to 160MHZ does not change anything.

Also tried using an old version ESP32 version and the problem still happens (although I supose that IF the problem is the bootloader then an old version does not fix it?)

The issue sounds kind of similar to ads1118 measures bad values after upgrade to 2025.7.0, but that is an old issue and the board was working before.

Does anyone know what can be the problem and or how could I resolve it, fix it or debug it?

Thanks

The config:

esphome:
  name: esp-energy-monitor-technickraum
  friendly_name: Energy Monitor Technickraum

  devices:
    - id: home_main_power_meter
      name: "Home Main Power Meter"

  on_boot:
    priority: -100
    then:
      - lambda: |-
          id(_glbl_timeout_ticks) = 0;
          id(_timer_tick).stop();
          id(washmachine_state).publish_state("Apagada");
      - delay: 10s

esp32:
  board: esp32dev
  framework:
    type: arduino
    advanced:
      minimum_chip_revision: "3.0"

substitutions:
  sml_totals_throttle_time: "60s"
  sml_uart_rx_pin: GPIO3
  sml_uart_tx_pin: GPIO1

packages:
  sml_meter_config: !include common/sml-meter.yaml

logger:
  baud_rate: 0
  level: WARN
  logs:
    esp-idf: ERROR
    # sml: DEBUG
    # adc: WARN
    ct_clamp: WARN
    sensor: WARN
    # template.sensor: WARN

api:
  encryption:
    key: "edited"

ota:
  - platform: esphome
    password: "edited"

wifi:
  ssid: !secret iot_ssid
  password: !secret iot_password_for_trusted_devices
  manual_ip:
    static_ip: 10.69.61.3
    gateway: 10.69.61.1
    subnet: 255.255.255.0
  use_address: esp-energy-monitor-technickraum.local
  ap:
    ssid: "Energy-Monitor-Technickraum"
    password: "edited"

captive_portal:

globals:
  - id: glbl_relay_latched
    type: bool
    restore_value: yes
    initial_value: "true"

  - id: glbl_timeout_length_ticks
    type: int
    restore_value: yes
    initial_value: "300"

  - id: _glbl_timeout_ticks
    type: int
    restore_value: no
    initial_value: "0"

script:
  - id: on_timer_end
    mode: single
    then:
      - select.set:
          id: washmachine_state
          option: "Esperando"
      - logger.log: "on_timer_end: washmachine_state should be Esperando!"

  - id: on_timer_stop
    mode: single
    then:
      - lambda: |-
          id(_timer_tick).stop();
          id(_glbl_timeout_ticks) = 0;
          // ESP_LOGD("script.on_timer_stop", "_timer_tick now stopped and _glbl_timeout_ticks is %d", id(_glbl_timeout_ticks));

  - id: _timer_tick
    mode: queued
    then:
      - delay: 1s
      - lambda: |-
          id(_glbl_timeout_ticks) += 1;

          if (id(_glbl_timeout_ticks) >= id(glbl_timeout_length_ticks)) {
            id(on_timer_end).execute();
            // ESP_LOGD("lambda._timer_tick", "_glbl_timeout_ticks is >= glbl_timeout_length_ticks  %d >= %d ", id(_glbl_timeout_ticks), id(glbl_timeout_length_ticks));
            id(_glbl_timeout_ticks) = 0;
            id(_timer_tick).stop();
            // ESP_LOGD("lambda._timer_tick", "_timer_tick now stopped!");
            return;
          }

          // ESP_LOGD("lambda._timer_tick", "_glbl_timeout_ticks is < glbl_timeout_length_ticks  %d < %d ", id(_glbl_timeout_ticks), id(glbl_timeout_length_ticks));
          id(_timer_tick).execute();

sensor:
  - platform: template
    name: Countdown Value
    id: countdown_value
    icon: mdi:timer
    accuracy_decimals: 0
    update_interval: 1s
    lambda: |-
      return id(_glbl_timeout_ticks);

  - platform: adc
    pin: GPIO34
    id: adc_sensor_wash_machine
    attenuation: 12db
    accuracy_decimals: 2
    internal: true
    update_interval: 2s

  - platform: ct_clamp
    sensor: adc_sensor_wash_machine
    id: washmachine_current
    name: "Washmachine Current"
    unit_of_measurement: "A"
    device_class: current
    sample_duration: 1200ms
    update_interval: 2s
    filters:
      - clamp:
          min_value: 0
          max_value: 30
      - calibrate_linear:
          method: exact
          datapoints:
            - 0 -> 0
            - 0.004 -> 0
            - 0.006 -> 0
            - 0.007 -> 0
            - 0.010 -> 0
            - 0.014 -> 0.133
            - 0.1052 -> 2.9
            - 0.147 -> 4.03
            - 0.21 -> 5.61
            - 0.2995 -> 8
      - lambda: |-
          if (x < 0.055) {
            // ESP_LOGD("lambda", "Sent 0 for %f", x);
            return 0;
          } else {
            // ESP_LOGD("lambda", "Sent Real value %f", x);
            return x;
          }

  - platform: template
    name: "Washmachine Current (Round)"
    id: washmachine_current_round
    unit_of_measurement: "A"
    device_class: current
    lambda: |-
      return id(washmachine_current).state;
    update_interval: 2s
    filters:
      - median:
          window_size: 15
          send_every: 7
          send_first_at: 5
      - quantile:
          window_size: 60
          send_every: 30
          send_first_at: 10
          quantile: .9
    on_value:
      then:
        lambda: |-
          // ESP_LOGD("washmachine_current_round", "Value is %f", x);

          if (id(washmachine_state).current_option() == "Esperando") {
            // ESP_LOGD("washmachine_current_round", "We do not change the Esperando state");
            return;
          }

          if (x < 0.10) {
            if (id(washmachine_state).current_option() == "Apagada") {
              return;
            }

            if (id(washmachine_state).current_option() == "Lavando" || id(washmachine_state).current_option() == "Calentando Agua") {
              if (!id(_timer_tick).is_running()) {
                id(_timer_tick).execute();
              }
              return;
            }

            id(washmachine_state).publish_state("Apagada");
            return;
          }

          if (id(_timer_tick).is_running()) {
            id(on_timer_stop).execute();
          }

          if (x >= 0.10 && x <= 7) {
            id(washmachine_state).publish_state("Lavando");
            return;
          }

          if (x > 7) {
            id(washmachine_state).publish_state("Calentando Agua");
            return;
          }

          // ESP_LOGE("washmachine_current_round", "We are outside of the valid ifs!");
          id(washmachine_state).publish_state("Apagada");

  - platform: template
    name: "Washmachine Power"
    id: washmachine_power
    unit_of_measurement: "W"
    device_class: power
    state_class: measurement
    lambda: |-
      return id(washmachine_current).state * 230;
    update_interval: 2s
  
  - platform: template
    name: "Washmachine Power Round"
    id: washmachine_power_round
    unit_of_measurement: "W"
    device_class: power
    state_class: measurement
    update_interval: 60s
    lambda: |-
      return id(washmachine_current_round).state * 230;
    
  # - platform: integration
  #   name: "Washmachine Energy"
  #   sensor: washmachine_power
  #   time_unit: h
  #   unit_of_measurement: "kWh"
  #   device_class: energy
  #   state_class: total_increasing
  #   accuracy_decimals: 3
  #   restore: false

  # - platform: integration
  #   name: "Washmachine Energy Round"
  #   sensor: washmachine_power_round
  #   time_unit: h
  #   unit_of_measurement: "kWh"
  #   device_class: energy
  #   state_class: total_increasing
  #   accuracy_decimals: 3
  #   restore: false

select:
  - platform: template
    name: "Washmachine State"
    id: washmachine_state
    icon: "mdi:washing-machine"
    options:
      - "Apagada"
      - "Calentando Agua"
      - "Lavando"
      - "Esperando"
    optimistic: true
    restore_value: true
    initial_option: "Apagada"

What version you had before update? Did you change anything on your yaml?
Set you log level to debug and observe raw adc values on esphome logs.

Hi,

I am not sure which version did I had to be honest, I was working for more than a one and half year doing updates in between. I also tried running 2026.2.2 and the problem is the same.

The logs show too big values also.

[20:30:11.430][D][ct_clamp:040]: 'Washmachine Current' - Raw AC Value: 0.824A after 4344 different samples (3620 SPS)
[20:30:11.475][D][lambda:166]: Sent Real value 21.997744
[20:30:11.577][S][sensor]: 'Washmachine Current' >> 22.00 A
[20:30:12.138][S][sensor]: 'Countdown Value' >> 0 
[20:30:12.968][S][sensor]: 'Washmachine Power' >> 5059.5 W
[20:30:13.135][S][sensor]: 'Countdown Value' >> 0 
[20:30:13.430][D][ct_clamp:040]: 'Washmachine Current' - Raw AC Value: 0.824A after 4349 different samples (3624 SPS)
[20:30:13.440][D][lambda:166]: Sent Real value 22.005011
[20:30:13.541][S][sensor]: 'Washmachine Current' >> 22.01 A
[20:30:14.136][S][sensor]: 'Countdown Value' >> 0 
[20:30:14.968][S][sensor]: 'Washmachine Power' >> 5061.2 W
[20:30:15.169][S][sensor]: 'Countdown Value' >> 0 
[20:30:15.466][D][ct_clamp:040]: 'Washmachine Current' - Raw AC Value: 0.826A after 4408 different samples (3673 SPS)
[20:30:15.489][D][lambda:166]: Sent Real value 22.052935
[20:30:15.569][S][sensor]: 'Washmachine Current' >> 22.05 A
[20:30:16.140][S][sensor]: 'Countdown Value' >> 0 
[20:30:17.002][S][sensor]: 'Washmachine Power' >> 5072.2 W

So I have connect it again to another known load and I see the Raw AC value reporting around 1.28A with or without an active load. So the problem is probably at the ADC level. I do not see any other hardware defect, the resistors and condenser look correct...

I'd like to see raw adc voltage, not ct-clamp value.
Can you remove "internal: true" from adc and give it a name as well.

It all depends on your goals, but the internal ADC on esp chips is not very good. Some people have been successful using them for some things. I avoid using them at all.

There are many ready made devices that do a decent job of measuring power and many of them can use esphome and/or connect easily to Home Assistant. I use a combination of SonOff and Shelly devices. Shelly makes the PM mini that just measures power (no control).

I don't see how the boot loader could impact the running code (but I suppose it is possible). This one is likely going to be hard to debug. You could have a hardware issue or there really could be something in the software that has changed and makes it no longer work.

If you really want to debug it, you will need to go back to first principles and make sure everything is working they way you (really all the things/components your are dependent on) expect it to.

I would just buy a Shelly PM mini for $10. It should be able to handle your washing machine just fine

My main worry with that is that AFAIK those devices are not made to be used with inductive loads (and I have seen some horror cases in internet) That is why I went the CLAMP way, I do not want to have a fire hazard.

The issue is switching an inductive load with a relay (the relay needs to be rated for it). The PM has no relay. They also make devices that use a CT if you prefer. The EM is one from Shelly (but not the EM mini gen 4, which seems to be more like the PM mini gen 3).

I think Shelly really fucked up the naming of their devices, there's no logic at all anymore.

1 Like