using UART and BLE in nRF5 SDK Project on nRF58240 DK

Hello,

I have created my own BLE project. My main looks like this:

int main(void)
{
    //bool erase_bonds;

    // Initialize.
    log_init(); // uses UART
    timer_init();
    //bsp_board_init(BSP_INIT_LEDS);
    power_management_init();
    buttons_leds_init(&erase_bonds);
    uart_init(); // uses UART
    ble_stack_init();
    gatt_init();
    peer_manager_init();
    db_discovery_init();
    //hrs_c_init();
    sdcs_c_init();
    bas_c_init();
    scan_init();

    NRF_LOG_INFO("SDCS client started.");

    scanning_start(&erase_bonds);  

    NRF_LOG_INFO("Waiting for Button press...");
    NRF_LOG_INFO("Button 2 -> start authentication");
    NRF_LOG_INFO("Button 3 -> remote access open door request");
    NRF_LOG_INFO("Button 4 -> set new encryption key via uart");

    // Enter main loop.
    for (;;)
    {
        idle_state_handle();
    }
}

As you can see I initialize some modules (e.g. the logging module and the uart module) and I start scanning. After that I can establish a connection to another BLE (peripheral) device.

Debug log:

<info> app_timer: RTC: initialized.
<info> app: SDCS client started.
<info> app: Starting scan.
<info> app: Waiting for Button press...
<info> app: Button 2 -> start authentication
<info> app: Button 3 -> remote access open door request
<info> app: Button 4 -> set new encryption key via uart
<info> app: Connected. m_conn_handle: 0x0
<info> app: GATT ATT MTU on connection 0x0 changed to 247.
<info> app: BLE_GATTC_EVT_EXCHANGE_MTU_RSP
<info> app: Data length for connection 0x0 updated to 251.
<info> app: SDCS discovered.
<info> ble_sdcs_c: Configuring CCCD. CCCD Handle = 14, Connection Handle = 0
<info> app: BLE_GATTC_EVT_WRITE_RSP, gatt_status: 0x0
<info> app: BLE_GATT_HVX_NOTIFICATION

As soon as the central is connected to the peripheral I can initiate the required authentication process by pressing Button 2. For the authentication I use an encryption key for AES-128 CBC (no padding) encryption. So far this all works when the connected peripheral device is using the standard encryption key. It doesn't work when it has set a custom encryption key.

Now I want to add functionality to my central so that when I press Button 4 on my central device it should show me some information on my RTT (terminal in Segger Embedded Studio) to instruct me to type in a desired encryption key. Then I want to be able to type in the desired encryption key by using UART (via MCU USB port (COM7) on my nRF52840 DK and PuTTY). Once I type q or Q in PuTTY terminal, UART should stop receiving via UART and let me process the input string in my firmware while still not running into errors.

So far I tried to realise this by using code from the examples/peripheral/uart/pca10056/blank/ses example solution. I can debug this example programm and it works.

Because I use the logging module and the uart module from the example program, I got an error in the initialization. I prevent that error from happening by disabling UART for the logging module (NRF_LOG_BACKEND_UART_ENABLED 0 in sdk_config.h). Now the logger is only using RTT.

