This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Scanning for the Short Local Name of an iOS device from an nRF52 based product

In the products that I am developing we use advertising packets in the scan response to pass some data around our devices. We wanted a way for the app we've made for iOS to participate. Mac doesn't give us full control over the Bluetooth stack, but it gives us the option to change the Short Local Name (BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME). We are having it modify that name to pack our data in it and modifying the scanner code I've written to parse the Short Local Name. The problem is I never receive the Short Local Name data from in the event data pointer. I've checked the raw data and see all the other devices I'm working on around the office, but never the altered Short Local Name. Using nRF Connect I can find the device and watch it update values live. It seems like the Softdevice filters away data from iOS devices or even all smart phones. I'm not sure what it is and I haven't any documents that suggest that. So my question is: am I not getting the data because it's not passed along by the Softdevice and are there some settings that I can change to reconfigure the Softdevice to pass that information along to my code.

The below code parses out the device Short Local Names fine, but I don't think that the data is ever getting passes to this snippet of code from the iPod I'm using to test, but the iPod does seem to broadcast the altered Short Local Name. There are a few log hex dumps where I can observe the raw packets and what has been found.

NRF_SDH_BLE_OBSERVER(scanner_observer, SCANNER_BLE_OBSERVER_PRIO, scan_on_ble_evt, NULL);

static uint32_t _adv_report_parse(uint8_t type, uint8_array_t * p_advdata, uint8_array_t * p_typedata)
{
	uint32_t index = 0;
	uint8_t * p_data;

	p_data = p_advdata->p_data;
//	NRF_LOG_HEXDUMP_DEBUG(p_data, 31);
	while (index < p_advdata->size) {
		uint8_t field_length = p_data[index];
		uint8_t field_type   = p_data[index + 1];

		if (field_type == type) {
			p_typedata->p_data = &p_data[index + 2];
			p_typedata->size   = field_length - 1;
			return NRF_SUCCESS;
		}
		index += field_length + 1;
	}
	return NRF_ERROR_NOT_FOUND;
}

static void scan_on_ble_evt(ble_evt_t const * p_ble_evt, void * p_context)
{
	ret_code_t err_code;
	if (p_ble_evt == NULL)
	{
		NRF_LOG_DEBUG("Context: %02x Event %02x", p_context, p_ble_evt);
	}
	else if((p_ble_evt->header.evt_id == BLE_GAP_EVT_ADV_REPORT) && (scanner_enabled))
	{
		uint8_array_t adv_data;
		uint8_array_t dev_name;
		sensor_data_pkt_t snsr_dat;

		// Prepare advertisement report for parsing.
		adv_data.p_data = (uint8_t *)p_ble_evt->evt.gap_evt.params.adv_report.data.p_data;
		adv_data.size   = p_ble_evt->evt.gap_evt.params.adv_report.data.len;

		uint8_t dev_index;

		err_code = _adv_report_parse(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, &adv_data, &dev_name);
        if(err_code != NRF_SUCCESS)
		{
			err_code = _adv_report_parse(BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME, &adv_data, &dev_name);
			if(!err_code)
			{
				void * data_pkt = ble_advdata_parse((uint8_t*)adv_data.p_data, adv_data.size, BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME);
				if(data_pkt)
				{
					NRF_LOG_HEXDUMP_DEBUG(data_pkt, 10);
                    if(find_MAC(data_pkt, &dev_index))
					{
						memcpy(&snsr_dat.data, data_pkt + BLE_GAP_ADDR_LEN, adv_data.size - BLE_GAP_ADDR_LEN);
						snsr_dat.dev_idx = dev_index;
						xQueueSend(m_scanner_q, &snsr_dat.data, pdMS_TO_TICKS(10));
					}
				}
			}
		}
		if(!err_code)
		{
			void * data_pkt = ble_advdata_parse((uint8_t*)adv_data.p_data, adv_data.size, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA);
			if(data_pkt)
			{
				if(!find_MAC((uint8_t *)p_ble_evt->evt.gap_evt.params.adv_report.peer_addr.addr, &dev_index))
				{
					dev_index = 0xFF;	// TODO this can be made simpler by changing the find MAC function to return the index of 0xFF if not found.
				}
			memcpy(&snsr_dat.data, data_pkt, sizeof(snsr_dat.data));
			snsr_dat.dev_idx = dev_index;
			xQueueSend(m_scanner_q, &snsr_dat.data, pdMS_TO_TICKS(10));
//			NRF_LOG_HEXDUMP_DEBUG(snsr_dat.data, sizeof(snsr_dat));

			}
		}
		if(find_MAC((uint8_t *)p_ble_evt->evt.gap_evt.params.adv_report.peer_addr.addr, &dev_index))
		{
			err_code = _adv_report_parse(BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA, &adv_data, &dev_name);
			if(err_code != NRF_SUCCESS)
				_adv_report_parse(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, &adv_data, &dev_name);
			if (err_code == NRF_SUCCESS)
			{
				void * data_pkt = ble_advdata_parse((uint8_t*)adv_data.p_data, adv_data.size, BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA);
				if(data_pkt)
				{
					main_pkt_hdr_t* p_main_hdr = (main_pkt_hdr_t*)(data_pkt + 2);
					if(p_main_hdr->counter != m_dev_trig.devs[dev_index].ctr)
					{
						NRF_LOG_DEBUG("New packet! Current counter: %d, Incoming counter: %d", m_dev_trig.devs[dev_index].ctr, p_main_hdr->counter);
						m_dev_trig.devs[dev_index].ctr = p_main_hdr->counter;
						memcpy(&snsr_dat.data, data_pkt, sizeof(snsr_dat.data));
						snsr_dat.dev_idx = dev_index;
						xQueueSend(m_scanner_q, &snsr_dat.data, pdMS_TO_TICKS(10));
						NRF_LOG_HEXDUMP_DEBUG(snsr_dat.data, sizeof(snsr_dat));
					}
				}
			}
		}
	}
	// ALWAYS resume scanning
	if (scanner_enabled)
	{
		err_code = sd_ble_gap_scan_start(NULL, &m_scan_buffer);
		if ((err_code != NRF_SUCCESS) && (err_code != NRF_ERROR_INVALID_STATE))
		{
			APP_ERROR_CHECK(err_code);
		}
	}
}

