The build
How the solar hot tub works
This is a solar heat-exchanger on a Canadian Spa Toronto, not a plug-in spa heater. The shell holds about 1,100 litres. Five 405 W panels charge a 24 V lithium bank through four Victron SmartSolar chargers — a 100/30, a 100/50, and two 150/45s. Eight more panels from Oxford are bought and not on the roof yet. A Smart Shunt is the official bank meter. That bank feeds two 600 W elements in a 150 L drum. A coiled copper pipe inside the drum is the heat exchanger: when the drum is hotter than the tub, a transfer pump pushes tub water through the coil. A Raspberry Pi in the garden decides when that is safe.
The idea
The old meter had a simple rule: heat when the battery sat high, stop before it sagged. The Pi still keeps a 26.2 / 25.2 V floor, but it stages the elements from Smart Shunt surplus — watts charging the bank — and dumps leftover PV into the drum when the battery is already full. Two physical switches are permits, not thermostats. Opening Heat enable stops the elements only. Transfer enable is a separate permit for the pump, so leftover tank heat can still move after heating stops.
The live numbers at the top of this page are pushed from that Pi about every 30 seconds. This site cannot reach the garden. There are no controls here on purpose.
The tub
The spa is a Canadian Spa Toronto. It holds about 1,100 litres. The tub's own Canadian Spa controls are set to 32°C — that is the soak target on the topside panel. The Pi does not talk to that panel. Solar heat is a separate loop: a 150 L drum, a copper coil, a transfer pump, and a hard 40°C cap on the tub so a stuck permit cannot cook the soak. That cap stops the pump, not the elements — the drum is allowed to keep storing heat up to 85°C.
The heat store
The keg is not in the house. It sits in a garden shed. The drum is wrapped, then enclosed in a sealed 100 mm Celotex box on all six sides so the overnight drop is standing loss through that jacket — not a bare tank in the wind. Last night on this page is that number: 21:00–07:00 London, degrees and kWh out of 150 L, and how fast it fell. Hotter evenings should lose more; a bigger gap to shed air should cool faster. Nights where the heaters or the pump ran are called out so they do not look like insulation failure.
The bank
Two Renogy 12 V 100 Ah packs in series, so the shunt sees a 24 V 100 Ah class battery. Instant Readout gives voltage, current, and SoC. That SoC is coulomb counting against the 100 Ah setting — it will not tell you if the cells have lost capacity. The useful tells on this page are equivalent cycles (discharge Ah ÷ 100), time spent at or above 95%, how close the bank gets to the 25.2 V heat floor, and voltage sag when a 600 W element turns on. Sag includes the BMS and the cables. A slow rise in that milliohm number is what to watch, not a single day.
Drum capture is tank rise versus element kWh. Coil is tub gain versus drum drop while the pump ran. Array vs sky is yield against horizontal GHI times the array size — the roof is not horizontal, so it is a weather check, not a datasheet performance ratio. Clipped PV is hours at ≥95% SoC with the array still making power and the heaters off.
What is in the box
- Tub
- Canadian Spa Toronto
- About 1,100 litres. Topside controls set to 32°C. The Pi does not drive that panel.
- Brain
- Raspberry Pi Zero WH
- Python service, SH1106 OLED, LAN dashboard. ARMv6, ~512 MB.
- Solar
- Five 405 W panels on the roof
- Three from Facebook Marketplace (£170) and two Maxeon SunPower Performance 6 all-black from Bimble Solar (£165.40 including delivery). Two on a Victron SmartSolar 100/30, three on a 100/50. Eight more panels from Oxford (£500 for the lot) are bought and not installed.
- Chargers
- Victron SmartSolar 100/30, 100/50, 2×150/45
- 100/30 and 100/50 paid £97.95 and £134.12. Two 150/45s are fitted but currently off, as is the 100/30. Instant Readout over Bluetooth — no VE.Direct cable.
- Battery
- 24 V 100 Ah LiFePO4
- Two Renogy 12 V 100 Ah Core packs in series, from Facebook Marketplace, £269.10 the pair. BMS stays in circuit. A Victron Smart Shunt is the official bank voltage.
- Bank meter
- Victron Smart Shunt
- Instant Readout over Bluetooth. Heat stages from this current and SoC when it is live. Chargers are for PV and yield, not surplus.
- Heat store
- 150 L stainless keg + copper coil
- Amazon keg, £50.98. Two 600 W 24 V elements heat the drum water. Tub water is pumped through a coiled copper pipe inside. The keg sits in a garden shed: wrapped, then a sealed 100 mm Celotex box on all six sides. The drum can keep heating to 85°C after the tub has hit 40°C.
- Transfer
- Heat-transfer pump + Hall flow meter
- Pushes tub water through the copper coil when the drum is hotter. Stops at 40°C in the tub. sourcingmap SEN-HZ21WI G1/2" meter (£12.79) is the dry-run interlock on BCM 5.
- Senses
- Three DS18B20 probes
- Hot tub, heat tank, ambient air. Victron Instant Readout over Bluetooth from the chargers, plus a Smart Shunt for bank voltage, current, and SoC.
- Switching
- Industrial 24 V marine relays
- They switch the heavy heater voltage. Pi GPIO only drives 24 V optocoupler modules; those then pull in the marine relay coils.
- Permits
- Heat enable + Transfer enable
- Dry-contact switches. Heat enable is the elements; Transfer enable is the pump. Open means that loop is off.
What the kit cost
These are the pieces I actually paid for and wrote down. Two Renogy 12 V 100 Ah Core LiFePO4 batteries were £269.10 the pair on Facebook Marketplace. Eight more panels from Oxford (£500) are in that total and not on the roof yet. Cabling, lugs, the copper coil, the pump, the Pi, and the rest of the garden clutter are still missing — they add up, but they were not the headline spend. String fuses are in the pile; I did not write that amount down.
| Piece | Where | Paid |
|---|---|---|
Two Renogy 12 V 100 Ah Core LiFePO4 batteries Series for a 24 V 100 Ah bank. £269.10 for the pair, Facebook Marketplace. | Facebook Marketplace | £269.10 |
Three 405 W solar panels Facebook Marketplace. On the roof now — part of the live 2,025 W array. | Facebook Marketplace | £170.00 |
Two 405 W Maxeon SunPower Performance 6 panels All-black, MCS approved, end-of-line stock from Bimble Solar. £35 of the total was delivery. On the roof now. | Bimble Solar | £165.40 |
Eight solar panels (not installed) Facebook Marketplace, Oxford. £500 for eight. Still on the ground — not on the roof, not on a charger, not in Instant Readout. | Facebook Marketplace (Oxford) | £500.00 |
Victron SmartSolar MPPT 100/30 Two of the five roof panels. 100 V, 30 A, 12/24 V. Currently powered off. | Amazon | £97.95 |
Victron SmartSolar MPPT 100/50 Three of the five roof panels. 100 V, 50 A, 12/24 V. Currently the live charger. | Amazon | £134.12 |
Two Victron SmartSolar MPPT 150/45 Fitted and in the Instant Readout map. Currently powered off, waiting for strings. | Not recorded | Not recorded |
Victron Smart Shunt Official 24 V bank voltage, current, and state of charge. Heat uses this reading when Instant Readout is live. | Not recorded | Not recorded |
150 L stainless keg Heat store in a garden shed. Wrapped, then sealed 100 mm Celotex on all six sides. Two 600 W 24 V elements and a copper coil sit inside. | Amazon | £50.98 |
sourcingmap SEN-HZ21WI Hall flow meter G1/2", DC 5 V, 1–30 L/min. Transfer-loop dry-run interlock on the Pi (BCM 5). £12.79. | Bought | £12.79 |
PV string fuses Fuses for the panel strings. Bought; the amount was not written down with this update. | Not recorded | Not recorded |
| Listed above | £1400.34 | |
Five 405 W panels is 2,025 W on the roof. Eight more from Oxford (£500) are bought and not fitted. Four Victron SmartSolar chargers sit in the map: the 100/30 takes two panels, the 100/50 takes three, and two 150/45s are fitted but currently off, as is the 100/30. The 100/50 is the live charger. A Victron Smart Shunt is the bank voltage. The 150 L keg is the heat store, not the spa. Recorded spend is £1400.34, of which £900.34 is already in the plant. Payback against the live kit will take a year of the running totals at the top of this page — the extra chargers, the shunt, string fuses, and oddments are still extra.
When the heaters run
Both elements are about 50 A at 24 V, so they are staged. Industrial 24 V marine relays switch that heating current — not the Pi, and not the small optocoupler modules on its GPIO. One element is the base load. The second only joins when there is enough surplus. Staging uses the Smart Shunt: charging watts into the bank, plus whatever the elements are already drawing. That sum is “available for heat.” A stale or missing shunt keeps the heaters off — the chargers cannot stand in for surplus. Clouds get a 20 s hold-on and a 45 s hold-off so the relays do not chatter.
A 600 W element cannot absorb 300 W of solar on its own. Below about 650 W of surplus both heaters stay off, otherwise they would discharge the bank. That is the right call while the battery still has room. Once the bank is full the MPPTs would otherwise throttle and throw that 300 W away. Dump mode turns heater 1 on for leftover PV (about 80 W or more) so the drum takes it. The rest of the 600 W comes from the battery until SoC drops out of dump, the sun fades, voltage hits 25.2 V, or the drum hits 85°C. Better a short dip from a full pack than clipping the array. Heater 2 still needs about 1,250 W. Solar-only never dumps.
Off
Battery under 25.2 V (until it recovers to 26.2 V), Heat enable open, heat mode off, drum at 85°C, no live Smart Shunt, or no drum temp.
Surplus — one heater
Heat enable closed, shunt live, voltage in the 26.2 / 25.2 V band, drum under 85°C, and about 650 W available (charging watts plus any element already on). The tub can already be at 40°C.
Surplus — both heaters
Same as one heater, and about 1,250 W available. Heater 2 waits 1.5 s. The second 600 W should come from surplus, not only the battery.
Dump leftover PV
Normal from 95% SoC, holiday from 90%. If the array is still making about 80 W, heater 1 turns on even when that is under 650 W — otherwise a full battery clips the panels. The 600 W element takes the rest from the bank until SoC, sun, 25.2 V, or 85°C stops it. Solar only never dumps.
Heat enable must be closed and the drum must be under 85°C. The tub sitting at 40°C does not stop the elements — they heat the drum, not the spa shell. A missing drum probe fails safe to off. Relays chatter is avoided with hysteresis: heat resumes below 82°C in the drum. Heater 2 waits about 1.5 seconds after Heater 1 so both 50 A loads do not slam on together. 26.2 / 25.2 V is still a hard floor, not the staging rule.
When the pump runs
Transfer is a separate loop from the elements. The pump pushes tub water through the copper coil in the drum. Heaters can already be off — leftover drum heat should still move. Opening Heat enable does not stop the pump. If the tub hits 40°C the pump stops, but the elements can keep charging the drum. The pump also stays off if the tub is under 30°C, the tank is under 60°C, or within 2°C of ambient: that looks like the heating return hose (or the tub probe) is in air, not water, and pumping then would run dry.
On
Transfer enable closed, tank at least 60°C (stays on down to 50°C), tank at least 2°C hotter than the tub, tub between 30°C and 40°C, and the tub not within 2°C of ambient. Heaters may already be off.
Off
Transfer enable open, tank under 60°C (below 50°C once running), delta under 1°C, tub at 40°C, tub under 30°C, tub within 2°C of ambient, or a missing tub/tank probe. A restart or loop error also drops the pump.
Fail-safe
Everything starts off. A restart or a loop error drops the pump and both heaters. A missing drum probe drops the heaters. A missing tub probe drops the pump only — the drum can still heat. A dead Instant Readout on the Smart Shunt drops the heaters — leftover drum heat can still transfer. Voltage from the chargers is not enough to stage heat. The 40°C tub cap stops the pump, not the elements; the drum may keep rising to 85°C. The pump also pauses if the tub is under 30°C, the tank is under 60°C, or sitting within 2°C of ambient (hose-out / dry-run guard). The LiFePO4 BMS stays in the circuit; the Pi's 26.2 V / 25.2 V window is a hard floor, not a battery manager.
GPIO is 3.3 V only. That side only pulls 24 V optocoupler inputs. The coils of the industrial marine relays are 24 V from a separate supply with a shared ground; those contacts switch the heater voltage. The OLED and temperature probes take 3.3 V from the Pi, never 5 V. A 1.3" SH1106 screen on the Pi shows one big reading at a time — temps, PV, volts, and why something is blocked.
The software
The runtime is a Raspberry Pi Zero WH. It is ARMv6 with about half a gig of RAM, so there is no Docker and no IDE running on it. I edit on a Windows PC and copy Python onto the Pi. A systemd service called httm runs a 1 Hz control loop: read probes and Victron Instant Readout, decide, drive three automatic relays, update the OLED, then push a sanitised snapshot here.
Instant Readout is Bluetooth listen-only — no VE.Direct cable. The LAN dashboard is for wiring and thresholds. This public page is read-only: temperatures, solar, the Smart Shunt, relay state, and the two enable switches. No GPIO map, no charger keys, no controls.
Control logic
About once a second the Pi reads the probes and the Victron Instant Readout, decides, then drives three automatic relays: pump, heater 1, heater 2. Relays 4–6 stay unused. The two dry-contact switches are permits, not thermostats. Thresholds live in config on the Pi; these are the numbers this build uses.
| Check | This build |
|---|---|
| Loop | About 1 Hz. Relays start off and drop off on restart, crash, or a loop error. |
| Heat enable | Permit for the two 600 W elements only. Open means heaters off. Does not stop the pump. |
| Transfer enable | Permit for the heat-transfer pump. Independent of the heaters. |
| Battery voltage | Hard floor only: heaters on at 26.2 V, off under 25.2 V. Staging is surplus watts from the Smart Shunt, not this voltage. |
| Surplus bands | Available = shunt charging watts + heaters already on. Under ~650 W both off; ~650–1,250 W heater 1; above ~1,250 W both. Hold-on 20 s, hold-off 45 s. |
| Dump leftover PV | Normal from 95% SoC, holiday from 90%. Heater 1 on if leftover PV is about 80 W or more, even under 650 W, so a full battery does not clip the array. Solar only never dumps. |
| Heat mode | Live-tab choice, no restart: solar only (surplus, no dump), normal (surplus + dump above 95%), holiday (dump from 90%), or heat off (elements forced off). |
| Second heater | Joins above ~1,250 W available, after a 1.5 s delay. Dump does not force heater 2 on leftover PV. |
| Heat store | 150 L drum with a coiled copper pipe inside, in a garden shed: wrapped, then sealed 100 mm Celotex on all six sides. Elements off at 85°C, resume below 82°C. Pump can still run while the drum is at the cap. |
| Hot tub | Pump off at 40°C, resume below 39°C. Elements keep heating the drum. Pump also off under 30°C (resume at 31°C). Separate from the spa topside set to 32°C. |
| Hose-out | If the tub reading is within 2°C of ambient, the pump pauses (resume when the gap is 3°C). Treats a return hose or probe in air as a dry-run risk. Heaters can stay on. Missing ambient skips this check. |
| Transfer delta | Pump on when tank − tub ≥ 2°C, off when the delta falls under 1°C. The tank must also be 60°C to start (hysteresis: stop below 50°C) so the coil is not circulating lukewarm store water. |
| Missing sensors | No drum reading: heaters off. No tub reading: pump off, drum can still heat. No live Smart Shunt: heaters off, pump still allowed. |
Available for heat is the Smart Shunt charging watts plus any heaters already on. Dump (normal above 95% SoC, holiday from 90%) also counts leftover PV, so a full battery with 300 W of sun still turns heater 1 on. A stale or missing shunt keeps the heaters off; the pump can still run. Heater 2 waits about 1.5 seconds after heater 1 so both 50 A loads do not slam on together. The Pi never switches the heater current itself: GPIO is 3.3 V into 24 V optocoupler modules, and industrial marine relays take the heating voltage.
If you copy this
This is a personal garden build, written up so the rules are visible — not a product, a kit, or a certified spa or heating control. The numbers above are what this tub uses. They are not a claim that the same window is safe on your battery, tank, or wiring.
Hot water, lithium storage, and high-current 24 V DC can injure people and start fires. I am not an electrician, this is not advice, and there is no warranty. If you reuse any of the ideas, the design, the wiring, and whatever happens next are yours. I am not responsible for injury, damage, or a copy that behaves differently from what you read here.
Where it is
This is on the real Canadian Spa Toronto now — panels, chargers, battery, 150 L drum, copper coil, transfer pump, and the two 600 W elements. The Pi in the garden is running the loop. This page is the public window.