Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting the appearance characteristic of the device #88

Closed
JabberwockPL opened this issue Sep 26, 2017 · 51 comments
Closed

Setting the appearance characteristic of the device #88

JabberwockPL opened this issue Sep 26, 2017 · 51 comments
Assignees

Comments

@JabberwockPL
Copy link

The code for the cadence/speed sensor is almost done, however, its application is hit-and-miss. One particular app that has worked before now refuses to do so, disconnecting with cryptic messages... Some apps do not recognize the sensor at all. On the other hand, some other apps do and they show the correct inputs. nRF Connect is always solid, showing exactly what I need (but the apps themselves are bad).

I have even downloaded the nRF SDK, as they have an example code for cadence/speed. The code does not look fundamentally different from mine (well, mostly yours :) ), but there is one notable difference: they set the appearance of the device to Speed and Cadence Sensor.

Naturally, I wanted to do the same, but it seems the characteristic is not exposed at all in the library - the device name is set with .init and that is about it. Is there any way I can set it?

@nkolban
Copy link
Owner

nkolban commented Sep 26, 2017

Howdy,
The bad news is that the ESP-IDF master on Git changed significantly. My guess is Espressif are getting closer to releasing their 3.0. Two major areas changed. First, the BLE API exposed by ESP-IDF changed in breaking ways which had to be resolved. Following that, it was found that the toolchain (the C++ compiler) now failed to generate "good code" for execution. See issue #82.

More than that, the Arduino-ESP32 library opts to build against the latest ESP-IDF and that means that the breakages in ESP-IDF have flowed through to breakages in Arduino-ESP32.

That said ... that isn't the issue you are discussing.

Help me to help you ... assume that I am juggling so many parallel stories that I can't keep context straight in my head. Please try and make each issue as self contained as possible or, at a minimum, point to other issues that I should pre-read before reading the current one.

Can you clarify whether or not your ESP32 BLE app is a BLE Server or a BLE Client. I am going to assume it is a BLE Server that advertises its presence.

By setting the appearance, can I assume that you mean exposing this characteristic?

https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.gap.appearance.xml

If yes, then my immediate guess is that you can create a BLE Server that contains a service that contains a characteristic with 16bit UUID 0x2A01 as documented and described above. What you would do is create and expose a new characteristic. Read the specification of that characteristic and see if it makes sense.

If after reading that and the BLE C++ documentation we make available it is still a puzzle ... then I really want to hear back. If something is confusing, then I want to try and resolve that confusion in the documentation so that both you and future readers/users can understand. If something isn't clear, then I consider that my problem to fix.

@JabberwockPL
Copy link
Author

I understand you have a lot to deal with, so I appreciate your help even more!

I have read the specs and considered creating the characteristic (yes, it is a BLE Server, sorry for not being more specific). I just thought that if I create it from pService, which in my case is cycling speed/cadence sensor, then it appears under that service and the Generic Access characterstic of Appearance would remain unaffected. I have just tested it and it is indeed the case - new Appearance shows up under the cycling service and the Generic Access Appearance remains Uknown.

And I do not know how I can create it from the Generic Access service (0x1800), as it does seem to be exposed in the code. If it is accessible, I do not know how to do that. In the nRF SDK the characteristic is set at the creation of the device, together with its name and connection parameters. In your BLEDevice.cpp it seems only the name can be assigned.

@chegewara
Copy link
Collaborator

Can you paste link to nRF code you reference to?

@JabberwockPL
Copy link
Author

I have downloaded the nRF SDK, but the code is online here:
http://storage.tokor.org/pub/nrf51/doc/html/nrf6310_2s110_2ble__app__cscs_2main_8c_source.html

searching for 'appearance' should get you to the relevant part of the code.

@nkolban
Copy link
Owner

nkolban commented Sep 26, 2017

@JabberwockPL Ohhh!! I think I see what you are saying. Let me play it back to you and see if this sound right ...


We have a service type with UUID 0x1800 called "Generic Access" .. see:

https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.generic_access.xml

This service is a prescribed service and can have a variety of characteristic values including 0x2A01 (Appearance). How do I use the BLE C++ libraries in order to expose characteristic 0x2A01 as belonging to service 0x1800?


Does that sound about right? I don't have an immediate answer for that and will have to study to assist.

@nkolban nkolban removed the waiting label Sep 26, 2017
@JabberwockPL
Copy link
Author

Well, actually if I do not create the characteristic at all, nRF already shows that the Generic Access service has that characteristic, it only shows in the log „'[0] Unknown' received” and the characterstic is visible under the service as '[0] Unknown'. But of course I would like to set to according to my needs.

