Packet retransmission at client & loss at server

Hello,

I am a student currently studying Bluetooth Low Energy (BLE).

I am working with two Adafruit nRF52832 boards (Adafruit Feather nRF52 Bluefruit LE - nRF52832, link to product: www.adafruit.com/.../3406), setting up a 1-to-1 connection as a server and client. Currently, I'm using the "write without response" method with a 10ms connection interval and a 5ms event length. I have also set the TxPower to 4 and the data rate to 2Mbps. (Arduino Environment)

In this setup, the client writes a packet to the server every 10ms and the packet size is 8 bytes (two float values), with first element containing the time of transmission and the second element containing packet count sent by the client.

I monitored the values written by the client every 10ms and observed what the server receives. I noticed a delay in the packets on the client side, likely due to retransmission. Given the 10ms connection interval, new packets should ideally be sent every 10ms. However, the packet numbers at t+10ms and t+20ms are sometimes identical, indicating a delay in transmission. (Each sample row in my picture represents a 10ms interval)

SERVER

On the server side, I observed that packets were occasionally duplicated or lost. The duplication happens when the server reads the same packet value at both t+10ms and t+20ms, likely due to retransmission. (Each sample row in my picture represents a 10ms interval)

CLIENT

Additionally, packet loss occurs when the expected consecutive packet numbers do not arrive at the server within the expected intervals. (Each sample row in my picture represents a 10ms interval)

CLIENT

Is packet retransmission, delay, or loss a common issue in BLE communication, or could there be something incorrect in my setup? I would greatly appreciate any advice on this matter.
My code is included below.

Thank you in advance for your guidance.

CLINET CODE

#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.h>
#include <Wire.h>
#include <utility/imumaths.h>

#define dataNum 2 // timestampe 포함
#define dataSize dataNum*4 // default: 20, maximum: 224
#define packetTime 8 // unit: 1.25ms
#define SLAVE_ADDRESS 0x61  // I2C 슬레이브 주소

BLEClientService        hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);

float packetCount = 0;
uint8_t buffer[dataNum * sizeof(float)];

void setup()
{
  Serial.begin(115200);
  while ( !Serial ) delay(10);   // for nrf52840 with native usb

  Serial.println("Bluefruit52 Peripheral Server");
  Serial.println("--------------------------------------\n");

  Serial.println("Initialize the Bluefruit nRF52 module");
  // Change MTU size
  uint16_t requestedMtu = dataSize;
  Bluefruit.configCentralConn(requestedMtu+3, 4, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); // BLE_GAP_EVENT_LENGTH_DEFAULT = 3
  
  Bluefruit.begin(0, 1); // maximum connections as Peripheral = 0, Central = 1
  Bluefruit.setTxPower(4);
  Bluefruit.setName("Bluefruit52 Central");

  // Initialize HRM client
  hrms.begin();
  hrmc.begin();
  
  // Increase Blink rate to different from PrPh advertising mode
  Bluefruit.setConnLedInterval(250);
  
  // Callbacks for Central
  Bluefruit.Central.setConnInterval(packetTime, packetTime);
  Bluefruit.Central.setDisconnectCallback(disconnect_callback);
  Bluefruit.Central.setConnectCallback(connect_callback);

  /* Start Central Scanning
   * - Enable auto scan if disconnected
   * - Interval = 160 ms, window = 80 ms
   * - Don't use active scan
   * - Filter only accept HRM service
   * - Start(timeout) with timeout = 0 will scan forever (until connected)
   */
  Bluefruit.Scanner.setRxCallback(scan_callback);
  Bluefruit.Scanner.restartOnDisconnect(true);
  Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
  Bluefruit.Scanner.filterUuid(hrms.uuid);
  Bluefruit.Scanner.useActiveScan(false);
  Bluefruit.Scanner.start(0);                   // // 0 = Don't stop scanning after n seconds

  /*************************I2C***************************/
  Wire.setPins(25, 26);  // SCL = 25, SDA = 26 핀 설정
  Wire.begin(SLAVE_ADDRESS);  // 슬레이브 모드로 I2C 초기화
  Wire.onRequest(requestEvent);  // 마스터의 읽기 요청이 들어올 때 호출될 콜백 설정
  Wire.setClock(400000);  // Set I2C speed 나중에 필요하면 사용
}

