nRF Connect SDK Migration: From Legacy SDK in Under a Week
Nordic deprecated its legacy nRF5 SDK in favor of nRF Connect SDK. The migration is mostly mechanical but the Kconfig model and west manifests require new habits.

Nordic deprecated nRF5 SDK in favor of nRF Connect SDK (NCS) built on Zephyr. Our migration from nRF5 SDK 17.1 to NCS 2.6.0 took five engineer-days spread across one calendar week — mostly mechanical, but Kconfig, devicetree, and west manifests require new habits. This is the checklist that worked for a codebase with SoftDevice BLE, custom drivers, and production MCUboot.
Related: Zephyr west workspace setup, Zephyr vs FreeRTOS scheduling.
Scope of migration
| Component | nRF5 SDK 17.1 | NCS 2.6.0 |
|---|---|---|
| RTOS | FreeRTOS (optional) / bare | Zephyr |
| BLE stack | SoftDevice S140 | Zephyr Bluetooth host + Nordic controller |
| Build | SES / Make | west + CMake + ninja |
| Pin config | sdk_config.h | Kconfig + devicetree |
| Bootloader | nrf_bootloader / DFU | MCUboot + mcumgr |
| Logging | NRF_LOG | LOG_MODULE_REGISTER |
~45k LOC application C, 12 custom drivers, 3 board variants.
Day 0: inventory
Exported list:
- All
#include <nrfx_*.h>usage - SoftDevice BLE services and GATT table
app_timerandnrf_drv_clockcalls- NVMC / fstorage usage
- UICR / FICR dependencies
- IRQ handlers naming SoftDevice vectors
Tools: ripgrep + spreadsheet. No code changes yet.
Day 1–2: west workspace + blinky
Follow west workspace reproducible setup:
west init -m https://github.com/nrfconnect/sdk-nrf --mr v2.6.0 ncs-project
cd ncs-project
west update
Build Nordic sample:
west build -b nrf52840dk_nrf52840 zephyr/samples/basic/blinky
west flash
Confirm JLink / nrfjprog paths match old toolchain — our CI uses nrfutil device now.
Mechanical mapping table
| nRF5 SDK | NCS / Zephyr |
|---|---|
nrf_gpio_pin_set() | gpio_pin_set_dt() or gpio_pin_set() |
app_timer_create() | k_timer or k_work_delayable |
nrf_delay_ms() | k_msleep() |
NRF_LOG_INFO() | LOG_INF() |
sdk_config.h CONFIG_* | prj.conf + Kconfig fragments |
nrfx_spim | spi_nrfx_spim devicetree node |
fstorage | NVS / file system / flash map |
| SoftDevice observer | BT_CONN_CB_DEFINE, bt_gatt_* |
Not 1:1 — budget time for behavioral differences.
Devicetree vs board files
Old: custom_board.h with pin defines.
New: boards/arm/myboard/myboard.dts:
/ {
model = "My Board";
compatible = "vendor,myboard";
leds {
compatible = "gpio-leds";
led0: led_0 {
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
};
};
};
Overlay for variants: app/boards/nrf52840dk_nrf52840.overlay during bring-up, then proper board definition.
Footgun: Forgot status = "okay" on SPI node — driver probe silently failed.
BLE migration (largest chunk)
SoftDevice GATT table → Zephyr GATT macros:
Old:
BLE_SERVICE_DEF(m_svc, ...);
New:
BT_GATT_SERVICE_DEFINE(m_svc,
BT_GATT_PRIMARY_SERVICE(&my_uuid),
BT_GATT_CHARACTERISTIC(..., BT_GATT_CHRC_READ, ...),
);
Connection parameter negotiation API names changed — re-read Nordic UART sample in NCS 2.6.
Radio timeslot API for proprietary 2.4 GHz — not portable; we kept proprietary on nRF5 longer, migrated in phase 2.
Kconfig migration
Exported old sdk_config.h enabled symbols → translated to prj.conf:
CONFIG_GPIO=y
CONFIG_SPI=y
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_LOG=y
CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_ASSERT=y
Use menuconfig to discover Zephyr equivalents — search by keyword.
Flash / NVS / settings
Old fstorage record in last flash pages → Zephyr settings subsystem:
CONFIG_FLASH=y
CONFIG_FLASH_PAGE_LAYOUT=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS_SECTOR_COUNT=8
Migration tool read old records via JLink once, wrote to NVS on first boot — one-time factory script.
MCUboot
Replaced custom nrf bootloader with MCUboot + serial recovery for factory:
CONFIG_BOOTLOADER_MCUBOOT=y
See mcumoot docs in NCS — slot sizes in devicetree partitions must match linker scripts.
CI changes
Old: Makefile + nrf5 SDK path env var.
New: Docker image with west + NCS tag, build:
west build -b myboard app --sysbuild # sysbuild for multi-image MCUboot
Build time 2.3× longer — ccache helped.
Tests that caught regressions
- BLE throughput GATT notify flood — Zephyr buffer pool defaults too small (
CONFIG_BT_L2CAP_TX_BUF_COUNT) - SPI sensor at 8 MHz — devicetree
sck-pindrive strength missing, scope showed rounded clock - Deep sleep current —
CONFIG_SYS_POWER_MANAGEMENTinteraction with UART wake
Timeline honesty
| Phase | Days |
|---|---|
| Workspace + blinky | 0.5 |
| Board devicetree | 1 |
| Drivers (SPI, GPIO, timers) | 1.5 |
| BLE services | 1.5 |
| Bootloader + NVS migration | 0.5 |
| CI + fix regressions | 1 |
One engineer full-time, another reviewing. Calendar week with meetings.
What we deferred
Proprietary radio on timeslot API — stayed on nRF5 chip for one product line until Zephyr MPSL exposed needed hooks.
Full SoftDevice coexistence — not available; dual-image not worth it.
What I would do differently
Run Nordic's migration webinar checklist before touching drivers — we rewrote one driver that had Zephyr shim already in NCS.
Start from closest NCS sample (peripheral UART, etc.) and diff, not blank CMakeLists.
Keep nRF5 CI green one release while NCS branch matures — parallel shipping reduced pressure.
Related
West workspace for manifest pinning. Zephyr vs FreeRTOS scheduling for RTOS behavior changes affecting timing.
Manish Bookreader
Electronics enthusiast, Embedded Systems Expert, Linux/Networking programmer, and Software Engineer passionate about AI, electronics, books, and cooking.

