Background & Goal
I'm a retired EE who has worked on analog, digital, ASIC & hardware and C, Perl & Python CAD software over my 40+ year career. I use a Raspberry Pi 4B running Debian 13 (trixie) Linux for CAD because it's fun and -- if I download software that causes problems with the system -- it's easy to swap in a backup SD and try something different. The Raspberry Pi 4B is an arm64 computer, not an amd64 computer, though, and, when I tried using a QEMU x86_64 Linux instance to run Microchip's MPLAB X IDE, it took hours to load & and hours trying to start the IDE before I gave up and asked Claude.ai for help. duck.ai allows you to run Claude Haiku 3.6 and I was happy with that. However, I found that using a free Claude.ai account and running Sonnet 4.6 was a better choice for this project. Claude.ai summarizes the problems we found & it (with a little help from me) fixed below:
Microchip's XC compiler toolchain can be compiled and run on a Raspberry Pi 4B running Debian 13 (Trixie). The missing piece was a way to download the compiled .hex file into a Curiosity Nano dsPIC33CK64MC105 evaluation board — without needing a full x86 MPLAB X installation.
Microchip provides pymcuprog, an open-source Python programming tool that supports nEDBG-based Curiosity Nano boards via the EDBG/CMSIS-DAP protocol. On paper this should work natively on ARM Linux. In practice, four distinct problems blocked it.
USB HID never appeared
hidraw3 was created then immediately destroyed by a USB reset triggered by the mass storage driver.
QEMU held the USB device
A running x86_64 QEMU instance with USB forwarding kept exclusive control of the nEDBG, causing resets.
Wrong packpath argument
pymcuprog expected the device scripts folder itself, not the top-level DFP version directory.
Ping response: 9912 ✓
Full read/write/erase access to the dsPIC33CK64MC105 from the Raspberry Pi 4B. No x86 needed.
Problem Diagnosis
| Symptom | Root Cause | Fix | Status |
|---|---|---|---|
Unable to connect to USB device (open failed) |
QEMU had exclusive USB ownership via USB forwarding | Shut down the QEMU instance before using pymcuprog | FIXED |
/dev/hidraw3 disappears seconds after plugging in |
Linux automount tried to mount the nEDBG's MSD interface, FAT errors caused a USB reset destroying all interfaces | udev rule with UDISKS_IGNORE; QEMU shutdown was the real fix |
FIXED |
/dev/hidraw3 owned by root, mode 0600 |
No udev rule for hidraw subsystem with Atmel VID 0x03EB |
udev rule targeting SUBSYSTEM=="hidraw" with ATTRS{idVendor}=="03eb" |
FIXED |
Unable to setup stack using the given packpath |
packpath pointed to DFP version root; pymcuprog needs the device-specific scripts subdirectory | Point packpath to scripts/dspic33ck64mc105 |
FIXED |
The Curiosity Nano's onboard programmer enumerates with Atmel VID 0x03EB (PID 0x2175), not Microchip VID 0x04D8. This is correct — Microchip acquired Atmel and the nEDBG firmware predates the rebrand. The pyedbglib source already handles this correctly; the issue was purely the udev rules and the QEMU conflict.
The file nedbg_dspic33ck64mc105.py inside the DFP pack contains Python 2 syntax (except E, e: and xrange()). This file is a Jython script for use inside MPLAB X and is not loaded by pymcuprog. pymcuprog uses common/debugprovider.py and dspic33ck64mc105pds.py instead, which are Python 3 compatible.
Installation
Install pymcuprog and pyedbglib
$ pip install pymcuprog pyedbglib --break-system-packages
Download the dsPIC33CK Device Family Pack
Download Microchip.dsPIC33CK-MC_DFP from packs.download.microchip.com and extract it. The relevant directory is the versioned subfolder — in this example, version 1.8.299.
$ mkdir -p ~/microchip/packs $ cd ~/microchip/packs $ # Extract your downloaded .atpack file (it's a zip) $ unzip Microchip.dsPIC33CK-MC_DFP.1.8.299.atpack -d dsPIC33CK-MC_DFP/1.8.299 $ # Verify the scripts directory exists for your device $ ls ~/microchip/packs/dsPIC33CK-MC_DFP/1.8.299/scripts/dspic33ck64mc105/ nedbg_dspic33ck64mc105.py dspic33ck64mc105pds.py version.py common/
USB & udev Setup
The Curiosity Nano nEDBG is a composite USB device presenting four interfaces: HID (CMSIS-DAP), CDC-ACM (serial), Mass Storage, and DGI. Linux needs permission rules to make the HID interface accessible to non-root users.
Create the udev rule
# Microchip / Atmel nEDBG (Curiosity Nano onboard programmer) # VID 03eb = Atmel (used by nEDBG even on post-acquisition boards) SUBSYSTEM=="usb", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2175", MODE="0666", GROUP="plugdev" SUBSYSTEM=="hidraw", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2175", MODE="0666", GROUP="plugdev" SUBSYSTEM=="tty", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2175", MODE="0666", GROUP="plugdev" SUBSYSTEM=="block", ATTRS{idVendor}=="03eb", ATTRS{idProduct}=="2175", ENV{UDISKS_IGNORE}="1", ENV{UDISKS_AUTO}="0"
$ sudo udevadm control --reload-rules $ # Unplug and replug the Curiosity Nano, then verify: $ ls -la /dev/hidraw* /dev/ttyACM* crw-rw-rw-+ 1 root plugdev 242, 3 Feb 26 16:02 /dev/hidraw3 crw-rw-rw-+ 1 root plugdev 166, 0 Feb 26 16:02 /dev/ttyACM0
If you are running a QEMU virtual machine with USB device forwarding pointing at the Curiosity Nano, pymcuprog will fail and the USB device will reset repeatedly. You must shut down (not suspend) the QEMU instance before using pymcuprog. The two cannot share the device.
Verify the device is enumerated correctly
$ lsusb | grep 03eb Bus 001 Device 006: ID 03eb:2175 Atmel Corp. nEDBG CMSIS-DAP $ python3 -c "import hid; [print(d) for d in hid.enumerate() if d['vendor_id']==0x03eb]" {'vendor_id': 1003, 'product_id': 8565, 'serial_number': 'MC020023603FAS001511', 'manufacturer_string': 'Microchip Technology Incorporated', 'product_string': 'nEDBG CMSIS-DAP', ...}
When the Curiosity Nano is plugged in you should see in dmesg: hid-generic binding hidraw3, cdc_acm binding ttyACM0, and usb-storage binding the MSD interface as sda. The key sign of success is no USB reset following these lines.
The Critical packpath Argument
This is the most counterintuitive part. pymcuprog's --packpath argument must point to the device-specific scripts directory inside the DFP, not the top-level version directory.
Internally, nvmpic.py does this:
sys.path = [os.path.normpath(packpath)] + sys.path sys.path = [os.path.normpath(packpath + "//common")] + sys.path from common.debugprovider import provide_debugger_model
For the import to succeed, packpath must be the directory that directly contains the common/ subdirectory and the dspic33ck64mc105pds.py file.
# ✗ WRONG — points to DFP version root --packpath ~/microchip/packs/dsPIC33CK-MC_DFP/1.8.299 # ✓ CORRECT — points to device scripts directory --packpath ~/microchip/packs/dsPIC33CK-MC_DFP/1.8.299/scripts/dspic33ck64mc105
Usage
Convenience wrapper script
Save typing with a small shell wrapper:
#!/bin/bash PACKPATH=~/microchip/packs/dsPIC33CK-MC_DFP/1.8.299/scripts/dspic33ck64mc105 pymcuprog -t nedbg --packpath $PACKPATH -d dspic33ck64mc105 "$@"
$ chmod +x ~/bin/picprog
Ping — verify connection
$ picprog ping Connecting to any nedbg Connected to nEDBG CMSIS-DAP from Microchip (serial number MC020023603FAS001511) Debugger firmware version 1.30.35 Debugger hardware revision 0 Device mounted: 'dspic33ck64mc105' Pinging device... Ping response: 9912 Done.
Write a hex file
$ picprog write -m flash -f myprogram.hex --verify
Read flash memory
$ picprog read -m flash
Erase the device
$ picprog erase
Write with erase first (typical programming flow)
$ picprog erase && picprog write -m flash -f myprogram.hex --verify
Complete Fix Summary
-
01
Shut down any QEMU instance that has USB forwarding to the Curiosity Nano. QEMU takes exclusive ownership of the USB device and prevents any other process from opening it. This also causes repeated USB resets visible in
dmesg. -
02
Install pymcuprog and pyedbglib via pip. The nEDBG's Atmel VID
0x03EBis already handled correctly inpyedbglib/hidtransport/cyhidapi.py— no source patching needed. -
03
Create
/etc/udev/rules.d/99-microchip.ruleswith rules for bothSUBSYSTEM=="usb"andSUBSYSTEM=="hidraw"matchingATTRS{idVendor}=="03eb". The hidraw rule is essential — hidapi opens/dev/hidrawX, not the raw USB device. -
04
Download the dsPIC33CK-MC_DFP device pack from packs.download.microchip.com and extract it. Confirm the
scripts/dspic33ck64mc105/directory exists inside the version folder. -
05
Pass the device scripts directory as
--packpath, not the top-level DFP directory. The correct path ends in.../scripts/dspic33ck64mc105. pymcuprog prepends this tosys.pathto import thecommonmodule directly. -
06
Verify with
pymcuprog ping. A successful response shows Ping response: 9912 — the device ID for the dsPIC33CK64MC105 is0x26B8= 9912 decimal.
A complete, native ARM Linux toolchain for compiling and programming dsPIC33CK firmware. Microchip XC compiler builds the hex file on the Raspberry Pi, and pymcuprog downloads it to the Curiosity Nano — no x86 machine, no MPLAB X, no QEMU required.