This blog is to describe how to use the nRF52 Series USB interface to communicate with Android host. In the nRF52 Series, nRF52840, nRF52833 and nRF52820 have supported the full speed USB 2.0. Instead of using the Bluetooth Low Energy, they can communicate the date between android host through USB interface.
In this blog, I would show how to use the USB CDC class for Android communication.
Hardware Requirement
- nRF52840 / nRF52833 / nRF52820 Development Board x 1
- USB Cable (type-C to Micro USB) x 1
- Android Phone x 1
SimpleUsbTerminal

This Android app provides a line-oriented terminal / console for devices with a serial / UART interface connected with a USB-to-serial-converter.
It supports USB to serial converters based on
- FTDI FT232, FT2232, …
- Prolific PL2303
- Silabs CP2102, CP2105, …
- Qinheng CH340
and devices implementing the USB CDC protocol like
- Arduino using ATmega32U4
- Digispark using V-USB software USB
- BBC micro:bit using ARM mbed DAPLink firmware
https://github.com/kai-morich/SimpleUsbTerminal
USB communication
Regarding on USB communication, it can use either USB Bulk or USB CDC class approach.
USB Bulk
Inside Nordic nRF5 SDK, the USB Mass Storage Class example uses bulk transfers, so you can take a look at that example if you are interested in bulk transfers. The usbd_hid_generic example does not use bulk transfers, since the HID specification defines that HID class devices should use Interrupt transfers and not bulk transfers.
How to transmit BULK via USB HID?
https://devzone.nordicsemi.com/f/nordic-q-a/44442/how-to-transmit-bulk-via-usb-hid
USB BULK Example of nRF52840 & S140 (v6.0.0-6) & SDK14.1
https://devzone.nordicsemi.com/f/nordic-q-a/27276/usb-bulk-example-of-nrf52840-s140-v6-0-0-6-sdk14-1
USB CDC
USB communications device class (or USB CDC class) is a composite Universal Serial Bus device class.
Nordic nRF5 SDK
There are 4 difference USB device library classes inside the nRF52 SDK as below.

