The idea:
I recently bought a small photovoltaics system, which can output up to 800W. This does not sound like much (and it isn’t) but still, 50% of the yield gets fed into the grid and is wasted, because I don’t even get any kind of compensation for it.
The biggest single consumer in the house is a hot water boiler with 2500W - the problem now is, when it turns on, it turns on completely and draws 2500W for a couple of minutes (or longer - depending on the temperature of the water). This is obviously not ideal, because that way I use 100% of the produced solar-power for a few minutes but waste 75% of it the rest of the time (disclaimer: the values are representative, not measured)
So the optimal solution would be, to only deliver excess power to the boiler.
The how:
There are off the shelf solutions for this problem. The drawback of them is, firstly they are expensive - you don’t only need a special heat-rod (which alone is ~750€), but also the appropriate controllers/electronics. And secondly, I have a regular wall mounted 50l boiler which doesn’t even have an opening to add another heater.
So DIY it is: From what I’ve figured out, these “special” heat rods are simple thyristor-dimmers. And where are thyristor-dimmers usually used?! That’s right: stage lighting. And what are heating rods? that’s right: lightbulbs, that don’t emmit light (ohmic consumers). And what does most stage equipment have? that’s right: the ability to be controlled via DMX
The Components:
First of all you need a boiler - a dumb-boiler would be ideal. One that does not have any electronics, but a simple bi-metal switch. Then I got one of these Dimmers. Also you need an ESP32 board, a MAX485 RS-485 TTL to RS485 Converter and a female XLR plug. Quite cucial for optimal power distribution is a way to measure the current total power comsumption of your house. I use a Shelly 3EM for that. A bit of wiring, and that’s it for a basic setup.
You might also want a way to measure the boiler’s temperature, so you can give it more power if it is too cold - on days without sun for example, or just to have it at a decent temperature at all times a DS18B20 (+ 4.7 kΩ resistor) should do the trick
The Setup:
In my specific case I had to convert my boiler, because it’s a rather weird contraption and also had electronics and a display - I didn’t want these things, so I replaced the electronics with two shelly 1…
When altering a boiler keep in mind Mythbusters 2007 E20 (and others) - it’s definitely not not dangerous. Never disable the thermal fuses and/or overpreassure valves.
Anyways - I’m not going into that for now and assume it’s a “dumb-boiler” that is used.
The actual basic conversion is quite simple and straight forward: follow this guide to control the dimmer. It’s a lot more simple than that long thread suggests at first glance. You simply connect the MAX485 to the ESP32 like explained here - but you don’t need a resistor - it’s already on the board. Also I drive everything with 5V - it’s just more convenient to simply use a USB charger HOWEVER - don’t power the MAX485 through the ESP32 - mine wouldn’t boot like that, because it draws too much power (I guess).
Here’s my ESPHome YAML:
esphome:
name: boiler-dmx-controller
esp32:
board: esp32dev
framework:
type: arduino
external_components:
- source: github://andyboeh/esphome-dmx512
dallas:
- pin: GPIO16
update_interval: 10s
substitutions:
device_name: "Boiler DMX Controller"
wifi:
networks:
- ssid: !secret wifi_ssid
password: !secret wifi_pass
- ssid: !secret wifi_ssid_EZ
password: !secret wifi_pass
ap:
ssid: "${device_name} wifi"
password: "NdnbtPAtF6sv6F"
captive_portal:
uart:
id: uart_bus
baud_rate: 250000
tx_pin: GPIO5
stop_bits: 2
dmx512:
id: dmx
uart_id: uart_bus
# enable_pin: 33
periodic_update: true
tx_pin: GPIO5
uart_num: 1
logger:
api:
password: !secret api_ota_pass
ota:
password: !secret api_ota_pass
text_sensor:
- platform: wifi_info
ip_address:
name: ${device_name} IP
sensor:
- platform: uptime
name: ${device_name} Uptime
- platform: wifi_signal
name: ${device_name} WiFi Signal
update_interval: 60s
- platform: dallas
address: 0xe74c1ak648e76f84
name: "${device_name} Boiler Temperature"
accuracy_decimals: 2
output:
- platform: dmx512
channel: 1 #<--- make sure it's the same channel as on the dimmer
universe: dmx
id: dmx_1
# min_power: 0.40
max_power: 0.75 #<--- you might want to set this, because the dimmer can only deliver 10A. when everything is set up, use a power meter to slowly determine an appropriate value
light:
- platform: monochromatic
name: "${device_name} Dimmer"
output: dmx_1
id: dimmer_01
default_transition_length: 1s
gamma_correct: 0
Now you should be able to control the dimmer via ESPHome - test it with a light bulb (NO LED or other energy saving bulbs - it has to be a good old filament bulb) and make sure the manual slider on the dimmer is set to 0 - otherwise it overrides the DMX signal.
Now we have to find a way to tell the dimmer how much power it should give to the boiler.
I went the Node-Red way and have made a simple flow that reads the total power consumption of the house and if it is below zero (which means power get’s fed into the grid) turns up the dimmer one step. And if the total power consumption is over 40W it goes down one step. I also added different percentage steps, depending on how much excess power there is (this value can flucutate fast and drastically if you turn on/off devices in your home)
Additionally I have added a dummy-thermostat so I can set a minimum temperature for the boiler, which it tries to get to under all circumstances (that’s what the DS18B20 is for) - it just controls an input_boolean, Node red can work with
Here’s my flow:
of course you should’t disable the first node - saw my mistake after I took the screenshot
and here the code:
[{"id":"ab4cb0742f777d75","type":"trigger","z":"37d6892f50d80d96","name":"","op1":"","op2":"check","op1type":"nul","op2type":"str","duration":"5","extend":true,"overrideDelay":false,"units":"s","reset":"","bytopic":"all","topic":"topic","outputs":1,"x":230,"y":1580,"wires":[["cf9132730574bee5"]]},{"id":"cf9132730574bee5","type":"api-current-state","z":"37d6892f50d80d96","name":"Total Wattt House","server":"938bbb0d.d39038","version":3,"outputs":1,"halt_if":"","halt_if_type":"str","halt_if_compare":"is","entity_id":"sensor.shelly_3em_total_power","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":410,"y":1620,"wires":[["ab4cb0742f777d75","ae63cd85d799e401"]]},{"id":"a5963674c0db6d6f","type":"server-state-changed","z":"37d6892f50d80d96","name":"Total Watt House","server":"938bbb0d.d39038","version":4,"exposeToHomeAssistant":false,"haConfig":[{"property":"name","value":""},{"property":"icon","value":""}],"entityidfilter":"sensor.shelly_3em_total_power","entityidfiltertype":"exact","outputinitially":false,"state_type":"str","haltifstate":"","halt_if_type":"num","halt_if_compare":"is","outputs":1,"output_only_on_state_change":false,"for":"0","forType":"num","forUnits":"minutes","ignorePrevStateNull":false,"ignorePrevStateUnknown":false,"ignorePrevStateUnavailable":false,"ignoreCurrentStateUnknown":false,"ignoreCurrentStateUnavailable":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"eventData"},{"property":"topic","propertyType":"msg","value":"","valueType":"triggerId"}],"x":140,"y":1680,"wires":[["ab4cb0742f777d75","ae63cd85d799e401"]]},{"id":"ae63cd85d799e401","type":"api-current-state","z":"37d6892f50d80d96","name":"Boiler too cold?","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"on","halt_if_type":"str","halt_if_compare":"is","entity_id":"input_boolean.boiler_climate_dummy","state_type":"str","blockInputOverrides":true,"outputProperties":[],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":440,"y":1760,"wires":[["97a89b61d54cce19"],["8095276a47c81476"]]},{"id":"97a89b61d54cce19","type":"delay","z":"37d6892f50d80d96","name":"","pauseType":"rate","timeout":"5","timeoutUnits":"seconds","rate":"1","nbRateUnits":"5","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":true,"allowrate":false,"outputs":1,"x":630,"y":1700,"wires":[["49825630d88e78f4"]]},{"id":"8095276a47c81476","type":"switch","z":"37d6892f50d80d96","name":"","property":"payload","propertyType":"msg","rules":[{"t":"lte","v":"-150","vt":"num"},{"t":"lte","v":"-50","vt":"num"},{"t":"lte","v":"-1","vt":"num"},{"t":"gte","v":"750","vt":"num"},{"t":"gte","v":"500","vt":"num"},{"t":"gte","v":"200","vt":"num"},{"t":"gte","v":"150","vt":"num"},{"t":"gte","v":"40","vt":"num"},{"t":"else"}],"checkall":"false","repair":false,"outputs":9,"x":950,"y":1780,"wires":[["d3d9e7e77ed85dfd"],["412cdf5be245a302"],["2aee9936649440d9"],["772c67e66f3b68c2"],["9d650f4f4e4c7190"],["8df2920dffe00f17"],["29c563a2e7f783c8"],["60128712d20d3016"],[]]},{"id":"772c67e66f3b68c2","type":"api-current-state","z":"37d6892f50d80d96","name":"Dimmer >50%","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"50","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.boiler_dimmer_zustand","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1260,"y":1840,"wires":[["08663637889827c3"],["9d650f4f4e4c7190"]]},{"id":"9d650f4f4e4c7190","type":"api-current-state","z":"37d6892f50d80d96","name":"Dimmer >20%","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"20","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.boiler_dimmer_zustand","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1260,"y":1900,"wires":[["4e3d2a6665392e64"],["8df2920dffe00f17"]]},{"id":"8df2920dffe00f17","type":"api-current-state","z":"37d6892f50d80d96","name":"Dimmer >5%","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"5","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.boiler_dimmer_zustand","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1250,"y":1960,"wires":[["0c609a3ba2e6e6f1"],["29c563a2e7f783c8"]]},{"id":"29c563a2e7f783c8","type":"api-current-state","z":"37d6892f50d80d96","name":"Dimmer >3%","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"3","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.boiler_dimmer_zustand","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1250,"y":2020,"wires":[["1e0fc71dc4a79c71"],["60128712d20d3016"]]},{"id":"60128712d20d3016","type":"api-current-state","z":"37d6892f50d80d96","name":"Dimmer >2%","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"2","halt_if_type":"num","halt_if_compare":"gt","entity_id":"sensor.boiler_dimmer_zustand","state_type":"str","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1250,"y":2080,"wires":[["2998d908fe8b5576"],["33b4b4431d11473e"]]},{"id":"49825630d88e78f4","type":"api-call-service","z":"37d6892f50d80d96","name":"Dimmer set 100%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_pct\": \"100\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":910,"y":1620,"wires":[[]]},{"id":"d3d9e7e77ed85dfd","type":"api-call-service","z":"37d6892f50d80d96","name":"+5%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"+5\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":1600,"wires":[[]]},{"id":"412cdf5be245a302","type":"api-call-service","z":"37d6892f50d80d96","name":"+3%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"+3\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":1660,"wires":[[]]},{"id":"2aee9936649440d9","type":"api-call-service","z":"37d6892f50d80d96","name":"+1%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"+1\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":1720,"wires":[[]]},{"id":"08663637889827c3","type":"api-call-service","z":"37d6892f50d80d96","name":"-49%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"-49\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":1840,"wires":[[]]},{"id":"4e3d2a6665392e64","type":"api-call-service","z":"37d6892f50d80d96","name":"-19%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"-19\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":1900,"wires":[[]]},{"id":"0c609a3ba2e6e6f1","type":"api-call-service","z":"37d6892f50d80d96","name":"-4%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"-4\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":1960,"wires":[[]]},{"id":"1e0fc71dc4a79c71","type":"api-call-service","z":"37d6892f50d80d96","name":"-2%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"-2\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":2020,"wires":[[]]},{"id":"2998d908fe8b5576","type":"api-call-service","z":"37d6892f50d80d96","name":"-1%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_step_pct\": \"-1\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1510,"y":2080,"wires":[[]]},{"id":"33b4b4431d11473e","type":"api-current-state","z":"37d6892f50d80d96","name":"temp < 50","server":"938bbb0d.d39038","version":3,"outputs":2,"halt_if":"55","halt_if_type":"num","halt_if_compare":"lte","entity_id":"sensor.boiler_unten_durchschnittstemperatur","state_type":"num","blockInputOverrides":false,"outputProperties":[{"property":"payload","propertyType":"msg","value":"","valueType":"entityState"},{"property":"data","propertyType":"msg","value":"","valueType":"entity"}],"for":"0","forType":"num","forUnits":"minutes","override_topic":false,"state_location":"payload","override_payload":"msg","entity_location":"data","override_data":"msg","x":1380,"y":2140,"wires":[["629e6cc208f4d569"],["2998d908fe8b5576"]]},{"id":"629e6cc208f4d569","type":"api-call-service","z":"37d6892f50d80d96","name":"set 2%","server":"938bbb0d.d39038","version":5,"debugenabled":false,"domain":"light","service":"turn_on","areaId":[],"deviceId":[],"entityId":["light.boiler_steuerung_dimmer"],"data":"{\"brightness_pct\": \"2\"}","dataType":"json","mergeContext":"","mustacheAltTags":false,"outputProperties":[],"queue":"none","x":1550,"y":2140,"wires":[[]]},{"id":"938bbb0d.d39038","type":"server","name":"Home Assistant","version":4,"addon":true,"rejectUnauthorizedCerts":true,"ha_boolean":"y|yes|true|on|home|open","connectionDelay":true,"cacheJson":true,"heartbeat":false,"heartbeatInterval":30,"areaSelector":"friendlyName","deviceSelector":"friendlyName","entitySelector":"friendlyName","statusSeparator":"at: ","statusYear":"hidden","statusMonth":"short","statusDay":"numeric","statusHourCycle":"h23","statusTimeFormat":"h:m"}]
And you’re done! There’s not much more to it
The Pros:
For me this has helped me to get my self consumed solar energy level from 50-60% to around 99%
As for cost-efficiency, the dimmer + ESP32 + MAX485 + DS18B20 were around 70€ - considering that I store between 1 and 2 kWh of solar power in the boiler each day, this means I break even after a year (at current energy prices - which will go up quite significantly by the end of the year). Quite a good deal I would say, even if I am wel laware, that the efficiency will be a lot worse in the winter … the only thing I don’t know is how long the dimmer will work … I drive it mostly at low settings, 10-50% usually and have a fan built in to keep it cool, but it’s definitely not meant to be on 24/7.
The Cons:
The dimmer humms quite audibly … it doesn’t matter in my case, but you should consider it.
The Afterthought:
Hot water is just energy stored as heat - I know how much water there is and I know the temperature, therefore I know how much energy is stored in my boiler - so I made two template sensor and two utility meters (one for the energy fed into the boiler and one for the energy used) and implemented it as a “battery” into the energy dashboard - it’s quite interesting to see how much energy a shower consumes in a short amount of time
This was one long shower in the early afternoon
it’s 50l, so 1°K difference is 58Wh energy difference
Here’s the code for the template sensors
sensor:
- platform: template
sensors:
boiler_energy_stored:
unit_of_measurement: "kWh"
icon_template: mdi:lightning-bolt
value_template: >
{% set value1 = states('sensor.boiler_temp') %}
{{ (((value1|float ) * (0.058|float)) |round(4, default="not available") ) }}
boiler_energy_stored_inverted:
unit_of_measurement: "kWh"
icon_template: mdi:lightning-bolt
value_template: >
{% set value1 = states('sensor.boiler_temp') %}
{{ (((value1|float ) * (-0.058|float)) |round(4, default="not available") ) }}
the utility meters I configured through the UI - make sure to keep net-consumption disabled, so they only recognise positive changes
Only issue I still have with this is, that the temperature tends to fluctuate - and every change in temperature, even if insignificant, gets seen as a change in stored energy - so it’s not super accurate, unless there is a big change
The End