/**************************************************************************/
void readTimedata(float (&timeData)[dataNum]) {
  sensors_event_t accelerometerData;  // 선언된 변수 이름 수정
  timeData[0] = millis();              // timestamp를 imuData의 첫 번째 요소에 저장
  packetCount++;
  memcpy(buffer, timeData, sizeof(buffer));
  timeData[1] = packetCount;
  hrmc.write((uint8_t*)timeData, sizeof(timeData));
}

/**************************************************************************/
void loop() {
  float timeData[dataNum];

  if (Bluefruit.connected()) {
    readTimedata(timeData);
    // Serial.println(imuData[1]);
  }
}

/**********************************callback*********************************/
/**
 * Callback invoked when scanner pick up an advertising data
 * @param report Structural advertising data
 */
void scan_callback(ble_gap_evt_adv_report_t* report)
{
  // Since we configure the scanner with filterUuid()
  // Scan callback only invoked for device with hrm service advertised
  // Connect to device with HRM service in advertising
  Bluefruit.Central.connect(report);
}

/**
 * Callback invoked when an connection is established
 * @param conn_handle
 */
void connect_callback(uint16_t conn_handle)
{
  Serial.println("Connected");
  Serial.print("Discovering Service ... ");

  // If HRM is not found, disconnect and return
  if ( !hrms.discover(conn_handle) )
  {
    Serial.println("Found NONE");

    // disconnect since we couldn't find HRM service
    Bluefruit.disconnect(conn_handle);

    return;
  }

  // Once HRM service is found, we continue to discover its characteristic
  Serial.println("Found it");
  
  Serial.print("Discovering characteristic ... ");
  if ( !hrmc.discover() )
  {
    // chr is mandatory, if it is not found (valid), then disconnect
    Serial.println("not found !!!");  
    Serial.println("characteristic is mandatory but not found");
    Bluefruit.disconnect(conn_handle);
    return;
  }
  Serial.println("Found it");

  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);
  // Request MTU exchange for rx_client
  if (connection) {
    connection->requestMtuExchange(dataSize + 3);  // Request MTU of 247 bytes
  }
}

/**
 * Callback invoked when a connection is dropped
 * @param conn_handle
 * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
 */
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
}

void requestEvent() {
  // 요청이 있을 때 I2C 마스터에게 데이터를 전송
  Wire.write(buffer, sizeof(buffer));
}


SERVER CODE

#include <bluefruit.h>
#include <Adafruit_LittleFS.h>
#include <InternalFileSystem.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BNO055.h>
#include <utility/imumaths.h>

#define dataNum 2
#define dataSize dataNum*4 // default: 20, maximum: 224
#define packetTime 8 // * 1.25ms
#define SLAVE_ADDRESS 0x60  // I2C 슬레이브 주소

BLEService        hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);

BLEDis bledis;    // DIS (Device Information Service) helper class instance

volatile bool updateVALUEFlag = false;  // A flag to signal when to read IMU data

float receivedData[dataNum];
float tempData[dataNum];
uint8_t buffer[dataNum * sizeof(float)];

