Skunk Werks · Circuit Design · Rev D

moon-temp-tracer — Wheatstone-Bridge Thermal Sensor Circuit

Gen-2 moon-tracking thermal sensor. Replaces the single-ended divider used in moon_temp_ads1115 with a Wheatstone half-bridge + LM4040 excitation feeding the ADS1115's differential inputs — power-gated, two independently addressed sensor units on one I²C bus.

Schematic

① EXCITATION REFERENCE ② BRIDGE NETWORK — SENSOR UNIT 1 (U1, ADDR→GND ⇒ 0x48) ③ ADS1115 (U1) ④ I²C BUS + HOST (ESP32-C3, existing) ⑤ SENSOR UNIT 2 — identical to Unit 1, ADDR→VDD ⇒ 0x49 SWITCHED_3V3 (from Q1 power gate — see ⑦) R4 1.2kΩ VEXC ≈ 2.048V U3 LM4040AEM3-2.048 A-grade, ±0.1%, ≤15ppm/°C C1 10µF C2 100nF Bias current ≈1.04mA through R4; after bridge draw (≈100-245µA) LM4040 still sees ≥800µA — well above its ~60µA min bias spec across the full temp range. C1/C2: ceramic, X7R — stable across -55..125°C (better cold-temp behavior than tantalum/electrolytic). C1 in 0805+ case, ≥10V rating to keep DC-bias derating modest. Vexc bus — same net feeds R1 top, R3 top, and AIN0 directly R1 10.0kΩ 0.01% ±5ppm/°C (RNCF0603TKY10K0) R2 10.0kΩ 0.01% ±5ppm/°C (RNCF0603TKY10K0) AIN1 = V(mid-ref) — bridge AINP R3 10.0kΩ 0.01% ±5ppm/°C (co-located, same swing as NTC) J1 — short flying leads to bar-mounted NTC RT1 — B57861S0103F045, 10kΩ, B=3988K thermal-epoxied to steel bar (remote) AIN3 = V(mid-sense) — bridge AINN U1 ADS1115IDGSR I²C 0x48 AIN0 (Vexc / rail, single-ended) AIN1 AIN2 — NC (spare) AIN3 ADDR→GND ⇒ addr 0x48 VDD +3V3 GND SDA SCL ALERT/RDY — NC (unused, polled single-shot per firmware) +3V3 R5 4.7kΩ R6 4.7kΩ pull-ups live here only — not duplicated on sensor boards ESP32 -C3 (existing) GPIO21 (SDA) GPIO20 (SCL) GPIO_PWR_EN (new, TBD) → drives Q1 gate, see ⑦ (power gate, below) Sensor cable run <1m — standard 4.7kΩ pull-ups + 400kHz fast-mode OK. Twisted pair for SDA/SCL, ground reference run alongside (board sits next to WiFi radio + buck reg). R1/R2 (ref) + R3/RT1 (sense) + U4 LM4040 — same values as Unit 1 U2 ADS1115IDGSR 0x49 SDA SCL ADDR→VDD ⇒ addr 0x49 (only difference from Unit 1) ⑦ POWER GATE (host board) — switches SWITCHED_3V3 feeding both sensor cables +3V3 (always-on — also feeds ESP32) Q1 P-MOSFET SOT-23 S D SWITCHED_3V3 → feeds R4 in both sensor units (see ②) R7 10kΩ — default OFF at boot/reset G GPIO_PWR_EN ESP32 GPIO (new, TBD) — active-LOW: LOW = sensors ON, HIGH/floating = OFF Settling time: R4(1.2kΩ)+C1(10µF) → τ≈12ms. Firmware must wait ≥5τ (~50-100ms) after enabling SWITCHED_3V3 before triggering the first ADS1115 conversion. Self-heating: board draw ≈4mW when on; gated to ~1-2s per 60s read (~2-3% duty) → ~30-40× lower average dissipation than continuous-on. NTC is remote (not on this board), so board self-heating doesn't couple directly into the reading anyway. moon_temp_tracer Bridge Sensor Circuit — Rev D (power-gated) Sheet 1 of 1 · schematic (not layout) See notes below for full BOM/rationale

MUX / PGA config — per read cycle, both units

ReadMUX[2:0]AINPAINNPGAPurpose
Bridge diff010AIN1AIN3010 (±2.048V)Bridge output → temperature
Rail / Vexc100AIN0GND001 (±4.096V)Excitation health, read straight off the top of R1/R3

AIN2 is unused. ADS1115 only supports four fixed differential pairs — AIN0-AIN1, AIN0-AIN3, AIN1-AIN3, AIN2-AIN3 — so putting the rail monitor on AIN0 (single-ended) forces the bridge pair onto AIN1-AIN3.

Why a bridge

moon_temp_ads1115 uses a single-ended divider (3.3V → 10kΩ fixed → ADS1115 → NTC → GND), giving ~55 m°C/count. Switching to a Wheatstone half-bridge on the ADS1115's differential inputs gets three things at once: much finer resolution near the balance point, rejection of common-mode noise/lead-resistance error on the signal path (both sides of the differential pair pick up the same noise, which cancels), and a ratiometric relationship to the excitation voltage rather than an absolute one.

Component values and why

PartValueRationale
U1, U2ADS1115IDGSR (TI, VSSOP-10)Standard industrial grade, not the AEC-Q100 automotive ADS1115BQDGSRQ1 variant — same core spec/data rate per TI's own pages; automotive qualification buys nothing here and typically costs more / has tighter sourcing.
R1, R2, R3RNCF0603TKY10K0 (Stackpole), 10.0kΩ, 0.01%, ±5ppm/°CMatched reference divider (R1/R2) plus the sense-leg fixed resistor (R3), which has no partner to cancel against — its own drift is direct sensor error, so it gets the same spec. This part comfortably exceeds the 0.1%/≤25ppm/°C this design originally called for.
RT1, RT2B57861S0103F045 (TDK/EPCOS), 10kΩ NTC, B25/100=3988K ±0.3%Epoxy bead, PTFE-insulated 50mm leads — good fit for thermal-epoxy mounting to the steel bar. Resistance tolerance ±1% only affects absolute matching between the two sensor units, not the bridge math.
U3, U4LM4040AEM3-2.048, A-grade2.048V variant lines up with the ADS1115's own ±2.048V PGA setting. A-grade (~±15ppm/°C) matters because its drift scales the entire bridge output proportionally.
R41.2kΩBias resistor for the LM4040 shunt. From SWITCHED_3V3 this gives ≈1.04mA; after the bridge draws its own 100-245µA, ≥800µA still flows through the LM4040 — comfortably above its ~60µA minimum bias spec.
C1, C210µF / 100nF, ceramic, X7RBulk + high-frequency decoupling at the Vexc node. X7R stays stable across -55°C to +125°C — better cold-temperature behavior than tantalum/electrolytic, which degrade below about -25°C.
R5, R64.7kΩI²C pull-ups — placed once, at the ESP32 end only. Stay on the ESP32's always-on 3.3V, not the switched branch.
Q1P-channel MOSFET, e.g. DMG2305UX / AO3401A (SOT-23)High-side switch gating SWITCHED_3V3. Placeholder recommendation — current draw here is trivial.
R7~10kΩQ1 gate pull-up to 3.3V — keeps the sensor branch OFF by default during ESP32 boot/reset.

Worked numbers (B57861S0103F045, B=3988K, -30°C to 45°C)

NTC resistance is highly non-linear over this range: ~4.3kΩ at 45°C → ~206kΩ at -30°C. With R3=10kΩ (bridge nulled at 25°C):

TempR_NTCV_diff (AIN1−AIN3)
-30°C≈206kΩ≈ -0.93V
25°C10kΩ0V (balanced)
45°C≈4.3kΩ≈ +0.41V

PGA ±2.048V gives ~2.2× headroom over the -30°C extreme, avoiding clipping even with thermistor tolerance and self-heating factored in. Resolution at that PGA is 62.5µV/LSB; near 25°C that's roughly ~328 counts/°C — about 18× finer than the existing single-ended design's ~55 m°C/count. Temperature is recovered by inverting the actual bridge equation for R_NTC from V_diff (not a linear approximation), then feeding that into the Beta equation.

Power-gating

Both sensor boards are only powered during the brief read window, not continuously. SWITCHED_3V3 is a branch off the main 3.3V rail, gated by a single high-side P-channel MOSFET (Q1) at the host end — one switch for both sensor cables, since they're already read back-to-back in the same MQTT SendData cycle. The ESP32's own 3.3V supply is upstream of Q1 and stays continuously powered. SWITCHED_3V3 feeds the ADS1115's VDD too, not just the LM4040/bridge chain.

Settling time: R4 (1.2kΩ) + C1 (10µF) at the Vexc node form an RC with τ≈12ms. Firmware needs to wait ≥5τ (~50-100ms) after enabling SWITCHED_3V3 before triggering the first ADS1115 conversion. Trivial against a 60s read interval.

Self-heating: board draw when on is ≈4mW. Continuous-on sustains that 24/7; gated to a ~1-2s window every 60s (~2-3% duty cycle) drops the average to ~100-130µW — a 30-40× reduction. The NTC itself isn't on this board (remote on the steel bar via short flying leads), so board self-heating doesn't couple directly into the sensing element regardless.

Electrical check: with the branch de-energized, VDD/Vexc/all analog bridge nodes sit at 0V together. SDA/SCL/ADDR digital pins on a powered-down ADS1115 are still held at 3.3V by the host-side pull-ups — within spec, since the datasheet's digital I/O absolute max is a fixed GND-0.3V to 5.5V range, not referenced to VDD.

Addressing

SensorADS1115 ADDR pinI²C address
Unit 1GND0x48 (same as existing moon_temp_ads1115)
Unit 2VDD0x49

Both share the ESP32's existing I²C bus (GPIO21=SDA, GPIO20=SCL). SDA/SCL address options (0x4A/0x4B) are still free for future expansion.

Physical / layout notes

Relationship to Moon_Temp_Overview

Moon_Temp_Overview documents a separate, related project — the gen-1 deployment (moon-temp-001), a static rig using three single-ended channels (two moonlit reference sensors averaged together, differenced in software against one shaded sensor). moon-temp-tracer is gen-2: different hardware, mechanically tracking the moon via stepper motors — not a continuation or replacement of the gen-1 rig, just related in concept.

Deferred to later passes