contains firmwares and PCB layouts for my own homebrew homebrew setup
  • C 63.7%
  • Python 13.6%
  • C++ 7.9%
  • TypeScript 4.6%
  • JavaScript 4.1%
  • Other 6.1%
Find a file
Bruce 32509040fa ha release ha-20260902-1602
Config release for the homelab ha-brewery role to pin.
Source commit 7d227ee.
2026-09-02 16:02:48 +08:00
boil_controller build: keep HA release tags out of the firmware version 2026-08-27 20:54:01 +08:00
brewery_common brewery_common: reap idle netlog clients so slots can't leak 2026-08-22 22:30:18 +08:00
homeassistant ha release ha-20260902-1602 2026-09-02 16:02:48 +08:00
old Brewery monorepo: temp probe + boil controller + shared component 2026-06-28 17:03:03 +08:00
recipes recipes: log the trub dump, dry hop and terminal gravity 2026-08-30 05:41:55 +08:00
stand_alone_temp_probe tilt: restore force_update, keep retain off 2026-08-27 23:28:59 +08:00
tilt_sensor tilt_sensor: 915 MHz LoRa link test using two Wio-E5 minis 2026-08-26 17:04:05 +08:00
tools tools: publish Home Assistant config as a versioned release 2026-08-27 14:44:56 +08:00
.gitignore gitignore: never commit .env 2026-08-26 11:51:26 +08:00
CLAUDE.md docs: point CLAUDE.md at the recipes and brew logs 2026-08-30 05:41:55 +08:00
README.md tilt_sensor: new XIAO nRF52840 hydrometer project 2026-08-26 11:18:05 +08:00

Brewery control system

Monorepo of firmware and hardware for an automated HERMS brewery, built around a small fleet of networked temperature probes feeding a central heating controller — all coordinated over MQTT (with Home Assistant discovery, so the whole rig shows up in HA automatically).

Layout: stand_alone_temp_probe/ (RTD probe firmware), boil_controller/ (HERMS/boil controller firmware), brewery_common/ (shared ESP-IDF component: OTA, firmware version-check, network log tap — used by both firmwares via a component-manager path dependency), old/ (reference designs + the original KiCad schematic), tools/ (build helper), homeassistant/ (operator dashboard, brew phases and the fermenter dose controller), recipes/ (BeerXML exports), tilt_sensor/ (BLE hydrometer — nRF52840, Arduino toolchain, in progress).

            ┌──────────────────────┐
   HLT  ───▶│ temp probe (RTD)     │──┐
   mash ───▶│ temp probe (RTD)     │──┤   mechination/<id>/temperature
   boil ───▶│ temp probe (RTD)     │──┤      (WiFi, retained, >=1 Hz)
   …    ───▶│ temp probe (RTD)     │──┤
            └──────────────────────┘  │
                                      ▼
                             ┌─────────────────┐
                             │   MQTT broker   │  (+ Home Assistant)
                             └─────────────────┘
                                      │ subscribes to the probe temps
                                      ▼
                       ┌────────────────────────────┐
                       │  boil / HERMS controller   │  ESP32-WROVER
                       │  PID ──▶ ULN2003A ──▶ SSRs │
                       └────────────────────────────┘
                               │            │
                           HLT element   Boil-kettle
                          (heats HERMS)    element

The firmwares we host here

1. stand_alone_temp_probe/ — temperature probe

Seeed XIAO ESP32-C6 + Adafruit MAX31865 RTD amplifier. Reads a PT100/PT1000 RTD, joins WiFi, and publishes its temperature to MQTT (retained, ≥ 1 Hz) with Home Assistant discovery and RTD-fault reporting. One probe per measured point — give each a distinct device id (hlt, herms_out, boil_kettle, …) so they share the MQTT tree without colliding.

Note: the firmware is WiFi + MQTT (see main/temp_probe.h, idf_component.yml). The probe's own README.md still describes an earlier Zigbee revision and is being updated — trust the WiFi/MQTT build.

2. boil_controller/ — boil / HERMS controller

ESP32-WROVER + ULN2003A + 2× mains SSR. Subscribes to the probes' MQTT temperatures, runs a PID loop per heated vessel, and drives the SSRs with time-proportional output:

  • HERMS channel — modulates the HLT element to hold the mash temperature (wort recirculating out of the HERMS coil), with an HLT over-temp cutout.
  • Boil channel — drives the boil-kettle element to a setpoint.

