Hello everyone
I installed the scr_485 to support my hot water system, which is normally powered by gas.
Since I have a 3-phase power connection in my house, I use a WEM3080T to monitor the consumption values precisely.
With my 2.8kw solar system, however, I fed a lot of power into the grid and got very little in return. The scr_485 is now the ideal solution for me to convert the power into hot water.
At the moment I am still testing the settings of the scr_485 and I am using the 4th array of the WEM3080T in NEM mode to record the total grid power, phase A+B+C.
Here is a video of my Homeassistant dashboard.
Jack from support@devicebit.com noticed that the power values displayed are not correct.
Does anyone have any ideas?
I had not made any further changes in the auto-control logic.
https://youtu.be/3Nozqqa3w0A
This is how I built the Control Box
My YAML FILE is like this:
#################################################
##scr-485.yaml##
#################################################
## Add in your secrets.yaml
# wifi_ssid: 'XXXX'
# wifi_password: 'XXXXXXXXXXX'
#################################################
substitutions:
version: "1.1.6.2"
esphome:
name: scr-485
friendly_name: scr_485
platformio_options:
# platform: https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
board_build.flash_mode: dio
## If you want to use auto mode please enable auto_mode_switch below, if you have battery you can enable with_battery
# on_boot:
# then:
# - switch.turn_on: auto_mode_switch
# - switch.turn_on: with_battery
esp32:
board: esp32-c3-devkitm-1
framework:
type: arduino
## Enable logging
# logger:
# level: DEBUG
## Enable Home Assistant API
api:
# encryption:
# key: "MYAPIKEY"
reboot_timeout: 0s
ota:
# password: "MYOTAPASSWORD"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
## Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "scr-485"
# password: "123456"
web_server:
port: 80
# local: true
version: 2
time:
- platform: sntp
id: sntp_time
timezone: Europe/Berlin
servers:
- 0.pool.ntp.org
- 1.pool.ntp.org
- 2.pool.ntp.org
captive_portal:
globals:
- id: auto_mode
type: bool
restore_value: true
initial_value: 'false'
- id: global_with_battery
type: bool
restore_value: true
initial_value: 'false'
- id: interval_flag
type: bool
restore_value: true
initial_value: 'false'
- id: non_pvp_flag
type: bool
restore_value: true
initial_value: 'false'
http_request:
useragent: esphome/device
# timeout: 10s
id: http_request_data
status_led:
pin:
number: GPIO08
inverted: true
# light:
# - platform: status_led
# name: "Status Led"
# pin:
# number: GPIO08
# inverted: true
uart:
id: mod_bus
tx_pin: 0
rx_pin: 1
baud_rate: 9600
stop_bits: 1
modbus:
# flow_control_pin: 8
id: modbus1
modbus_controller:
- id: modbus_device
address: 0x01 ## address of the Modbus slave device on the bus
modbus_id: modbus1
setup_priority: -10
update_interval: 3s
sensor:
# - platform: modbus_controller
# modbus_controller_id: modbus_device
# name: "scr_485_adc_read"
# id: im1266_voltage
# register_type: holding
# address: 0x0001
# # unit_of_measurement: "ADC"
# value_type: U_WORD
# # accuracy_decimals: 4
# # filters:
# # - multiply: 0.0001
- platform: template
name: "Grid Power"
id: grid_power
unit_of_measurement: "W"
update_interval: 3s
- platform: template
name: "Battery Power"
id: battery_power
unit_of_measurement: "W"
update_interval: 3s
text_sensor:
- platform: template
name: "X Current Time"
lambda: |-
//uint32_t dur = id(uptime_s).state;
auto time = id(sntp_time).now();
char buffer[17];
sprintf(buffer, "%02u:%02u", time.hour, time.minute);
return {buffer};
icon: mdi:clock-start
update_interval: 5s
- platform: template
name: "Version"
id: device_version
lambda: |-
return {"${version}"};
number:
- platform: modbus_controller
modbus_controller_id: modbus_device
id: scr_485_set_adc
name: "scr_485_set_adc"
address: 0x0002
value_type: U_WORD
# multiply: 1.0
min_value: 0
max_value: 3500
- platform: template
id: restore_power
name: "6 Restore Power"
optimistic: true
min_value: 0
max_value: 3500
step: 1
initial_value: 0
restore_value: True
unit_of_measurement: "W"
mode: box
- platform: template
id: max_power
name: "6 Max Power"
optimistic: true
min_value: 1
max_value: 3500
step: 1
initial_value: 1000
restore_value: True
unit_of_measurement: "W"
mode: box
- platform: template
id: threshold
name: "5 Threshold"
optimistic: true
min_value: -200
max_value: 200
step: 0
initial_value: -20
restore_value: True
unit_of_measurement: "W"
mode: box
- platform: template
id: hysteresis
name: "5 Hysteresis"
optimistic: true
min_value: 0
max_value: 200
step: 0
initial_value: 50
restore_value: True
unit_of_measurement: "W"
mode: box
- platform: template
id: set_power
name: "Set Power"
unit_of_measurement: "W"
optimistic: true
min_value: 0
max_value: 4096
step: 1
# initial_value: 1000
restore_value: True
mode: box
on_value:
then:
- number.set:
id: scr_485_set_adc
value: !lambda |-
uint16_t input_val = id(set_power).state;
uint16_t output_val = 0;
uint16_t max_power_val = id(max_power).state;
if (input_val <= 0){
output_val = 0;
}else if (input_val <= max_power_val) {
output_val = 1450 + (input_val - 0) * (4096 - 1450) / (max_power_val - 0);
}else{
output_val = 4096;
}
return output_val;
- platform: template
id: auto_start_time
name: "1 Start Time"
optimistic: true
min_value: 0
max_value: 24
step: 1
initial_value: 9
restore_value: True
mode: box
- platform: template
id: auto_end_time
name: "2 End Time"
optimistic: true
min_value: 0
max_value: 24
step: 1
initial_value: 16
restore_value: True
mode: box
- platform: template
id: interval_seconds
name: "4 Interval Seconds"
unit_of_measurement: "s"
optimistic: true
min_value: 1
max_value: 30
step: 1
initial_value: 3
restore_value: True
mode: box
interval:
- interval: 1s
then:
lambda: |-
static int counter = 0;
counter++;
if (counter >= id(interval_seconds).state) {
counter = 0;
id(interval_flag) = true;
ESP_LOGI("main", "Interval triggered");
}
- interval: 1s
then:
## During non-PV power generation period, reset the set power to the maximum power
- if:
condition:
# lambda: 'return id(auto_mode);'
and:
- lambda: 'return id(interval_flag);'
- lambda: 'return id(auto_mode);'
- lambda: 'return !id(non_pvp_flag);'
- lambda: 'auto time = id(sntp_time).now(); return (time.hour == id(auto_end_time).state) && (time.minute == 0);'
then:
- lambda: |-
id(non_pvp_flag) = true;
id(set_power).publish_state(id(restore_power).state);
ESP_LOGI("main", "non_pvp_flag");
- if:
condition:
# lambda: 'return id(auto_mode);'
and:
- lambda: 'return id(interval_flag);'
- lambda: 'return id(auto_mode);'
- lambda: 'return id(non_pvp_flag);'
- lambda: 'auto time = id(sntp_time).now(); return time.hour >= id(auto_start_time).state && time.hour < id(auto_end_time).state;'
then:
- lambda: |-
id(non_pvp_flag) = false;
ESP_LOGI("main", "pvp_flag");
## Main regulation logic
- if:
condition:
# lambda: 'return id(auto_mode);'
and:
- lambda: 'return id(interval_flag);'
- lambda: 'return id(auto_mode);'
then:
- lambda: |-
id(interval_flag) = false;
ESP_LOGI("main", "interval_flag");
## with Battery
- if:
condition:
lambda: 'return id(global_with_battery);'
then:
- http_request.get: ## IP of the meter that measures the battery power.
url: "http://192.168.178.XXX/monitorjson"
headers:
Content-Type: "application/json"
###### Put your Basic MYAUTHORISATION
Authorization: 'Basic MYAUTHORISATION'
verify_ssl: false
on_response:
then:
- logger.log:
format: "Response status: %d, Duration: %u ms"
args:
- status_code
- duration_ms
- lambda: |-
json::parse_json(id(http_request_data).get_string(), [](JsonObject root) {
// WEM3080
if (root.containsKey("Data") && root["Data"].is<JsonArray>() && root["Data"].size() > 2) {
int32_t tmp_battery_power = root["Data"][2];
//tmp_battery_power = -tmp_battery_power; //for test
id(battery_power).publish_state(tmp_battery_power);
}
});
# Get Grid Power
- http_request.get: ## IP of the meter that measures the grid power.
url: "http://192.168.178.XXX/monitorjson"
headers:
Content-Type: "application/json"
###### Put your Basic MYAUTHORISATION
Authorization: 'Basic MYAUTHORISATION'
verify_ssl: false
on_response:
then:
- logger.log:
format: "Response status: %d, Duration: %u ms"
args:
- status_code
- duration_ms
- lambda: |-
json::parse_json(id(http_request_data).get_string(), [](JsonObject root) {
// WEM3080
if (root.containsKey("Data") && root["Data"].is<JsonArray>() && root["Data"].size() > 2) {
int32_t tmp_grid_power = root["Data"][2];
id(grid_power).publish_state(tmp_grid_power);
}
// WEM3080T/WEM3050T/WEM3046T
if (root.containsKey("Datas") && root["Datas"].is<JsonArray>() && root["Datas"].size() > 2) {
// If your solar PV uses phase A, it is ["Datas"][0][2], if it is phase B, it is ["Datas"][1][2], if it is phase C, it is ["Datas"] [2][2], if it is phase A+B+C, it is ["Datas"] [3][2] if WEM3080T is used in NET Metering Mode
int32_t tmp_grid_power = root["Datas"][3][2];
id(grid_power).publish_state(tmp_grid_power);
}
});
# Calculating
- if:
condition:
lambda: 'auto time = id(sntp_time).now(); return time.hour >= id(auto_start_time).state && time.hour < id(auto_end_time).state;'
then:
- lambda: |-
int32_t tmp_grid_power = id(grid_power).state;
int32_t tmp_threshold = id(threshold).state;
int32_t tmp_hysteresis = id(hysteresis).state;
int32_t tmp_set_power = id(set_power).state;
if ((id(global_with_battery))&&id(battery_power).state<0) {
tmp_grid_power = tmp_grid_power - id(battery_power).state;
}
if (tmp_grid_power < tmp_threshold - tmp_hysteresis) {
tmp_set_power += abs(tmp_grid_power - tmp_threshold);
if (tmp_set_power < 0) {
tmp_set_power = 0;
}
if (tmp_set_power > id(max_power).state) {
tmp_set_power = id(max_power).state;
}
if (id(auto_mode)) {
id(set_power).publish_state(tmp_set_power);
}
}
if (tmp_grid_power > tmp_threshold + tmp_hysteresis) {
tmp_set_power -= abs(tmp_grid_power - tmp_threshold);
if (tmp_set_power < 0) {
tmp_set_power = 0;
}
if (tmp_set_power > id(max_power).state) {
tmp_set_power = id(max_power).state;
}
if (id(auto_mode)) {
id(set_power).publish_state(tmp_set_power);
}
}
switch:
- platform: template
name: "3 Auto Mode Switch"
id: auto_mode_switch
restore_mode: DISABLED
turn_on_action:
- globals.set:
id: auto_mode
value: 'true'
turn_off_action:
- globals.set:
id: auto_mode
value: 'false'
# - number.set:
# id: set_power
# value: '0.0'
lambda: |-
return id(auto_mode);
- platform: template
name: "3 With Battery"
id: with_battery
restore_mode: DISABLED
turn_on_action:
- globals.set:
id: global_with_battery
value: 'true'
turn_off_action:
- globals.set:
id: global_with_battery
value: 'false'
lambda: |-
return id(global_with_battery);
button:
- platform: restart
name: "Restart"
Thank you for sharing the video.
I noticed that the boiler power adjusts automatically in response to the grid power (sum of phases A, B, and C). However, the explanation could be clearer if it included a comparative power chart showing both the grid power and the boiler power, or an hourly self-consumption analysis like the graph in this https://imeter.club/topic/606
I recommend using IAMMETER-cloud for this testing temporarily. Although we understand that some prefer to store all data locally, IAMMETER-cloud offers powerful charts and reports for analyzing the self-consumption rate. You can use IAMMETER-cloud for testing the SCR-485 and disable uploading once the testing is complete.
Hello everyone.
After a few problems with the water installation, I was finally able to install the heating element properly and I am more than satisfied with the results of the daily self-consumption of my surplus energy.
Here is a normal day before I was able to use the heating element with the scr_485 and a normal day now with the heating element and the scr_485 as a controller.
As you can see, I hardly export anything to the power grid anymore and can therefore at least have warm water available all day long, which I previously made with gas and of course still do when the sun isn't shining. And the great thing is, I'm sure I'll soon get my investment back with the gas I've saved.
So at this point, a big thank you to I-Meter for sending me the scr_485 and the opportunity to test the device.
Now I can continue to expand my solar system with peace of mind and will hardly give away any more solar power.
A normal day without scr_485
Personal use now with the scr_485
Here is a current circuit diagram of my self-built control system
so cool for your sharing.
So glad to see that nearly all of your solar yield energy is consumed now.😀