Here's a screen shot of the correct data in nRF Connect:

I'm using SDK15.3 and Softdevice 6.1.1 with my Windows 10 workstation running Segger Embedded Studio V4.50

Parents
  • HI Ryjan, 

    so there is no AD element with AD type 0x08 Â«Shortened Local Name» ? https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/

    Can you post the raw hex data that p_ble_evt->evt.gap_evt.params.adv_report.data.p_data points to?

    Best regards

    Bjørn

  • Ok, so I've gotten it to work and discovered some of the caveats to get it to work.

    We were generating a random sequence of numbers to identify a device and load that into the complete local name. But if the random values represent ASCII characters outside the standard (above 127) the encoding standards are not  the extended ASCII encoding. I think that they are UTF-8. This ends up extending characters and obfuscating the values I was looking for. Once we limited the generated random numbers to values only below 127 I was able to see the ID I planted in the complete local name and extract the data. I still haven't extracted the data successfully that follows the ID, but the issues I was having was due to the encoding of the data into the standard that is used to transfer text in the advertising packet.

  • Ok, glad to hear that you found the cause. So you have appended the ID to the Complete Local Name instead of the Shortened Local name?

  • It was an assumption of mine that it was the Shortened Local Name that iOS gave us access to. That was just based on the names that were displayed in nRF Connect. But it looks like nRF Connect defaults to the Complete Local Name, but in the absence of that, it displays the Shortened Local Name. The real fix was limiting the random characters used for identifying the device in our ID to single byte UTF-8 characters, which are the first 127 ASCII characters. We could also make sure that the ID is recorded in memory as any UTF-8 character, but we would need to make sure that the variable byte lengths used in characters that extend past the normal ASCII range are not causing issues with the actual length of the block of data to store the ID in. The simplest method to make sure that we kept the ID at a constant length is keep the characters to normal ASCII.

Reply
  • It was an assumption of mine that it was the Shortened Local Name that iOS gave us access to. That was just based on the names that were displayed in nRF Connect. But it looks like nRF Connect defaults to the Complete Local Name, but in the absence of that, it displays the Shortened Local Name. The real fix was limiting the random characters used for identifying the device in our ID to single byte UTF-8 characters, which are the first 127 ASCII characters. We could also make sure that the ID is recorded in memory as any UTF-8 character, but we would need to make sure that the variable byte lengths used in characters that extend past the normal ASCII range are not causing issues with the actual length of the block of data to store the ID in. The simplest method to make sure that we kept the ID at a constant length is keep the characters to normal ASCII.

Children
Related