void setup()
{
  Serial.begin(115200);
  while ( !Serial ) delay(10);   // for nrf52840 with native usb
  Serial.println("Bluefruit52 Central client");
  Serial.println("--------------------------------------\n");

  // Initialize the Bluefruit module
  Serial.println("Initialize the Bluefruit nRF52 module");
  uint16_t requestedMtu = dataSize;
  Bluefruit.configPrphConn(requestedMtu+3, 4, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); // MTU set to 247 bytes, BLE_GAP_EVENT_LENGTH_DEFAULT = 3
  Bluefruit.begin(); // maximum connections as Peripheral = 1, Central = 0 (default)
  Bluefruit.setTxPower(4);  // Set transmission power

  // Set the connect/disconnect callback handlers
  Bluefruit.Periph.setConnInterval(packetTime, packetTime);
  Bluefruit.Periph.setConnectCallback(connect_callback);
  Bluefruit.Periph.setDisconnectCallback(disconnect_callback);

  // Configure and Start the Device Information Service
  Serial.println("Configuring the Device Information Service");
  bledis.setManufacturer("Adafruit Industries");
  bledis.setModel("Bluefruit Feather52");
  bledis.begin();

  // Setup the Heart Rate Monitor service using
  // BLEService and BLECharacteristic classes
  Serial.println("Configuring the Heart Rate Monitor Service");
  setupHRM();

  // Setup the advertising packet(s)
  Serial.println("Setting up the advertising payload(s)");
  startAdv();
  Serial.println("\nAdvertising");

  /*************************I2C***************************/
  Wire.setPins(25, 26);  // SCL = 25, SDA = 26 핀 설정
  Wire.begin(SLAVE_ADDRESS);  // 슬레이브 모드로 I2C 초기화
  Wire.onRequest(requestEvent);  // 마스터의 읽기 요청이 들어올 때 호출될 콜백 설정
  Wire.setClock(400000);  // Set I2C speed 나중에 필요하면 사용
  
  // Board-specific Serial initialization delay (없애도 될 것 같음)
  #if defined(__AVR_ATmega32U4__) || defined(__SAM3X8E__)
    while (!Serial) delay(10);
  #endif
}

void startAdv(void)
{
  // Advertising packet
  Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
  Bluefruit.Advertising.addTxPower();

  // Include HRM Service UUID
  Bluefruit.Advertising.addService(hrms);

  // Include Name
  Bluefruit.Advertising.addName();
  Bluefruit.Advertising.restartOnDisconnect(true);
  Bluefruit.Advertising.setInterval(32, 244);    // in unit of 0.625 ms
  Bluefruit.Advertising.setFastTimeout(30);      // number of seconds in fast mode
  Bluefruit.Advertising.start(0);                // 0 = Don't stop advertising after n seconds  
}

void setupHRM(void)
{
  hrms.begin();

  // set up callback for receiving measurement
  hrmc.setProperties(CHR_PROPS_WRITE_WO_RESP | CHR_PROPS_READ); // CHR_PROPS_WRITE
  hrmc.setPermission(SECMODE_OPEN, SECMODE_OPEN); // SECMODE_OPEN or SECMODE_NO_ACCESS
  hrmc.setFixedLen(dataSize);
  hrmc.begin();
  hrmc.setWriteCallback(hrm_write_callback);
}

void connect_callback(uint16_t conn_handle)
{
  // Get the reference to current connection
  BLEConnection* connection = Bluefruit.Connection(conn_handle);

  char central_name[32] = { 0 };
  connection->getPeerName(central_name, sizeof(central_name));

  // Request MTU exchange for rx_client
  if (connection) {
    connection->requestMtuExchange(dataSize + 3);  // Request MTU of 247 bytes
  }

  uint16_t mtu;
  mtu = connection->getMtu();
  Serial.print("Connected to ");
  Serial.print(central_name);
  Serial.print(" with mtu size: ");
  Serial.print(mtu);
  
  connection->requestPHY(BLE_GAP_PHY_2MBPS);
}

/**
 * Callback invoked when a connection is dropped
 * @param conn_handle connection where this event happens
 * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
 */
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
  (void) conn_handle;
  (void) reason;

  Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
  Serial.println("Advertising!");
}

/**************************************************************************/
void loop() {
  if ( Bluefruit.connected() ) {
    updateIMUData();
  }
}

void hrm_write_callback(uint16_t conn_handle, BLECharacteristic* chr, uint8_t* data, uint16_t len)
{
  // 수신된 데이터가 float 배열의 크기인지 확인
  if (len == sizeof(float) * dataNum) {
    // 수신된 데이터를 float 배열 receivedData로 복사
    memcpy(receivedData, data, sizeof(receivedData)); // 배열의 크기만큼 복사
  } else {
    Serial.println("[RX]: 데이터 크기가 다름.");
  }
}

