Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Precision Temperature Controller

A simple, low-cost, modular, multi-channel temperature controller for holding a lab laser steady enough to stay injection-locked. The figure of merit is relative stability — keeping the temperature put — not absolute accuracy. A vertical slice around an AD7124-8 24-bit ADC and a Teensy 4.1 (C++), borrowing only the proven MAX1968 TEC power stage from the open-source Sinara Thermostat.

Specification, and every other number that appears twice: docs/sources.md. That file is the single home for values.

Read docs/design-report.md first — the official design report. Anyone planning to work on this instrument should go through it before touching anything else; it is the complete account of what the design is and why. The wider plan and conventions: docs/project-guide.md.


Repo map

├─ docs/
│  ├─ design-report.md     THE OFFICIAL DESIGN REPORT — read this before working on anything
│  ├─ project-guide.md     the full plan: phases, gates, conventions (Appendix A = firmware rules)
│  ├─ sources.md           canonical values — the single home for every number
│  └─ bom-part-selection.md
├─ firmware/            Teensy sketches: controller/, ad7124_firstlight/, ad7124_ratiometric/
├─ hardware/            reference-design/ (Sinara Thermostat) + datasheets/
├─ PCB/                 the KiCad project, reviews and fabrication outputs
└─ tools/               bench utilities + check_docs.py

The full lab notebook — session-by-session history, gate measurements and their artifacts — is kept in the lab's internal repository and is not published. This repository carries the design.

Design rubric

Every choice is judged against four things: low cost (the lab builds one per laser), JLCPCB-assembly-friendly, modular / multi-channel (a channel is a firmware loop over more ADC inputs and MAX1968 stages, not a redesign), and simple.

We copy the Sinara power-stage topology and its architecture lessons — nothing else. We diverge on purpose: AD7124-8 + Teensy/C++ against their ADC and STM32/Rust.

Firmware non-negotiables

Full conventions in Appendix A of docs/project-guide.md.

  • All math in plain floating point on the Teensy.
  • The PID loop closes locally — never depends on a network packet. Ethernet is telemetry only.
  • Read the ADC ID register first before trusting anything downstream.
  • Fail safe: open sensor or railed ADC → TEC current to zero, never to full heating.

Board bring-up — from the JLC box to a running control loop

Do the steps in order. Several of them are irreversible-if-skipped: the Teensy mod must happen before the Teensy is soldered down, and the TEC must not be connected until the front end is trusted.

Written after the session-42 schematic review. Every number here is traced to a datasheet; the derivations are in the lab's internal notebook (session 42).

Step 0 — Before you solder anything

0.1 Verify SW1's pinout against the vendor drawing. SW1 (MST-12D18G4, LCSC C49023767) is wired with pin 2 as the common pole, pin 1 to GND, pin 3 to +5V. That is only correct if pin 2 really is the wiper.

With a loose switch on the bench, meter across pin 1 and pin 3. It must read open in both slider positions. If it ever reads short, the footprint numbering is wrong and soldering it down puts a dead short across +5 V. Stop and re-map the footprint.

0.2 Confirm the four screw terminals are labelled. J4–J7 are four identical 5.08 mm 2-pin terminals and nothing physically stops you swapping them. Re-read from PCB/review-2026-08-10-drive-stage.md before wiring — this table was wrong for three sessions, naming J1 as the TEC and J4 as arm 2's sensor, which is the one swap that is not survivable:

Ref Signal
TEC J4 TEC+ (pin 1) / TEC− (pin 2)
Sensor — arm 1 (current excitation) J6 NTC_HI1 (pin 1) / NTC_LO1 (pin 2)
Sensor — arm 2 (ADR4525 excitation) J7 NTC_LO2 (pin 1) / NTC_HI2 (pin 2) ← pin order reversed vs J5/J6
Sensor — arms 3/4 (JP1-selected) J5 NTC_HI3 (pin 1) / NTC_LO3 (pin 2)

J1/J2/J3 are not sensor terminals. They are 2-pin headers for metering the MAXV/MAXIP/MAXIN trimmers, and they are pin headers rather than screw terminals — the shape is the tell.

Plugging the TEC into a sensor terminal is survivable — the 1 kΩ series resistors (R19–R23, R28, R29) limit the fault to ~0.4 mA into the AD7124's clamp diodes against a 10 mA absolute maximum. The reverse is not: a sensor in J4 sits directly across a 6 V, 3 A bipolar driver. Label the panel.

0.3 JP1 silkscreen. A = ADR4525 (+2V5), B = AD7124 REFOUT. A 2-pin shorting block bridges A–C or C–B, never both. Jumper removed entirely floats the divider and rails AIN3/AIN4 — detectable, harmless.

Step 1 — The Teensy mod: cut VUSB ↔ VIN

Do this before the Teensy 4.1 is soldered to the board. It is far easier on a loose Teensy and the pads are on the underside.

Why

The board ORs two 5 V sources into the Teensy's VIN through Schottky diodes:

+5V (backplane, J8 A9–A17) ──▶|── D3 ──┐
                                        ├── U1.48  VIN
VUSB (U1.49) ─────────────▶|── D2 ──────┘

Both diodes are oriented correctly (verified: pin 1 is the cathode on the MBR120VLSFT1G symbol, so both conduct into VIN). But a stock Teensy 4.1 ships with VUSB and VIN joined by a trace on the underside. Leave it intact and:

  • +5V from the backplane reaches VUSB through D3, so the board back-feeds the host PC's USB port whenever the crate is powered.
  • With USB also plugged in, the backplane supply and the host's 5 V fight each other across D3 with nothing but their output impedances between them.
  • D2 is shorted out and does nothing.

How

  1. On the underside of the Teensy 4.1, find the two small pads joined by a short trace, marked for separating VUSB from VIN (PJRC documents this as the standard "external power" mod — check their photo for the exact location on your board revision).
  2. Before cutting, meter continuity between the VUSB pad and the VIN pin. It should read ~0 Ω. This confirms you have found the right trace.
  3. Cut the trace with a sharp knife. Cut once, cleanly — do not scrape a wide gouge.
  4. After cutting, meter again. VUSB to VIN must now read open (> 1 MΩ). This is the authoritative check; do not proceed on visual inspection alone.
  5. Note the cut in the build record. It is invisible once the Teensy is mounted, and the next person to touch the board cannot tell by looking.

If you ever replace the Teensy, repeat this step. A fresh Teensy dropped into a working board re-introduces the fault silently.

Step 2 — First power-on (no TEC, no sensors)

2.1 SW1 to the OFF position (~SHDN low) before applying power. This is the only shutdown path on the board — firmware cannot disable the MAX1968 (noted in the lab notebook, session 42, kept internally). D1 lit = driver armed.

2.2 Apply +5 V from the backplane. Confirm at the test points:

Rail Expected Source
+5V 5.0 V backplane J8
+3.3VD 3.3 V Teensy onboard LDO (U1.46)
+3.3VA 3.3 V U3 TPS7A2033
Net-(U5-AVDD) = +3.3VA through R25 (0 Ω)
+2V5 2.500 V U4 ADR4525
Net-(U4-IN) ≈ 4.87 V +5V through R16 (100 Ω)
Net-(U2-MAXIP) (= REF) 1.500 V MAX1968 internal reference
Net-(U2-MAXV) wherever R6 is turned 50 kΩ trimpot, not preset
Net-(U2-MAXIP) wherever R14 is turned 50 kΩ trimpot, not preset
Net-(U2-MAXIN) wherever R10 is turned 50 kΩ trimpot, not preset

2.3 Read the AD7124 ID register before trusting anything downstream (project rule, Appendix A). Expect 0x1_. This is the first-light gate.

Step 3 — Sensors, still no TEC

Connect the NTCs to J6/J7/J5 and confirm the DC node voltages with a DMM before believing any conversion. Predicted values (10 kΩ NTC at 25 °C):

Arm Excitation Nodes Expected
1 (J6) IOUT 50 µA on AIN7 IOUT node / NTC_HI1 / NTC_LO1 1.835 / 0.735 / 0.235 V
REFIN1± = 1.835 / 0.735 V → V_REF = 1.100 V
2 (J7) +2V5 NTC_HI2 (AIN13) / NTC_LO2 (AIN12) 1.869 / 0.631 V
3/4 (J5) JP1: A = +2V5, B = REFOUT NTC_HI3 (AIN3) / NTC_LO3 (AIN4) 2.500 / 1.250 V

