Engineering studies

NightMare · 15 min · Engineering study

What the AC’s sound can tell us

I built Watson, an ESP32 acoustic sensor that reports fan activity, compressor activity and short beeps from an existing air conditioner. Could one microphone provide useful feedback without modifying the appliance? The first recordings looked encouraging. Speech, an alarm and a six-second false compressor episode showed what the initial detector had missed.

Classic ESP32, one INMP441 microphone and one Consul CCN07GB installation. Historical evaluations are kept separate below. The spectrogram is newly derived from an original recording; the evaluation charts reproduce saved results.

  • The working result is three independent observations delivered through NightMare Network. Adler’s complete automatic state-recovery controller is still being rebuilt.
  • The useful improvements came from examining specific failures: temporal variation for the fan, a narrower compressor band and preserved timing for beep events. Each introduced a trade-off that needed its own evaluation.
Back to NightMare

Three observations from one microphone

Adler could send infrared commands, but another remote or the appliance buttons could change the AC without updating its record. Watson’s job was to observe what the appliance was doing from the outside. The hardware was an ESP32 and one INMP441 I2S microphone near the AC; acquisition used 16 kHz mono audio.

The fan may run with the compressor off. A short acknowledgement beep is a different kind of evidence. The final interface therefore separates fan presence, compressor presence and a transient beep event. Watson publishes these observations; Adler combines them with command history. A detected tone alone cannot identify the requested action or the resulting setpoint.

Sources and evidence: [1] · [2]

The first 99.3% concealed a useful mistake

The initial controlled replay used 18 recordings: three each of OFF, FAN and COMPRESSOR, both in clean conditions and with talking. The report recorded 99.3% correct decisions across 15,656 decided analysis windows, with 17 of 18 files perfect. Those were many overlapping decisions from a small controlled set.

The imperfect recording had the AC off while someone talked. Part of the speech was classified as fan. That mattered more than the aggregate score: ordinary room activity could occupy the same bands as the target sound. The broad compressor band looked clean in this pilot, but it had not yet encountered the low-frequency interference that later broke it.

The spectrogram below is a fresh view of an original OFF-with-talking recording. It shows why a band-energy threshold needs further evidence: speech carries substantial energy through the fan bands. It is an illustrative excerpt, not a claim that these eight seconds reproduce the historical classifier error.

Speech occupies the fan’s bands

Scroll horizontally to inspect the full time range.

0k1k2k3k4k0s2s4s6s8sFrequency (Hz) · Time (s)
−100 dBFS−25 dBFS

Dashed region: 500–2,000 Hz fan bands. Display cells sum four FFT bins and use a fixed color scale. This excerpt illustrates interference; it does not re-score the historical pilot.

Derived from the first 8 s of 2026-09-30_104340_off_talking_01.wav: 16 kHz PCM, 1,024-point Hamming windows. Fresh visualization of an original recording.

Download data and provenance (JSON)

Sources and evidence: [3] · [12]

The fan’s steadiness helps separate it from speech

Speech could exceed the fan’s level in the very bands used for detection. Tightening the threshold removed real fan audio too. Comparing how the energy varied over time provided a better distinction. In the initial failing example, rolling 500–1,000 Hz variation was reported around 1.4 dB for the fan and 9.8 dB for speech over a two-second history. These were examples, not universal cutoffs.

The revised fan rule requires both mid-band and high-band energy, together with limited temporal variation. At least one second of history is required before assessing steadiness; a separate two-second hold is required before publishing a candidate. A tiny history can appear artificially steady, while a hold answers whether a candidate lasts long enough. Neither check replaces the other.

On the 88-event reviewed set, false fan windows fell from 3,010 to 1,544. Missed fan windows increased from 336 to 1,265. Television over a running fan could make the mixture appear too variable; a reviewed TV-over-fan event lost all 872 scored windows. Steady unrelated machinery could still resemble the fan. The change improved one failure mode while exposing another.

Fewer false fans, more missed fans
  • v1
  • v2
View chart data
View chart data
Analysis windowsv1v2
False fan detection3,0101,544
Missed fan activity3361,265

88 reviewed events; 53,014 windows; 63 time groups. Fan absent: 19,748 windows; present: 33,266. v1 also had 36 present windows not yet published. v1/v2 identify detector iterations.

Download data and provenance (JSON)
Fan rule and exploratory alternatives

