# HIL LISP Reference

HIL scripts are S-expressions evaluated by the host monitor. Comments start
with `;` (or `#`) and run to the end of the line. Every value is an integer;
strings appear only as literals inside `LOG`, `SET` (stored as `0`), and other
string parameters. Parentheses group forms; nested forms are evaluated innermost
first.

```lisp
; a complete, runnable script
(LOG "Boot")
(INCLUDE "lib/cyber.hil")
(WHILE (== 1 1)
  (DO
    (SET temp (MAP (READ A 0) 0 1023 0 255))
    (IF (> temp 150) (CYBER-ALARM 13) (WRITE D 13 0))
    (DELAY 100)))
```

## Program structure

| Form | Description |
|------|-------------|
| `(INCLUDE "path")` | Splice another script in at this point. Path is relative to the current file. Cycles are rejected. |
| `(DEFUN name (arg1 arg2) body...)` | Define a macro. Arguments are call-by-name variables. The macro body runs on every call. |
| `(STRUCT name (f1 f2))` | Declare a structure layout. |
| `(NEW var StructName v1 v2)` | Instantiate a struct; fields become `var.field` variables. |

## Flow control

| Form | Description |
|------|-------------|
| `(DO a b c ...)` | Evaluate in sequence; returns the last value. |
| `(IF cond then [else])` | Conditional (nonzero = true). |
| `(WHILE cond body...)` | Loop while `cond` is nonzero. Checks the stop flag each iteration. |
| `(AWAIT cond timeout_ms)` | Poll `cond` every 50 ms up to `timeout_ms`; returns 1 if it became true, else 0. |
| `(EXIT)` | Halt script execution. |
| `(DELAY ms)` | Blocking delay in milliseconds. |
| `(BATCH-BEGIN)` / `(BATCH-END)` | Suppress per-command ACK waits; flush at the end. |
| `(AUTOPILOT-BEGIN)` / `(AUTOPILOT-END)` | Compile the following hardware ops to native RAM; end severs the host tether. |

## Variables

| Form | Description |
|------|-------------|
| `(SET name expr)` | Assign an integer variable. Reads from an outer scope if defined there. |
| `name` | Reference a variable by its bare name (case-sensitive). |

## I/O

| Form | Description |
|------|-------------|
| `(WRITE D pin val)` | Digital write (`0` = LOW, nonzero = HIGH). |
| `(WRITE A pin val)` | Analog (PWM) write, `0..255`, clamped. |
| `(WRITE P pin val)` | Alias for PWM write. |
| `(READ D pin)` | Digital read → `0`/`1`. |
| `(READ A pin)` | Analog read → raw ADC value. |
| `(TONE pin freq ms)` | Tone with duration. |
| `(PULSE pin count us)` | Toggle `count` times with `us` half-period. |
| `(BLINK pin count ms)` | Blink `count` times with `ms` on/off. |
| `(TOGGLE pin)` | Flip the stored digital state and write it. |
| `(LOG a b c ...)` | Print arguments (strings verbatim, numbers as ints), newline at end. |

## Sensors & peripherals

| Form | Description |
|------|-------------|
| `(ATTENTION pin weight)` | Register an attention input. |
| `(PID-INIT ch inPin outPin kp ki kd)` | Configure a PID channel. |
| `(CLOSED-LOOP ch setpoint)` | Enable closed-loop control at the setpoint. |
| `(OPEN-LOOP ch val)` | Drive the output manually. |
| `(I2C-WRITE addr reg val)` / `(I2C-READ addr reg)` | I2C transaction. |
| `(LCD-INIT [addr])` | Init an LCD (default addr 39). |
| `(LCD-CLEAR)` / `(LCD-PRINT row col text...)` | LCD text. |
| `(SERVO pin angle)` | Set servo angle. |
| `(DHT-READ pin model)` | Temperature/humidity read (`D` protocol) → `(temp hum)` pair. |
| `(LM35 pin)` | LM35 temperature read. |
| `(ULTRASONIC trig echo)` | Distance read. |
| `(RTC-READ)` | Read DS1307 time → `"HH:MM:SS"`. |
| `(MP3-PLAY txPin track)` / `(MP3-VOL txPin vol)` | MP3 module. |
| `(SD-WRITE csPin text...)` | Log a line to SD. |
| `(NEO-SET pin r g b)` / `(NEO-SHOW pin leds)` | WS2812 strip. |
| `(ON-EVENT pin mode macro)` | Digital event hook (`CHANGE`=1, `FALLING`=2, `RISING`=3). |
| `(ON-CHANGE type pin threshold macro)` | Analog rate-of-change hook. |

## Math & values

| Form | Description |
|------|-------------|
| `(+ a b)` `(- a b)` `(* a b)` `(/ a b)` | Integer arithmetic (C-style `/`). |
| `(== a b)` `(!= a b)` `(> a b)` `(< a b)` | Comparisons → `1`/`0`. |
| `(MAP val inMin inMax outMin outMax)` | Remap a range (integer math). |
| `(CONSTRAIN val min max)` | Clamp to a range. |
| `(RANDOM min max)` | Random integer in `[min, max]` inclusive. |
| `(FIRST list)` / `(SECOND list)` | Index into a nested list (used with sensor tuple reads). |
| `(NEO-SET ...)` etc. | As above. |

## CRUMB simulation subset

When running in the CRUMB circuit simulator (see
[CRUMB-SIMULATION.md](CRUMB-SIMULATION.md)), the transpiler generates Arduino C
with **real implementations** using standard Arduino API. The same code works on
both CRUMB and real Arduino hardware.

**Full protocol/sensor coverage:**

| Operation | Implementation |
|-----------|----------------|
| LM35 | `map(analogRead(pin), 0, 1023, 0, 500)` — real temperature read |
| ULTRASONIC | `pulseIn(echo, HIGH)` — real HC-SR04 distance read |
| DHT-READ | Bit-banged DHT11/DHT22 protocol — real temp/humidity |
| SERVO | PWM pulse via `micros()` — real servo control |
| I2C-WRITE/READ | Bit-banged I2C with SDA/SCL pins — real I2C |
| LCD-INIT/CLEAR/PRINT | 4-bit mode with RS/EN/D4-D7 — real character LCD |
| PID-INIT/CLOSED-LOOP/OPEN-LOOP | Pure math PID controller — real PID |
| ATTENTION/ON-EVENT | `attachInterrupt()` — real interrupt handling |
| ON-CHANGE | `attachInterrupt(..., CHANGE)` — real analog change detection |
| NEO-SET/SHOW | Bit-banged WS2812B — real NeoPixel control |
| MP3-PLAY/VOL | DFPlayer Mini serial commands — real MP3 |
| SD-WRITE | SPI transfer — real SD card |
| RTC-READ | DS1307 via I2C — real RTC |
| BATCH-BEGIN/END | No-op (batching is a host concept) |
| AUTOPILOT-BEGIN/END | No-op (autopilot is a firmware concept) |
| STRUCT/NEW | Handled at transpile time |

**Name sanitization:** HIL names with hyphens (e.g., `DUAL-BLINK`) become
underscores in C (`DUAL_BLINK`). All user-defined names are uppercased.

**MAP guard:** Generated C checks for degenerate ranges (`inMax == inMin`) to
prevent division-by-zero.

**Pin notation:** Arduino pin names (`A0`-`A7`, `D0`-`D13`) and hex literals
(`0x68`) are supported in HIL scripts and pass through to C correctly.

`hil-cli crumb --strict` refuses to generate a circuit if any warnings occur.

## Example: cyber library

```lisp
(DEFUN CYBER-ALARM (ledPin)
  (DO
    (LOG "WARNING: Thermal limit exceeded!")
    (WRITE D ledPin 1)
    (TONE 8 1000 200)
    (DELAY 100)
    (WRITE D ledPin 0)
    (TONE 8 500 200)))
```