@chegewara
Copy link
Collaborator

chegewara commented Sep 26, 2017

Yes, but you can't add any characteristic to generic access 0x1800 and generic attributes 0x1801 service. You need to create service as you can see at https://devzone.nordicsemi.com/tutorials/8/

Step 4: Add our service

and i dont think you can add name to those services, but i will investigate it further.

@JabberwockPL
Copy link
Author

But I do not want to add my own chatacteristic, it seems Appearance is created anyway at the device creation (as nRF Connect shows). I just want to set it, just like you allow setting the name of the device (characteristic 0x2A00) with BLEDevice init(). I cannot figure out from your code how exactly you set the name (I.e. 0x2A00) at the device creation, but I guess the method (and time and place) should be the same for the appearance.

@chegewara
Copy link
Collaborator

https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEDevice.cpp#L191
errRc = ::esp_ble_gap_set_device_name(deviceName.c_str());

Mail me and we can talk in Polish, imperiaonline4@gmail.com.

@chegewara
Copy link
Collaborator

@nkolban
Copy link
Owner

nkolban commented Sep 26, 2017

Ooooh ... I had made a mental note to go research how "appearance" was going to be set system wide but it looks like you may have found the answer (you are AWESOME). Are we saying that setting the device name when we create a BLEDevice() is what causes the value of 0x1800 -> 0x2a01 to be set?

@nkolban
Copy link
Owner

nkolban commented Sep 26, 2017

Ohh ... wait again ... as soon as I re-read, it appears we have two characteristics ...

@chegewara
Copy link
Collaborator

But i guess answer to the question is, you cant change appearance name in server. Its up to client to match service UUID and display desired name.

@JabberwockPL
Copy link
Author

Name... or an icon. So I think add icon is a good candidate, as it has two bytes, exactly as the apperance UUID.

@JabberwockPL
Copy link
Author

Or I could skip the guessing and check GATT_UUID_GAP_ICON:
https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/5738f83aeb59361a0a2eda2460113f6dc9194271/stack/include/gattdefs.h

... and it is a match: 0x2A01

@chegewara
Copy link
Collaborator

Well, my guessing is not about how its handled, its about how good is this answer to you.

@JabberwockPL
Copy link
Author

Well, as I understand, the function is not exposed in the Arduino library, so for now I cannot do anything about it. However, I am not sure how useful it would be anyway - I mean, it would be extremely silly if the app developers have detected the sensors based on their icons and not on the services they provided? So while I cannot exclude that, it might be that my emulated sensor differs in other ways from commercial sensors.

My next candidate is manufacturer's name, as most examples seem to have that set as well. That I can access creating the relevant service, so it should be no problem.

@chegewara
Copy link
Collaborator

Almost found it, just need to investigate if it can be set in arduino.

@chegewara
Copy link
Collaborator

chegewara commented Sep 26, 2017

OK, it is possible to do it with arduino ide. You need to those 2 includes:
#include "gap_api.h" #include "gattdefs.h"
this line
GAP_BleAttrDBUpdate(GATT_UUID_GAP_ICON, (tGAP_BLE_ATTR_VALUE ) *attr);
and to figure out how to fill in attr. This is structure:

typedef union {
    tGAP_BLE_PREF_PARAM     conn_param;
    BD_ADDR                 reconn_bda;
    UINT16                  icon;
    UINT8                   *p_dev_name;
    UINT8                   addr_resolution;

} tGAP_BLE_ATTR_VALUE;

https://github.com/espressif/esp-idf/blob/65f57e5da7287576d98f657ea45163411bc3a564/components/bt/bluedroid/stack/include/gap_api.h#L116-L123

PS: now its time to find function to set manufacturer's name

@chegewara
Copy link
Collaborator

chegewara commented Sep 26, 2017

About manufacturer's name im not sure, but this is what i found.
https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers

I found also

m_advData.manufacturer_len = 0;
m_advData.p_manufacturer_data = nullptr;
. This is hard coded in Kolban's library, so its not very helpful.

@JabberwockPL
Copy link
Author

I have already set the manufacturer's name, it is a characteristic in a separate service (Device Info). I will try to use the code you have provided.

@JabberwockPL
Copy link
Author

I could not get your code to work, so I googled a bit and found this:
https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/5738f83aeb59361a0a2eda2460113f6dc9194271/stack/btm/btm_devctl.c#1086

I have copied the code verbatim and set p_name (just to test the name change), but it does not work, it throws 'unknown reference to' for the procedure.