The documented baseline uses median 500–1,000 Hz ≥ −62 dBFS, median 1,000–2,000 Hz ≥ −65 dBFS, temporal variation in 1,000–2,000 Hz ≤ 6 dB, at least one second of history, a half-second median stage and a two-second publication hold. Detector v1/v2 labels refer to algorithm iterations.

Exploratory logistic regression, small trees and random forests were also considered. Early cross-validation scores from related recordings were insufficient evidence to deploy a model. The chosen rule remained explicit and inexpensive; any replacement still needed a material improvement under grouped evaluation.

Sources and evidence: [3] · [4] · [1]

The alarm that sounded like an acknowledgement

The operational review on 2 October found 25 true and 41 false detections among 66 reviewed beep events. Thirty-five of the false events were associated with an alarm. The alarm’s pitch clustered around 4,058–4,062 Hz, while real AC beeps clustered near 4,117–4,125 Hz. Their durations overlapped. Increasing a duration or loudness threshold alone could not reliably separate them.

A retrospective 4,115–4,128 Hz gate retained 24 of 25 true beeps and none of the 41 false detections in that reviewed cohort. It was a useful hypothesis, tested on the same examples that suggested it. It did not establish a future false-alarm rate, and a narrow gate could reject a weak or shifted genuine tone.

The saved incident metadata also records narrower runtime experiments. The committed classic-ESP32 baseline retained a broader 4,050–4,180 Hz search with contrast and timing checks. Keeping those versions separate prevents a promising operational adjustment from becoming an invented “final validated” configuration.

What the reviewed beep events actually contained
  • Count
View chart data
View chart data
Reviewed eventsCount
Real AC beep25
False: alarm35
False: other sound6

Historical operational review: 66 reviewed detections from 67 triggered beep events. These counts describe that reviewed cohort; they do not measure missed beeps or a future false-alarm rate.

Download data and provenance (JSON)

Sources and evidence: [5] · [1] · [7]

Six seconds of a compressor that never started

Two saved events, COMPRESSOR_ON at 11:52:20 and COMPRESSOR_OFF at 11:52:26, captured one false episode of roughly 6.3 seconds. Both reviews say the compressor stayed off. The broad 30–80 Hz energy rose enough to pass the detector’s threshold and hold, but the underlying sound was different.

The historical analysis identified a component around 37.1 Hz and another near 74.2 Hz, compared with about 58.6 Hz for the real compressor. A broad band mixed these different shapes together. Splitting it into lower, primary and upper bands exposed the difference: the real compressor had strong 52–65 Hz energy; the false episode did not.

Those approximate example levels motivated a narrower primary feature. They were not distributions across all operating conditions. I preserved sideband diagnostics for investigation, while treating a sideband rejection rule as a separate idea that needed its own false-rejection evaluation.

The interference and compressor had different spectral shapes
  • Real compressor
  • False episode

Scroll horizontally to compare all band levels.

-60-50-40-30-2030–45 HzReal: ≈ -52 dBFSFalse: ≈ -31 dBFS52–65 HzReal: ≈ -33 dBFSFalse: ≈ -57 dBFS65–80 HzReal: ≈ -57 dBFSFalse: ≈ -42 dBFSdBFS
Compare band energy (dBFS)
Compare band energy (dBFS)
HzReal compressorFalse episode
30–45≈ -52≈ -31
52–65≈ -33≈ -57
65–80≈ -57≈ -42

Approximate band levels from two historical examples, not population distributions. Exact incident files are identified in the downloadable evidence. This chart compares reported measurements, not reconstructed spectra.

Download data and provenance (JSON)

Sources and evidence: [5] · [12] · [6]

A different measurement needs a new threshold

The old PC rule summed 30–80 Hz power and compared it with −38 dB. The new feature summed only 52–65 Hz. Summed power changes when bins are removed, even if the physical sound stays identical. Copying −38 into the new feature would therefore implement a different decision rule.

The calibration used 117 labelled files: 27,830 compressor-ON and 47,541 OFF scored windows in 87 time groups. The primary-band medians were −32.6 dBFS with the compressor on and −59.1 dBFS off. The broader band’s OFF tail carried considerably more interference. These distributions provided a basis for searching thresholds.

Sources and evidence: [6]

Choose a stable region before the recall cliff

The sweep below includes every reported integer threshold from −52 to −35 dBFS. The practical choice was −43 dBFS: near the best balanced accuracy, inside a broad plateau and away from the sharp recall loss above −41. At −38 on the new band, recall fell to 71.57%; at −43 it was 96.21%. The corresponding false-positive rates were 0.91% and 1.02% over 47,541 OFF windows.

