Roast DSL

by Jeff

A roast program says what you'll do to a batch before you do it. Roast DSL is the small language Roast Harness runs and the Aden Experimental models write. A program is three things, in order: the charge, the steps and the drop.

await machine({ id: "aillio-bullet-r1", sensor: "ibts" })
  .charge({ temp: 205, weight: 450 })
  .step({ at: 0, setting: "heater", value: 8 })
  .step({ at: 0, setting: "drum", value: 9 })
  .step({ when: { bean: { above: 160 } }, at: 240, setting: "fan", value: 3 })
  .step({ id: "fc", when: { bean: { above: 196 } }, after: 420, at: 450,
          setting: "heater", value: 5 })
  .step({ from: "fc", after: 30, setting: "fan", value: 5 })
  .drop({ from: "fc", after: 90 })

450 g go into a 205 °C drum. The heater and drum are set at the charge. The fan steps up when the bean reaches 160 °C. The heater eases off at 196 °C, or seven minutes after the turning point if the bean never gets there. The fan opens 30 seconds after that, and the beans drop 90 seconds later: a development time, not a clock time.

The rules

  • Charge first, drop last. Both are required. The same steps on 150 g at 160 °C and on 1 kg at 220 °C are different roasts, so the charge is part of the program.
  • Each step moves one channel, in the machine's own unit: a 0–9 power setting on one roaster, a percentage on another, gas pressure on a third. Settings hold until the next move on the same channel.
  • Triggers follow Artisan's alarms. A step fires at a time after the charge, when a reading crosses a value (the bean, the drum air, the exhaust or their rates of rise), or a set time after another step. Steps can be named, and can wait on or be cancelled by one another, so two steps that cancel each other make a branch.
  • The drop always comes. It can wait for a temperature, but never on a condition that might not arrive. A floor keeps it from coming too early, a late cap ends the roast if the reading never comes, and the machine's own time limit ends any roast.

What it compiles to

Running a program doesn't touch the roaster. It produces a routine, a plain JSON object, and Roast Harness runs the roaster from that. A program can be checked by running it: a channel the machine doesn't have, a missing charge or a drop before a step fails at once, and the error names the valid options:

SequenceError: machine "aillio-r1" has no setting "heat"; valid settings: "drum", "fan", "heater", "prs"

The machine registry behind those checks knows the channels and ranges of 322 roasters.

Why a language

A roast written as code can be read, compared, versioned and shared. It runs the same way every time. And a model can write one that's checked before it ever reaches a roaster.