In South Africa, our electricity supply is somewhat erratic, due to around 2 decades of under-investment, and incredible levels of corruption. As a result, many residents have resorted to installing solar power, with battery backup to ride out the 2-4 hour "load shedding" periods experienced on a daily basis. As an initial grudge purchase, I was looking for ways to save as much money as possible to shorten the payback period. An obvious candidate would be to reduce the amount of energy imported from the grid to power my resistive element water heater (aka geyser). While there are similar products available locally (search for Geyserwise), they were both expensive, and cloud-based. I wanted a solution that addressed both of those issues.

Geysers in South Africa typically have a 3-4kW spiral element, with a bimetallic thermostat inserted into a dry tube that runs down the middle of the element. This can be set to various target temperatures, by turning the screw on the thermostat. This would be considered a dumb thermostat, because it's not possible to read back the current temperature, nor change the target temperature remotely. The plan was to replace the dumb thermostat with a smart one, and an adjacent relay that would turn the power on and off to maintain the target temperature.

There was one initial change that I got a plumber to do for me, which was to change the existing 3kW element to a 2kW one. This conveniently coincided with a geyser replacement due to a leak, so the plumber was on site already, and the entire geyser was outside of the roof (as a new/replacement unit). This was done because the amount of energy drawn instantaneously by a resistive element cannot be varied externally, except perhaps using a varistor. I was a bit nervous about trying that, especially after seeing comments that it would not be a nice load to drive from an inverter. Swapping the element was simpler in the end, and meant that the load of the geyser and other houshold consumption was usually within the excess solar power generated, and didn't end up drawing from the battery (too much).

Second step was then to find or make a smart thermostat. This wasn't something that I wanted to experiment too much with, as my geyser is in the ceiling, and is either dark, or REALLY HOT to get in there and mess around. I therefore purchased a Geyserwise thermostat, which incorporates a thermistor, and a failsafe bimetallic cutout at 90C. I also looked around to find a suitable smart relay that could interface with the thermistor directly, as I didn't want the relay to be left on, because Home Assistant had crashed, or my wifi was flaky.

One important consideration to be aware of when planning to connect an external sensor to a smart relay is isolation. Many high power smart relays available locally include power measurement, which could be nice to have, but at the same time, are not designed to connect external sensors, and use a non-isolated power supply to the microcontroller. What this basically means is that the ground level of the microcontroller is referenced to the AC supply, and any external sensor connected to that microcontroller is essentially at 220V AC! As a result, devices such as the CBI Astute Smart Isolator and similar devices were excluded from the list.

One device I did find that ticked the boxes was the Sonoff THR320. This has a relay rated at 20A, which is adequate for a 2kW element. It is also designed to talk to 1Wire sensors, to gather temperature and humidity data. Opening it up, I saw a transformer being used to provide an isolated supply to the microcontroller. Perfect!

The THR320 also makes use of an ESP32, which makes it programmable using ESPHome firmware. The only problem was that the pin chosen by Sonoff to interface with the 1Wire sensors sits in the ADC2 peripheral. This is fine for a digital sensor, but the thermistor is analog, and the ADC2 peripheral cannot be used at the same time as WiFi!

That left me with a few options:

  1. Turn off the WiFi when taking readings, then turn it back on again.
  2. Jumper the RJ9 connector to a different pin on the ESP32, that can be used concurrently with the WiFi.
  3. Create an interposer/translator that interfaces with the thermistor via its own ADC, and presents those results to the ESP32 via 1Wire.
  4. Replace the thermistor with a 1Wire temperature sensor.

1. seemed like a recipe to crash the ESP32, turning WiFi on and off every minute or so, and generally a bad idea. 2. required some seriously fine microsoldering that I wasn't comfortable attempting, because I couldn't find any suitable test points that would be easy targets to solder to, and the pin pitch of the ESP32 was too fine for me.

I ended up going with 3., purely because I didn't think of 4. at the time I was doing the project. If I were to redo this project, I would probably do 4., because it is a much simpler approach!

Having found the OneWireHub project on GitHub , i figured this would be a perfect solution. I looked around for available ATTiny controllers and purchased a few ATTiny412s. Unfortunately, OneWireHub had not been ported to the 412, so I had to try again.

Fortunately, it turned out I had an ATTiny85 in my parts bin already, and made up a small veroboard with it on. Pictures of the interposer are available on the project page. I made a simple program building on the OneWireHub, and maintaining a small running average to flatten any spikes. In retrospect, I probably overcomplicated things again, and should have simply reported whatever reading was obtained from the thermistor, and let the ESP32 do the filtering, purely because it is a lot easier to update the ESP32 than the ATTiny!

With the smart thermostat taken care of, actually automating the energy savings comes into play. I have a Sunsynk inverter that I monitor over RS485, using the KellerZA/Sunsynk add-on. That allows me to see the battery state of charge, as well as the current battery charge rate. By default, the thermostat's ESPHome firmware boots up with a target of 55C, set 60 seconds after power on to reduce power spikes in the neighbourhood when the power returns. This acts as if it were a dumb geyser. Since the geyser is powered by the inverter, though, it doesn't usually lose power during service interruptions. Once Home Assistant connects to the ESPHome API endpoint, HA sets it to 45C by default. The smaller the difference between the target temperature and ambient, the lower the rate of heat loss, and there is enough water at 45C for the family to shower, given our usage patterns.

Home Assistant is then configured to switch the geyser off between 16:00 and 09:00 if the grid is disconnected, to prevent the geyser from heating from the batteries overnight. Otherwise, if the geyser starts to heat, HA instructs the inverter to "charge the battery from the grid" at 0A, which essentially means that the house runs from the grid without charging the battery. When it stops heating, HA resets the inverter's configuration to how it was previously. And for the "use excess solar" use case, when the battery's State of Charge is above 70%, and the battery is charging at greater than 2500W for 5 minutes, set the geyser target temperature to 70C, and when the battery discharges at more than 500W for 5 minutes, set the target temperature back to 45C. This works pretty well, because if the battery discharges for a bit, the remaining solar power generated after it dips below 2000W is often enough to top up the batteries to 100% or close to it.