This is a work in progress so I will be updating this post as I make progress with the build and deployment of the CircuitSetup 6ch Energy Monitor running ESPHome.
I live in the USA therefore my home has the typical residential 200A 240V split-phase service. The 120V loads in the home are balanced between the two phases, and 240V appliances draw from both phases.
I am installing the energy monitor at my exterior panel where the 200A breaker is, and where the breakers for my 240V appliances (oven, dryer, 2 x HVAC) are.
The electrical panel is not fully weatherproof (it may be fine for breakers, but I wouldn’t trust it for electronics) and I have my doubts on what one is allowed to put inside the panel, especially when it is not fully insulated (the energy monitor is a bare board), so I opted to install it externally inside a wall mounted weatherproof enclosure.
Old picture of the open panel…
Below is a picture of where it will be mounted. It is below the main panel so as not to obstruct the door which has to be able to open full 90 degrees without obstruction. Also, the conduit with the CT clamp wires will dome out of the bottom and enter the enclosure on the lower side. As long as I seal things up properly this should help keep things dry inside the enclosure.
The board on the left holds to PCB mounted transformers that I am using to provide a voltage reference for L2, and power the energy monitor from L1… plus a voltage reference for L1. I added fuses for safety given the smallest breakers I can find are 15A. The small transformer outputs 9VAC nominal so the actual voltage is around 15VAC (bit high but fine). The other transformer has 2 6V AC secondaries connected in series that end up outputting about 16VAC.
The next step was to calibrate the voltage measurements for L1 and L2. I have a benchtop 5 1/2 digit DMM that I was using to measure line voltage and since both transformers are powered by the same outlet, I calibrated both to show the same values. Below is a graph from Grafana that makes it so much easier to see if the two measurements are “close enough”. Contrary to what some may think, the voltage changes a lot and quickly depending on the load. The transformer powering our house also powers the house next door so if their HVAC turns on, it will likely result in a small fluctuation. I turned mine off for a bit to try to stabilize it but the DMM (set to slow!) was still changing rapidly. Also, I believe there is some time delay between measurements on both chips therefore I don’t think you can get the same exact reading on both at the same time. By using the Grafana chart I settled on ensuring that the two readings more or less followed each other. The chart is set to last 5 minutes, so if you look at the right side you can see where I figured I could stop as it was “good enough”.
I did not research all the technical specifications related to accuracy, repeatability of measurements, calibration, and temperature related drift, but I am pretty sure that the overall system accuracy is relatively low (in other words not measurement grade, but perfectly fine for energy monitoring). That is one reason I limited the decimals shown as showing more is likely just showing you noise. Plus… seeing 120.237V is more confusing that of any use… 120.2V is more likely to be correct and adds useful information (ie a load causing a small voltage sag). During the calibration I set the ESPHome code to update every 1s, but there was still some noticeable delay between the 2 voltage measurements (the alternative is that there is something causing larger than I would expect fluctuations).
The calibration I am attempting is sometimes called “system calibration”. The goal is not to calibrate just one part of the system, for example the board, but everything from the sensor to the board. I have labeled the CTs so that once installed, they will be connected back to the same inputs I used during calibration. For amperage calibration I am using a 60W incandescent lightbulb connected to the same power outlet as the energy meter using a modified extension cord. The modification is just wrapping the hot wire in a loop with 4 windings so that the current measured is 4 times higher which will make calibration easier. I have 2 CTs and a good quality Clamp Amperemeter that I am calibrating to. I should check my Clamp Amperemeter comparing it to current measurements of my DMM (which is definitely more accurate) but I have not done that yet. I can’t use my DMM in place of the clamp amperemeter because my DMM would only measure the actual current, not 4 x. My clamp Amperemeter reads 1.756A so I calibrated both CTs to show 1.75A (I limited the decimals to 2). I will repeat this for the 2 other pairs of inputs and then I will be ready to install the energy meter. This is why I got rid of the single shared calibration constants.
I also ordered the addon 6ch board so I can measure my 2 HVAC systems (4 CTs needed) but it is on backorder. Hopefully I can calibrate it without having to tear everything out if already installed…
Below is the ESPHome code I am currently using. Please keep in mind I am still experimenting and far from finished. One thing I did was subtract oven, and dryer power from the whole house power to get the HVAC power but… I forgot the 80A feed that powers the rest of the house. So what I am getting for HVAC is not that, but everything except Oven and Dryer. I am leaving it in the code for experimentation purposes for now. The code below is mostly by CircuitSetup… I am just adjusting it to my needs.
# Example config for when jp8-jp11 are all bridged - this connects all the voltage channels and allows for power to be calculated by the meter directly.
# Boards >= v1.4 jp8-jp11 are removed and have all voltage channels connected
substitutions:
devicename: home-energy-meter
devicename_no_dashes: home_energy_meter
friendly_devicename: "Home Energy Meter"
device_description: "Home Energy Meter"
appliance1: "Dryer"
appliance2: "Oven"
appliance3: "HVAC"
# Current Transformers:
# 20A/25mA SCT-006: 11143
# 30A/1V SCT-013-030: 8650
# 50A/1V SCT-013-050: 15420
# 80A/26.6mA SCT-010: 41660
# 100A/50ma SCT-013-000: 27518
# 120A/40mA: SCT-016: 41787
# 200A/100mA SCT-024: 27518
# current_cal: '41660'
current_cal_ct1: '41660'
current_cal_ct2: '41660'
current_cal_ct3: '42350'
current_cal_ct4: '42200'
current_cal_ct5: '41660'
current_cal_ct6: '41660'
# Jameco 9VAC Transformer:
# For meter versions:
# >= v1.3: 7305
# voltage_cal: '5355'
v1_cal: '5025'
v2_cal: '5385'
# Interval of how often the wifi info is updated
update_interval_wifi: "10s"
# Interval of how often the power is updated
update_interval_s: "10s"
esphome:
name: ${devicename}
comment: ${device_description}
platform: ESP32
board: nodemcu-32s
wifi:
ssid: !secret iot_wifi_ssid
password: !secret iot_wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "${devicename} Hotspot"
password: !secret iot_wifi_password
#Faster than DHCP. Also use if can't reach because of name change
# manual_ip:
# static_ip: 192.168.3.196
# gateway: 192.168.3.1
# subnet: 255.255.255.0
# dns1: 192.168.1.25
# dns2: 192.168.1.26
#Manually override what address to use to connect to the ESP.
#Defaults to auto-generated value. Example, if you have changed your
#static IP and want to flash OTA to the previously configured IP address.
#use_address: 192.168.3.196
# Enable logging
logger:
# Enable Home Assistant API
api:
password: !secret api_pwd
ota:
password: !secret ota_pwd
web_server:
port: 80
spi:
clk_pin: 18
miso_pin: 19
mosi_pin: 23
text_sensor:
- platform: wifi_info
ip_address:
name: "${friendly_devicename}: IP"
icon: "mdi:ip-outline"
update_interval: ${update_interval_wifi}
ssid:
name: "${friendly_devicename}: SSID"
icon: "mdi:wifi-settings"
update_interval: ${update_interval_wifi}
bssid:
name: "${friendly_devicename}: BSSID"
icon: "mdi:wifi-settings"
update_interval: ${update_interval_wifi}
mac_address:
name: "${friendly_devicename}: MAC"
icon: "mdi:network-outline"
scan_results:
name: "${friendly_devicename}: Wifi Scan"
icon: "mdi:wifi-refresh"
disabled_by_default: true
sensor:
- platform: wifi_signal
name: "${friendly_devicename}: WiFi Signal"
update_interval: ${update_interval_wifi}
device_class: signal_strength
#IC1
- platform: atm90e32
cs_pin: 5
phase_a:
voltage:
name: "${friendly_devicename}: L1 V"
id: ic1Volts
accuracy_decimals: 1
current:
name: "${friendly_devicename}: L1 A"
id: ct1Amps
# The max value for current that the meter can output is 65.535. If you expect to measure current over 65A,
# divide the gain_ct by 2 (120A CT) or 4 (200A CT) and multiply the current and power values by 2 or 4 by uncommenting the filter below
filters:
- multiply: 4
power:
name: "${friendly_devicename}: L1 W"
id: ct1Watts
filters:
- multiply: 4
gain_voltage: ${v1_cal}
gain_ct: ${current_cal_ct1}
phase_b:
current:
name: "${friendly_devicename}: ${appliance1} L1 A" #Dryer
id: ct2Amps
power:
name: "${friendly_devicename}: ${appliance1} L1 W" #Dryer
id: ct2Watts
gain_voltage: ${v1_cal}
gain_ct: ${current_cal_ct2}
phase_c:
current:
name: "${friendly_devicename}: ${appliance2} L1 A" #Oven
id: ct3Amps
power:
name: "${friendly_devicename}: ${appliance2} L1 W" #Oven
id: ct3Watts
gain_voltage: ${v1_cal}
gain_ct: ${current_cal_ct3}
frequency:
name: "${friendly_devicename}: L1 Hz"
device_class: frequency
chip_temperature:
name: "${friendly_devicename}: L1 Chip Temperature"
id: l1_chip_temperature
device_class: temperature
line_frequency: 60Hz
gain_pga: 1X
update_interval: ${update_interval_s}
#IC2
- platform: atm90e32
cs_pin: 4
phase_a:
current:
name: "${friendly_devicename}: ${appliance2} L2 A" #Oven
id: ct4Amps
power:
name: "${friendly_devicename}: ${appliance2} L2 W" #Oven
id: ct4Watts
gain_voltage: ${v2_cal}
gain_ct: ${current_cal_ct4}
phase_b:
current:
name: "${friendly_devicename}: ${appliance1} L2 A" #Dryer
id: ct5Amps
power:
name: "${friendly_devicename}: ${appliance1} L2 W" #Dryer
id: ct5Watts
gain_voltage: ${v2_cal}
gain_ct: ${current_cal_ct5}
phase_c:
#this voltage is only needed if monitoring 2 voltages
voltage:
name: "${friendly_devicename}: L2 V"
id: ic2Volts
accuracy_decimals: 1
current:
name: "${friendly_devicename}: L2 A"
id: ct6Amps
# The max value for current that the meter can output is 65.535. If you expect to measure current over 65A,
# divide the gain_ct by 2 (120A CT) or 4 (200A CT) and multiply the current and power values by 2 or 4 by uncommenting the filter below
filters:
- multiply: 4
power:
name: "${friendly_devicename}: L2 W"
id: ct6Watts
gain_voltage: ${v2_cal}
gain_ct: ${current_cal_ct6}
frequency:
name: "${friendly_devicename}: L2 Hz"
device_class: frequency
chip_temperature:
name: "${friendly_devicename}: L2 Chip Temperature"
id: l2_chip_temperature
device_class: temperature
line_frequency: 60Hz
gain_pga: 1X
update_interval: ${update_interval_s}
#Total Amps
# - platform: template
# name: "${friendly_devicename}: Total Amps"
# id: totalAmps
# lambda: return id(ct1Amps).state + id(ct2Amps).state + id(ct3Amps).state + id(ct4Amps).state + id(ct5Amps).state + id(ct6Amps).state ;
# accuracy_decimals: 2
# unit_of_measurement: A
# device_class: current
# update_interval: ${update_interval_s}
#Total Watts
# - platform: template
# name: "${friendly_devicename}: Total Watts"
# id: totalWatts
# lambda: return id(ct1Watts).state + id(ct2Watts).state + id(ct3Watts).state + id(ct4Watts).state + id(ct5Watts).state + id(ct6Watts).state ;
# accuracy_decimals: 1
# unit_of_measurement: W
# device_class: energy
# update_interval: ${update_interval_s}
#kWh
# - platform: total_daily_energy
# name: "${friendly_devicename}: Total kWh"
# power_id: totalWatts
# filters:
# - multiply: 0.001
# unit_of_measurement: kWh
# device_class: energy
# state_class: total_increasing
#Appliance3 Watts (HVAC)
- platform: template
name: "${friendly_devicename}: ${appliance3} W"
id: appliance3TotalWatts
lambda: return id(ct1Watts).state + id(ct6Watts).state - id(ct2Watts).state - id(ct3Watts).state - id(ct4Watts).state - id(ct5Watts).state ;
accuracy_decimals: 1
unit_of_measurement: "W"
device_class: energy
update_interval: ${update_interval_s}
#Appliance3 Amps (HVAC)
- platform: template
name: "${friendly_devicename}: ${appliance3} A"
id: appliance3TotalAmps
lambda: return id(ct1Amps).state + id(ct6Amps).state - id(ct2Amps).state - id(ct3Amps).state - id(ct4Amps).state - id(ct5Amps).state ;
accuracy_decimals: 2
unit_of_measurement: "A"
device_class: current
update_interval: ${update_interval_s}
#Appliance1 Watts (Dryer)
- platform: template
name: "${friendly_devicename}: ${appliance1} W"
id: appliance1TotalWatts
lambda: return id(ct2Watts).state + id(ct5Watts).state ;
accuracy_decimals: 1
unit_of_measurement: "W"
device_class: energy
update_interval: ${update_interval_s}
#Appliance1 Amps (Dryer)
- platform: template
name: "${friendly_devicename}: ${appliance1} A"
id: appliance1TotalAmps
lambda: return id(ct2Amps).state + id(ct5Amps).state ;
accuracy_decimals: 2
unit_of_measurement: "A"
device_class: current
update_interval: ${update_interval_s}
#Appliance2 Total Watts (Oven)
- platform: template
name: "${friendly_devicename}: ${appliance2} W"
id: appliance2TotalWatts
lambda: return id(ct3Watts).state + id(ct4Watts).state ;
accuracy_decimals: 1
unit_of_measurement: "W"
device_class: energy
update_interval: ${update_interval_s}
#Appliance2 Total Amps (Oven)
- platform: template
name: "${friendly_devicename}: ${appliance2} A"
id: appliance2TotalAmps
lambda: return id(ct3Amps).state + id(ct4Amps).state ;
accuracy_decimals: 2
unit_of_measurement: "A"
device_class: current
update_interval: ${update_interval_s}
#Watts - L1 & L2
- platform: template
name: "${friendly_devicename}: L1 & L2 W"
id: l1l2Watts
lambda: return id(ct1Watts).state + id(ct6Watts).state ;
accuracy_decimals: 1
unit_of_measurement: "W"
device_class: energy
update_interval: ${update_interval_s}
#Amps - L1 & L2
- platform: template
name: "${friendly_devicename}: L1 & L2 A"
id: l1l2Amps
lambda: return id(ct1Amps).state + id(ct6Amps).state ;
accuracy_decimals: 2
unit_of_measurement: "A"
device_class: current
update_interval: ${update_interval_s}
#kWh - L1
- platform: total_daily_energy
name: "${friendly_devicename}: L1 kWh"
power_id: ct1Watts
filters:
- multiply: 0.001
accuracy_decimals: 1
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
#kWh - L2
- platform: total_daily_energy
name: "${friendly_devicename}: L2 kWh"
power_id: ct6Watts
filters:
- multiply: 0.001
accuracy_decimals: 1
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
#kWh - Whole House (L1 + L2)
- platform: total_daily_energy
name: "${friendly_devicename}: L1 & L2 kWh"
power_id: l1l2Watts
filters:
- multiply: 0.001
accuracy_decimals: 1
unit_of_measurement: "kWh"
device_class: energy
state_class: total_increasing
switch:
- platform: restart
name: "${friendly_devicename}: Restart"
button:
- platform: safe_mode
name: "${friendly_devicename}: Restart (Safe Mode)"
time:
- platform: sntp
id: sntp_time
To be continued…