In the previous post (The nRF52840 USB Dongle Tutorial (Part 1)), we explored how to use the new nRF52840 USB Dongle to enable nRF Connect PC applications such as the Bluetooth Low Energy application (central and peripheral emulator), the nRF Cloud Gateway application, and the RSSI Viewer application.
More importantly, we also talked about how the dongle can be used as a development kit (similar to the main nRF52840 development kit). The low price and small size of this dongle make it perfect for prototyping projects that require devices in a small form factor (e.g. large-scale Bluetooth mesh deployments).
However, the dongle also has a few disadvantages compared to the fully-featured nRF52840 development kit.
Advantages
- More compact and much smaller in size
- Plugs directly into and powers from a USB Type A port (making it perfect for powering via a USB wall adapter)
- Ideal for prototyping and quickly developing proof-of-concepts
- Much lower cost ($10 compared to around $45 for the full DK)
- Can be easily mounted onto a breadboard (after some soldering work)
Disadvantages
- No support for an onboard debugger
- Requires some work to get external peripherals connected including soldering
- Limited peripheral support (15 GPIOs only) compared to full access to all pins on the full development kit
- A limited number of built-in LEDs and buttons. It provides 4 LEDs (One Green and one multicolor LED (R, G, B)) and one user-configurable button. This compares to 4 (Green) LEDs and 4 user-configurable buttons on the full development kit
Simple BLE Project Description
We’ll be going over the implementation of a simple BLE application using this dongle, and we’ll do this over a series of two posts.
The project will implement a BLE Peripheral device that includes one external button and one external LED.
We could’ve just used the built-in LEDs and button, but that would be no fun. Besides, learning how to configure the GPIO will help us in the long-run for adding other sensors and I/O devices such as displays, temperature sensors and all sorts of other peripherals.
The basic operation of the project:
- The dongle will operate as a BLE Peripheral that allows a connection from a BLE Central device
- We will implement our own custom service with two characteristics:
- The LED can be controlled by writing to a characteristic
- The button status will be reported via an additional characteristic (that can be either read or automatically notified)
- We’ll use one of the built-in LEDs (Green LED) to indicate connectivity (Solid for connected and flashing for disconnected/advertising)
In today’s post will go over the following steps:
- Installing the necessary software packages
- Getting your hardware ready for prototyping on a breadboard
- Physically connecting the dongle and external peripherals (LED and button) on the breadboard
- Implementing the firmware modifications necessary to add the external LED and button to the GPIOs of the dongle
- Flashing the application to the dongle
- Testing basic functionality
In next week’s post we’ll go over:
- Design of our custom GATT (services and characteristics) to allow control of the LED and reporting the state of the button (pressed vs. released)
- Full implementation of the firmware application on the nRF52840 USB Dongle
- Testing of the device by connecting to and interacting with it from a mobile application
In future posts, we will build upon this foundation and hardware to add more features including:
- Implementing a Central and Peripheral device pair that communicate in long-range mode (Bluetooth 5 Coded PHY) over long distances
- Implementing a weather station device that reads temperature, humidity, and other environmental parameters and transfers the data to another dongle connected to an external display to show the latest readings
- Bluetooth mesh network of multiple nodes implemented on the dongle
What project would you build using the nRF52840 USB dongle? Or maybe you’re already using it for a project? Let me know in the comments section below.
Requirements
Software
- SES (Segger Embedded Studio download link)
- nRF5 SDK version 15.2.0 (download link)
- nRF Connect for Desktop (download link)
- Install the Programmer application within nRF Connect for Desktop
For further details for steps 1 & 2, follow my previous tutorial here: The complete cross-platform nRF development tutorial. For step 3, refer back to part 1 of the nRF52840 USB dongle tutorial series: The nRF52840 USB Dongle Tutorial (Part 1).
Hardware
Here are the hardware pieces I am using for this project:
- One nRF52840 USB Dongle
- One breadboard
- Two header rows of 10 pins each
- Breadboard jumper or copper wires
- One LED
- One push button switch
- One resistor (I’m using a 220 Ohm resistor)
- Optional: USB male to female extension cable (or simply connect the dongle directly to the PC)
Preparing the Hardware
Step 1: Solder the header rows to the dongle (one row on each side of the castellated edges):

