2015年1月15日 星期四

Dbus : simple sender and receiver


No tedious words, I post code directly:

sender:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dbus/dbus.h>

#define LOCAL      static 

#ifndef TRUE
 #define TRUE      1
 #define FALSE      0
#endif
 
LOCAL void SendData(DBusConnection *connection, char *pData)
{
 DBusMessage *dbmsg;
 DBusPendingCall *pending;
 DBusMessage* reply;
 
 int len;
 
 dbmsg = NULL; pending = NULL; reply = NULL;
 
 dbmsg = dbus_message_new_signal ("/org/share/linux","org.share.linux", "Customize");
  
 len = strlen(pData);
 
    if(FALSE == dbus_message_append_args(dbmsg, 
    DBUS_TYPE_STRING, &pData, 
    DBUS_TYPE_INT32,  &len, 
    DBUS_TYPE_INVALID))
  return;
 
 printf("Message cutomize sending : %s , length = %d\n", pData, len);
 /* Send the signal */
 
    dbus_connection_send(connection, dbmsg, NULL);

 if(NULL != dbmsg)
  dbus_message_unref(dbmsg);
 if(NULL != pending)
  dbus_pending_call_unref(pending);
 
 if(NULL != pending) 
  dbus_message_unref(reply);
  
}/*SendConfig*/

LOCAL void SendQuit(DBusConnection *connection)
{

 DBusMessage *message;

 message = dbus_message_new_signal ("/org/share/linux",

 "org.share.linux",

 "Quit");

 /* Send the signal */

 dbus_connection_send (connection, message, NULL);
 dbus_message_unref (message);
}/*SendQuit*/


int main (int argc, char **argv)
{
 DBusConnection *connection;
 DBusError error;

 dbus_error_init (&error);
 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);

 if(NULL == connection)
 {
  printf ("Failed to connect to the D-BUS daemon: %s", error.message);
  dbus_error_free (&error);
  return 1;
 }

 if( argc == 1)
 {
  printf("errror : no argument !\n");
  return 0;
 }/*if*/
 int i;

 for ( i = 1; i < argc; i++)
 {
 
  if (0 == strncmp(argv[i],"-c", 256) )
  {
   if(i + 1 < argc) 
    SendData(connection, argv[i + 1]);
   else
    SendData(connection, "test string"); 
  }
  else if ( 0 == strncmp(argv[i],"-q", 256) )
  {
   SendQuit(connection);
  }/*if*/
 }
 
 return 0;
}/*main*/

receiver.c :


#include <stdio.h>

#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <glib.h>

#ifndef TRUE
 #define TRUE      1
 #define FALSE      0
#endif

#define LOCAL      static 

LOCAL DBusHandlerResult dbus_filter(DBusConnection *connection, 

                    DBusMessage *message, void *user_data)
{
 if (FALSE != dbus_message_is_signal(message,"org.share.linux","Customize" ) )
 {
  DBusError dberr;
  char *pData;
  int len;
  
  pData = NULL;
  len = 0;
  dbus_error_init(&dberr);
  
  dbus_message_get_args(message, &dberr, 
   DBUS_TYPE_STRING, &pData,
   DBUS_TYPE_INT32 , &len, 
   DBUS_TYPE_INVALID);
  
  if(FALSE != dbus_error_is_set(&dberr)) 
  {
   printf("__LINE__ = %d\n", __LINE__);
   dbus_error_free(&dberr);       
  }
  else
  {
   printf("Message cutomize received : %s , length = %d\n", pData, len);
   return DBUS_HANDLER_RESULT_HANDLED;
  }
 }/*if*/
 

 if( FALSE != dbus_message_is_signal(message,"org.share.linux","Quit" ) )
 {
  printf("Message quit received\n");

  GMainLoop *loop = (GMainLoop*) user_data;
  g_main_loop_quit(loop);
  
  return DBUS_HANDLER_RESULT_HANDLED;
 }

 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}/*dbus_filter*/


int main(int argc, char *argv[])
{
 DBusConnection *connection;
 DBusError error;

 /* glib main loop */

 GMainLoop *loop;

 loop = g_main_loop_new(NULL,FALSE);

 dbus_error_init(&error);

 connection = dbus_bus_get(DBUS_BUS_SESSION, &error);

 if ( dbus_error_is_set(&error) )
 {
  printf("Error connecting to the daemon bus: %s",error.message);

  dbus_error_free(&error);
  return 1;
 }

 dbus_bus_add_match (connection,

 "type='signal',interface='org.share.linux'",NULL);

 dbus_connection_add_filter (connection, dbus_filter, loop, NULL);

 /* dbus-glib call */

 dbus_connection_setup_with_g_main(connection,NULL);

 /* run glib main loop */

 printf("wait receiving data:\n");
 g_main_loop_run(loop);

 return 0;
}/*main*/


Makefile :


CC := gcc
CFLAGS := -g
BITS_PATH  := x86_64-linux-gnu
#BITS_PATH := i386-linux-gnu

INC := -I/usr/include/dbus-1.0 -I/usr/lib/$(BITS_PATH)/dbus-1.0/include
INC += -I/usr/include/glib-2.0 -I/usr/lib/$(BITS_PATH)/glib-2.0/include
LIB :=  -L/usr/lib/$(BITS_PATH) -ldbus-1  -ldbus-glib-1

all:
        $(CC) $(CFLAGS)  receiver.c -o receiver $(INC) $(LIB)   
        $(CC) $(CFLAGS)  sender.c -o sender $(INC) $(LIB)
clean:
        rm -f sender receiver 

To run the 2 examples, you should build it first.
 And open a terminal and type:


gaiger@i5-3210m:~/this_working_folder$ ./receiver 
wait receiving data:

Then open another terminal and type:

