Fox Red Labs / Builds / Tools and languages
Type Python at a prompt on a TI chip
MicroPython's own Zephyr port builds with one config file and runs on the CC2340R5 and the MSPM33 C321A: a prompt on the probe's serial port and Pin from Python.
HardNo wiring
Parts and steps
Your board
You need
- LP-EM-CC2340R5 LaunchPad development kit Buy the board
- LP-XDS110ET debugger Buy the debugger that programs the board
- A Mac to build and flash from builds and flashes
Set up your computer once: 1 step. Done already? Start at step 2.
Install the Zephyr 4.4.2 workspace, the compiler and the west environment; NO_FLASH=1 stops the script after its build so it does not touch a board.TERMINAL
Then clone MicroPython v1.29.0 (commit
0fd6c573e); nothing in it is edited.Replace <repository folder> and <MP> before you run this.
NO_FLASH=1 <repository folder>/cc2340r5/zephyr/setup-intel-mac.sh git clone --depth 1 --branch v1.29.0 https://github.com/micropython/micropython.git <MP>You should see: The script ends with
NO_FLASH set. Built, not flashed.and<MP>holds MicroPython.
Copy prj_cc2340r5.conf and lp_em_cc2340r5.overlay from ports/micropython/zephyr-cc2340r5 into a folder <proj>, then build.TERMINAL
The conf replaces MicroPython's own
prj.conf, which overflows the chip's RAM by 46.8 kB; the overlay adds the ADC channels.Replace <zephyr workspace>, <toolchain>, <MP>, <build> and <proj> before you run this.
. <zephyr workspace>/.venv/bin/activate export ZEPHYR_TOOLCHAIN_VARIANT=cross-compile CROSS_COMPILE=<toolchain>/arm-zephyr-eabi/bin/arm-zephyr-eabi- cd <zephyr workspace>/zephyr west build -p always -b lp_em_cc2340r5 <MP>/ports/zephyr -d <build> -- -DCONF_FILE=<proj>/prj_cc2340r5.conf -DEXTRA_DTC_OVERLAY_FILE=<proj>/lp_em_cc2340r5.overlayYou should see: About 3 minutes later the build ends with zero errors: 174.9 kB of flash (34.15 %) and 29.9 kB of RAM (82.99 %). The image is
<build>/zephyr/zephyr_crc32.hex.Clip the LP-XDS110ET onto the board and plug it into the Mac with its USB-C cable; the board has no USB port of its own.BENCH
Read the probe serial with
/Applications/ti/ccs2101/ccs/ccs_base/common/uscif/xds110/xdsdfu -e.You should see: The probe shows up as an XDS110 and
xdsdfu -eprints its serial number.Back up the main flash and the CCFG before the first write, then flash the _crc32 image, which carries the boot checksums.TERMINAL
Restore later with the same
flashcommand onrestore.hex.Replace <UniFlash folder>, <backup folder outside the repo> and <build> before you run this.
DSLITE=<UniFlash folder>/deskdb/content/TICloudAgent/osx/ccs_base/DebugServer/bin/DSLite; CCXML=cc2340r5/CC2340R5.ccxml; BK=<backup folder outside the repo> $DSLITE memory -c $CCXML -r 0x0,0x20000 -o $BK/main_flash.bin $DSLITE memory -c $CCXML -r 0x4E020000,0x200 -o $BK/ccfg.bin python3 cc2340r5/oad/images.py merge $BK/restore.hex $BK/main_flash.bin@0x0 $BK/ccfg.bin@0x4E020000 $DSLITE flash -c $CCXML -e -f -u <build>/zephyr/zephyr_crc32.hexYou should see: DSLite writes the image and reports success; the restore hex reads back byte for byte against the backup.
Open the REPL with the helper that holds the serial port open and sends lines (115200 baud, /dev/cu.usbmodem<your-probe-serial>1).TERMINAL
Replace <your-probe-serial> before you run this.
python3 ports/micropython/zephyr-cc2340r5/repl.py /dev/cu.usbmodem<your-probe-serial>1 out.log --reset -- 'print(1+1)'You should see:
out.logholds the bannerMicroPython v1.29.0 on 2026-10-24; zephyr-lp_em_cc2340r5 with cc2340r5, then the line2.Drive DIO15, the green LED pin, and read an ADC pin from Python.TERMINAL
Pins are named by Zephyr devicetree label and number; the ADC number is the channel from the overlay, not the DIO number.
Replace <your-probe-serial> before you run this.
python3 ports/micropython/zephyr-cc2340r5/repl.py /dev/cu.usbmodem<your-probe-serial>1 out.log -- 'import machine' 'led = machine.Pin(("gpio0", 15), machine.Pin.OUT)' 'led.on(); print(led.value())' 'led.off(); print(led.value())' 'print(machine.ADC(("adc0", 2)).read_u16())'You should see:
out.logshows1after led.on(),0after led.off(), then an ADC number (about 42,000 to 48,000 on a floating pin). The LED itself was not watched; the state is known from the read-back.
You need
- LP-MSPM33C321A LaunchPad development kit the board
- A Mac to build and flash from builds and flashes
Clone MicroPython v1.29.0 (commit 0fd6c573e) from a fresh folder outside this repository; nothing in it is edited.TERMINAL
git clone --depth 1 --branch v1.29.0 https://github.com/micropython/micropython.git mpYou should see: The
mpfolder holds MicroPython.Build the Zephyr tree this port needs: Zephyr main plus the pinned pull requests, with picolibc and cmsis_6.TERMINAL
Run it from
ports/micropython/zephyr-mspm33c321a. The kinds areuart,i2candspi; this version usesuart../zephyr-tree.sh "$PWD/zw" uartYou should see: About 10 minutes later the
zwfolder holds the merged Zephyr tree.Build MicroPython's ports/zephyr for lp_mspm33c321a with MicroPython's own prj.conf plus mspm33c321a.conf (watchdog off, GPIO, I2C, SPI).TERMINAL
MP="$PWD/mp" ZW="$PWD/zw" OUT="$PWD/out" KIND=uart ./build.shYou should see: About 4 minutes later
out/zephyr/zephyr.hexexists: 292.3 kB of flash (28.5 % of 1 MB) and 83.6 kB of RAM (32.7 % of 256 kB) with the GPIO driver.Back up the board, pad the image to a multiple of 16 bytes (DSLite writes 128-bit words), convert it to hex at 0x10000000 and flash it.TERMINAL
Restore later with the same
flashcommand on a hex made from the savedmain-flash.bin.Replace <your-probe-serial> before you run this.
python3 tools/benchmark/backup.py save mspm33c321a --probe <your-probe-serial> python3 -c "b=open('out/zephyr/zephyr.bin','rb').read(); open('p.bin','wb').write(b+b'\xff'*(-len(b)%16))" arm-zephyr-eabi-objcopy -I binary -O ihex --change-addresses 0x10000000 p.bin p.hex DSLite flash -c mspm33c321a/MSPM33C321A_<your-probe-serial>.ccxml -e -f -v p.hexYou should see: DSLite writes and verifies the 1 MB image in about 20 s.
Open the REPL on the probe's serial port (115200 baud) and read two pins with their pull resistors on: PA12 pulled up, then pulled down.TERMINAL
Replace <your-probe-serial> before you run this.
python3 ports/micropython/zephyr-mspm33c321a/repl.py /dev/cu.usbmodem<your-probe-serial>1 out.log --reset -- 'print(1+1)' 'import machine' 'print(machine.Pin(("gpioa", 12), machine.Pin.IN, machine.Pin.PULL_UP).value())' 'print(machine.Pin(("gpioa", 12), machine.Pin.IN, machine.Pin.PULL_DOWN).value())'You should see:
out.logholds the bannerMicroPython v1.29.0 on 2026-10-25; zephyr-lp_mspm33c321a with mspm33c321a, then2, then1for the pull-up and0for the pull-down.
Could work
Or hand it to a coding assistant
Paste this in a terminal. It hands off to an AI assistant.
git clone --depth 1 --filter=blob:none https://github.com/Fox-Red-Labs/fox-red-labs.git && cd fox-red-labs
./start.sh cc2340r5 micropython --assistant claudegit clone --depth 1 --filter=blob:none https://github.com/Fox-Red-Labs/fox-red-labs.git && cd fox-red-labs
py start.py cc2340r5 micropython --assistant claudeIt runs the steps above and reports what it saw.
Not Claude Code? Copy this prompt instead.
Or hand it to a coding assistant
Paste this in a terminal. It hands off to an AI assistant.
git clone --depth 1 --filter=blob:none https://github.com/Fox-Red-Labs/fox-red-labs.git && cd fox-red-labs
./start.sh mspm33c321a micropython --assistant claudegit clone --depth 1 --filter=blob:none https://github.com/Fox-Red-Labs/fox-red-labs.git && cd fox-red-labs
py start.py mspm33c321a micropython --assistant claudeIt runs the steps above and reports what it saw.
Not Claude Code? Copy this prompt instead.
If something goes wrong
| A download fails | Rerun the script; it keeps what it already fetched. |
The link stops with `region RAM' overflowed by 46.8 kB`` | The build used MicroPython's own prj.conf: pass -DCONF_FILE as shown. |
It stops with relocation truncated to fit: R_ARM_THM_JUMP11 ... nlr_push_tail | Keep CONFIG_MICROPY_CONFIGFILE="mpconfigport.h" and do not use the minimal conf. |
xdsdfu -e lists nothing | Check the clip-on connection and use a data cable. |
| The flash fails | Close CCStudio or any other program that holds the probe, and rerun it. |
More troubleshooting
| Nothing appears | Check that the port is the probe's second serial port and that no other program holds it. |
AttributeError: 'Pin' object has no attribute 'toggle' | Use led.value(1 - led.value()). After a Ctrl-D soft reboot a pad you made an output stays one: use machine.reset(). |
Every fix for this board: the FAQ.
| The clone fails | Check the network; the tag is v1.29.0. |
A later build stops with `fatal error: alloca.h: No such file or directory` | The picolibc module is missing: west update picolibc (the script does it) and pass the folder as PICO. |
| The build stops naming a variable | Set it. |
machine_wdt.c stops the build | CONFIG_WATCHDOG=n is missing; use mspm33c321a.conf. |
DSLite says Error in image size ... should be divisible by 16 | The padding step was skipped. |
More troubleshooting
The next call says Error initializing emulator (Error -261) | Run xds110reset -s <your-probe-serial> -a toggle -d 200 and flash again. |
ValueError: device gpioc not found appears | CONFIG_GPIO is off: build with mspm33c321a.conf. Send one-line statements; a multi-line def typed line by line is indented wrongly. |
A read of machine.I2C hangs the console | Press the reset button; use writes only. |
Every fix for this board: the FAQ.
What to build next
Every build. The code for all of them is in the GitHub repository.
Evidence and limitsMicroPython 1.29.0 runs on the CC2340R5 and the MSPM33 C321A through its own Zephyr port: a prompt at 115200 baud and Pin from Python on both, ADC, interrupts, PWM, SPI and I2C calls on the CC2340R5. Ran on 2 boards. Results, sources, timing and limits.
MicroPython on the LP-EM-CC2340R5
Expected result: MicroPython 1.29.0 runs on the CC2340R5: a prompt at 115200 baud, DIO15 driven from Python, and the ADC read.
What this shows
- MicroPython's own Zephyr port runs unmodified on a chip with 36 kB of RAM, once its
prj.confis replaced by one file that drops networking, sensors and file systems. - The image is 174.8 kB of flash and 29.9 kB of RAM with a 20 kB Python heap (19.1 kB free after boot).
- Pin.irq (10 of 10 edges), PWM with duty_ns, SPI and I2C calls ran from the REPL.
What this does not establish, and limits
What this does not establish
- That the LED blinked to the eye: the pin read-back changed and the LED was not watched.
- That SPI data is right: the loopback wire was not fitted; the HDC2080 read and I2C.scan() did not work.
- That the
duty_u16form of PWM is right in MicroPython 1.29.0: it overflows at 1 kHz; useduty_nsor apply the one-cast patch in the folder. - That it works on Windows or Linux: only an Intel Mac was used.
Limits to keep in mind
- There is no file system, so scripts are typed or pasted at the prompt and re-sent after each reset.
- Pin.irq loses its edge when Pin.init() is called on the same pin; call irq() again after init().
- An empty Python loop takes 0.63 microseconds a pass at 48 MHz.
Sources
Versions, sizes and timing
| MicroPython | v1.29.0, commit 0fd6c573e, unmodified | README notes |
|---|---|---|
| Zephyr | 4.4.2, Zephyr SDK 0.17.4 GCC 12.2.0 as a cross compiler | README notes |
| Image | 174.8 kB of flash (34.2 % of 512 kB), 29.9 kB of RAM (83.0 % of 36 kB), 20 kB heap | |
| Free heap after boot | 19.1 kB of the 20 kB heap | |
| Python speed | 10,000 empty loop passes in 6,323 microseconds; ADC.read_u16() about 7.2 microseconds |
Evidence
- 2026-10-24: the banner
MicroPython v1.29.0 ... zephyr-lp_em_cc2340r5 with cc2340r5appeared on the first flash of the first image; print(1+1) printed 2. - led.on() read back 1 and led.off() read back 0 on DIO15; machine.ADC(('adc0', 2)).read_u16() read 65,471 with the pin pulled up and 16 pulled down.
- 2026-10-25: Pin.irq ran its handler 10 of 10 expected times; PWM with duty_ns, SPI and I2C calls ran.
- Built from a copy of only the conf and the overlay outside the repository, about 3 minutes, zero errors: 174.9 kB flash (34.15 % of 512 kB) and 29.9 kB RAM (82.99 % of 36 kB). README notes
Other boards
- MSPM33C321A, MSPM0G3519Could work ran in part
- MSPM0G3507ran another way
- MSPM0L1306, F2800137
What the status words mean
Works ran here and did what it shouldCould work some of it ran; real porting effort is left; hover for whatNot for this chip the chip or its vendor software cannot; hover for why
MicroPython on the LP-MSPM33C321A
Expected result: MicroPython 1.29.0 prints its banner and a >>> prompt at 115200 baud on the MSPM33 C321A, pulled-up and pulled-down inputs read 1 and 0, and float math works.
What this shows
- MicroPython's own Zephyr port runs unmodified, with its own
prj.confas shipped, on the Cortex-M33 chip. - The image is 292.3 kB of flash and 83.6 kB of RAM with a 48 kB Python heap (47.1 kB free after boot).
- machine.Pin and float math run from the REPL on the
KIND=uartimage; machine.SPI calls ran from theKIND=spiimage.
What this does not establish, and limits
What this does not establish
- That machine.I2C works: it opens and writes return, but every read hangs until a hardware reset.
- That machine.ADC or machine.PWM work: Zephyr has no driver for this chip's ADC or PWM.
- That the LED blinked to the eye: the pin state is known from the output register and the LED was not watched.
- That the image from the fresh-folder build ran: it produced an image that was not flashed.
Limits to keep in mind
- Zephyr's released
lp_mspm33c321aboard has no serial port, GPIO, I2C or SPI driver; this port needs Zephyr main plus TI's open pull requests, and pull requests move. - I2C and SPI cannot be in one image: pull requests 110526 and 119347 carry different copies of the UNICOMM driver, so each gets its own build (
KIND=i2c,KIND=spi). Pin.value()reads 0 right afterled.on(); read the output register instead.- An empty Python loop costs 803 CPU cycles a pass at 160 MHz (5.0 microseconds); the cause is not found.
Sources
Versions, sizes and timing
| MicroPython | v1.29.0, commit 0fd6c573e, unmodified | README notes |
|---|---|---|
| Zephyr | main 1405adc9a604 plus open pull requests 116768 and 118511 (UART console and GPIO; 110526 for I2C and 119347 for SPI, each with 118511), GCC 12.2.0 from Zephyr SDK 0.17.4 | README notes |
| Image | 292.3 kB of flash (28.5 % of 1 MB), 83.6 kB of RAM (32.7 % of 256 kB), 48 kB heap | |
| Free heap after boot | 47.1 of 48 kB after gc.collect() | |
| Python speed | 10,000 empty loop passes in 50,182 microseconds |
Evidence
- 2026-10-25: the banner
MicroPython v1.29.0 ... zephyr-lp_mspm33c321a with mspm33c321aappeared and print(1+1) printed 2. - A pin with PULL_UP read 1 and with PULL_DOWN read 0; 2**0.5*1000 printed 1414.2135.
- The same steps were run from a scratch copy of only the listed files and produced an image (the image from that build was not flashed).
Other boards
- MSPM0G3519Could work ran in part
- MSPM0G3507, CC2340R5ran another way
- MSPM0L1306, F2800137
What the status words mean
Works ran here and did what it shouldCould work some of it ran; real porting effort is left; hover for whatNot for this chip the chip or its vendor software cannot; hover for why
Result grid
Works ran here and did what it shouldShould work it builds; only naming, packaging or build glue is in the way; hover for whichCould work some of it ran; real porting effort is left; hover for whatNot for this chip the chip or its vendor software cannot; hover for why
Experiments (1)
| Status | What was tried | Result |
|---|---|---|
| Could work | Does MicroPython beat C to a first blink on the Pico 2, and by how much on the second change? Raspberry Pi Pico 2 H, 2026-10-11 | MicroPython: 65 s from download to a changed blink; C: 87 s including a build, with the tools already installed. A timing from a fresh Mac was not done. |
Source files
- MicroPython on the TI MSPM0G3519 and MSPM0G3507 (MSPM0 G3507)
- MicroPython on Zephyr for the LP-EM-CC2340R5 (CC2340R5)
Code brought from elsewhere
This project uses code that was written for other chips or tools. Each links to how to get it running.
- MicroPython 1.29.0 on the LP-EM-CC2340R5 through upstream's Zephyr port Works MicroPython's own Zephyr port runs on the 36 kB CC2340R5: a >>> prompt, with Pin, ADC, PWM, SPI and I2C from Python.
- MicroPython 1.29.0 on the LP-MSPM0G3519 and LP-MSPM0G3507 Works A new MicroPython port boots on the MSPM0 G3519 with the MicroPython banner and drives a 3.5 inch touch screen from Python.
- MicroPython 1.29.0 on the LP-MSPM33C321A through upstream's Zephyr port Works MicroPython's own Zephyr port runs with its stock prj.conf on the MSPM33 C321A: a >>> prompt, Pin, float math and SPI from Python.
Get the code and tell us how it wentThe clone command, the board folder with its AGENTS.md, reports from others, and the three report forms.
Get the code
Clone the repository and open the board's folder below. AGENTS.md is a plain text file of notes for AI coding assistants, and llms.txt at the root indexes the site for one.
git clone https://github.com/Fox-Red-Labs/fox-red-labs.git
- LP-MSPM0G3507: folder
mspm0g3507/.- README: the board's lab notebook (what was run, in order)
- Notes for an AI coding assistant working in this repository: notes for an AI assistant; copy it into your own project if you use one
- FAQ: errors and fixes
The other 2 board folders
- LP-EM-CC2340R5: folder
cc2340r5/.- README: the board's lab notebook (what was run, in order)
- Notes for an AI coding assistant working in this repository: notes for an AI assistant; copy it into your own project if you use one
- FAQ: errors and fixes
- LP-MSPM33C321A: folder
mspm33c321a/.- README: the board's lab notebook (what was run, in order)
- Notes for an AI coding assistant working in this repository: notes for an AI assistant; copy it into your own project if you use one
- FAQ: errors and fixes
- LP-EM-CC2340R5: folder
Code is BSD-3-Clause, writing is CC BY 4.0; third-party folders keep their own licenses.
Built it? Tell us
Built this, or got stuck? Report it on GitHub with a few short answers. Your report shows here in your words, marked as yours. A failure that repeats on the bench gets its fix here.