BLE on Photon 2 not accessible with Europe iPhone

Have a Photon 2 based system connected to a sensor and sending periodic telemetry to the cloud. The device also uses BLE to make sensor data available locally in real time. Using an app like nRFConnect or BLE Hero on iPhone or Android we see the Photon 2 advertising as a peripheral device, connect, and get the notify messages. Using latest DeviceOS 6.3.4. We’ve been beta testing for about a year and have no problems with WiFi telemetry or BLE communication and monitoring in the US.

Now we have our first devices in Europe (Germany) and have run into a problem where a user with a Europe issued iPhone doesn’t see the Photon 2 BLE. Our US phones in Europe continue to see the devices and are able to connect and monitor. The phone apps (nRFConnect or BLE Hero) start off with a screen showing all the BLE devices it “sees”. But the Europe iPhone doesn’t show our Photon 2 service being advertised - the US phone right beside does show our device in the list.

The Europe iPhone might show a list of 70+ devices and the US iPhone also shows 70+ devices but the lists are slightly different. The key difference of course being that our Photon 2 is on the US phone list but NOT the Europe phone list.

Are there some BLE frequency broadcast differences between continents? We’ve tried all manner of settings changes on iPhone and within the apps but can never get the Phonton 2 BLE to show up for our European testers.

Can you please specify which iPhone specifically is being used in Europe?

I have an iPhone 15 Pro from the US, and an iPhone 16 Pro from Europe and both pick up my Photon advertisements. BLE runs on the same frequency as WiFi, and it’s not a restricted band anywhere in the world.

The problem iPhone is a 15 Pro Max model number MU783ZD/A running iOS 26.2.1.

I also thought that BLE was at 2.4GHz and was the same everywhere in the world. I don’t see anything in DeviceOS BLE functions that could possibly cause us to be missed in a BLE scan. There are filters in both BLE tools on iPhone but we tried all filters off (the default state) and also various combinations of filters to no avail.

So we are at a loss to explain why BLE doesn’t work for this specific combination of Photon 2 and iPhone.

I'm certain you've already checked this, but it bites me sometimes: can you confirm that one phone is not automatically pairing with the device terminating the advertising routine?

It might be worth turning off the Bluetooth radio of all phones except one to ensure the device is not paired.

Ah, that’s a good idea! I’ll have our German partner check for a broadcast now that I’m home in the US – there’s no other device around that has previously connected.

Curious though, after I connected to ensure the telemetry was periodically streamed through a notify characteristic - I purposefully disconnected so our partner could connect. Is there a BLE spec for how soon a device starts advertising after a connection is removed? Maybe these BLE tools don’t actually disconnect when you go back to the scan screen?

It's generally up to the device to re-advertise, which should happen automatically with Device OS. But, I'm not sure how quickly - within a second or two. If I remember correctly, nRF Connect will remain connected when returning back to the scan screen.

So some bad news - now that I’m in the US, our German partner tries scanning for the Photon 2 device on his iPhone and it STILL doesn’t show up in a scan list. He tried using an iPad and his Mac with similar BLE tools and it doesn’t show up on any of these devices either. So the theory that my iPhone was holding on to the BLE connection is apparently incorrect.

One thing I noticed when I was there is that there are hundreds of BLE devices in the scan list. It is a fairly populous area but I have never seen that many devices at once. Of course that didn’t affect my ability to scan for and connect to the Photon 2.

At this point I think I have no alternative but to add to the Log messages info about BLE to see if it is in advertising state or not. Perhaps enabling the TRACE level would show some BLE log events that could be useful in determining our problem?

Can you use BLE Hero or nRF Connect to capture the raw advertisement data and post it here? You will need to use the Android version since iOS doesn’t have access to the raw advertisement data

Don’t have access to an Android device in Germany currently - and presumably couldn’t use one to capture the advertising data anyway since our Particle device does not show in a scan list (on any European Apple device anyway.)

I have added some diagnostics to our code to capture the raw advertising data on the Particle side though. Here in the US at startup when the BLE object is being built in setup(), I get this from the log handler:

0000001525 [app] INFO: SETUP:BLE name AQ1-d859688b8
0000001529 [app] INFO: SETUP:BLE advertising:(7) 02 01 06 03 03 1A 18

The second line being the raw advertising data shown as 7 bytes in hex. It should of course be exactly the same in Germany. On the nRF app here in the US this shows as device “AQ1-d859688b8” advertising the service “Environmental Sensing” (0x181a).

Then a short BLE connection with the nRF app here in the US shows this in the log handler:

0000030003 [app] INFO: LOOP:BLE diag: Connected 0, Advertising 1
0000039733 [wiring.ble] TRACE: Connected
0000040004 [app] INFO: LOOP:BLE diag: Connected 1, Advertising 0
0000050005 [app] INFO: LOOP:BLE diag: Connected 1, Advertising 0
0000060006 [app] INFO: LOOP:BLE diag: Connected 1, Advertising 0
0000070007 [app] INFO: LOOP:BLE diag: Connected 1, Advertising 0
0000080008 [app] INFO: LOOP:BLE diag: Connected 1, Advertising 0
0000084312 [wiring.ble] TRACE: Disconnected
0000090009 [app] INFO: LOOP:BLE diag: Connected 0, Advertising 1

All as expected. So still completely stumped as to why this doesn’t show up in Germany on Iphone, iPad or Mac. As seems to be the usual case, I suspect the problem eventually turns out to be something simple.

Can you send me your firmware and I’ll give it a go on my EU devices.

Not sure how to get a .bin file to you. It is the same code with only some extra Log.info() additions. Obviously I can’t send you the sources.

It runs on a bare Photon 2 but the log output will include errors reaching the sensors. It does the BLE advertising which is the key issue here. The BLE name will be AQ1-XXXXXXXX where XXXXXXXX is the ending digits of the Photon 2 id.

We have confirmed in a second location that the BLE advertising does not show on a scan list. Unfortunately, we cannot duplicate this in our lab as the BLE advertising shows up on every device / computer / laptop / tablet we can find.

So we are in an effort to find where this bug exists: the HAL, DeviceOS, or our code. Our code follows the application notes here → Bluetooth LE | Reference | Particle . We advertise the “environmental sensing” service.

Have you tried to reduce the name length from 12 characters? I had exactly the same issue and have had to reduce the advertised name to 7 characters to get the scan list to work consistently. Using iPhone and Android in Europe/UK.

This is interesting and I’ll give it a try to see if this fixes our issue. I’ll set our device name to AQ1-xxx where xxx are the last 3 characters of the device id (instead of the last 9 of the device id.) This makes our name 7 characters.

Odd thing is though, in our lab, the scan list of seeable devices includes “AirPods Pro” which is 11 characters and I would assume that the same name would be used in the UK and EU - and also that they would show in an nRF Connect scan in the UK / EU just like they do here.

There is also a confusing DeviceOS thing where there appears to be two ways to set the BLE device name; (1) BLE.setDeviceName() and also (2) in the advertising packet via advData.append(BleAdvertisingDataType::COMPLETE_LOCAL_NAME, theName, theName.length()). Only the first (1) method actually sets the name that shows up in a scan using an app like nRF Connect. The second (2) method can be seen in the nRF Connect app when the log level is debug and you look at the advertising packet - sure enough it is tucked into the advertising packet - but it is not the name that shows in the scan list even though you would think that nRF Connect gets the list of named devices it sees from grabbing any advertising packets it sees.

BLE.setDeviceName() is the only one that works for me. The explanation I received from Particle was that the advertising package is 32 bytes and this includes the service UUID leaving a small space for the name. If the name is set more than the available space then it fails silently (not set).

Yes, this is true. In our case, the service id (environmental sensing) is only two bytes so we have lots of room in the advertising packet.

It will be another day till I have scan / connect results out of our beta team in Germany. This is the only place where our Photon 2 BLE advertising doesn’t show up.