I have dug some more and found that the advertising data also contains the appearance icon, which I thought might be even more import for detection. So between this:
BLEAdvertising *pAdvertising = pServer->getAdvertising();
pAdvertising->start();

you just need to put this:
pAdvertising->setAppearance(1157);

And it worked! nRF Connect even before the connection sees my device as a cycling sensor!

However, strangely enough, it still asks for the characteristic 0x2A01 and it comes up with nothing. Oh, and the cycling apps still do not work... Back to investigations, I guess.

@nkolban
Copy link
Owner

nkolban commented Sep 26, 2017

I have posed a question against the underlying ESP-IDF APIs ... see:

espressif/esp-idf#1042

@JabberwockPL
Copy link
Author

Oh my, it seemed like a simple question at first! I hope for their answer...

@nkolban
Copy link
Owner

nkolban commented Sep 28, 2017

That's a good thought ... how separate should a "BLE Server" advertisement be from the actual properties that the advertised server truly exposes. Should setting an attribute in the advertisement force the server to have the advertised property? Should setting a property in the server force the advertisement to be consistent with the properties offered? I can't say I disagree with either of those.

@chegewara
Copy link
Collaborator

chegewara commented Sep 28, 2017

Its up to you. In my opinion it should be done in ble stack, but as for now you can add some code like:
if(m_advData.appearanse != 0) GAP_BleAttrDBUpdate(GATT_UUID_GAP_ICON, &m_advData.appearance);
This can help users to set this properly and to avoid a lot of headache and/or deep digging.

PS Im little busy this days, thats why it took me so long to find it and test it.

@JabberwockPL
Copy link
Author

@chegewara
I have tried to use your code and it did not work... Probably I have not initiated the data properly. Could you post the whole code?

@chegewara
Copy link
Collaborator

chegewara commented Sep 28, 2017

@JabberwockPL Like i said, it was tested with esp-idf, i have to prepare Arduino ide first but i have no time. But it should work ith arduino too
#include "gap_api.h"
#include "gattdefs.h"
tGAP_BLE_ATTR_VALUE attr;
attr.icon = 0x485;
GAP_BleAttrDBUpdate(GATT_UUID_GAP_ICON, &attr);

If you can provide service, i think its 0x1816, and characteristics you are trying to run then i can test it with BLE library.

@JabberwockPL
Copy link
Author

This results in:

