2015年1月12日 星期一

BLE in Linux One : Bluez GATT Server Concept


     Once we setting bluetooth low energy(BLE) advertising, we would want to setting this beacon being connectable. The clients (like phone, computer) could read/upload data to/from that.

    That applying is very wide: for example, the all-day heartbeat  measurer in current,  the sensors and recorder are connected physicallly as one device. The patient will feel it is not comfortable when he carry it (Just like carry urine bag but smaller) . If the manufacturer of heartbeat measurer introduce  BLE technology, the recorder could be  indenpendent device.
The connection between recorder and sensors is wireless. It is not necessary to carry recorder like shoulder bag all day, it could be put in desk.


Why not apply classical bluetooth for that goal ? For classical bluetooth spends too much energy, mercury battery could not afford such costing.


Ok, for our major issue: setting gatt service in linux.

In my environment, I use fedora 21 + bluez 5.

If dbus version of your linux distribution is lower than 1.6, you should use bluez 4.

DO NOT force upgrading your dbus version to 1.6, unless you know what you do.


The gatt service warm-up be the follow steps:


0. Download and untar the bluez package from bluez

(Use tar  Jxvf    XXX.tar.xz    to uncompress file)

1. Build bluez on your computer.

 For me, the configure parameters  of bluez are:

./configure CFLAGS="$CFLAGS -Wno-sign-compare" --disable-udev  --prefix=$PWD/built --enable-experimental  --enable-maintainer-mode


The --enable-experimental  and  --enable-maintainer-mode set gatt-service example being included into compilation. and --disable-udev is for avoiding configuration error(and the udev module is useless for me). CFLAGS="$CFLAGS -Wno-sign-compare" set compiler to ignore sign/unsigned warning (the Makefile of Bluez set compiler to treat  warning as error!).


2. Run the bluetoothd program, which is under src folder of bluez

The bluetoothd is a daemon program to manage all bluetooth device and (maybe all) service.

Once we have built this bluez, we could run the sbluetoothd as test.

But before the running, we should stop the one bluetoothd which has been run:

 sudo killall -9 bluetoothd  

we could read the help of bluetoothd.

 [gaiger@localhost src]$ ./bluetoothd -h
Usage:
  bluetoothd [OPTION...]

Help Options:
  -h, --help                  Show help options

Application Options:
  -d, --debug=DEBUG           Specify debug options to enable
  -p, --plugin=NAME,..,       Specify plugins to load
  -P, --noplugin=NAME,...     Specify plugins not to load
  -C, --compat                Provide deprecated command line interfaces
  -E, --experimental          Enable experimental interfaces
  -n, --nodetach              Run with logging in foreground
  -v, --version               Show version information and exit