We have finally figured out what is happening here, which includes a deeper understanding of how BLE works and how the DeviceOS BLE class actually should be used. Our initial code simply set the BLE name, and then appended our service id to the advertising data like this:

// as part of STARTUP -> 
  String id;
  id = "AQ1-" + System.deviceID().substring(15);    // AQI-last 9 of id
  BLE.setDeviceName(id.c_str());

...

// within setup()
  BLE.on();
  // Add ESS characteristics to service
  BLE.addCharacteristic(aqiCharacteristic); 
  BLE.addCharacteristic(vocCharacteristic);
  BLE.addCharacteristic(co2Characteristic);
  BLE.addCharacteristic(temperatureCharacteristic);
  BLE.addCharacteristic(humidityCharacteristic);
  BLE.addCharacteristic(pm1Characteristic);
  BLE.addCharacteristic(pm25Characteristic);
  BLE.addCharacteristic(pm10Characteristic);
  // Advertising data setup
  BLE.setAdvertisingInterval(1600);     // interval set at 1s
  BleAdvertisingData advData;
  // only advertise the ESS service
  advData.appendServiceUUID(uuidEnvironmentalSensingService);
  BLE.advertise(&advData);              // Start advertising

This actually works on almost every BLE host device in the US; iphone, android, Windows, Mac, iPad, ... The Particle device (Argon, Photon 2, Boron) advertises and you can use a scanner and see your named device in a scan list, connect, and read data. This does NOT work on some older Android devices in the US and for some reason also does NOT work on almost all mobile devices, tablets or computers in the EU.

When you read the the DeviceOS docs, this coding seems like a good way to accomplish naming your BLE device other than the default name. When you test it here in the US you'll likely find it works. I do not know for sure how the BLE.setDeviceName() function works but assume it must shove the name into the advertising data - and it must be preserved to some extent when later in setup() we create our advData and append our service id. Whatever BLE.setDeviceName() does, it must not be 100% according to BLE specs as this does NOT work worldwide. So we have learned that BLE.setDeviceName() should ONLY be used as part of Particle's provisioning mode (which we do not use.)

The correct way to startup BLE and name your device something other than the default is to build out your advertising data directly like this:

// within setup()
  BLE.on();
  BLE.addCharacteristic(aqiCharacteristic); 
  BLE.addCharacteristic(vocCharacteristic);
  BLE.addCharacteristic(co2Characteristic);
  BLE.addCharacteristic(temperatureCharacteristic);
  BLE.addCharacteristic(humidityCharacteristic);
  BLE.addCharacteristic(pm1Characteristic);
  BLE.addCharacteristic(pm25Characteristic);
  BLE.addCharacteristic(pm10Characteristic);
  // Advertising data setup
  BLE.setAdvertisingInterval(1600);     // interval set at 1s
  BleAdvertisingData advData;
  // the flags are automatically included in advData so these next two lines not needed
  //uint8_t flagsValue = BLE_SIG_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
  //advData.append(BleAdvertisingDataType::FLAGS, &flagsValue, sizeof(flagsValue));
  String bleIdFull;
  bleIdFull = "PRE-" + System.deviceID().substring(15);   // PRE-last 9 of id
  advData.append(BleAdvertisingDataType::COMPLETE_LOCAL_NAME, (uint8_t*)&bleIdFull.c_str()[0], bleIdFull.length());
  // only advertise the ESS service
  advData.appendServiceUUID(uuidEnvironmentalSensingService);                   
  BLE.advertise(&advData);              // Start advertising

This method works on all devices we have had access to in the US and the EU. Of course you must be mindful of the length of advData so you don't use more than the max 31 bytes. So in this process you might be forced to truncate the name for instance if your service id is longer than 2 bytes like ours or you need to include additional data. Here is a great explanation for what an advertising packet looks like and is composed -> https://academy.nordicsemi.com/courses/bluetooth-low-energy-fundamentals/lessons/lesson-2-bluetooth-le-advertising/topic/advertisement-packet/.

Long story short? DON'T use BLE.setDeviceName() to set your BLE device name.