Côté PC

Installation

$ sudo apt-get install python3-pip libglib2.0-dev
$ sudo pip3 install bluepy

Tests en lignes de commande

Vérification de l’adresse MAC

$ bluetoothctl
[bluetooth]# scan on
Discovery started
[CHG] Device 02:80:E1:00:00:AB RSSI: -89
[bluetooth]# exit

Test avec Gatttool

Gatttool est un utilitaire du driver Bluez

$ gatttool -b 02:80:E1:00:00:AB -I
[02:80:E1:00:00:AB][LE]> connect
Attempting to connect to 02:80:E1:00:00:AB
Connection successful
[02:80:E1:00:00:AB][LE]> primary
attr handle: 0x0001, end grp handle: 0x0004 uuid: 00001801-0000-1000-8000-00805f9b34fb
attr handle: 0x0005, end grp handle: 0x000b uuid: 00001800-0000-1000-8000-00805f9b34fb
attr handle: 0x000c, end grp handle: 0x0011 uuid: 0000ff10-0000-1000-8000-00805f9b34fb
attr handle: 0x0019, end grp handle: 0x001f uuid: 0000181a-0000-1000-8000-00805f9b34fb
[02:80:E1:00:00:AB][LE]> characteristics
handle: 0x0002, char properties: 0x20, char value handle: 0x0003, uuid: 00002a05-0000-1000-8000-00805f9b34fb
handle: 0x0006, char properties: 0x4e, char value handle: 0x0007, uuid: 00002a00-0000-1000-8000-00805f9b34fb
handle: 0x0008, char properties: 0x4e, char value handle: 0x0009, uuid: 00002a01-0000-1000-8000-00805f9b34fb
handle: 0x000a, char properties: 0x0a, char value handle: 0x000b, uuid: 00002a04-0000-1000-8000-00805f9b34fb
handle: 0x000d, char properties: 0x0c, char value handle: 0x000e, uuid: 0000ff12-0000-1000-8000-00805f9b34fb
handle: 0x000f, char properties: 0x12, char value handle: 0x0010, uuid: 0000ff11-0000-1000-8000-00805f9b34fb
handle: 0x001a, char properties: 0x12, char value handle: 0x001b, uuid: 00002a6e-0000-1000-8000-00805f9b34fb
handle: 0x001d, char properties: 0x12, char value handle: 0x001e, uuid: 00002a6d-0000-1000-8000-00805f9b34fb
[02:80:E1:00:00:AB][LE]> char-write-cmd 0x000e 00 # Switch Off Led
[02:80:E1:00:00:AB][LE]> char-write-cmd 0x000e 01 # Switch On led
[02:80:E1:00:00:AB][LE]> char-write-cmd 0x001c 01  # Start Temp and Pressure Measurements
[02:80:E1:00:00:AB][LE]> char-read-hnd 0x001b  # Read Temperature Char
Characteristic value/descriptor: d3 00 
[02:80:E1:00:00:AB][LE]> char-read-hnd 0x001e  # Read Pressure Char
Characteristic value/descriptor: d6 8d 01 00

IHM en Python

$ chmod +x bleiotEnib.py 
$ ./bleiotEnib.py 02:80:e1:00:00:ab

bleiot_python.png

bleiotEnib.py
#! /usr/bin/python3
#=======================================================================
# $ ./bleiotEnib.py  02:80:e1:00:00:ab
#=======================================================================
from bluepy.btle import UUID, Peripheral, ADDR_TYPE_PUBLIC, DefaultDelegate
from tkinter import *
import argparse
import time
import struct
import binascii
#=======================================================================
def STM32_UUID(val):
    return UUID("0000%04X-0000-1000-8000-00805F9B34FB" % val)

ENVIRONMENT_SERVICE_UUID = 0x181A
E_PRESSION_CHAR_UUID    = 0x2A6D
E_TEMPERATURE_CHAR_UUID = 0x2A6E
#E_HUMIDITY_CHAR_UUID    = 0x2A6F

LED_SERVICE_UUID = 0xFF10
LEDSTATE_CHAR_UUID  = 0xFF11
LEDSWITCH_CHAR_UUID = 0xFF12

CCCD_UUID = 0x2902

e_temperature_handle = None
e_pression_handle = None
m_tap_handle = None

ledswitch_handle = None
ledstate_handle = None

ledstate = 0
data_temperature=0
data_state=0
data_pression=0

