project
Ghost Hand
Public Unreviewedby John Lauer
A small battery gadget that keeps an automatic sink faucet running while you use the sink: it learns the faucet's timeout, re-arms with the shortest blip, and stops after 10 minutes or when picked up.
main
John Lauer
Rev 2: aim, status-dot lights, Adafruit 1317 battery, rev 2 cost, revisions, firmware
363a22f
1d ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
/*
* board.h : Ghost Hand rev A pin map and board constants
*
* MCU: ATtiny1616-MNR (tinyAVR 1-series, VQFN-20), 3.3 V from a TPS7A0233 LDO.
* Source of truth: design/netlist.json (U1) and design/calcs.json.
*
* VQFN pin | Port | Net | Function used by the firmware
* ---------+------+----------------+--------------------------------------------
* 19 | PA0 | UPDI | programming only
* 20 | PA1 | CHG_STAT | input, MCP73831 STAT via 68k/100k (low = charging)
* 1 | PA2 | ACC_INT1 | input, LIS2DW12 INT1, fully async pin (wakes power-down)
* 2 | PA3 | IR940_G | TCB1 WO (default PORTMUX), 2 x 940 nm emitters
* 5 | PA4 | RX_PWR | GPIO high = TLV9062 receiver and RX_REF bias on
* 6 | PA5 | IR850_G | TCB0 WO (default PORTMUX), 850 nm emitter
* 7 | PA6 | VBAT_SENSE | ADC0 AIN6, VBAT/2 from an always-on 4.7M/4.7M divider
* 8 | PA7 | RX_SIG | AC0 AINP0 (receiver output)
* 14 | PB0 | SCL | TWI0 SCL (default pins), 4.7k pull-up
* 13 | PB1 | SDA | TWI0 SDA (default pins), 4.7k pull-up
* 12 | PB2 | VBUS_SENSE | input, 5 V USB -> 3.0 V, fully async pin
* 11 | PB3 | (unconnected) | input with pull-up (was VBAT_SENSE_EN on the first draft)
* 10 | PB4 | LED_EN_N | output, low = WS2812 rail on (P-FET Q3)
* 9 | PB5 | LED_DIN | output, WS2812B-2020 data (bit-banged)
* 15 | PC0 | ACC_INT2 | input, unused by this firmware
* 16 | PC1 | (unused) | input with pull-up
* 17 | PC2 | ECHO_EVT | EVOUT2 debug output (AC0 event) during a session
* 18 | PC3 | (unused) | input with pull-up
*/
#ifndef BOARD_H
#define BOARD_H
#ifndef F_CPU
#define F_CPU 5000000UL /* OSC20M (20 MHz fuse) / 4 */
#endif
/* ---- PORTA ---- */
#define PIN_CHG_STAT 1 /* PA1 */
#define PIN_ACC_INT1 2 /* PA2 */
#define PIN_IR940_G 3 /* PA3, TCB1 WO */
#define PIN_RX_PWR 4 /* PA4 */
#define PIN_IR850_G 5 /* PA5, TCB0 WO */
#define PIN_VBAT_SENSE 6 /* PA6, ADC0 AIN6 */
#define PIN_RX_SIG 7 /* PA7, AC0 AINP0 */
/* ---- PORTB ---- */
#define PIN_SCL 0 /* PB0 */
#define PIN_SDA 1 /* PB1 */
#define PIN_VBUS_SENSE 2 /* PB2 */
#define PIN_PB3_UNUSED 3 /* PB3, no longer connected */
#define PIN_LED_EN_N 4 /* PB4 */
#define PIN_LED_DIN 5 /* PB5 */
/* ---- PORTC ---- */
#define PIN_ACC_INT2 0 /* PC0 */
#define PIN_PC1_UNUSED 1
#define PIN_ECHO_EVT 2 /* PC2, EVOUT2 */
#define PIN_PC3_UNUSED 3
/* ---- LEDs ---- */
#define LED_COUNT 5 /* chain: 0 = D4 STATUS dot, 1..4 = D5..D8 gauge 25/50/75/100 % */
#define LED_STATUS 0 /* rev 2: the status dot shows what the rev 1 logo showed */
#define LED_GAUGE0 1
/* ---- Accelerometer ---- */
#define ACC_I2C_ADDR 0x19 /* LIS2DW12, SA0 tied high */
/*
* Resting orientation of the device in accelerometer axes (unit vector * 1000 mg).
* The case stands the board so that +Z points up when it sits on the counter.
* If the case puts the board on edge, change this (e.g. X = -1000 for "board
* vertical, IR window facing the faucet"). Only used for the "level-ish" check
* before a session starts; pick-up and tilt are measured against the actual
* orientation captured at set-down.
*/
#define REST_X_MG 0
#define REST_Y_MG 0
#define REST_Z_MG 1000
/* ---- Timing and thresholds (calcs.json "timing") ---- */
#define SESSION_CAP_MS 600000UL /* hard 10 minute cap */
#define SETTLE_STILL_MS 1500U /* still and level for 1.5 s -> session */
#define SETTLE_GIVEUP_MS 15000U /* no set-down within 15 s -> sleep */
#define GAUGE_SHOW_MS 3000U /* charge shown at session start */
#define LISTEN_MS 2000U /* listen with echo disabled */
#define LISTEN_MAX_MS 5000U /* extend if the faucet pulses slowly */
#define STILL_TOL_MG 60 /* still = within 60 mg of the anchor sample */
#define PICKUP_DELTA_MG 250 /* 0.25 g change vs set-down vector -> stop */
#define TILT_LIMIT_DEG 30 /* > 30 deg from rest -> stop / not level */
#define LOW_BATT_MV 3450U /* stop under load below this */
#define NO_LIGHT_BATT_MV 3330U /* below this, do not even light the red LED */
#define BATT_CHECK_MS 10000U /* battery re-check period in a session */
/* ---- Echo limits (calcs.json "echo") ---- */
#define ECHO_EXTRA_US 5 /* echo = faucet pulse + 5 us */
#define ECHO_MIN_US 8
#define ECHO_MAX_US 40 /* emitter limit: <= 40 us */
#define ECHO_DEFAULT_US 25 /* used when no width was measured */
#define ECHO_BLANK_US 150 /* comparator blanking after each echo */
#define ECHO_DUTY_WINDOW_MS 10 /* duty guard window */
#define ECHO_DUTY_MAX_US 200 /* <= 2 % of 10 ms */
#define EMITTER_ROTATE_MS 300 /* 850 / 940 / both rotation ("moving hand") */
#define PULSE_GROUP_MS 3 /* pulses closer than this = one interrogation */
/* ---- Receiver threshold (DAC0 against VREF 1.1 V) ---- */
#define RX_REF_MV 300 /* nominal RX_REF bias */
#define RX_THRESH_OVER_MV 60 /* threshold = noise floor + 60 mV */
#define RX_THRESH_MIN_MV 330
#define RX_THRESH_MAX_MV 1000
#define RX_SETTLE_MS 60 /* RX_REF: 1 uF with ~9 k -> 9 ms tau */
/* ---- LED current cap ----
* WS2812B-2020-V6: 12 mA per colour channel at full scale. Rev 2 lights are
* 1.5 mm clear-resin dots sitting right on each LED, so a dot looks several
* times brighter than rev 1's spread-out logo at the same drive: the levels in
* leds.c are about half of rev 1's, and the cap is 640 (~30 mA for all five)
* to keep the 150 mAh cell's peak load low (0.2 C) and the 3.3 V LDO well
* under its 60 mA budget with the MCU and receiver (about 4 mA). */
#define LED_SUM_MAX 640
#endif