Step 2: Mount the dongle onto a breadboard:

Step 3: Connect the LED as shown in the following picture. The LED’s anode pin (long leg) is connected to the resistor to VDD on the dongle, and the LED’s cathode node (short leg) is connected to Pin 1.10.

Step 4: Connect the external button as shown in the following picture. The button is connected to Pin 0.24 on one end and to GND on the other.

As a reference, here’s the schematic for the connections to the dongle:

Step 5: Connect the dongle to the PC

Firmware Modifications for Connecting the Peripherals (LED and Push Button)
The nRF52840 USB dongle obviously utilizes the nRF52840, but that doesn’t mean it’s identical to the nRF52840 development kit. In order to build and run an example originally created for the development kit on the USB dongle, a few changes need to be made to the firmware and configuration.
There are a few examples in the nRF5 SDK that have SES project files for the USB dongle (named PCA10059), but knowing which changes need to be made can help when creating your own examples from scratch or modifying an existing project to run on the USB dongle.
I highly recommend reading through this excellent tutorial over at Nordic’s website before moving forward: nRF52840 Dongle Programming Tutorial
We’ll take the BLE template (PCA10056/nRF52840 development kit) example in the nRF5 SDK and use it as a base for our project. The example can be found at <SDK root>/examples/ble_peripheral/ble_app_template/.
- Step 1: Make a copy of the template example folder and name it appropriately (I named it “nRF52840_Dongle”).
- Step 2: Rename the pca10056 folder to pca10059
It’s also helpful to rename the project/solution file accordingly - Step 3: Replace the flash_placement.xml file with the following code (which you can get from any of the PCA10059 examples included in the SDK)
<!DOCTYPE Linker_Placement_File> <Root name="Flash Section Placement"> <MemorySegment name="FLASH" start="$(FLASH_PH_START)" size="$(FLASH_PH_SIZE)"> <ProgramSection load="no" name=".reserved_flash" start="$(FLASH_PH_START)" size="$(FLASH_START)-$(FLASH_PH_START)" /> <ProgramSection alignment="0x100" load="Yes" name=".vectors" start="$(FLASH_START)" /> <ProgramSection alignment="4" load="Yes" name=".init" /> <ProgramSection alignment="4" load="Yes" name=".init_rodata" /> <ProgramSection alignment="4" load="Yes" name=".text" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_soc_observers" inputsections="*(SORT(.sdh_soc_observers*))" address_symbol="__start_sdh_soc_observers" end_symbol="__stop_sdh_soc_observers" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_ble_observers" inputsections="*(SORT(.sdh_ble_observers*))" address_symbol="__start_sdh_ble_observers" end_symbol="__stop_sdh_ble_observers" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".pwr_mgmt_data" inputsections="*(SORT(.pwr_mgmt_data*))" address_symbol="__start_pwr_mgmt_data" end_symbol="__stop_pwr_mgmt_data" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".nrf_queue" inputsections="*(.nrf_queue*)" address_symbol="__start_nrf_queue" end_symbol="__stop_nrf_queue" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_stack_observers" inputsections="*(SORT(.sdh_stack_observers*))" address_symbol="__start_sdh_stack_observers" end_symbol="__stop_sdh_stack_observers" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_req_observers" inputsections="*(SORT(.sdh_req_observers*))" address_symbol="__start_sdh_req_observers" end_symbol="__stop_sdh_req_observers" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".sdh_state_observers" inputsections="*(SORT(.sdh_state_observers*))" address_symbol="__start_sdh_state_observers" end_symbol="__stop_sdh_state_observers" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".nrf_balloc" inputsections="*(.nrf_balloc*)" address_symbol="__start_nrf_balloc" end_symbol="__stop_nrf_balloc" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".cli_command" inputsections="*(.cli_command*)" address_symbol="__start_cli_command" end_symbol="__stop_cli_command" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".crypto_data" inputsections="*(SORT(.crypto_data*))" address_symbol="__start_crypto_data" end_symbol="__stop_crypto_data" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_const_data" inputsections="*(SORT(.log_const_data*))" address_symbol="__start_log_const_data" end_symbol="__stop_log_const_data" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_backends" inputsections="*(SORT(.log_backends*))" address_symbol="__start_log_backends" end_symbol="__stop_log_backends" /> <ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections" address_symbol="__start_nrf_sections" /> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".cli_sorted_cmd_ptrs" inputsections="*(.cli_sorted_cmd_ptrs*)" runin=".cli_sorted_cmd_ptrs_run"/> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".fs_data" inputsections="*(.fs_data*)" runin=".fs_data_run"/> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_dynamic_data" inputsections="*(SORT(.log_dynamic_data*))" runin=".log_dynamic_data_run"/> <ProgramSection alignment="4" keep="Yes" load="Yes" name=".log_filter_data" inputsections="*(SORT(.log_filter_data*))" runin=".log_filter_data_run"/> <ProgramSection alignment="4" load="Yes" name=".dtors" /> <ProgramSection alignment="4" load="Yes" name=".ctors" /> <ProgramSection alignment="4" load="Yes" name=".rodata" /> <ProgramSection alignment="4" load="Yes" name=".ARM.exidx" address_symbol="__exidx_start" end_symbol="__exidx_end" /> <ProgramSection alignment="4" load="Yes" runin=".fast_run" name=".fast" /> <ProgramSection alignment="4" load="Yes" runin=".data_run" name=".data" /> <ProgramSection alignment="4" load="Yes" runin=".tdata_run" name=".tdata" /> </MemorySegment> <MemorySegment name="RAM" start="$(RAM_PH_START)" size="$(RAM_PH_SIZE)"> <ProgramSection load="no" name=".reserved_ram" start="$(RAM_PH_START)" size="$(RAM_START)-$(RAM_PH_START)" /> <ProgramSection alignment="0x100" load="No" name=".vectors_ram" start="$(RAM_START)" address_symbol="__app_ram_start__"/> <ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections_run" address_symbol="__start_nrf_sections_run" /> <ProgramSection alignment="4" keep="Yes" load="No" name=".cli_sorted_cmd_ptrs_run" address_symbol="__start_cli_sorted_cmd_ptrs" end_symbol="__stop_cli_sorted_cmd_ptrs" /> <ProgramSection alignment="4" keep="Yes" load="No" name=".fs_data_run" address_symbol="__start_fs_data" end_symbol="__stop_fs_data" /> <ProgramSection alignment="4" keep="Yes" load="No" name=".log_dynamic_data_run" address_symbol="__start_log_dynamic_data" end_symbol="__stop_log_dynamic_data" /> <ProgramSection alignment="4" keep="Yes" load="No" name=".log_filter_data_run" address_symbol="__start_log_filter_data" end_symbol="__stop_log_filter_data" /> <ProgramSection alignment="4" keep="Yes" load="No" name=".nrf_sections_run_end" address_symbol="__end_nrf_sections_run" /> <ProgramSection alignment="4" load="No" name=".fast_run" /> <ProgramSection alignment="4" load="No" name=".data_run" /> <ProgramSection alignment="4" load="No" name=".tdata_run" /> <ProgramSection alignment="4" load="No" name=".bss" /> <ProgramSection alignment="4" load="No" name=".tbss" /> <ProgramSection alignment="4" load="No" name=".non_init" /> <ProgramSection alignment="4" size="__HEAPSIZE__" load="No" name=".heap" /> <ProgramSection alignment="8" size="__STACKSIZE__" load="No" place_from_segment_end="Yes" name=".stack" address_symbol="__StackLimit" end_symbol="__StackTop"/> <ProgramSection alignment="8" size="__STACKSIZE_PROCESS__" load="No" name=".stack_process" /> </MemorySegment> </Root>
- Step 4: Edit the “.emProject” file in a text editor and modify the following
c_preprocessor_definitions=”BOARD_PCA10056; to c_preprocessor_definitions=”BOARD_PCA10059; - Step 5: In sdk_config.h, make sure that NRF_LOG_BACKEND_UART_ENABLED is disabled
#ifndef NRF_LOG_BACKEND_UART_ENABLED #define NRF_LOG_BACKEND_UART_ENABLED 0 #endif
- Step 6: Switch the selected configuration to “Release”
- Step 7: Let’s go ahead and change the advertising name to make it more obvious when we test from the mobile app. Modify the code in main.c:
#define DEVICE_NAME "nRF52840 Dongle" /**< Name of device. Will be included in the advertising data. */
- Step 8: Add macros to define the pin configuration of the LED and push button. As we mentioned in the hardware preparation section above, we’ll be connecting the LED to pin 1.10 and the push button to pin 0.24. To configure this, we define the following macros:
// External GPIO Button Definitions #define EXTERNAL_BUTTON NRF_GPIO_PIN_MAP(0,24) // Connected to P0.24 #define MAIN_LED NRF_GPIO_PIN_MAP(1,10) // Connected to P1.10
For now, we will just define the pins. In the next tutorial, we’ll implement the full implementation code needed to initialize, trigger the LED, monitor the push button, and the remaining functionality of the application.
Building and Flashing the Firmware to the Dongle
By default, the “ble_app_template” example that we used behaves as follows:
- It advertises as “Nordic_Template” and flashes the onboard green LED while it’s advertising
- It allows a connection from a BLE Central and turns the green LED solid ON when connected
- It does not, however, implement any services and characteristics.
We can still test that it’s running correctly on the nRF52840 dongle by observing the green LED behavior and connecting to it from a BLE Central device (a BLE mobile app).
First, build the example from within SES by right-clicking on the Project name in the Project Explorer window and choosing Build.
Once the project is built, now we can go through the steps of flashing the binary to the dongle.
Step 1: Launch the nRF Connect for Desktop application