Leave-one-group-out selection over 87 time groups chose cutoffs from −44 to −42 dBFS, with held-out balanced accuracy 0.9743. That supported choosing a region rather than treating the fine-grid optimum, −42.5, as a physical constant. The optional sideband guard stayed disabled: some false examples overlapped true compressor audio, and rejecting their tails could reject real operation too.

Why −43 dBFS, and what happens at −38
60%80%100%0%2%4%False positives · separate scale-52-48-43-38-35Threshold (dBFS)60%80%100%0%2%4%False positives · separate scale-52-43-35Threshold (dBFS)
  • Recall
  • Balanced accuracy
  • False positives
Recall96.21%False positives1.02%Balanced accuracy97.59%

Shaded: near-optimal region, −50 to −41 dBFS. Default: −43 dBFS. Values are the 18 reported integer thresholds; no interpolation or new classifier run.

View all 18 thresholds
View all 18 thresholds
dBFSRecallFalse positivesBalanced accuracy
-5296.6%3.47%96.57%
-5196.44%2.48%96.98%
-5096.34%2.04%97.15%
-4996.31%1.8%97.25%
-4896.3%1.64%97.33%
-4796.29%1.52%97.38%
-4696.29%1.41%97.44%
-4596.29%1.38%97.45%
-4496.28%1.29%97.49%
-4396.21%1.02%97.59%
-4296.1%0.91%97.6%
-4195.9%0.91%97.49%
-4093.06%0.91%96.08%
-3984.7%0.91%91.9%
-3871.57%0.91%85.33%
-3767.7%0.91%83.39%
-3667.64%0.91%83.37%
-3567.64%0.91%83.37%

117 labelled files; 27,830 compressor-ON and 47,541 OFF windows; 87 time groups. Source: saved compressor derivation report.

Download data and provenance (JSON)

Sources and evidence: [6]

Porting the FFT changed which beeps survived

Beep detection used raw spectral features, contrast against neighboring bands, duration, pitch stability and short gaps. The half-second median used for motors would erase a roughly 150 ms tone, so beep events needed their own pipeline.

The embedded FFT grew from 1,024 to 2,048 samples. At 16 kHz this doubled the physical window from 64 to 128 ms, while the hop remained 512 samples, or 32 ms. Substituting 128 ms directly into the event timing shortened estimated tone runs and merged nearby beeps. The spectrum could look correct while the resulting events changed.

A calibration across 21 reviewed event files compared effective timing footprints. Keeping 64 ms reproduced all 69 PC-reference detections with zero extras. Using 128 ms reproduced 44. The Hamming window’s weak outer regions help explain why the detector’s effective timing footprint need not equal its nominal window length.

This was a parity result against the reference detector on those files. It did not establish that every real beep would be detected. It did establish that the port needed to preserve temporal semantics as well as frequency-domain calculations.

Timing changes which beeps survive
  • Matched / 69
View chart data
View chart data
Reference detections reproducedMatched / 69
64 ms69
80 ms63
96 ms57
128 ms44

21 reviewed event files; 69 reference detections. Zero extra detections at all four settings. This measures reference-detector parity on recorded files.

Download data and provenance (JSON)

Sources and evidence: [8] · [9]

Three different checks before trusting the port

First, numerical parity: the documented 112-file host comparison checked compiled firmware feature calculations against a float64 reference. Band levels agreed to roughly 0.001 dB and fan variation to about 0.0001 dB. These tolerances tested the calculation, not acoustic classification accuracy.

Second, behavioral parity: the same comparison checked candidates, publication changes and beep events. It reported no fan/compressor candidate mismatches and matching published transitions and events. A small numerical error near a threshold can change a whole state transition, so feature agreement alone would not have been enough.

Third, on-device checks: four recorded golden windows ran through the actual ESP32 FFT/FPU path. The worst reported difference was 0.018 dB against a 0.05 dB tolerance. A separate microphone test presented five 4,118 Hz tones and observed five transient beep events. These exercised hardware and transport on a small controlled sample.

Sources and evidence: [1] · [10]

What labelled replay showed—and where it still failed

A separate 117-file replay compared the recalibrated firmware with the older PC v2 configuration, scoring windows after publication. The compressor improved substantially in that comparison. Fan recall stayed similar and its false-positive rate was slightly worse. The change included feature and threshold calibration, so the compressor improvement cannot be attributed to the processor alone.

