Home | pfodApps/pfodDevices | WebStringTemplates | Java/J2EE | Unix | Torches | Superannuation | CRPS Treatment | | About Us

Forward Logo (image)      

HotWater from Excess Solar
How to store excess generation in the hot water tank

by Matthew Ford 1st Feb 2024 (originally posted 17th August 2023)
© Forward Computing and Control Pty. Ltd. NSW Australia
All rights reserved.

How to Store Excess Solar Cell generation
in your Hot Water tank

Update 1st February 2024 – Made Solar Hot Water Monitor multi-connection capable and moved cost calculation to pfodDevice.
Update 13th January 2024 – IoTaWatt does not recover from transient power failures. Remove its USB power cable for 10 sec to reboot it.
Tips for a more reliable system :-
i) size to hot water tank for two (2) days use,
ii) size the Solar Cells for twice the hot water heating element kWs so that the hot water is still heated on cloudy days.


In the current system, when there is excess solar cell production is exported to the grid for a rebate of $0.07 / kWhr. When there is insufficient solar cell production electricty is imported from the grid at a cost of $0.32 / kWhr. In addition to this the hot water is heated via a separate “Off Peak” (Controlled Load) meter for $0.15 / kWhr.

This project aims to store the excess solar production in the hot water and so save $0.08 / kWhr. To do this the hot water heating element needs to be rewired from the Off Peak meter to the normal Net Meter which handles the solar and the house electricity. Also a triac needs to be added in series with the hot water element and controlled to only allow heating when there is excess solar cell production. This project is an add-on the IoTaWatt monitoring system described here.

An XIAO-ESP32C3 board controls a triac to supply power to the hot water tank when there is excess Solar generation. The XIAO-ESP32C3 interrogates the IoTaWatt via http requests to obtain the current Import/Export kW, the accumulated electricity cost for today (and yesterday's total cost) and the kWhrs that have been supplied to the hot water tank.

To allow for no sun days there are two fall back options:-
1) a “1hr Boost” button on the control app which will supply 1hr of electricity to the hot water element regardless of solar cell production and
2) a manual switch in the switch board that will restore the system to its original Off Peak setup.

The “1hr Boost” button will heat the hot water immediately but at a cost of $0.32 / kWhr, whereas switching the manual rotary switch back to Off Peak will delay heating the water until the next off time the Controlled Load meter is turned on by the electricity distributor, typically overnight, but at a reduced cost of $0.15 / kWhr.

Two modules are presented. The control module with the XIAO-ESP32C3 and triac which turns the hot water heater on and off and whose pfodApp display includes the Solar and Boost 1hr buttons and a monitoring module consisting of just an ESP32 board which omits the control functions but displays the Import/Export, Costs and accumulated Hot Water kWhrs. is family friendly by not having the Solar and Boost 1hr buttons.

Below is a plot from IoTaWatt of the hot water turning on the solar output reaches 4kW and then turning off and on as other appliances in the home use the available solar. The Blue trace is the hot water kW (~3.5kW element) and the Green trace is the Main import/export. The negative values indicate net power export.

The first turn off is due to the washing machine taking the solar power to heat its water. The second turn off is when the Coffee machine is heating up. The little ripples in the Green trace there after are the Coffee machine regulating its temperature. Just after 9:50 a toaster is turned on, but by then there is enough solar to supply both it and the hot water, until about 9:54 when the kettle is turned on as well. Then the hot water turns off until the kettle has boiled.

Parts List (prices exclude shipping)

Control Module
40A Solid State Switch (240 AC triac DC control) and 10-400A heat sink (see text below) ~ e.g. CG Instrument Store ZGT-40 DA on Aliexpress ~US$16.00
XIAO – ESP32 C3 board ~ US$5.00
Red Led – e.g. Jaycar ZD1790 ~US$1.50 or Sparkfun COM-00528 ~ US$1.05
220Ohm resistor – eg Sparkfun COM-17994 ~ US$0.25
Junction Box (clear lid) 115 x 90 x 55mm – e.g DGWLG store on Aliexpress ~US$10.00
5V USB power supply USB-C plug OR 5V USB with micro USB plug (Jaycar MP3536 Australian Plug) + USB 2.0 Micro Female to USB-C Male Adapter (Sparkfun PRT-20668) ~ US$22.00
20A 1-0-2 rotary cam change over switch – e.g.Yaming electric Store on Aliexpress (see switch diagram below) ~ US$7.50
2 pin plug and socket – e.g. Jaycar PP2020 ~US$2.30
Heat shrink, power wiring and lugs.
pfodApp Android App ~ US$12.5