void updateIMUData() {
  memcpy(buffer, receivedData, sizeof(buffer)); // 여기에 IMU 데이터를 읽고 receivedData에 저장하는 로직 추가
}

void requestEvent() {
  // 요청이 있을 때 I2C 마스터에게 데이터를 전송
  Wire.write(buffer, sizeof(buffer));
}

Parents
  • The client code had an error, and the correct code is as follows.

    #include <bluefruit.h>
    #include <Adafruit_LittleFS.h>
    #include <InternalFileSystem.h>
    #include <Wire.h>
    
    #define dataNum 2 // timestampe 포함
    #define dataSize dataNum*4 // default: 20, maximum: 224
    #define packetTime 8 // unit: 1.25ms
    #define SLAVE_ADDRESS 0x61  // I2C 슬레이브 주소
    
    BLEClientService        hrms(UUID16_SVC_HEART_RATE);
    BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
    
    float packetCount = 0;
    uint8_t buffer[dataNum * sizeof(float)];
    
    void setup()
    {
      Serial.begin(115200);
      while ( !Serial ) delay(10);   // for nrf52840 with native usb
    
      Serial.println("Bluefruit52 Peripheral Server");
      Serial.println("--------------------------------------\n");
    
      Serial.println("Initialize the Bluefruit nRF52 module");
      // Change MTU size
      uint16_t requestedMtu = dataSize;
      Bluefruit.configCentralConn(requestedMtu+3, 4, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); // BLE_GAP_EVENT_LENGTH_DEFAULT = 3
      
      Bluefruit.begin(0, 1); // maximum connections as Peripheral = 0, Central = 1
      Bluefruit.setTxPower(4);
      Bluefruit.setName("Bluefruit52 Central");
    
      // Initialize HRM client
      hrms.begin();
      hrmc.begin();
      
      // Increase Blink rate to different from PrPh advertising mode
      Bluefruit.setConnLedInterval(250);
      
      // Callbacks for Central
      Bluefruit.Central.setConnInterval(packetTime, packetTime);
      Bluefruit.Central.setDisconnectCallback(disconnect_callback);
      Bluefruit.Central.setConnectCallback(connect_callback);
    
      /* Start Central Scanning
       * - Enable auto scan if disconnected
       * - Interval = 160 ms, window = 80 ms
       * - Don't use active scan
       * - Filter only accept HRM service
       * - Start(timeout) with timeout = 0 will scan forever (until connected)
       */
      Bluefruit.Scanner.setRxCallback(scan_callback);
      Bluefruit.Scanner.restartOnDisconnect(true);
      Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
      Bluefruit.Scanner.filterUuid(hrms.uuid);
      Bluefruit.Scanner.useActiveScan(false);
      Bluefruit.Scanner.start(0);                   // // 0 = Don't stop scanning after n seconds
    
      /*************************I2C***************************/
      Wire.setPins(25, 26);  // SCL = 25, SDA = 26 핀 설정
      Wire.begin(SLAVE_ADDRESS);  // 슬레이브 모드로 I2C 초기화
      Wire.onRequest(requestEvent);  // 마스터의 읽기 요청이 들어올 때 호출될 콜백 설정
      Wire.setClock(400000);  // Set I2C speed 나중에 필요하면 사용
    }
    
    /**************************************************************************/
    void readTimedata(float (&timeData)[dataNum]) {
      timeData[0] = millis();              // timestamp를 imuData의 첫 번째 요소에 저장
      packetCount++;
      memcpy(buffer, timeData, sizeof(buffer));
      timeData[1] = packetCount;
      hrmc.write((uint8_t*)timeData, sizeof(timeData));
    }
    
    /**************************************************************************/
    void loop() {
      float timeData[dataNum];
    
      if (Bluefruit.connected()) {
        readTimedata(timeData);
        // Serial.println(imuData[1]);
      }
    }
    
    /**********************************callback*********************************/
    /**
     * Callback invoked when scanner pick up an advertising data
     * @param report Structural advertising data
     */
    void scan_callback(ble_gap_evt_adv_report_t* report)
    {
      // Since we configure the scanner with filterUuid()
      // Scan callback only invoked for device with hrm service advertised
      // Connect to device with HRM service in advertising
      Bluefruit.Central.connect(report);
    }
    
    /**
     * Callback invoked when an connection is established
     * @param conn_handle
     */
    void connect_callback(uint16_t conn_handle)
    {
      Serial.println("Connected");
      Serial.print("Discovering Service ... ");
    
      // If HRM is not found, disconnect and return
      if ( !hrms.discover(conn_handle) )
      {
        Serial.println("Found NONE");
    
        // disconnect since we couldn't find HRM service
        Bluefruit.disconnect(conn_handle);
    
        return;
      }
    
      // Once HRM service is found, we continue to discover its characteristic
      Serial.println("Found it");
      
      Serial.print("Discovering characteristic ... ");
      if ( !hrmc.discover() )
      {
        // chr is mandatory, if it is not found (valid), then disconnect
        Serial.println("not found !!!");  
        Serial.println("characteristic is mandatory but not found");
        Bluefruit.disconnect(conn_handle);
        return;
      }
      Serial.println("Found it");
    
      // Get the reference to current connection
      BLEConnection* connection = Bluefruit.Connection(conn_handle);
      // Request MTU exchange for rx_client
      if (connection) {
        connection->requestMtuExchange(dataSize + 3);  // Request MTU of 247 bytes
      }
    }
    
    /**
     * Callback invoked when a connection is dropped
     * @param conn_handle
     * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
     */
    void disconnect_callback(uint16_t conn_handle, uint8_t reason)
    {
      (void) conn_handle;
      (void) reason;
    
      Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
    }
    
    void requestEvent() {
      // 요청이 있을 때 I2C 마스터에게 데이터를 전송
      Wire.write(buffer, sizeof(buffer));
    }

