This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Reliability of the BLE UART example

In my application I need a reliable BLE link between peripheral (in this case based on a nRF52840) and a PC / Laptop. I need to stream data at a rate of 200kbps with a very high reliability. i.e loss of more than 0.0001% of the data is unacceptable.

My data is divided into 236 byte packets. 4 bytes sequence number, counter, 228 bytes data, and 4 bytes CRC.

The CRC is used to check consistency of the data, and the sequence number to check for missing data.

I've read other forum posts and it seems that the BLE UART example is the most applicable to my case.

I downloaded nRF5_SDK_17.1.0_ddde560 and Segger v5.68 (Nordic edition).

I have a nRF52840-DK (PCA10056)

The setup looks roughly like this:

PC->UART->nRF52840-DK->BLE->PC

The RF conditions are ideal. The DK and the PC are about 30cm apart, and there are not many other BT devices in the vicinity

I made the following changes to the UART example (nRF5_SDK_17.1.0_ddde560\examples\ble_peripheral\ble_app_uart\main.c):

diff --git a/ble_peripheral/ble_app_uart/main.c b/ble_peripheral/ble_app_uart/main.c
index 36d1a82..54ddf49 100644
--- a/ble_peripheral/ble_app_uart/main.c
+++ b/ble_peripheral/ble_app_uart/main.c
@@ -91,8 +91,8 @@

 #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 MIN_CONN_INTERVAL               MSEC_TO_UNITS(8, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */
+#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(8, 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). */
@@ -583,10 +583,10 @@ static void uart_init(void)
         .tx_pin_no    = TX_PIN_NUMBER,
         .rts_pin_no   = RTS_PIN_NUMBER,
         .cts_pin_no   = CTS_PIN_NUMBER,
-        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
+        .flow_control = APP_UART_FLOW_CONTROL_ENABLED,
         .use_parity   = false,
 #if defined (UART_PRESENT)
-        .baud_rate    = NRF_UART_BAUDRATE_115200
+        .baud_rate    = NRF_UART_BAUDRATE_230400
 #else
         .baud_rate    = NRF_UARTE_BAUDRATE_115200
 #endif

I wrote a Python script to generate test traffic in the above format and send it on a COM port:

import serial
import threading
import sys
import time
import binascii
import os


class UartSender:
    def __init__(self, com_port, baud=115200, rtscts=True, test_size=228, test_string='data', inter_packet_gap_ms=1000):
        self.uart = serial.Serial(com_port, baud, rtscts=rtscts) #115200 or 460800, 921600
        self.uart_sending_thread = None
        self.running = False
        self.packet_counter = 0 #Receiver can keep track of packet counter
        self.data_array = bytearray()
        self.ipg = inter_packet_gap_ms / 1000.0 #Python is likely inaccurate below 1000ms

        #contruct test data
        data = bytearray(test_string, 'utf-8')
        for i in range(0, int(test_size/len(data))):
            self.data_array.extend(data)
        print(len(self.data_array))

    @staticmethod
    def still_running_checker(caller):
        return caller.running

    @staticmethod
    def thread_send(uart, still_running_checker, caller):
        while still_running_checker(caller):
            send_array = bytearray(caller.packet_counter.to_bytes(4, byteorder = 'little'))
            send_array.extend(caller.data_array)
            send_array.extend(bytearray(binascii.crc32(send_array).to_bytes(4, byteorder='little')))
            send_array.extend(bytearray('\n', 'utf-8'))
            uart.write(send_array) #Should block until all bytes are sent
            caller.packet_counter += 1
            time.sleep(caller.ipg)

    def start_sending(self):
        self.uart_sending_thread = threading.Thread(target=self.thread_send, args=(self.uart, self.still_running_checker,
                                                                                   self))
        self.running = True
        self.uart_sending_thread.start()

    def stop_sending(self):
        if self.running:
            self.running = False
            self.uart_sending_thread.join(2)

    def disconnect(self):
        self.stop_sending()
        self.uart.close()

def main(args):
    uart = UartSender("COM45", baud=230400, rtscts=False, inter_packet_gap_ms=8)
    uart.start_sending()
    if __name__ == '__main__':
        try:
            time.sleep(3*60*60)
        except KeyboardInterrupt:
            print("exiting on KeyboardInterrupt")
            uart.disconnect()
            try:
                sys.exit(0)
            except SystemExit:
                os._exit(0)


    uart.stop_sending()

if __name__=="__main__":
    main(sys.argv)

This then goes to the nRF and the nRF sends it over BLE back to the PC

