# AshLight Rev B — design calculations

Generated by `hardware/design.py` from the constants at the top of that file. Change a
constant, re-run, and this page, the BOM and the netlists all update together.

## LED current sinks

Each of the four vertical zones has its own linear current sink: MCP4728 DAC → resistor
divider → TLV9002 op-amp → AO3400A → 10 Ω sense resistor. The LED current is a true DC
current set by an analog voltage. There is **no PWM anywhere in the LED path**, so steady
mode has no flicker by construction.

| Quantity | Value |
|---|---|
| DAC full scale (internal ref, gain 1) | 2.048 V |
| Divider 47 k / 10 k → set voltage full scale | 359.3 mV |
| Sense resistor | 10 Ω (1 %) |
| **Full-scale current per zone** | **35.9 mA** |
| LEDs per zone (parallel, each with 22 Ω ballast) | 6 |
| Current per LED at full scale | 5.99 mA (rated 250 mA, so very lightly driven) |
| DAC resolution at the sink | 8.8 µA per code |

### Brightness ladder and runtime

Five levels on a perceptual (roughly geometric) ladder rather than the render's linear
10/25/50/75/100 %: the eye responds to ratios, and a sleep light needs a truly dim
bottom step. The table lives in firmware (`al_levels` in `firmware/src/ashlight_core.c`) and can be retuned without hardware changes.

Runtime uses a 5000 mAh 21700, 90 % usable to the 3.10 V
cutoff (4500 mAh), plus 4.0 mA electronics overhead while on.
A linear sink draws the same current from the cell as flows through the LEDs. Lumens use the Lumileds typical
32 lm @ 200 mA scaled linearly, which is conservative because these LEDs are more efficient at low current.

| Level | % of full | mA / zone | mA / LED | Total LED mA | Est. lumens | Est. runtime |
|---|---|---|---|---|---|---|
| 1 | 3 % | 1.08 | 0.18 | 4.3 | 0.7 lm | **541 h** (22.6 days) |
| 2 | 8 % | 2.87 | 0.48 | 11.5 | 1.8 lm | **290 h** (12.1 days) |
| 3 | 20 % | 7.19 | 1.20 | 28.7 | 4.6 lm | **137 h** (5.7 days) |
| 4 | 45 % | 16.17 | 2.69 | 64.7 | 10.3 lm | **66 h** (2.7 days) |
| 5 | 100 % | 35.93 | 5.99 | 143.7 | 23.0 lm | **30 h** (1.3 days) |

Candle mode averages about 84 % of the selected level (bottom zone 97 %, flame tip 69 %), so it runs slightly longer than steady mode at the same level.

### Headroom (will the current hold up as the battery drains?)

Worst case at the 3.10 V cutoff, full current, max LED Vf:
`Vf_max 2.25 + ballast 132 mV + sense 359 mV + FET 100 mV + P-switch 20 mV = 2.86 V`
→ **+0.24 V margin** at cutoff. The current stays regulated over the
whole usable battery range, so the brightness does not sag as the battery empties.

### Op-amp offset at the lowest level

TLV9002 offset is 0.4 mV typ / 1.6 mV max. At level 1 the set voltage is
10.8 mV, so worst-case zone-to-zone mismatch is about
±15 % (typically ±4 %). Behind the diffuser this reads as a slight
vertical gradient, not as flicker. If the samples show visible banding, firmware can store a
per-zone trim in EEPROM at end-of-line test.

## Thermal

| Element | Dissipation |
|---|---|
| Each sink FET, on USB power (4.4 V), full current | 70 mW |
| Each sink FET, full battery (4.2 V), full current | 63 mW |
| All four sinks, worst case | 282 mW |
| LEDs at full current | 280 mW |
| bq24073 while fast charging (5.0 V in, 3.2 V cell) | 1.33 W peak, falling as the cell charges |

The charger is the hottest part. Charge current is deliberately set to 742 mA
(0.14 C) rather than the chip's 1.5 A maximum, which keeps the base cool in a plastic
enclosure. The bq24073 also has internal 125 °C thermal regulation, and firmware pauses charging when the
RP2040 die (next to the charger) passes 60 °C.

## Charging

| Parameter | Setting |
|---|---|
| Fast charge (R_ISET 1200 Ω) | 742 mA |
| Termination | 10 % (74 mA), ITERM open |
| Input limit, default (EN2=0, EN1=1) | 475 mA typ, VIN-DPM active (safe on any USB port) |
| Input limit when the C source advertises ≥1.5 A on CC (EN2=1, EN1=0) | 1464 mA |
| Safety timer (R_TMR 68.0 k) | 9.1 h fast charge, 54 min pre-charge |
| Charge temperature window (cell NTC on flex) | 0–50 °C (bq24073 TS: 75 µA bias, 0.3 V hot / 2.1 V cold) |
| Full charge from empty | ≈ 7.8 h on a 1.5 A+ charger; ≈ 13 h on a 500 mA USB port |

USB-C CC thresholds read by the MCU (5.1 k Rd): default 0.25–0.61 V, 1.5 A 0.70–1.16 V,
3 A 1.31–2.04 V. The higher input limit is enabled only after the source advertises
1.5 A or more.

## Battery protection and standby

| Event | Threshold | Who acts |
|---|---|---|
| Low-battery warning | 3.40 V (loaded) | firmware: one soft double-dip, then caps brightness at level 3 |
| Lamp cutoff | 3.10 V for 2 s | firmware: fades out, latched until USB or > 3.45 V |
| Over-discharge | 2.4 V | DW01A + 2 × AO3400A disconnect the cell |
| Over-charge | 4.3 V | DW01A (backup; charger terminates at 4.2 V) |
| Over-current / short | ~2.5 A / instantaneous | DW01A (150 mV across ~60 mΩ) |

Standby drain with the lamp off: the 3.3 V rail is switched off by the soft latch, so the
RP2040, flash, DAC, op-amps, reference and battery divider draw nothing. What remains is LDO
shutdown <1 µA + DW01A 3 µA + bq24073 battery leakage ~6 µA ≈ **10 µA**. Over a year that's about
90 mAh, under 2 % of the cell. The cell's own self-discharge is larger.

## Power latch

The button switches SYS (battery) onto BTN_HI. Through D1 that raises the RT9013 enable, the
3.3 V rail comes up, the RP2040 boots in a few milliseconds and drives PWR_HOLD high through D2 to
keep itself powered. USB VBUS also enables the rail through D1, so the lamp is always programmable
and can acknowledge charging when plugged in. When the lamp is off and nothing is pending, firmware
releases PWR_HOLD and everything downstream of the LDO shuts down. Q8 level-shifts BTN_HI to the
3.3 V GPIO so the same button press is also read as a normal input.
