vendredi 17 septembre 2010

Utiliser le profil bluetooth serial port profile (SPP) sur votre iphone

Introduction :

Actuellement l'iPhone utilise les profils suivants : Profil mains-libres (HFP 1.5), Profil d’accès à l’annuaire téléphonique (PBAP), Profil de distribution audio avancée (A2DP), Profil de contrôle à distance audio/vidéo (AVRCP), Profil de réseau personnel (PAN) et Profil de périphériqued’interface utilisateur (HID).

Il n'utilise donc pas de profil série. Cependant, il existe une librairie open-source qui permet de rendre le bluetooth de l'iPhone compatible avec ce profil.

Nous allons donc voir comment utiliser et intégrer le profil SPP dans une application Xcode. Pour cela, on va se fixer un objectif simple : se connecter en bluetooth via le profil SPP.

Pré-requis :

- Un iphone jailbreaké
- Les lirairies btstack qui sont disponibles ici
- Xcode
- Dans cydia, recherchez la librairie "btstack" et installez la. 

1ère étape :

On va commencer pas créer un projet sous Xcode que l'on appellera SPPbluetooth.
Nous allons ensuite créer un dossier Plugins. Dans celui-ci, nous mettrons le dossier btstack ainsi que libBTstack.dylib. Nous allons ajouter ce dossier dans notre projet. Pour cela, clique droit sur le nom de notre projet puis Add Existing Files, on prend le dossier Plugins et on valide (Add).
Dans le dossier "Classes" nous ajouterons RFCOMMLayer.h et RFCOMMLayer.c. Même méthode que précédemment, clique droit sur "Classes" puis "Add Existing Files".
Si tout c'est bien passé vous vous retrouvez dans cette configuration :



2ème étape :

On va dans "Projet" puis "Edit" et "Project Settings". Dans l'onglet "build", on sélectionne dans configuration "All Configurations" puis on cherche Other C Flags.
Une fois trouvé, on entre dans sa case : -l/chemin_de_votre_projet/nom_de_votre_projet/Plugins
Puis on valide en faisant ok.

On peut maintenant compiler pour vérifier si il y a aucune erreur. Pour cela, on doit sélectionner dans "Overview" Device et non Simulateur. Un message devrait apparaître comme quoi l'iPhone n'est pas connecté ce qui est normal !

On est quasiment prêt à coder mais avant on va essayer de comprendre un minimum du code !

3ème étape :

On va se baser sur le fichier rfcomm.c : http://btstack.googlecode.com/svn/trunk/example/rfcomm.c et s'intéresser au lignes importantes.

bd_addr_t addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};  Il faudra entrer l'adresse MAC de notre appareil.
int RFCOMM_CHANNEL_ID = 1; Nous utiliserons le channel 1.
char PIN[] = "0000"; Pour simplifier les choses, je n'utiliserai pas de mot de passe pour la connexion. Ici il est initialisé avec quatre '0'.
case L2CAP_DATA_PACKET: C'est dans cette partie qu'aura lieu l'envoie et la réception de données. Durant la connexion bluetooth,  le maître et l'esclave s'échangent pas mal de données avant qu'ils soient totalement appairés.
case HCI_EVENT_PACKET: Dans cette partie sont gérées la connexion, la déconnexion et les erreurs de ces dernières.
bt_send_cmd(&hci_write_authentication_enable, 0); Étant donnée que l'on n'utilise pas de mot de passe, nous mettons l'authentification à '0'.
bt_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, PIN); Si on utilise un mot de passe cette ligne est requise. Elle envoie le code PIN à l'appareil. 4 représente le nombre de chiffres utilisés (0000).
bt_send_cmd(&l2cap_create_channel, addr, 0x03); On se connecte en utilisant le profil SPP ce dernier correspond au code hexadecimal 0x03.
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON ); On active le bluetooth de notre téléphone et on affiche l'icône du bluetooth.
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_OFF); Comme vous l'avez compris on désactive le bluetooth de notre téléphone et on retire l'icône.
bt_close(); On ferme la connexion

4ème étape :


Bon maintenant on se lance ! Nous allons transformer se fichier source en un fichier compatible iPhone. Nous allons donc travailler dans le fichier SPPbluetoothAppDelegate.

On commence par ajouter les déclarations :

#import "SPPbluetoothAppDelegate.h"
#import "SPPbluetoothViewController.h"
#import "RFCOMMLayer.h"

hci_con_handle_t ConHandle = 0;

@implementation SPPbluetoothAppDelegate
@synthesize window;
@synthesize viewController;

bd_addr_t addr = {0x00, 0x80, 0x98, 0xea, 0x25, 0x3e}; 
int RFCOMM_CHANNEL_ID = 1;
//char PIN[] = "0000"; je n'utilise pas de mot de passe
int DEBUG = 0;
hci_con_handle_t con_handle;
uint16_t source_cid;
int fifo_fd;


Puis la fonction qui gère la transmission des paquets (maitre-esclave) :