Monitor Module
Any ESP32 board – e.g. Fire_Beetle ESP32 – E ~US$9.00
Enclosure – e.g. Medium Room Sensor Enclosure ~US$4.20
5V USB power supply USB-C plug – e.g Raspberry Pi 4 Power Supply (Australian Plug) ~US$10.50
pfodApp Android App from above

This project also needs an IoTaWatt unit and supporting power supplies and current transformers to be installed to monitor total Import/Export and Solar and OffPeak hot water usage.

The Triac only needs to 20A and the heat sink only needs to be able to handle 44W, but it is inexpensive to use higher powered versions. The Triacs available from Aliexpress are very cheep. There have been reports of counterfeit FOTEK solid state AC switches. Although the solid state switch used here is not a 'FOTEK' one, 20A solid state switches from Digikey cost ~US$100 so do not be surprised if your solid state switch fails. Just be prepared to use the Rotary Switch to restore the hot water heater to Off Peak if the triac fails.


There are three (3) modes of operation to heat the hot water. The manual rotary switch on the switch board allows the choice between Solar and the the original Off Peak (Ctrl Load) setting. On the Solar setting the app allows the choice between Solar or 1hr Booost.

With the manual switch in the Off Peak setting a separate meter supplies power to the water tank at times determined by the electricity supplier. Electricity supplied via this meter is at the concessional rate of 15c/kWh. In this setting any excess solar is exported for 7c/kWh refund. In the Off Peak setting the triac is bypassed to provide electricity to the hot water tank when ever the electricity supplier switches the Controlled Load meter on.

If the manual switch is in the Off position the triac and hot water element are isoloated from both the Mains and the Off Peak supplies. If the manual rotary switch on the switch board is in the Off or Off Peak Position, the pfodApp will display “Hot Water Off Peak or Off”.

With the manual switch in the Solar position, the pfodApp controls when the hot water is heated. There are two settings Solar and 1hr Boost.

In the Solar setting, the triac is kept off until there is more than 4kW of excess solar being exported. At that point the triac is turned on and if the hot water tank is not completely up to temperature then its thermostat supplies power the element. This power is registered by a separate CT and the kWs being used by the hotwater tank is displayed as an Orange pie segment on the app's Export/Import pie chart. The Export value is the total available excess Solar for use by other home appliances. If say a electric kettle and a toaster is turned on then the 3.5kW hotwater + 2kW kettle + 2kW toaster = 7.5kW would exceed the available excess Solar and the system would start to import power. The software detects that (from the IoTaWatt readings) and turns off the hotwater to allow the kettle and toaster to run on the excess Solar. So in Solar mode any house appliances take precedence over supplying power to the hot water heater.

The remaining segment of the pie chart when Exporting indicates if there is sufficient excess solar to turn on major appliance. That is it is Green if there is 2.5kW or move available an Yellow otherwise.

Pressing the 1hr Boost button switches the triac on for 1hr regardless of the excess solar available. This means that if the hotwater tank is not completely hot the element will consume 3.5kW either from the excess solar or importing from the mains.
Again if the hot water tank is drawing power there is Orange pie segment indicated on the app. However when Boosting Hot Water, the Export value only shows the available excess solar after subtracting the hot water usage. This is because if you now turn on a kettle, the hot water does not turn off and you will be importing power to run the kettle.

The displayed “Cost today” is obtained from the IoTaWatt calculated output which sums the various Import/Export and Off Peak kWhrs multiplied by their respective dollar amounts. The Cost today is positive, and shown in Red, if you are paying for the electricity. The Cost today is negative, and shown in Green, if you are getting a refund because the value of the Exports exceeded the cost of the Imports (and Off Peak if any).

How to tell if the hot water tank is completely heated

With the manual switch in the Solar position, than with the app will display “Hot Water was Fully Heated” when hot water tank if power was applied by the hot water thermostat was off. This is determined by checking if the triac is turned on but the Solar hotwater CT is not measuring any current flowing through the hot water element. That is the hot water tank thermostat has switched off. In any case the accumulated hot water kWhrs for today is shown . If the end of the day the hot water is not showing as was Fully Heated, then the displayed kWhrs can be used to indicate how close to fully heated the hot water is. For this household typically hot water usage is between 5kWhrs and 9kWhrs each day. A whole tank of cold water takes about 15kWhrs to heat. Yesterday's values are also shown so that any overnight OffPeak kWhrs are accounted for. Yesterday's cost includes any overnight (up to midnight) consumption from heaters etc.

When the System does not Work