For example,
USBD BLE UART Example
This example uses the USB CDC ACM module. See the documentation of this module for links to relevant specification documents. For explanation of the BLE part, see UART/Serial Port Emulation over BLE example.
This example shows how to use the CDC ACM USB class, commonly known as Virtual COM port, with the Nordic UART Service.
https://infocenter.nordicsemi.com/topic/sdk_nrf5_v16.0.0/usbd_ble_uart_example.html
Initialize the USB CDC_ACM Class
// USB DEFINES START
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
app_usbd_cdc_acm_user_event_t event);
#define CDC_ACM_COMM_INTERFACE 0
#define CDC_ACM_COMM_EPIN NRF_DRV_USBD_EPIN2
#define CDC_ACM_DATA_INTERFACE 1
#define CDC_ACM_DATA_EPIN NRF_DRV_USBD_EPIN1
#define CDC_ACM_DATA_EPOUT NRF_DRV_USBD_EPOUT1
static char m_cdc_data_array[BLE_NUS_MAX_DATA_LEN];
/** @brief CDC_ACM class instance */
APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
cdc_acm_user_ev_handler,
CDC_ACM_COMM_INTERFACE,
CDC_ACM_DATA_INTERFACE,
CDC_ACM_COMM_EPIN,
CDC_ACM_DATA_EPIN,
CDC_ACM_DATA_EPOUT,
APP_USBD_CDC_COMM_PROTOCOL_AT_V250);
Handler of the USB Module
// USB CODE START
static bool m_usb_connected = false;
/** @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t */
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
app_usbd_cdc_acm_user_event_t event)
{
app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
switch (event)
{
case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN:
{
/*Set up the first transfer*/
ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
m_cdc_data_array,
1);
UNUSED_VARIABLE(ret);
ret = app_timer_stop(m_blink_cdc);
APP_ERROR_CHECK(ret);
bsp_board_led_on(LED_CDC_ACM_CONN);
NRF_LOG_INFO("CDC ACM port opened");
break;
}
case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
NRF_LOG_INFO("CDC ACM port closed");
if (m_usb_connected)
{
ret_code_t ret = app_timer_start(m_blink_cdc,
APP_TIMER_TICKS(LED_BLINK_INTERVAL),
(void *) LED_CDC_ACM_CONN);
APP_ERROR_CHECK(ret);
}
break;
case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
break;
case APP_USBD_CDC_ACM_USER_EVT_RX_DONE:
{
ret_code_t ret;
static uint8_t index = 0;
index++;
do
{
if ((m_cdc_data_array[index - 1] == '\n') ||
(m_cdc_data_array[index - 1] == '\r') ||
(index >= (m_ble_nus_max_data_len)))
{
if (index > 1)
{
bsp_board_led_invert(LED_CDC_ACM_RX);
NRF_LOG_DEBUG("Ready to send data over BLE NUS");
NRF_LOG_HEXDUMP_DEBUG(m_cdc_data_array, index);
do
{
uint16_t length = (uint16_t)index;
if (length + sizeof(ENDLINE_STRING) < BLE_NUS_MAX_DATA_LEN)
{
memcpy(m_cdc_data_array + length, ENDLINE_STRING, sizeof(ENDLINE_STRING));
length += sizeof(ENDLINE_STRING);
}
ret = ble_nus_data_send(&m_nus,
(uint8_t *) m_cdc_data_array,
&length,
m_conn_handle);
if (ret == NRF_ERROR_NOT_FOUND)
{
NRF_LOG_INFO("BLE NUS unavailable, data received: %s", m_cdc_data_array);
break;
}
if (ret == NRF_ERROR_RESOURCES)
{
NRF_LOG_ERROR("BLE NUS Too many notifications queued.");
break;
}
if ((ret != NRF_ERROR_INVALID_STATE) && (ret != NRF_ERROR_BUSY))
{
APP_ERROR_CHECK(ret);
}
}
while (ret == NRF_ERROR_BUSY);
}
index = 0;
}
/*Get amount of data transferred*/
size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
NRF_LOG_DEBUG("RX: size: %lu char: %c", size, m_cdc_data_array[index - 1]);
/* Fetch data until internal buffer is empty */
ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
&m_cdc_data_array[index],
1);
if (ret == NRF_SUCCESS)
{
index++;
}
}
while (ret == NRF_SUCCESS);
break;
}
default:
break;
}
}
static void usbd_user_ev_handler(app_usbd_event_type_t event)
{
switch (event)
{
case APP_USBD_EVT_DRV_SUSPEND:
break;
case APP_USBD_EVT_DRV_RESUME:
break;
case APP_USBD_EVT_STARTED:
break;
case APP_USBD_EVT_STOPPED:
app_usbd_disable();
break;
case APP_USBD_EVT_POWER_DETECTED:
NRF_LOG_INFO("USB power detected");
if (!nrf_drv_usbd_is_enabled())
{
app_usbd_enable();
}
break;
case APP_USBD_EVT_POWER_REMOVED:
{
NRF_LOG_INFO("USB power removed");
ret_code_t err_code = app_timer_stop(m_blink_cdc);
APP_ERROR_CHECK(err_code);
bsp_board_led_off(LED_CDC_ACM_CONN);
m_usb_connected = false;
app_usbd_stop();
}
break;
case APP_USBD_EVT_POWER_READY:
{
NRF_LOG_INFO("USB ready");
ret_code_t err_code = app_timer_start(m_blink_cdc,
APP_TIMER_TICKS(LED_BLINK_INTERVAL),
(void *) LED_CDC_ACM_CONN);
APP_ERROR_CHECK(err_code);
m_usb_connected = true;
app_usbd_start();
}
break;
default:
break;
}
}
Hello Jimmy. Is it possible to replace the CP2104 of nrf52 feather with PL2303 to do the programming and communication?
LikeLike
Hi, I don’t have information on the adafruit feather. I can’t give you the answer.
LikeLike
Thats okay. Nice blog, BTW!
LikeLike
Hi Jimmy,
The nRF52833 is compatible with Arduino IDE, I mean is it possible to connect nRF52833-DK through the Arduino IDE?
LikeLike
I haven’t tried it yet. But I think it is possible. It only needs to get the correct bsp.
LikeLike