I note here : the -d argument would make the output be tedeious, and the -n argument would make the programming running on the front-end.
What is plugin? those are gatt service (example) which have been included in bluez.
Check the files under profiles and plugin, we could watch what service bluez have done:


 [gaiger@localhost src]$ ls ../profiles/  
 alert cyclingspeed health   input   sap     time  
 audio deviceinfo  heartrate network  scanparam  
 cups  gap      iap    proximity thermometer  

 [gaiger@localhost src]$ ls ../plugins/*.c  
 ../plugins/autopair.c    ../plugins/hostname.c ../plugins/sixaxis.c  
 ../plugins/external-dummy.c ../plugins/neard.c   ../plugins/wiimote.c  
 ../plugins/gatt-example.c  ../plugins/policy.c  

Now we try to enable the timer service in bluetoothd:

 [gaiger@localhost src]$ sudo ./bluetoothd --plugin=time -n   
 bluetoothd[8511]: Bluetooth daemon 5.27
bluetoothd[8511]: Starting SDP server
bluetoothd[8511]: Ignoring (cli) hostname
bluetoothd[8511]: Ignoring (cli) wiimote
bluetoothd[8511]: Ignoring (cli) autopair
bluetoothd[8511]: Ignoring (cli) policy
bluetoothd[8511]: Ignoring (cli) gatt_example
bluetoothd[8511]: Ignoring (cli) neard
bluetoothd[8511]: Ignoring (cli) sap
bluetoothd[8511]: Ignoring (cli) a2dp
bluetoothd[8511]: Ignoring (cli) avrcp
bluetoothd[8511]: Ignoring (cli) network
bluetoothd[8511]: Ignoring (cli) input
bluetoothd[8511]: Ignoring (cli) hog
bluetoothd[8511]: Ignoring (cli) health
bluetoothd[8511]: Ignoring (cli) gap
bluetoothd[8511]: Ignoring (cli) scanparam
bluetoothd[8511]: Ignoring (cli) deviceinfo
bluetoothd[8511]: Ignoring (cli) alert
bluetoothd[8511]: Ignoring (cli) proximity
bluetoothd[8511]: Ignoring (cli) thermometer
bluetoothd[8511]: Ignoring (cli) heartrate
bluetoothd[8511]: Ignoring (cli) cyclingspeed
bluetoothd[8511]: Ignoring (cli) external_dummy
bluetoothd[8511]: Bluetooth management interface 1.7 initialized

Then run the script I have provided in the last article, we could use BLE scanner (android's app) to detect this service.





Now we know that is a good example for us to set a gatt service.

Take look at the time service code,  in profiles/time/server.c  (buttom):


struct btd_profile time_profile = {
 .name  = "gatt-time-server",
 .adapter_probe = time_server_init,
 .adapter_remove = time_server_exit,
};

static int time_init(void)
{
 return btd_profile_register(&time_profile);
}

static void time_exit(void)
{
 btd_profile_unregister(&time_profile);
}

BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
   BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
   time_init, time_exit)

The declaration of macro BLUETOOTH_PLUGIN_DEFINE be

#define BLUETOOTH_PLUGIN_DEFINE(name, version, priority, init, exit)

(in src/plugin.h). it is , to define what function is for initialization(entrance), what is for exit. As this example, the entrance and exit are just for setting/un-setting structure  btd_profile, the structure are for customizing gatt service distinctly.

The structure btd_profile has 3 member, are name, service_init and service_exit, respectively.

Now look at the function time_server_init ,  time_server_exit and relative functions  in the same file:


#define CURRENT_TIME_SVC_UUID  0x1805
#define REF_TIME_UPDATE_SVC_UUID 0x1806

#define LOCAL_TIME_INFO_CHR_UUID 0x2A0F
#define TIME_UPDATE_CTRL_CHR_UUID 0x2A16
#define TIME_UPDATE_STAT_CHR_UUID 0x2A17
#define CT_TIME_CHR_UUID  0x2A2B

static uint8_t current_time_read(struct attribute *a,
     struct btd_device *device, gpointer user_data)
{
 struct btd_adapter *adapter = user_data;
 uint8_t value[10];

 if (encode_current_time(value) < 0)
  return ATT_ECODE_IO;

 attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);

 return 0;
}


static uint8_t local_time_info_read(struct attribute *a,
    struct btd_device *device, gpointer user_data)
{
 struct btd_adapter *adapter = user_data;
 uint8_t value[2];

 DBG("a=%p", a);

 tzset();

 /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
  * format (offset from UTC in number of 15 minutes increments). */
 value[0] = (uint8_t) (-1 * timezone / (60 * 15));

 /* FIXME: POSIX "daylight" variable only indicates whether there
  * is DST for the local time or not. The offset is unknown. */
 value[1] = daylight ? 0xff : 0x00;

 attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);

 return 0;
}

static gboolean register_current_time_service(struct btd_adapter *adapter)
{
 bt_uuid_t uuid;

 bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);

 /* Current Time service */
 return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
    /* CT Time characteristic */
    GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
    GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
       GATT_CHR_PROP_NOTIFY,
    GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
      current_time_read, adapter,

    /* Local Time Information characteristic */
    GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
    GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
    GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
      local_time_info_read, adapter,

    GATT_OPT_INVALID);
}