undefined reference to GAP_BleAttrDBUpdate(unsigned short, tGAP_BLE_ATTR_VALUE*)'
`

But no worries, it was most likely not the main source of the problem (but rather non-standard implementation of sensor handling by the apps, as I have written here.

@chegewara
Copy link
Collaborator

Try with this, should have working on arduino:

extern "C"{ #include "gap_api.h" #include "gattdefs.h" }

@JabberwockPL
Copy link
Author

JabberwockPL commented Sep 28, 2017

It compiles, but ESP32 goes into reboot loop:

Rebooting...
ets Jun  8 2016 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0010,len:4
load:0x3fff0014,len:732
load:0x40078000,len:0
load:0x40078000,len:11572
entry 0x40078a14
Starting BLE work!
abort() was called at PC 0x400d975f on core 0

Backtrace: 0x40088330:0x3ffdb560 0x4008842f:0x3ffdb580 0x400d975f:0x3ffdb5a0 0x400dadce:0x3ffdb5c0 0x400daf5a:0x3ffdb5e0 0x400db2a5:0x3ffdb620 0x400db2e5:0x3ffdb650 0x400db2f5:0x3ffdb670 0x400d6a0a:0x3ffdb690 0x400d57dd:0x3ffdb6b0 0x400d461d:0x3ffdb6f0 0x4010e17d:0x3ffdb710 0x4010a03e:0x3ffdb750

Commenting out GAP_BleAttrDBUpdate(GATT_UUID_GAP_ICON, &attr); elimates the loop so it must be the cause.

@JabberwockPL
Copy link
Author

Update:
I have managed to set the appearance by using:

pAdvertising->setAppearance(1157);

However, after reading the specs again, I have realized that some apps might expect the service UUID to be exposed in advertising data themselves. For that purpose I have used:

pAdvertising->setServiceUUID(BLEUUID((uint16_t)0x1816));

but this returns the error:
E (5844) BLEAdvertising: << esp_ble_gap_config_adv_data: rc=258 Invalid argument

@chegewara
Copy link
Collaborator

Good to hear.

@nkolban i have a question about BLEAdvertising class creator. There is 2 variables with exact same structure, m_advData and m_advDataScanResponse, and both are set as config adv data. What is purpose to have it both?

@chegewara
Copy link
Collaborator

chegewara commented Oct 9, 2017

@JabberwockPL if you follow the routines then you can see that setServiceUUID() sets up m_advDataScanResponse.p_service_uuid, but this error is from this line:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEAdvertising.cpp#L141

which is just after

errRc = ::esp_ble_gap_start_advertising(&m_advParams);

I dont know if somewhere else in your code m_advData.p_service_uuid is updated, but if not then from constructor it is nullptr. My advice is to try add those lines or alter it:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEAdvertising.cpp#L100-L101
with:

m_advData.service_uuid_len = 2;
m_advData.p_service_uuid = reinterpret_cast<uint8_t*>(&espUUID->uuid.uuid16);

@JabberwockPL
Copy link
Author

No, that was not the problem. setServiceUUID is supposed to setup the value, so it does not need to be set up earlier. The actual problem is described here:

https://www.esp32.com/viewtopic.php?f=13&t=2253

All it took was to put the UUID in the long form:

pAdvertising->setServiceUUID("00001816-0000-1000-8000-00805f9b34fb");

now I can happily declare that almost all apps are detecting the sensor correctly!

Again, thank you very, very much, I could not have done it without the help...

@nkolban
Copy link
Owner

nkolban commented Oct 9, 2017

@chegewara
re: m_advData and m_advDataScanResponse

In BLE there are always two advertising packets. One that is always broadcast and one that is returned to a client when it receives a broadcast advertisement and then says I want the additional advertisement packet. I'm probably getting the technical words wrong here ... but I believe this is called a scan response. At a high level ... imagine that the BLE Server is broadcasting adverts .... when a client receives an advert ... that may be all it needs ... however it is the clients choice to immediately turn around and ask the BLE Server give me the scan response advert.

In ESP-IDF, we define two adverts. One which contains the always advertised packet ... and a second advert that is returned by the server only when the client asks for the scan response advert.

Notice that one structure is set scan_response = false and the other set scan_response = true.

@nkolban
Copy link
Owner

nkolban commented Oct 9, 2017

We may have a relationship between this issue and #115

@chegewara
Copy link
Collaborator

chegewara commented Oct 9, 2017

Maybe this can explain something:
https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/gatt_server_service_table/README.rst

Actually, there are two way to create server service and characteristics. One is use the esp_gatts_create_service or esp_ble_gatts_add_char and etc. The other way is use esp_ble_gatts_create_attr_tab. The important things: the two ways cannot use in the same service, but can use in different service.

@chegewara
Copy link
Collaborator

chegewara commented Oct 9, 2017

#88 (comment)

@JabberwockPL ive found the problem why it is working this way

pAdvertising->setServiceUUID("00001816-0000-1000-8000-00805f9b34fb");

but doesnt work that way

pAdvertising->setServiceUUID(0x1816);

This is the clue:

E (5844) BLEAdvertising: << esp_ble_gap_config_adv_data: rc=258 Invalid argument

@nkolban When we change this line it will works:
https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/BLEAdvertising.cpp#L100

m_advDataScanResponse.service_uuid_len = 16;

Why? I dont know. Just works.

BTW pAdvertising->setAppearance(1157); doesnt work for me

@nkolban
Copy link
Owner

nkolban commented Oct 9, 2017

The sad thing about this issue is that I created a post back in June warning others not to use a uuid_len of anything other than 16 ... and failed to heed my own advice:

https://www.esp32.com/viewtopic.php?f=13&t=2253

Taking an action to resolve properly.

@nkolban
Copy link
Owner

nkolban commented Oct 9, 2017

Code changed ... commit pending.

@nkolban
Copy link
Owner

nkolban commented Oct 11, 2017

Code changed. Please let us know if we are good now.

@JabberwockPL
Copy link
Author

So I should now to try code it with:

pAdvertising->setServiceUUID(0x1816);

I wil do it, but it might take a bit, as it requires partial dismantling my bike installation (and I am having a blask using it :) ). But I do plan to have some other code changes, so I will get around to it.

@nkolban
Copy link
Owner

nkolban commented Oct 11, 2017

The new API is:

pAdvertising->addServiceUUID(BLEUUID((uint16_t)0x1816);

@JabberwockPL
Copy link
Author

Ok, I will try that.

@nkolban
Copy link
Owner

nkolban commented Oct 29, 2017

Closing for just now. Re-open as needed.

@nkolban nkolban closed this as completed Oct 29, 2017
@muradjabir
Copy link

This can now be set using esp_ble_gap_config_local_icon() on both Arduino and the IDF.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants