-
Notifications
You must be signed in to change notification settings - Fork 539
Description
Operating System
Others
IDE version
PlatformIO Core, version 6.1.18
Board
ProMicro NRF52840
BSP version
nicenano (platform: nordicnrf52; board: adafruit_feather_nrf52840_sense; framework: arduino)
Sketch
#include <bluefruit.h>
// UUIDs for a simple service and characteristic.
// You can generate your own, but these will work for the demo.
#define BUG_SVC_UUID "adaf-0001-0000-1000-8000-00805f9b34fb"
#define BUG_CHAR_UUID "adaf-0002-0000-1000-8000-00805f9b34fb"
// BLE Service and Characteristic objects
BLEService bugSvc(BUG_SVC_UUID);
BLECharacteristic bugChar(BUG_CHAR_UUID);
void setup() {
Serial.begin(115200);
while (!Serial && millis() < 2000) {
// Wait for serial monitor to open
}
Serial.println("Adafruit Bluefruit Fixed-Length notify() Bug Demo");
Serial.println("-------------------------------------------------");
// Initialize Bluefruit library
Bluefruit.begin();
Bluefruit.setName("NotifyBugDemo");
// Setup the Service
bugSvc.begin();
// Setup the Characteristic - THIS IS THE KEY PART
bugChar.setProperties(CHR_PROPS_NOTIFY);
bugChar.setPermission(SECMODE_OPEN, SECMODE_OPEN);
bugChar.setFixedLen(40); // Configure with a fixed length to trigger the bug
bugChar.begin();
// Setup Advertising
Bluefruit.Advertising.addService(bugSvc);
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(320, 1600); // 200ms, 1000ms
Bluefruit.Advertising.start(0); // Advertise indefinitely
Serial.println("Ready. Connect with a BLE scanner app (e.g., nRF Connect).");
Serial.println("Enable notifications on the characteristic, then send any character over this serial monitor to trigger the test.");
}
void loop() {
// Wait for a BLE connection
if (!Bluefruit.connected()) {
return;
}
// Wait for a character from the Serial monitor to trigger the test
if (Serial.available() <= 0) {
return;
}
// Read all available characters to clear the buffer
while(Serial.available() > 0) Serial.read();
Serial.println("\n--- TRIGGERING BUG ---");
// 1. Send a long string to fill the BLE stack's internal buffer
const char* long_str = "This_is_a_long_string_to_fill_buffer"; // 38 characters
Serial.print("Sending long string: '");
Serial.print(long_str);
Serial.println("'");
bugChar.notify((uint8_t*) long_str, strlen(long_str));
delay(100); // Small delay between notifications
// 2. Send a short string
const char* short_str = "short"; // 5 characters
Serial.print("Sending short string: '");
Serial.print(short_str);
Serial.println("'");
bugChar.notify((uint8_t*) short_str, strlen(short_str));
Serial.println("--- TEST COMPLETE ---");
Serial.println("Check your BLE scanner. The notification for 'short' will likely be corrupted.");
Serial.println("\nSend another character to repeat the test...");
}
What happened ?
Summary:
When using BLECharacteristic::notify(data, len) on a characteristic configured with a fixed length (e.g., setFixedLen(40)), the stack appears to transmit the full fixed length of the characteristic, including stale data from its internal buffer from previous, longer transmissions.
Analysis:
The notify() implementation correctly copies the provided len bytes into the stack's internal transmit buffer, but then transmits the full fixed length of the characteristic without clearing or respecting the new, shorter length. This causes stale data from previous calls to leak into the current transmission.
The Adafruit library appears to be a wrapper around the Nordic nRF5 SDK's "SoftDevice," which is the low-level driver for the BLE radio. The function that notify() ultimately calls is sd_ble_gatts_hvx().
This low-level function takes a pointer to the data and a pointer to the length of the data to be sent. The Nordic documentation does not specify that it will send more bytes than requested. This strongly implies that the bug is in the Adafruit library's wrapper code—it's likely ignoring the len parameter provided and instead telling the SoftDevice to send the full, fixed length of the characteristic from its internal, un-sanitized buffer.
Workaround:
The workaround is to manually create a 40-byte buffer in user code, memset it to zero, copy the desired string into it, and then call notify() with a hardcoded length of 40.
Environment:
Board: (ProMicro nRF52840, nice!nano)
IDE: PlatformIO
How to reproduce ?
Create a characteristic with CHR_PROPS_NOTIFY and setFixedLen(40).
In the loop(), trigger two notify() calls in succession with a short delay.
The first call should send a long string (e.g., 30 bytes).
The second call should send a short string (e.g., 10 bytes).
Use a BLE scanner app (like nRF Connect) to observe the notifications.
Expected Behavior:
The second notification packet should contain only the 10 bytes of the short string.
Actual Behavior:
The second notification packet contains the 10 bytes of the short string, immediately followed by the trailing 20 bytes from the first, longer string.
Debug Log
No response
Screenshots
No response