static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter)
{
 const char *path = adapter_get_path(adapter);

 DBG("path %s", path);

 if (!register_current_time_service(adapter)) {
  error("Current Time Service could not be registered");
  return -EIO;
 }

 if (!register_ref_time_update_service(adapter)) {
  error("Reference Time Update Service could not be registered");
  return -EIO;
 }

 return 0;
}

static void time_server_exit(struct btd_profile *p,
      struct btd_adapter *adapter)
{
 const char *path = adapter_get_path(adapter);

 DBG("path %s", path);
}

That is very obvious:

The function time_server_init register 2 service : register_current_time_service and register_ref_time_update_service. In the function register_current_time_service, the code call the function bt_uuid16_create with uuid REF_TIME_UPDATE_SVC_UUID to create a service, and add 2 characteristic  CT_TIME_CHR_UUID and LOCAL_TIME_INFO_CHR_UUID by calling function gatt_service_add which be added function local_time_info_read and current_time_read. In these pointed function, the data be sent by calling function attrib_db_update.


 bt_uuid16_create : create service.
gatt_service_add:  set characteristic and set function pointer corresponding the characteristic.
attrib_db_update: sent data to client.

The functions are declared in src/plugin.h and attrib/gatt-service.h:

Note, gatt_service_add:


gboolean gatt_service_add(struct btd_adapter *adapter, uint16_t uuid,
bt_uuid_t *svc_uuid, gatt_option opt1, ...);


It is an uncertain parameter function, we could register several characteristic in formal of :


gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
    /*first  characteristic */
    GATT_OPT_CHR_UUID16, FRIST_CHR_UUID,
    GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
       GATT_CHR_PROP_NOTIFY,
    GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
     first_pointer_read, adapter,

    /* 2nd  characteristic */
    GATT_OPT_CHR_UUID16, SECOND_CHR_UUID,
    GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
    GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
      second_pointer_read, adapter,
     /* 3rd characteristic */
    GATT_OPT_CHR_UUID16, THIRD_CHR_UUID,
    GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
    GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,     

     third_pointer_read, adapter, 

     : 

     GATT_OPT_INVALID);

You could refer to the other gatt service example, I recommend plugins/gatt-example.c.

If you want to customize your gatt service, It is complicated to create a whole new one but without changing original example: it is hard to modify Makefile files! Just backup and modify the  plugins/wiimote.c file,  It maybe be the most useless one for being refered.


NOTE : If you want to know more detailedly about how to implement or modify  a gatt-server ( peripheral) as yours, please read the article written by me, which is  the sequel of this one.


