Documentation
FermPilot docs
Reference for the fermentation model, sensor integrations, milestone alerts, and REST API.
Getting started
FermPilot runs a kinetic fermentation simulation from four inputs: yeast strain, original gravity (°Plato), fermentation temperature (°C), and pitch rate (million cells/mL/°P). The simulation outputs a time series covering gravity, ABV, yeast biomass, and VDK (diacetyl precursor) at hourly resolution.
No calibration is required to use the built-in strains. To get started:
- Go to Simulate in the sidebar.
- Select a yeast strain, enter your original gravity and fermentation temperature.
- If you have a known pitch rate, enter it directly. Use the Calculate from cells & volume expander if you need to convert from cell count or slurry volume.
- Click Run Simulation.
The result page shows three decision outputs immediately: cold crash timing, diacetyl rest window, and terminal gravity. Charts for all four variables are below those.
You can also try the simulator without an account at fermpilot.com/tools/fermentation-calculator. Results are not saved on the public tool.
The model
FermPilot solves a system of four coupled ordinary differential equations at each time step using fourth-order Runge-Kutta (RK4) integration with a 2-hour step size. The variables are substrate (S, °Plato), ethanol (P, %ABV), yeast biomass (X, g/L), and VDK precursor (V, mg/L).
Four coupled ODEs
The specific growth rate follows the Monod model with ethanol inhibition:
μ = μ_max × S/(K_s + S) × (1 − P/P_m)^n
The four state equations:
dS/dt = −(1/Y_xs) × μ × X dP/dt = (Y_ps/Y_xs) × μ × X dX/dt = (μ − k_d) × X dV/dt = α × (dX/dt) + β × X − γ × X × V
The VDK equation (Leudeking-Piret) couples formation to growth rate (α term) and non-growth-associated production (β term), with reabsorption driven by viable yeast contact (γ term). This captures the characteristic VDK peak during exponential growth followed by decline during conditioning.
Temperature correction uses the Arrhenius relationship. When a temperature profile is provided, the rate constants are adjusted at each step based on the active temperature.
Strain parameters
Each built-in strain has its own Monod constants (μ_max, K_s, P_m, n) and VDK kinetic parameters (α, β, γ, k_d). These are sourced from:
- Boulton & Quain, Brewing Yeast and Fermentation, Blackwell Science (2001)
- Casey & Ingledew, CRC Critical Reviews in Microbiology 13:219–280 (1986)
- Verbelen et al., Biotechnology Letters 28:1515–1525 (2006)
Built-in strains: US-05/WLP001/WY1056, S-04, WY1968/WLP002, WLP530, W-34/70/WLP830, S-23, WLP351, BE-256, WLP568, W-13/WLP037, Nottingham, WY1318/WLP022, Voss Kveik.
Pro accounts can add custom strains. Monod constants can be entered manually from supplier data or calibrated from your own batch history using the Nelder-Mead optimizer (Settings → Strains → Calibrate).
Pitch rate
Pitch rate sets the initial biomass X₀ in the simulation. A higher pitch rate reduces lag phase and results in faster gravity drop; a lower pitch rate extends lag phase and typically increases ester production. Standard targets are 0.75–1.0 M/mL/°P for ales and 1.0–1.5 M/mL/°P for lagers.
Use the inline pitch rate calculator (expand Calculate from cells & volume in the simulate form) to convert cell count plus batch volume to M/mL/°P. It accepts liquid packets, harvested slurry, and dry yeast, and applies date-based viability models. Standalone version: fermpilot.com/tools/pitch-rate-calculator.
Reading results
Gravity curve
The gravity chart shows predicted °Plato over the simulation window. The curve follows a sigmoid shape: slow initial drop during lag phase, rapid drop during exponential growth, then a plateau as substrate is exhausted and ethanol inhibition suppresses further growth.
Terminal gravityis marked with a dashed vertical line at the hour the model predicts substrate effectively exhausted (gravity change <0.01 °P per 6 hours). This is the earliest point at which cold crashing is safe.
The grey band visible in some simulations represents the ±5% confidence region from parameter uncertainty. Actual terminal gravity will depend on water chemistry, mash efficiency, and yeast health factors not captured in the kinetic model.
Diacetyl (VDK)
The VDK chart shows the predicted vicinal diketone concentration in mg/L. The curve peaks during exponential growth (when α × dX/dt is largest) then declines as yeast reabsorb the precursor during conditioning.
Rest start hour: the point at which VDK begins its decline. Raising temperature at this hour accelerates reabsorption.
Predicted clearance: the hour at which VDK drops below 0.10 mg/L (the standard packaging threshold) after a diacetyl rest is initiated at rest start temperature. The clearance prediction runs a separate phase simulation at rest temperature with biomass held at the rest-start value.
Lager strains are more sensitive to residual diacetyl. If brewing a lager and the predicted clearance is close to your packaging deadline, lower the threshold to 0.05 mg/L or take a forced diacetyl test.
Cold crash timing
The decision card shows the predicted number of hours until terminal gravity is reached from simulation start (pitch time). If you entered a brew date, the card converts this to an absolute date and time.
Cold crash can begin once: (1) terminal gravity is predicted, and (2) VDK is below your packaging threshold. In practice, confirm with two gravity readings 24 hours apart before crashing.
Actual vs. predicted
On any saved run, you can log actual gravity readings. Navigate to the run detail page and use the Log a reading form, or connect a Tilt or iSpindel sensor to push readings automatically.
Actual readings appear as orange dots on the gravity chart, overlaid on the predicted curve. Divergence between actual and predicted is normal on the first few batches with a strain. After 3–5 batches, the pattern of divergence tells you whether to adjust your pitch rate or temperature, or whether to calibrate a custom strain.
Milestone alerts
FermPilot can send email alerts when fermentation milestones are predicted to occur. Three alert types are supported:
- VDK rest start: sent at the predicted hour to begin the diacetyl rest
- VDK clear: sent when VDK is predicted below packaging threshold after rest
- Terminal gravity: sent when the gravity curve converges
To enable alerts for a run:
- Enter a brew date in the simulate form. This anchors the prediction to wall-clock time.
- Check Email me when milestones are reached.
- Enter the email address to send alerts to.
- Run the simulation. Alerts are scheduled at creation time from the predicted hour offsets.
Alerts are delivered within one hour of the predicted milestone time. They are sent once and not retried. If the fermentation runs faster or slower than predicted, the timing will be off. Use actual vs. predicted tracking to catch large divergences early.
Sensor setup
FermPilot accepts live gravity readings from Tilt Hydrometers and iSpindel devices via HTTP POST. Both use the same API key for authentication.
1. Generate your API key
- Sign in and go to Settings.
- Under Tilt Integration, click Generate API key.
- Copy the key immediately. It is shown once and not stored.
The key format is fp_ followed by 64 hex characters. One key covers both Tilt and iSpindel endpoints. To rotate the key, click Regenerate: the previous key is invalidated immediately, so update your device configuration first.
2. Tilt Hydrometer
The Tilt broadcasts gravity and temperature via Bluetooth. A bridge device (Tilt Pi, phone app, or custom script) forwards readings to FermPilot via HTTP POST.
Tilt Pi configuration
Edit /home/pi/tiltbridge/settings.ini and set:
[cloud] url = https://fermpilot.com/api/runs/YOUR_RUN_ID/tilt headers = Authorization: Bearer fp_YOUR_API_KEY
Find your run ID in the URL when viewing a simulation: /simulate/clx9abc123def456 → run ID is clx9abc123def456. The pre-filled URL is also shown in the Connect a sensor panel on the run detail page.
Restart the bridge after editing: sudo systemctl restart tiltbridge
Request format
POST /api/runs/{runId}/tilt
Authorization: Bearer fp_YOUR_API_KEY
Content-Type: application/json
{ "SG": 1.045, "Temp": 68 }SG is required (specific gravity, 0.980–1.200). Temp is optional (°F). Additional Tilt fields (Color, Beer) are accepted and ignored.
Tilt cloud CSV import
- Open your Tilt Cloud spreadsheet → File → Download → CSV.
- In FermPilot, open the run → expand Connect a sensor.
- Under Tilt Hydrometer, click Choose CSV file.
The import reads SG and DateTime columns. Duplicate timestamps are skipped.
3. iSpindel
The iSpindel is an open-source Wi-Fi floating hydrometer that measures gravity via tilt angle. It can POST to any HTTP endpoint on your local network or the internet.
iSpindel configuration
In the iSpindel web interface (http://iSpindel.local or the device IP), go to Configuration and set:
| Service type | HTTP |
| Server | fermpilot.com |
| Port | 443 |
| URL | /api/runs/YOUR_RUN_ID/ispindel |
| Token | fp_YOUR_API_KEY |
| HTTPS | Enabled |
The Token field maps to the Authorization: Bearer header. Interval can be set to whatever polling frequency you prefer. 15 minutes is typical.
Request format
POST /api/runs/{runId}/ispindel
Authorization: Bearer fp_YOUR_API_KEY
Content-Type: application/json
{
"name": "iSpindel000",
"gravity": 1.045,
"temperature": 20.1,
"temp_units": "C",
"angle": 25.3,
"battery": 4.04,
"RSSI": -76
}gravity is required (SG, 0.980–1.200). temperature and temp_units are optional. All other iSpindel fields are ignored.
FAQ
Why does the predicted terminal gravity differ from my actual FG?
The Monod model uses published kinetic parameters for each strain. Real-world attenuation depends on wort fermentability (mash temperature, adjunct ratio, enzyme activity), yeast health, dissolved oxygen at pitch, and water chemistry; none of which the model has visibility into. First-batch predictions are approximate. After 3–5 batches with a strain, use the Nelder-Mead calibration tool to fit parameters to your actual data.
Can I model a step mash or multi-temperature fermentation?
Pro and Brewery accounts can define a temperature profile with up to 6 stages. Each stage specifies a start hour and temperature in °C. The engine corrects growth rates using the Arrhenius relationship at each step. Add stages using the + Add stage button in the simulate form.
What does the VDK clearance prediction assume?
The clearance hour is computed by simulating a rest phase starting at the predicted rest start hour. During the rest phase, growth is set to zero (yeast are flocculating) and only the reabsorption term (γXV) is active. This assumes the diacetyl rest temperature is sufficient to keep yeast metabolically active. Below 15 °C, reabsorption slows significantly and the prediction will be optimistic.
My Tilt/iSpindel readings show 201 OK but nothing appears on the chart.
The chart polls for new readings every 10 seconds. If readings arrive but do not appear: (1) confirm the run ID in the webhook URL matches the run you have open. Each run has its own ID; (2) check that the reading timestamp is within the simulation window (readings before the brew date or after the simulation end are stored but not plotted); (3) hard-refresh the page.
How many generations can I repitch before calibrating?
The built-in parameters assume a healthy first-generation pitch. Viability and vitality decline with each generation, and the Monod constants shift as the yeast population adapts to your house conditions. Most brewers see meaningful drift after 5–8 generations. Run a calibration after generation 4 and again after generation 8 to keep predictions accurate.
Does FermPilot integrate with Brewfather?
Yes. Go to Settings → Brewfather and add your API key. In the simulate form, click Import from Brewfather to pull batch parameters (OG, strain, temperature) directly from a Brewfather batch.
What is the difference between Pro and Brewery tiers?
Pro adds unlimited simulations, custom strain library, temperature profiles, batch comparison, CSV export, and milestone email alerts. Brewery adds multi-vessel dashboard (all tanks at a glance), up to 10 team seats, and REST API access for integration with a LIMS or ops tools. See the pricing page.
API reference
The FermPilot REST API uses JSON. All endpoints are HTTPS only.
Base URL: https://fermpilot.com
Authentication
Two mechanisms depending on the endpoint:
| Mechanism | Endpoints | How to pass |
|---|---|---|
| API key | /tilt, /ispindel | Authorization: Bearer fp_... |
| Session cookie | All other endpoints | Automatic (browser) |
Unauthenticated requests return 401. Requests to runs not owned by the authenticated identity return 404. Ownership is not revealed.
/api/runs/{runId}/tiltPush a gravity reading from a Tilt bridge. Auth: API key.
| Parameter | Type | Required | Description |
|---|---|---|---|
| SG | number | Yes | Specific gravity, e.g. 1.045. Range: 0.980–1.200. |
| Temp | number | No | Temperature in °F. Stored in reading notes. |
// 201 Created
{ "ok": true, "readingId": "clxr9mn234pqr567" }| Status | Error | Description |
|---|---|---|
| 400 | SG is required | Missing or non-numeric SG field. |
| 400 | SG out of range (0.980–1.200) | SG outside valid beer gravity range. |
| 401 | Unauthorized | API key missing, malformed, or not found. |
| 404 | Run not found | runId does not exist or is not owned by this key. |
/api/runs/{runId}/ispindelPush a gravity reading from an iSpindel. Auth: API key. Same key as the Tilt endpoint.
| Parameter | Type | Required | Description |
|---|---|---|---|
| gravity | number | Yes | Specific gravity in SG units, e.g. 1.045. Range: 0.980–1.200. |
| temperature | number | No | Temperature value. Unit specified by temp_units. |
| temp_units | string | No | "C" or "F". Defaults to "C" if omitted. |
| name | string | No | Device name. Stored in reading notes. |
// 201 Created
{ "ok": true, "readingId": "clxr9mn234pqr567" }| Status | Error | Description |
|---|---|---|
| 400 | gravity must be a specific gravity value... | Missing, non-numeric, or out-of-range gravity field. |
| 401 | Unauthorized | API key missing, malformed, or not found. |
| 404 | Run not found | runId does not exist or is not owned by this key. |
/api/runs/{runId}/readings/importBulk import from JSON array or Tilt cloud CSV. Auth: session cookie. Duplicates skipped.
JSON body (Content-Type: application/json)
{
"readings": [
{ "recordedAt": "2024-01-15T10:00:00Z", "sg": 1.053 },
{ "recordedAt": "2024-01-15T10:30:00Z", "sg": 1.051 }
]
}CSV body (Content-Type: text/csv)
Tilt cloud export format. Required columns: SG, DateTime.
// 200 OK
{ "inserted": 2, "skipped": 0 }/api/runs/{runId}/readingsAll gravity readings for a run, ordered by recordedAt ascending. Auth: session.
[
{
"id": "clxr9mn234pqr567",
"runId": "clx9abc123def456",
"recordedAt": "2024-01-15T10:00:00.000Z",
"gravityPlato": 13.27,
"notes": "Tilt: 68°F",
"createdAt": "2024-01-15T10:00:01.123Z"
}
]Key management
/api/account/tilt-keyCheck if a key exists. Returns { "configured": true }. Does not return the key value.
/api/account/tilt-keyGenerate a new key. Invalidates any existing key. Returns { "key": "fp_..." } once. Copy before closing.
/api/account/tilt-keyRevoke the current key. Returns { "ok": true }.
FermPilot · fermentation planning for serious brewers. Free tools · Sign in