The first two ULN2003A channels drive the SSRs; the other five are broken out for future use (pumps, alarm, …). It supports OTA updates over MQTT so we can iterate without touching the hardware. See boil_controller/README.md.

3. tilt_sensor/ — BLE hydrometer (in progress)

Seeed XIAO nRF52840 Sense. A homebrew Tilt clone: a sealed float in the fermenter that derives specific gravity from its inclination (LSM6DS3TR-C IMU) and BLE-broadcasts gravity plus temperature.

The odd one out in this repo — Arduino / arduino-cli, not ESP-IDF, and BLE, not WiFi/MQTT, so it does not use brewery_common. Bridging its beacon into MQTT/HA is a later job for the HA side. Right now it holds one bring-up sketch, battery_test, for validating the cell and the BQ25101 charge path. See tilt_sensor/README.md.

Temperature sensors

Each is an independent standalone probe publishing to MQTT. Planned points:

Probe device id Measures Used by the controller for
hlt Hot Liquor Tank HERMS over-temp cutout
herms_out MLT output / mash temp off the HERMS coil HERMS PID process variable
boil_kettle Boil kettle Boil PID process variable
(more) e.g. mash-in, ambient future channels / logging

Topic convention (shared prefix): mechination/<device_id>/temperature, with availability at mechination/<device_id>/status and a per-reading …/temperature/status that goes offline on an RTD fault (so the controller can fail-safe).

old/ — previous designs (reference only)

The earlier brewery was Bluetooth-based and is superseded by the WiFi/MQTT design above. Kept for reference:

  • old/brewery-circuit/the original controller schematic + PCB (Brewery.kicad_pcb, KiCad/2019): ESP32-WROVER + ULN2003A + LTC2986. This is where the boil-controller pinout came from.
  • old/ssr/ — BLE-central SSR firmware (an ESP-IDF BLE example, not the real app)
  • old/hw/SSR/ — single-channel SSR PCB (PN2222A driver + opto) — still useful HW
  • old/hw/Temp_probe/ — temp-probe PCB
  • old/temp_sensor/ — MicroPython MAX31865 driver
  • old/zephyr_temp/ — Zephyr RTOS temperature app
  • old/web/ — the old Web-Bluetooth brewery dashboard

The old boil-controller firmware was not recovered (nothing referencing the LTC2986 exists under ~/dev, and the old system was Bluetooth-based), so boil_controller/ is a clean rebuild. The hardware schematic survives in old/brewery-circuit/, and the controller pinout is taken from it:

Function GPIO
SSR — HERMS/HLT (ULN OUT6) GPIO26
SSR — Boil (ULN OUT7) GPIO27
Spare ULN outputs (OUT35) GPIO32/33/25
LTC2986 SPI (unused) MOSI 23, MISO 19, SCLK 18, CS 5, DRDY 4

⚠️ The board's first two ULN channels (DO1/DO2) land on GPIO34/35, which are input-only — they can't drive outputs. The unit as built wires the SSRs to OUT6/OUT7 (GPIO26/27); any of DO3..DO7 would do. The LTC2986 is dropped from the design (temps come over MQTT) but is held quiescent in firmware since it's still populated on the board. Details in boil_controller/README.md.

Building

Both firmwares are independent ESP-IDF projects (ESP-IDF ≥ 5.3; tested with the installed ~/esp/esp-idf). Build each from its own directory:

. ~/esp/esp-idf/export.sh

# Temperature probe (XIAO ESP32-C6)
cd stand_alone_temp_probe
idf.py set-target esp32c6
idf.py menuconfig          # WiFi, MQTT, device id, RTD type/pins
idf.py -p <PORT> flash monitor

# Boil controller (ESP32-WROVER)
cd ../boil_controller
idf.py set-target esp32
idf.py menuconfig          # WiFi, MQTT, sensor topics, ULN GPIOs, PID gains
idf.py -p <PORT> flash monitor

The two boards target different chips (esp32c6 vs esp32), so keep their build trees separate (each has its own sdkconfig). A convenience script is at tools/build.sh:

./tools/build.sh both        # build both
./tools/build.sh probe       # just the temp probe
./tools/build.sh boil flash  # build + flash the controller (auto-detects port)

After the controller's first USB flash, subsequent updates can go over the air — see boil_controller/README.md.