What went wrong, in order, and what each failure was actually caused by. Kept because the diagnoses are more useful than the design description: most of these cost a night each, and none of them were what they first looked like.
The first architecture was wrong and was abandoned on paper. The plan was a high-voltage H-bridge on a 30 V rail driving the electrodes directly. It does not survive contact with small-signal MOSFETs: the low-side sources stop sitting at ground once the output must float, gate drive turns into bootstrap territory, and gate-source voltage tracks the HV rail past the ±20 V rating. The replacement — an H-bridge on the low-voltage primary of a step-up transformer — removed all three problems and threw in isolation and structural charge balance for free.
Ordered parts are not received parts. The design specified TO-92 BS250/BS170; SOT-23 BSS84/2N7002 arrived. Electrically equivalent for this job, but they need adapters, and their pin order is not what the datasheet drawing suggests. Separately, the 2N3904 level shifters specified in the schematic were never actually ordered — two spare 2N7002 took their place, which needed 100 kΩ gate pull-downs added (a bipolar base with no current is off by itself; a floating MOSFET gate is not).
Symptom: first application of 12 V, the 47 Ω shunt heated until it smoked.
Diagnosis: the two low-side N-channel devices were installed with source and drain swapped. Their body diodes then formed a permanent conducting path from the 12 V rail through the shunt to ground. Nothing else was wrong — not the schematic, not the firmware, not the layout. Only the assumption about which adapter pin was which.
Fix: rework the jumpers to the measured pin roles rather than the assumed ones.
Lesson, and now a project rule: diode-test every transistor on the board and wire to what you measured. This rule is why the board map records measured roles per hole, and why the high-side devices in the final build have deliberately mirrored pinouts between the two legs.
Symptom: after a clean power-up and a clean set of measurements, the shunt overheated again. Cold checks kept coming back normal.
First hypothesis was wrong. "Something got knocked out of place" is not a diagnosis, and static measurements never reproduced it.
Actual cause: a thicket of bare wire. The breadboard build had a dense tangle of bare copper and protruding resistor legs, some of them springy. A leg would sag onto a neighbouring node in the crowded bridge/star area — 12 V rail, ground star and both bridge outputs all within a few columns — creating an intermittent path to ground. Touching the board made it, touching the board broke it. Static measurement cannot catch an intermittent mechanical short, which is why the cold checks kept passing.
Fix: separate every bare leg, sleeve the long ones, and inspect the dense zone visually. The shunt survived four overheating events and still measured 47 Ω afterwards.
Lesson: for a device that goes on a person, a nest of bare leads is not acceptable construction. This is what pushed the project onto a soldered board.
A related instrumentation lesson from the same evening: the telemetry caught the fourth overheat live — a minute of clean idle, then intermittent chaos between 0.47 mA and the ADC's full-scale 10.89 mA, then two minutes pinned at full scale while the shunt heated, then a sudden return to zero when the accidental contact broke. The current alarm behaved exactly as designed. Logging turned an unexplainable event into a timeline.
Symptom: telemetry reported 0.53 mA of primary current with the power stage completely de-energised. Physically impossible.
Diagnosis: the ADS1115's switched-capacitor input draws a small dynamic input current. Pushed through the 10 kΩ filter resistor, it lifts the input by ~10 mV, and the ADC samples in phase with its own charge injection — so it reads a larger offset than a multimeter sees. Confirmed by measuring 0 mV across the shunt itself, 10.0 kΩ from the ADC pin to the shunt leg, and 2 Ω from module ground to the star. The hardware was fine.
Fix: measure the zero at startup and subtract it (zero re-measures on demand).
Lesson: when an impossible number appears, measure the physical quantity independently before touching anything. And measure on metal legs, not board holes — hole contact on that breadboard produced 20–80 mV of noise on its own.
Before the first high-power run, the four gate signals were captured directly. Results:
- phase period 40.10 ms (25 Hz) ✓
- PWM carrier 40 µs (25 kHz) ✓
- low-side windows ≈273 µs, high-side pulses 15.5 µs at 64 % amplitude ✓
- Q1&Q3 overlap = 0 µs, Q2&Q4 overlap = 0 µs over 120 ms of capture ✓
- diagonals Q1↔Q4 and Q2↔Q3 coincide ✓
This turned "I hope the switching logic is right" into a measured fact, and it is the reason the phase machine has been treated as untouchable ever since. It cost one evening and a $10 logic analyser.
Symptom: the bridge produced nothing. The control panel showed a healthy amplitude, the status LED was correct, the telemetry was plausible.
Diagnosis: set pwm 40000 had been issued. LEDC clocks from 40 MHz, so at 10-bit
resolution the maximum is ≈39 kHz; the driver rejected the setup with div_param=0. The
firmware ignored the return value and answered #OK. The Q1/Q2 channels stayed detached and
the high-side switches never turned on.
Fix: check the return value of ledcAttach, roll the frequency back to 25 kHz on failure,
report #ERR, and cap the carrier at 38 kHz.
Lesson: a UI that reports what it commanded rather than what the hardware did is worse than no UI. Check driver return codes, particularly for things that fail quietly.
For a while the ear felt nothing at all while every measurement said the device worked. Two separate causes, found in order:
1. The firmware's own ceiling. dutymax was 60 %, so "amplitude 100 %" meant 60 % of
available drive, and the pulse width was fixed at 250 µs. Lifting both ceilings to 100 % and
1000 µs is a ×6.7 change in delivered charge. That was the single biggest gain in the project.
2. The impedance model, once it was written down honestly. The load on the secondary (R_lim 10 kΩ + ear ≈ 40 kΩ) reflects to the primary as ≈50 kΩ / 4² ≈ 3.1 kΩ, which is far larger than the bridge loop itself (47 Ω shunt + switches + winding resistance ≈ 150 Ω). Two consequences:
- Nearly the full rail appears across the primary. The shunt is not stealing power — that hypothesis died here.
- On the secondary,
I = 48 V / (R_lim + Z). With Z ≈ 40 kΩ that is ≈0.95 mA, which at 1000 µs is ≈950 nC — right at the bottom edge of the sensation threshold. Exactly matching "just barely felt".
This log had recorded a wrong conclusion before that, and it is worth preserving: an
earlier entry claimed halving R_lim from 10 kΩ to 5 kΩ would double the current. That is
only true when Z ≈ 0. At Z ≈ 40 kΩ, changing the denominator from 50 kΩ to 43.3 kΩ is a gain
of ×1.17, not ×2. R_lim only becomes a meaningful lever after the contact impedance
has been brought down.
Corrected lever table, from I = 48·k / (R_lim + Z):
| Change | Current | Gain | Cost |
|---|---|---|---|
| baseline: Vprim 12 V, R_lim 10 kΩ, Z ≈ 40 kΩ | 0.95 mA | — | — |
| Vprim 12 → 18 V | 1.44 mA | ×1.5 | 2 minutes with a screwdriver |
| Z 40 kΩ → 15 kΩ (proper electrode + saline) | 1.92 mA | ×2.0 | 5 minutes |
| both | 2.88 mA | ×3.0 | 7 minutes |
| R_lim 10 kΩ → 3.3 kΩ alone | 1.11 mA | ×1.17 | soldering, not worth it yet |
| R_lim 3.3 kΩ after Z drops to 15 kΩ | 2.62 mA | a further ×1.37 | soldering, later |
Raising the rail to 18 V was done and confirmed the model with a second data point: the threshold moved to 500 µs, exactly as predicted. The lesson is the ordering — measure the dominant impedance before optimising anything else, or you will solder for a 17 % gain while a 5-minute electrode change is sitting there offering 100 %.
Symptom (first time): all pulses stopped. Everything looked healthy — firmware running, LED correct, 12 V present.
Diagnosis: a cold joint where the boost converter's OUT− met the ground star. Without a shared ground the gate drive has no return path, so the bridge does nothing while every indicator says it should.
Symptom (second time, two days later): the same wire, off again.
Fix: heavier wire in a screw terminal. There is no solder in that node any more.
Lesson: the ground star is a mechanical problem, not an electrical one. Any wire that carries power gets a terminal or, failing that, stranded wire, a fully tinned end, a cone- shaped joint (a ball is a cold joint) and a blob of hot glue an inch back so strain lands on the glue rather than the solder. And in the troubleshooting order, ring out the star first — it has been the answer more often than anything else.
Once the device worked, the remaining weakness moved out of the electronics entirely. Measuring an ear clip on its own — plug to conductive rubber, through a drop of gel — showed hundreds of ohms to kilohms with readings that jumped when the lead was wiggled: the thin wire barely contacts the conductive rubber. That resistance is in series with everything and it varies from session to session.
The whole remaining shortfall to the 250 µs target (×2.0 in current) is plausibly sitting in that contact. Which is why the next step in this project is a multimeter measurement of the ear/electrode impedance, not a circuit change.
"The control panel won't start." Root cause: a forgotten earlier instance still holding TCP port 8765 and the serial port. The failure was cosmetic-fatal — launched by double-click, the error window closed faster than it could be read. Fixed by having a new instance evict the old one and by keeping the error window open. The user-facing rule became one line: if it does not start, start it again.
The board's COM connector became unreliable as a power input — brown-outs and resets, the whole USB device dropping off the bus, while its data path stayed fine. The rule now is: supply through the native USB port, use COM for data and flashing only, keep both cables attached while flashing.
The panel holds the serial port and will break a firmware upload mid-way. Close it first.
- Nothing failed the way it presented. "Dead processor" was a loose ground wire, twice. "Panel won't start" was a stale process. "Zero output" was a rejected PWM frequency. "Impossible current reading" was ADC input bias.
- Static tests do not catch intermittent mechanical faults. Two overheat events were invisible to every cold measurement.
- Measure the thing, do not reason about it. Pin roles, impedances, timing — every time reasoning replaced measurement here, it cost a night.
- Write down numbers, not adjectives. "It doesn't work" takes a night to diagnose; a table of readings takes a minute.