gaiger@i5-3210m:~/sandbox/dbus$ ./sender -c AA
Message cutomize sending : AA , length = 2

You would find the string(AA) has been sent to the receiver.

TODO:  If you want a warning in the sender when the sending has fail (for example: the receiver program is not under running), you should use dbus_connection_send_with_reply to replace dbus_connection_send.

2015年1月11日 星期日

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.


2015年1月7日 星期三

BLE in Linux Zero : How to Set an iBeacon

The setting of Bluetooth low energy  makes persons confused.
In there  , I organize the bluetooth low energy in linux.


There are lots blog/discussing about this (like http://stackoverflow.com/questions/26853011/broadcasting-message-in-bluetooth-low-energy-mode )



First, your linux distribution should be installed bluez package.
Most have been done.

We would use two commandline tools:
hcitool and hciconfig.

hciconfig  could inquiry/set devices (bluetooth card in local computer)

like :

hiconfig
hci0:    Type: BR/EDR  Bus: USB
    BD Address: 74:E5:43:96:87:A2  ACL MTU: 1021:8  SCO MTU: 64:1
    DOWN 
    RX bytes:196507 acl:2915 sco:0 events:9293 errors:0
    TX bytes:166857 acl:3037 sco:0 commands:6061 errors:0


the hci0 is the device symbol for bluez.
If you have more then one bluetooth device, that would be hci0, hci1...etc.

There is a "DOWN" flag to note this device being disable.
 We should use
sudo hciconfig hci0 up     : to be on working
sudo hciconfig hci psan  : to be scanable (by the other device, like android phone)

 sudo hciconfig hci0 up

 sudo hciconfig hci0 pscan

 hciconfig
hci0:    Type: BR/EDR  Bus: USB
    BD Address: 74:E5:43:96:87:A2  ACL MTU: 1021:8  SCO MTU: 64:1
    UP RUNNING PSCAN 
    RX bytes:197113 acl:2915 sco:0 events:9329 errors:0
    TX bytes:167553 acl:3037 sco:0 commands:6097 errors:0




and also, hciconfig hci0 noscan : stop to be scanable.
hciconfig hci noleadv  : stop ble advertising.


hcitool could set  what data to advertise and the advertise frequency.
(hcitool -i cmd ....)

The maximum data length be 31 bytes,  but availalbe is 29 byte only( 2 bytes for length and type ) 

hcitool should start as 0x08 and 0x0008, for
#OGF = Operation Group Field = Bluetooth Command Group = 0x08
#OCF = Operation Command Field = HCI_LE_Set_Advertising_Data = 0x0008

the ibeacon(apple's) format be :

 <IBEACONPROFIX> <UUID> <MAJOR> <MINOR> <POWER>

Here IBEACONPROFIX are number for identifying apple's format.

IBEACONPROFIX = 1E 02 01 1A 1A FF 4C 00 02 15

UUID: 16 byte data, you could use uuidgen this commandline tool to get this unique number.
 MAJOR, MINOR and POWER : 0000 to ffff
  
Totally, the ibeacon script be :

#!/bin/bash
# ref: http://www.theregister.co.uk/Print/2013/11/29/feature_diy_apple_ibeacons/
set -x
# inquiry local bluetooth device
#hcitool dev
export BLUETOOTH_DEVICE=hci0
#sudo hcitool -i hcix cmd <OGF> <OCF> <No. Significant Data Octets> <iBeacon Prefix> <UUID> <Major> <Minor> <Tx Power> <Placeholder Octets>

#OGF = Operation Group Field = Bluetooth Command Group = 0x08
#OCF = Operation Command Field = HCI_LE_Set_Advertising_Data = 0x0008
#No. Significant Data Octets (Max of 31) = 1E (Decimal 30)
#iBeacon Prefix (Always Fixed) = 02 01 1A 1A FF 4C 00 02 15

export OGF="0x08"
export OCF="0x0008"
export IBEACONPROFIX="1E 02 01 1A 1A FF 4C 00 02 15"

#uuidgen  could gerenate uuid
export UUID="4a 4e ce 60 7e b0 11 e4 b4 a9 08 00 20 0c 9a 66"
#export UUID="B9 40 7F 30 F5 F8 46 6E AF F9 25 55 6B 57 FE 6D"
#export UUID="76 E8 B4 E0 7E B5 11 E4 B4 A9 08 00 20 0C 9A 66"
export MAJOR="00 01"
export MINOR="00 00"
export POWER="C5 00"


# initialize device
sudo hciconfig $BLUETOOTH_DEVICE up
# disable advertising
sudo hciconfig $BLUETOOTH_DEVICE noleadv
# stop the dongle looking for other Bluetooth devices
sudo hciconfig $BLUETOOTH_DEVICE noscan

sudo hciconfig $BLUETOOTH_DEVICE pscan


sudo hciconfig $BLUETOOTH_DEVICE leadv

# advertise
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x0008 $IBEACONPROFIX $UUID $MAJOR $MINOR $POWER
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x0006 A0 00 A0 00 00 00 00 00 00 00 00 00 00 07 00
sudo hcitool -i $BLUETOOTH_DEVICE cmd 0x08 0x000a 01

echo "complete"

 hcitool cmd 0x08 0x0006 A0 00 A0 00 ...

 the first two bytes(A0 00) is "min interval"

And the 2nd two bytes (A0 A0 ) is "Max interval"

 A0 00, be 0x00A0(big endian), it is 160 in decimal.

The granularity of ble is 0.625ms. 

Here, min and max interval are 160* 625ms = 100ms both.


(ref : http://stackoverflow.com/questions/21124993/is-there-a-way-to-increase-ble-advertisement-frequency-in-bluez)

Then,  you could use android app to detect the ibeacon exist.