This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Libuarte_async bug(s) - missing data / wrong buffer returned.

Hi,

We've been having problems trying to implement a high-speed uart communication between the nRF52832 and an STM32.

We switched to using libuarte so we can use DMA even though our communication protocol has variable-sized packets Unfortunately, after several seconds of working perfectly, we suddenly start seeing errors. In between good new data blocks, we suddenly have one block that has old data. So either the data was never written by DMA, or it was written somewhere other than we are told to read from. It was only for the amount of data that the NRF_LIBUARTE_ASYNC_EVT_RX_DATA reported. It seemed kinda like the timeout triggered before the data was actually copied by the DMA or something along those lines.

We've reduced the transmission speed to 115200, and the error still occurs. We've used a logic analyzer to check if the data is really being transferred - it definitely is, but the nordic is not getting it. We've tried it with SDK 15.0, 15.3 and 16.0, the problem remains.

Since our codebase is way too complex to try and post something helpful here, we tried recreating the issue with two nRF52840 DKs.

Unfortunately, we're not getting the same errors, but are getting another error in the first RX event. First, right after we initialize libuarte_async we get NRF_LIBUARTE_ASYNC_EVT_ERROR errorSrc 0 - this started with SDK 16.0, it wasn't occurring when using SDK 15.x with ported libuarte. Then we get a bunch of skipped bytes reported (check the attached project):

<info> app: Rx 128@x20004B84
<error> app: RX: expected x65, got x39 instead
<error> app: RX: expected x3A, got x3D instead
<error> app: RX: expected x3E, got x41 instead
<error> app: RX: expected x42, got x45 instead
<error> app: RX: expected x46, got x49 instead
<error> app: RX: expected x4A, got x4D instead

Note: We're using arm-none-eabi-gcc  v4.9.3 to build it the test project, targeting nrf52840 chip sitting on development kits. They are connected and both running the same firmware, both doing RX and TX using libuarte.

ble_app_libuarte_test.tar.gz

