How the open-lead detector firmware works — and how to reconfigure a shipped unit over USB serial without touching the code.
The Blinky Hawk continuously answers one question: are my leads actually connected to anything? It flashes and beeps for continuity, warns on live voltage, and every behavioral knob — thresholds, beeper, timings — is stored in on-chip EEPROM and adjustable with plain-text serial commands.
This manual describes a hardware revision 3 board, which is what every current unit is. Revision 2 differs in two visible ways — it has DIP switches, and its buzzer is wired differently — and exactly one V2 unit exists. If you have it, read the appendix; everything else here applies to both.
The firmware loops continuously and classifies the test leads into one of three states:
| State | Meaning | LED | Speaker |
|---|---|---|---|
| FLOATING (open) | The leads are not connected to anything (or to a resistance above the active threshold). | Dim blue flash, max 1 Hz | Silent |
| CLOSED (continuity) | The leads see a low resistance path. | Green flash, max 2 Hz | Beep on contact, then a repeat beep every second while held (configurable) |
| VOLTAGE | An external voltage is present across the leads. The open/closed test is bypassed for safety. | Red flash, max 2 Hz | Double beep on detection (configurable) |
While charging over USB the normal alerts are replaced by a slow dim red blink (green once the battery reads full), and at power-on the LED blinks green 1–4 times to show the battery level in quarters.
The analog front end measures a pseudo-differential voltage: the positive
sense input is sampled by the RA4M1's 14-bit ADC and compared against a reference. (By
default the negative side is a fixed 1.25 V pseudo-reference — see
NEGFIX/NEGV — rather than a live second ADC read.) At rest the
differential sits near 0 V, so everything works on the magnitude of the deviation
from zero.
Each pass of the main loop runs this sequence:
VOLTFAST × REFBAND from
REFCENTER, voltage is declared immediately. Otherwise
VOLTAVG readings are averaged and compared against the
REFBAND window. Voltage present → state = VOLTAGE, and the MOSFET test
is skipped entirely.TESTAGREE times in a row before it counts.STABLECOUNT consecutive passes before the LED/speaker switch to it, so a
single noisy reading can't make the alerts stutter.When the MOSFET switches off, the positive sense node is briefly pulled away from its resting
point and then recovers back toward zero differential. How fast it recovers
depends on the impedance across the leads — a dead short snaps back quickly, an open lead
drifts back slowly. The DETMETHOD key selects how that recovery is turned into a
single number to compare against the threshold. In every method a larger metric means
more open, so the threshold table and the comparison work the same way for all
three:
DETMETHOD | Metric | Threshold units | How it works |
|---|---|---|---|
| 0 — single | |differential| | volts | The original method: take one differential reading SETTLEPREUS µs
after the MOSFET switches off, and threshold its magnitude. |
| 1 — time-to-return | recovery time | milliseconds | Sample the recovery and measure how long the differential takes to fall back within
DETBAND of the resting center. Longer = more open. Naturally tolerant of
unit-to-unit gain differences, since it measures a time constant rather than an
amplitude. |
| 2 — area | tail integral | volt·milliseconds | Integrate the magnitude of the recovery from DETAREAUS µs to
DETWINUS µs. Larger area = more open. The tail window skips the
large initial dip (which is nearly identical for every lead condition). |
The threshold units change with the method. Because
THRESH00…THRESH11 are compared directly against the metric, switching
DETMETHOD changes what those four numbers mean (volts, milliseconds, or
volt·milliseconds). After changing the method you must re-tune the threshold values for the
new units, then !SAVE. The unit ships on method 1
(time-to-return), so its thresholds are in milliseconds.
Everything in CODE-LIKE type above is an EEPROM configuration
key. The full reference, including defaults and allowed ranges, is in
section 7.
| Pattern | Meaning |
|---|---|
| Dim blue flash (1 Hz max) | Leads floating / open. |
| Green flash (2 Hz max) | Continuity (closed). |
| Red flash (2 Hz max) | Voltage present — detection bypassed. |
| Slow dim-red blink (25% duty) | Charging over USB; normal alerts suppressed. !ALERTS,1 re-enables them for the session, but using the detector on USB power is not recommended: supply noise causes false alerts, and the cable can couple the meter to earth ground. |
| Slow dim-green blink while plugged in | Charging and the battery reads full (≥ BATTFULLPCT %). |
| 1–4 green blinks at power-on | Battery level: 1 = 0–25%, 2 = 25–50%, 3 = 50–75%, 4 = 75–100%. |
| Off between flashes | Normal — every alert is a rate-limited flash, not a steady light, to save power. |
CONTREP/CONTREPMS).VOLTPULSES/VOLTREP). Voltage is a priority alert — it sounds
immediately even if a continuity beep just fired.CONTFREQ and
VOLTFREQ can still be set independently, but moving either far from 4 kHz
mostly just makes that alert quieter.BEEPMIN ms of the previous one, so an intermittent connection can't
machine-gun the beeper.!SET,BOOTMUTE,0.!SET,BEEP,0 then !SAVE.The unit stores four open/closed thresholds and uses one of them at a time.
Which one is active is set by THRESHSEL, an ordinary configuration key:
THRESHSEL | Position | Config key | Factory default | Sensitivity |
|---|---|---|---|---|
| 0 | 00 | THRESH00 | 0.15 V | Most sensitive — triggers on very high resistances. |
| 1 | 01 | THRESH01 | 0.54 V | |
| 2 | 10 | THRESH10 | 0.45 V | The factory setting. |
| 3 | 11 | THRESH11 | 0.62 V | Least sensitive — only firmer connections read as closed. |
Selection takes effect on the very next detection pass — there is no reboot
and no !SAVE needed to try one out. Over serial the change is announced as
$DIP,<position>,<threshold>. Follow with !SAVE to make it
the unit's boot setting:
!SET,THRESHSEL,0 // switch to the most sensitive slot $CFG,THRESHSEL,0 $DIP,0,0.1500 !SAVE $OK,save
Why the odd position names? The four slots are called
00/01/10/11
because on the revision 2 board they were chosen with a pair of DIP switches, and the
names carried over into the key names, the host tooling, and the $DIP message.
On a V3 board they are simply slots 0–3.
What each slot means is re-programmable. The four values are just EEPROM
entries: !SET,THRESH00,0.02 followed by !SAVE permanently changes
what slot 00 means on that unit. This lets one firmware build ship with each unit's
sensitivity map tuned to its application.
Running flat out, Blinky Hawk draws about 11 mA. Asleep it draws about 2 mA — and because it can still detect while asleep, sleeping is the normal operating state rather than a standby mode. The unit spends almost all of its life there and wakes fully only when it finds something.
After SLEEPSEC seconds in which the leads have read nothing but OPEN, the
firmware parks every load it can switch — the LED rail, the speaker, the battery-sense divider,
and optionally the bridge MOSFET — and puts the RA4M1 into Software Standby:
CPU and peripheral clocks stopped, RAM retained. A real-time-clock interrupt wakes it every
SLEEPTICKMS. Every SLEEPTICKS wakes it runs one cheap probe, and goes
straight back to sleep unless the probe finds continuity or voltage — in which case the unit
returns to full-rate operation and alerts normally.
Worst-case detection latency is therefore
SLEEPTICKMS × SLEEPTICKS.
DETMETHOD and the live threshold — it is only cheaper, averaging
SLEEPAVG readings instead of VOLTAVG and skipping the display
debounce.VOLTFAST × REFBAND),
never the tight averaged window, so lead noise cannot wake the unit on every tick.SLEEPSEC,0 disables sleeping entirely.SLEEPHB ticks says "asleep,
not dead". Set SLEEPHB to 0 to suppress it — each flash costs real current.SLEEPTICKMSThe wake period is produced by the RTC's periodic interrupt, which only offers a fixed ladder
of rates. Whatever you set is snapped to the nearest available rung and the
snapped value is written back, so !CFG always reports the rate actually in force:
2000100050025012563311684 ms
The rungs below 125 ms are rounded: 63 is really 62.5 ms (1/16 s) and 4 is really 3.90625 ms (1/256 s). Only the reported latency arithmetic uses the rounded number; the interrupt itself runs at the exact rate.
Faster is not automatically better. Sleep current is dominated by how often
the unit probes, not by the standby floor — halving SLEEPTICKMS roughly doubles
the average draw unless you raise SLEEPTICKS to keep the probe rate constant.
Below roughly 30 ms the probe no longer fits inside the tick, the unit stops idling
between wakes, and the draw climbs back toward the 11 mA awake figure. The bottom rungs
exist for measuring that knee, not for shipping.
As shipped: SLEEPTICKMS 63 and
SLEEPSEC 1 — the unit sleeps a second after the leads go open and checks
them about sixteen times a second, for roughly 2 mA average. These are the factory
defaults, so !DEFAULTS restores them rather than undoing them.
A probe taken out of standby measures a few percent high — the analog path has not fully
settled and the rails have just come back up. If a threshold tuned on the bench sits below the
sleeping unit's closed-lead reading, continuity will silently fail to wake the
unit. The SLEEPTHR00…SLEEPTHR11 keys give each slot its own wake
threshold for exactly this case; 0 means "use the ordinary THRESH value
for that slot", which is the default.
| Command | What it does |
|---|---|
!SLEEP |
Arm the timeout to fire as soon as sleeping is allowed, instead of waiting out
SLEEPSEC. Issue it over USB, then unplug — the unit sleeps the moment
it is idle and off USB. !SLEEP,0 cancels.
Replies $OK,sleep,armed. |
!SLEEPTEST |
Run one sleeping-mode probe right now, awake and on USB, and report exactly what it decided — state, metric, the wake threshold used, the awake threshold, resting differential, return time, and area. The probe normally runs unplugged and silent, so this is the only way to see its numbers directly. |
!SLEEPLOG |
Dump the probe history recorded while the unit was asleep (a 64-entry ring, oldest
first) as $SLOG rows. !SLEEPLOG,0 clears it. |
!FLOOR,<0–3> |
Park the board in one fixed state so a series ammeter reads a stable current:
1 = parked with the bridge resting, 2 = parked with the bridge off
(the delta against 1 is the bridge's own draw), 3 = parked but idling instead of
in standby (the delta against 1 is what standby buys). !FLOOR,0 exits. |
!SLEEPLOG is the answer to "it won't wake off USB". The
sleeping probe runs with USB unplugged and so cannot print — the unit behaves differently
precisely when you cannot watch it. Every probe therefore records its decision to RAM.
Unplug, exercise the leads, plug back in, and !SLEEPLOG shows what it actually
measured. Rows are marked SLEEP or AWAKE,
and comparing the two is the point: the ground reference — and therefore the metric — is not
the same on battery as it is on the bench. Tune wake thresholds against the
SLEEP rows, not against USB readings.
Connect over USB and open the port at 115200 baud. Any serial terminal works (Arduino Serial Monitor, PuTTY, a Python script). The protocol is line-based text:
! and end with a newline. Arguments
are comma-separated. Commands are case-insensitive.$ and is comma-separated —
easy for a program to parse. Human-readable status lines are printed alongside.| Command | What it does | Reply |
|---|---|---|
!SET,<key>,<value> |
Set a configuration value in RAM. Takes effect immediately; values outside the
allowed range are clamped. Not yet permanent — follow with !SAVE. |
$CFG,<key>,<value> (the value actually stored) |
!GET,<key> | Report one configuration value. | $CFG,<key>,<value> |
!CFG | Dump every configuration key. | One $CFG row per key, then $CFGEND |
!SAVE | Write the RAM configuration to EEPROM — this is what makes changes survive a power cycle. | $OK,save |
!LOAD | Discard unsaved RAM changes and reload from EEPROM. | $OK,load |
!DEFAULTS | Load factory defaults into RAM (EEPROM untouched
until you !SAVE). | $OK,defaults |
!SN | Report the unit's serial number. | $SN,<value> (empty if unassigned) |
!SN,<value> | Write the serial number. Persists to its own
EEPROM block immediately (no !SAVE needed). Max 15 characters, no
commas. | $SN,<value> then $OK,sn |
Each unit can carry a serial number — a short identity string (up to 15
characters) written once, usually at first bring-up, and read back on every boot. It is stored
in its own EEPROM block, separate from the configuration, so it survives
!DEFAULTS, a !SAVE of new settings, and even a firmware update that
changes the config layout. Assign one with !SN,<value>; read it with a bare
!SN. The current serial number is also reported at the end of every
$STATUS line as sn=<value> (empty when unassigned).
The configuration GUI uses this. When you save a unit's configuration, the host GUI records that unit's full config — keyed by serial number — into a master CSV, giving you an as-shipped record of every unit. If a unit has no serial number yet, the GUI prompts you to assign one.
| Command | What it does |
|---|---|
!STATUS or !? | Print the $STATUS summary line (mode, DIP position, active threshold, detection method, live metric, charge/battery state, unsaved-changes flag, serial number…). |
!ALERTS[,0|1] | Re-enable the normal alerts while charging (1) or restore the charging blink (0). Bare = toggle. Auto-clears on unplug. |
!DIAG[,0|1] | Enter/exit diagnostic mode (bare = toggle). See section 9. |
!STREAM[,0|1] | Continuous raw ADC streaming on/off (diagnostic mode only). |
!RATE,<ms> | Streaming interval in milliseconds. |
!VMODE,<0|1|2> | Voltage detection override: 0 = auto, 1 = force "voltage present", 2 = disable voltage detection. |
!MOSFET,<-1|0|1> | MOSFET override: −1 = automatic (normal detection), 0 = hold off, 1 = hold on. Diagnostic mode only. |
!CAP[,<ms>] | High-speed ADC capture across a MOSFET toggle; dumps $CAPSTART/$CAP/$CAPEND rows for plotting transients. |
Described in full in section 5.
| Command | What it does |
|---|---|
!SLEEP / !SLEEP,0 | Arm / cancel "sleep as soon as allowed". Arm over USB, then unplug. |
!SLEEPTEST | Run one sleeping-mode probe now and print what it decided ($SLEEPTEST). |
!SLEEPLOG / !SLEEPLOG,0 | Dump / clear the probe history recorded while asleep. |
!FLOOR,<0–3> | Park the board in a fixed state for a series-ammeter current measurement. |
| Line | Meaning |
|---|---|
$STATUS,… | Key=value status summary (sent after most commands). |
$CFG,<key>,<value> | One configuration value. |
$CFGEND | End of a !CFG dump. |
$SN,<value> | The unit serial number (empty if unassigned). |
$OK,<what> / $ERR,<what>,<detail> | Command acknowledged / failed. |
$DIP,<pos>,<threshV> | The active threshold slot changed — new position (0–3) and the threshold now in force. Emitted in response to !SET,THRESHSEL (and, on a V2 board, when a DIP switch is flipped). The name is historical; host tooling keys on it. |
$SLEEPTEST,… | Result of !SLEEPTEST: state, metric, wake threshold, awake threshold, resting differential, return time, area, method. |
$SLOGSTART,… / $SLOG,… / $SLOGEND | Sleep-probe log dump. Each $SLOG row is index, SLEEP/AWAKE, state, metric, threshold, return time, resting differential. |
$DIAG,<ms>,<rawPos>,<rawNeg>,<posV>,<negV>,<diffV> | One streamed raw sample. |
$CAPSTART… / $CAP… / $CAPEND | Transient-capture dump (raw counts; header carries full-scale and Vref for conversion). |
All keys are set with !SET,<key>,<value> and persisted with
!SAVE. Boolean keys take 0 or 1. Out-of-range values are clamped to the range
shown and the clamped value is echoed back.
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
HWREV | 2/3 | 3 | 2 … 3 | Which PCB this module is plugged into. This is a statement of fact about the hardware, not a preference — it decides whether D8 is a buzzer leg (V3) or a DIP-switch input (V2). It survives !DEFAULTS, exactly like the serial number, so a factory reset can never hand D8 to the buzzer on a V2 board. Change it only if a board is genuinely rebuilt. See the appendix. |
SPKDIFF | bool | 1 | 0/1 | V3 only: drive the piezo differentially (anti-phase across D8/D9) for roughly +6 dB over single-ended drive. 0 parks D8 low and drives D9 alone. Ignored on V2, whose buzzer is single-ended by construction. |
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
REFCENTER | volts | 0.018 | −1 … 1 | Resting differential center. Voltage detection measures deviation from this point. |
REFBAND | volts | 0.025 | 0.001 … 1 | Half-width of the "no voltage present" window around REFCENTER. |
THRESHSEL | 0–3 | 3 | 0 … 3 | Which threshold slot is active: 0 = THRESH00, 1 = THRESH01, 2 = THRESH10, 3 = THRESH11. Takes effect on the next detection pass. Ignored on a V2 board, where the DIP switches decide. |
THRESH00 | units* | 0.15 | 0.001 … 3.3 | Open/closed threshold for slot 00 (THRESHSEL 0) — the most sensitive setting. |
THRESH01 | units* | 0.54 | 0.001 … 3.3 | Threshold for slot 01 (THRESHSEL 1). |
THRESH10 | units* | 0.45 | 0.001 … 3.3 | Threshold for slot 10 (THRESHSEL 2) — the factory setting. |
THRESH11 | units* | 1.00 | 0.001 … 3.3 | Threshold for slot 11 (THRESHSEL 3) — the least sensitive setting. |
DETMETHOD | 0/1/2 | 1 | 0 … 2 | Detection method: 0 = single reading (volts), 1 = time-to-return (ms), 2 = tail area (V·ms). See section 2. Changing this changes the units of the THRESH keys. |
DETBAND | volts | 0.05 | 0.005 … 1 | Method 1 only: the differential is "returned" once it comes back within this band of the resting center. |
DETWINUS | µs | 1500 | 200 … 5000 | Methods 1 & 2: maximum recovery-sampling window. Also the timeout — if the differential never returns, the metric maxes out (reads as fully open). |
DETAREAUS | µs | 400 | 0 … 5000 | Method 2 only: where the area integration starts, measured from the MOSFET-off instant. Skips the common initial dip so only the discriminating tail is integrated. |
VOLTFAST | multiplier | 5.0 | 1 … 50 | A single reading beyond VOLTFAST×REFBAND declares voltage instantly (no averaging). |
VOLTAVG | count | 10 | 1 … 50 | Readings averaged for the normal voltage-present decision. |
TESTAGREE | count | 1 | 1 … 10 | Consecutive matching MOSFET tests required before a result counts. |
STABLECOUNT | count | 2 | 1 … 10 | Detection passes a new state must repeat before the alerts switch (display debounce). |
SETTLEPREUS | µs | 300 | 0 … 5000 | Settle time between MOSFET-off and the test reading (DETMETHOD 0 only; methods 1 & 2 sample from the toggle instant). |
SETTLEPOSTMS | ms | 3 | 0 … 50 | Idle after the test reading before the MOSFET returns on. |
NEGFIX | bool | 1 | 0/1 | 1 = use a fixed pseudo-reference (NEGV) for the negative side; 0 = read the negative sense pin live. |
NEGV | volts | 1.25 | 0 … 3.3 | The fixed pseudo-reference voltage used when NEGFIX is 1. |
* THRESH units follow DETMETHOD:
volts when method 0, milliseconds when method 1, volt·milliseconds when method 2.
The unit ships on method 1, so THRESH11's 1.00 default is
1.00 ms. Slots 00–10 still carry their old method-0 volt figures and
have not been re-characterized for method 1 — only slot 11, the factory selection, is
a bench-tuned value. Re-tune a slot before relying on it.
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
LED | bool | 1 | 0/1 | Master enable for the detection LED alerts (charging/battery indications still show). |
BEEP | bool | 1 | 0/1 | Master enable for the speaker. !SET,BEEP,0 + !SAVE = permanently silent unit. |
BOOTMUTE | bool | 1 | 0/1 | Enable the "leads shorted at boot mutes audio for the session" feature. |
PASSIVE | bool | 1 | 0/1 | 1 = passive buzzer driven with a tone at the pitch below; 0 = active buzzer (fixed tone, pin simply switched on/off). |
CONTFREQ | Hz | 4000 | 100 … 10000 | Continuity beep pitch (passive buzzer only). The buzzer is a resonator and is only usefully loud near 4 kHz — moving far from it mostly just makes the alert quieter. |
VOLTFREQ | Hz | 4000 | 100 … 10000 | Voltage beep pitch (passive buzzer only). Same 4 kHz as CONTFREQ — the two alerts differ by pulse count, not pitch. |
CONTPULSES | count | 1 | 1 … 5 | Pulses per continuity beep. |
VOLTPULSES | count | 2 | 1 … 5 | Pulses per voltage beep (2 = the signature double-beep). |
CONTREP | bool | 1 | 0/1 | Keep re-beeping while continuity holds. |
VOLTREP | bool | 0 | 0/1 | Keep re-beeping while voltage persists. |
CONTREPMS | ms | 1000 | 100 … 60000 | Repeat period for the continuity beep. |
VOLTREPMS | ms | 1000 | 100 … 60000 | Repeat period for the voltage beep (when VOLTREP=1). |
BEEPMIN | ms | 250 | 50 … 10000 | Minimum gap between beep sequences — the anti-machine-gun rate cap. |
These set the length of the individual pulses within a beep, as distinct from
CONTREPMS/VOLTREPMS (how often a beep repeats) and
CONTPULSES/VOLTPULSES (how many pulses each beep contains). Shorter
on-times make a tighter, clickier alert; longer ones make it more insistent.
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
CONTONMS | ms | 100 | 1 … 2000 | Continuity pulse on-time at first contact. |
CONTHOLDMS | ms | 100 | 1 … 2000 | Continuity pulse on-time for the ongoing re-beep while the connection is held. Separate from CONTONMS so the first contact can be a short click and the reminder a longer tone (or the reverse). |
CONTOFFMS | ms | 10 | 1 … 2000 | Gap between pulses within a multi-pulse continuity beep. |
VOLTONMS | ms | 20 | 1 … 2000 | Voltage pulse on-time. |
VOLTOFFMS | ms | 10 | 1 … 2000 | Gap between pulses within the voltage beep — this is the gap that separates the VOLTPULSES pulses into an audibly distinct burst. |
Each of the three detection states has its own brightness, flash length, and rate cap. The hue is fixed in firmware — blue, green and red are the meanings — but every other aspect is configurable, including switching a state's indication off entirely by setting its brightness to 0.
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
LEDFLOATBR | 0–255 | 20 | 0 … 255 | Floating (blue) brightness. Deliberately dim — it is the state the unit is in most of the time. 0 = leave this state dark. |
LEDFLOATMS | ms | 50 | 1 … 10000 | Floating flash on-time. |
LEDFLOATPER | ms | 1000 | 1 … 60000 | Floating: minimum gap between flash starts — i.e. a 1 Hz rate cap. |
LEDCLOSEDBR | 0–255 | 64 | 0 … 255 | Continuity (green) brightness. 0 = this state dark. |
LEDCLOSEDMS | ms | 200 | 1 … 10000 | Continuity flash on-time. |
LEDCLOSEDPER | ms | 500 | 1 … 60000 | Continuity rate cap (2 Hz by default). |
LEDVOLTBR | 0–255 | 200 | 0 … 255 | Voltage (red) brightness. 0 = this state dark. |
LEDVOLTMS | ms | 200 | 1 … 10000 | Voltage flash on-time. |
LEDVOLTPER | ms | 500 | 1 … 60000 | Voltage rate cap (2 Hz by default). |
Brightness and duty cycle are battery. The LED is one of the larger
switchable loads on the board. Raising LEDFLOATBR or lengthening
LEDFLOATMS costs runtime continuously, because floating is the state the unit
sits in almost all the time — the other two only fire when you have found something.
Behavior is described in section 5.
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
SLEEPSEC | seconds | 1 | 0 … 65535 | Open-lead idle time before the unit sleeps. 0 disables sleeping entirely. Anything other than an open lead restarts the countdown, as does being on USB or in diagnostic mode. |
SLEEPTICKMS | ms | 63 | 4 … 2000 | Base wake period. Snapped to the nearest rate the RTC can produce (2000/1000/500/250/125/63/31/16/8/4) and the snapped value is written back. |
SLEEPTICKS | count | 1 | 1 … 255 | Probe every N wake ticks. Worst-case detection latency is SLEEPTICKMS × this. Raise it alongside a faster tick to hold the probe rate — and the current — constant. |
SLEEPAVG | count | 3 | 1 … 50 | Readings per sleeping voltage check (against VOLTAVG when awake). Any one reading over the instant-bypass band wakes the unit; fewer reads = cheaper and quieter. |
SLEEPHB | ticks | 32 | 0 … 255 | Heartbeat flash every N wake ticks. Counts ticks, not seconds — speed up SLEEPTICKMS and the heartbeat speeds up with it, so rescale this to match. 0 = no heartbeat. |
SLEEPPARK | bool | 0 | 0/1 | 1 = park the bridge MOSFET OFF while asleep, saving its leg of the current; 0 = leave it in the resting state it uses when awake. |
SLEEPTHR00 | units* | 0 | 0 … 3.3 | Wake threshold for slot 00. 0 = use THRESH00. |
SLEEPTHR01 | units* | 0 | 0 … 3.3 | Wake threshold for slot 01. 0 = use THRESH01. |
SLEEPTHR10 | units* | 0 | 0 … 3.3 | Wake threshold for slot 10. 0 = use THRESH10. |
SLEEPTHR11 | units* | 1.2 | 0 … 3.3 | Wake threshold for slot 11. 0 = use THRESH11. |
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
CHGTHRESH | volts | 2.0 | 0.5 … 3.3 | VBUS-sense (VBUS/2) level above which the unit considers itself USB-powered / charging. |
BATTEMPTY | volts | 3.70 | 2.5 … 4.0 | Battery voltage mapped to 0%. Set above the cell's electrical floor on purpose: below roughly 3.6 V the analog baseline drifts enough that lead movement alone can trip the voltage detector, so the gauge is calibrated to read empty while the unit is still trustworthy. Lowering this to chase runtime buys false alerts. |
BATTFULL | volts | 4.20 | 3.0 … 4.5 | Battery voltage mapped to 100%. |
BATTFULLPCT | % | 90 | 50 … 100 | Charge percentage at/above which the charging blink turns green. |
| Key | Type | Default | Range | What it controls |
|---|---|---|---|---|
LOOPMS | ms | 50 | 1 … 1000 | Main-loop pacing. The CPU sleeps (WFI) between passes — larger = lower power, slower response. |
!SET) and the EEPROM config (what it will boot with).
!SAVE copies RAM → EEPROM; !LOAD copies EEPROM → RAM.$STATUS line reports
dirty=1 when the RAM config differs from what was last saved/loaded.!SAVE (or that first-boot seed). The firmware never writes EEPROM on its own
during normal operation, so data-flash wear is not a practical concern.!DEFAULTS then !SAVE.!DEFAULTS, !SAVE,
or a config-layout change. It is written only by !SN,<value>. See
section 6.!DIAG,1 switches the unit into diagnostic mode, intended for bench work and
for host software (e.g. the Python configuration GUI). Detection keeps running (so the LED
stays meaningful) unless you take manual control:
!STREAM,1 streams raw ADC samples as $DIAG rows at the
!RATE interval — watch the front end live.!MOSFET,0 / !MOSFET,1 parks the bridge MOSFET for probing;
!MOSFET,-1 returns it to automatic.!CAP,5 performs a microsecond-resolution capture of both ADC channels
across a MOSFET toggle (5 ms window here) and dumps the buffer — ideal for plotting
the discharge transient that the open/closed threshold discriminates.!VMODE,2 disables the voltage-present bypass while characterizing the
open/closed test (remember to restore !VMODE,0).!DIAG,0 exits and clears the streaming/MOSFET overrides.
!SET,BEEP,0 $CFG,BEEP,0 !SAVE $OK,save
!SET,THRESH00,0.02 // smaller threshold = more sensitive $CFG,THRESH00,0.0200 !SET,THRESHSEL,0 // and make that slot the active one $CFG,THRESHSEL,0 $DIP,0,0.0200 !SAVE $OK,save
!SET,REFCENTER,-0.005 $CFG,REFCENTER,-0.0050 !SET,REFBAND,0.03 $CFG,REFBAND,0.0300 !SAVE $OK,save
!SET,CONTREPMS,5000 // remind every 5 s instead of every 1 s !SET,CONTHOLDMS,20 // and shorten the reminder pulse !SAVE
Detuning CONTFREQ away from 4 kHz also makes it quieter, but
it fights the buzzer's resonance rather than working with it — shortening the pulse or slowing
the repeat gives a cleaner result.
!CFG
$CFG,REFCENTER,-0.0100
$CFG,REFBAND,0.0250
… (one row per key) …
$CFGEND
!LOAD $OK,load // RAM restored to the last-saved EEPROM state
!SET,DETMETHOD,1 // metric is now recovery time in ms $CFG,DETMETHOD,1 !SET,THRESH11,0.9 // re-tune the active slot in the NEW units (ms) $CFG,THRESH11,0.9000 !SAVE $OK,save
!SET,SLEEPSEC,1 // sleep a second after the leads go open $CFG,SLEEPSEC,1 !SET,SLEEPTICKMS,63 // wake ~16x/sec (snaps to the 1/16 s RTC rung) $CFG,SLEEPTICKMS,63 !SET,SLEEPTICKS,1 // probe on every wake !SET,SLEEPHB,0 // no heartbeat flash (it counts TICKS, not seconds) !SAVE $OK,save
!SLEEPTEST // leads shorted: what does the sleeping probe see? $SLEEPTEST,CLOSED,metric=0.3820,thr=0.4500,awakethr=0.4500,… // If that says CLOSED but a real sleeping unit stays asleep, the // difference is the battery ground reference, not the probe logic. // Unplug, exercise the leads, plug back in, and look at the real thing: !SLEEPLOG $SLOGSTART,64,412,method=0 $SLOG,0,SLEEP,FLOAT,0.5100,0.4500,… $SLOG,1,AWAKE,CLOSED,0.3800,0.4500,… … $SLOGEND // SLEEP rows reading high against thr = raise the wake threshold for // that slot, leaving the awake threshold alone: !SET,SLEEPTHR10,0.55 !SAVE
!SN // check first — is one already set? $SN, // empty = unassigned !SN,BH-2026-001 $SN,BH-2026-001 $OK,sn // written to EEPROM immediately; no !SAVE needed
These are properties of the Blinky Hawk PCB and are not runtime-configurable — changing them requires editing and re-flashing the firmware (BlinkyHawk_RA4M1.ino).
| Function | Pin / value | Notes |
|---|---|---|
| MCU | Seeed XIAO RA4M1 | Renesas RA4M1, 14-bit ADC, 3.3 V reference. |
| Sense positive | A2 | Pseudo-differential positive input. |
| Sense negative | A1 | Not connected on V3 — leave NEGFIX=1. Setting NEGFIX=0 on a V3 board reads a floating pin; the firmware allows it for bench work but warns. On V2 it is a real input. |
| Bridge MOSFET gate | D7 | HIGH = resting/bridge on. |
| Speaker, "hot" leg | D9 | Passive or active buzzer (PASSIVE key). |
| Speaker, anti-phase leg | D8 | V3: the piezo sits across D8/D9 and is driven differentially (SPKDIFF). V2: this pin is a DIP-switch input instead — see the appendix. |
| D10 | — | V3: no connection, parked as an output low. V2: DIP-switch second digit. |
| Status LED | LED1 on the PCB (SK6812, data pin 6) | Colors are compile-time; brightness, flash length, and rate caps are configurable per state. On V3, LED1 is wired straight to the battery rail and cannot be power-gated, so its quiescent draw is a hardware floor rather than something the firmware can park. |
| USB / charge sense | A3 | Reads VBUS/2 through a divider. |
| Battery sense | BAT_DET / BAT_READ_EN | Onboard Vbatt/2 divider, gated by the enable pin. |
| Serial | USB-CDC, 115200 baud | Line-based text protocol (section 6). |
Also compile-time: the LED hues, the charging-blink cadence, the sleep heartbeat's flash length and brightness, the 30-second post-boot grace period before the unit may sleep, and the transient-capture buffer size. (LED brightness/timing and beep pulse shaping are configurable — see section 7.)
One revision 2 unit exists. If you are not holding it, skip this
appendix — everything else in this manual describes the V3 board that all current units use.
The firmware supports both from a single build, switched by the HWREV
configuration key.
| Revision 2 | Revision 3 (current) | |
|---|---|---|
| Threshold selection | A 4-way DIP switch on the board, read live — flip it and the next detection pass uses the new threshold. No USB needed. | The THRESHSEL configuration key. No switch on the board. |
| Buzzer | Single-ended, driven from D9 alone. SPKDIFF does nothing. |
Piezo across D8/D9, driven anti-phase for roughly +6 dB (SPKDIFF). |
| D8 / D10 | DIP-switch inputs, with pull-ups. | D8 is the buzzer's second leg; D10 is unused. |
| A1 (sense negative) | A real input — NEGFIX=0 works. |
No connection. Leave NEGFIX=1. |
Two of the four switch positions (on pins D8 and D10) select which stored threshold is
active. Each pin has a pull-up and the switch shorts it to ground, so a switch that is
ON reads 0 and one that is OFF reads 1. The position code is
the two pin readings, first digit = D8, second digit = D10 — which
is where the THRESH00…THRESH11 key names come from:
| Position | D8 switch | D10 switch | Config key | Factory default |
|---|---|---|---|---|
| 00 | ON | ON | THRESH00 | 0.15 V |
| 01 | ON | OFF | THRESH01 | 0.54 V |
| 10 | OFF | ON | THRESH10 | 0.45 V (factory-set position) |
| 11 | OFF | OFF | THRESH11 | 0.62 V |
The remaining two switch positions are wired into the analog bridge rather than to the microcontroller: they bypass resistors to set the permanent series resistance. The pairs are silkscreened on the PCB and are moved together — the switches the firmware reads tell it which resistance the other pair just selected.
HWREV must stay at 2 on this board, and it does so on its own.
It survives !DEFAULTS, and any unit whose stored configuration predates the V3
firmware is pinned to HWREV 2 automatically when that configuration is
migrated forward. You should never need to set it. If it were set to 3 on a V2 board, the
firmware would drive D8 as a push-pull buzzer output into whatever the DIP switch has it
connected to, and threshold selection would stop responding to the switches.
Everything else in this manual applies unchanged — detection methods, the
low-power sleep mode, alerts, the serial protocol, and every configuration key not listed
above. THRESHSEL and SPKDIFF are simply ignored.