Welcome to the Pycom forum Forum
    • \[\[global:header.categories\]\]
    • \[\[global:header.recent\]\]
    • \[\[global:header.tags\]\]
    • \[\[global:header.popular\]\]
    • Login

    Explore Pybytes | Official Documentation | Report a Firmware Bug/Issue | GitHub

    Callback when BLE client subscribes to notify characteristic?

    Scheduled Pinned Locked Moved WiPy 2&3
    bluetoothble
    8 Posts 2 Posters 3.4k Views 3 Watching
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • S Offline
      semireg
      last edited by

      Is it possible to get a callback when a BLE client subscribes to a notify characteristic?

      I tried this:

              self.my_char = self.service.characteristic(uuid=Ble.MY_CHAR_UUID,
                                                                properties=Bluetooth.PROP_NOTIFY,
                                                                value=0x1234)
              self.my_char.callback(trigger=Bluetooth.CHAR_NOTIFY_EVENT,
                                           handler=self.my_char_handler)
      

      No callback.

      I would expect that this API would allow me to start/stop resource-consuming peripherals only when needed.

      Thoughts?

      J 1 Reply Last reply Reply Quote 1
      • J Offline
        jmarcelino @semireg
        last edited by jmarcelino

        @semireg
        That's not available at the moment. What you get with CHAR_NOTIFY_EVENT is a notification for the client that the value has been updated.

        We are planning work on BLE interface later this quarter, I'll add that as a feature request.

        1 Reply Last reply Reply Quote 1
        • S Offline
          semireg
          last edited by

          I see that now in the source code at https://github.com/pycom/pycom-micropython-sigfox/blob/0411a97a97b1061c7931ab4a4de981af3c004c7e/esp32/mods/modbt.c

          #define MOD_BT_GATTC_ADV_EVT                                (0x0001)
          #define MOD_BT_GATTS_CONN_EVT                               (0x0002)
          #define MOD_BT_GATTS_DISCONN_EVT                            (0x0004)
          #define MOD_BT_GATTS_READ_EVT                               (0x0008)
          #define MOD_BT_GATTS_WRITE_EVT                              (0x0010)
          #define MOD_BT_GATTC_NOTIFY_EVT                             (0x0020)
          #define MOD_BT_GATTC_INDICATE_EVT                           (0x0040)
          

          I've worked quite a bit with BLE but I'm new to the pycom-micropython-sigfox codebase. Does anyone have any pointers on where I could start to begin adding this capability?

          J 1 Reply Last reply Reply Quote 0
          • J Offline
            jmarcelino @semireg
            last edited by jmarcelino

            You would start with modbt.c as you found, that's the MicroPython interface to the ESP-IDF SDK.

            The actual Bluetooth C interface would be written against the ESP32 ESP-IDF Bluetooth API and specifically the GATTS part but I'm not sure it gives you that level of access - it's a high level GATT server view you don't see the CCCD etc - so you might have to add it to ESP-IDF using the lower APIs.

            That source is also open but a more appropriate forum for questions around that would be esp32.com

            1 Reply Last reply Reply Quote 1
            • S Offline
              semireg
              last edited by semireg

              Digging through https://github.com/espressif/esp-idf/blob/master/components/bt/bluedroid/api/include/esp_gatts_api.h led me to https://github.com/pycom/pycom-micropython-sigfox/blob/master/esp32/mods/modbt.c - from here, lines 551-580 are of interest.

                              if (attr_obj->is_char) {    // characteristic
                                  bt_gatts_char_obj_t *char_obj = (bt_gatts_char_obj_t *)attr_obj;
                                  char_obj->events |= MOD_BT_GATTS_WRITE_EVT;
                                  if (char_obj->trigger & MOD_BT_GATTS_WRITE_EVT) {
                                      mp_irq_queue_interrupt(gatts_char_callback_handler, char_obj);
                                  }
                              } else {    // descriptor
                                  if (attr_obj->uuid.len == ESP_UUID_LEN_16 && attr_obj->uuid.uuid.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) {
                                      uint16_t value = param->write.value[1] << 8 | param->write.value[0];
                                      if (value == 0x0001) {  // notifications enabled
                                          bt_gatts_char_obj_t *char_obj = (bt_gatts_char_obj_t *)attr_obj->parent;
                                          // the size of value[] needs to be less than MTU size
                                          esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id, char_obj->attr_obj.handle, char_obj->attr_obj.value_len, char_obj->attr_obj.value, false);
                                      }
                                  }
                              }
              

              There are a few ways to implement this feature. Enabling notifications requires the client to write to the server's characteristic descriptor. This comes in as a ESP_GATTS_WRITE_EVT and gets translated to an MP_QSTR_CHAR_WRITE_EVENT (presumably, Bluetooth.CHAR_WRITE_EVENT). This then triggers a callback via mp_irq_queue_interrupt(gatts_char_callback_handler, char_obj);

              Descriptors are not exposed in the python interface so passing along the attr_obj that represents the descriptor isn't a good option.

              One solution would be to expose the descriptor notification value on the characteristic. That way, Bluetooth.CHAR_NOTIFY_EVENT could be reused for the server. If that's not possible because of a 1:1 relationship in the bt_locals_dict_table[], then a new constant could be defined such as CHAR_SUBSCRIBE_EVENT. Again, the actual values of the characteristic descriptor should be accessible on the characteristic itself.

              Lastly, the esp-idf GATT Server Example Walkthrough features a code snippet that seems more complete, in that it handles notifications and indications (write with and without response). I'm pasting that here for easy reference.

              case ESP_GATTS_WRITE_EVT: {                          
                   ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, conn_id %d, trans_id %d, handle %d\n", param->write.conn_id, param->write.trans_id, param->write.handle);
                   if (!param->write.is_prep){
                      ESP_LOGI(GATTS_TAG, "GATT_WRITE_EVT, value len %d, value :", param->write.len);
                      esp_log_buffer_hex(GATTS_TAG, param->write.value, param->write.len);
                      if (gl_profile_tab[PROFILE_B_APP_ID].descr_handle == param->write.handle && param->write.len == 2){
                          uint16_t descr_value= param->write.value[1]<<8 | param->write.value[0];
                          if (descr_value == 0x0001){
                              if (b_property & ESP_GATT_CHAR_PROP_BIT_NOTIFY){
                                  ESP_LOGI(GATTS_TAG, "notify enable");
                                  uint8_t notify_data[15];
                                  for (int i = 0; i < sizeof(notify_data); ++i)
                                  {
                                       notify_data[i] = i%0xff;  
                                   }
                                   //the size of notify_data[] need less than MTU size
                                   esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id,  
                                                               gl_profile_tab[PROFILE_B_APP_ID].char_handle,  
                                                               sizeof(notify_data),  
                                                               notify_data, false);
                              }
                          }else if (descr_value == 0x0002){
                               if (b_property & ESP_GATT_CHAR_PROP_BIT_INDICATE){
                                   ESP_LOGI(GATTS_TAG, "indicate enable");
                                   uint8_t indicate_data[15];
                                   for (int i = 0; i < sizeof(indicate_data); ++i)
                                   {
                                       indicate_data[i] = i % 0xff;
                                    }
                                    //the size of indicate_data[] need less than MTU size
                                   esp_ble_gatts_send_indicate(gatts_if, param->write.conn_id,  
                                                               gl_profile_tab[PROFILE_B_APP_ID].char_handle,  
                                                               sizeof(indicate_data),  
                                                               indicate_data, true);
                              }
                           }
                           else if (descr_value == 0x0000){
                               ESP_LOGI(GATTS_TAG, "notify/indicate disable ");
                           }else{
                               ESP_LOGE(GATTS_TAG, "unknown value");
                           }
                      }
                  }
                  example_write_event_env(gatts_if, &a_prepare_write_env, param); 
                  break;
              }
              

              Reference: https://github.com/espressif/esp-idf/blob/master/examples/bluetooth/gatt_server/tutorial/GATT_SERVER_EXAMPLE_WALKTHROUGH.md

              @jmarcelino, thanks for the replies. Where is a good place to learn about how /esp32/build/WIPY/release/genhdr/qstr/mods__modbt.c.qstr is used? Is this the resource files to be edited or are these files populated automatically from scanning the source? Thank you!

              1 Reply Last reply Reply Quote 0
              • S Offline
                semireg
                last edited by

                I've got this working to my satisfaction. I'll clean up the code and submit a pull request. I don't fully understand how MP_DEFINE_CONST_FUN_OBJ_X works... I'll have to brush up on http://micropython-dev-docs.readthedocs.io/en/latest/adding-module.html and make sure my changes are sane.

                0_1515429866239_pycomsubscribechar.gif

                1 Reply Last reply Reply Quote 0
                • S Offline
                  semireg
                  last edited by

                  I've created a pull request at https://github.com/pycom/pycom-micropython-sigfox/pull/121. Let me know if a github issue should be created for this, too.

                  J 1 Reply Last reply Reply Quote 2
                  • J Offline
                    jmarcelino @semireg
                    last edited by

                    @semireg
                    Thank a lot, looks great. Good call on catching the descriptor write in modbt.c

                    We'll review it and it should make into the next release.

                    1 Reply Last reply Reply Quote 0

                    Hello! It looks like you're interested in this conversation, but you don't have an account yet.

                    Getting fed up of having to scroll through the same posts each visit? When you register for an account, you'll always come back to exactly where you were before, and choose to be notified of new replies (either via email, or push notification). You'll also be able to save bookmarks and upvote posts to show your appreciation to other community members.

                    With your input, this post could be even better 💗

                    Register Login
                    • 1 / 1
                    • First post
                      Last post

                    Pycom on Twitter

                    Pycom