#=======================================================================
#                LED Service
#=======================================================================
class LedService():
    serviceUUID = STM32_UUID(LED_SERVICE_UUID )
    ledswitch_char_uuid = STM32_UUID(LEDSWITCH_CHAR_UUID)
    ledstate_char_uuid = STM32_UUID(LEDSTATE_CHAR_UUID)
    
    def __init__(self, periph):
        self.periph = periph
        self.led_service = None
        self.ledswitch_char = None
        self.ledswitch_cccd = None
        self.ledstate_char = None
        self.ledstate_cccd = None
	
    def enable(self):
        """ Enables the class by finding the service and its characteristics. """
        global ledswitch_handle
        global ledstate_handle

        if self.led_service is None:
            self.led_service = self.periph.getServiceByUUID(self.serviceUUID)
        if self.ledswitch_char is None:
            self.ledswitch_char = self.led_service.getCharacteristics(self.ledswitch_char_uuid)[0]
            ledswitch_handle = self.ledswitch_char.getHandle()     
        if self.ledstate_char is None:
            self.ledstate_char = self.led_service.getCharacteristics(self.ledstate_char_uuid)[0]
            ledstate_handle = self.ledstate_char.getHandle()
            self.ledstate_cccd = self.ledstate_char.getDescriptors(forUUID=CCCD_UUID)[0]

    def set_ledswitch_notification(self, state):
        if self.ledswitch_cccd is not None:
            if state == True:
                self.ledswitch_cccd.write(b"\x01\x00", True)
            else:
                self.ledswitch_cccd.write(b"\x00\x00", True)

    def set_ledstate_notification(self, state):
        if self.ledstate_cccd is not None:
            if state == True:
                self.ledstate_cccd.write(b"\x01\x00", True)
            else:
                self.ledstate_cccd.write(b"\x00\x00", True)
                
    def set_switch_led_on(self):
           self.ledswitch_char.write(b"\x01", True)             

    def set_switch_led_off(self):
           self.ledswitch_char.write(b"\x00", True)  

    def disable(self):
        set_ledswitch_notification(False)
        set_ledstate_notification(False)

#=======================================================================
#            Environment Service Class
#=======================================================================
class EnvironmentService():
    serviceUUID =           STM32_UUID(ENVIRONMENT_SERVICE_UUID)
    temperature_char_uuid = STM32_UUID(E_TEMPERATURE_CHAR_UUID)
    pression_char_uuid =    STM32_UUID(E_PRESSION_CHAR_UUID)

    def __init__(self, periph):
        self.periph = periph
        self.environment_service = None
        self.temperature_char = None
        self.temperature_cccd = None
        self.pression_char = None
        self.pression_cccd = None

    def enable(self):
        """ Enables the class by finding the service and its characteristics. """
        global e_temperature_handle
        global e_pression_handle

        if self.environment_service is None:
            self.environment_service = self.periph.getServiceByUUID(self.serviceUUID)
        if self.temperature_char is None:
            self.temperature_char = self.environment_service.getCharacteristics(self.temperature_char_uuid)[0]
            e_temperature_handle = self.temperature_char.getHandle()
            self.temperature_cccd = self.temperature_char.getDescriptors(forUUID=CCCD_UUID)[0]
        if self.pression_char is None:
            self.pression_char = self.environment_service.getCharacteristics(self.pression_char_uuid)[0]
            e_pression_handle = self.pression_char.getHandle()
            self.pression_cccd = self.pression_char.getDescriptors(forUUID=CCCD_UUID)[0]

    def set_temperature_notification(self, state):
        if self.temperature_cccd is not None:
            if state == True:
                print("temperature write")				
                self.temperature_cccd.write(b"\x01\x00", True)
            else:
                self.temperature_cccd.write(b"\x00\x00", True)

    def set_pression_notification(self, state):
        if self.pression_cccd is not None:
            if state == True:
                print("pression write")
                self.pression_cccd.write(b"\x01\x00", True)
            else:
                self.pression_cccd.write(b"\x00\x00", True)

    def disable(self):
        set_temperature_notification(False)
        set_pression_notification(False)

#=======================================================================
#                MyDelegate Class
#=======================================================================
class MyDelegate(DefaultDelegate):
    
    def handleNotification(self, hnd, data):
        global data_pression,data_temperature, data_state
        if (hnd == e_temperature_handle): 
            concat_dat = binascii.b2a_hex(data[::-1])	# [::-1] reverse the string
            temp_f = float(int(concat_dat,16))/10                       
            print("Temp received:" + str(temp_f))
            data_temperature = temp_f              
            
        elif (hnd == e_pression_handle):        
            concat_dat = binascii.b2a_hex(data[::-1])
            pression_f = float(int(concat_dat,16))/100  
            print("Pression received:" + str(pression_f))
            data_pression = pression_f

        elif (hnd == ledstate_handle):   
            data_state = data

