Skip to content

Repository files navigation

Wattplot

Give an old solar panel a second life.

A raised garden bed whose canopy is a working solar panel β€” the same square foot grows tomatoes and generates electricity. Symbiosis of energy production and agriculture: the plants get filtered afternoon shade where they need it most; the panel keeps generating on a structure that would have been lumber anyway.

Open-source end-to-end: 3D model, sun simulation, wind-load analysis, cut lists, schematic (rev B), ESPHome firmware (v3.2), PCB layout, and a GitHub Pages site. MIT license, no paywalls, no telemetry.

Status: working prototype. The Mini v2.4 is built and running on the bench. The full-size build (Longi 620W) is mechanically and aerodynamically validated by FreeCAD 3D model + ASCE 7-22 wind calc + geometric shadow raycaster. Firmware v3.2 is compiled and ready to flash (the chip is currently wedged; see Status & roadmap below).

Live site: mokahlo.github.io/wattplot Β· 3D booth viewer: mokahlo.github.io/wattplot/booth/ Β· Data dashboard: mokahlo.github.io/wattplot/data.html

Two builds, one structure

Same bed, same 72" corner posts, same panel rails β€” pick your tilt mechanism:

Basic Smart
Tilt Fixed, pinned prop strut (0/15/25/35Β°) Motorized, 0–35Β°, linear actuator
Electronics None ESP32 controller + sensors + PCB
Storm response Manual stow: pull pin, lay flat (2 min) Auto-fold on wind, plus manual stow
Panel Salvaged/upcycled panel ideal New 620 W bifacial (or any preset)
Cost ~$400–650 (salvage panel, incl. soil) ~$1,600
Time A weekend 10–15 hr + electronics
Guide docs/build_basic.md docs/build_guide.md

Start with Basic. It's the whole idea in its cheapest form: a raised bed that shades its crop and pays you back in watts, built with a drill and a saw. Smart is the flagship upgrade β€” every Basic build has the strut holes and pivot line to accept the actuator later.

Status & roadmap

Layer State Notes
3D model βœ… Validated FreeCAD assembly, STEP+STL+FCStd export. 5 panel presets.
Sun simulation βœ… Validated analysis/sun_simulator.py β€” annual kWh, daily DLI, tomato yield.
Wind load βœ… Validated ASCE 7-22, Cat II 700-yr, Exp C, Phoenix.
Cut lists βœ… Validated models/cut_list.py β€” every board, every cut.
Mini v2.4 (electronics) βœ… Built & running on bench 18Γ—14β€³, 10W panel, kickstand actuator, ESPHome firmware.
Full-size structural (Basic) βœ… Designed 8Γ—5 ft bed, no electronics. Weekend build.
Full-size Smart (electronics) 🟑 Firmware ready, chip wedged Schematic rev B + firmware v3.2 compiled; needs physical BOOT+RESET.
Custom PCB βœ… Designed Schematic + PCB layout. JLCPCB-ready.
GitHub Pages site βœ… Live Dark theme, 10+ pages, 3D viewer, data dashboard, gallery, diagrams.
Booth materials 🟑 Mid-refresh One-pager + FAQ + poster + sim ready; needs new symbiosis framing.
Trademark (WattPlot name) 🟑 Coexistence request drafted docs/_internal/COEXISTENCE_REQUEST.md. Awaiting Andrew Welch's reply.

What this means in practice: the design is complete, the documentation is live, the firmware builds clean. The two remaining "blocked" items are (1) recovering the wedged ESP32-S3 so we can flash v3.2, and (2) hearing back from the WattPlot.com operator on whether coexistence on the name is OK.

Design rules (enforced)

