How is a bluetooth device friendly name retrieved?

I have SparkFun_Apollo3_AmbiqSuite_BSP examples “ble_freertos_amdtps” and “ble_freertos_amdtpc” compiled, using AmbiqMicroSDK Rel2.2.0, and running on each of two Redboard Artemis boards. The client (amdtpc) retrieves the MAC address of the server (amdtps) and displays it under the menu options “Show Scan Results” and “Create Connection”. I want to also display the friendly name, which is set to “Amdtp” in “amdtp_main.c” for the server. The scan results retrieved by the client with the function “AppScanGetResult()” in file “ble_menu.c” do not include the friendly name (it is not part of the “appDevInfo_t” structure). I have been trying to find a function that the client can use to retrieve the friendly name, but have been unsuccessful. I know the server broadcasts its name because the “Nrf Connect App”, running on an Android phone, sees and displays it prior to connecting. I did find the function “AppDbGetDevName()” in the “App_Framwork_API.pdf” document but there is no way to specify a handle or device ID to indicate which of the responding devices I want the name for. Regardless the function does not return anything when called from the client. Does anyone know how to retrieve the friendly name?

amddtpc_main.c function = amdtpcProcMsg

switch(pMsg->hdr.event)

case DM_SCAN_REPORT_IND:

#ifdef BLE_MENU

if ( pMsg->connOpen.peerRpa[0] > 1 && pMsg->connOpen.peerRpa[0] < 20) {

for (int i = 1 ; i < (pMsg->connOpen.peerRpa[0] -3); i++){

am_menu_printf(“%c”,pMsg->connOpen.peerRpa*);*
}
am_menu_printf(" = scan result\r\n");
}
#endif
Scan fills pMsg with possible connections. peerRpa array holds name. If it is a real chip, position [0] is the length of the name + 3, so… [0] = length, [1],[2].[3].etc to length - 3 will be the name. I specified length > 0 and < 20 in above statement. Worked for me and even found two chips with 2 different names. Have fun.

Hi Kerry,

I did figure out that dmEvt_t *pMsg was a pointer to the mother load of scan results, including the bd_addr and any friendly names. I passed the pointer to the ble_menu.c function “BleMenuInit()” function so that I could use it in the “showScanResults()” function. I’ve been trying to use the function DmFindAdType(), as per the example “SDK/third_party/exactle/sw/apps/watch/watch_main.c”, to locate the friendly name in pMsg->scanReport.pData but it isn’t finding it even though I can see it in the debugger memory display. Maybe I’ll have better luck with the code you are suggesting.

Randy

Yep…those names were obviously created with amdtps. I found the name hiding deep in the pMsg with Seggar Ozone and a J-Link edu mini.

Kerry,