#=======================================================================
#                Bleiot Class
#=======================================================================
class Bleiot(Peripheral):
    """
    Bleiot module. Instance the class and enable to get access to the Bleiot Sensors.
    The addr of your device has to be know, or can be found by using the hcitool command line 
    tool, for example. Call "> sudo hcitool lescan" and your bleiot's address should show up.
    """
    def __init__(self, addr):
        Peripheral.__init__(self, addr, addrType=ADDR_TYPE_PUBLIC)
        self.environment = EnvironmentService(self)
        self.led = LedService(self)

#=======================================================================
#                Functions
#=======================================================================
def askFordatas():
    bleiot.waitForNotifications(args.t)
    strTemperature.set(str(data_temperature) + ' °C')
    strPression.set(str(data_pression) + ' hPa')   
    strLedState.set(str(data_state) + ' --- ' + time.ctime())  
    ui.after(1,askFordatas)
    
def onPushSwitch():
    global ledstate
    if ledstate == 0:
        print("switch on")
        bleiot.led.set_switch_led_on()
        ledstate = 1
    else:
        print("switch off")
        bleiot.led.set_switch_led_off()
        ledstate = 0
        
#=======================================================================
#                Main Function
#=======================================================================
if  __name__ == "__main__" :
    parser = argparse.ArgumentParser()
    parser.add_argument('mac_address', action='store', help='MAC address of BLE peripheral')
    parser.add_argument('-n', action='store', dest='count', default=0, type=int, help="Number of times to loop data")
    parser.add_argument('-t',action='store',type=float, default=0.1, help='time between polling')
    
    args = parser.parse_args()

    print('Connecting to ' + args.mac_address)
    bleiot = Bleiot(args.mac_address)
    print('Connected...')
    bleiot.setDelegate(MyDelegate())
    
    print('Enabling selected sensors...')   
    bleiot.environment.enable()
    bleiot.environment.set_temperature_notification(True)
    bleiot.environment.enable()
    bleiot.environment.set_pression_notification(True)       
    bleiot.led.enable()
    bleiot.led.set_ledstate_notification(True)    
    
    print('All requested sensors and notifications are enabled...')
    time.sleep(1.0)    
    #-------------------------------------------------------------------
    ui=Tk()
    ui.title("BLEIOT")
    ui.geometry("200x200")

    labelTemperature=Label(ui, text="Temperature", font=("Arial", 10), fg="black")
    labelTemperature.pack()
 
    strTemperature = StringVar()
    TemperatureEntry=Entry(ui, textvariable=strTemperature)
    TemperatureEntry.pack()
    
    labelPression=Label(ui, text="Pression", font=("Arial", 10), fg="black")
    labelPression.pack()
 
    strPression=StringVar()
    PressionEntry=Entry(ui, textvariable=strPression)
    PressionEntry.pack()
    
    switchButton=Button(ui, text="SWITCH LED", font=("Arial",10, "bold"), bg="seagreen3", fg="black", bd=3, relief=RAISED, command=onPushSwitch)
    switchButton.pack()
    
    strLedState=StringVar()
    strLedEntry=Entry(ui, textvariable=strLedState)
    strLedEntry.pack() 
    
    ui.after(1,askFordatas)
    
    ui.mainloop()  
	
#=======================================================================


Interface en C

Installation

gattlib

test

$ ./bleiotEnib 02:80:e1:00:00:ab
switch led handle = 14 
state led handle = 16 
temperature handle = 27 
pressure handle = 30 
Notification uuid = fb349b5f-8000-0080-0010-00006e2a0000 value: 1980
Notification uuid = fb349b5f-8000-0080-0010-00006e2a0000 value: 23814110
Notification uuid = fb349b5f-8000-0080-0010-00006e2a0000 value: 1980
Notification uuid = fb349b5f-8000-0080-0010-00006e2a0000 value: 23814110
Notification uuid = fb349b5f-8000-0080-0010-00006e2a0000 value: 1980
...

bleiotEnib.c

bleiotEnib.c
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "gattlib.h"

#define ENV_CHAR_PRESSURE		"00002a6d-0000-1000-8000-00805f9b34fb"
#define ENV_CHAR_TEMPERATURE	"00002a6e-0000-1000-8000-00805f9b34fb"
#define LED_CHAR_SWITCH			"0000ff12-0000-1000-8000-00805f9b34fb"
#define LED_CHAR_STATE			"0000ff11-0000-1000-8000-00805f9b34fb"

gatt_connection_t* m_connection;
static int opt_handle = -1;
char buffer[10];

uuid_t env_char_temperature_uuid;
uuid_t env_char_pressure_uuid;
uuid_t led_char_switch_uuid;
uuid_t led_char_state_uuid;

uuid_t g_uuid;

//======================================================================
void notification_cb(const uuid_t* uuid, const uint8_t* data, size_t data_length, void* user_data) {
	
	char uuid_str[MAX_LEN_UUID_STR + 1];
	int i;

	gattlib_uuid_to_string(uuid, uuid_str, MAX_LEN_UUID_STR + 1);
	printf("Notification uuid = %s value: ", uuid_str);

	for (i = 0; i < data_length; i++) {
		printf("%d", data[i]);
	}
	printf("\n");
}
//======================================================================
static void usage(char *argv[]) {
	printf("%s <device_address>\n", argv[0]);
}
//======================================================================
void int_handler(int dummy) {
	gattlib_disconnect(m_connection);
	exit(0);
}
//======================================================================
gatt_read_cb_t read_cb(void) {
}
//======================================================================
int main(int argc, char *argv[]) {
	char input[256];
	char* input_ptr;
	uint16_t temperature_handle = 0, pressure_handle = 0, switchLed_handle = 0, stateLed_handle=0;
	int i, ret, total_length, length = 0;
	void* adapter;

	if (argc != 2) {
		usage(argv);
		return 1;
	}
	
	ret = gattlib_adapter_open(NULL, &adapter);
			if (ret) {
				fprintf(stderr, "ERROR: Failed to open adapter.\n");
				return 1;
			}

	m_connection = gattlib_connect(adapter, argv[1], BDADDR_LE_PUBLIC, BT_SEC_LOW, 0, 0);
					if (m_connection == NULL) {
						fprintf(stderr, "Fail to connect to the bluetooth device.\n");
						return 1;
					}

	// Convert characteristics to their respective UUIDs
	ret = gattlib_string_to_uuid(ENV_CHAR_TEMPERATURE, strlen(ENV_CHAR_TEMPERATURE) + 1, &env_char_temperature_uuid);
	ret = gattlib_string_to_uuid(ENV_CHAR_PRESSURE, strlen(ENV_CHAR_PRESSURE) + 1, &env_char_pressure_uuid);
	ret = gattlib_string_to_uuid(LED_CHAR_SWITCH, strlen(LED_CHAR_SWITCH) + 1, &led_char_switch_uuid);
	ret = gattlib_string_to_uuid(LED_CHAR_STATE, strlen(LED_CHAR_STATE) + 1, &led_char_state_uuid);

	// Look for handle for CHARACTERISTIC_UUID
	gattlib_characteristic_t* characteristics;
	int characteristic_count;
	ret = gattlib_discover_char(m_connection, &characteristics, &characteristic_count);
					if (ret) {
						fprintf(stderr, "Fail to discover characteristic.\n");
						return 1;
					}
	
	for (i = 0; i < characteristic_count; i++) {
		if (gattlib_uuid_cmp(&characteristics[i].uuid, &env_char_temperature_uuid) == 0) 
		{
			temperature_handle = characteristics[i].value_handle;
			printf("temperature handle = %d \n", temperature_handle);
		} 
		else if (gattlib_uuid_cmp(&characteristics[i].uuid, &env_char_pressure_uuid) == 0) 
		{
			pressure_handle = characteristics[i].value_handle;
			printf("pressure handle = %d \n", pressure_handle);
		}
		else if (gattlib_uuid_cmp(&characteristics[i].uuid, &led_char_switch_uuid) == 0) 
		{
			switchLed_handle = characteristics[i].value_handle;		
			printf("switch led handle = %d \n", switchLed_handle);
		}	
		else if (gattlib_uuid_cmp(&characteristics[i].uuid, &led_char_state_uuid) == 0) 
		{
			stateLed_handle = characteristics[i].value_handle;		
			printf("state led handle = %d \n", stateLed_handle);
		}			
	}
	free(characteristics);

	// Enable Status Notification
	uint16_t enable_notification = 0x0001;
	gattlib_write_char_by_handle(m_connection, temperature_handle + 1, &enable_notification, sizeof(enable_notification));	
	sleep(1);	
	gattlib_write_char_by_handle(m_connection, pressure_handle + 1, &enable_notification, sizeof(enable_notification));	
	sleep(1);	
	gattlib_write_char_by_handle(m_connection, stateLed_handle +1, &enable_notification, sizeof(enable_notification));
	sleep(1);	
	
	// Register notification handler
	gattlib_register_notification(m_connection, notification_cb, NULL);

	signal(SIGINT, int_handler);		// Register handler to catch Ctrl+C

	while(1) {
		/*enable_notification = 0x0001;
		gattlib_write_char_by_handle(m_connection, switchLed_handle + 1, &enable_notification, sizeof(enable_notification));		
		sleep(1);
		enable_notification = 0x0000;
		gattlib_write_char_by_handle(m_connection,switchLed_handle + 1, &enable_notification, sizeof(enable_notification));
		sleep(1);	
		*/		
	}

	return 0;
}
//======================================================================