On the PC there is another Python script that uses the Bleak library to listen on the TX Characteristic (UUID: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E) for notifications. Once a notification is received, the data is parsed to extract the sequence number and CRC. The CRC is also calculated again over the data. See function notification_received in the following code:

import asyncio
import binascii
from bleak import BleakClient
from bleak import BleakScanner
import time
import threading

detected_peripherals = {}

RX_CHAR = '6E400002-B5A3-F393-E0A9-E50E24DCCA9E'
TX_CHAR = '6E400003-B5A3-F393-E0A9-E50E24DCCA9E'

class BLEReceiver:
    def __init__(self, logger):
        self.client = None
        self.loop = asyncio.get_event_loop()
        self.logger = logger
        self.running = False
        self.ble_notif_thread = None
        self.prev_count = 0
        self.lost_msgs = 0


    def connect(self, index):
        if self.client is None:
            try:
                key_list = list(detected_peripherals)
                device = detected_peripherals[key_list[index]]
                self.logger.log(20, "creating bleak client\n")
                self.client = BleakClient(device)
                self.logger.log(20, "connecting to device {}\n".format(str(device)))
                self.loop.run_until_complete(connect(self.client))

                while not self.client.is_connected:
                    asyncio.sleep(0.1)
                self.client.set_disconnected_callback(self.on_disconnect)
                self.logger.log(20, "connected...!\n")

                self.ble_notif_thread = threading.Thread(target=self.thread_proc, args=(self.notification_received,
                                                                                        self.still_running_checker,
                                                                                        self))
                self.running = True
                self.prev_count = 0
                self.ble_notif_thread.start()
                return True
            except Exception as e:
                raise e
                #self.client = None
                #return False

    def parse_rx_data(self, bytes):
        offset = 0
        ret = {}
        crc = binascii.crc32(bytes[0:len(bytes)-5]) #Calculate the CRC, skip last 5 bytes as they are the CRC and '\n'
        offset, ret['CNT'] = extract_word_32(bytes, offset) #The count (sequence number)
        offset, ret['DATA'] = extract_data(bytes, 2, offset, offset+228)#extract data
        offset, ret['CRC'] = extract_word_32(bytes, offset)#Extract CRC

        return ret, crc == ret['CRC'] #Return data and CRC result

    def notification_received(self, sender, data):
        ret, crc_check = self.parse_rx_data(data)
        
        if ret['CNT'] > self.prev_count + 1: #Check for a missing sequence number (aka 'count')
            self.logger.log(40, 'Missing '+ str(self.prev_count) + '\n')
            if (self.prev_count + 1) % 100 == 0: #If a multiple of 100 is missing, reset the lost message count
                self.lost_msgs = 0
            self.lost_msgs += 1
        self.prev_count = ret['CNT']
        
        if not crc_check: #Log a CRC failure
            self.logger.log(40, 'CRC FAIL ' + str(ret['CNT']) + '\n')
            self.lost_msgs += 1
            
        if ret['CNT'] % 100 == 0: #On every 100th packet, print the loss percentage
            self.logger.log(40, 'Received '+ str(ret['CNT']) + ' messages. Loss percentage: ' + str(self.lost_msgs) + '%\n')
            self.lost_msgs = 0

    @staticmethod
    def still_running_checker(caller):
        return caller.running

    @staticmethod
    def thread_proc(notification_callback, still_running_checker, caller):
        try:
            while still_running_checker(caller):
                caller.loop.run_until_complete(enableNotification(caller.client, TX_CHAR, notification_callback))

                if still_running_checker(caller):
                    caller.loop.run_until_complete(asyncio.sleep(1))
                else:
                    caller.logger.log(40, "stopping notifications thread!\n")
                    caller.loop.run_until_complete(disableNotification(caller.client, TX_CHAR))
        except Exception as e:
            caller.logger.log(40, "notifications thread terminated!\n {}\n".format(e))

    def on_disconnect(self, arg):
        self.logger.log(40, "ble stack disconnected!\n")
        self.running = False
        self.client = None

    def disconnect(self):
        if self.running:
            self.logger.log(40, 'disable notification....!\n')
            self.running = False
            # if self.ble_notif_thread != None:
            self.ble_notif_thread.join(2)
            self.logger.log(40, '...notification disabled!\n')
            self.logger.log(40, 'disconnect initiated...!\n')
            self.loop.run_until_complete(disconnect(self.client))
            self.logger.log(40, '...disconnect completed!\n')
            self.client = None

The problem is that about 5-10% of the traffic is lost. Either it is a missing sequence number (i.e. data did not arrive in the first place) or a CRC failure which seems to be due to a couple of bytes missing.