Reply
  • The client code had an error, and the correct code is as follows.

    #include <bluefruit.h>
    #include <Adafruit_LittleFS.h>
    #include <InternalFileSystem.h>
    #include <Wire.h>
    
    #define dataNum 2 // timestampe 포함
    #define dataSize dataNum*4 // default: 20, maximum: 224
    #define packetTime 8 // unit: 1.25ms
    #define SLAVE_ADDRESS 0x61  // I2C 슬레이브 주소
    
    BLEClientService        hrms(UUID16_SVC_HEART_RATE);
    BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
    
    float packetCount = 0;
    uint8_t buffer[dataNum * sizeof(float)];
    
    void setup()
    {
      Serial.begin(115200);
      while ( !Serial ) delay(10);   // for nrf52840 with native usb
    
      Serial.println("Bluefruit52 Peripheral Server");
      Serial.println("--------------------------------------\n");
    
      Serial.println("Initialize the Bluefruit nRF52 module");
      // Change MTU size
      uint16_t requestedMtu = dataSize;
      Bluefruit.configCentralConn(requestedMtu+3, 4, BLE_GATTS_HVN_TX_QUEUE_SIZE_DEFAULT, BLE_GATTC_WRITE_CMD_TX_QUEUE_SIZE_DEFAULT); // BLE_GAP_EVENT_LENGTH_DEFAULT = 3
      
      Bluefruit.begin(0, 1); // maximum connections as Peripheral = 0, Central = 1
      Bluefruit.setTxPower(4);
      Bluefruit.setName("Bluefruit52 Central");
    
      // Initialize HRM client
      hrms.begin();
      hrmc.begin();
      
      // Increase Blink rate to different from PrPh advertising mode
      Bluefruit.setConnLedInterval(250);
      
      // Callbacks for Central
      Bluefruit.Central.setConnInterval(packetTime, packetTime);
      Bluefruit.Central.setDisconnectCallback(disconnect_callback);
      Bluefruit.Central.setConnectCallback(connect_callback);
    
      /* Start Central Scanning
       * - Enable auto scan if disconnected
       * - Interval = 160 ms, window = 80 ms
       * - Don't use active scan
       * - Filter only accept HRM service
       * - Start(timeout) with timeout = 0 will scan forever (until connected)
       */
      Bluefruit.Scanner.setRxCallback(scan_callback);
      Bluefruit.Scanner.restartOnDisconnect(true);
      Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
      Bluefruit.Scanner.filterUuid(hrms.uuid);
      Bluefruit.Scanner.useActiveScan(false);
      Bluefruit.Scanner.start(0);                   // // 0 = Don't stop scanning after n seconds
    
      /*************************I2C***************************/
      Wire.setPins(25, 26);  // SCL = 25, SDA = 26 핀 설정
      Wire.begin(SLAVE_ADDRESS);  // 슬레이브 모드로 I2C 초기화
      Wire.onRequest(requestEvent);  // 마스터의 읽기 요청이 들어올 때 호출될 콜백 설정
      Wire.setClock(400000);  // Set I2C speed 나중에 필요하면 사용
    }
    
    /**************************************************************************/
    void readTimedata(float (&timeData)[dataNum]) {
      timeData[0] = millis();              // timestamp를 imuData의 첫 번째 요소에 저장
      packetCount++;
      memcpy(buffer, timeData, sizeof(buffer));
      timeData[1] = packetCount;
      hrmc.write((uint8_t*)timeData, sizeof(timeData));
    }
    
    /**************************************************************************/
    void loop() {
      float timeData[dataNum];
    
      if (Bluefruit.connected()) {
        readTimedata(timeData);
        // Serial.println(imuData[1]);
      }
    }
    
    /**********************************callback*********************************/
    /**
     * Callback invoked when scanner pick up an advertising data
     * @param report Structural advertising data
     */
    void scan_callback(ble_gap_evt_adv_report_t* report)
    {
      // Since we configure the scanner with filterUuid()
      // Scan callback only invoked for device with hrm service advertised
      // Connect to device with HRM service in advertising
      Bluefruit.Central.connect(report);
    }
    
    /**
     * Callback invoked when an connection is established
     * @param conn_handle
     */
    void connect_callback(uint16_t conn_handle)
    {
      Serial.println("Connected");
      Serial.print("Discovering Service ... ");
    
      // If HRM is not found, disconnect and return
      if ( !hrms.discover(conn_handle) )
      {
        Serial.println("Found NONE");
    
        // disconnect since we couldn't find HRM service
        Bluefruit.disconnect(conn_handle);
    
        return;
      }
    
      // Once HRM service is found, we continue to discover its characteristic
      Serial.println("Found it");
      
      Serial.print("Discovering characteristic ... ");
      if ( !hrmc.discover() )
      {
        // chr is mandatory, if it is not found (valid), then disconnect
        Serial.println("not found !!!");  
        Serial.println("characteristic is mandatory but not found");
        Bluefruit.disconnect(conn_handle);
        return;
      }
      Serial.println("Found it");
    
      // Get the reference to current connection
      BLEConnection* connection = Bluefruit.Connection(conn_handle);
      // Request MTU exchange for rx_client
      if (connection) {
        connection->requestMtuExchange(dataSize + 3);  // Request MTU of 247 bytes
      }
    }
    
    /**
     * Callback invoked when a connection is dropped
     * @param conn_handle
     * @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
     */
    void disconnect_callback(uint16_t conn_handle, uint8_t reason)
    {
      (void) conn_handle;
      (void) reason;
    
      Serial.print("Disconnected, reason = 0x"); Serial.println(reason, HEX);
    }
    
    void requestEvent() {
      // 요청이 있을 때 I2C 마스터에게 데이터를 전송
      Wire.write(buffer, sizeof(buffer));
    }

Children
No Data
Related