This system depends on there being enough excess Solar for long enough to reheat the hot water that the family has used, i.e. 5kWhr to 9kWhrs. If there are one or more rainy days, you can use the displayed kWhrs to see if you need should manually switch to OffPeak. 5kWhrs or more is sufficient for 2 days OffPeak are needed. If the hot water runs cold you can use the 1hr Boost to heat about a ¼ of a tank. Choosing 1hr Boost will heat the water immediately and show “Hot Water was Fully Heated” if the tank is completely heated, but at a cost of 32c/kWhr. Manually switching to OffPeak over night will delay heating, but at the cheaper rate of. 15c/kWhr.

The Yesterday's hot water kWhrs together with todays hot water kWhr help you decide in the evening if you need to switch to OffPeak overnight to reheat the hot water.



The Junction box was slotted to take the Triac heat sink and the DC control pins of the triac were driven from the D5 and GND pins of the XIAO board. The Red led in series with the 220ohm resistor was connected to 3V3 and D7 as shown below. In addition two wires were added to D4/D3 to detect when the switch board rotary switch was not in the Solar position.

The AC Power wiring is as shown below. (pdf version here)

The three screw connectors, shown in the circuit diagram above, enable the triac controller to be easily removed if necessary. There is also a two pin plug for the Rotary Switch Monitor input.

The 3 position rotary switch enables the hot water to be switched back to Off Peak if there is insufficient Solar OR if the triac controller fails.

There are two CT's (Current Transformers) which feed the IoTaWatt Power Monitor (in addition to the Voltage Transformers). The Net Power CT measures the net import/export of current from the mains. The Solar Hotwater CT measures the current going to the Hot Water tank when the Rotary Switch is in the Triac controlled HotWater position. The IoTaWatt Power Monitor makes these measurements available to the AC Triac control uC via HTTP requests. See the Software description below.


The SolarHotWater.ino sketch (SolarHotWater.zip) and it supporting libraries (libraries.zip) are used to program the XIAO ESP32C3 module. They should also work for other ESP32 modules. The XIAO ESP32C3 module was chosen for its small size and its external antenna to give better reach when mounted inside the metal mains meter box.


Install Arduino IDE V1.8.19 or higher.
Install the ESP32 board support following these instructions. V2.0.11 was used here. Other versions will have their own set of bugs.
Unzip the libraries.zip file to the Arduino libraries directory.
Unzip the SolarHotWater.zip file to the Arduino sketch directory.
In the Arduino IDE choose the XIAO_ESP32C3 board
Open the SolarHotWater.ino file and program the XIAO ESP32C3

If you have problems programming the XIAO ESP32C3, try these steps:-
– disconnect the ESP32-C3 from USB
– press the boot button on your ESP32-C3 and hold it.
– plug in the USB
– press the upload button in Arduino IDE
– wait a few seconds
– then release the boot button

If you do not see any DEBUG output when you un-comment #define DEBUG, then set the USB CDC on Boot to Disabled in the Arduino Tools menu and reprogram.
After programming you can connect the ESP32C3 to your local WiFi router and set its static IP using Auto WiFi Config. If the Wifi does not connect after configuration and sets up a config AccessPoint, just reboot.

Solar Hot Water Monitor

The monitor software is similar but without the control buttons and the code to turn the triac on and off. Use the code in SolarHotWaterMonitor.zip This code assumes a XIAO-ESP32C3 board with an RGB led on D2, but can be modified for other ESP32 boards with different led pins.

Software Description SolarHotWater.ino

The SolarHotWater.ino sketch has been setup to drive the triac via D5 and to drive the Auto WiFi Config indicator led/resistor connected between D7 and 3V3.

The SolarHotWater.ino sketch sets up two processes. The loop() code handles connections from pfodApp to display the Import/Export pie chart and the current Solar Hot Water setting and the Solar and 1hr Boost buttons.

The second process is an AsyncHTTPRequest request which runs in the background requesting the current Import/Export Watts and the HotWater Watts from the IoTaWatt unit. The IoTaWatt is expected to be on, but that IP can be changed editing the URL in the sendRequest() method.

There are three requests[Watts_3Ph.d2,Watts_3Ph.d2,Solar_HW.d2,OffPeak_HW.d2,time.local]&begin=s-5s&end=s&group=all&format=csv&missing=skip
which requests the 3Phase Watts and the Solar Hot Water and OffPeak watts averaged over the last 5secs
which requests the accumulated Whrs Imported and Exported and Solar and OffPeak kWhr since midnight. This is called at one minute intervals to update the cost today and the accumulated hot water kWhrs from both solar and OffPeak.
which requests the accumulated Whrs Imported and Exported and Solar and OffPeak kWhr for yesterday. This is called once a day to show yesterdays cost and the accumulated hot water kWhrs from both solar and OffPeak.