void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
bd_addr_t event_addr;
static uint8_t msc_resp_send = 0;
static uint8_t msc_resp_received = 0;
static uint8_t credits_used = 0;
static uint8_t credits_free = 0;
uint8_t packet_processed = 0;
switch (packet_type) {
case L2CAP_DATA_PACKET:
//      received 1. message BT_RF_COMM_UA
if (size == 4 && packet[1] == BT_RFCOMM_UA && packet[0] == 0x03){
packet_processed++;
_bt_rfcomm_send_uih_pn_command(source_cid, 1, RFCOMM_CHANNEL_ID, 100);
}
//  received UIH Parameter Negotiation Response
if (size == 14 && packet[1] == BT_RFCOMM_UIH && packet[3] == BT_RFCOMM_PN_RSP){
packet_processed++;
_bt_rfcomm_send_sabm(source_cid, 1, RFCOMM_CHANNEL_ID);
}
// received 2. message BT_RF_COMM_UA
if (size == 4 && packet[1] == BT_RFCOMM_UA && packet[0] == ((RFCOMM_CHANNEL_ID << 3) | 3) ){
packet_processed++;
_bt_rfcomm_send_uih_msc_cmd(source_cid, 1, RFCOMM_CHANNEL_ID, 0x8d);  
}
// received BT_RFCOMM_MSC_CMD
if (size == 8 && packet[1] == BT_RFCOMM_UIH && packet[3] == BT_RFCOMM_MSC_CMD){
packet_processed++;
uint8_t address = packet[0] | 2; // set response 
packet[3]  = BT_RFCOMM_MSC_RSP;  //  "      "
rfcomm_send_packet(source_cid, address, BT_RFCOMM_UIH, 0x30, (uint8_t*)&packet[3], 4);
msc_resp_send = 1;
}
// received BT_RFCOMM_MSC_RSP
if (size == 8 && packet[1] == BT_RFCOMM_UIH && packet[3] == BT_RFCOMM_MSC_RSP){
packet_processed++;
msc_resp_received = 1;
}
uint8_t send_credits_packet = 0;
if (credits_used >= 0x30 ) {
send_credits_packet = 1;
credits_used -= 0x30;
}
if (msc_resp_send && msc_resp_received) {
send_credits_packet = 1;
msc_resp_send = msc_resp_received = 0;
}
if (send_credits_packet) {
uint8_t initiator = 1;
                uint8_t address = (1 << 0) | (initiator << 1) |  (initiator << 1) | (RFCOMM_CHANNEL_ID << 3); 
rfcomm_send_packet(source_cid, address, BT_RFCOMM_UIH_PF, 0x30, NULL, 0);
}
if (!packet_processed){
hexdump( packet, size );
}
break;
case HCI_EVENT_PACKET:
switch (packet[0]) {
case BTSTACK_EVENT_STATE:
if (packet[2] == HCI_STATE_WORKING) {
bt_send_cmd(&hci_write_local_name, "SPP");
}
break;
case BTSTACK_EVENT_POWERON_FAILED:
exit(1);
break;          
case HCI_EVENT_LINK_KEY_REQUEST:
bt_flip_addr(event_addr, &packet[2]); 
bt_send_cmd(&hci_link_key_request_negative_reply, &event_addr);
break;
/* case HCI_EVENT_PIN_CODE_REQUEST: je n'utilise pas de mot de passe
bt_flip_addr(event_addr, &packet[2]); 
bt_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, PIN);
printf("Please enter PIN %s on remote device\n", PIN);
break;*/
case L2CAP_EVENT_CHANNEL_OPENED:
bt_flip_addr(event_addr, &packet[3]);
uint16_t psm = READ_BT_16(packet, 11); 
source_cid = READ_BT_16(packet, 13); 
con_handle = READ_BT_16(packet, 9);
if (packet[2] == 0) {
_bt_rfcomm_send_sabm(source_cid, 1, 0);
} else {
exit(1);
}
break;
case HCI_EVENT_DISCONNECTION_COMPLETE:
exit(0);
break;
case HCI_EVENT_COMMAND_COMPLETE:
if ( COMMAND_COMPLETE_EVENT(packet, hci_write_local_name) ) {
bt_send_cmd(&hci_write_authentication_enable, 0);
}
if ( COMMAND_COMPLETE_EVENT(packet, hci_write_authentication_enable) )
                                        {
bt_send_cmd(&l2cap_create_channel, addr, 0x03);
}
break;
default:
// unhandled event
if(DEBUG) printf("unhandled event : %02x\n", packet[0]);
break;
}
break;
default:

if(DEBUG) printf("unhandled packet type : %02x\n", packet_type);
break;
}
}

Rendez-vous ensuite dans applicationDidFinishLaunching pour activer la connexion au lancement de l'application  :


- (void)applicationDidFinishLaunching:(UIApplication *)application {    
    
bool btOK = false;
run_loop_init(RUN_LOOP_COCOA);
if (bt_open()){
UIAlertView* alertView = [[UIAlertView alloc] init];
alertView.title = @"Problème Bluetooth";
alertView.message = @"Connection to BTstack failed!\n"
"Please make sure that BTstack is installed correctly.";
[alertView addButtonWithTitle:@"Dismiss"];
[alertView show];
} else {
bt_register_packet_handler(packet_handler);
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_ON);
btOK = true;
}
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setBackgroundColor:[UIColor clearColor]]; 
UIView *view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame]; 
view.backgroundColor = [UIColor clearColor];
[window addSubview: view];
        [window makeKeyAndVisible];
}

Et pour finir on gère la déconnexion lorsque l'application se termine :



- (void)applicationWillTerminate:(UIApplication *)application {
if (ConHandle) {
bt_send_cmd(&hci_disconnect, ConHandle, 0x03);       
}
bt_send_cmd(&btstack_set_power_mode, HCI_POWER_OFF );
bt_close();
}




Voila on vient de réaliser une application très simple qui se connecte en bluetooth via le profil SPP.
Vous pouvez telecharger le projet SPPbluetooth en zip ici

Pour plus d'informations vous pouvez toujours consulter : http://code.google.com/p/btstack



Aucun commentaire:

Enregistrer un commentaire