Thermal camera: what happened on 15 October
The phone app now streams at 25 fps with the screen off. Updates reach the phone over Tailscale with no taps. Heat from the CPU is visible within 2 seconds. A heat-flow campaign was still recording when this was written.
The day in order
- Overnight: the second millikelvin campaign finished at 03:30. The results page gained the heat-flow movie as its lead, a no-load control, and the finding that the four bright spots are not CPU heat.
- Repository: the project became a git repository and was pushed to a private GitHub repo. A first
AGENTS.mdwas written and checked against the code by 21 agents: 99 of 116 proposed corrections were applied. - Charging: the new USB-C hub charges the phone at 1.39 A. The phone went from 26% to 47% over about an hour.
- Updates: an update pushed over Tailscale ran with no taps once two phone settings were changed (see below). Verified at 08:21.
- Screen off: the Spacesheep video fell to about 12–14 fps with the screen off. Cause found and fixed in app 1.3.2, committed as
822eede. - Heat-flow campaign: six recordings planned, starting 09:04 with all cores. The first GPU attempt lost frames when the phone app restarted (details below); the retry was clean.
- 11:01: this session moved from the M5 to the Intel. The recordings and the campaign stayed on the M5.
The phone app: 25 fps with the screen off
With the screen off the video dropped to about 12–14 fps, while the USB and HTTP stream stayed at 25 fps. The first guess was that Chromium throttles the page's timers when the screen is off. That was not the cause.
The cause: with the screen off, the phone holds its big CPU cores at half clock, 1.2 GHz instead of 2.4 GHz. The WebView's main thread then needed about 75 ms per frame, so only about 13 frames a second got through.
The fix moves the frame work out of the page's timers: the app pushes each new camera frame into the page, and the page builds the video frame from a lookup table with no canvas. That takes about 5 ms per frame.
Frames a second reaching the encoder, from the app's own counter. With the screen on: 22.7 before, 25.1 after. The camera makes 25 a second.
Millikelvin: heat shows within 2 seconds
Averaging the first 20 seconds after each load start shows heat leaving the CPU area within 2 seconds. Noise was lowest when the camera's shutter was calibrated 5 seconds before each start.
Noise per one-second picture, millikelvin, over 5–7 load starts. The control runs the same shutter timing with no load: its largest changes stay under 40 mK, against about 530 mK with load.
- Lock-in at a 20-second period: 3.1 mK per pixel calibrated, 4.5 mK raw.
- At a 120-second period the camera's own swing is ±0.69 to 0.83 °C in every mode tried. Mounting the camera off the target is still the main fix.
- Four bright spots at the top of the keyboard react within a second. They respond about as much to one core as to 18, so they are not CPU heat. Fans or a reflection are likely; not tested.
- Camera temperature reading: the word the code calls the scene mean averages 41 °C while the hottest pixel in view averages 36.7 °C. A scene mean cannot exceed the hottest pixel, so this reading is the camera's internal temperature. The published conclusions stand, but the code's label is wrong. The decisive test has not been run.
Updates over Tailscale with no taps
From the home network, deploy-phone.sh --http with the phone's tailnet name now uploads a build, waits until the phone runs it, and stops with a clear message if the phone asks for a tap. The phone runs the new build about 8 seconds after the upload, and the camera stream, its settings and publishing all come back by themselves.
Four conditions must all hold, each found by a failed attempt:
- The installed build declares the update permission (app 1.3.1 or later).
- "Install unknown apps" is allowed for the app. This is a phone setting the operator changed.
- Play Protect app scanning is off. Every new build is otherwise blocked behind a scan prompt.
- The build is signed with the M5's debug key. A build from another machine cannot update the installed app.
Repository and review
- GitHub: a private repository holds the app, the deploy script, the experiment pipeline (logs and results, not the 14 GB of raw frames) and copies of the project's pages. Five commits on
main. - AGENTS.md is the one description of the project. Its claims were checked against the code; 99 of 116 corrections were applied.
- Secrets scan: no API keys or tokens are in the committed files. Two items are flagged for a decision: the hub's mirror contains two Google Doc links and some third-party material, so the repository should stay private.
- Other pages: a side session refreshed three of the project's Spacesheep pages (remote setup, live stream, reverse-engineered) to show what is built.
Charging and the clicking sound
Charging: the new hub delivers 1.39 A into the battery at 3.86 V, about 5.3 W. That is 28% an hour at the start. The phone reports plain 5 V USB with no fast charging. ColorOS also offers a charge limit, which is not yet set.
The click: the camera's firmware log shows its shutter opening about every 9 seconds, because auto-FFC is on in the factory settings the phone is on now. That is the most likely source of the click. It has not been confirmed by ear. Turning auto-FFC off would stop it, but changes how the camera drifts, so that is a decision for the operator.
The heat-flow campaign (in progress)
The question: where does the heat from different kinds of load go, and how fast does it travel? Every run uses the same schedule, 60 s on and 120 s off, six times, with the camera's shutter timed to each start. Runs are on the M5; the phone is the same Realme.
| Run | Load | Result |
|---|---|---|
| F1 | All 16 cores | Recorded: 29,469 frames, no gaps. |
| F2 | GPU (a Metal compute load) | First attempt lost 137 frames and had 4 stream errors when the phone app restarted at 09:38. Android had stopped the app for a WebView update. The retry is clean. |
| F3 | No load (control) | Clean. |
| F4 | 12 efficiency cores (background priority) | Clean, finished 10:51. |
| F5 | 6 performance cores | Recording since 10:51. |
| F6 | Fill and drain: 16 cores, 5 min on, 10 min off | Not started. |
The analysis code is written: flow.py makes the averaged movies, compares noise-reduction methods, finds arrival times and draws kymographs; flow_compare.py puts the loads side by side. A test on one start from an earlier run gave a front speed of about 1.6 mm per second within 60 mm. That is one start, so it is not a result, and the movies are not made yet.
Open items and decisions
- Placeholders: two empty pages were created for the heat-flow report by mistake. Deleting one needs your approval.
- Auto-FFC: leave the clicking on, or turn auto-FFC off for the heat-flow report?
- Phone API token: a proposal to require a shared secret on the phone's control endpoints is waiting; it has not been started.
- Stale pages: the hub and the millikelvin page still show the old hub figures (an Anker 332 "due" date, 500 mA) and the hub still says "Pixel 10".
- Battery: set the ColorOS charge limit to 80%.
- Visibility: this summary is private. Say the word if you want it public.
Not verified
- The click's source: from the firmware log, not by ear.
- The power estimate for a lit screen: a research estimate.
- The front speed: one start from an earlier run.
- Why the four bright spots react: not tested.