mercredi 29 septembre 2010

Plugwise on Linux !

Introduction :

Plugwise utilise un logiciel, pour installer et utilises ses prises, qui fonctionne uniquement sous windows. Pourtant, une version open-source a été réalisée sous Linux pour commander ses prises. Voici l'auteur de ce travail : http://www.maartendamen.com/category/pol-plugwise-on-linux/

Cependant, cette version demande encore l'usage de Windows pour l'installation : appairer le stick USB avec la prise maître et la prise maître avec les prises esclave. Je vais donc m'aider du code de Maarten Damen et tenter de réaliser l'appairage sous Linux.

1ère étape :


D'après la documentation technique de Maarten Damen, il y a un jeu de questions-réponses entre les prises et le stick USB. Ainsi dés lors ou on envoi un message on a une réponse. Ce message est en fait une trame codée toujours de la même façon.

Cette trame se constitue de la manière suivante :
\x05\x05\x03\x030017000D6F000037B1E5007BAA\x0d\x0a
soit décomposée \x05\x05\x03\x03 0017 000D6F000037B1E5 00 7BAA \x0d\x0a

Voici à quoi correspond chaque paquets :
\x05\x05\x03\x03 : correspond à l'entête de la trame
0017 : correspond au mode de l'ordre (ON-OFF ou Conso) ici mode ON-OFF
000D6F000037B1E5 : adresse MAC de la prise
00 : valeur du mode 00 = OFF et 01 = ON
7BAA : valeur du CRC16
\x0d\x0a : correspond à la fin de la trame

Je vais donc essayer dans un premier temps d'obtenir ce type de trame. Pour cela, je vais utiliser un sniffeur ZigBee. C'est le protocole de communication qu'utilisent les prises Plugwise. Ce sniffeur est, en fait, une carte electronique constitué d'un récepteur ZigBee. Cette carte est reliée à un ordinateur qui enregistre (avec un logiciel spécifique) les données reçues par le récepteur. 


Sniffeur ZigBee


2ème étape :