How can I get a reliable data transfer from peripheral to PC?

Parents
  • I made the following changes to the example and SDK to change the TX characteristic from notify to indicate:

    UART example:

    diff --git a/ble_peripheral/ble_app_uart/main.c b/ble_peripheral/ble_app_uart/main.c
    index 36d1a82..e60d938 100644
    --- a/ble_peripheral/ble_app_uart/main.c
    +++ b/ble_peripheral/ble_app_uart/main.c
    @@ -91,8 +91,8 @@
    
     #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 MIN_CONN_INTERVAL               MSEC_TO_UNITS(8, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */^M
    +#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(8, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */^M
     #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). */
    @@ -412,6 +412,10 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
                                                  BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                 APP_ERROR_CHECK(err_code);
                 break;
    +        case BLE_GATTS_EVT_HVC:^M
    +            //NRF_LOG_INFO("Handle Value Confirmation.");^M
    +            APP_ERROR_CHECK(err_code);^M
    +            break;^M
    
             default:
                 // No implementation needed.
    @@ -545,11 +549,12 @@ void uart_event_handle(app_uart_evt_t * p_event)
                             err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                             if ((err_code != NRF_ERROR_INVALID_STATE) &&
                                 (err_code != NRF_ERROR_RESOURCES) &&
    -                            (err_code != NRF_ERROR_NOT_FOUND))
    +                            (err_code != NRF_ERROR_NOT_FOUND) &&^M
    +                            (err_code != NRF_ERROR_BUSY))^M
                             {
                                 APP_ERROR_CHECK(err_code);
                             }
    -                    } while (err_code == NRF_ERROR_RESOURCES);
    +                    } while ((err_code == NRF_ERROR_RESOURCES) || (err_code == NRF_ERROR_BUSY));^M
                     }
    
                     index = 0;
    @@ -583,10 +588,10 @@ static void uart_init(void)
             .tx_pin_no    = TX_PIN_NUMBER,
             .rts_pin_no   = RTS_PIN_NUMBER,
             .cts_pin_no   = CTS_PIN_NUMBER,
    -        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
    +        .flow_control = APP_UART_FLOW_CONTROL_ENABLED,^M
             .use_parity   = false,
     #if defined (UART_PRESENT)
    -        .baud_rate    = NRF_UART_BAUDRATE_115200
    +        .baud_rate    = NRF_UARTE_BAUDRATE_230400^M
     #else
             .baud_rate    = NRF_UARTE_BAUDRATE_115200
     #endif
    

    SDK:

    diff --git a/ble/ble_services/ble_nus/ble_nus.c b/ble/ble_services/ble_nus/ble_nus.c
    index ca77de6..c629401 100644
    --- a/ble/ble_services/ble_nus/ble_nus.c
    +++ b/ble/ble_services/ble_nus/ble_nus.c
    @@ -98,11 +98,13 @@ static void on_connect(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
     
         if ((err_code == NRF_SUCCESS)     &&
             (p_nus->data_handler != NULL) &&
    -        ble_srv_is_notification_enabled(gatts_val.p_value))
    +        /*ble_srv_is_notification_enabled(gatts_val.p_value))*/
    +        ble_srv_is_indication_enabled(gatts_val.p_value))
         {
             if (p_client != NULL)
             {
    -            p_client->is_notification_enabled = true;
    +            //p_client->is_notification_enabled = true;
    +            p_client->is_indication_enabled = true;
             }
     
             memset(&evt, 0, sizeof(ble_nus_evt_t));
    @@ -147,9 +149,11 @@ static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
         {
             if (p_client != NULL)
             {
    -            if (ble_srv_is_notification_enabled(p_evt_write->data))
    +            /*if (ble_srv_is_notification_enabled(p_evt_write->data))*/
    +            if (ble_srv_is_indication_enabled(p_evt_write->data))
                 {
    -                p_client->is_notification_enabled = true;
    +                //p_client->is_notification_enabled = true;
    +                p_client->is_indication_enabled = true;
                     evt.type                          = BLE_NUS_EVT_COMM_STARTED;
                 }
                 else
    @@ -300,7 +304,8 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
         add_char_params.max_len           = BLE_NUS_MAX_TX_CHAR_LEN;
         add_char_params.init_len          = sizeof(uint8_t);
         add_char_params.is_var_len        = true;
    -    add_char_params.char_props.notify = 1;
    +    //add_char_params.char_props.notify = 1;
    +    add_char_params.char_props.indicate = 1;
     
         add_char_params.read_access       = SEC_OPEN;
         add_char_params.write_access      = SEC_OPEN;
    @@ -330,7 +335,8 @@ uint32_t ble_nus_data_send(ble_nus_t * p_nus,
             return NRF_ERROR_NOT_FOUND;
         }
     
    -    if (!p_client->is_notification_enabled)
    +    //if (!p_client->is_notification_enabled)
    +    if (!p_client->is_indication_enabled)
         {
             return NRF_ERROR_INVALID_STATE;
         }
    @@ -345,7 +351,8 @@ uint32_t ble_nus_data_send(ble_nus_t * p_nus,
         hvx_params.handle = p_nus->tx_handles.value_handle;
         hvx_params.p_data = p_data;
         hvx_params.p_len  = p_length;
    -    hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    +    //hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    +    hvx_params.type   = BLE_GATT_HVX_INDICATION;
     
         return sd_ble_gatts_hvx(conn_handle, &hvx_params);
     }
    diff --git a/ble/ble_services/ble_nus/ble_nus.h b/ble/ble_services/ble_nus/ble_nus.h
    index 8c5e977..62b4dbe 100644
    --- a/ble/ble_services/ble_nus/ble_nus.h
    +++ b/ble/ble_services/ble_nus/ble_nus.h
    @@ -140,6 +140,7 @@ typedef struct
     typedef struct
     {
         bool is_notification_enabled; /**< Variable to indicate if the peer has enabled notification of the RX characteristic.*/
    +    bool is_indication_enabled;
     } ble_nus_client_context_t;
     
     
    

    Credit:

    https://devzone.nordicsemi.com/f/nordic-q-a/37951/how-to-change-the-characteristic-property-from-notify-to-indicate

    However the results do not improve dramatically.

    I think the problem is that the characteristic is sometimes overwritten by the peripheral before the python script can read it, and therefore it seems that the data is lost.

    The only way I can think of is to implement another characteristic that can function as an ACK channel where the laptop can acknowledge receipt of a packet. Only when the peripheral receives the ACK does it write new data to the TX characteristic

Reply
  • I made the following changes to the example and SDK to change the TX characteristic from notify to indicate:

    UART example:

    diff --git a/ble_peripheral/ble_app_uart/main.c b/ble_peripheral/ble_app_uart/main.c
    index 36d1a82..e60d938 100644
    --- a/ble_peripheral/ble_app_uart/main.c
    +++ b/ble_peripheral/ble_app_uart/main.c
    @@ -91,8 +91,8 @@
    
     #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 MIN_CONN_INTERVAL               MSEC_TO_UNITS(8, UNIT_1_25_MS)             /**< Minimum acceptable connection interval (20 ms), Connection interval uses 1.25 ms units. */^M
    +#define MAX_CONN_INTERVAL               MSEC_TO_UNITS(8, UNIT_1_25_MS)             /**< Maximum acceptable connection interval (75 ms), Connection interval uses 1.25 ms units. */^M
     #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). */
    @@ -412,6 +412,10 @@ static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
                                                  BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
                 APP_ERROR_CHECK(err_code);
                 break;
    +        case BLE_GATTS_EVT_HVC:^M
    +            //NRF_LOG_INFO("Handle Value Confirmation.");^M
    +            APP_ERROR_CHECK(err_code);^M
    +            break;^M
    
             default:
                 // No implementation needed.
    @@ -545,11 +549,12 @@ void uart_event_handle(app_uart_evt_t * p_event)
                             err_code = ble_nus_data_send(&m_nus, data_array, &length, m_conn_handle);
                             if ((err_code != NRF_ERROR_INVALID_STATE) &&
                                 (err_code != NRF_ERROR_RESOURCES) &&
    -                            (err_code != NRF_ERROR_NOT_FOUND))
    +                            (err_code != NRF_ERROR_NOT_FOUND) &&^M
    +                            (err_code != NRF_ERROR_BUSY))^M
                             {
                                 APP_ERROR_CHECK(err_code);
                             }
    -                    } while (err_code == NRF_ERROR_RESOURCES);
    +                    } while ((err_code == NRF_ERROR_RESOURCES) || (err_code == NRF_ERROR_BUSY));^M
                     }
    
                     index = 0;
    @@ -583,10 +588,10 @@ static void uart_init(void)
             .tx_pin_no    = TX_PIN_NUMBER,
             .rts_pin_no   = RTS_PIN_NUMBER,
             .cts_pin_no   = CTS_PIN_NUMBER,
    -        .flow_control = APP_UART_FLOW_CONTROL_DISABLED,
    +        .flow_control = APP_UART_FLOW_CONTROL_ENABLED,^M
             .use_parity   = false,
     #if defined (UART_PRESENT)
    -        .baud_rate    = NRF_UART_BAUDRATE_115200
    +        .baud_rate    = NRF_UARTE_BAUDRATE_230400^M
     #else
             .baud_rate    = NRF_UARTE_BAUDRATE_115200
     #endif
    

    SDK:

    diff --git a/ble/ble_services/ble_nus/ble_nus.c b/ble/ble_services/ble_nus/ble_nus.c
    index ca77de6..c629401 100644
    --- a/ble/ble_services/ble_nus/ble_nus.c
    +++ b/ble/ble_services/ble_nus/ble_nus.c
    @@ -98,11 +98,13 @@ static void on_connect(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
     
         if ((err_code == NRF_SUCCESS)     &&
             (p_nus->data_handler != NULL) &&
    -        ble_srv_is_notification_enabled(gatts_val.p_value))
    +        /*ble_srv_is_notification_enabled(gatts_val.p_value))*/
    +        ble_srv_is_indication_enabled(gatts_val.p_value))
         {
             if (p_client != NULL)
             {
    -            p_client->is_notification_enabled = true;
    +            //p_client->is_notification_enabled = true;
    +            p_client->is_indication_enabled = true;
             }
     
             memset(&evt, 0, sizeof(ble_nus_evt_t));
    @@ -147,9 +149,11 @@ static void on_write(ble_nus_t * p_nus, ble_evt_t const * p_ble_evt)
         {
             if (p_client != NULL)
             {
    -            if (ble_srv_is_notification_enabled(p_evt_write->data))
    +            /*if (ble_srv_is_notification_enabled(p_evt_write->data))*/
    +            if (ble_srv_is_indication_enabled(p_evt_write->data))
                 {
    -                p_client->is_notification_enabled = true;
    +                //p_client->is_notification_enabled = true;
    +                p_client->is_indication_enabled = true;
                     evt.type                          = BLE_NUS_EVT_COMM_STARTED;
                 }
                 else
    @@ -300,7 +304,8 @@ uint32_t ble_nus_init(ble_nus_t * p_nus, ble_nus_init_t const * p_nus_init)
         add_char_params.max_len           = BLE_NUS_MAX_TX_CHAR_LEN;
         add_char_params.init_len          = sizeof(uint8_t);
         add_char_params.is_var_len        = true;
    -    add_char_params.char_props.notify = 1;
    +    //add_char_params.char_props.notify = 1;
    +    add_char_params.char_props.indicate = 1;
     
         add_char_params.read_access       = SEC_OPEN;
         add_char_params.write_access      = SEC_OPEN;
    @@ -330,7 +335,8 @@ uint32_t ble_nus_data_send(ble_nus_t * p_nus,
             return NRF_ERROR_NOT_FOUND;
         }
     
    -    if (!p_client->is_notification_enabled)
    +    //if (!p_client->is_notification_enabled)
    +    if (!p_client->is_indication_enabled)
         {
             return NRF_ERROR_INVALID_STATE;
         }
    @@ -345,7 +351,8 @@ uint32_t ble_nus_data_send(ble_nus_t * p_nus,
         hvx_params.handle = p_nus->tx_handles.value_handle;
         hvx_params.p_data = p_data;
         hvx_params.p_len  = p_length;
    -    hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    +    //hvx_params.type   = BLE_GATT_HVX_NOTIFICATION;
    +    hvx_params.type   = BLE_GATT_HVX_INDICATION;
     
         return sd_ble_gatts_hvx(conn_handle, &hvx_params);
     }
    diff --git a/ble/ble_services/ble_nus/ble_nus.h b/ble/ble_services/ble_nus/ble_nus.h
    index 8c5e977..62b4dbe 100644
    --- a/ble/ble_services/ble_nus/ble_nus.h
    +++ b/ble/ble_services/ble_nus/ble_nus.h
    @@ -140,6 +140,7 @@ typedef struct
     typedef struct
     {
         bool is_notification_enabled; /**< Variable to indicate if the peer has enabled notification of the RX characteristic.*/
    +    bool is_indication_enabled;
     } ble_nus_client_context_t;
     
     
    

    Credit:

    https://devzone.nordicsemi.com/f/nordic-q-a/37951/how-to-change-the-characteristic-property-from-notify-to-indicate

    However the results do not improve dramatically.

    I think the problem is that the characteristic is sometimes overwritten by the peripheral before the python script can read it, and therefore it seems that the data is lost.

    The only way I can think of is to implement another characteristic that can function as an ACK channel where the laptop can acknowledge receipt of a packet. Only when the peripheral receives the ACK does it write new data to the TX characteristic

Children
No Data
Related