My current problem is that I don't know exactly how I can make it work with the buttons. Currently I start scanning for BLE peripheral devices near the end of main() before I run into that infinite loop. This way button 2 and 3 work (but get errors because my peer has a custom encryption key) and button 4 doesn't work (it doesn't even show my NRF_LOG_INFO("...") commands on RTT when I press button 4 right after the BLE connection has been established).

my event handler:

void bsp_event_handler(bsp_event_t event)
{
    ret_code_t err_code;

    switch (event)
    {
    /*
        case BSP_EVENT_KEY_0:
        {   
            NRF_LOG_INFO("Button 1 pressed.");
            // Start execution.
            NRF_LOG_INFO("SDCS client started.");

            scanning_start(&erase_bonds); 
            
            // Handle any pending log operation(s), then sleep until the next event occurs.
            for (;;)
            {
                idle_state_handle();
            } 
            break;
        }
        */
        case BSP_EVENT_KEY_1:
        {
            NRF_LOG_INFO("Button 2 pressed.");

            p_sdcs_auth->value = auth_value;

            p_sdcs_auth = &auth_params;
            p_sdcs_auth->auth_failed = false;
            
            #ifdef TEST
              sdcs_auth_challenge_test(p_sdcs_auth); // sendet eine fertige challenge
            #else
              sdcs_auth_challenge(p_sdcs_auth); // Funktion für Echtfall enthält ebenfalls Test-Funktionalität die aktiviert werden kann
            #endif
            
            ble_gattc_write_params_t const p_write_params = 
            {
              .write_op = BLE_GATT_OP_WRITE_REQ, // type: uint8_t , description: GATT write operations: https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s140.api.v7.2.0%2Fgroup___b_l_e___g_a_t_t___w_r_i_t_e___o_p_s.html
              .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, // type: uint8_t , description: Execute prepared write. 
              .handle = 0x0D, // uint16_t , description: Handle to the attribute to be written.
              .offset = 0, // uint16_t, description: Offset in Bytes. For WRITE_CMD and WRITE_REQ, offset must be 0. 
              .len = p_sdcs_auth->len , // uint16_t, description: Length of Data in Bytes, Data Pipe: Length 19 but 20 Bytes
              //.p_value = &value, // type: uint8_t const * , description: Pointer to the value data. 
              .p_value = p_sdcs_auth->value,
            };
            
            NRF_LOG_INFO("gattc_write_pipe(): authentication challenge");
            gattc_write_pipe(p_write_params);
            break;
        }
        case BSP_EVENT_KEY_2:
        {
            NRF_LOG_INFO("Button 3 pressed.");

            p_sdcs_auth->value = auth_value;

            p_sdcs_auth = &auth_params;

            sdcs_remote_access_timed_open_door(p_sdcs_auth);

            ble_gattc_write_params_t const p_write_params = 
            {
              .write_op = BLE_GATT_OP_WRITE_REQ, // type: uint8_t , description: GATT write operations: https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.s140.api.v7.2.0%2Fgroup___b_l_e___g_a_t_t___w_r_i_t_e___o_p_s.html
              .flags = BLE_GATT_EXEC_WRITE_FLAG_PREPARED_WRITE, // type: uint8_t , description: Execute prepared write. 
              .handle = 0x0D, // uint16_t , description: Handle to the attribute to be written.
              .offset = 0, // uint16_t, description: Offset in Bytes. For WRITE_CMD and WRITE_REQ, offset must be 0. 
              .len = p_sdcs_auth->len , // uint16_t, description: Length of Data in Bytes, Data Pipe: Length 19 but 20 Bytes
              //.p_value = &value, // type: uint8_t const * , description: Pointer to the value data. 
              .p_value = p_sdcs_auth->value,
            };
            
            NRF_LOG_INFO("gattc_write_pipe(): sdcs release cylinder for release time (0xE1)");
            gattc_write_pipe(p_write_params);

            break;
        }
        case BSP_EVENT_KEY_3:
        {
            NRF_LOG_INFO("Button 4 pressed.");
            NRF_LOG_INFO("Please enter a new encryption_key: ");
            uart_receive();
            break;
        }
        case BSP_EVENT_SLEEP:
            nrf_pwr_mgmt_shutdown(NRF_PWR_MGMT_SHUTDOWN_GOTO_SYSOFF);
            break;

        case BSP_EVENT_DISCONNECT:
            err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
            if (err_code != NRF_ERROR_INVALID_STATE)
            {
                APP_ERROR_CHECK(err_code);
            }
            break;

        case BSP_EVENT_WHITELIST_OFF:
            whitelist_disable();
            break;

        default:
            break;
    }
}

Can you please give me some advice on what the problem could be and what the options are to make it work?

Idealy I would like to start scanning for BLE peripherals (with a Button 1 press) after I set the custom encryption key (with a Button 4 press and input via PuTTY). You can see I commented the button 1 event in my event handler code because even with a modified main() it didn't not work.

  • Hi,

    Using RTT for logging makes sense for logging if you need the UART for other tasks (also, there are two UART instances on the nRF52840 so you could use the second if needed, see for instance this thread).

    Regarding some buttons working, I see you use the BSP, and that will initialize all the buttons when you call bsp_init() with BSP_INIT_BUTTONS. So I wonder if you have checked by setting a breakpoint and verified that way that you don't get BSP_EVENT_KEY_3 for button 4 presses? Perhaps a subsequent issue happens that prevents the log from being output (more likely if you use deferred logging), and that makes you think the problem is with the button? This hypothesis would also be very relevant for your BSP_EVENT_KEY_0 where you enter an eternal loop going into idle. So you would never continue from this point. So while this is commented now it is clearly wrong, and cannot work (regardless of what the intention is).

  • I put a Breakpoint on the first line inside the code block of the case BSP_EVENT_KEY_3 and debugged the program again. When I pressed button 4, the debug didn't reach the breakpoint and the program didn't stop.

    When I debug the bsp_init() I can't find an obvious connection between the BSP_EVENT_KEY_3 constant (I found that one in bsp.h) and the backend functionality (interrupt?). How do I have to initialize the logger and the uart module so I can use both?

    I talked to my colleague about the problem with UART and from his point of view it could be a problem to use the physical USB MCU port for both UART (uart module) and RTT (logger module).

    In future we also want to port our firmware from the nRF52840 DK to the nRF52840 Dongle. We want to code the firmware in a way so it can work on the Dongle. We need to be able to set the encryption_key via uart on the dongle.

    My colleague also advised me to take a look at the examples\peripheral\usbd_ble_uart\pca10056\s140\ses example project. He said this example uses both physical USB ports on the nRF52840 DK. Can you confirm this? Also how can I work with the second USB port on the nRF52840 DK (It's labeled "nRF USB" in the "nRF52840_DK_User_Guide_v1.2.pdf" but there isn't any detailed information about it in the document)? What are the differences between both of these USB ports?

    The logger functionality is not super important (but it's nice to have) so I suggested to disable the logger module and only use the uart module as an experiment so see if that works. I will try that next.

  • When I disable the logger module, debug the program and press button 4, the program stops on my breakpoint and runs through this code afterwards:

    case BSP_EVENT_KEY_3:
            {
                NRF_LOG_INFO("Button 4 pressed.");
                NRF_LOG_INFO("Please enter a new encryption_key: ");
                uart_receive();
                break;
            }

    uart_receive() in main.c:

    static void uart_receive()
    {
        NRF_LOG_INFO(("\r\nUART started.\r\n"))
        //printf("\r\nUART started.\r\n");
    
        while (true)
        {
            uint8_t cr;
            while (app_uart_get(&cr) != NRF_SUCCESS);
            while (app_uart_put(cr) != NRF_SUCCESS);
            /*
            while (app_uart_get(&cr) == NRF_SUCCESS) {
              if ( !(cr == 'q' || cr == 'Q'))
              app_uart_put(cr);
            };
            */
    
            if (cr == 'q' || cr == 'Q')
            {
                NRF_LOG_INFO("\r\nExit UART.\r\n");
                //printf(" \r\nExit UART.\r\n");
                /*
                while (true)
                {
                    // Do nothing.
                }
                */
            }
        }
    }

    I am connected to COM7 via Putty with baud rate: 115200, 8 data bits, 1 stop bits, parity: none, flow control: none (same settings like in examples/peripheral/uart example solution) but I can not see/type anything in Putty.

  • I just realized I commented the printf() commands before. Now I uncommented them and I get build errors:

  • Ok... From the error-log I guessed that I have to set up retargeting in a correct way.

    Because of that I checked the retarget.c and sdk_config.h of the peripheral/uart example solution and copied these lines into the sdk_config.h of my own project:

    // <q> RETARGET_ENABLED  - retarget - Retargeting stdio functions
     
    
    #ifndef RETARGET_ENABLED
    #define RETARGET_ENABLED 0
    #endif
    
    // <h> nrf_fprintf - fprintf function.
    
    //==========================================================
    // <q> NRF_FPRINTF_ENABLED  - Enable/disable fprintf module.
     
    
    #ifndef NRF_FPRINTF_ENABLED
    #define NRF_FPRINTF_ENABLED 1
    #endif
    
    // <q> NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED  - For each printed LF, function will add CR.
     
    
    #ifndef NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED
    #define NRF_FPRINTF_FLAG_AUTOMATIC_CR_ON_LF_ENABLED 1
    #endif
    
    // <q> NRF_FPRINTF_DOUBLE_ENABLED  - Enable IEEE-754 double precision formatting.
     
    
    #ifndef NRF_FPRINTF_DOUBLE_ENABLED
    #define NRF_FPRINTF_DOUBLE_ENABLED 0
    #endif
    
    // </h> 
    //==========================================================

    Then I tried to rebuild the solution and still got linker errors. I did a bit more research and found this topic:

    https://devzone.nordicsemi.com/f/nordic-q-a/88123/build-environment-problem/370762

    From what I remember I recently had to install SEGGER Embedded Studio for ARM 5.42a to build the peripheral/uart example. I then tried to build my current project with SEGGER Embedded Studio for ARM 5.42a instead of 7.10.

    With that older version I was able to build my current project without errors. I will debug and continue on friday.

    Can you please provide some information on the questions I had in my initial answer post? Thank's in advance...

Related