The replay report uses different scored totals because warm-up and publication behavior differ: fan 75,344 firmware / 75,576 PC windows; compressor 75,371 / 75,299. Recall is computed within positive windows and false-positive rate within negative windows. These totals identify the evaluation; they are not the denominator of both rates.

Aggregate results still hid interference failures. In the separate compressor derivation report, the TV subset contained 680 ON and 376 OFF windows and had 0.500 recall and 0.500 false-positive rate. That small correlated subset warrants more TV recordings; it does not establish a universal 50% failure rate around televisions.

117-file replay: recall / false-positive rate
ObservationOlder PC v2Firmware
Fan96.7% / 5.8%96.8% / 6.6%
Compressor76.9% / 1.2%96.1% / 1.2%

Sources and evidence: [11] · [6]

Keep the evidence sets separate

The 18-recording pilot, 88-event fan comparison, 201-triggered-event operational review, 117-file compressor derivation, 21-file beep calibration and 112-file parity suite answer different questions. Counts and percentages from these stages cannot be pooled into one accuracy claim. Triggered-event reviews also omit sounds that never triggered a capture.

Adjacent FFT windows overlap, and smoothing adds further dependence. The saved evaluation excluded warm-up and transition-adjacent ambiguity, grouped events within 120 seconds and used grouped threshold checks. Historical single-state labels were mapped into fan/compressor labels with an assumption: compressor running implies fan running on this appliance. That is a constraint of the old evidence, not proof of independent fan ground truth.

For this publication I checked the original fan CSV counts, threshold rows, beep calibration report and the two incident reviews. The new spectrogram was derived from the identified WAV excerpt. Historical alarm-cohort statistics and approximate episode band levels remain attributed to the saved engineering account; they have not become new replay results.

Sources and evidence: [3] · [12] · [5]

Deliver useful observations from a bounded device

The firmware separates acquisition, analysis and publication with bounded queues. A missing audio block resets dependent FFT/history context rather than joining unrelated samples into a fictional continuous signal. Fan and compressor earn their own publication holds; beeps remain transient events that are not replayed after reconnection.

The documented classic-ESP32 measurements put analysis near 3.5 ms per 32 ms hop, roughly 11% of one core. Broker-connected free internal heap was about 35 KB, with a largest free block around 24 KB. Those are measurements of that configuration. Network tasks, debug streaming and flash writes compete for the same resources; OTA is a maintenance operation during which acquisition is suspended.

Microphone disconnection must not turn into “AC off.” The sensors retain connection information, and NightMare carries availability and freshness alongside observations. These semantics make the acoustic node usable by a controller without pretending every missing value is a physical transition.

Sources and evidence: [1]

What this establishes, and the next useful tests

The result is an implemented acoustic observation node, calibrated and evaluated around one appliance and microphone position. It can supply evidence of activity changed outside Adler’s own commands. It cannot read every mode or setpoint, and the high-level Adler reconciliation controller is still being rebuilt.

The next discriminating recordings should target the weak conditions: prolonged OFF periods without commands, TV over a running fan, alarms near the AC tone, verified compressor transitions and varied microphone positions across separate days. Keep independent session groups held out before tuning. A new hardware arrangement would need its own paired recordings and evaluation.

Reproduction and source material

The repository contains feature extraction, event review, grouped evaluation, firmware parity and self-test entry points. The presentation’s build-watson-evidence.py script extracts a curated snapshot from the original audio folders, versioned reports and historical ledger, recording SHA-256 hashes. It generates the spectrogram display cells and verifies reported chart counts; it does not re-run all detector benchmarks.

The downloadable JSON contains chart values, methods, source filenames and hashes. Recordings and private review notes remain outside the website bundle. Full reproduction requires the matching recording set, labels, configurations and source revision described in each report.

Sources and evidence: [12] · [10] · [1]

Sources and evidence

Implementation details and evaluation reports are linked to fixed source revisions.

  1. Firmware architecture, resource limits and documented validation
  2. Independent fan/compressor observations
  3. 18-recording pilot and 88-event fan evaluation
  4. Fan stationarity rule and threshold rationale
  5. Historical 2 October review: alarm cohort and approximate phantom-episode measurements, preserved in the evidence snapshot
  6. 117-file compressor derivation: distributions, full threshold sweep and grouped selection
  7. Beep event detector
  8. 21-file timing-footprint calibration
  9. Spectral and temporal feature calculations
  10. Firmware/reference parity harness
  11. 117-file labelled firmware/PC replay report
  12. Chart values, recording-derived display data, methods and SHA-256 provenance (JSON)