Parents
  • I am not aware of any issue, but using the latest is in general the recommended.

    My only input is that I would recommend to increase .timeout_us = 100 to 300 (or possible more for testing). Reasoning is that depending on other interrupts that may be occurring (causing the uart to be pending), you want to make sure that you can always handle the uart interrupt in time before the next may occur.

    "errorSrc 0" was very odd, maybe you can set a breakpoint on the error and double check value. Making sure RXD is always pulled up can be something to check.

    Best regards,
    Kenneth

  • Hi, We're using the latest SDK (16.0.0), but we first ported to 15.0 because that's what the project is using. I have also increased timeout_us to 500 for testing, but it didn't change a thing. We're still seeing a bunch of wrong incoming data, usually in the first RX event. I have confirmed that the other side is sending a continuous stream of bytes from 0x00 - 0x9A, then it resets and starts with 0x00 again. This doesn't make sense, since it reports values above 0x9A, which are not being sent over the line at all: 

    <warning> app: Boot
    <error> app: NRF_LIBUARTE_ASYNC_EVT_ERROR errorSrc 4
    <error> app: NRF_LIBUARTE_ASYNC_EVT_ERROR errorSrc 4
    <warning> app: Test TX started.
    <info> app: Debug logging for UART over RTT started.
    <error> app: NRF_LIBUARTE_ASYNC_EVT_ERROR errorSrc 4
    <info> app: Rx 128@x20004B84
    <error> app: RX: expected x65, got xAB instead
    <error> app: RX: expected x11, got xD5 instead
    <error> app: RX: expected x3B, got xE5 instead
    <error> app: RX: expected x4B, got xF5 instead
    <error> app: RX: expected x5B, got x85 instead
    <error> app: RX: expected x86, got x89 instead
    <error> app: RX: expected x8A, got x8D instead
    <error> app: RX: expected x8E, got x91 instead
    <error> app: RX: expected x92, got x95 instead
    <error> app: RX: expected x96, got x99 instead
    <error> app: RX: expected x9A, got x9D instead
    <error> app: RX: expected x03, got xA1 instead
    <error> app: RX: expected x07, got xA5 instead
    <error> app: RX: expected x0B, got xA9 instead
    <error> app: RX: expected x0F, got xAD instead
    <error> app: RX: expected x13, got xB1 instead
    <error> app: RX: expected x17, got xB5 instead
    <error> app: RX: expected x1B, got xB9 instead
    <error> app: RX: expected x1F, got xBD instead
    <error> app: RX: expected x23, got xC1 instead
    <error> app: RX: expected x27, got xC5 instead
    <error> app: RX: expected x2B, got xC9 instead
    <error> app: RX: expected x2F, got xCD instead
    <error> app: RX: expected x33, got xD1 instead
    <error> app: RX: expected x37, got xD5 instead
    <error> app: RX: expected x3B, got xD9 instead
    <error> app: RX: expected x3F, got xDD instead
    <error> app: RX: expected x43, got xE1 instead
    <error> app: RX: expected x47, got xE5 instead
    <error> app: RX: expected x4B, got xE9 instead
    <error> app: RX: expected x4F, got xED instead
    <error> app: RX: expected x53, got xF1 instead
    <error> app: RX: expected x57, got xF5 instead
    <error> app: RX: expected x5B, got xF9 instead
    <error> app: RX: expected x5F, got xFD instead
    <error> app: RX: expected x63, got x81 instead
    <error> app: Errors: 36 -> when got 128 @ x20004B84
    <info> app: Rx 128@x20004C04
    <info> app: Rx 128@x20004C84
    

    As for the errorSrc 0 thing, I found the error, not sure how I've missed it before: in libuarte_async.c in the handler for NRF_LIBUARTE_DRV_EVT_ERROR, you didn't actually copy the errorSrc. Basically, the .data portion is missing: 

            nrf_libuarte_async_evt_t evt = {
                .type = NRF_LIBUARTE_ASYNC_EVT_ERROR,
                .data = {
                    .errorsrc = p_evt->data.errorsrc
                }
            };

    Now that I've added that I get the errorsrc4 == framing error, which makes sense, since the other side is already sending while this side is initializing, meaning RX line is changing, and so we get framing errors (a few, right after initialization).

  • Can you check if starting the HFCLK may help? E.g. nrf_drv_clock_hfclk_request()/sd_clock_hfclk_request().

    Do you have a logic analyzer trace of the serial incomming data? I just want to check the size of the packets, the delay between packets/bytes and baud rate.

    Gasper said:
    As for the errorSrc 0 thing, I found the error, not sure how I've missed it before: in libuarte_async.c in the handler for NRF_LIBUARTE_DRV_EVT_ERROR, you didn't actually copy the errorSrc.

    Thanks, I will report it.

    Best regards,
    Kenneth 

  • Enabling hfclk doesn't change anything. 

    You can find a logic analyzer trace in the csv file: 8473.rx.tar.gz. I've truncated the trace at the beginning to where Rx started - the nrf_libuarte_async_enable call returned in the middle of the first 0x62 byte, which is probably the framing error reported at the beginning. The first full buffer is reported (NRF_LIBUARTE_ASYNC_EVT_RX_DATA) after ~2.826ms at timestamp 2.10230902. The log for this trace is:

    <warning> app: Boot
    <warning> app: Test TX started.
    <info> app: Debug logging for UART over RTT started.
    <error> app: NRF_LIBUARTE_ASYNC_EVT_ERROR errorSrc 4
    <info> app: Rx 128@x20004B84
    <error> app: RX: expected x65, got x89 instead
    <error> app: RX: expected x8A, got x8D instead
    <error> app: RX: expected x8E, got x91 instead
    <error> app: RX: expected x92, got x95 instead
    <error> app: RX: expected x96, got x99 instead
    <error> app: RX: expected x9A, got x9D instead
    <error> app: RX: expected x03, got xA1 instead
    <error> app: RX: expected x07, got xA5 instead
    <error> app: RX: expected x0B, got xA9 instead
    <error> app: RX: expected x0F, got xAD instead
    <error> app: RX: expected x13, got xB1 instead
    <error> app: RX: expected x17, got xB5 instead
    <error> app: RX: expected x1B, got xB9 instead
    <error> app: RX: expected x1F, got xBD instead
    <error> app: RX: expected x23, got xC1 instead
    <error> app: RX: expected x27, got xC5 instead
    <error> app: RX: expected x2B, got xC9 instead
    <error> app: RX: expected x2F, got xCD instead
    <error> app: RX: expected x33, got xD1 instead
    <error> app: RX: expected x37, got xD5 instead
    <error> app: RX: expected x3B, got xD9 instead
    <error> app: RX: expected x3F, got xDD instead
    <error> app: RX: expected x43, got xE1 instead
    <error> app: RX: expected x47, got xE5 instead
    <error> app: RX: expected x4B, got xE9 instead
    <error> app: RX: expected x4F, got xED instead
    <error> app: RX: expected x53, got xF1 instead
    <error> app: RX: expected x57, got xF5 instead
    <error> app: RX: expected x5B, got xF9 instead
    <error> app: RX: expected x5F, got xFD instead
    <error> app: RX: expected x63, got x81 instead
    <error> app: Errors: 31 -> when got 128 @ x20004B84
    <info> app: Rx 128@x20004C04
    

    Tx started roughly 20us after Rx. Spacing between packets is roughly 10us. Baudrate is set in fw to 460800. Tx packet size is 155 bytes (increasing from 0x00 to 0x9A), Rx buffer size is 128 and the size mismatch is intentional as in the original project we have variable packet lengths. Since this is a data stream (for our use case as well), we don't particularly care that packet end isn't detected in this case, but we do need it (and it works fine) for a different mode of operation in our core project.

  • Gasper said:
    You can find a logic analyzer trace in the csv file: 8473.rx.tar.gz.

    I do not see any errors in this trace?

    This doesn't really display what I am looking for. I thought maybe you could make a logic analyzer trace (e.g using https://www.saleae.com/) where you did a trace of the UART RX pin and toggled a IO on UART errors, this would make it easier to go to the where the issue occurred and look at the actual timing on a bit level for previous bytes (if you were able to also output on UART TX the expected value and received value, then we could really see on a bit level for the previous UART RX what may have happened). The .csv is not detailed enough for this. 

    How frequently does an error occur?

Reply
  • Gasper said:
    You can find a logic analyzer trace in the csv file: 8473.rx.tar.gz.

    I do not see any errors in this trace?

    This doesn't really display what I am looking for. I thought maybe you could make a logic analyzer trace (e.g using https://www.saleae.com/) where you did a trace of the UART RX pin and toggled a IO on UART errors, this would make it easier to go to the where the issue occurred and look at the actual timing on a bit level for previous bytes (if you were able to also output on UART TX the expected value and received value, then we could really see on a bit level for the previous UART RX what may have happened). The .csv is not detailed enough for this. 

    How frequently does an error occur?