The IoTaWatt needs to be configured to integrate the total 3Ph watts to calculate the Import and Export values. IoTaWatt also need separate CT's for the Solar how water current and the OffPeak hot water current

The same parser, parseResponse(), is used for each of these requests. The requests also ask for the local time from IoTaWatt. This is used to reboot the ESP32 each morning just after midnight. This reboot cleans up any memory leaks and is a trigger to request yesterday's values.

Because the loop() code and the AsyncHTTPRequest requests and responses run on separate tasks, volatile variables protected by locks are used to exchange state between the AsyncHTTPRequest task and the loop().

The graphical pfodApp display was designed using pfodGUIdesigner. Three (3) basic display widgets were designed:- the pie chart, (Dwg_1.cpp) a button (Button.cpp) and an updateable label (Label.cpp). The pfodGUI designer generated the necessary code to display these widgets. That code was then augmented to add setters/getters to update the widgets with the latest values/state. See the pfodGUIdesigner tutorial for how to specify which primitive elements of the widget are updateable. The generated code includes debug code to display command handling (commented out in SolarHotWater.zip)

This code at the global level instantiates the widgets and connects them to the pfodParser command processing so that their commands are automatically routed to the correct widget.

Dwg_1 _dwg_1(&parser, &dwgs); // create dwg object and add to parser processing
Button solarButton(&parser, &dwgs); // create dwg object and add to parser processing
Button boostButton(&parser, &dwgs); // create dwg object and add to parser processing
Label hotwaterkWhrs(&parser, &dwgs); // create dwg object and add to parser processing
Label hotwaterState(&parser, &dwgs); // create dwg object and add to parser processing
Label hotwaterFull(&parser, &dwgs); // create dwg object and add to parser processing
Label totalCost(&parser, &dwgs); // create dwg object and add to parser processing

The sendMainDwg() method positions and inserts the widgets. The Solar and 1hr Boost buttons interact with the hotwaterState label. When the buttons are pressed they call back to solarButtonCallback() or boostButtonCallback()) to handle the update of the hotwaterState label and update the control states, HotWaterAuto and HotWaterCanHeat and stop and start the 1hr timer for the boost function.

If the pfodGUIdesigner had been used to design a widget containing all three items, then that widget code could have handled the display update of the hotwaterState label as the buttons were pressed because widgets handle their own display updates via sendUpdate(), but since these are separate instances the button call back methods call sendMainMenuUpdate() to force an update of all display items with the latest state.

Software Description SolarHotWaterMonitor.ino

The SolarHotWaterMonitor.ino sketch differs from the SolarHotWater.ino in a number of ways. It does not have the Solar / Boost 1hr buttons, does not control the triac, allows multi-connections from mobile running pfodApp and accumulates the daily cost itself instead of relying on IoTaWatt for that calculation.

The particular IoTaWatt used here seems to have a fault in the Real Time Clock that prevents its internal clock from running. The main result of this is that the Integrators that accumulate the Import/Export Whrs are not accurate. To overcome this the raw Watts (plus for Import and minus for Export) are requested via a http request from IoTaWatt every 5secs and accumulated in a separate task (see httpGetTask.cpp). Every minute the current accumulated values are saved to a SPIFFS file. This provides protection for unexpected reboots. Once every 24hrs, just after midnight the accumulated values are rolled over to the previous day and reset to zero.


This page covered using excess solar generation to heat the hot water. If the house requires more power, the hot water is automatically switched off if needed to prevent importing electricity from the mains and then switched back on when excess solar generation is available. pfodApp is used to display the current Export/Import power and whether the hot water is being heated as well as the accumulated cost of electricity for today. These values are sourced from the IoTaWatt monitor previously installed. A 1hr Boost button is available to heat the how water regardless of the solar production and a manual rotary switch was installed on the switch board to allow the hot water to be switch back to the separate Controlled Load (OffPeak) meter in cases of extended overcast weather.

AndroidTM is a trademark of Google Inc. For use of the Arduino name see http://arduino.cc/en/Main/FAQ

The General Purpose Android/Arduino Control App.
pfodDevice™ and pfodApp™ are trade marks of Forward Computing and Control Pty. Ltd.

Forward home page link (image)

Contact Forward Computing and Control by
©Copyright 1996-2024 Forward Computing and Control Pty. Ltd. ACN 003 669 994