Three constraints guide every part of the design:

  1. No miter cuts. Every cut is a 90Β° square cut. Joints are butt, half-lap, or lap. (You don't need a miter saw.) Caveat: post lateral bracing is still unresolved precisely because the conventional answer needs miters - see "Open structural questions" below.
  2. All hardware off the shelf. Hinges, panel clamps, bolts, screws, rod, and pins are standard sizes from Home Depot, McMaster, or solar-mounting suppliers (IronRidge / Unirac / Quick Mount). No custom metal parts.
  3. Simple, common dimensions. All lumber from standard stock lengths (8 ft, 10 ft, 12 ft) with reasonable waste. No fractional-inch stock lengths. 96" panel rails (2x6x8ft, no waste), 72" corner posts (4x4x8ft, 24" waste), 89"/37.6" wall skin between the posts (1x6x8ft cedar).

Interactive 3D model

Open the 3D viewer - drag to orbit, scroll to zoom. Loads the live STEP-derived STL.


See it at Maker Faire Bay Area 2026

Sept 25-27, 2026 Β· Mare Island Naval Shipyard, Vallejo CA.

A working Mini v2.4 on the table, 24" live-sim dashboard next to it, printed poster, take-home cut-list cards. The booth package (booth plan, demo script, FAQ, parts list, interactive viewer, sim dashboard) lives in booth/.

If you're at the faire, come by. If you want to exhibit your own agrivoltaic / solar / smart-garden project, the booth package documents what worked (and what to skip) for next time.


Fits any panel up to 8Γ—5 ft: bring your own

A single Wattplot planter is bounded by 8-ft lumber stock: 8 ft long, 5 ft wide. The bed is sized to the panel (with up to 0.5" overhang per side), and the cut list is derived from the bed.

A Wattplot is, at heart, an example of symbiosis between energy production and agriculture: the same square foot grows tomatoes and generates electricity, because the panel that shades the crop is the panel that powers the irrigation. The structure would be lumber either way; the panel is what turns a planter into a power plant.

A natural fit is to use decommissioned rooftop panels that would otherwise be landfilled β€” a 12-year-old 250 W residential panel is still a 235 W panel, perfectly useful for shade plus some power, and you delay recycling by 10–20 years. The design supports both new and salvaged panels; "bring your own."

Five validated panel presets are in wattplot_params.py:

Preset L Γ— W (in) New W Derated W
longi_620W (new bifacial) 97.0 Γ— 44.6 620 W 620 W
residential_60cell 65.0 Γ— 39.0 250 W 235 W (12 yr)
residential_72cell 77.0 Γ— 39.0 300 W 288 W (8 yr)
commercial_96cell 65.0 Γ— 41.0 400 W 388 W (6 yr)
large_format_1m65 65.0 Γ— 41.0 400 W 392 W (4 yr)
import wattplot_params as P
P.apply_panel_preset('residential_60cell')  # bed resizes automatically

For custom panels, set PANEL['L_in'], PANEL['W_in'], wattage, panel_age_years, panel_bifacial, and the bed + derated wattage are computed for you. See docs/upcycling.md for the full guide (lumber math, MPPT sizing, when a salvage panel is not a good fit).


Build photos

The Mini v2.4 build is on the bench (see Status table). Photos of the full-size build will go in renders/build_photos/ once the full-size prototype exists; until then, the booth package in booth/ has the wood-frame renders that represent the as-designed geometry. The template below describes the shot list once a physical build is in hand.

Build the entire apparatus: see docs/build_guide.md for the step-by-step assembly guide (8 phases, ~10-15 hours). (Note: this doc is written for the v2 architecture; treat it as design intent and cross-check against firmware/wattplot.yaml for the current pin map and entity names.)

Test & validation: see docs/test_checklist.md for per-component and per-system tests, with a final sign-off checklist. (Same staleness caveat as build_guide.md.)

Photo template (for the build log):

# Subject Angle Notes
1 Overview of completed build Iso from southeast, 20Β° elevation Frame at 35Β° tilt, full bed
2 Bed close-up Front (south wall) Show 1x6 skin over 2x4 cleats, 2x4 header
3 Post-to-rail joint Iso from north Show 4x4 post top + 2x6 rails
4 Hinge detail Side, 12" away One hinge in close-up, show leaf + knuckle + 1⁄2" pin
5 Actuator mount Side 2x6 PT clevis on north rail, 2x6 wall block, 1⁄2" pin
6 Panel mounting Above, looking down 6Γ— aluminum mid-clamps on the rails
7 PCB in enclosure Above, enclosure open JST-XH connectors, ESP32-S3, DRV8871s, INA219s visible
8 Wiring close-up Side, 6" away Cable carrier with motor + actuator leads
9 Soil sensors Soil cross-section DS18B20 + soil moisture in the bed
10 Soil filled + planted Front 4 tomato seedlings, 11.25" soil depth
11 Dashboard on phone Phone in hand HA dashboard showing tilt, motor current, panel power
12 Canopy at 35Β° (max tilt) Iso from east Power mode, structural max
13 Canopy flat (storm) Iso from east Folding mode, stowed

Add your photos to renders/build_photos/ and link them in this section.



What's in the box

wattplot.py                            ← top-level pipeline:  python wattplot.py
wattplot_params.py                     ← single source of truth for ALL parameters
models/
  freecad/                             ← FreeCAD parametric 3D model
    materials.py                       ← wood species, fasteners, hardware
    parts/                             ← one file per part (bed_wall, frame,
      _helpers.py, bed_wall.py,        ←   hinge, panel_clamp, actuator_mount,
      frame.py, panel.py, hinge.py,    ←   skids, diagonal_brace, ...)
      panel_clamp.py, skid.py,
      actuator_mount.py
    assemble.py                         ← imports all parts, exports STEP+STL+FCStd
    _run.py                             ← freecadcmd entry point
  shadow_raycaster.py                  ← geometric bed-shadow from 3D panel
  render_3d_views.py, render_svg_views.py
analysis/
  sun_simulator.py                     ← annual kWh, bed DLI, tomato yield
  wind_load.py                         ← ASCE 7-22 force + safety factors
  engineering_drawing.py               ← side-view engineering drawings
  pcb_schematic.py                     ← PCB block-diagram generator
renders/                               ← generated PNGs (mostly gitignored)
firmware/                              ← ESPHome firmware for the controller
docs/                                  ← design + build + test docs (see below)
  index.html                           ← GitHub Pages 3D viewer
  control_law.md                       ← firmware spec (state machine, PI loop)
  pcb_design.md                        ← custom PCB spec (KiCad-ready)
  wiring.md                            ← pin-by-pin wiring from PCB to apparatus
  sensor_placement.md                  ← where each sensor mounts + why
  build_basic.md                       ← Basic tier: fixed pinned tilt, no electronics
  build_guide.md                       ← step-by-step build (8 phases, ~10-15 hours)
  test_checklist.md                    ← per-component + integration tests
  watering.md                          ← smart planter: sensors + solenoid + automation
  logging.md                           ← v2.5: MQTT log streaming to wattplot.log

All design rules, the build, the wiring, and the tests are documented. wattplot_params.py is the single source of truth - change a value there and the whole pipeline (3D model, shadow, sun sim, wind sim) updates in ~10 seconds.


The design (one paragraph)

An 8 ft Γ— 3.7 ft planter with 27.5" walls (29" rim height β€” top of the wheelchair-accessible seated-gardening range, with access from both long sides) holding 25.5" of soil, carrying a solar panel on four 72" 4x4 corner posts (walk-under canopy). The panel sits on 2x6 rails laid across the post tops and tilts about its long axis. The bed is the ballast - no ground anchors. In the Basic build the panel rests on a pinned 2x4 prop strut (fixed tilt, set by hand). In the Smart build it's driven by a linear actuator (0-35Β°; storm fold = flat), and the controller uses a PI loop on motor current to reduce tilt under wind load, then returns to the commanded angle when wind drops.

35Β° is the structural max, and the post height is why. Raising the canopy to 6 ft puts panel drag on a ~6 ft lever arm about the bed edge - roughly 3Γ— the moment of a bed-level panel. At 35Β° the structure holds SF 2.55 against overturning; 45Β° drops to 1.89 and 90Β° to 1.26, both below the 2.0 target. The old 90Β° "sun-on-bed / wring-out" modes are retired - to give the bed full sun, stow the panel flat instead.

Frame material: all lumber for sustainability (FSC Douglas Fir where available). Hardware (hinges, panel clamps) is metal where the load demands.

Power architecture - only two sources

            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
            β”‚ 620W bifacial   β”‚
            β”‚ main panel      β”‚  ← the only solar source
            β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β”‚  DC bus (30-40V, 0-18A)
        β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
        β”‚                 β”‚
        β–Ό                 β–Ό
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚ Micro-   β”‚      β”‚ MPPT (Victron     β”‚
  β”‚ inverter β”‚      β”‚  100/30 or EPEver  β”‚
  β”‚ (AC out) β”‚      β”‚  Tracer 4210AN)   β”‚
  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜      β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚                     β”‚
       β–Ό                     β–Ό
   [240V AC]            β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                        β”‚ 12V      β”‚  ← the only battery
                      β”‚ LiFePO4  β”‚
                      β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                           β”‚
                           β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  ESP32 +     β”‚
                    β”‚  DRV8871     β”œβ”€β”€β–Ί Linear actuator (panel tilt)
                    β”‚  BMI160 IMU  β”‚     ↑ closed-loop position
                    β”‚  INA219      β”‚     β”‚ actual tilt
                    β”‚  DS18B20     β”‚     β”‚
                    β”‚  soil sensor β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Only two energy sources: the main 620W panel and the 12V battery. No separate trickle panel. The main panel feeds both the microinverter (for AC) and a real hardware MPPT (Victron SmartSolar 100/30 or EPEver Tracer 4210AN) for 12V battery charging - both from the same panel via a Y-splitter on the MC4 leads. The 620W panel produces 50-100Γ— more energy than the controller needs, so it's a non-issue.

Earlier revisions (v2.0-2.3) used a DPS5005 programmable buck + UART-MPPT pattern for the full-size build. That was retired: the DPS5005 was a hack (using a bench PSU as a charge controller) and was also undersized for the 620W panel (5A max output would have thrown away ~90% of the panel's potential). The mini build (v2.4) uses a standalone Sunapex 10A MPPT - no host connection, IP67 waterproof, LiFePO4-aware out of the box. The full-size build needs the larger Victron/EPEver above. See docs/build_guide.md Β§7.

Key design numbers (Phoenix, AZ, Cat II 700-yr, Exp C)

  • Wind: 115 mph 3-sec gust design. At 25.5" soil fill (~4,800 lb dead load in the 27.5"-wall bed), the structure passes safety factor β‰₯ 2.0 from 0-35Β° tilt β€” 35Β° is the max operating angle, set by the 72" post height. Rated deployed wind: ~130 mph at 35Β°. Stowed flat (0Β°) the panel carries no drag or uplift and only the posts are loaded (SF 26.8); stow is the storm answer for both tiers (manual pin on Basic, auto-fold on Smart). The bed depth is set by dry-soil risk: at 4 wall courses a bone-dry bed falls to SF 1.53, so the build ships 5 courses.
  • Power (azimuth tracking 35Β° tilt, Phoenix 2025): 2,240 kWh/year.
  • Tomato yield (35Β° tilt): ~124 kg/year from 4 plants. Vertical (90Β°) gives full bed sun but ~50% less power and more heat stress.
  • Best balance: static 35Β° or azimuth tracking 35Β°, depending on whether you value simplicity or kWh.

See analysis/wind_load_report.md and analysis/sun_simulator.py for the underlying calculations.

Open structural questions

Raising the canopy onto 72" posts solved the walk-under/reach-under problem but opened two checks. Both are now analyzed β€” and the first one fails as currently specced:

  1. Post bending. analysis/post_bending.py checks the 4x4 posts as cantilevers carrying panel drag at their base connection (the wind analysis in analysis/wind_load.py only checks the structure tipping as a rigid body β€” a separate failure mode). Result: FAIL at the 35Β° operating cap (SF 0.65 vs. target 1.5, worst-case load sharing). Unbraced, the real bending-safe tilt limit is closer to 20Β° than 35Β°. Two remedies, either sufficient: (A) upsize to 6x6 posts (SF 2.53, passes with margin) or (B) add lateral bracing (below) and confirm the residual base moment fits a standard bracket. See analysis/post_bending_report.md for the full numbers.
  2. Lateral bracing. A 6-ft post-and-beam frame needs diagonal bracing. The retired panel-frame diagonal doesn't apply here, and the obvious knee brace wants 45Β° miters, which collides with design rule #1 (no miter cuts). Square-cut gusset plates are the answer β€” a square-cut brace (or a plywood/steel gusset alone) bolted flat across the post/rail corner carries the same axial force a mitered brace would, with zero non-90Β° cuts. Sized in analysis/post_bending.py (Β§Bracing): ~335 lb axial demand at the 35Β° cap, spec target β‰₯ 500 lb β€” well within off-the-shelf structural angle brackets.

Do not build the full-size Smart tier with 4x4 posts, unbraced, at 35Β° tilt until one of the two remedies above is locked in. Both are tracked before any full-size build; this is a first-pass calc, not a stamped one β€” get a PE review before anything goes in the ground.

The smart controller (target design)

Every priority is clamped to ΞΈ_max = 35Β° (the structural cap β€” see docs/control_law.md). The old 90Β° "wring-out" and "bed-sun" modes are retired: to give the bed full sun or dry it out, stow flat at 0Β° instead, where the panel shades nothing and carries ~zero wind load.

priority  source                              sets ΞΈ_desired / lights
─────────────────────────────────────────────────────────────────
   1      user override                       arbitrary 0-35
   2      hard current limit                  ΞΈ = 0 (safety)
   3      NWS rain forecast + dry soil        ΞΈ = 0 (capture rain)
   4      NWS wind forecast > 50 mph          ΞΈ = 15 (preemptive)
   5      wind β‰₯ 50% of I_safe                pause tracking
   6      soil wet 72h+                       ΞΈ = 0 (stow flat, sun dries bed)
   7      soil dry 48h+ + no rain β†’ conserve  ΞΈ = 35
   8      time-of-day + tracking mode         ΞΈ = 0-35 (azimuth track, capped)
   L1     battery SOC < 50%                  lights off
   L2     natural DLI > target               lights off
   L3     DLI deficit > 0 (need light)       lights on (pre/post-dawn)
   L4     hard constraint                    8 hr dark minimum

Goal: keep motor current below I_safe, while maximizing commanded tilt for sun exposure.

docs/control_law.md is the canonical version of this table; the firmware enforces the 35Β° cap in the commanded_tilt number component.

How to run

# Full pipeline (3D model export + sun sim + wind sim)
python wattplot.py

# Just the simulation (skip the 3D export)
python wattplot.py --skip-model

# Just one analysis
python analysis/sun_simulator.py
python analysis/wind_load.py

# Override a parameter at the command line
python wattplot.py --tilt 50

# View the 3D model in a browser
open renders/viewer.html

Dependencies

  • Python 3.10+
  • numpy, pandas, matplotlib, pvlib, shapely, scipy (for analysis)
  • FreeCAD 1.0+ (for the 3D model - freecadcmd is auto-detected on Windows in C:\Program Files\FreeCAD *\bin\)
  • ruff (for lint)
pip install numpy pandas matplotlib pvlib shapely scipy ruff

The 3D model is built by FreeCAD. On Windows with FreeCAD 1.0+ installed in the default location, the orchestrator finds it automatically. To override, set $FREECADCMD to the path of freecadcmd.exe.

Hardware reference β€” Smart tier (target spec, not yet built)

This is the full flagship BOM. Basic tier deletes the actuator, MPPT-for-controller, LiFePO4, ESP32/PCB, and grow light rows, and swaps the new 620W panel for a salvaged one β€” see docs/build_basic.md.

Component Spec ~$
Bed walls 1x6 cedar skin (5 courses, 27.5" tall) + 2x4 PT cleats + 2x4 headers; 29" accessible rim 185
Corner posts 4 Γ— 4x4 PT, 72" (walk-under canopy support) 60
Panel rails 4 Γ— 2x6 cedar, laid flat across the post tops 45
Soil 25.5" fill β‰ˆ 2.2 cu yd β‰ˆ 4,500 lb (the ballast) 160
Frame rails 2x6 PT DF, 2 Γ— 8 ft long + 2 Γ— 8 ft cross 60
Diagonal brace 2x4 PT DF, 1 Γ— 10 ft 15
Skids 4x4 PT DF, 2 Γ— 8 ft 30
Hinges 4 Γ— galvanized butt hinges 4"Γ—4", 1⁄2" pin, +96" 1⁄2" rod 35
Panel clamps 6 Γ— aluminum mid-clamps, 35mm channel 18
Linear actuator 12V, 4" stroke, IP65, 330 lb 60
MPPT charge controller (full-size) Victron SmartSolar 100/30 (30A, 100V) or EPEver Tracer 4210AN (40A, 100V) 200
Panel 620W bifacial (LONGi Hi-MO X10 or similar) 200
Microinverter Enphase IQ7+ or APsystems DS3, 240V, UL 1741 150
12V 100Ah LiFePO4 LiTime or similar 230
ESP32 + custom PCB w/ DRV8871, INA219, BMI160, sensors 120
200W LED grow light full spectrum, IP65 (v2) 130
Misc (screws, bolts, wire, irrigation) 50
Total parts ~$1,400

All structural lumber is FSC Douglas Fir where available. No welding. No concrete. See bom.md for sourcing notes.

The MPPT is a real hardware charge controller (Victron or EPEver) sized for the 620W panel - no firmware-side MPPT loop, no UART setpoint commands. ESP32 reads MPPT telemetry (panel V/I, battery V, charge state) over VE.Direct / RS-485 for energy monitoring and Home Assistant visibility. The same panel also feeds the microinverter for AC output via a Y-splitter. No separate trickle panel needed.

Project status

  • Parametric 3D model (FreeCAD) with STEP / STL / FCStd export, one file per part (models/freecad/parts/)
  • All-wood perimeter frame design (2x6 rails + 2x4 brace, half-lap bed corners)
  • ASCE 7-22 wind load analysis, Phoenix, Exp C, Cat II 700-yr
  • Geometric shadow raycaster (uses actual 3D panel)
  • Annual sun + yield simulator (5 tilt schedules, Phoenix weather)
  • Engineering side-view drawings (with frame + actuator + hinge detail)
  • PCB spec (KiCad-ready) - docs/pcb_design.md
  • Wiring diagram (pin-by-pin) - docs/wiring.md
  • Sensor placement plan - docs/sensor_placement.md
  • Build guide (8 phases, ~10-15 hours) - docs/build_guide.md
  • Test & validation checklist - docs/test_checklist.md
  • ESPHome firmware (PI controller, NWS polling, fold logic) - firmware/
  • Order custom PCB from JLCPCB
  • Real-world deployment validation

Prior art & acknowledgments

Wattplot builds on the work of many open-source projects. If you find their work useful, please support them.

Agrivoltaic simulation

  • NREL/bifacial_radiance - gold-standard bifacial PV ray-tracer. Our 2D shadow_raycaster.py is a simplified version of what bifacial_radiance does in 3D.
  • NREL/InSPIRE - agrivoltaic tutorials, scripts, and research workflows.
  • DailyAgrivoltaicOperation (astuhlmacher) - dual-axis panel optimization under crop constraints, the academic version of what Wattplot does in firmware.
  • PASE 1.0 - Python Agrivoltaic Simulation Environment, energy + crop dual-objective.

Solar tracker controllers

  • Helioduino (NachtRaveVL) - mature LDR-based sun tracker for Arduino. The reference for "professional grade" tracker control.
  • SolarArduino (HDwayne) - ESP32 sun tracker with wind safety using an anemometer (folds to safety position for 15 min if wind > 5 m/s). The pattern for our wind-safety state machine in docs/control_law.md comes from here.
  • Sunchronizer (Nerdiyde) - ESP32 + 6000N linear actuator + BMI160 IMU for closed-loop position feedback. We adopted the IMU approach for the same reason (drift-free actual tilt angle).
  • f2knpw/ESP32_Solar_Tracker - Lite ESP32 solar tracker with sun-position calc, sleep mode, OTA.

Smart solar chargers (MPPT pattern)

  • Earlier revisions of Wattplot (v2.0-2.3) used a DPS5005-as-MPPT pattern (similar to OSPController and fugu-mppt-firmware below). This was retired in v2.4 because the DPS5005 was both a hack (using a bench PSU as a charge controller) and undersized for the 620W panel. The current build uses off-the-shelf hardware MPPTs (Sunapex HC-SM10A on the mini, Victron/EPEver on the full-size) - the firmware no longer commands a charge controller, it only reads telemetry.
  • OSPController (Open Solar Project) - ESP32 controls a commercial DPS5005 buck via UART for MPPT. Good reference if you want to revive the UART-MPPT pattern (e.g., to add telemetry from a Victron VE.Direct port to a custom control loop).
  • fugu-mppt-firmware (fl4p) - ESP32 MPPT firmware, 95% efficient synchronous buck. Reference for designing a custom MPPT from scratch (we deliberately chose not to).
  • akgang ESP32 MPPT - single-file Arduino ESP32 MPPT with INA226, web dashboard, NASA POWER + OWM forecasts. Reference for the MPPT loop algorithm (perturb-and-observe with dither) if we ever need to bring back a firmware-side MPPT step.

Weather + solar (IoT pattern)

DIY raised bed + solar

  • POSCAS - Parametric Open Source Cold-Frame Agrivoltaic System. The closest analog to Wattplot in philosophy (open-source, parametric, agrivoltaic) but for a cold frame. Worth studying.
  • Vege Garden Automation (Rototron) - solar-powered soil sensors + MQTT + HA on a raised bed. Validates the IoT + raised bed + solar pattern Wattplot uses.

Standards

  • ASCE 7-22 - wind load provisions. Our analysis/wind_load.py uses ASCE 7-22 Table 26.10-1 for velocity pressure exposure coefficients.
  • pvlib - solar position + clear-sky modeling. Industry standard, NREL-developed.
  • IEC 61215 / UL 61730 - panel safety standards. Our 620W bifacial panel is certified to these.
  • UL 1741 / IEEE 1547 - grid-tie inverter safety. We use a commercial microinverter (Enphase IQ7+, APsystems DS3) that meets these, so the user doesn't have to.

Plug-and-play solar laws (regulatory)

  • Utah SB 190 (2024) - first comprehensive balcony solar law, 800W plug-in allowance.
  • California AB 1076 (2022) - most generous, 5 kW plug-in allowance.
  • Colorado HB 22-1015 (2022) - 800W plug-in, similar to Utah.

Wattplot's 620W panel is below the 800W threshold in Utah and Colorado, and well within California's 5 kW cap. Design fits the regulatory window for plug-and-play solar in all three states.

Tools we use


License

This project is licensed under the MIT License - see LICENSE.

You are free to use, modify, and sell products based on this design. Attribution appreciated.

Contributing

Issues, PRs, and forks welcome. The system is small enough that you should be able to read the whole codebase in an afternoon.

If you build one, send photos.

Acknowledgments

  • pvlib (Sandia / pvlib-team) for solar position + clear-sky modeling
  • cadquery for parametric CAD
  • shapely for 2D geometry
  • ASCE 7-22 for the wind provisions
  • The "balcony solar" laws in Utah (SB 190), California (AB 1076), and Colorado (HB22-1015) for inspiring the plug-and-play direction

About

DIY 8x3.7 ft planter with adjustable-tilt bifacial solar canopy. Parametric 3D model + sun sim + ASCE 7-22 wind load + smart-folding controller, all driven by a single parameter file.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages