Open talldrin opened 1 year ago
The following code is working in a ESP32. Note that my voltage sensor comes from homeassistant, you can use a fixed value if you want or don't have a voltage meter.
substitutions:
update_time: never
disp_name: "house-power-sensor"
esphome:
name: house-power-sensor
friendly_name: house-power-sensor
esp32:
board: esp32dev
framework:
type: arduino
# Enable logging
logger:
level: debug
# Enable Home Assistant API
api:
encryption:
key: "USE YOUR ENCRYPTION KEY HERE"
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "house-power-sensor"
password: "PROVIDE AP PASSWORD"
time:
- platform: homeassistant
id: homeassistant_time
interval:
- interval: 5s
then:
- script.execute: readCTs
script:
- id: readCTs
then:
- component.update: ct1Amps
- delay: 500ms
- component.update: ct1Watts
- delay: 500ms
- component.update: ct2Amps
- delay: 500ms
- component.update: ct2Watts
- delay: 500ms
- component.update: ct3Amps
- delay: 500ms
- component.update: ct3Watts
- delay: 500ms
- component.update: ct4Amps
- delay: 500ms
switch:
- platform: restart
name: "Restart"
sensor:
- platform: ct_clamp
sensor: adc_sensor_1
name: ct1Amps
id: ct1Amps
update_interval: never
sample_duration: 200ms
filters:
- calibrate_linear:
- 0 -> 0
- 0.07972 -> 7.72
- platform: adc
pin: GPIO34
id: adc_sensor_1
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: adc_sensor_2
name: ct2Amps
id: ct2Amps
update_interval: never
sample_duration: 200ms
filters:
- calibrate_linear:
- 0 -> 0
- 0.08278 -> 7.71
- platform: adc
pin: GPIO35
id: adc_sensor_2
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: adc_sensor_3
name: ct3Amps
id: ct3Amps
update_interval: never
sample_duration: 200ms
filters:
- calibrate_linear:
- 0 -> 0
- 0.08317 -> 7.84
- platform: adc
pin: GPIO36
id: adc_sensor_3
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: adc_sensor_4
name: ct4Amps
id: ct4Amps
update_interval: never
sample_duration: 200ms
#filters:
#- calibrate_linear:
#- 0 -> 0
#- 0.08277 -> 7.80
- platform: adc
pin: GPIO39
id: adc_sensor_4
attenuation: 11db
internal: true
- platform: wifi_signal
name: "wifi_signal"
update_interval: 1min
- platform: uptime
name: "uptime"
id: Uptime
update_interval: 1min
#Watts per channel
- platform: template
name: CT1 Watts
id: ct1Watts
lambda: return id(ct1Amps).state * id(ic1Volts).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: never
- platform: template
name: CT2 Watts
id: ct2Watts
lambda: return id(ct2Amps).state * id(ic1Volts).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: never
- platform: template
name: CT3 Watts
id: ct3Watts
lambda: return id(ct3Amps).state * id(ic1Volts).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: never
- platform: total_daily_energy
name: CT1 Total kWh
power_id: ct1Watts
filters:
- multiply: 0.001
unit_of_measurement: kWh
icon: "mdi:flash"
- platform: total_daily_energy
name: CT2 Total kWh
power_id: ct2Watts
filters:
- multiply: 0.001
unit_of_measurement: kWh
icon: "mdi:flash"
- platform: total_daily_energy
name: CT3 Total kWh
power_id: ct3Watts
filters:
- multiply: 0.001
unit_of_measurement: kWh
icon: "mdi:flash"
- platform: template
name: Total Amps
id: totalAmps
lambda: return id(ct1Amps).state + id(ct2Amps).state + id(ct3Amps).state ;
accuracy_decimals: 2
unit_of_measurement: A
icon: "mdi:flash"
update_interval: 15s
- platform: template
name: Total Watts
id: totalWatts
lambda: return id(totalAmps).state * id(ic1Volts).state;
accuracy_decimals: 1
unit_of_measurement: W
icon: "mdi:flash"
update_interval: 15s
- platform: total_daily_energy
name: Total kWh
power_id: totalWatts
filters:
- multiply: 0.001
unit_of_measurement: kWh
icon: "mdi:flash"
- platform: homeassistant
name: "Voltage"
id: ic1Volts
entity_id: sensor.house_voltage
internal: true
- platform: template
name: Voltage
id: voltageupdate
lambda: return id(ic1Volts).state;
accuracy_decimals: 1
unit_of_measurement: V
update_interval: 60s
Thanks! I will give it try.
Thanks! I will give it try.
Did this work for you?
The following code is working in a ESP32. Note that my voltage sensor comes from homeassistant, you can use a fixed value if you want or don't have a voltage meter.
Thank you for this! This has got me close, but I'm having issues calibrating via calibrate_linear. I think I'm being dense because I'm not sure what number I should be using.
I'm assuming it should be: [22:49:43][D][sensor:094]: 'ct1Amps': Sending state 0.15046 A with 2 decimals of accuracy This is my zero load state. & [22:50:28][D][sensor:094]: 'ct1Amps': Sending state 0.33105 A with 2 decimals of accuracy this is with a 2kw resistive load.
I believe this means it should be:
(2000/245v - to give me 8.16A) - I'm having issues regardless how I calculate this with being unable to zero, and receiving either negative or huge wattage numbers.
e..g the above now becomes: -1,623W instead of zero.
[22:54:24][D][sensor:094]: 'ct1Amps': Sending state -6.62770 A with 2 decimals of accuracy [22:54:25][D][sensor:094]: 'CT1 Watts': Sending state -1623.78662 W with 0 decimals of accuracy
Any assistance appreciated. I've never done calibrate_linear before and I feel like I'm missing something. I'm using the ESP32 - 30 pin version (devkit v1) and I've tried cutting this down to just the CT1 config to make it easier to read the logs etc, and it was no help.
Any help appreciated. I feel like I'm getting nowhere with the documentation / other examples for calibration
Thanks!
also FYI: Some of the CT4 stuff is missing in your code above :)
By way of example, a common SCT013-000 will theoretically generate a 0V signal at the ESP32 gpio pin when there is a 0 amp load connected through the CT. Similarly, the same CT will generate a 1.1V signal at the ESP32 when there is a 100A load. This provides a pretty good starting point for calibrating the ct_clamp
platform using the following filter config:
- calibrate_linear:
method: exact
datapoints:
- 0 -> 0
- 1.1 -> 100
If you want to improve low current readings then you can remove any load wire through the CT and look for the corresponding "RAW AC" line in the ESPHOME log. The number here is the left side of the correlation mapping as it basically represent the voltage at the gpio pin. The right side should be set to zero as that represents the load current.. In essence, we have added a second correlation line that maps low values to zero rather than having them go negative:
[13:18:54][D][ct_clamp:041]: 'Channel 4 Current' - Raw AC Value: 0.001A after 229 different samples (1145 SPS)
- calibrate_linear:
method: exact
datapoints:
- 0 -> 0
- 0.001 -> 0
- 1.1 -> 100
If you have a multimeter that can measure resistance values then you can go a further step to improve that last correlation pair. The 1.1 -> 100
mapping assumes that the board is using a 22 ohm resistor to convert the 50mA signal that the CT produces at 100A into a 1.1V signal at the gpio pin. This is based on ohms law that voltage = current x resistance. So 0.05 x 22 is equal to 1.1V. However, the resistor is not generally exactly 22 ohms rather it is nominally within +/-1%. Mine is 22.25 ohms. So I can improve the correlation a little bit by using 1.1125 (i.e. 0.05 x 22.25) as the new left side of the last correlation mapping:
- calibrate_linear:
method: exact
datapoints:
- 0 -> 0
- 0.001 -> 0
- 1.1125 -> 100
This gets me to within a few percent of my reference meter that has higher tolerance resistors, internal voltage reference, 12bit ADC, high frequency sampling/smoothing etc. Not bad for a relatively inexpensive board.
I struggle with using things like a 60W bulb or 2kW space heaters to create correlation maps as there are too many wide tolerances involved that can easily make derived calibrations spurious. For a start these loads are at the low end of the CT's range and you would really want something at the high end. But loads of 80-100A are pretty hard to create and measure in the average home. You then have the difficulty of converting watts to amps given that residential voltages can easily vary +/-10% from nominal. But mileage may vary on this.
Hopefully helpful.
substitutions:
devicename: current-sensor
friendlyname: Current Sensor
esphome:
name: ${devicename}
friendly_name: ${friendlyname}
esp32:
board: esp32dev
framework:
type: arduino
wifi:
domain: !secret wifi_domain
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: ${devicename}
password: !secret wifi_ap_password
api:
logger:
ota:
sensor:
################ Channel 4 - 100A CT 50mA ################
- platform: adc
pin: GPIO39
name: Channel 4 ADC
id: channel_4_adc
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: channel_4_adc
name: Channel 4 Current
id: channel_4_current
update_interval: 2s
sample_duration: 200ms
filters:
- calibrate_linear:
method: exact
datapoints:
- 0 -> 0
- 0.001 -> 0 # for CT with non-zero offset
- 1.1125 -> 100 # For current based CT with 22.25 Ohm burden
- sliding_window_moving_average:
window_size: 5
send_every: 5
send_first_at: 5
Hopefully helpful.
100% yes. Thank you very much for this post. It had been driving me crazy and I have (what I think) is a working test bed, and proof that these boards will do exactly what I need (I just need smaller clamps)
Slightly embarrassingly, my issues were:
I've configured vs 750w, 1250w, and 2000w settings on a resistive heater and it can get within 18w of it at 750/1250 and within 40w of 2000 - More tweaking to do but i'll end up buying some smaller clamps for lower load testing. I used a multiplier to get near these figures (quick and dirty) but it lets me at least get what I need from the test in prep for a larger purchase of clamps on black friday :)
Plan is to use the large 100a clamp around connectivity between main consumer unit & loft so I can see directionality into/out of the consumer unit at grid and loft space (solar & batteries) - i'll be able to put about 35-38a load in for that configuration so I think that will be ok?
I'll then swap the other inputs to ~20/1V clamps so I can measure lower power / 13a devices.
Honestly, Thank you so much for your help - and for completeness here's (the main sensor section) of the code used. I've pulled in my voltage from HomeAssistant into ESPHome - as the solar battery system has a CT on the mains incoming, I know the incoming voltage pretty accurately. (in the code below, I've used a GROUP (sensor.voltage_group) in Home Assistant, so that if in future I want/need to change the device providing voltage, or I want to average multiple, I can just add the entities in).
It's a shame there's no fleshed out examples on Github / Mottramlabs site - I feel like these boards feel very good quality and they'd sell super well if it was a bit more noob-friendly. (or someone wrote a great guide that didn't assume you knew esphome calibration etc. prior)
Thanks Again! Matt
time:
- platform: homeassistant
id: homeassistant_time
switch:
- platform: restart
name: "Restart"
sensor:
################ Pull Voltage in from Home Assistant ################
- platform: homeassistant
name: "Voltage"
id: current_voltage_from_ha
entity_id: sensor.voltage_group
internal: true
- platform: template
name: Voltage
id: voltageupdate
lambda: return id(current_voltage_from_ha).state;
accuracy_decimals: 1
unit_of_measurement: V
update_interval: 1s
################ Channel 1 - 100A CT 50mA ################
- platform: adc
pin: GPIO34
name: Channel 1 ADC
id: channel_1_adc
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: channel_1_adc
name: Channel 1 Current
id: channel_1_current
update_interval: 2s
sample_duration: 200ms
filters:
- multiply: 4.0
- calibrate_linear:
method: exact
datapoints:
#- 0 -> 0
- 0.003 -> 0 # for CT with non-zero offset
- 0.089 -> 2 # For current based CT with 22.25 Ohm burden
- sliding_window_moving_average:
window_size: 3
send_every: 3
send_first_at: 3
##Watts
- platform: template
name: CT1 Watts
id: channel_1_watts
lambda: return id(channel_1_current).state * id(current_voltage_from_ha).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: 2s
################ Channel 2 - 100A CT 50mA ################
- platform: adc
pin: GPIO35
name: Channel 2 ADC
id: channel_2_adc
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: channel_2_adc
name: Channel 2 Current
id: channel_2_current
update_interval: 2s
sample_duration: 200ms
filters:
#- calibrate_linear:
#method: exact
#datapoints:
#- 0 -> 0
#- 0.002 -> 0 # for CT with non-zero offset
#- 1.1115 -> 100 # For current based CT with 22.25 Ohm burden
- sliding_window_moving_average:
window_size: 5
send_every: 5
send_first_at: 5
##Watts
- platform: template
name: CT2 Watts
id: channel_2_watts
lambda: return id(channel_2_current).state * id(current_voltage_from_ha).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: never
################ Channel 3 - 100A CT 50mA ################
- platform: adc
pin: GPIO36
name: Channel 3 ADC
id: channel_3_adc
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: channel_3_adc
name: Channel 3 Current
id: channel_3_current
update_interval: 2s
sample_duration: 200ms
filters:
#- calibrate_linear:
#method: exact
#datapoints:
#- 0 -> 0
#- 0.002 -> 0 # for CT with non-zero offset
#- 3.3325 -> 300 # For current based CT with 22.25 Ohm burden
- sliding_window_moving_average:
window_size: 5
send_every: 5
send_first_at: 5
##Watts
- platform: template
name: CT3 Watts
id: channel_3_watts
lambda: return id(channel_3_current).state * id(current_voltage_from_ha).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: never
################ Channel 4 - 100A CT 50mA ################
- platform: adc
pin: GPIO39
name: Channel 4 ADC
id: channel_4_adc
attenuation: 11db
internal: true
- platform: ct_clamp
sensor: channel_4_adc
name: Channel 4 Current
id: channel_4_current
update_interval: 2s
sample_duration: 200ms
filters:
#- calibrate_linear:
#method: exact
#datapoints:
#- 0 -> 0
#- 0.002 -> 0 # for CT with non-zero offset
#- 1.1125 -> 100 # For current based CT with 22.25 Ohm burden
- sliding_window_moving_average:
window_size: 5
send_every: 5
send_first_at: 5
##Watts
- platform: template
name: CT4 Watts
id: channel_4_watts
lambda: return id(channel_4_current).state * id(current_voltage_from_ha).state;
accuracy_decimals: 0
unit_of_measurement: W
icon: "mdi:flash-circle"
update_interval: never
############# Total Daily Energy - kWh
- platform: total_daily_energy
name: CT1 Total kWh
power_id: channel_1_watts
filters:
- multiply: 0.001
unit_of_measurement: kWh
icon: "mdi:flash"
############# Total Amps - A
- platform: template
name: Total Amps
id: totalAmps
lambda: return id(channel_1_current).state + id(channel_2_current).state + id(channel_3_current).state + id(channel_4_current).state ;
accuracy_decimals: 2
unit_of_measurement: A
icon: "mdi:flash"
update_interval: 15s
############# Total Watts - W
- platform: template
name: Total Watts
id: totalWatts
lambda: return id(totalAmps).state * id(current_voltage_from_ha).state;
accuracy_decimals: 1
unit_of_measurement: W
icon: "mdi:flash"
update_interval: 15s
- platform: total_daily_energy
name: Total kWh
power_id: totalWatts
filters:
- multiply: 0.001
unit_of_measurement: kWh
icon: "mdi:flash"
Matt, glad the comments were useful. I picked up a few things in your response that I thought interesting and raised a couple of questions/follow-ups. Hope that is OK.
You mention that the CT direction labelling is wrong, can I ask how you determined that ? This board alone cannot measure flow direction as you need simultaneous access to voltage phase to achieve that. If indeed the label is wrong then you probably have counterfeit CTs which is unfortunately common through some online marketplaces. If so then I would replace the CTs through a specialist supplier.
I see you are using a standard two element heater to create your calibration maps. Can I ask if you have a reference meter to reliably determine the actual wattage of the unit before trying to calibrate the CT ? I also have a 750/1250/2000W heater but the numbers on the box are very different to the actual wattage of the device which is 870/1,340/2,210. If I used the box numbers to calibrate the CT then I would be building in a ~10% error. I would be better off sticking with the theoretical calibration numbers or alternatively buying a relatively cheap but somewhat accurate "kill-a-watt" style device.
One thing to be aware of, If you are going to use lower value CTs then be very careful to only use them on circuits that are guaranteed to have a maximum current load under the nominal value of the CT. For example, passing a 10A load through a 5A CT will likely fry the ESP32 gpio pin. I would suggest always using a higher value CT than the fuse of the circuit being measured for safety.
For completeness, if using a voltage based CT (rather than the 50mA type above). Then the starting point calibration is even easier. The CT will generate 0V when the load is 0A and 1V when the load is at it's nominal value. So for a 30A CT the config would be:
- calibrate_linear:
method: exact
datapoints:
- 0 -> 0
- 1 -> 30
Hope that is OK.
Of course :D feel free, to ask anything ^
If indeed the label is wrong then you probably have counterfeit CTs which is unfortunately common through some online marketplaces.
They were indeed, Cheap & cheerful imports from eBay. I'd have to re-test to see if it makes a difference, but in early testing (albeit with issues) it seemed to. I'll see where I can get them for a decent price that might be a little less grey market. These CTs feel pretty shoddy in comparison to other clamps I've used for other things (e.g. EV charger, Solar battery charger). The "YHDC" print on the side I don't think is overly clear making me think they're potentially none genuine. (unless that's "normal")
Can I ask if you have a reference meter to reliably determine the actual wattage of the unit before trying to calibrate the CT ? Yes - I've got a 3pin "kill-a-watt" style meter which I trust as accurate (or at least accurate enough) - my heater comes in pretty much smack on the money of the 750/1250/2000. I've also tested this against a Shelly 1PM which is "there abouts" on my heater & plug in K-A-W. Although not perfect it's probably "good enough" for my needs.
I'll do some better calibration and setup now I know that "this works" as a solution, this has all been a bit rough and ready as a POC really.
I would suggest always using a higher value CT than the fuse of the circuit being measured for safety.
Yep :) So the thought being 20A/1v CTs will give me enough to cover 13A/220V appliances with plenty of headroom to protect the esp. I'll only use the larger 100A clamps for the cabling between my solar & battery setup, and the main consumer unit (I intend to add extra battery storage meaning it'll be able to push/pull about 48A to/from the main consumer unit under full load - IF I can generate that much usage :) ). Although it's less than "perfect" I'll probably stick to 20A CTs for all "plug in" device monitoring - as anything smaller will likely be fused in the plug at 13A anyway.
Thank you for the example for the voltage CT, this entire thread has been extremely helpful. I found the calibration process infuriating as everyone mentions how to do it - but no one has a "did you check this?" type troubleshooting steps when it doesn't do what you expect.
Sorry everyone for the bump in this. I picked up the 4 Port ESP32-s2 version of the boards... Wemos ESP32-S2 Mini After fighting for a couple days (dead esp32-s2 boards) and ending up needing to bypass the use of esphome web and manually flashing it I at least have a single esp32-s2 board seen by home assistant. I realised that once i plugged it into the 4-port board, it would no longer find the device. Searching around I have found here and realised I would need a bit more code to get this going. I used the example above and slightly changing the top-most section I seem to get an error in relation to the ADC pins. I have tried searching online for the ADC pins and it seems that the supplied GPIO pins above seem to be correct.
` sensor.adc: [source /config/esphome/wemoss2-4-port.yaml:80] platform: adc
ESP32S2 doesn't support ADC on this pin. pin: GPIO34 id: adc_sensor_1 attenuation: 11db internal: True sensor.adc: [source /config/esphome/wemoss2-4-port.yaml:97] platform: adc
ESP32S2 doesn't support ADC on this pin. pin: GPIO35 id: adc_sensor_2 attenuation: 11db internal: True sensor.adc: [source /config/esphome/wemoss2-4-port.yaml:114] platform: adc
ESP32S2 doesn't support ADC on this pin. pin: GPIO36 id: adc_sensor_3 attenuation: 11db internal: True sensor.adc: [source /config/esphome/wemoss2-4-port.yaml:131] platform: adc
ESP32S2 doesn't support ADC on this pin. pin: GPIO39 id: adc_sensor_4 attenuation: 11db internal: True '
Any tips for this eager user to get this to work (I have YHDC sct-13 clamps 100A/50MA to use on the house to replace a pain in the ass efergy monitor
I have managed to work out a few things on getting this going... i Have linked this issue in my own separate notes issue to help others with the style that I have.
I just purchased one of these from your ebay store. Do you have the code setup for ESP Home that you could provide? I am planning on using this with home assistant.