Children
  • Sure, I can make the trace, although that will not help a thing. The low-level UART errors (framing) make sense, they only appear when the libuarte is being initialized/enabled, so a few of those at the beginning isn't the problem. The problem is the data itself, for which the errors are reported when the buffer fills out, at which point the data is checked and copied to a queue for ble transmission, and then the libuarte buffer is freed.

    Anyway, here is the trace, together with the test project that generated the trace: trace.tar.gz

    The project is tested on two nrf52840_DK boards, so you could test it as well, might be easier than this back and forth.

    On the other hand, increasing the idle timeout on our main project helped the issue, although I still feel it shouldn't be an issue in the first place, at least not like that. I would understand missing bytes if callback handling took to long (in the real project we don't parse the data in the callback handler, we just copy it to our main buffer queue).

  • Hi,

    I've tried your example and after tweaking it a bit i see no errors. I've made following changes:

    - commented out  printf("\r\nUART started.\r\n"); i didn't check if it is the case but if retarget goes to uart then it will collide with libuarte driving same uart.

    - moved initialization of libuarte after advertising init and adding 1second delay before start. This is to ensure that both boards are up and running before TX is started. If RX is started when TX is ongoing then there is no possibility to synchronize. RX is started in the middle of a byte and received data is garbled

    - changed initilization of last_received_byte to (TX_SIZE- 1) to ensure that first byte is not received as error.

    Source file attached.

    Krzysztof

    /**
     * Copyright (c) 2014 - 2019, Nordic Semiconductor ASA
     *
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without modification,
     * are permitted provided that the following conditions are met:
     *
     * 1. Redistributions of source code must retain the above copyright notice, this
     *    list of conditions and the following disclaimer.
     *
     * 2. Redistributions in binary form, except as embedded into a Nordic
     *    Semiconductor ASA integrated circuit in a product or a software update for
     *    such product, must reproduce the above copyright notice, this list of
     *    conditions and the following disclaimer in the documentation and/or other
     *    materials provided with the distribution.
     *
     * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
     *    contributors may be used to endorse or promote products derived from this
     *    software without specific prior written permission.
     *
     * 4. This software, with or without modification, must only be used with a
     *    Nordic Semiconductor ASA integrated circuit.
     *
     * 5. Any software provided in binary form under this license must not be reverse
     *    engineered, decompiled, modified and/or disassembled.
     *
     * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
     * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
     * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
     * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     *
     */
    /** @file
     *
     * @defgroup ble_sdk_uart_over_ble_main main.c
     * @{
     * @ingroup  ble_sdk_app_nus_eval
     * @brief    UART over BLE application main file.
     *
     * This file contains the source code for a sample application that uses the Nordic UART service.
     * This application uses the @ref srvlib_conn_params module.
     */
    
    
    #include <stdint.h>
    #include <string.h>
    #include "nordic_common.h"
    #include "nrf.h"
    #include "ble_hci.h"
    #include "ble_advdata.h"
    #include "ble_advertising.h"
    #include "ble_conn_params.h"
    #include "nrf_sdh.h"
    #include "nrf_sdh_soc.h"
    #include "nrf_sdh_ble.h"
    #include "nrf_ble_gatt.h"
    #include "nrf_ble_qwr.h"
    #include "app_timer.h"
    #include <bsp.h>
    #include "ble_nus.h"
    #include "app_util_platform.h"
    #include "nrf_gpio.h"
    #include "nrf_pwr_mgmt.h"
    
    #include "nrf_libuarte_async.h"
    #include "app_simple_timer.h"
    
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    #ifndef NRF_LOG_LEVEL
    #define NRF_LOG_LEVEL 4
    #endif
    
    #define APP_BLE_CONN_CFG_TAG            1                                           /**< A tag identifying the SoftDevice BLE configuration. */
    
    #define DEVICE_NAME                     "Nordic_UART"                               /**< Name of device. Will be included in the advertising data. */
    #define NUS_SERVICE_UUID_TYPE           BLE_UUID_TYPE_VENDOR_BEGIN                  /**< UUID type for the Nordic UART Service (vendor specific). */
    
    #define APP_BLE_OBSERVER_PRIO           3                                           /**< Application's BLE observer priority. You shouldn't need to modify this value. */
    
    #define APP_ADV_INTERVAL                64                                          /**< The advertising interval (in units of 0.625 ms. This value corresponds to 40 ms). */
    
    // #define APP_ADV_DURATION                BLE_GAP_ADV_TIMEOUT_GENERAL_UNLIMITED                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    #define APP_ADV_DURATION                18000                                       /**< The advertising duration (180 seconds) in units of 10 milliseconds. */
    
    #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(20, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
    #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(75, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */
    #define SLAVE_LATENCY                   0                                           /**< Slave latency. */
    #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)             /**< Connection supervisory timeout (4 seconds), Supervision Timeout uses 10 ms units. */
    #define FIRST_CONN_PARAMS_UPDATE_DELAY  APP_TIMER_TICKS(5000)                       /**< Time from initiating event (connect or start of notification) to first time sd_ble_gap_conn_param_update is called (5 seconds). */
    #define NEXT_CONN_PARAMS_UPDATE_DELAY   APP_TIMER_TICKS(30000)                      /**< Time between each call to sd_ble_gap_conn_param_update after the first call (30 seconds). */
    #define MAX_CONN_PARAMS_UPDATE_COUNT    3                                           /**< Number of attempts before giving up the connection parameter negotiation. */
    
    #define DEAD_BEEF                       0xDEADBEEF                                  /**< Value used as error code on stack dump, can be used to identify stack location on stack unwind. */
    
    #define RX_SIZE 128
    #define TX_SIZE 155
    
    #define GPIO_ERROR     NRF_GPIO_PIN_MAP(0,28)
    #define GPIO_RX     NRF_GPIO_PIN_MAP(0,29)
    
    typedef struct {
        uint8_t * const queue;  // constant pointer to normal data - pointer can't be changed once initialized
        uint32_t tail;
        uint32_t length;
        const uint32_t maxsize;      // constant maxsize - can't be changed once initialized
    } ringbuf_u8_t;
    
    #define RINGBUF_U8_DECLARE_INIT(__ringbuf_name__, __ringbuf_size__) \
            static uint8_t __ringbuf_name__ ## _queue[__ringbuf_size__];    \
            static ringbuf_u8_t __ringbuf_name__ = {                        \
                    .queue = __ringbuf_name__ ## _queue,                \
                    .maxsize = __ringbuf_size__,                        \
                    .tail = 0,                                          \
                    .length = 0};
    
    void ringbuf_u8_push(ringbuf_u8_t * buf, uint8_t val);
    uint8_t ringbuf_u8_peek(ringbuf_u8_t * buf);
    uint8_t ringbuf_u8_peek_last(ringbuf_u8_t * buf);
    uint8_t ringbuf_u8_pop(ringbuf_u8_t * buf);
    uint32_t ringbuf_u8_space_available(ringbuf_u8_t * buf);
    uint32_t ringbuf_u8_get_length(ringbuf_u8_t * buf);
    
    NRF_LIBUARTE_ASYNC_DEFINE(libuarte, 0, 2, NRF_LIBUARTE_PERIPHERAL_NOT_USED, 3, RX_SIZE, 8);
    /** uses 
        name: libuarte
        uart: UART0
        timer0: TIMER2
        rtc1: not used
        timer1: TIMER3
        buffer size: 128
        buffer number: 8
    */
    
    RINGBUF_U8_DECLARE_INIT(databuf, 4096);
    
    
    BLE_NUS_DEF(m_nus, NRF_SDH_BLE_TOTAL_LINK_COUNT);                                   /**< BLE NUS service instance. */
    NRF_BLE_GATT_DEF(m_gatt);                                                           /**< GATT module instance. */
    NRF_BLE_QWR_DEF(m_qwr);                                                             /**< Context for the Queued Write module.*/
    BLE_ADVERTISING_DEF(m_advertising);                                                 /**< Advertising module instance. */
    
    static uint16_t   m_conn_handle          = BLE_CONN_HANDLE_INVALID;                 /**< Handle of the current connection. */
    static uint16_t   m_ble_nus_max_data_len = BLE_GATT_ATT_MTU_DEFAULT - 3;            /**< Maximum length of data (in bytes) that can be transmitted to the peer by the Nordic UART service module. */
    static ble_uuid_t m_adv_uuids[]          =                                          /**< Universally unique service identifier. */
    {
        {BLE_UUID_NUS_SERVICE, NUS_SERVICE_UUID_TYPE}
    };
    
    
    #define TIMER_VALUE       1000  /**< 1 mseconds timer time-out value. - simple timer is set to 1Mhz */
    
    #define PRINT_COUNT 100*1024
    
    static uint8_t data[TX_SIZE];
    static uint8_t last_received_byte = TX_SIZE-1;
    static uint32_t rx_count = 0, tx_count = 0;
    static uint32_t last_print_rx_count = 0, last_print_tx_count = 0;
    
    void uart_event_handler(void * context, nrf_libuarte_async_evt_t * p_evt)
    {
        nrf_libuarte_async_t * p_libuarte = (nrf_libuarte_async_t *)context;
        ret_code_t ret;
        uint8_t got_error = 0;
    
        switch (p_evt->type)
        {
            case NRF_LIBUARTE_ASYNC_EVT_ERROR:
                nrf_gpio_pin_toggle(GPIO_ERROR);
                NRF_LOG_ERROR("NRF_LIBUARTE_ASYNC_EVT_ERROR errorSrc %x", p_evt->data.errorsrc);
                NRF_LOG_FLUSH();
                break;
            case NRF_LIBUARTE_ASYNC_EVT_RX_DATA:
                nrf_gpio_pin_toggle(GPIO_RX);
                NRF_LOG_INFO("Rx %d@x%x", p_evt->data.rxtx.length, p_evt->data.rxtx.p_data);
                for (uint32_t i = 0; i < p_evt->data.rxtx.length; i++) {
                    ringbuf_u8_push(&databuf, p_evt->data.rxtx.p_data[i]);
                    if (p_evt->data.rxtx.p_data[i] != ((last_received_byte+1)%TX_SIZE) ) {
                        NRF_LOG_ERROR("at %d exp: x%02x, got x%02x", i, ((last_received_byte+1)%TX_SIZE), p_evt->data.rxtx.p_data[i]);
                        got_error++;
                    }
                    last_received_byte = p_evt->data.rxtx.p_data[i];
                }
                if (got_error > 0) {
                    NRF_LOG_ERROR("Errors: %d -> when got %d @ x%X", got_error, p_evt->data.rxtx.length, p_evt->data.rxtx.p_data);
                }
                nrf_libuarte_async_rx_free(p_libuarte, p_evt->data.rxtx.p_data, p_evt->data.rxtx.length);
                rx_count += p_evt->data.rxtx.length;
    
                break;
            case NRF_LIBUARTE_ASYNC_EVT_TX_DONE:
                ret = nrf_libuarte_async_tx(&libuarte, data, TX_SIZE);
                APP_ERROR_CHECK(ret);
    
                tx_count += TX_SIZE;
    
                break;
            default:
                break;
        }
    }
    
    
    static void send_over_ble() {
        uint32_t ret = NRF_SUCCESS;
        uint16_t len = databuf.length;
        while (len > 0 && ret == NRF_SUCCESS) {
            // limit to max ble data payload
            if (len > 244) { 
                len = 244;
            }
    
             // limit till the end of the buf
            if (len + databuf.tail > databuf.maxsize) {
                len = databuf.maxsize - databuf.tail;
            }
    
            ret = ble_nus_data_send(&m_nus, &databuf.queue[databuf.tail], &len, m_conn_handle);
            if (ret == NRF_SUCCESS ||               // 0: ok
                ret == NRF_ERROR_INVALID_STATE ||   // 5: notif disabled
                ret == NRF_ERROR_NOT_FOUND) {       // 8: not connected
                databuf.tail = (databuf.tail + len) % databuf.maxsize;
                databuf.length -= len;
                // NRF_LOG_INFO("BLE Tx %d: x%x rem %d", len, ret, databuf.length);
    
                ret = NRF_SUCCESS;  // if disconnected or notif disabled, go over it again until buf is empty basically
            } else if (ret != NRF_ERROR_RESOURCES) {
                NRF_LOG_ERROR("ble_nus_data_send ret x%x", ret);
            }            
            len = databuf.length;
        } 
    }
    
    
    static void gpio_init() {
    
        // bsp_board_init(BSP_INIT_LEDS);
        // bsp_board_leds_off();
        nrf_gpio_cfg_output(GPIO_ERROR);
        nrf_gpio_cfg_output(GPIO_RX);
        nrf_gpio_pin_clear(GPIO_ERROR);
        nrf_gpio_pin_clear(GPIO_RX);
    }
    
    #include "nrf_delay.h"
    
    static void libuarte_init() {
        nrf_libuarte_async_config_t nrf_libuarte_async_config = {
            .tx_pin     = TX_PIN_NUMBER,
            .rx_pin     = RX_PIN_NUMBER,
           // .baudrate   = NRF_UARTE_BAUDRATE_115200,
            // .baudrate   = NRF_UARTE_BAUDRATE_230400,
            .baudrate   = NRF_UARTE_BAUDRATE_460800,
            // .baudrate   = NRF_UARTE_BAUDRATE_921600,
            // .baudrate   = NRF_UARTE_BAUDRATE_1000000,
            .parity     = NRF_UARTE_PARITY_EXCLUDED,
            .hwfc       = NRF_UARTE_HWFC_DISABLED,
            .timeout_us = 500,
            .int_prio   = APP_IRQ_PRIORITY_HIGH
        };
    
        uint32_t err_code = nrf_libuarte_async_init(&libuarte, &nrf_libuarte_async_config, uart_event_handler, (void *)&libuarte);
    
        APP_ERROR_CHECK(err_code);
    
        nrf_libuarte_async_enable(&libuarte);
    
        nrf_delay_ms(1000);
    
        nrf_gpio_pin_set(GPIO_RX);
    
    
        for (uint32_t i = 0; i<TX_SIZE; i++) {
            data[i] = i;
        }
    
        err_code = nrf_libuarte_async_tx(&libuarte, data, TX_SIZE);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_WARNING("Test TX started.");
        NRF_LOG_FLUSH();
    }
    
    static uint32_t timestamp = 0;
    
    void timeout_handler(void * p_context)
    {
        timestamp++;
        // I guess nothing to do, we don't really care
    }
    
    uint32_t timestamp_get(void)
    {
        return timestamp;
    }
    
    
    /**@brief Function for assert macro callback.
     *
     * @details This function will be called in case of an assert in the SoftDevice.
     *
     * @warning This handler is an example only and does not fit a final product. You need to analyse
     *          how your product is supposed to react in case of Assert.
     * @warning On assert from the SoftDevice, the system can only recover on reset.
     *
     * @param[in] line_num    Line number of the failing ASSERT call.
     * @param[in] p_file_name File name of the failing ASSERT call.
     */
    void assert_nrf_callback(uint16_t line_num, const uint8_t * p_file_name)
    {
        NRF_LOG_ERROR("Assert failed: %d:%s", line_num, p_file_name);
        app_error_handler(DEAD_BEEF, line_num, p_file_name);
    }
    
    
    void app_error_fault_handler(uint32_t id, uint32_t pc, uint32_t info) {
        error_info_t *e = (error_info_t *)info;
    
        NRF_LOG_ERROR("ERROR: 0x%x - %s:%d fatal: id 0x%x pc 0x%x\n", e->err_code, e->p_file_name, e->line_num, id, pc);
        NRF_LOG_FLUSH();
        NRF_LOG_FINAL_FLUSH();
    
        app_error_save_and_stop(id, pc, info);
    
        // NVIC_SystemReset();
    }
    
    /**@brief Function for initializing the timer module.
     */
    static void timers_init(void)
    {
        uint32_t err_code = app_simple_timer_init();
        APP_ERROR_CHECK(err_code);
    
        err_code = app_simple_timer_start(APP_SIMPLE_TIMER_MODE_REPEATED,
                                           timeout_handler,
                                           TIMER_VALUE,
                                           NULL);
        APP_ERROR_CHECK(err_code);
    
    
        err_code = app_timer_init();
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Function for the GAP initialization.
     *
     * @details This function will set up all the necessary GAP (Generic Access Profile) parameters of
     *          the device. It also sets the permissions and appearance.
     */
    static void gap_params_init(void)
    {
        uint32_t                err_code;
        ble_gap_conn_params_t   gap_conn_params;
        ble_gap_conn_sec_mode_t sec_mode;
    
        BLE_GAP_CONN_SEC_MODE_SET_OPEN(&sec_mode);
    
        err_code = sd_ble_gap_device_name_set(&sec_mode,
                                              (const uint8_t *) DEVICE_NAME,
                                              strlen(DEVICE_NAME));
        APP_ERROR_CHECK(err_code);
    
        memset(&gap_conn_params, 0, sizeof(gap_conn_params));
    
        gap_conn_params.min_conn_interval = MIN_CONN_INTERVAL;
        gap_conn_params.max_conn_interval = MAX_CONN_INTERVAL;
        gap_conn_params.slave_latency     = SLAVE_LATENCY;
        gap_conn_params.conn_sup_timeout  = CONN_SUP_TIMEOUT;
    
        err_code = sd_ble_gap_ppcp_set(&gap_conn_params);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling Queued Write Module errors.
     *
     * @details A pointer to this function will be passed to each service which may need to inform the
     *          application about an error.
     *
     * @param[in]   nrf_error   Error code containing information about what went wrong.
     */
    static void nrf_qwr_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for handling the data from the Nordic UART Service.
     *
     * @details This function will process the data received from the Nordic UART BLE Service and send
     *          it to the UART module.
     *
     * @param[in] p_evt       Nordic UART Service event.
     */
    /**@snippet [Handling the data received over BLE] */
    static void nus_data_handler(ble_nus_evt_t * p_evt)
    {
    
        if (p_evt->type == BLE_NUS_EVT_RX_DATA)
        {
            // uint32_t err_code;
    
            NRF_LOG_DEBUG("Received data from BLE NUS.");
            NRF_LOG_HEXDUMP_DEBUG(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
    
            // for (uint32_t i = 0; i < p_evt->params.rx_data.length; i++)
            // {
            //     do
            //     {
            //         err_code = app_uart_put(p_evt->params.rx_data.p_data[i]);
            //         if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_BUSY))
            //         {
            //             NRF_LOG_ERROR("Failed receiving NUS message. Error 0x%x. ", err_code);
            //             APP_ERROR_CHECK(err_code);
            //         }
            //     } while (err_code == NRF_ERROR_BUSY);
            // }
            // if (p_evt->params.rx_data.p_data[p_evt->params.rx_data.length - 1] == '\r')
            // {
            //     while (app_uart_put('\n') == NRF_ERROR_BUSY);
            // }
        }
    
    }
    /**@snippet [Handling the data received over BLE] */
    
    
    /**@brief Function for initializing services that will be used by the application.
     */
    static void services_init(void)
    {
        uint32_t           err_code;
        ble_nus_init_t     nus_init;
        nrf_ble_qwr_init_t qwr_init = {0};
    
        // Initialize Queued Write Module.
        qwr_init.error_handler = nrf_qwr_error_handler;
    
        err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
        APP_ERROR_CHECK(err_code);
    
        // Initialize NUS.
        memset(&nus_init, 0, sizeof(nus_init));
    
        nus_init.data_handler = nus_data_handler;
    
        err_code = ble_nus_init(&m_nus, &nus_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling an event from the Connection Parameters Module.
     *
     * @details This function will be called for all events in the Connection Parameters Module
     *          which are passed to the application.
     *
     * @note All this function does is to disconnect. This could have been done by simply setting
     *       the disconnect_on_fail config parameter, but instead we use the event handler
     *       mechanism to demonstrate its use.
     *
     * @param[in] p_evt  Event received from the Connection Parameters Module.
     */
    static void on_conn_params_evt(ble_conn_params_evt_t * p_evt)
    {
        uint32_t err_code;
    
        if (p_evt->evt_type == BLE_CONN_PARAMS_EVT_FAILED)
        {
            err_code = sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_CONN_INTERVAL_UNACCEPTABLE);
            APP_ERROR_CHECK(err_code);
        }
    }
    
    
    /**@brief Function for handling errors from the Connection Parameters module.
     *
     * @param[in] nrf_error  Error code containing information about what went wrong.
     */
    static void conn_params_error_handler(uint32_t nrf_error)
    {
        APP_ERROR_HANDLER(nrf_error);
    }
    
    
    /**@brief Function for initializing the Connection Parameters module.
     */
    static void conn_params_init(void)
    {
        uint32_t               err_code;
        ble_conn_params_init_t cp_init;
    
        memset(&cp_init, 0, sizeof(cp_init));
    
        cp_init.p_conn_params                  = NULL;
        cp_init.first_conn_params_update_delay = FIRST_CONN_PARAMS_UPDATE_DELAY;
        cp_init.next_conn_params_update_delay  = NEXT_CONN_PARAMS_UPDATE_DELAY;
        cp_init.max_conn_params_update_count   = MAX_CONN_PARAMS_UPDATE_COUNT;
        cp_init.start_on_notify_cccd_handle    = BLE_GATT_HANDLE_INVALID;
        cp_init.disconnect_on_fail             = false;
        cp_init.evt_handler                    = on_conn_params_evt;
        cp_init.error_handler                  = conn_params_error_handler;
    
        err_code = ble_conn_params_init(&cp_init);
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for putting the chip into sleep mode.
     *
     * @note This function will not return.
     */
    static void sleep_mode_enter(void)
    {
        // uint32_t err_code = bsp_indication_set(BSP_INDICATE_IDLE);
        // APP_ERROR_CHECK(err_code);
    
        // // Prepare wakeup buttons.
        // err_code = bsp_btn_ble_sleep_mode_prepare();
        // APP_ERROR_CHECK(err_code);
    
        // // Go to system-off mode (this function will not return; wakeup will cause a reset).
        // err_code = sd_power_system_off();
        // APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for handling advertising events.
     *
     * @details This function will be called for advertising events which are passed to the application.
     *
     * @param[in] ble_adv_evt  Advertising event.
     */
    static void on_adv_evt(ble_adv_evt_t ble_adv_evt)
    {
        uint32_t err_code;
    
        switch (ble_adv_evt)
        {
            case BLE_ADV_EVT_FAST:
                // err_code = bsp_indication_set(BSP_INDICATE_ADVERTISING);
                // APP_ERROR_CHECK(err_code);
                break;
            case BLE_ADV_EVT_IDLE:
                sleep_mode_enter();
                break;
            default:
                break;
        }
    }
    
    
    /**@brief Function for handling BLE events.
     *
     * @param[in]   p_ble_evt   Bluetooth stack event.
     * @param[in]   p_context   Unused.
     */
    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        uint32_t err_code;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
                NRF_LOG_INFO("Connected");
                // err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                // APP_ERROR_CHECK(err_code);
                m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;
                err_code = nrf_ble_qwr_conn_handle_assign(&m_qwr, m_conn_handle);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GAP_EVT_DISCONNECTED:
                NRF_LOG_INFO("Disconnected");
                // LED indication will be changed when advertising starts.
                m_conn_handle = BLE_CONN_HANDLE_INVALID;
                break;
    
            case BLE_GAP_EVT_PHY_UPDATE_REQUEST:
            {
                NRF_LOG_DEBUG("PHY update request.");
                ble_gap_phys_t const phys =
                {
                    .rx_phys = BLE_GAP_PHY_AUTO,
                    .tx_phys = BLE_GAP_PHY_AUTO,
                };
                err_code = sd_ble_gap_phy_update(p_ble_evt->evt.gap_evt.conn_handle, &phys);
                APP_ERROR_CHECK(err_code);
            } break;
    
            case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
                // Pairing not supported
                err_code = sd_ble_gap_sec_params_reply(m_conn_handle, BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPP, NULL, NULL);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_SYS_ATTR_MISSING:
                // No system attributes have been stored.
                err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0, 0);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTC_EVT_TIMEOUT:
                // Disconnect on GATT Client timeout event.
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gattc_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            case BLE_GATTS_EVT_TIMEOUT:
                // Disconnect on GATT Server timeout event.
                err_code = sd_ble_gap_disconnect(p_ble_evt->evt.gatts_evt.conn_handle,
                                                 BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                APP_ERROR_CHECK(err_code);
                break;
    
            default:
                // No implementation needed.
                break;
        }
    }
    
    
    /**@brief Function for the SoftDevice initialization.
     *
     * @details This function initializes the SoftDevice and the BLE event interrupt.
     */
    static void ble_stack_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_sdh_enable_request();
        APP_ERROR_CHECK(err_code);
    
        // Configure the BLE stack using the default settings.
        // Fetch the start address of the application RAM.
        uint32_t ram_start = 0;
        err_code = nrf_sdh_ble_default_cfg_set(APP_BLE_CONN_CFG_TAG, &ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Enable BLE stack.
        err_code = nrf_sdh_ble_enable(&ram_start);
        APP_ERROR_CHECK(err_code);
    
        // Register a handler for BLE events.
        NRF_SDH_BLE_OBSERVER(m_ble_observer, APP_BLE_OBSERVER_PRIO, ble_evt_handler, NULL);
    }
    
    
    /**@brief Function for handling events from the GATT library. */
    void gatt_evt_handler(nrf_ble_gatt_t * p_gatt, nrf_ble_gatt_evt_t const * p_evt)
    {
        if ((m_conn_handle == p_evt->conn_handle) && (p_evt->evt_id == NRF_BLE_GATT_EVT_ATT_MTU_UPDATED))
        {
            m_ble_nus_max_data_len = p_evt->params.att_mtu_effective - OPCODE_LENGTH - HANDLE_LENGTH;
            NRF_LOG_INFO("Data len is set to 0x%X(%d)", m_ble_nus_max_data_len, m_ble_nus_max_data_len);
        }
        NRF_LOG_DEBUG("ATT MTU exchange completed. central 0x%x peripheral 0x%x",
                      p_gatt->att_mtu_desired_central,
                      p_gatt->att_mtu_desired_periph);
    }
    
    
    /**@brief Function for initializing the GATT library. */
    void gatt_init(void)
    {
        ret_code_t err_code;
    
        err_code = nrf_ble_gatt_init(&m_gatt, gatt_evt_handler);
        APP_ERROR_CHECK(err_code);
    
        err_code = nrf_ble_gatt_att_mtu_periph_set(&m_gatt, NRF_SDH_BLE_GATT_MAX_MTU_SIZE);
        APP_ERROR_CHECK(err_code);
    }
    
    
    
    /**@brief Function for initializing the Advertising functionality.
     */
    static void advertising_init(void)
    {
        uint32_t               err_code;
        ble_advertising_init_t init;
    
        memset(&init, 0, sizeof(init));
    
        init.advdata.name_type          = BLE_ADVDATA_FULL_NAME;
        init.advdata.include_appearance = false;
        init.advdata.flags              = BLE_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE;
    
        init.srdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
        init.srdata.uuids_complete.p_uuids  = m_adv_uuids;
    
        init.config.ble_adv_fast_enabled  = true;
        init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
        init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
        init.evt_handler = on_adv_evt;
    
        err_code = ble_advertising_init(&m_advertising, &init);
        APP_ERROR_CHECK(err_code);
    
        ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
    }
    
    
    /**@brief Function for initializing the nrf log module.
     */
    static void log_init(void)
    {
        ret_code_t err_code = NRF_LOG_INIT(timestamp_get);
        APP_ERROR_CHECK(err_code);
    
        NRF_LOG_DEFAULT_BACKENDS_INIT();
    }
    
    
    /**@brief Function for initializing power management.
     */
    static void power_management_init(void)
    {
        ret_code_t err_code;
        err_code = nrf_pwr_mgmt_init();
        APP_ERROR_CHECK(err_code);
    }
    
    
    /**@brief Function for starting advertising.
     */
    static void advertising_start(void)
    {
        uint32_t err_code = ble_advertising_start(&m_advertising, BLE_ADV_MODE_FAST);
        APP_ERROR_CHECK(err_code);
    }
    
    /**@brief Application main function.
     */
    int main(void)
    {
        bool erase_bonds;
    
        // Initialize.
        sd_clock_hfclk_request();
        gpio_init();
        log_init();
        NRF_LOG_WARNING("Boot");
        timers_init();
        power_management_init();
        ble_stack_init();
        gap_params_init();
        gatt_init();
        services_init();
        advertising_init();
        conn_params_init();
    
        // Start execution.
       // printf("\r\nUART started.\r\n");
        NRF_LOG_INFO("Debug logging for UART over RTT started.");
        advertising_start();
    
        libuarte_init();
    
        uint32_t timestamp_rx = 0, timestamp_tx = 0;
    
        while (true)
        {
            if (tx_count >= last_print_tx_count + PRINT_COUNT) {
                NRF_LOG_INFO("Tx %dk bytes @ %dk/s, total %dk", 
                    PRINT_COUNT/1024, 
                    PRINT_COUNT / (timestamp - timestamp_tx), 
                    tx_count/1024);
                last_print_tx_count += PRINT_COUNT;
                timestamp_tx = timestamp;
            }
            if (rx_count >= last_print_rx_count + PRINT_COUNT) {
                NRF_LOG_INFO("Rx %dk bytes @ %dk/s, total %dk", 
                    PRINT_COUNT/1024, 
                    PRINT_COUNT / (timestamp - timestamp_rx), 
                    rx_count/1024);
                last_print_rx_count += PRINT_COUNT;
                timestamp_rx = timestamp;
            }
            send_over_ble();
            NRF_LOG_FLUSH();
        }
    }
    
    
    // helpers
    
    
    void ringbuf_u8_push(ringbuf_u8_t * buf, uint8_t val) {
        if (ringbuf_u8_space_available(buf) == 0) {
            uint8_t tmp = ringbuf_u8_pop(buf);
            // NRF_LOG_ERROR("buf full, popped x%02x, pushed x%02x\n", tmp, val);
        }
        uint32_t head = (buf->tail + buf->length) % buf->maxsize;
        buf->queue[head] = val;
        buf->length++;
    }
    
    uint8_t ringbuf_u8_peek(ringbuf_u8_t * buf) {
        return buf->queue[buf->tail];
    }
    
    uint8_t ringbuf_u8_peek_last(ringbuf_u8_t * buf) {
        uint8_t head = (buf->tail + buf->length) % buf->maxsize;
        return buf->queue[head];
    }
    
    uint8_t ringbuf_u8_pop(ringbuf_u8_t * buf) {
        if (buf->length == 0) {
            NRF_LOG_ERROR("ERROR: trying to pop a value out of empty buffer\n");
            return 0;
        }
        uint8_t val = buf->queue[buf->tail];
        buf->queue[buf->tail] = 0;
        buf->tail = (buf->tail + 1) % buf->maxsize;
        buf->length--;
        return val;
    }
    
    uint32_t ringbuf_u8_space_available(ringbuf_u8_t * buf) {
        return buf->maxsize - buf->length;
    }
    
    uint32_t ringbuf_u8_get_length(ringbuf_u8_t * buf) {
        return buf->length;
    }
    
    
    
    
    /**
     * @}
     */
    

Related