We have prototypes running on multiple platforms: BSOM, Boron, Argon and Photon 2. They all run pretty much the same code that gathers sensor records, stores them in memory ring buffer, publishes to cloud, and exposes BLE characteristics to respond to data dump requests. The BLE mode is peripheral. The characteristics look like this:
static BleCharacteristic commandWriteCharacteristic("Cmd", /*BleCharacteristicProperty::WRITE |*/ BleCharacteristicProperty::WRITE_WO_RSP, BleUuid("b088ef40-6c7c-4b82-b191-5376bd85a75f"), uuidEnvironmentalSensingService);
static BleCharacteristic commandResponseCharacteristic("CmdRsp", /*BleCharacteristicProperty::INDICATE |*/ BleCharacteristicProperty::NOTIFY, BleUuid("b088ef50-6c7c-4b82-b191-5376bd85a75f"), uuidEnvironmentalSensingService);
A Flutter app running on Andriod or iOS or MacOS subscribes to the NOTIFY characteristic, issues a command through the commandWriteCharacteristic and then stores / graphs the chunked records returned. Our code determines the MTU and then the maximum packet size (MTU-3) using:
ssize_t iCurrentAttMtu = BLE.getCurrentAttMtu(peer);
if (iCurrentAttMtu >= 23)
uiPacketSize = (size_t)iCurrentAttMtu-3; // subtract 3 bytes for ATT header
uiRecordsPerNotify = (unsigned int)(uiPacketSize / uiRecordSize);
This all works solid and reliably on BSOM, Boron, Argon - with any client (Android, iOS, MacOS). It fails on Photon 2 when the number of notifies required exceeds 10. For the various combinations of Particle devices / clients we see different values for uiPacketSize that have been negotiated between Flutter and Particle - varying from 184 bytes to 244 bytes.
We tried slowing down the Photon 2 with delay(), thinking it might be flooding the client with notifies too fast - didn't change the failure. Turned on { "wiring.ble", LOG_LEVEL_ALL} but that only showed us connects and disconnects. We tried hard coding the uiPacketSize rather than trying to determine it - and found we could break the system but not solve the problem.
So we instrumented our code with Log.info() statements to show us the write command coming in and notify packets sent out. There was no indication of a failure and in fact the logs showed all of the packets being sent to the client.
We turned to debug logs on the Flutter app which surfaces the problem. Here's the log on a successful write / notify request:
2026-08-11 12:49:20.697 AQ1 Scan[3002:6957886] [FBP-iOS] handleMethodCall: setNotifyValue
2026-08-11 12:49:20.727 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateNotificationStateForCharacteristic:
2026-08-11 12:49:20.727 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
flutter: [history] AQ1-eef43212 request: {"TS":1786380560,"N":96,"RF":2}
flutter: [history] AQ1-eef43212 write mode: without-response
2026-08-11 12:49:20.762 AQ1 Scan[3002:6957886] [FBP-iOS] handleMethodCall: writeCharacteristic
2026-08-11 12:49:20.837 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:20.837 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:20.878 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:20.878 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:20.912 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:20.912 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:20.942 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:20.942 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:20.972 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:20.972 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.002 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:21.002 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.042 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:21.043 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.089 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:21.089 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.122 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:21.122 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.197 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:21.197 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.225 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:49:21.225 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:49:21.227 AQ1 Scan[3002:6957886] [FBP-iOS] handleMethodCall: setNotifyValue
2026-08-11 12:49:21.254 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateNotificationStateForCharacteristic:
2026-08-11 12:49:21.254 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
We have the notify subscription, the write command, 11 notifies, and then the closing of the notify subscription.
The failing log:
2026-08-11 12:51:20.306 AQ1 Scan[3002:6957886] [FBP-iOS] handleMethodCall: setNotifyValue
2026-08-11 12:51:20.334 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateNotificationStateForCharacteristic:
2026-08-11 12:51:20.334 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
flutter: [history] AQ1-4a065494 request: {"TS":1786380680,"N":96,"RF":2}
flutter: [history] AQ1-4a065494 write mode: without-response
2026-08-11 12:51:20.335 AQ1 Scan[3002:6957886] [FBP-iOS] handleMethodCall: writeCharacteristic
2026-08-11 12:51:20.639 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.639 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:20.698 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.698 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:20.743 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.743 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:20.803 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.803 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:20.848 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.848 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:20.910 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.910 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:20.982 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:20.982 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:21.002 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:21.002 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:21.058 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:21.058 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
2026-08-11 12:51:21.119 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateValueForCharacteristic:
2026-08-11 12:51:21.119 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
...waiting...timeout
2026-08-11 12:51:28.340 AQ1 Scan[3002:6957886] [FBP-iOS] handleMethodCall: setNotifyValue
2026-08-11 12:51:28.360 AQ1 Scan[3002:6957886] [FBP-iOS] didUpdateNotificationStateForCharacteristic:
2026-08-11 12:51:28.360 AQ1 Scan[3002:6957886] [FBP-iOS] chr: b088ef50-6c7c-4b82-b191-5376bd85a75f
We have the same notify subscription, the write command, only 10 notifies followed by the timeout delay, and then the closing of the notify subscription. So even though our Photon 2 code shows it sends all the notifies, something happens underneath that chokes the process and the FLutter app never gets notify number 11 and times out.
Any experience out there with transferring larger chunks of data? Or have an idea for something else to log to determine the choke point?