Damn you’re good! After getting nowhere with my approach, I finally tried yours. With a few corrections to your code, voila I had the friendly name. First off, the function “amdtpcProcMsg” is in the file “amdtp_ main.c” not “amdtpc_main.c”. Then the line “am_menu_printf(”%c",pMsg->connOpen.peerRpa);" needs [ i ] appended to “peerRpa”. The problem that I have with your approach is that I don’t get the bd_addr that is associated with the friendly name, which I need later when selecting which device to connect to in the BLE_MENU. With my approach I found that the friendly name and bd_addr show up together in memory just beyond the scope of scanReport.pData so they are apparently not part of the scanResults at the point in the program when the BLE_MENU is being written. I still don’t know how to access them at that point but there must be a way. I’ll keep trying.

Randy

Kerry,

Update to my previous reply: I found that I could get the bd_addr from pMsg->scanReport.addr[0] thru [5], and like you I found that I got two different addresses for the same friendly name. One was addrType 0 (random) the other addrType 1 (public).

Randy

beautiful. You’ve got it!

I’m trying to trace:

ble_menu.c case GAP_MENUID_SCAN_START calls

amdtp.main.c AmdtpScanStart which calls

app_master_leg.c AppScanStart which calls

dm_scan.c dmScanStart which sends a message of DM_SCAN_MSG_API_START to

DM SCAN EVENT HANDLER???

Should you happen to stumble across the file / function which is called when that message fires, I would love to know. So…which file with which function handles the DM SCAN EVENT. I’ll get it eventually by loading all possible files in Ozone and setting breakpoints everywhere 'till I catch the sucker.

I’m losing it in the messaging and can’t find the messages destination as of yet. It’s getting tied up in the freertos tasking/messaging. I’m trying to convert the client amdtpc to Arduino losing the freertos.

I’ve re-written the uart to use Serial.printf and things are working up to the DM_SCAN_MSG_API_START at which time it goes ga ga due to no freertos task/messaging. Server works great in Arduino minus the freertos. Maybe I should just try to get freertos Arduino working? No big deal, just curious.

I believe the message is handled by:

app_master.c AppMasterProcDmMsg

switch (pMsg->hdr.event)

case DM_SCAN_REPORT_IND

ps: pMsg does have all the info.

sweet…

Kerry,

It does appear to be necessary to parse the friendly names from the “case DM_SCAN_REPORT_IND:” point in the code as per your approach. The code apparently loops through the responding devices and replaces the contents of the pMsg data with each pass. By the time one gets to the BLE_MENU this data is no longer in scope and I haven’t found any function to retrieve them at that point. I could save them in my own data structure and pass a pointer to it to BLE_MENU for later retrieval but for now it suffices to just display it before the BLE_MENU connect option 4. One thing I did find is that the byte just before &peerRpa[0] contains the length = len + type + friendly name. So your upper limit of i < (pMsg->connOpen.peerRpa[0] -3) can be replaced with that value and will then handle variable length names which are not 5 characters long. There does appear to be a problem with how the names get written to memory. Some of the characters get values that are not within the ascii range. For instance I detect a device called “Maggie’s Room” that has values of 0xe2 0x80 0x99 at the location of the [']. When I run the Nrf Connect App on my android phone I see the name displayed correctly so I assume the problem lies with the code in the SDK. Also, the name at location peerRpa[0] may not be the DM_ADV_TYPE_LOCAL_NAME (value 0x9). I have a Sphero device that has a local name of BB-7BBA (which is how the Nrf Connect displays it), but the name at peerRpa[0] is 'Sphero-Tu******ot" (with a type value of 0x7), where the **** are more of those non-ascii bytes. The local name follows this name. For now, however, I am able to differentiate which device I want to connect to.

With regard to your problem with DM_SCAN_MSG_API_START are you working with SDK 2.4.0? I looked at the dmScanStart function in SDK 2.2.0 and 2.3.0 and find an HCI_**** message in that function rather than the DM_SCAN_MSG_API_START that you are chasing. I’m still trying to find my way around the mind numbing maze of files and functions in 2.2.0 so I haven’t started working with the newer releases yet.

Randy

I am using SDK 2.4.2. I did succeed in getting freeRtos working in Arduino but it needs a lot of cleanup work b4 I can tell why it works now…but, … it does work.

I learned a bit about the amdtp naming convention while writing my own naming convention for the server version running under Arduino. amdtpScanDataDisc is an array in amdtp_main.c for the amdtps server example.

I modified it by commenting out the definition there, declaring an external version:

extern uint8_t amdtpScanDataDisc[22]; //Works

and placing the code for naming in a cpp file in my Arduino sketch:

void setLocalName( String s_Name ){

//following line works - came from amtdp_main.c close to beginning

//extern const uint8_t amdtpScanDataDisc[22] = {8,DM_ADV_TYPE_LOCAL_NAME,‘R’,‘E’,‘V’,‘1’,‘_’,‘3’,‘W’};

int n = s_Name.length(); //Length of string set in ino file as global String (doesn’t include teminator)

if ( n > 20) {

s_Name = s_Name.substring(0,20); //Only allow up to 20 char’s

n = s_Name.length(); //need length to change to uint8_t (new length after substring)

}

int i = 2; //1st char(0) is adv type char + # of char’s, 2nd char(1) is adv type

char c_AdvName[n+1]; //create Array large enough to hold chars of string + teminator char

strcpy(c_AdvName, s_Name.c_str()); //copy string to char array

//1st char in array must be = to # char’s in name (no terminator) + 1 for adv. type. If off by even 1,

// the name will not show up in scans

amdtpScanDataDisc[0] = n+1; //set first array pos to # of char’s + 1 for the type of adv packet

amdtpScanDataDisc[1] = DM_ADV_TYPE_LOCAL_NAME; // type of adv packet

for(i=2; i<n+2; i++){ //Add the char’s from the string to the amdtpScanDataDisc array starting at pos 2

amdtpScanDataDisc = c_AdvName[i-2];
}
}
That may hopefully help to understand the character issues going on. Anyway, I agree…this is one massive complex example. I had originally thought I might want to write a BLE library based on this stuff, but I don’t think I’ll get around to that. Spring is coming so no more time to sit on the couch and play.

Kerry,

I’m bothered by the fact that the throughput timer in the server, once started, continues to run and cause the function showThroughput() to be called, sending msgs to the uart terminal, even after selecting the menu options to stop sending data. In addition, I created two new menu options, one to have the Client close the connection, the other to request that the Server close the connection. Both options work but again the server throughput timer continues to run even after the connection is closed. I can stop this behavior by adding the function WsfTimerStop() to the servers “connection close” option, but then I can’t restart it again (even using WsfTimerStartSec()) without resetting the board. I can also prevent the showThroughput() function from being called if connId == 0, but this seems to me to be just a bandaid. Have you encountered this situation and if it bothered you did you find a solution?

Randy

Yeah, I noticed that as well. Not a bother for what I’m trying to do though…sorry

I have added in showThroughput() :

// only restart timer if throughput

if (gTotalDataBytesRecev != 0) WsfTimerStartSec(&measTpTimer, 1);

else measTpStarted = false;

This way it will restart again when data is received in amdtpDtpRecvCback()

Thanks for the replies. I did figure out where I was going wrong with trying to restart the timer. I needed to set measTpStarted to false, as Paul has done in his code, along with making the WsfTimerStop() call so that the call to WsfTimerStartSec() in amdtpDtpRecvCback() would execute. Paul’s addition of “if(gTotalDataBytes != 0)” is also a nice touch and cleans things up in the output.

Randy

I finally made sense of why showThroughput() continues to get called even after there is no data exchange. The function WsfTimerStartSec() should not be in the showThroughput() function since it resets the timer every time that function is called. And since gTotalDataBytes is set to zero just before the call to the timer function, a throughput of zero gets displayed when the timer times out and the showThroughput function gets called again. This cycle will repeat for ever until the board is reset. What should be in the showThroughput function instead is “measTpStarted = false;” (which Paul added) in order to allow the WsfTimerStartSec() function to get called at the end of the amdtpDtpRecvCback() function, which is were the timer function belongs. With these changes to the showThroughput() function I only get msgs from that function when data has been exchanged and gTotalDataBytes > 0. Also, I no longer need to use the WsfTimerStop() function to kill the repetitive calls to showThroughput().

I had already developed a solution for BLE communicating between Apollo3 and Linux ( Raspberry Pi/ Ubuntu). I have now extended this for BLE between 2 Apollo3 boards, one running as client other as server. https://github.com/paulvha/apollo3.

Based on the Ambiq Amdtp client menu structure : It can exchange test data, set the server led on /off, perform a simple chat, set and read any digital pin, read any analog pin, read the internal temperature , provide the battery level in percentage and (if enabled on the server) provide BME280 data to the client

It will work on both with the current 1.0.30 Apollo3 board library, as well as the adjusted library containing the more secure EXACTLE/Cordio from Ambiq 2.3.2 using the normal Arduino interface structure.

Fantastic…can’t wait to try it…

Confirmed working for redboard nano as server and Edge2 as client.

WOW…hooked both up to two com ports and ran Termite on Com3 - Server, Com4 - Client. Turned on client debug. Can chat back and forth, turn on lights, etc. etc. and…follow the command sequence.

REALLY, REALLY EXCEPTIONALLY NICE WORK HERE. WHAT A TOOL for exploring BLE on these boards with Windows10 and Arduino. I used SDK 2.4.2 and Core 1.0.30 - both unmodified. WOW!

SPEECHLESS HERE … AMAZING!!

Paul,

What great timing for you to have provided your code! I was just getting ready to create a new post regarding what I believed to be errors in the calculation of battery voltage and temperature, in the Ambiq SDK 2.2.0 (and probably all versions) examples such as “(SDK)\boards\apollo3_evb\examples\adc_vbat”, when I downloaded and looked at what you did. And I found that you have made the same corrections that I believe were needed. The first is that in the examples the FIFO sample value gets scaled by dividing by 65,636, which would be correct for a 16 bit value, but the ADC registers are only 14 bit, which should use 16,384 as the scaling factor as you have done. For VBATT I get a sample value of 12,206 (using a reference voltage of 1.5V) which results in VCC = 3.35 V using the 14 bit scale value and 0.84 V using the 16 bit scale factor. Obviously, the 3.35 V result is reasonable, whereas the 0.84 V result is not. The other error, which I haven’t figured out yet, is that the temperature FIFO sample value is about 43,840, which is impossible given a 14 bit register. I think the problem is related to the use of a 10 bit, instead of 14 bit, resolution for temperature in the example problem. Oddly enough, the resulting temperature, using the incorrect 16 bit scaling factor as done in the example, is about 72 degF as compared to my room thermostat reading of 69 degF. I’ll have to study your code at length to know what you’ve done, but it appears at a quick glance that you have taken the default values for resolution which are 14 bit. Since your results are in close agreement with an independent temperature measurement your code appears to be working correctly. Another error that I think is present in the example, although it does not affect the results, are the expected temperature and voltage ranges given respectively as -40 F to 225 F and 0.825 V to 1.283 V. The given temperature range results in a maximum difference of 265 F or 147 C. For a specified sensitivity of 3.8 mV/C, for the temperature sensor, the resulting voltage difference would be 559 mV as compared to the given range of 458 mV. I believe that a more credible expected temperature range would be the maximum operating range for the chip, which is given in the Apollo3 Blue data sheet as -40 C to 85 C (-40 F to 185 F) for which the expected voltage difference would be 475 mV. I don’t find anywhere in the data sheet document where a temperature range or voltage reference point is given for the temperature sensor.

One additional error that I have found is in the Arduino “Example4_analogRead” problem for which the equations are correct but the result gives a temperature of 40 C (104 F) instead of the expected 21 C (70 F). I believe the problem relates to the internal pad number of 102 that is assigned to the temperature sensor. I have not found any documentation to validate the correct values for these pads. The battery voltage divider, which is assigned pad number 103, provides a correct result of about 3.32 V.

I’m looking forward to further exploring and executing Paul’s work.

Randy

The explanation for the temperature result being reasonable in the example problem referred to above, which uses a 16 bit scale factor, is as follows:

The FULL_SAMPLE from the FIFO is used for temperature, which includes the 6 fraction bits. A bit mask of 0xFFC0 is then applied, which zeros the fraction bits. Then, since a 10 bit precision was used for temperature, the resulting value that includes 6 additional bits is a 16 bit number (the same as shifting the 10 bit number left 6 bits). Therefore, using a 16 bit scaling factor is appropriate and gives a correct result. Alternatively, a 14 bit precision can be used and the FULL_SAMPLE shifted right 6 bits and then use a 14 bit scaling factor. However, use of the code “g_ui16ADCTEMP_code = AM_HAL_ADC_FIFO_SAMPLE(sSample.ui32Sample);” will grab the sample without the 6 fraction bits and the 14 bit scaling factor can be applied without any further bit shifting. In Paul’s code, a default precision of 14 bits is used and the sample is obtained without the 6 fraction bits. Then a 14 bit scale factor is applied.