You have an inspection script running in the camera IDE, and now the camera has to run it by itself at every power-up, on a line with no PC attached. The IDE's save path can make you wait: before it writes anything it pairs the camera's virtual USB drive with its serial port, polling once a second until the operating system finishes enumeration, and the save control stays disabled until the pair is made. You do not have to wait. The drive is an ordinary mass-storage volume, and a file you copy there yourself behaves exactly like one the IDE writes. Copy your code onto it as main.py and the camera boots straight into your program. The steps below cover the copy, the proof that it runs headless, and the cases where a smart camera is the better standalone platform.
Why the IDE save menu makes you wait
A MicroPython camera hands the host two devices over one cable: a serial console (the REPL) and a virtual mass-storage drive — the uPY disk — that holds your .py files in the camera's internal flash. Before the IDE saves a file, it confirms which mounted drive belongs to which serial port. It cannot guess: with a second camera, a card reader or a phone on the same bus, a wrong match would write your code somewhere else entirely. So it polls for the match once per second, and while the operating system is still settling its device tables after enumeration, the save control stays disabled.
The decision is simple: nothing is broken, and for a one-script deploy you should not wait at all. Copy the file in your file manager and the camera treats it exactly like an IDE-written file. Wait for the IDE's own save only when you want the controls that come alive at the same moment — restart, reset and debug.
What you need
- A camera whose firmware runs MicroPython and mounts the virtual drive over USB. Not every board camera qualifies: most industrial board cameras are sensors that stream frames to a host, and the code lives on that host.
- A full-data USB cable. Charge-only cables power the board but mount nothing, and a worn cable makes the drive appear and vanish mid-copy.
- A serial terminal, for the verification steps and for boot messages.
- Your script, saved somewhere you can find it.
On the host-driven side of the same cell, use a cable rated for the movement it will see. The LS-CBL-0219 is a 3 m USB3.0 camera cable in a high-flex drag-chain rating; the camera cables category covers straight, screw-locked and power-I/O variants. And if a USB3 camera keeps connecting at USB 2.0 speed, fix the USB 2.0 enumeration before you blame the camera.
Save the script as main.py, step by step
- Connect the camera and let the drive mount. Give the operating system its few seconds of enumeration and watch for the new volume.
- Open the drive. You should see any existing main.py, boot.py and helper files.
- Copy your script onto the drive under the exact name main.py, overwriting what is there. If your editor hides extensions, list the folder in a terminal and confirm it is main.py, not main.py.txt.
- Eject the drive. This pushes the copy out of the host's buffers into the camera's flash; it is the most-skipped step and the one that truncates files.
- Open the serial terminal, stop anything running, and type
import main. The file executes once, end to end. Fix any error it raises and repeat from step 3. - Cut power completely, restore it with no host software running, and watch for your heartbeat. The camera is deployed.
Here is the smallest main.py worth shipping — a heartbeat you can see across the room. The machine and time module names are MicroPython's own; the pin name and the timing values are examples to replace with your board's pinout and your loop rate:
# main.py - heartbeat skeleton
import machine
import time
run_led = machine.Pin("LED", machine.Pin.OUT) # example pin name, board-specific
while True:
run_led.value(1) # LED on
time.sleep_ms(200) # 200 ms
run_led.value(0) # LED off
time.sleep_ms(200)
The test that proves it: import main blinks the LED at 1 / (0.2 s + 0.2 s) = 2.5 blinks per second, and a cold power cycle brings the blink back with no PC anywhere near it.
Verify it like a deployment, not a demo
import main proves the file parses and runs — nothing more. The deployment proof is the cold cycle:
- Cut power fully, not a soft reset, restore it, and confirm the heartbeat returns within the first seconds. Repeat ten times; an intermittent auto-run failure shows up as one silent boot in ten.
- Read the boot messages over serial during at least one of those cycles. A camera that boots into an error you never saw will do it again on the line.
- If
import mainworks but power-up does not, the file name is wrong nine times out of ten. The tenth case is firmware: autorun failures have been reported against MicroPython 1.22 and 1.23 release builds, so reflash a current build from the project's official releases and repeat the cold cycles. - Keep hardware bring-up in boot.py and your working loop in main.py. boot.py runs first at power-up, and splitting the two keeps a bad loop edit from taking the whole boot down with it.
The numbers behind the wait
Two delays stack while the IDE pairs the drive: the host's enumeration and the IDE's fixed 1 s polling. Enumeration of a new mass-storage device typically settles in 2–5 s on a busy host with hubs and other storage attached. Worst case before the save control frees: 5 s of enumeration plus up to 1 s to the next poll — call it 5–6 s.
The copy itself is trivial. A 12,400-byte script (about 12 kB) moving at a conservative 1 MB/s of effective mass-storage throughput — 1,048,576 bytes per second — takes 12,400 / 1,048,576 = 0.012 s, about 12 ms. The wait is hundreds of times the copy: 5 s / 0.012 s is about 420. So for anyone deploying more than once, copy the file yourself and keep the IDE for tuning sessions, where its reset-and-run is worth a 5-second wait.
When the IDE is right, and when a smart camera is
Keep the IDE in the loop while you tune thresholds and optics; a 5-second save wait is nothing against an hour of iteration. Two other situations point at different platforms.
Many units, versioned logic, PLC handshakes. A smart camera runs the inspection on the device itself, and that category exists for exactly this: smart cameras inspect with no host PC. The LS-SMC-0063 is a 1.6 MP smart camera with a 16 mm lens and its own white light source on GigE. When custom code is involved, match SDK and firmware versions as you deploy custom code to a smart camera; a version mismatch is the classic silent failure. To publish results from the device onto the plant network, running OCR standalone on an edge camera shows the pattern.
- Interface
- GigE
- Resolution
- Up to 3 MP
A host-driven build. Your code never leaves the PC, deployment is a file copy on the PC, and the camera is chosen for the image, not for scripting. A USB3 board camera such as the LS-BCM-0003 is a 3.1 MP board-level camera with a 1/1.8" global-shutter sensor at 120 fps; browse board-level cameras for mounts and formats. If the board streams through a bridge, match the bridge board before you stream.
- Sensor format
- 1/1.8"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 5 MP
Common mistakes that break auto-run
- main.py.txt. Editors and file managers hide extensions by default, and the camera ignores the misnamed file completely. Turn extensions on.
- Cutting power before ejecting. The copy can still be sitting in host buffers; a truncated main.py fails to parse and the camera boots to a silent prompt.
- Editing the drive while the script runs. On most boards the volume is writable only when your script is stopped: stop, copy, eject, restart.
- A hub or a front-panel extension port. Enumeration through hubs takes the longest of anything here; deploy from a root port.
- No error handling. One unhandled exception — a sensor read that times out — drops the script to the REPL and the station stops with it. Wrap the main loop in try/except, log the error, carry on.
Pre-ship checks for a headless camera
Ten minutes of checks catches every failure above before the panel goes on:
- Ten cold power cycles, heartbeat every time, no host attached.
- A serial header still reachable inside the enclosure, because the boot messages you need later are the ones you cannot get later.
- try/except around the loop, with the last error readable over serial.
- The firmware version written on the unit's build sheet, next to the date of the main.py copy.
- Selective logging: keep the last good image and the exception log, not every frame — saving the active view as a PNG covers the capture-to-file step, and every write wears the camera's internal flash.
Run the wider board camera evaluation checklist for inspection systems before you commit the mechanical design. For the rest of the station, the vision systems topic page collects the integration guides, the camera finder narrows sensors, and the system configurator walks camera, lens and light in one pass. For a specific pairing question, the contact page reaches the applications team.
Questions
Why does the IDE save option stay greyed out after plugging the camera in?
The IDE will not write until it can pair the camera's virtual drive with its serial port, and it polls for that match once a second. A busy operating system can take several seconds to finish enumerating the new mass-storage device, so the save control stays disabled through those polls. Give it a few seconds after connecting; if the option never returns, the drive did not mount, and the cable or port is the first suspect.
Does the script run with no IDE or PC connected?
Yes. main.py executes at every power-up on its own, after boot.py if one exists. The IDE is a development tool only: write and test there, then leave the camera powered from the machine's supply. Keep a serial console within reach for the first deployments, because boot messages over serial are the fastest way to see why a headless camera came up empty.
Which file names does the camera run automatically?
Two: boot.py runs first, main.py second. Anything else — helper modules, calibration tables, thresholds — just sits on the drive until main.py imports or reads it. Keep the auto-run file small and push the working code into modules you import, so a bad edit to one module still leaves a camera that boots to a prompt you can fix from.
Why would a script run when imported but not after a power cycle?
Check the file name first: an editor that appends .txt leaves main.py.txt, which the camera ignores. Second, eject before cutting power, because an unflushed copy can arrive truncated and fail to parse. Third, firmware: main.py autorun failures have been reported on MicroPython 1.22 and 1.23 release builds, so reflash a current version and repeat the cold power-cycle test.
How much can a script write before camera flash wear matters?
Work it from frame size times rate. A 640 x 480 8-bit grayscale frame is 640 x 480 = 307,200 bytes, about 300 kB; at one frame per minute that is 307,200 x 1,440 = 442 MB a day. Camera-board flash is sized in gigabytes and survives a finite number of writes, so log the last good image and the exceptions, not every frame.
What is the fastest way to load the same script onto several cameras?
Treat each camera as a USB stick: plug it in, copy main.py to the mounted drive, eject, unplug, next unit. Windows and Linux both let you script that copy so the wrong file name cannot be picked. The IDE route repeats its drive-to-port pairing wait on every unit, which is exactly the delay you remove by copying the files yourself.