17 則留言:

  1. root@phidgetsbc:~/bluez-5.30/src# ./bluetoothd --plugin=time -n
    bluetoothd[14343]: Bluetooth daemon 5.30
    bluetoothd[14343]: Starting SDP server
    bluetoothd[14343]: Ignoring (cli) hostname
    bluetoothd[14343]: Ignoring (cli) wiimote
    bluetoothd[14343]: Ignoring (cli) autopair
    bluetoothd[14343]: Ignoring (cli) policy
    bluetoothd[14343]: Ignoring (cli) gatt_example
    bluetoothd[14343]: Ignoring (cli) neard
    bluetoothd[14343]: Ignoring (cli) sap
    bluetoothd[14343]: Ignoring (cli) a2dp
    bluetoothd[14343]: Ignoring (cli) avrcp
    bluetoothd[14343]: Ignoring (cli) network
    bluetoothd[14343]: Ignoring (cli) input
    bluetoothd[14343]: Ignoring (cli) hog
    bluetoothd[14343]: Ignoring (cli) health
    bluetoothd[14343]: Ignoring (cli) gap
    bluetoothd[14343]: Ignoring (cli) scanparam
    bluetoothd[14343]: Ignoring (cli) deviceinfo
    bluetoothd[14343]: Ignoring (cli) alert
    bluetoothd[14343]: Ignoring (cli) proximity
    bluetoothd[14343]: Ignoring (cli) thermometer
    bluetoothd[14343]: Ignoring (cli) heartrate
    bluetoothd[14343]: Ignoring (cli) cyclingspeed
    bluetoothd[14343]: Ignoring (cli) external_dummy
    bluetoothd[14343]: Bluetooth management interface 1.4 initialized
    bluetoothd[14343]: Failed to obtain handles for "Service Changed" characteristic
    bluetoothd[14343]: Failed to register org.bluez.LEAdvertisingManager1
    bluetoothd[14343]: Failed to register LEAdvertisingManager1 interface for adapter
    bluetoothd[14343]: Not enough free handles to register service
    bluetoothd[14343]: Current Time Service could not be registered
    bluetoothd[14343]: gatt-time-server: Input/output error (5)
    bluetoothd[14343]: hci0 Load Connection Parameters failed: Unknown Command (0x01)

    回覆刪除
  2. I was hoping you could help me with the above errors..
    Great article! thanks!

    回覆刪除
    回覆
    1. 0. Did you modify your bluez code for this result ?
      1. if not, have you used the others gatt services? (wiimote, deviceinfo..and so forth..)
      2. if you modified, please post code you changed.

      刪除
    2. Hello! Have you had a chance to look into this?

      刪除
  3. Hello! Thanks for your response!
    0 - I did not modify the bluez code. I'm using version 5.30, btw
    1 - I tried: time, heartrate, input and health.. they all had the same exact input.

    I am trying this on phidget SBC 3, that helps...

    Kernel Version
    Linux version 3.14.27 (james@james-VirtualBox) (gcc version 4.7.3 (Ubuntu/Linaro 4.7.3-12ubuntu1) ) #2 Fri Jun 12 13:35:01 EDT 2015
    Distribution
    Debian GNU/Linux 7


    Thanks! I look forward to your response!

    回覆刪除
    回覆
    1. Sorry I have not tried to modify bluez in non-x86 based computer. Have you tried it zeroth in your x86 pc ?
      I guess that is driver's issue : the bluetooth driver in phidget SBC 3 does not be updated with time . Maybe you could try to disable the tradition bluetooth mode to see if it works or not.

      刪除
    2. I found a point : do not use Bluez version later than 5.28, those would occur endpoint errors.

      刪除
  4. Hi,
    My setup: Intel Edison ,yocto linux + bluez5.27.

    Error: On Ediosn Side....

    root@edison:~/bluez-5.27# sh script.sh &
    [1] 9692
    + export BLUETOOTH_DEVICE=hci0
    + BLUETOOTH_DEVICE=hci0
    + export OGF=0x08
    + OGF=0x08
    + export OCF=0x0008
    + OCF=0x0008
    + export 'IBEACONPROFIX=1E 02 01 1A 1A FF 4C 00 02 15'
    + IBEACONPROFIX='1E 02 01 1A 1A FF 4C 00 02 15'
    + export 'UUID=4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66'
    + UUID='4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66'
    + export 'MAJOR=00 01'
    + MAJOR='00 01'
    + export 'MINOR=00 00'
    + MINOR='00 00'
    + POWER='C5 00'
    + hciconfig hci0 up
    root@edison:~/bluez-5.27# + hciconfig hci0 noleadv
    LE set advertise enable on hci0 returned status 12
    + hciconfig hci0 noscan
    + hciconfig hci0 pscan
    + hciconfig hci0 leadv
    + hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66 00 01 00 00 C5 00
    < HCI Command: ogf 0x08, ocf 0x0008, plen 32
    1E 02 01 1A 1A FF 4C 00 02 15 4A 4E CE 60 7E B0 11 E4 B4 A9
    08 00 20 0C 9A 66 00 01 00 00 C5 00
    > HCI Event: 0x0e plen 4
    01 08 20 00
    + hcitool -i hci0 cmd 0x08 0x0006 A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
    < HCI Command: ogf 0x08, ocf 0x0006, plen 15
    A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
    > HCI Event: 0x0e plen 4
    01 06 20 0C
    + hcitool -i hci0 cmd 0x08 0x000a 01
    < HCI Command: ogf 0x08, ocf 0x000a, plen 1
    01
    > HCI Event: 0x0e plen 4
    01 0A 20 0C
    + echo complete
    complete
    ^C
    [1]+ Done sh script.sh
    root@edison:~/bluez-5.27# src/bluetoothd --plugin=time -n
    bluetoothd[9701]: Bluetooth daemon 5.27
    bluetoothd[9701]: Starting SDP server
    bluetoothd[9701]: Ignoring (cli) hostname
    bluetoothd[9701]: Ignoring (cli) wiimote
    bluetoothd[9701]: Ignoring (cli) autopair
    bluetoothd[9701]: Ignoring (cli) policy
    bluetoothd[9701]: Ignoring (cli) gatt_example
    bluetoothd[9701]: Ignoring (cli) neard
    bluetoothd[9701]: Ignoring (cli) sap
    bluetoothd[9701]: Ignoring (cli) a2dp
    bluetoothd[9701]: Ignoring (cli) avrcp
    bluetoothd[9701]: Ignoring (cli) network
    bluetoothd[9701]: Ignoring (cli) input
    bluetoothd[9701]: Ignoring (cli) hog
    bluetoothd[9701]: Ignoring (cli) health
    bluetoothd[9701]: Ignoring (cli) gap
    bluetoothd[9701]: Ignoring (cli) scanparam
    bluetoothd[9701]: Ignoring (cli) deviceinfo
    bluetoothd[9701]: Ignoring (cli) alert
    bluetoothd[9701]: Ignoring (cli) proximity
    bluetoothd[9701]: Ignoring (cli) thermometer
    bluetoothd[9701]: Ignoring (cli) heartrate
    bluetoothd[9701]: Ignoring (cli) cyclingspeed
    bluetoothd[9701]: Ignoring (cli) external_dummy
    bluetoothd[9701]: Bluetooth management interface 1.3 initialized
    bluetoothd[9701]: hci0 Load Connection Parameters failed: Unknown Command (0x01)
    bluetoothd[9701]: No agent available for request type 2
    bluetoothd[9701]: device_confirm_passkey: Operation not permitted
    bluetoothd[9701]: No agent available for request type 2
    bluetoothd[9701]: device_confirm_passkey: Operation not permitted
    bluetoothd[9701]: No agent available for request type 2
    bluetoothd[9701]: device_confirm_passkey: Operation not permitted

    On Android App:
    Status: connecting...

    I get pairing request but when I press pair option then "device_confirm_passkey: Operation not permitted" error comes on Edison.

    Please help to solve this issue.
    Thanks

    回覆刪除
    回覆
    1. Watch out this line :

      hcitool -i hci0 cmd 0x08 0x0008 1E 02 01 1A 1A FF 4C 00 02 15 4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66 00 01 00 00 C5 00


      You has set the bluetooth as "dual mode", please refer to this post http://stackoverflow.com/questions/30966837/how-to-set-bluez-mode or my the other post http://gaiger-programming.blogspot.tw/2015/07/bluetooth-low-energy-customizing-gatt.html to set it as BLE only.

      刪除
  5. Also are you using two different terminals to run bluetoothd deamon and script?
    That is not possible with Edison as it is connected via serial port and only 1 serial terminal can be opened.
    How so I solve this?
    Thanks

    回覆刪除
  6. remove the parameter -n, the bluetoothd would run in daemon defaultly.

    回覆刪除
  7. hello
    I hope you find the solution
    I made every thing you made
    but still I have a problem : when I scan by BLE scanner I find the Bluez device but when I connect to it I don`t find any service ??
    thanks in advance.

    回覆刪除
  8. and this error appears:

    (bluetoothd:32357): GLib-CRITICAL **: Source ID 45 was not found when attempting to remove it

    回覆刪除
  9. Hi When I ran sudo ./bluetoothd --plugin=time -n, I got the following log:
    bluetoothd[4818]: Bluetooth daemon 4.99
    bluetoothd[4818]: Starting SDP server
    bluetoothd[4818]: Ignoring (cli) pnat
    bluetoothd[4818]: Ignoring (cli) audio
    bluetoothd[4818]: Ignoring (cli) sap
    bluetoothd[4818]: Ignoring (cli) input
    bluetoothd[4818]: Ignoring (cli) serial
    bluetoothd[4818]: Ignoring (cli) network
    bluetoothd[4818]: Ignoring (cli) proximity
    bluetoothd[4818]: Ignoring (cli) service
    bluetoothd[4818]: Ignoring (cli) gatt_example
    bluetoothd[4818]: Ignoring (cli) alert
    bluetoothd[4818]: Ignoring (cli) health
    bluetoothd[4818]: Ignoring (cli) thermometer
    bluetoothd[4818]: Ignoring (cli) hciops
    bluetoothd[4818]: Ignoring (cli) mgmtops
    bluetoothd[4818]: Ignoring (cli) formfactor
    bluetoothd[4818]: Ignoring (cli) storage
    bluetoothd[4818]: Ignoring (cli) adaptername
    bluetoothd[4818]: Ignoring (cli) wiimote
    bluetoothd[4818]: Ignoring (cli) dbusoob
    bluetoothd[4818]: Failed to init time plugin
    bluetoothd[4818]: adapter_ops_setup failed

    How to resolve the last two errors?

    回覆刪除
  10. My explanation has based on bluetooth version 5.X, but yours is version 4.99.

    回覆刪除
  11. Hi,
    My setup : Raspberry pi 2 + Bluez 5.27 and a bluetooth dongle.

    The problem is when I type 'which bluetoothd' it didn't return anything.
    So I installed bluetooth again by typing the command `sudo apt-get install --no-install-recommends bluetooth`
    It installed bluetooth demon 4.99 only. But I have bluez 5.27. Pretty much confused.

    回覆刪除
  12. see the result now:

    pi@raspberrypi ~/bluez/bluez-5.32/src $ sudo ./bluetoothd --plugin=time -n
    bluetoothd[11651]: Bluetooth daemon 5.32
    bluetoothd[11651]: Starting SDP server
    bluetoothd[11651]: Ignoring (cli) hostname
    bluetoothd[11651]: Ignoring (cli) wiimote
    bluetoothd[11651]: Ignoring (cli) autopair
    bluetoothd[11651]: Ignoring (cli) policy
    bluetoothd[11651]: Ignoring (cli) gatt_example
    bluetoothd[11651]: Ignoring (cli) neard
    bluetoothd[11651]: Ignoring (cli) sap
    bluetoothd[11651]: Ignoring (cli) a2dp
    bluetoothd[11651]: Ignoring (cli) avrcp
    bluetoothd[11651]: Ignoring (cli) network
    bluetoothd[11651]: Ignoring (cli) input
    bluetoothd[11651]: Ignoring (cli) hog
    bluetoothd[11651]: Ignoring (cli) health
    bluetoothd[11651]: Ignoring (cli) gap
    bluetoothd[11651]: Ignoring (cli) scanparam
    bluetoothd[11651]: Ignoring (cli) deviceinfo
    bluetoothd[11651]: Ignoring (cli) alert
    bluetoothd[11651]: Ignoring (cli) proximity
    bluetoothd[11651]: Ignoring (cli) thermometer
    bluetoothd[11651]: Ignoring (cli) heartrate
    bluetoothd[11651]: Ignoring (cli) cyclingspeed
    bluetoothd[11651]: Ignoring (cli) external_dummy
    bluetoothd[11651]: Bluetooth management interface 1.9 initialized
    bluetoothd[11651]: Failed to obtain handles for "Service Changed" characteristic
    bluetoothd[11651]: Not enough free handles to register service
    bluetoothd[11651]: Current Time Service could not be registered
    bluetoothd[11651]: gatt-time-server: Input/output error (5)


    I had to press ctrl+c to terminate this.

    回覆刪除