AI Foundry · thermal camera research

Thermal Master P3 — optics, streaming and mount for ET-SoC-1 PCB-backside monitoring

Research notes, Fri 9 Oct 2026 (week 41), written for AI Foundry. AI Foundry work. Scope: continuous monitoring of the BACK of an ET-SoC-1 PCIe card's PCB at 3–10 cm in the Studio 45 rack. Builds on three prior gyroscope reports rather than repeating them: - 3 Oct, camera + Pixel streaming — the rig, the hub, ScreenStream, P2 Pro vs P3. - 6 Oct, P3 reading list — lock-in precedents, the research lineage, emissivity. - 6 Oct, riser + mount §4 — the box contents, cable chain, long-run rules.


1. Optics — pixel pitch settled by geometry

The vendor FOV and focal length over-determine the pitch, so it can be derived rather than trusted:

pitch array size HFOV from 2·atan(W/2f), f = 4.3 mm matches vendor 40° × 30.2°?
12 µm 3.072 × 2.304 mm 39.31° × 30.00° yes, within 1.7%
17 µm 4.352 × 3.264 mm 53.68° no, off by 34%

→ 12 µm confirmed, and the vendor says so too. The P-Series manual's spec table (rev. 28 Mar 2025) reads Detector VOx 256×192 @12µm, NETD ≤35mK, 25Hz, FOV 40°(H)x30.2°(V), −20°C~600°C, ±2°C (±2% of reading), Manual Focusing Lens, body 57 × 27 × 17.2 mm (manual PDF; vendor page — note its slug is p3-pcb-master: PCB work is the marketed use). Band is 8–14 µm (manual FAQ ⑤). IFOV = 12 µm / 4.3 mm = 2.79 mrad from the pitch, 2.73 mrad from 40°/256 — the same number twice. The camera reports its sensor as InfiRay RTC2121W in register 0x0e (p3-ir-camera issue #2); USB VID 0x3474 is registered to Yantai Raytron, InfiRay's parent, and the interface string is com.thermalmaster.pios3 (Gough Lui's USBTreeView dump). The likely module is the InfiRay Tiny1-C, whose lens options are 2.0 / 3.2 / 4.3 mm — unconfirmed.

Three corrections to the working spec: - The range is two bands, not one sweep. −20…150 °C and ~100…550 °C, not selectable together; the advertised 600 °C does not appear in the high band as reviewers saw it (LON.TV, IT-Connect). This matches p3-ir-camera's high/low gain exactly. Use the −20…150 °C band. - The f-number is published nowhere. The DOF column below assumes f/1.0; at f/1.2 every DOF figure is 20% larger. The detector class is spec'd at f/1.0 and commercial 4.3 mm LWIR optics run f/1.0–f/1.2 (MKS SUPIR 4.3 mm f/1.2). - Neither vendor app exports per-pixel data. PCWorld "was unable to find an option to export the measurement data as a CSV file" (review); video is burned-in palette only. That alone decides the streaming question: anything radiometric has to come from the open drivers, not the app.

Footprint against distance

Two columns, because at these distances the far-field formula is wrong. W = 2·d·tan(FOV/2) assumes d ≫ f; the thin-lens result is W = array · (d − f)/f, which subtracts one focal length (4.3 mm) of standoff. The difference is 23% at 2 cm and under 3% past 15 cm.

distance far-field W × H (mm) thin-lens W × H (mm) mm/pixel magnification DOF at f/1.0 45 mm package spans
2 cm 14.6 × 10.8 11.2 × 8.4 0.044 0.274× 0.41 mm 1027 px (overflows)
3 cm 21.8 × 16.2 18.4 × 13.8 0.072 0.167× 1.00 mm 627 px (overflows)
5 cm 36.4 × 27.0 32.6 × 24.5 0.128 0.094× 2.97 mm 353 px (overflows)
6.2 cm 45.1 × 33.5 41.2 × 30.9 0.161 0.075× 4.64 mm 279 px — just fills the frame
8 cm 58.2 × 43.2 54.1 × 40.6 0.211 0.057× 7.86 mm 213 px
10 cm 72.8 × 54.0 68.4 × 51.3 0.267 0.045× 12.4 mm 168 px
15 cm 109.2 × 80.9 104.1 × 78.1 0.407 0.030× 28.4 mm 111 px
20 cm 145.6 × 107.9 139.8 × 104.9 0.546 0.022× 50.8 mm 82 px
37 cm (shelf edge, 3 Oct plan) 269.3 × 199.7 261.3 × 195.9 1.021 0.012× 176 mm 44 px

Derived with f = 4.3 mm, 12 µm pitch, blur circle = one pixel, f/1.0 assumed (the P3's aperture is not published). DOF is the binding constraint, not resolution. At 3 cm the whole depth of field is 1.0 mm, so a fixed fixture would have to hold its standoff to ±0.5 mm across hours of thermal expansion — not achievable with printed plastic beside a 60–80 °C board. At 8 cm the DOF is ±4 mm, which a printed fixture holds easily.

Design distance: 7–8 cm. It puts the 45 mm package on 213 pixels at 0.21 mm/pixel, keeps the whole package plus its decoupling field in frame, and has enough DOF to survive the fixture creeping.

The "focus from 8 mm" claim is the one spec to distrust

It is not in the manual at all, and the vendor page gives three mutually inconsistent ranges on one page — "8mm … to 80yd", "3-in to 33-ft", "From 8mm to 30m" (vendor page). No reviewer has measured the near end. Gough Lui only repeats "as close as 8mm" and calls it "ridiculously small".

The physics says why to be careful: at an 8 mm object distance a 4.3 mm lens sits at 1.16× magnification, needing the lens to travel 5.0 mm past its infinity position inside a 17.2 mm body. The field would be 2.64 × 1.98 mm, i.e. 10 µm per pixel, with 0.4 mm of DOF — and above 1:1 the effective f-number roughly doubles, costing about 4× the signal and visibly spoiling the 35 mK figure. Treat 8 mm as an optical limit, not a working spec. Measure it on day one with a ruler in frame; it changes nothing about the 7–8 cm design point.

There is no macro accessory for the P3 — its macro is the manual focus ring. The siblings P2 Pro / P2 Max take a magnetic macro lens, which the same manual rates at a 4 cm working distance resolving 0.25 mm; it is not sold separately at any published price. So nothing to buy here.


2. The protocol and the drivers (verified against the repos)

p3-ir-camera (github.com/jvdillon/p3-ir-camera, Apache-2.0, 95★, 37 commits, last commit 2026-02-07 e3205dc, no releases, Python ≥3.10, classifier POSIX :: Linux only):

Six more independent drivers now exist, all newer than jvdillon's:

repo lang / licence ★ last commit what it adds
skywalker1905/thermal-camera-viewer Python / Apache-2.0 16 2026-09-21 PyQt5 GUI, ROI stats, 6 palettes, MP4 via ffmpeg, .deb, writes to v4l2loopback /dev/video10. 0 open issues
ferrolho/thermal-master-viewer JS / Apache-2.0 0 2026-08-16 WebUSB, in a browser, works on Android. PNG + per-pixel CSV export, hover temperature, wake-lock
xaionaro-go/thermalmaster Go / CC0-1.0 9 2026-09-06 v4l2loopback, 40+ colormaps, joint bilateral upsampling
josepgomis/unofficial-thermalmaster-p3-sdk Python / Apache-2.0 1 2026-10-02 ROS 2 Humble, 3474:45a2 only. The most recently maintained
heatpeek/HeatPeek Swift / MIT 0 2026-09-23 macOS via IOUSBHost, no libusb; also handles the P2 Pro
Phalphy/OpenP3 Python / MIT 17 2026-01-15 abandoned at 2 commits

Not UVC, under any mode — three independent confirmations. The interfaces are vendor class 0xFF: iface 0 alt 0 commands, iface 1 alt 0 idle, iface 1 alt 1 video with ep 0x81 IN / 0x02 OUT (ferrolho README, which also explicitly rejects a community claim of bInterfaceClass=0x0E); OpenP3 calls it "a proprietary USB handshake that prevents it from working with standard UVC drivers"; HeatPeek's table lists the P3 as "vendor protocol" against the P2 Pro's "UVC". Every "UVC" in this ecosystem is a synthetic v4l2loopback sink the host writes into, not the camera presenting itself. Class 0xFF is the reason this is tractable: it is not in WebUSB's protected-class list and no OS driver binds to it, so a browser can claim it (Chromium intent-to-ship on interface-class filtering).

Android: no native open-source app or library handles 3474:45A2. The nearest analogue, JAMBD/android_infiray_viewer, hard-filters 0x0BDA:0x5830 and uses a UVC-class device_filter.xml that can never match the P3's 0xFF interfaces. So the 6 Oct finding stands — a native Kotlin app would be the first. But it is now unnecessary (§3).


3. The geometry problem the mount has to solve first

From the 3 Oct rack survey: each ET card stands at the back of its open frame, its PCB back facing the CPU tower cooler a few cm away, with a 120 mm fan against the heatsink side. So the 3–10 cm working distance in the ask is not a preference — it is all the gap there is. Three ways out, in increasing cost:

  1. Move the card to PCIEX8, slot 4, about 61 mm farther from the CPU — free, keeps Gen4 x8, already worked out in the 6 Oct report §3. This is what turns a 3 cm gap into a ~9 cm one and makes the whole 7–8 cm design point possible.
  2. A 45° fold mirror — a periscope, so the camera lies flat alongside the card instead of facing it. The optical path still measures 7–8 cm, but the required gap collapses to the mirror's diagonal (about 35 mm for a 25 × 25 mm mirror). This is the cheapest fix if the card cannot move. - Use bare polished aluminium or front-surface gold, never an SiO/SiO₂-protected mirror. A DTIC study measured protected aluminium falling to 71% at 45° (and 54% at 60°) in the 8–12 µm band for one polarisation, while bare aluminium changes only ~0.5% between 0° and 60° (ADA113288); Tydex attributes the dip to the Si–O bond absorbing at 9–11 µm and recommends unprotected aluminium for in-instrument use. Diamond-polished 6061 measures about 95% over 7–14 µm (Michigan State, 2002); protected gold averages >98% from 0° to 60° (Excelitas, Edmund). - The mirror's own 2–5% emission adds a bias at the mirror's temperature, so keep it out of the card's plume and log its temperature; and check the sightline, because a fold mirror can return the camera's own cold housing into the frame (narcissus).
  3. A riser or cable kit — the 6 Oct report's §3 options, €120–140, which also buys a place for a power meter and a hard cut-off.

A fourth option is to accept an oblique view. At 30–45° off normal the footprint keystones and the apparent emissivity of FR4 falls, but for differential work (this pixel vs that pixel, this second vs last second) it is nearly free. Say so in the record rather than pretending the geometry is clean.

The camera's own thermal envelope — the constraint that sizes the mount

The P-Series manual states a recommended service temperature of −10 °C to +55 °C and a storage range of −40 °C to +85 °C (manual PDF, Precautions and spec table). Parked 7 cm from a 60–80 °C board inside a rack, the camera's metal body sits in the plume. Two consequences:

  1. The mount must keep the camera in moving room air, not in the card's exhaust. The 6 Oct report already said "keep it out of the exhaust… a microbolometer drifts with its own temperature"; the manual turns that from advice into a limit. A thin printed heat break between the fixture and the camera cradle, and a sightline that crosses the plume rather than sitting in it, are the two cheap measures.
  2. Log the camera's own temperature. The firmware log readable from the status register carries shutter events (P3_PROTOCOL.md); an external DS18B20 taped to the body is the simpler route, and it gives the drift regressor the covariate it needs.

The manual also confirms that emissivity, distance, ambient temperature, humidity and reflected temperature are all host-side corrections ("Temperature Correction: Emissivity, Distance, Environmental temperature"), consistent with issue #2 finding no USB traffic when emissivity changes. Capture raw and correct later.


4. The physics that sets the standoff — and it is not 3 cm

The PCB is a low-pass filter in both space and time, and its blur, not the camera's pixel, is what limits what the backside can show. Treating the card as a conducting plate losing heat from both faces, the lateral spreading length is L = sqrt(k_eff·t / 2h).

For a 6-layer, 1.57 mm card with 6 × 35 µm copper, the in-plane effective conductivity is 52 W/m·K (13% copper by thickness; FR4 in-plane ~0.9, through-plane ~0.3, copper 385 W/m·K):

convection on the back h (W/m²K) spreading length L steady-state hot-spot FWHM
still air 5 91 mm ~210 mm
natural convection 10 64 mm ~150 mm
light airflow in the rack 30 37 mm ~87 mm
the card's 120 mm fan 60 26 mm ~61 mm

So in steady state the backside is a board-wide blob, 60–210 mm across, wider than the 45 mm package. No standoff and no pixel pitch recovers per-shire structure from a static backside image. That matches the lab's own 30 Sep finding that a single frame of the lid "shows nearly one temperature", and the 6 Oct reading list's conclusion that lock-in is the right tool for this view.

Lock-in changes the answer, because modulation frequency sets the resolution. The thermal diffusion length is µ = sqrt(α/(π·f)), with in-plane α = 3.1 × 10⁻⁵ m²/s:

modulation period in-plane µ (resolution) camera pixels per µ at 8 cm at 20 cm
0.05 Hz 20 s 14 mm 66 26
0.1 Hz 10 s 9.9 mm 47 18
0.5 Hz 2 s 4.4 mm 21 8
1 Hz 1 s 3.1 mm 15 6
2 Hz 0.5 s 2.2 mm 10 4

Against this there is a ceiling: the heat has to cross the board, and τ = t²/α_z low-passes it. With bare FR4 (k_z ≈ 0.3) τ ≈ 14 s, corner 0.011 Hz; under a BGA's dense via field (k_z ≈ 5–20) τ ≈ 0.2–0.8 s, corner 0.19–0.75 Hz. The usable window is roughly 0.1–1 Hz, giving 3–10 mm of effective spatial resolution — which is shire-scale, so the experiment is worth doing. LOCK-IN.md's --period 1.0 default sits at the top of that window; plan to sweep 1, 2, 5, 10 and 20 s periods and expect amplitude to rise and resolution to fall as the period lengthens.

Three conclusions that change the hardware plan

  1. The camera massively oversamples the physics. At 8 cm, 0.21 mm/pixel against a 3–10 mm diffusion length is 15–47× oversampling. Bin the frames instead: 4 × 4 binning turns 35 mK of temporal noise into 8.8 mK (√16) at 0.84 mm/pixel, still 4–12 px per µ, for the cost of a reshape(...).mean(). ⚠️ But see §10: temporal noise is not the limit — a 0.6 °C fixed-pattern non-uniformity is, and binning does not touch it. Lock-in does.
  2. 15–20 cm is sufficient, and safer. At 20 cm the pixel is 0.55 mm, 2 × 2 binning gives 1.1 mm at 17 mK, and 6–18 px per µ — still oversampled. In exchange the camera leaves the plume (the manual's 55 °C limit), the DOF grows to ±25 mm so no fixture tolerance matters, the whole 146 × 108 mm frame covers the card, and the mount becomes a clamp on a shelf post instead of a fixture inside the gap. If the goal is die-activity mapping, mount at 15–20 cm and don't build the close-up fixture at all.
  3. The close-up fixture is for a different target. 7–8 cm earns its keep on discrete heat sources that really are small and self-emitting — the VRM FETs and inductors on the back, individual decoupling caps, a solder defect, a bare package lid or a decapped die. Those are not blurred by the board, because they are the source. Build the fixture if that is the experiment; don't build it for the die map.

5. Streaming — the decisive finding is WebUSB

Neither vendor app helps. No casting, no RTSP, no radiometric export: PCWorld could find no CSV export (review), video is burned-in palette, and the Android still is a 1440 × 1080 "radiometric" JPEG while Windows writes 512 × 384 — two formats neither of which the other can re-analyse (Gough Lui). Turn X³ off: "The X³ image enhancement mode did not deliver significant benefits… while increasing the chances of image artifacts" (same review).

But the camera's vendor interface class 0xFF means a browser can claim it. WebUSB's protected-class filter blocks audio, video, HID, mass storage, smart card and wireless-controller interfaces; vendor-specific is not on that list (Chromium intent-to-ship), and no OS driver binds to class 0xFF, so the interface is free. Chrome for Android has supported WebUSB since Chrome 61 (MDN compat); Android WebView does not, and Firefox and Safari have no WebUSB at all.

ferrolho/thermal-master-viewer (Apache-2.0, pushed 2026-08-16) already does this, and its README's platform table says of Android: "works — plug the camera straight into the phone and open the page." It gives 25 fps at 256 × 192, hover for per-pixel temperature, live max/min/centre/average, freeze, one-click PNG and per-pixel CSV in °C, and a Screen Wake Lock to keep the display on. A hosted copy answers HTTP 200 at https://ferrolho.github.io/thermal-master-viewer/ (checked Fri 9 Oct 2026). It needs a secure context — https:// or http://localhost; a file:// URL will not work — and on Linux the same udev rule for 3474:45a2.

That is a zero-install path to a live thermal view on the Pixel today, with radiometric data the vendor app cannot give. It is not yet streaming off the phone, which is the gap to close.

Closing the gap: a WebSocket push is a small fork

The repo is 11 small ES modules; web/js/usb.js (10 KB) is the whole device layer and its Camera class hands each frame out as {ir, thermal, w, h} with thermal a Uint16Array(256*192) (source). Adding ws.send(thermal.buffer) in the frame loop is tens of lines. Bandwidth: 98,304 B/frame, so 2.4 MB/s at 25 Hz, 0.5 MB/s at 5 Hz — fine over Wi-Fi, and 5 Hz is the sane default given §4's 0.1–1 Hz physics.

The blocker is HTTPS, and it is a one-line fix with a prerequisite. A page on https://ferrolho.github.io cannot open a ws:// socket to a tailnet host — mixed content. Options: - Enable HTTPS on the tailnet and host the fork there. tailscale serve already fronts http://intel/ on port 80 → 127.0.0.1:31080, and I verified on this machine (Fri 9 Oct) that nothing answers on port 443, so HTTPS certificates are not yet provisioned. Turning them on is an admin-console toggle plus tailscale cert (Enabling HTTPS, Tailscale Serve). Then the phone loads https://<host>.tailf64870.ts.net/thermal/, which is a secure context, and wss:// to the same origin needs no extra certificate work. Note the hostname lands in public Certificate Transparency logs. - Serve the page from http://localhost on the phone (Termux + python3 -m http.server), which also counts as a secure context, and ws:// from an http: page is not mixed content. Clunkier, no new certs.

Why the phone is still the wrong permanent host

So: phone for the walk-around and the first pictures today; Linux for the permanent rig.


6. Material for a printed fixture

Numbers from manufacturer data, with the load condition stated, because HDT without a load is meaningless:

material heat deflection / softening source verdict for this job
PLA ~55–60 °C Tg, softens in warm air — no. Creeps under its own weight beside this card
PETG HDT 68 °C at 0.45 MPa, 62 °C at 1.8 MPa Prusament PETG TDS v1.1 (ISO 75, reseller-hosted copy) marginal. Fine for an unloaded cradle, not for a cantilever
ASA ~93 °C at 0.45 MPa ("no visible deformation up to nearly 93 °C", Prusa) Prusament ASA primary choice. Needs an enclosure and ≥250 °C hotend
PC Blend "heat tolerance up to 113 °C" (test condition unstated) Prusament PC Blend fallback if ASA warps; hygroscopic, 270 °C+
PA / nylon, PPS, PEEK higher still — overkill, and nylon absorbs water and moves

Creep, not Tg, is the failure mode. A part at 50 °C under constant load relaxes over days even well below its HDT, and §1's DOF table says the standoff has to hold to a few mm. Two measures beat any material choice: keep the plastic unloaded (let it locate the camera, not span a distance) and make the span metal. Print the cradle and the focus-ring collar; buy the arm.


7. Design brief for the fixture

Body to design around: 57 × 27 × 17.2 mm, metal, ~26 g, USB-C female socket at one end, manual focus ring, no tripod thread and no clip (manual spec table; the 6 Oct report's box inventory). Assume nothing about the focus ring's diameter until it is measured with calipers.

  1. Design distance 7–8 cm, with 15–20 cm as the default for die-map work (§4). At 7–8 cm the DOF is ±4 mm, which printed plastic holds; at 3 cm it is ±0.5 mm, which it does not.
  2. Anchor to the card's own PCIe bracket, not the case frame. Counter-intuitive but right: what matters is relative motion between camera and target. Tied to the bracket, camera and board shake together and the image is still; tied to the frame, every fan pulse moves the target across 0.2 mm pixels. The 3 Oct note about clamping to a shelf post applies to the 37 cm standoff, where the pixel is 1 mm and the card is far away; it inverts at 8 cm. Keep a second, independent mount point on the frame only as a safety tether.
  3. Never touch the board. Locate off the bracket's screw hole and the slot's own geometry; keep ≥5 mm clearance to every component and ≥10 mm to anything with a heatsink.
  4. Thermal break between fixture and cradle. The camera is rated −10…+55 °C (§1) and its own drift is a first-order error term. A narrow, thin-walled neck between the hot-side anchor and the camera cradle, plus an open cradle that lets room air over the body, costs nothing. Do not shroud the camera itself.
  5. Baffle selectively, never fully. A full hood traps heat (breaking 4) and changes the convection the experiment is measuring. Use a single flat blade between the camera and the nearest hot reflector — the CPU cooler — and leave the rest open. The thing being excluded is the camera's own cold image and the cooler's, reflected off copper and connectors; an emissivity patch on the target (Scotch 88 vinyl tape, ε = 0.96, FLIR) does far more for that than any baffle.
  6. Focus-ring lock as a printed split collar with one M3 screw, not tape. Tape was the 6 Oct recommendation and it works, but a collar is repeatable and survives being knocked. Print it in the same material; add a scribed index mark so the setting can be written down.
  7. Strain relief in two places: a printed P-clip holding the camera's in-box 48–50 cm extension to the fixture within 30 mm of the plug, and a second clip where the extension leaves the chassis. The plug must never carry load — the EEVblog thread traced intermittent disconnects on this camera to plug engagement (thread).
  8. Print it matte black and keep it out of frame. Any printed surface inside the field of view is a high-emissivity object at fixture temperature and will bias the scene's apparent background.
  9. Mark it. The 3 Oct report's label rule: "IR camera, please don't move — ping Yaroslav", and tell Nate.

The split that makes the material problem go away: print only the cradle, the collar and the clips (all unloaded, ASA); buy the span. A steel super-clamp and arm does not creep, does not care about 50 °C, and can be re-aimed without reprinting.


8. Day-one measurements (all cheap, all settle an open question)

  1. Ruler in frame at 5, 8, 10 and 20 cm. Settles the footprint table against the real lens and whatever "distance" the vendor means. Five minutes.
  2. Close-focus limit. Wind the ring to its near stop and find the distance where a 1 mm feature is sharp. Settles the "8 mm" claim.
  3. The gap. Card back to CPU cooler, card top to the shelf above, bracket screw to package centre. This is the measurement the whole mount depends on, and the 3 Oct report already flagged it as a one-minute check nobody has done.
  4. Camera body temperature after an hour in position, against the 55 °C limit.
  5. Shutter cadence and frame loss over 10 minutes — confirm ~90 s, count dropped frames.
  6. Warm-up drift: 30 minutes staring at a fixed, taped target; plot the apparent temperature. The 6 Oct rule is to throw the first 20 minutes away; this says whether that is enough.
  7. A plugged-and-unplugged test on whatever host is chosen — the camera must come back without hands.
  8. Binning check: compute the pixel-to-pixel standard deviation on a flat taped target at 1 × 1 and 4 × 4; confirm the √16 improvement toward ~9 mK.

9. Risks and unknowns

Protocol and software - p3-ir-camera has had no commit since 7 Feb 2026 and has an unanswered refactor PR (#20). It is one person's reverse engineering; the mitigation is that five independent re-implementations now exist in Python, Go, JS and Swift, so the protocol knowledge is no longer single-sourced. - Every published observation is from firmware v00.00.02.17. No repo records a protocol-breaking update, and none of them would know. The 3 Oct rule stands: do not let the vendor app update the camera's firmware. - USBError crashes were real and were fixed by chunked reads, but recovery is Linux-only: on macOS the error is an overflow and on Windows "the device is not working, and is not recoverable" (#7). Any multi-hour logger needs a supervisor that reconnects, as the 6 Oct report already said. - The shutter-segment constants are P3-measured and silently wrong on other models (#17) — a reminder that this code can lose a frame segment without raising anything. Validate frame sizes, do not trust them.

Measurement - ±2 °C is useless for absolute die temperature and fine for differential work. Everything in §4 depends only on differences. Never publish an absolute number from this camera without a reference. - The P3's own body is the coldest thing in its field of view. Copper, connectors and the heatsink will mirror it. Tape what matters; state emissivity assumptions. - X³ super-resolution must be off, both because Gough Lui found it adds artefacts and because binning (§4) is the opposite operation and the one that helps. - Camera drift over hours against the 55 °C service limit is, on current evidence, the largest uncontrolled error term. It has no published characterisation for this camera.

Still unknown after this research - The f-number (sets every DOF figure in §1 to within 20%). - The real close-focus limit and whether 1.16× magnification is achievable. - The focus ring's diameter and travel — needed before any collar is drawn. - The card's actual layer count and copper fraction, which set §4's spreading length. The 52 W/m·K figure is a 6-layer assumption, not a measurement of this board. - Whether 0xFF interface-0 also carries bulk endpoints 0x84/0x05: ferrolho says yes, jvdillon documents interface 0 as control-only. Unreconciled; lsusb -v on the real device settles it. - Whether the Pixel enumerates the camera through a PD hub at all. No published report of a Pixel plus this camera plus a hub exists; the 3 Oct report flagged the same gap and it is still open.


10. What practitioners actually get, and the correction it forces

The physics converges from two directions

My §4 fin calculation (steady-state blur 60–210 mm) and the photothermal literature agree once you separate DC from modulated measurement. Burgholzer et al., Heat diffusion blurs photothermal images with increasing depth, give the depth-dependent resolution limit δr(x) = π·x / ln(SNR) — blur grows linearly with depth and SNR helps only logarithmically, with lateral resolution 2.4× worse than axial. At x = 1.6 mm and SNR 1000 that is ~0.73 mm axial, ~1.8 mm lateral; at SNR 100, ~2.6 mm. My µ = sqrt(α/πf) gives 3.1 mm at 1 Hz. Two independent methods, the same answer: 1–3 mm is the floor from the backside, and the camera over-resolves it by ~10×.

Timing agrees too: through 1.6 mm of FR-4, L²/α ≈ 10–20 s with the slowest mode at 1–2 s, and TI's TMP108 datasheet measures a 68 s board time constant on a 32-mil board. FR-4 is through-plane 0.29–0.343 and in-plane 0.81–1.059 W/m·K (FR-4); copper-heavy power regions reach ~100 W/m·K and signal layers 10–50 (US9113557), i.e. ~67× anisotropy, which smears laterally exactly as §4 predicts. A compute burst shows on the back over 1–10 s, never milliseconds.

🛑 The correction: fixed-pattern non-uniformity, not NETD, is the noise floor

Gough Lui's flat-field test on the P3 read 25.0–25.6 °C across a uniform target — a 0.6 °C spread, about 17× the 35 mK NETD, and it is spatially fixed, so it looks exactly like a hot spot (review). This breaks §4's binning claim as stated: binning and frame averaging reduce only the temporal noise; they do nothing about a fixed offset. Three consequences:

  1. Lock-in is not a nice-to-have, it is the only way to beat the dominant error. Demodulating at a known frequency rejects every static offset, including FPN, housing drift and the reflected scene. That is why the two hobby results exist at all: 330 µW on a 33 kΩ resistor (LOCK-IN.md) and 1.6 mW on a VBUS divider plus a readable decapped AD9042 die (Dmytro Engineering, Hackaday 28 Oct 2024). Neither publishes a mK floor, and the naive 35 mK/√1500 ≈ 0.9 mK is wrong for exactly this reason.
  2. Every DC image needs a reference frame, captured with the shutter closed, and re-captured after each automatic shutter event. Dmytro's T2S+ work does this by forcing the shutter shut and subtracting; the P3 has a documented shutter command (0136…) for the same purpose.
  3. Research-grade lock-in reaches 20–35 µK after 1000 s on 128×128 cooled systems (MPI Halle); a low-cost microbolometer rig works only over 0.01–1 Hz, with phase contrast approaching the detection limit above that (TUM). That window is the same one §4 derived from the board. Everything points at 0.01–1 Hz.

Practitioner settings worth copying

EEVblog's Fraser, the Thermal Imaging moderator, runs the two best threads on this (PCBA profiling and repair, minimum specification):

The closest published precedent to this experiment

Igor's Lab imaged the rear of an RTX 5000 PCB behind the NVVDD VRM and found 78–80.5 °C, attributed to via-field current concentration and "limited horizontal copper spreading" — with no stated emissivity and no validation against telemetry. Guru3D notes a backplate makes rear readings "indicative only". That is the honest ceiling of the genre, and it is still a useful result.

Surface prep

Nobody has published continuous PCB-backside monitoring to infer die activity with one of these cameras. The practitioner base is fault-finding and spot checks. That remains true after this search, as it was after the 6 Oct one.


11. Mount hardware: what exists

MakerWorld blocks automated fetching (HTTP 403 on makerworld.com/en/models/2283135, checked Fri 9 Oct 2026 — the same wall the 6 Oct session hit). From search indexing, model 2283135 is a "Thermal Master P3 protective cover system": the designer's stated motive is that the factory frame is too fragile, it adds a rotating lens cover for storage, and the rear piece is explicitly meant to be adapted — the listing invites users to make their own fittings, and a GoPro-mount version is part of it. Print profile: 0.4 mm nozzle, 0.1 mm layers, 3 walls, 15% infill, ~2.1 h. That is close to the ideal starting point for §7: an enclosing cradle with a user-replaceable back. makerworld.com/models/2283135 — open it in a browser and check the fit against calipers before printing; the file list could not be read.

MakerWorld 2903257 is a handheld grip whose oversized focus knob is worth copying (6 Oct report).

Thermal Master's own accessory line is thinner than the reviews imply. Its site lists a Phone Holder at $39.99 and nothing else for the P3; PCWorld mentions a bundle "with a 6D inspection stand" but names no price, and a site search for stand and 6D finds no such product — treat the 6D stand as unconfirmed. Fraser, on a desk mount: Thermal Master offers none for the P3 (EEVblog).

Buy the span, print the cradle. The 3 Oct report already specified the metalwork and it is still the right answer: SmallRig 2058 super clamp + SmallRig 2065 140 mm magic arm, $55–85 together, plus a ~$5 GoPro-to-¼"-20 adapter to meet the printed cover. Fraser independently prefers a parallel-arm boom over a gooseneck for exactly this job. Manfrotto's 035 clamp + 237HD arm is the heavier equivalent.

Cable: use the in-box 48–50 cm USB-C male-to-female extension and nothing else at the camera end. Leave the USB-A adapter in the box — EEVblog traced intermittent disconnects to its shallow plug, one user reporting disconnection "with the slightest lateral movement". For a longer run, the 6 Oct chain is hub → 2–3 m USB 2.0 A-to-A extension → a good C-female-to-A-male adapter → the in-box extension → camera.


12. The Linux path (the permanent rig)

Bandwidth is a non-issue. 197,632 B × 25 fps = 4.94 MB/s, about 8% of USB 2.0 High-Speed's nominal 480 Mbps, and the thermal half alone is 2.46 MB/s. Nothing here stresses a Pi 5 or a lab host.

Least-code route: thermal-camera-viewer's v4l2 sink. skywalker1905/thermal-camera-viewer (Apache-2.0, v3.3.0, last commit 2026-09-21, 0 open issues) installs as a .deb on Ubuntu, Debian and Raspberry Pi OS and ships thermal-camera-viewer-uvc, which writes frames to /dev/video10. Its README is explicit that it does not create the node — v4l2loopback does. From there any standard streamer works, which means no new code at all for a first picture on the tailnet.

v4l2loopback status (2026). Upstream is umlaeute/v4l2loopback; 0.15.4 was packaged by several distributions mid-2026 (Arch utils built 2026-06-28, Alpine edge 0.15.4-r0, 2026-07-11), Debian trixie carries 0.15.0-2 and sid 0.15.1-1 (tracker). ⚠️ Ubuntu 24.04 is still on the 0.12.7 line (0.12.7-2ubuntu5.1, changelog 2026-01-20, which fixes a Linux 6.16 build failure and extends compatibility to 6.15+ — ubuntuupdates); Ubuntu 25.10's DKMS package is on 0.15.0. Check apt policy v4l2loopback-dkms and uname -r on the actual host before promising this works, and expect a DKMS rebuild on every kernel update — which is the main argument against putting it on a shared lab machine.

Avoiding the kernel module entirely. Two of the drivers need no v4l2loopback: - josepgomis/unofficial-thermalmaster-p3-sdk (Apache-2.0, last commit 2026-10-02 — the most actively maintained of the lot), validated on ROS 2 Humble, 3474:45a2 only. - xaionaro-go/thermalmaster (Go, CC0-1.0, 2026-09-06), a single static binary, which is attractive for an unattended service.

A WebSocket + canvas dashboard shipping the raw 16-bit half is the right end state, because it keeps per-pixel temperature in the browser (hover, ROI, colormap changes without touching the capture) and costs 98,304 B/frame — 2.4 MB/s at 25 Hz, 0.49 MB/s at 5 Hz, which §4 says is all the physics justifies. No such dashboard exists for the P3; ferrolho's viewer is the nearest thing and already does the browser-side half of the work (§5).

No pre-built Pi or web dashboard exists for this camera — searched and not found. The three Linux-capable drivers are desktop viewers; a small aiohttp/FastAPI layer is a few hundred lines.

Dedicated Pi 5 over the lab host, for three reasons that are not about performance: a kernel module and a udev rule on a shared lab box need Roman's agreement (the 6 Oct report flagged this); a multi-day USB capture that wedges should not be on the machine running the workload; and the Pi can live on whichever tailnet the viewer is on. Against that, putting the capture on the lab host itself gives lock-in timing for free, because the workload and the camera share one clock — which for the lock-in experiment is a real advantage worth the permission conversation.