If a node is wrong, stop. Do not connect the TEC to debug a front end.

Step 4 — TEC connection and the operating envelope

Only after Step 3 passes.

TEC: RS PRO 2172415, Imax = 2.5 A (both directions), Vmax = 7.62 V @ Th 25 °C (8.1 V @ 50 °C), R_AC = 2.67 Ω @ 25 °C (3.01 Ω @ 50 °C), Qmax = 10.6 W, ΔTmax = 67 °C.

🔴 The three limits are trimpots, not fixed dividers. Nothing is preset. R6 → MAXV, R14 → MAXIP, R10 → MAXIN are 50 kΩ potentiometers from REF to GND; full travel reaches ±3.000 A, above this TEC's 2.5 A rating. They protect nothing until you turn them down. Transfer functions and travel are in PCB/review-2026-08-10-drive-stage.md.

Set them with firmware/board_monitor + tools/panel reading them live and the TEC unplugged. Targets, and how they interact with that TEC:

MAXV  → 4.00 V  ⇒  I ≤ 4.00/2.67 = 1.50 A  (cold)   ← THE BINDING LIMIT
                   I ≤ 4.00/3.01 = 1.33 A  (Th = 50 °C, TEC resistance rises)
MAXIP → 1.50 A  ⇒  matches what MAXV already allows; above this it cannot bind
MAXIN → 1.50 A  ⇒  same, in the heating direction
MAX1968 output range = ±4.3 V at VDD = 5 V

Set that way the board is a ±1.5 A machine, 60 % of the TEC's rating, in both directions. MAXV is what saturates the loop, not the current limit. Expect ~8.9 W of pumping at ΔT = 0.

Supply budget: at 1.5 A, P_TEC = 1.5² × 2.67 = 6.0 W → roughly 1.5 A drawn from +5 V including the Teensy. Confirm the LRS-100 has the headroom.

Sanity-check current direction before closing the loop. V_CTLI > 1.50 V = cooling, current flowing OS2 → OS1 (out of J4.1, into J4.2). Verify with a thermometer on the block and an open loop before you let the PID drive it.

Step 5 — Firmware guards that must be in place before closing the loop

These are not optional. The reasoning is in the lab's internal notebook (session 42).

  1. Open-sensor latch on arms 2/3/4. Those arms have no hardware fail-safe — an open sensor rails the reading to full scale, which reads as coldest, which commands maximum heating. A railed code (0xFFFFFF) must latch a fault and force CTLI to zero current, not be silently discarded and retried the way ad7124_ratiometric.ino:200 does.
  2. Check REF_DET_ERR on arm 1. Arm 1's hardware fail-safe is real but only fires through that flag — on an open sensor both V_AIN and V_REF go to zero, so the raw code is indeterminate. The flag is the signal, not the value.
  3. Never use analogWrite(pin, 0) as "off". That drives CTLI to 0.03 V ≈ full current in the heating direction. Zero current is 45.45 % duty (CTLI = 1.500 V).
  4. Clamp CTLI to its specified range, 0.5 V – 2.5 V, i.e. roughly 14 %–76 % duty.
  5. Per-arm reference buffer settings. Arm 1 needs REF_BUF on (0x01E0); arm 2 needs it off (0x0068), because REFIN2(−) is tied to GND, which is legal unbuffered and out of spec buffered. Copying arm 1's config onto arm 2 goes out of spec with no error flag.

Never do

  • Never power the board with SW1 armed and an untested loop. 1.5 A into a laser mount is enough to do damage before you can reach the switch.
  • Never connect the TEC before Step 3 passes.
  • Never solder a replacement Teensy without repeating Step 1.
  • Never plug the TEC into J5/J6/J7.

Working conventions

  • The bench is the final authority. Every calculation states the number it predicts, so it can be checked against reality. When the bench disagrees, the bench is right.
  • One fact, one home. A number lives in exactly one file — docs/sources.md if it appears more than once — and everywhere else links to it. Superseding means updating it there and deleting every surviving copy, not adding a note beside it.
python3 tools/check_docs.py

Checks the docs for oversized sections and duplication.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages