Skip to content

BLE Device Firmware Update (DFU)

This example shows testing of the DFU process using a test blinky image. It uploads the test blinky image to the device, and verifies it was uploaded correctly (it does not confirm the image, so it will never be executed on the target device).

It consists of two files:

conftest.py - Implements a fixture that connects to the first device with EmbedOps in the name.

test_dfu.py - Contains a test that sends data and asserts that the DFU process was successful.

This test also makes use of the pytest-asyncio module, which adds asyncio capabilities to PyTest.

import pytest
import pytest_asyncio
import logging
import time

from hil_sdk.version import __version__
from hil_sdk.interfaces.ble.ble_client import BLEClient
from hil_sdk.interfaces.ble.nordic.ble_dfu_service import BLEDFUService
from hil_sdk.interfaces.ble.ble_scanner import BLEScanner
from hil_sdk.interfaces.jlink_interface import JLinkInterface
from hil_sdk.interfaces.nrfjprog_interface import nrfjprog_flash

@pytest.fixture(autouse=True, scope="session")
def hil_version_fixture():
    logging.info(f"HIL SDK version {__version__}")

@pytest.fixture(autouse=True, scope="session")
def jlink_fixture(hil_version_fixture):

    logging.info("Creating J-Link interface...")
    return JLinkInterface("nRF5340_xxAA_APP")

@pytest.fixture(autouse=True, scope="session")
def flash_fixture(jlink_fixture, hil_extras_get_path):

    logging.info("Flashing target...")
    assert nrfjprog_flash(hil_extras_get_path("build/zephyr/merged_domains.hex"), "NRF53") == 0

    logging.info("Resetting target...")
    assert jlink_fixture.reset_and_go()

    time.sleep(2)  # Sleep for a couple of seconds to let the target device start up

@pytest.fixture
def reset_fixture(jlink_fixture):

    logging.info("Resetting target...")
    assert jlink_fixture.reset_and_go()

@pytest_asyncio.fixture
async def ble_dfu_fixture(reset_fixture):

    devices = await BLEScanner.find_devices()

    address = None
    device = None
    for d in devices:
        if "EmbedOps" in d.name:
            address = d.address
            device = d

    if address is None:
        logging.error('EmbedOps device not found!')
        assert False

    logging.info("EmbedOps address: " + address)

    client = BLEClient(device)

    await client.connect()

    dfu_service = BLEDFUService(client)

    yield dfu_service

    await client.disconnect()
import os
import asyncio
import pytest

@pytest.mark.asyncio
async def test_ble_dfu(ble_dfu_fixture):

    test_dir = os.path.dirname(os.path.abspath(__file__))
    test_image = os.path.join(test_dir, "app_update.bin")

    def update_callback(offset, total_size):

        percent = int(float(offset) / float(total_size) * 100)

        if percent % 10 == 0:
            print(f"DFU Progress: {percent}%")

    print("Starting DFU")
    dfu_status = await ble_dfu_fixture.image_upload(test_image, on_update=update_callback)
    print(f"DFU Complete: {dfu_status}")

    await asyncio.sleep(5)

    assert dfu_status