Building My Own Tool Scanner — Part 14: The Scanner Worked. The Camera Wasn’t Taking Pictures.

On 24 October, the local software checks were looking healthy.

Gridfinity could parse the cached A3 library. A real ratchet profile generated an insert that survived STEP re-import and closed STL validation. Replaying the saved glass-target image reproduced its previous measurement results.

At that stage, 190 desktop tests and 13 A3 tests passed.

There was one fairly significant qualification: the live scanner hadn't been verified. Its saved address wasn't responding.

Cached success is still cached

Those checks established useful things about the software and saved evidence. They didn't prove that the phone could take a fresh photograph of the object currently on the bed.

The scanner's confirmed address was now the router-reserved 192.168.68.116:8765, rather than the obsolete saved address.

With live access restored, a larger problem became visible.

The supposed wire-stripper capture was the old ratchet image.

Exactly the same wrong picture

This wasn't a matter of two silhouettes looking suspiciously similar.

The files had identical content: 5,722,804 bytes and the same SHA-256 hash. The phone's latest JPEG was still dated 5 October.

The camera application was running, but capture requests weren't producing a new image. Restarting it produced a fresh JPEG.

The underlying reason for the Android camera session stalling wasn't established. The proven fault was that the scanner accepted the existing file as though it were evidence of a successful new capture.

The running production scripts still used the older capture behaviour. Freshness safeguards developed during the local audit hadn't yet been deployed there.

A fix in the workstation directory hadn't travelled to the machine that needed it.

Waiting isn't evidence

The old approach effectively waited and then downloaded the expected filename.

That can work very consistently while being completely wrong. If the camera doesn't replace the file, every later stage can process an excellent copy of an earlier photograph.

The repair made the capture contract explicit.

The camera application was restarted for each shot. Control and capture broadcasts were targeted to it. The previous phone JPEG was removed before requesting the next image, and a new file had to arrive within a bounded wait.

The download went to a pending file. Minimum size and JPEG boundary markers were checked before replacing the workstation copy. A failed capture stopped the job rather than quietly turning into another scan of the last successful image.

The report also recorded the raw image's hash and saved timestamp.

None of those fields proves the physical measurement is correct. They make it much harder to lose track of which image was actually measured.

Test the ordinary route twice

After deployment, two consecutive normal API jobs photographed and traced the wire strippers actually on the bed.

Both reported bounds of 182.2 × 59.8 mm without the optional height correction. Their image hashes differed.

The incorrect result was preserved in a backup, then replaced with the corrected scan. Gridfinity refreshed and imported the new final profile.

That was the live evidence the earlier audit couldn't provide.

It also explained why local geometry checks and the camera failure could coexist. The mapping, tracing and CAD stages were capable of operating correctly on an image that shouldn't have been accepted as a new capture.

The boring boundary mattered

This wasn't an exotic optical limitation. It was an acquisition failure hidden behind a familiar filename.

The useful improvement was to make success mean more than “a JPEG exists”. The complete system needed evidence that the requested capture had actually produced one.

With that repaired and repeated, the wire-stripper image could finally be used for the next deliberate change.

Until now, filling internal holes had been sensible for most storage pockets. But sometimes I wanted the scanner to keep those details.

Adding that choice was about to expose another assumption the outline-only route had been politely hiding.

Next: Part 15 — Sometimes I Actually Want the Holes