Step 2: Run the Programmer application from within nRF Connect

Step 3: While connected to the PC, press the Reset button on the dongle to put it in DFU mode. LED 2 should start flashing Red.

Step 4: Click the Select device dropdown menu and you should be able to see a device listed as follows:

If you see multiple devices listed, disconnect any other development kits.
Step 5: Once you’ve selected the dongle, you should see the nRF52840 show up with the contents of its memory listed.

Step 6: Now, to flash the dongle we have to first add the hex files to be loaded. We need to add two files:
- The application hex file. This file is located under <example folder>/pca10059/s140/ses/Output/Release/Exe/
- The SoftDevice hex file. This file is located under <SDK>/components/softdevice/s140/hex/
Step 7: Once you’ve added the files, you should now see the File Memory Layout show the loaded hex files.

Step 8: The final step is to flash the dongle with the loaded files. You do this by pressing the Write button under Device.

Once you select Write the left-hand side layout overview will indicate the dongle is being flashed. The log output will also show log messages.

Once you flash it, the dongle will exit DFU mode, start the application, and will disappear from the device list and left view.

Note: remember that the nRF52840 USB Dongle does not have onboard debug capabilities, so I recommend that you verify your application as much as you can on the nRF52840 development kit before moving it over to the dongle.
Testing Functionality
To test the functionality all we want to do is make sure the BLE peripheral can be discovered from a BLE scanning mobile app such as the Nordic nRF Connect (iOS, Android) or the LightBlue Explorer app (iOS, Android).
After flashing the dongle and scanning from the mobile app, we can see that the advertising device shows up in the list. You should also see that the green LED (LED 1) is flashing while advertising.

Once you connect to the device from the mobile app, you’ll see that LED 2 turns solid green.

In the next tutorial, we’ll run more extensive testing once we’ve added our custom service and characteristics, LED control, button presses, etc.
Summary
In today’s tutorial, we learned how to develop BLE applications for the nRF52840 USB dongle – which makes an ideal device for prototyping. We covered:
- Installing the necessary software packages
- Getting your hardware ready for prototyping on a breadboard
- Physically connecting the dongle and external peripherals (LED and button) on the breadboard
- Implementing the firmware modifications necessary to add the external LED and button to the GPIOs of the dongle
- Building and flashing the application to the dongle
- Testing the functionality
Next week, we’ll continue this tutorial and implement the full application to allow:
- Control of an external LED from a BLE central connected to the dongle (the peripheral)
- Reporting of the button status to a BLE central connected to the dongle
What project would you build using the nRF52840 USB dongle? Or maybe you’re already using it for a project? Let me know in the comments section below.
To download the source code for this post, and be notified when the next blog post publishes, be sure to enter your email address in the form below!
Downloads: