nrf52840-platformio
Provides build and debug workflows for nRF52840 development on the PlatformIO framework.
Install
mkdir -p .claude/skills/nrf52840-platformio && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/17921" && unzip -o skill.zip -d .claude/skills/nrf52840-platformio && rm skill.zipInstalls to .claude/skills/nrf52840-platformio
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Build, flash, and debug nRF52840 projects with PlatformIO and the Adafruit BSP. Use when: setting up PlatformIO for nRF52840-DK, flashing via J-Link, restoring SoftDevice and Bootloader, understanding the flash memory layout, fixing 'pulsing LED' DFU mode, using firmware_signature.bin, selective app-only flash, checking UICR.BOOTLOADERADDR.Key capabilities
- →Build nRF52840 projects with PlatformIO and Adafruit BSP
- →Flash firmware via J-Link
- →Restore SoftDevice and Bootloader
- →Understand the flash memory layout
- →Fix 'pulsing LED' DFU mode
How it works
The skill guides the setup and use of PlatformIO for nRF52840-DK development, including flashing, bootloader restoration, and debugging, with specific configurations for the Adafruit BSP.
Inputs & outputs
When to use nrf52840-platformio
- →Flash nRF52840 firmware via J-Link
- →Restore bootloader and SoftDevice
- →Debug firmware on nRF52840-DK
- →Configure project flash memory layout
About this skill
nRF52840 mit PlatformIO (Adafruit BSP)
Hardware & Software
- Board: nRF52840-DK (pca10056)
- Debugger: SEGGER J-Link OB (onboard), S/N: 1050252111
- Platform:
nordicnrf5210.9.0 - Framework:
arduino(Adafruit Bluefruit52Lib / framework-arduinoadafruitnrf52 1.10601.0) - Board-ID:
nrf52840_dk_adafruit(Adafruit-Variante, nichtnrf52840_dk) - Serielle Ports (Nummerierung kann je nach Enumeration variieren
– mit
ls /dev/ttyACM*prüfen):- J-Link VCOM (Serial2 UART, P0.06=TX / P0.08=RX) → aktuell
/dev/ttyACM0 - J-Link CDC Command-Kanal → weiterer ACM-Port (kein UART-Output)
- App-TinyUSB-CDC = separater Port (erscheint nur wenn zweites USB-Kabel an J18 angesteckt)
- J-Link VCOM (Serial2 UART, P0.06=TX / P0.08=RX) → aktuell
platformio.ini (Minimal-Konfiguration)
[env:nrf52840dk]
platform = nordicnrf52
board = nrf52840_dk_adafruit
framework = arduino
lib_extra_dirs = ../../
monitor_port = /dev/ttyACM0 ; J-Link VCOM = Serial2 UART (Nummer ggf. anpassen)
monitor_speed = 115200
upload_protocol = jlink
lib_deps = adafruit/Adafruit nRFCrypto@^0.1.2
build_flags =
-DBOARD_NRF52DK
-I.pio/libdeps/nrf52840dk/Adafruit\ nRFCrypto/src
debug_svd_path = /home/daniel/.platformio/platforms/nordicnrf52/misc/svd/nrf52840.svd
debug_tool = jlink
build_type = debug
debug_build_flags = -O0 -ggdb3
debug_server_parameters = -rtos GDBServer/RTOSPlugin_FreeRTOS
Wichtig: Immer nrf52840_dk_adafruit verwenden, nicht nrf52840_dk
(Nordic BSP)!
Der Nordic-BSP hat ein falsches Pin-Mapping: LED_BUILTIN → Arduino-Pin 6
→ P0.17 (falsch, LEDs sind P0.13–P0.16).
Flash-Speicherlayout (nRF52840, Adafruit BSP)
| Region | Adresse | Inhalt |
|---|---|---|
| MBR | 0x00000000 | Nordic MBR (4 KB) |
| SoftDevice S140 | 0x00001000 | S140 BLE-Stack (~152 KB) |
| App | 0x00026000 | Adafruit Arduino App |
| Bootloader | 0x000F4000 | Adafruit DFU Bootloader |
| Bootloader Settings | 0x000FF000 | CRC + App-Metadaten (4 KB) |
| UICR | 0x10001000 | BOOTLOADERADDR @ 0x10001014 |
UICR prüfen
JLinkExe -device nRF52840_xxAA -if SWD -speed 4000 -autoconnect 1 << 'EOF'
mem32 0x10001014 1
exit
EOF
Erwarteter Wert: 0x000F4000 (Bootloader-Adresse).
Wert 0xFFFFFFFF → Bootloader fehlt oder UICR gelöscht → Full-Restore nötig.
Upload (Normalfall)
cd examples/nRF52_Basic
pio run -e nrf52840dk -t upload
PlatformIO schreibt automatisch:
firmware.bin@0x26000firmware_signature.bin@0xFF000(Bootloader-Settings mit App-CRC)
Ohne firmware_signature.bin startet der Bootloader die App nicht
und bleibt im DFU-Modus (pulsierendes LED).
⚠️ CPU bleibt nach jedem PIO-Upload gehalten
Ursache (hardcodiert in PIO):
~/.platformio/platforms/nordicnrf52/builder/main.py → _jlink_cmd_script() generiert:
h
loadbin firmware.bin, 0x26000
loadbin firmware_signature.bin, 0xFF000
r
q
r = Reset+Halt, q = quit – kein g (go). CPU friert nach jedem Upload ein.
Dauerlösung: extra_scripts = post:post_upload.py in platformio.ini
post_upload.py übernimmt zwei Aufgaben:
-
Pre-Upload – stellt den kompletten BLE-Stack (MBR, SoftDevice S140, Bootloader, UICR) aus der Framework-HEX wieder her, bevor PIO das App-Binary flasht. JLink vergleicht zuerst und flasht nur geänderte Sektoren – dauert ≈ 2–3 s extra wenn der Stack intakt ist, spart den manuellen Full-Restore wenn er gelöscht wurde.
-
Post-Upload – sendet
g(go) damit die CPU nach dem Upload tatsächlich startet.
# post_upload.py (examples/nRF52_Basic/post_upload.py)
import glob
import os
import subprocess
import tempfile
Import("env") # noqa: F821
def _jlink_run(commands):
with tempfile.NamedTemporaryFile(
mode="w", suffix=".jlink", delete=False) as f:
f.write("\n".join(commands) + "\n")
tmpfile = f.name
try:
return subprocess.run(
["JLinkExe", "-device", "nRF52840_xxAA", "-if", "SWD",
"-speed", "4000", "-autoconnect", "1", "-CommandFile", tmpfile],
capture_output=True, text=True,
)
finally:
os.unlink(tmpfile)
def jlink_restore_stack(source, target, env):
if env.get("UPLOAD_PROTOCOL") != "jlink":
return
fw_dir = env.PioPlatform().get_package_dir("framework-arduinoadafruitnrf52")
board_variant = env.BoardConfig().get("build.variant", "pca10056")
bl_dir = os.path.join(fw_dir, "bootloader", board_variant)
matches = sorted(glob.glob(os.path.join(bl_dir, "*.hex")))
if not matches:
print(f">> pre_upload: No bootloader HEX found in {bl_dir} – skipping")
return
bl_hex = matches[-1]
print(">> pre_upload: Restoring MBR + SoftDevice + Bootloader + UICR")
print(f" HEX: {os.path.basename(bl_hex)}")
r = _jlink_run(["h", f"loadfile {bl_hex}", "exit"])
print(">> pre_upload: BLE stack restored OK" if r.returncode == 0
else ">> pre_upload: Restore FAILED\n" + r.stderr)
def jlink_go_after_upload(source, target, env):
if env.get("UPLOAD_PROTOCOL") != "jlink":
return
print(">> post_upload: sending 'g' to release CPU halt...")
r = _jlink_run(["g", "exit"])
print(">> post_upload: CPU released (go OK)" if r.returncode == 0
else ">> post_upload: FAILED\n" + r.stderr)
env.AddPreAction("upload", jlink_restore_stack) # noqa: F821
env.AddPostAction("upload", jlink_go_after_upload) # noqa: F821
Hinweis:
/dev/stdinfunktioniert nicht als-CommandFilebei JLinkExe → immer Tempfile verwenden.
Manueller Einzel-Fix (ohne Post-Script):
# Tempfile nötig – /dev/stdin funktioniert nicht mit JLinkExe!
printf 'g\nexit\n' > /tmp/jlink_go.jlink
JLinkExe -device nRF52840_xxAA -if SWD -speed 4000 -autoconnect 1 \
-CommandFile /tmp/jlink_go.jlink
Flash-Zustand prüfen (vor g):
printf 'regs\nmem 0x00000000, 4\nexit\n' > /tmp/jlink_diag.jlink
JLinkExe -device nRF52840_xxAA -if SWD -speed 4000 -autoconnect 1 \
-CommandFile /tmp/jlink_diag.jlink
PC= gültige Adresse + kein Fault → nur CPU angehalten →ggenügtPC = FFFFFFFE+0x00000000 = FF FF FF FF→ SoftDevice gelöscht → Full-Restore nötigXPSR IPSR = 003 (HardFault)→ CPU in HardFault →ghilft nicht → Full-Restore nötig
Full-Restore (nach versehentlichem Chip-Erase)
Normalfall:
post_upload.pystellt den Stack automatisch vor jedem Upload wieder her. Der manuelle Restore unten ist nur nötig wenn auchpost_upload.pynicht mehr hilft (z. B. JLink-Verbindung verloren, PIO gar nicht startbar).
BL=~/.platformio/packages/framework-arduinoadafruitnrf52/bootloader/pca10056/pca10056_bootloader-0.9.0_s140_6.1.1.hex
# Schreibt erase + komplette HEX (MBR + SoftDevice + Bootloader + UICR) + go
printf "erase\nloadfile %s\nr\ng\nexit\n" "$BL" > /tmp/restore.jlink
JLinkExe -device nRF52840_xxAA -if SWD -speed 4000 -autoconnect 1 -CommandFile /tmp/restore.jlink
# Danach: App + firmware_signature.bin via PIO flashen
cd examples/nRF52_Basic && pio run -e nrf52840dk -t upload
Hinweis:
loadfilefür die.hex(enthält SoftDevice + Bootloader + UICR).loadbinfür die App.bin(ohne Header, immer mit Zieladresse0x26000).
⚠️ Häufige Falle: Partielles Erase löscht Bootloader
erase 0x26000 0x100000 löscht auch den Bootloader bei 0xF4000!
UICR bleibt erhalten → Boot-Loop / DFU-Pulsieren.
Sichere Alternative für App-only Update:
# Nur App-Seiten (0x26000–0x51FFF), Bootloader bei 0xF4000 bleibt
erase 0x26000 0x51000
loadbin firmware.bin 0x26000
Besser: immer pio run -t upload verwenden.
LED-Pin-Mapping (pca10056 / Adafruit BSP)
LED_RED = PIN_LED1 = Arduino 13 → P0.13 (LED1 auf DK)
LED_BLUE = PIN_LED2 = Arduino 14 → P0.14 (LED2 auf DK)
PIN_LED3 = Arduino 15 → P0.15 (LED3 auf DK)
PIN_LED4 = Arduino 16 → P0.16 (LED4 auf DK)
LED_STATE_ON = 0 (active-LOW)
initVariant() im BSP setzt alle 4 LEDs automatisch auf OUTPUT + OFF beim Start.
Direktes GPIO ohne BSP (Busy-Wait-Blink für Early-Boot-Test)
#define GPIO0_OUTSET (*(volatile uint32_t*)0x50000508UL)
#define GPIO0_OUTCLR (*(volatile uint32_t*)0x5000050CUL)
#define GPIO0_DIRSET (*(volatile uint32_t*)0x50000518UL)
#define LED1_BIT (1u << 13)
GPIO0_DIRSET = LED1_BIT;
GPIO0_OUTCLR = LED1_BIT; // ON (active-LOW)
for (volatile uint32_t d = 0; d < 400000; d++) {}
GPIO0_OUTSET = LED1_BIT; // OFF
Nützlich wenn delay() / FreeRTOS noch nicht initialisiert sind.
DFU-Modus erkennen
- Pulsierendes LED = Adafruit Bootloader im DFU-Modus
- Ursache:
firmware_signature.binfehlt oder ist ungültig - Fix:
pio run -t upload(schreibt signature automatisch)
Serial Monitor
J-Link VCOM (Serial2 / UART) – empfohlen für nRF52840-DK
- Port: J-Link VCOM (Serial2 UART) – aktuell
/dev/ttyACM0, Nummer kann variieren - Kein zweites USB-Kabel nötig
- Port-Nummer mit
ls /dev/ttyACM*oderpio device listermitteln
pio device monitor --port /dev/ttyACM0 --baud 115200
Im Code mit DBG_SERIAL-Makro (statt -DSerial=Serial2 Build-Flag):
#if defined(BOARD_NRF52DK)
#define DBG_SERIAL Serial2 // J-Link VCOM (ttyACM2)
#else
#define DBG_SERIAL Serial // TinyUSB CDC
#endif
Warum nicht
-DSerial=Serial2? Der Präprozessor würdeAdafruit_USBD_CDC SerialzuAdafruit_USBD_CDC Serial2umbenennen – und kollidiert dann mit der BSP-DeklarationUart Serial2. Build-Fehler.
TinyUSB CDC (Serial) – für XIAO / Feather
TinyUSB erzeugt einen eigenen CDC-Port (erscheint nach Serial.begin()).
monitor_port weglassen oder auf den USB-CDC-Port zeigen.
Warte-Schleife im Code:
while (!DBG_SERIAL && (millis() - startTime < 8000)) { /* ... */ }
---
*Content truncated.*
When not to use it
- →When using the Nordic BSP instead of Adafruit BSP
- →When the CPU remains held after a PlatformIO upload
- →When the UICR value is 0xFFFFFFFF, indicating a missing bootloader
Limitations
- →The Nordic-BSP has a wrong Pin-Mapping for LED_BUILTIN
- →CPU remains held after every PIO-Upload due to hardcoded JLinkExe commands
- →Pulsing LED indicates Adafruit Bootloader in DFU-Modus due to missing or invalid firmware_signature.bin
How it compares
This skill provides specific configurations and troubleshooting steps for nRF52840 development with PlatformIO and the Adafruit BSP, addressing common issues like CPU halting after upload.
Compared to similar skills
nrf52840-platformio side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| nrf52840-platformio (this skill) | 0 | 2mo | Review | Advanced |
| bfd-register-capture | 0 | 4mo | Review | Advanced |
| unreal-engine-cpp-pro | 43 | 3mo | No flags | Advanced |
| llvm-tooling | 1 | 6mo | Review | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by the78mole
View all by the78mole →You might also like
bfd-register-capture
Sonder4
Use when sampling STM32 peripheral registers, tracking register changes over time, or exporting register snapshots to CSV for hardware-state analysis.
unreal-engine-cpp-pro
sickn33
Expert guide for Unreal Engine 5.x C++ development, covering UObject hygiene, performance patterns, and best practices.
llvm-tooling
gmh5225
Expertise in LLVM tooling development including Clang plugins, LLDB debugger extensions, Clangd/LSP, and LibTooling. Use this skill when building source code analysis tools, refactoring tools, debugger extensions, or IDE integrations.
static-analysis
gmh5225
Expertise in LLVM-based static analysis including dataflow analysis, pointer analysis, taint tracking, and program verification. Use this skill when implementing security scanners, bug finders, code quality tools, or performing program analysis research.
memory-safety-patterns
sickn33
Implement memory-safe programming with RAII, ownership, smart pointers, and resource management across Rust, C++, and C. Use when writing safe systems code, managing resources, or preventing memory bugs.
debug-lldb
regenrek
Capture and analyze thread backtraces with LLDB/GDB to debug hangs, deadlocks, UI freezes, IPC stalls, or high-CPU loops across any language or project. Use when an app becomes unresponsive, switching contexts stalls, or you need thread stacks to locate lock inversion or blocking calls.