Les résultats ne sont pas satisfaisant :-(
J'arrive à voir les messages du stick USB et des prises, seulement, les trames semblent être codées d'une autre manière.

C'est peut-être la couche ZigBee qui transforme la trame avant de l'envoyer. Je pense donc qu'il faut que je sniffe la trame bien avant l'envoi. Je vais donc essayer de lire directement sur le port série.
Après plusieurs recherches il existe divers logiciels qui permettent de faire cela, je vais me tourner vers "portmon" qui est gratuit et qui  peut accepter deux connexions à la fois sur le port serie :

http://www.toocharger.com/telecharger/windows/portmon/16073.htm


3ème étape :

Bonne nouvelle ! j'obtiens les bonnes trames !


Je peux à présent m'occuper des trames lors de l'appairage. Pour ce faire, je vais réaliser un appairage avec le logiciel Plugwise et regarder ce qui se passe durant l'installation dans portmon.

4ème étape :


Constat :


- Plusieurs trames sont envoyées durant chaque appairage et dans un ordre précis.
- Il existe deux types d'appairage :
- L'appairage entre le stick USB et le Circle+ qui contient 5 trames :
   0001CAAB
   000AB43C
   000400010000000000000000000D6F00003 + adresse MAC
   000401010000000000000000000D6F00003 + adresse MAC
   0023000D6F00003
- L'appairage entre le Circle+ et les prises esclaves qui contient 4 trames :
   0008014068
   000701000D6F00003
   0023000D6F00003
   0018000D6F00003 + adresse MAC + X où X va de 01 à 3F. Autrement dit de 1 à 63, étant donné que l'on peut avoir 63 prises branchées sur le même réseau.

Il ne me reste plus qu'à me lancer dans le code !

5ème étape :

Voici une première version bêta. Pour l'instant, cette version n'appaire que les prises ayant pour identité "000D6F00003" et fonctionne sur les firmwares 2008-03-10 pour le Circle + et 2008-08-26 pour le stick USB. Je n'ai pas encore testé sur d'autres firmwares.

version v1 : http://github.com/hackstuces/PlugwiseOnLinux



Mode d'emploi :

Installer la librairie python-serial (apt-get install python-serial).
Mettre tous les fichiers dans le même dossier.
Puis ouvrir une console, se rendre dans le dossier ou se trouvent les fichiers et taper : python pair_pol_v1.py.
Vous pouvez à présent appairer vos prises sous linux.
L'appairage se fait uniquement si les prises sont initialisées. (voir ci-dessous pour initialiser)


Pour initialiser vos prises (sortie d'usine) :
Brancher vos prises pendant 3 secondes puis débrancher les pendant 3 secondes. Faire cette manipulation 3 fois de suite.

Vous pouvez me contacter sur hackstuces@gmail.com



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



mercredi 15 septembre 2010

Envoyer sur son iphone ses applications développées, sous Xcode, sans avoir de certificat Apple

Introduction :

Vous venez de réaliser votre application sous xcode et vous etes impatient de la tester sur votre iphone seulement au moment de la transferer ce message apparait : "Code Sign error: The identity 'iPhone Developer' doesn't match any valid certificate/private key pair in the default keychain"

Et oui, pour pouvoir transférer ses applications sur son iphone il faut faire partie du iPhone Developer Program et donc payer 79 €  l'année (rien que ça...).

Mais il existe une solution (pour les iphones jailbreakés) qui consiste à créer son propre certificat iphone. Il faut ensuite certifier son application et la transferer via "ssh" sur son iphone.

Pas de panique voici la démarche à suivre  ;-)

Pré-requis :

- Un iphone jailbreaké (j'utilise la version 3.1.3).
- Un mac ou un PC avec OSX dessus :-) (prochain tuto).
- Xcode (j'utilise la version 3.2.1)

1ère étape  :

On va commencer par créer un certificat. Pour cela rendez vous dans Applications puis Utilitaires. Lancez Trousseau d'accès. 
A partir du menu Trousseau d'accès (en haut à gauche) choisir "Assistant de certification" puis "Créer un certificat..."
Une fenetre "Assistant de certification" s'ouvre alors. Dans le champ "Nom" entrez le nom de certificat que vous voulez mais retenez le. Dans "Type d'identité" sélectionnez "Racine auto-signé" et dans "Type de certificat" "Signature de code".
Cochez "Me laisser ignorer les réglages par défaut" et faites continuer. Une fentre d'avertissement s'ouvre choisissez "Continuer".
Dans "Numéro de série" entrez n'importe quel nombre ou chiffre et dans "Période de la validité" laissez 365. Faites ensuite "Continuer" jusqu'à "Terminer".

2ème étape :

Nous allons maintenant effectuer quelques réglages sous Xcode. Ne branchez pas l'iphone pour le moment.
Ouvez votre projet puis dans "Project" choisissez "Edit Project Settings". 
Une fenêtre s'ouvre. Dans l'onglet Build puis "Configuration" sélectionnez dans le menu déroulant "All Configurations". Toujours dans l'onglet Build, se rendre dans la partie "Code Signing" et dans "Code Signing Identity" sélectionner Don't Code Sign". Faire de même pour "Any iPhone OS Device" qui se situe juste en dessous.
Fermez enfin la fenêtre.
Dans Overview, sélectionnez dans le menu déroulant  iPhone Simulator avec votre version et "Release" ou "Debug" cela n'a pas d'importance, il faudra juste retenir le mode que vous choisissez.
Exécutez ensuite le programme en faisant Build and Run, le simulateur se lance (Un dossier "Release-iphoneos" ou "Debug-iphoneos" a été crée dans le dossier build de votre programme).
Quittez le simulateur et retournez dans "Overview" sélectionnez "iPhone Device" avec votre version et exécuter le programme.
Normalement il devrait y avoir build succeeded et un message "Error Starting Executable No provisionned iPhone OS device connected" c'est normal vu que l'iPhone n'est pas branché.
Quittez Xcode.

3ème étape :

Sur votre iPhone, installez "Openssh" (si ce n'est pas deja fait), pour cela lancez Cydia et dans l'onglet recherche tapez openssh puis faites "Installer" en haut à gauche.
Connectez ensuite l'iphone au même réseau wifi que votre ordinateur : allez dans Réglages Wifi et sélectionnez le réseau en nottant l'adresse IP.

4ème étape :

Ouvrir le Terminal.
Entrez les commandes suivantes :
platform=/Developer/Platforms/iPhoneOS.platform
allocate=${platform}/Developer/usr/bin/codesign_allocate
export CODESIGN_ALLOCATE=${allocate}

Vous pouvez désormais signer votre code en entrant cette commande:
codesign -fs "nom de votre certificat" votre_projet/build/Release-iphoneos ou Debug-iphoneos/votre_projet.app/votre_projet

5ème étape :

Dans le dossier /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOSvotre_version.sdk se trouve un fichier SDKSettings.plist. Nous allons le modifier.
Ouvrir le terminal et tapez la commande suivante :
sudo nano /Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOSvotre_version.sdk/SDKSettings.plist

Dans la partie "DefaultProperties" mettre CODE_SIGNING_REQUIRED à NO et AD_HOC_CODE_SIGNING_ALLOWED à NO et vérifiez que CODE_SIGNING_ENTITLEMENTS est bien vide.
Vous devriez avoir ceci :

<key>AD_HOC_CODE_SIGNING_ALLOWED</key>
          <string>NO</string>
          <key>CODE_SIGNING_REQUIRED</key>
          <string>NO</string>
          <key>CODE_SIGN_ENTITLEMENTS</key>
           <string></string>

6ème étape :

Pour envoyer le programme sur l'iphone grâce au ssh il faut entrer cette commande:
scp -r programme.app root@ip_de_iphone_notée_précedement:/Applications
Il est demandé d'entrer un mot de passe, le mot de passe par défaut est : "alpine".


Il ne vous reste plus qu'à redémarer ou "respring" (si vous avez SBsettings) votre iphone pour voir apparaitre votre application. :-)