Nordic nRF Firmware Development with Zephyr & NCS: A Practical Guide
How we ship Nordic nRF52 / nRF54 firmware in 2026 — Zephyr workflow, NCS quirks, BLE peripheral patterns, OTA, and the migration path from older Nordic SDKs.
Nordic Semiconductor’s nRF lineup is the default for serious BLE products, and the Nordic Connect SDK (NCS) — built on Zephyr — is the default toolchain in 2026. The transition from the old nRF5 SDK was rocky for a couple of years; in 2026 NCS is mature enough to be the unambiguous starting point for new projects.
Here is the workflow we use, the gotchas that still bite, and the migration path for products on the legacy SDK.
Why NCS / Zephyr won
Three structural advantages held up over the multi-year transition:
- Multi-target support out of the box. A Zephyr application can target nRF52, nRF53, nRF54, and most non-Nordic Cortex-M chips with minimal porting. Vendor-locked SDKs lose to this every time over a 5-year horizon.
- Modern build system. West, CMake, and Kconfig replace the old IDE-driven, vendor-specific project files. Continuous integration becomes practical; reproducibility becomes default.
- Mature subsystems. Bluetooth Mesh, Matter, Thread, OpenThread, and OpenSSL/Mbed-TLS integrations all work without the patches that the legacy SDK era required.
If you are starting a new Nordic project in 2026, do it on NCS. If you are maintaining an old nRF5 SDK product, the question is when (not whether) to migrate.
The development environment
What we install on a new engineer’s machine on day one:
- VS Code with the official nRF Connect for VS Code extension. The extension manages NCS toolchain installation and provides build/flash UI on top of West.
- nRF Connect for Desktop — for SoC programming and logging.
- Segger J-Link drivers (J-Link Mini or J-Link OB on the dev kit).
- nRF Util — Nordic’s command-line tool for DFU, programming, and trace.
For headless / CI environments:
west+ Zephyr SDK installed via Nordic’s toolchain managerpyocdorprobe-rsas an alternative to J-Link if the hardware is set up for it
A first-time developer should be able to flash blinky in under an hour from a clean machine. If it takes longer, something is wrong with the install.
A clean project layout
We use a west workspace with the application as a separate repo:
my-product/
├── west.yml (manifest pinning NCS version)
├── app/
│ ├── CMakeLists.txt
│ ├── prj.conf
│ ├── boards/
│ │ └── my_custom_board.overlay
│ └── src/
│ ├── main.c
│ └── ble/
└── deps/ (managed by west)
west.yml pins the NCS version explicitly. We do not float on main — every commit reproducibly builds against a known SDK version. Updating NCS is a deliberate, tested change.
BLE peripheral patterns
For a typical BLE peripheral product (sensor, wearable, smart device), the Zephyr bt subsystem covers the heavy lifting. The skeleton:
static void on_connected(struct bt_conn *conn, uint8_t err) {
if (err) { LOG_ERR("conn failed (err %u)", err); return; }
LOG_INF("connected");
// Enable encryption, request connection params, etc.
}
static void on_disconnected(struct bt_conn *conn, uint8_t reason) {
LOG_INF("disconnected (reason %u)", reason);
bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), NULL, 0);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = on_connected,
.disconnected = on_disconnected,
};
Patterns we use on every project:
- Connection-parameter negotiation in the connected callback. Phones often connect with aggressive parameters that shred battery; request what the application actually needs.
- Bond management with
bt_settingsso pairing survives reboot. Storage onnvsorzms. - GATT attribute permissions that require encryption for any sensitive characteristic.
BT_GATT_PERM_READ_ENCRYPTandBT_GATT_PERM_WRITE_ENCRYPTare not optional on production firmware. - MITM-resistant pairing using LE Secure Connections with passkey or numeric comparison if the product has any UI.
For deeper battery analysis on BLE products see our BLE battery post.
OTA / DFU on NCS
Nordic’s MCUboot integration is mature and the right default in 2026.
The pattern:
- Bootloader: MCUboot, configured for swap-using-scratch or swap-using-move, with hardware-rooted public-key verification (key in OTP/FUSES on nRF54, or in the bootloader image on nRF52).
- Image signing in CI using a key that lives in an HSM, not in the repo.
- DFU transport: SMP over BLE (Simple Management Protocol). Phone-side libraries: nRF Connect Device Manager on Android/iOS, or your own implementation using the SMP protocol.
- A/B partitions: primary and secondary, with a scratch area for swap. Configure partition layout in
pm_static.ymlfor predictable manufacturing. - Rollback: MCUboot reverts to the previous image if the new one doesn’t confirm itself within a configurable boot window.
See our OTA post for the broader patterns. The Nordic-specific things to know:
- nRF52840 has 1 MB flash; partition planning is tight. nRF54L15 with 1 MB flash is more comfortable. nRF5340 and nRF54H20 with their secondary cores are even more so.
- DFU over BLE is slow (5–15 minutes for a 500 KB image on a 4-second connection interval). Plan UX accordingly.
Migration from legacy nRF5 SDK
If you have a product on the old nRF5 SDK, plan migration like this:
Phase 0: confirm the product is supported on NCS. nRF52 family is. Older nRF51 chips are not — they are end-of-life in NCS.
Phase 1: bring up NCS for the same hardware in parallel. Build a minimum BLE peripheral that advertises and connects. This proves the toolchain and board file work.
Phase 2: port the GATT services and application logic. This is the bulk of the work — typically 4–8 weeks for a moderate-complexity product.
Phase 3: port DFU. The legacy SDK used Nordic’s proprietary DFU; NCS uses MCUboot. You will need an interim release that accepts both DFU types so existing devices in the field can migrate.
Phase 4: ship the migration update via OTA. Existing devices receive the legacy-format DFU that installs the NCS-based firmware. After this, all subsequent updates are MCUboot.
The migration is usually a quarter of work for a small product, longer for complex ones. Doing it on a non-rushed schedule is much cheaper than doing it during an incident.
What we hand over
For a Nordic firmware engagement we ship:
- A west workspace with pinned NCS version
- Application code with documented architecture
- Board files for production hardware
- MCUboot bootloader with signed-image verification
- DFU host integration (mobile or desktop)
- CI pipeline that builds, signs, and produces ready-to-flash images
- Power profile data and a battery model spreadsheet
If you are starting a Nordic-based product or migrating from the legacy SDK, we have shipped both several times.
Keep reading
-
Connectivity
BLE Mesh vs Thread vs Zigbee: Picking the Right Mesh in 2026
BLE Mesh, Thread, and Zigbee compared for product teams in 2026 — protocol fit, ecosystem support, Matter compatibility, and the trade-offs we weigh on real projects.
Read -
Embedded
Firmware Architecture Patterns for IoT Products
Event-driven, hierarchical state machines, hardware abstraction layers — the firmware architecture patterns that survive past v1 and don't require a rewrite at v3.
Read -
Embedded
IoT Power Budget Modelling: A Spreadsheet That Predicts Battery Life
How to build a power-budget spreadsheet for an IoT product — duty cycles, sleep currents, derating — that predicts battery life within 10% of measured.
Read