MicroPython for Microcontrollers Module 9
Packages, .mpy files and custom firmware interview questions
Interview questions on packages, .mpy files and custom firmware: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
Why can't you pip install a package onto a Pico, and what do you use instead?
Answered in the lesson Libraries with mip and micropython-lib
-
Fresher
What is an .mpy file, and what does a board gain when it imports one instead of the .py file?
-
Fresher
What is the difference between a frozen module and an .mpy file in /lib?
-
Fresher
What is the difference between a natmod and a user C module in MicroPython?
Answered in the lesson Native modules in C: natmods and user C modules
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
Forty Pico W boards installed umqtt.simple with mip.install("umqtt.simple") over three months, and some now behave differently when the broker drops the connection. How do you tell what each board runs, and how do you make the next installs the same everywhere?
- What it tests
- Knowing that an install without a version takes the newest one, reading what a board holds, and pinning versions of packages and their dependencies for a fleet.
- First step
- Start from how the boards were installed: with no version, each install asked the index for latest.json, the newest version on that day, so boards installed months apart can hold different code. Compare the files on the boards, their SHA-256 against the hashes of each version's package file, before changing anything.
- Follow-ups
-
- How do you tell from the board which version it has?
- How do you pin the dependencies of your own package as well?
- What would you do about the boards that already run the newer version?
From the lesson Libraries with mip and micropython-lib
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A teammate's package installs with mpremote mip install github:team/sensor on their computer. On yours it prints Package may be partially installed and the import fails. How do you find the cause, and what do you change so it cannot happen again?
- What it tests
- Reading mip's messages, the format of package.json (pairs, relative sources, forward slashes), how sources are rewritten with a version, and checking a package before you publish it.
- First step
- Read the line before the warning: mip names what failed, Package not found for the package file, File not found with the path for one file, or an error with an HTTP status. Then compare the two installs: the same version or branch, the same package.json, and what is already in /lib on each board.
- Follow-ups
-
- Why does it work on your teammate's machine?
- What do you check in package.json before publishing?
- How do you clean up a board after a partial install?
From the lesson Libraries with mip and micropython-lib
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
After a fleet of Picos moves from MicroPython 1.22 to 1.29.0, every .mpy module in /lib still imports except one, which fails with ValueError: incompatible .mpy file. What is different about that one, and how do you fix the fleet and the build?
- What it tests
- Knowing that bytecode files check only the .mpy version while machine-code files also check the sub-version, reading a header, and pinning the cross-compiler to the firmware.
- First step
- Read the headers of a module that loads and of the one that fails. The bytecode files start 4d 06 00 1f: version 6, which 1.29.0 still reads. The failing one is likely machine code, third byte 0x12: sub-version 2, the format of 1.22, while 1.29.0 requires 3 for any file with machine code.
- Follow-ups
-
- Why do the other files still load?
- How do you stop this at the next upgrade?
- What would have happened with a version 5 file?
From the lesson Precompile to .mpy with mpy-cross
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
One repository feeds Pico, Pico W and ESP32 boards, and some modules use @micropython.native. Design the build and deploy steps so that a board can never be sent an .mpy file it cannot import.
- What it tests
- Splitting bytecode from machine-code modules, deriving -march from each board's _mpy, pinning mpy-cross, and a pre-flight check of headers before anything is copied.
- First step
- Group the targets by what their firmware reads, not by product name: the Pico and the Pico W share one sys.implementation._mpy, 4870, armv6m, and the ESP32 reports 11014, xtensawin. Bytecode modules build once for all; each machine-code module builds once per architecture. Then decide where the check goes.
- Follow-ups
-
- How does the build know which modules need -march?
- Where does the check against the board run, and with which number?
- What do you do when a board reports an _mpy you have no build for?
From the lesson Precompile to .mpy with mpy-cross
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A Pico app with 25 modules in /lib raises MemoryError during its imports at start, but runs once the largest module is removed. Would precompiling everything to .mpy help, and what would you measure before and after?
- What it tests
- Telling the compiler's RAM at import from the bytecode's RAM afterwards, measuring with gc and micropython.mem_info, and knowing when freezing or ROMFS is the next step.
- First step
- Separate two costs. While a .py module imports, the board's compiler needs RAM of its own on top of the bytecode it makes; an .mpy file removes that cost, but its bytecode still lands in RAM. Measure gc.mem_free() after a gc.collect() at points through the imports, to see which cost runs out.
- Follow-ups
-
- What does mpy-cross change in the numbers you measured?
- What if MemoryError comes back with every module precompiled?
- Why might the order of imports matter?
From the lesson Precompile to .mpy with mpy-cross
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your Pico firmware freezes the app's station package. A bug in it is found after 300 boards ship, and a teammate copies a fixed station to /lib on a test board, but the bug is still there. Explain why, and how you would ship the fix and design the next firmware.
- What it tests
- The order of sys.path with frozen modules and ROMFS, what a frozen module's __file__ shows, and designing firmware that can be fixed in the field.
- First step
- Check which copy runs before touching code: import station, then print(station.__file__). On the rp2 port sys.path is '', '.frozen', then /lib, so the frozen copy is found first and the file in /lib is never read. The fix cannot reach the boards as a file in /lib.
- Follow-ups
-
- How do you get the fix onto the 300 boards now?
- What would you freeze differently next time?
- Would a ROMFS image have avoided this?
From the lesson Frozen modules, manifests and ROMFS
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
After flashing a custom Pico firmware built with FROZEN_MANIFEST, the board starts at the prompt, main.py on its flash never runs, and import asyncio fails. The stock firmware works. What went wrong in the build, and how do you check a manifest before flashing?
- What it tests
- That a manifest replaces the board's manifest, what the rp2 port freezes (_boot.py, asyncio, drivers), and checking a manifest's output before it reaches a board.
- First step
- Both symptoms point at the port's own frozen modules. The rp2 port's _boot.py mounts the flash filesystem at each start, and asyncio is frozen by the port's board manifest. Look at the first lines of the manifest: without include("$(PORT_DIR)/boards/manifest.py"), none of them is in the firmware.
- Follow-ups
-
- Why does main.py not run?
- How do you check the list of frozen modules on the board itself?
- What else would you check in the manifest?
From the lesson Frozen modules, manifests and ROMFS
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Senior to staff
A fleet of 200 Pico W boards runs out of RAM, and its app changes about once a month. You can ship one new firmware now, and only over USB at a service visit. Would you freeze the app, move it to ROMFS, or keep .mpy files in /lib? Defend the choice.
- What it tests
- Weighing RAM, update cost and risk for frozen code, ROMFS and files; partitions set at build time; sys.path order; and planning the one firmware build well.
- First step
- Write down what each change will cost in the field. Frozen code saves the RAM, but a monthly change becomes a firmware flash over USB at a visit. .mpy files in /lib change easily but keep the bytecode in RAM, which is what runs short. ROMFS runs from flash and changes with a new image, if the firmware has a partition.
- Follow-ups
-
- What would you still freeze in the new firmware?
- How large should the ROMFS partition be?
- How does the app behave if a board has no valid image?
From the lesson Frozen modules, manifests and ROMFS
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A Modbus gateway on ESP32 boards with stock firmware spends most of its time computing CRC-16 over 256-byte frames in Python. Would you use viper, a natmod or a user C module, and how would you prove the change is correct and worth it?
- What it tests
- Choosing between viper, natmods and user C modules from constraints, measuring before and after, and proving equality with a reference and known check values.
- First step
- Start from the constraints. Stock firmware rules out a user C module. A 256-byte frame costs 2,048 shift steps in the bitwise CRC, each several bytecode instructions in Python; measure the time per frame on the board first, with time.ticks_us() around the call, so the gain can be shown in numbers.
- Follow-ups
-
- Why not viper?
- How do you show the new version gives the same CRC?
- What would change if the boards ran firmware you build?
From the lesson Native modules in C: natmods and user C modules
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
You port a C library to a natmod. It compiles and passes its tests on your computer, but the natmod build fails, pointing at the library's lookup tables and a global counter. What do natmods not support, and how do you change the C without changing its results?
- What it tests
- Knowing which kinds of C data a natmod's loader accepts, changing a library to fit without changing its results, and testing the changed C against the original.
- First step
- Read the error, then the natmod page's list of what its loader supports. Sort the library's globals by the section the compiler puts each in: code, read-only data, data with initial values, or zeroed data. Note how each table and the counter are declared, const or not, static or not, with a value or not, before changing a line.
- Follow-ups
-
- What do you do with a table the library writes to?
- How do you prove the changed library gives the same results?
- When is a user C module the better home for this library?
From the lesson Native modules in C: natmods and user C modules
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Your product ships a natmod for Pico, ESP32 and ESP32-C3 boards, with a Python fallback. Design the tests and the CI that keep every natmod and the fallback in agreement, and make a board that runs the fallback visible.
- What it tests
- Testing a C core and a Python reference together, building per architecture, a loader that survives a missing or a wrong file, and making silent fallbacks observable in the field.
- First step
- Make one set of test vectors the source of truth: known check values, such as 0x4B37 for "123456789" in CRC-16/MODBUS, edge cases, and random frames with a fixed seed. Run them against the Python reference and against the plain C core on the CI machine before any cross-compiler runs.
- Follow-ups
-
- Which tests can run without boards, and which cannot?
- How should the loader behave when the natmod is wrong for the board?
- How do you find boards that run the fallback in the field?
From the lesson Native modules in C: natmods and user C modules
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
All MicroPython for Microcontrollers interview questions · The MicroPython for Microcontrollers track