Archives d’Auteur

Raspberry Pi, WiFi, ssh et ligne de commande

J’ai rebranché mon vieux Raspberry Pi pour les besoins de geekeries personnelles. J’ai vu qu’il existait une version Lite de Raspbian et j’ai choisi de l’utiliser car mon Raspberry Pi n’est pas de première jeunesse et ma carte SD ne fait que 4 Go. J’ai eu une petite surprise en démarrant le système : il n’y a pas de serveur X. Il faut donc tout faire en ligne de commande. Challenged accepted!

Démarrer le Raspberry Pi

Raspbian Lite est téléchargeable sur le site de la fondation Raspberry. La version actuelle est Jessie. Je me suis aidé de deux articles que j’avais écrit il y a déjà longtemps pour créer ma carte SD avec l’image du système. J’ai branché mon clavier, ma télé, l’alimentation et j’ai obtenu prompt.

Changer le layout du clavier

Parce que qu’un système qwerty quand on a un clavier azerty c’est pas sympa, surtout si on a besoin d’utiliser des symboles, j’ai commencé par passer en azerty. Internet vous propose plusieurs techniques, j’ai utilisée celle-ci :

sudo dpkg-reconfigure keyboard-configuration

La configuration est intuitive avec une interface type ncurse. Quand c’est terminé, il suffit de redémarrer avec la commande reboot.

Configuration du WiFi

Aucune difficulté à ce que Raspbian reconnaisse mon dongle WiFi. Je l’ai branché et il est directement apparu comme wlan0 quand j’ai appelé la commande ifconfig. La partie un peu moins fun est la configuration du réseau WiFi (SSID + passphrase). La fondation Raspberry propose un tutoriel pour cela. Je crois avoir eu besoin de redémarrer à nouveau pour que ça fonctionne (en plus de la procédure expliquée dans le lien).

Configuration IP manuelle

En général, le serveur DHCP de votre box assignera toujours la même IP à votre Raspberry Pi. Il est quand même plus prudent de lui affecter une adresse IP fixe pour le retrouver facilement. Idéalement, il faudrait aussi demander à votre box de ne plus attribuer cette adresse IP par DHCP…

J’ai tout d’abord regardé quelle était mon adresse actuelle, quel masque de sous-réseau était utilisé et quelle était l’adresse de la gateway :

$ ifconfig wlan0
wlan0     Link encap:Ethernet  HWaddr 74:da:38:5d:fc:6b  
          inet addr:192.168.1.17  Bcast:192.168.1.255  Mask:255.255.255.0
          inet6 addr: 2a01:cb05:8980:9400:944d:6692:7db0:9552/64 Scope:Global
          inet6 addr: fe80::9d35:79ba:e404:4253/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:14436 errors:0 dropped:400 overruns:0 frame:0
          TX packets:4818 errors:0 dropped:1 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:17129734 (16.3 MiB)  TX bytes:701974 (685.5 KiB)
$ ip route
default via 192.168.1.1 dev wlan0  metric 303 
192.168.1.0/24 dev wlan0  proto kernel  scope link  src 192.168.1.17  metric 303

J’ai ensuite modifié le fichier /etc/network/interfaces de manière adaptée :

cat /etc/network/interfaces
[...]

allow-hotplug wlan0
iface wlan0 inet static
    address 192.168.1.17
    netmask 255.255.255.0
    gateway 192.168.1.1
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

[...]

Activation de ssh

La dernière étape pour que mon système puisse être headless était d’activer le serveur SSH et ainsi pouvoir me connecter depuis mon ordinateur de bureau. La fondation Raspberry explique comment faire ceci dans ce tutoriel. Avec Raspbian Lite, il suffit de créer un fichier ayant pour nom ssh, à la racine de la carte SD. J’ai donc éteint mon Raspberry Pi et j’ai inséré ma carte mémoire dans mon Mac et j’ai utilisé le terminal :

$ cd /Volumes/boot/
$ touch ssh

Une fois le Raspberry Pi redémarré, vous pouvez regarder si le serveur SSH est démarré avec la commande suivante :

ps -eaf | grep ssh

Si vous un brin plus optimiste, vous pouvez essayé directement depuis votre PC :

$ ping 192.168.1.17
PING 192.168.1.17 (192.168.1.17): 56 data bytes
64 bytes from 192.168.1.17: icmp_seq=0 ttl=64 time=18.548 ms
64 bytes from 192.168.1.17: icmp_seq=1 ttl=64 time=6.127 ms
^C
--- 192.168.1.17 ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 6.127/12.337/18.548/6.211 ms
imac-de-pierre:/ pierregradot$ ssh pi@192.168.1.17
The authenticity of host '192.168.1.17 (192.168.1.17)' can't be established.
ECDSA key fingerprint is SHA256:xv5CB0epvX+e8sKhgLFp2bP+1jzU8j7PzGAVAnZs8q4.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.17' (ECDSA) to the list of known hosts.
pi@192.168.1.17's password: 

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Mon Apr  3 16:22:55 2017
pi@raspberrypi:~ $ echo "Hell yeah!"
Hell yeah!
pi@raspberrypi:~ $ 

Et voilà ! J’ai débranché le Raspberry Pi de la télé, j’ai rangé mon clavier, et c’était bon !

PS : en fait, si vous avez un câble Ethernet, vous pouvez vous passer d’un clavier et d’une télé. Voici comment faire : Headless Raspberry Pi Setup.

 

 

Publicités

C vs C++ : initialisation des variables statiques

On lit souvent C/C++. Hé hé… S’il est indéniable que le ++ de C++ n’est pas là pour rien, il faut aussi savoir qu’il y a des différences en forme de pièges dans la partie « commune ». Ces différences sont particulièrement trompeuses quand on écrit du code C++ qu’on souhaite être compatible C. On va s’intéresser aujourd’hui à l’initialisation des variables statiques.

Voici un code C++ a priori anodin :

#include <stdlib.h>

int getValue()
{
    return rand();
}

int f(int c)
{
    static const int initial = getValue();

    if (initial != c)
        return c;
    else
        return initial;
}

int main()
{
    return f(42);
}

La ligne intéressante est la ligne 10 : on initialise une variable statique avec la valeur de retour d’une fonction.

Plaçons ce code dans un fichier main.cpp, plaçons ce fichier dans un projet Eclipse, et laçons un build :

[pre]16:52:21 **** Incremental Build of configuration Debug for project Experiences ****
Info: Internal Builder is used for build
g++ -std=c++0x -O0 -g3 -pedantic -Wall -Wextra
-c -o « src\\main.o » « ..\\src\\main.cpp »
g++ -o Experiences.exe « src\\main.o »

16:52:21 Build Finished (took 498ms)[/pre]

Renommons simplement le fichier pour forcer sa compilation en C. En effet, GCC se base sur l’extension du fichier pour savoir comment le compiler. Voici le résultat du build dans ce cas :

[pre]16:53:12 **** Incremental Build of configuration Debug for project Experiences ****
Info: Internal Builder is used for build
gcc -std=c99 -O0 -g3 -pedantic -Wall -Wextra
-c -o « src\\main.o » « ..\\src\\main.c »
..\src\main.c: In function ‘f’:
..\src\main.c:10:32: error: initializer element is not constant
static const int initial = getValue();
^

16:53:12 Build Finished (took 139ms)
[/pre]

L’erreur est claire. On pourrait soupçonner le qualificatif const mais cela ne change rien si on l’enlève. Le résultat est le même si la variable est statique au fichier au lieu d’être statique à la fonction.

Sans aller creuser dans les spécifications des langages, on se doute que C résoud la valeur d’une variable statique à la compilation alors que C++ autorise qu’elle soit résolue à l’exécution. Pour cela, le compilateur va sans doute générer un peu de code pour savoir si la variable a déjà été initialisée et l’initialiser le cas échéant (inutile de préciser que cela a un coût). On peut le vérifier en regardant le code assembleur de la fonction f() :

[pre] f(int):
0040144d: push %ebp
0040144e: mov %esp,%ebp
00401450: sub $0x18,%esp
10 static const int initial = getValue();
00401453: mov $0x40d020,%eax
00401458: movzbl (%eax),%eax
0040145b: test %al,%al
0040145d: jne 0x40148a
0040145f: movl $0x40d020,(%esp)
00401466: call 0x4014c4
0040146b: test %eax,%eax
0040146d: setne %al
00401470: test %al,%al
00401472: je 0x40148a
00401474: call 0x401440
00401479: mov %eax,0x40d028
0040147e: movl $0x40d020,(%esp)
00401485: call 0x4014bc
12 if (initial != c)
0040148a: mov 0x40d028,%eax
0040148f: cmp 0x8(%ebp),%eax
00401492: je 0x401499
13 return c;
00401494: mov 0x8(%ebp),%eax
00401497: jmp 0x40149e
15 return initial;
00401499: mov 0x40d028,%eax
16 }
[/pre]

Changeons l’initialisation de notre variable par quelque chose de constant, par exemple :

static const int initial = 666;

Voici le nouveau code assembleur, beaucoup plus simple, de la même fonction f() :
[pre] f(int):
0040144d: push %ebp
0040144e: mov %esp,%ebp
12 if (initial != c)
00401450: cmpl $0x29a,0x8(%ebp)
00401457: je 0x40145e
13 return c;
00401459: mov 0x8(%ebp),%eax
0040145c: jmp 0x401463
15 return initial;
0040145e: mov $0x29a,%eax
16 }
[/pre]

Pour avoir un code similaire en C, il faudrait implémenter une protection similaire, comme ceci :

#include <stdbool.h>
int f(int c)
{
    static int initial;
    static bool guard = false;

    if (guard == false)
    {
        guard = true;
        initial = rand();
    }

    if (initial != c)
        return c;
    else
        return initial;
}

On voit quelques inconvénients : perte de lisibilité, perte du qualificatif const, aucune garantie de thread-safety. A noter qu’en C++, le caractère thread-safe d’un tel code semble dépendre de la version du langage, comme débattu dans cette discussion stackoverflow « Is local static variable initialization thread-safe in C++11?« .

C’est tout pour aujourd’hui, vous êtes prévenus 🙂

Édition du 05/04/2017 : il s’avère en fait que la nécessité d’un initializer constant en C ne se limite pas aux variables déclarés avec le mot-clé static. Cette contrainte s’applique à toutes les variables ayant une static storage duration, comme expliqué dans cette discussion stackoverflow « Error “initializer element is not constant” when trying to initialize variable with const« . Ainsi, cette contrainte s’applique aussi aux variables globales.


XBee et XCTU sous Linux

Cela doit bien faire 2 ans que deux modules XBee (des Pro S1 de chez Digi) traînent dans mes tiroirs et je n’en avais rien fait. Ayant pour projet de faire des capteurs de température et d’humidité sans fil, je me suis dit que c’était le bon moment de les utiliser. L’idée est d’envoyer les données à un Raspberry Pi pour traitement. J’ai donc pris mon XBee Explrer Dongle, j’y ai connecté un module Xbee et je l’ai branché à mon PC. Digi fournit un logiciel pour gérer les modules XBee connectés à un PC, les configurer, envoyer et recevoir des données : il s’agit de XCTU. De nombreux tutoriels sur Internet disent que XCTU n’est disponible que sous Windows mais cette époque est révolue ! Vous pouvez télécharger les différentes versions pour Windows, Linux et Mac ici.

Installation et lancement

Si vous êtes sous Linux et que vous ne savez pas si vous devez opter pour la version 32 ou la version 64 bits, il vous suffit de taper la commande uname -a pour savoir si vous avez un processeur 32 ou 64 bits. Exemple dans mon cas :

~$ uname -a
Linux pgradot-xubuntu 4.4.0-53-generic #74-Ubuntu SMP 
Fri Dec 2 15:58:04 UTC 2016 i686 i686 i686 GNU/Linux

Le i686 m’indique que mon processeur est 32 bits.

Une fois le téléchargement terminé, rendez le fichier exécutable et lancez-le avec les droits administrateurs :

$ chmod u+x 40002880_G.run
$ sudo ./40002880_G.run

Un wizard vous guide dans l’installation de XCTU. Par défaut, le logiciel s’installe dans /opt/Digi où un sous-dossier XCTU-NG est créé. On y trouve launcher et app (le premier lançant le second). Si vous lancez l’un des deux sans les droits administrateurs, cela ne fera qu’ouvrir une petite fenêtre qui ne contient rien et que vous ne pourrez pas fermer… Ayez le réflexe sudo, ça marchera beaucoup mieux ! Vous verrez alors ceci :

xbee 1 startup

Ajout de modules

Vous pouvez ajouter des modules radios ou les découvrir en cliquant que les icônes en haut à gauche. L’ info-bulle vous avait sans doute déjà fait deviner le mode opératoire. Le mien étant branché via un dongle USB, il apparaît comme étant /dev/ttyUSB0 :

xbee 2 add device

XCTU récupère et affiche tout la configuration du nouveau module :

xbee 3 device added

Onglets configuration, console et réseau

XCTU est intuitif et l’info-bulle de droite nous dit qu’il existe 3 onglets, chacun correspondant à un mode :

  1. Le mode configuration, ouvert par défaut, permet de voir la configuration des modules comme montré précédemment mais aussi de la modifier et d’envoyer la nouvelle configuration au module.
  2. Le monde console permet soit d’interagir via des commandes AT avec un module, soit d’envoyer des paquets sur le réseau. Vous pouvez enregistrer des paquets et jouer des séquences d’envoi. Certainement très pratique pour des tests.
  3. Le mode réseau, pour avoir un aperçu de la topologie du réseau sans fil à portée.

Commandes AT

Si vous souhaitez envoyez des commandes AT à votre module, il suffit d’aller dans l’onglet console et d’appuyer sur le bouton Open (qui se transforme en bouton Close, visible en vert dans l’image ci-dessous). Vous tapez alors +++ dans à gauche, vous attendez une seconde et le module répond OK pour indiquer qu’il est bien passé en mode commande. Tapez alors vos commandes en les terminant par entrée et le module vous répondra. Sans activité de votre part pendant 3 secondes, le module sort automatiquement et silencieusement du mode commande et l’envoi d’une nouvelle commande sera alors sans réponse. Sparkun a écrit un très bon aide-mémoire pour les commandes AT disponibles.

xbee 4 at

Pour aller plus loin

Exploring XBees and XCTU par Sparkun


Appels virtuels dans les constructeurs et destructeurs C++

Dans son livre Effective C++, Scott Meyers consacre l’item 9 aux appels virtuels dans les constructeurs et les destructeurs. Il déconseille de les utiliser.

J’ai écrit deux classes pour expérimenter les problèmes qu’il expose et je les ai testé sous Windows 7 avec MinGW. J’ai commencé par écrire une classe de base, dans laquelle le constructeur fait appel à une fonction virtuelle log(). L’objectif est de tracer la création des objets. La fonction est virtuelle pour permettre aux classes filles de personnaliser les logs, et l’appel est fait dans le constructeur de la classe mère car il est forcément appelé lors de l’instanciation d’une classe de la hiérarchie. Voici le code :

#include <iostream>

class Base
{
public:
    Base()
    {
        std::cout << "Base::Base()" << std::endl;
        log();
    }

    virtual ~Base()
    {
    }

    virtual void log()
    {
        std::cout << "Base::log()" << std::endl;
    }
};

class Derived : public Base
{
public:
    Derived()
    {
        std::cout << "Derived::Derived()" << std::endl;
    }

    void log()
    {
        std::cout << "Derived::log()" << std::endl;
    }
};

int main()
{
    Base b;
    Derived d;
}

Et voici la sortie en console, après une compilation sans warning :

Base::Base()
Base::log()
Base::Base()
Base::log()
Derived::Derived()

On constate que tout ce passe comme attendu quand on crée b mais pas franchement quand on construit d. En effet, lors de sa création, on construit d’abord la partie Base de l’objet. Pendant cette construction, l’objet n’est pas encore considéré comme étant de type Derived, mais comme étant de type Base. Sa vtable résout donc l’appel virtuel en appelant une fonction de la classe Base. Il est impossible de faire appel à Derived::log().

Que se passe t-il si la fonction était virtuelle pure ? On serait bien obligé d’aller chercher l’implémentation fournie par la classe fille, non ? Pour tester cela, on modifie la classe mère et on enlève la ligne Base b; du main() pour alléger la sortie et surtout parce on ne peut plus instancier cette classe car elle devient abstraite :

class Base
{
public:
    Base()
    {
        std::cout << "Base::Base()" << std::endl;
        log();
    }

    virtual ~Base()
    {
    }

    virtual void log() = 0;
};

Cette fois-ci, la compilation ne se passe pas bien : le compilateur émet un warning et l’édition des liens échoue :

..\src\main.cpp: In constructor 'Base::Base()':
..\src\main.cpp:9:13: warning: pure virtual 'virtual void Base::log()'
          called from constructor
[...]
C:\Users\X-pigradot\workspaceTestPGT\Experiences\Debug/../src/main.cpp:9:
          undefined reference to `Base::log()'

Il n’y a donc vraiment pas moyen d’appeler la fonction de la classe fille.

Notez au passage qu’une fonction virtuelle pure peut avoir une implémentation. Oui, oui. Cela pourrait servir à fournir une implémentation par défaut. Que se passe t-il alors ? Puisque lors de la construction de la partie Base de Derived, on ne peut qu’appeler des fonctions de la classe Base, qu’appelle t-on ? Essayons de rajouter une implémentation à Base::log() :

void Base::log()
{
    std::cout << "Base::log()" << std::endl;
}

Le warning à la compilation reste mais l’erreur d’édition des liens disparaît et on peut exécuter le code :

Base::Base()
Base::log()
Derived::Derived()

En fait, ce code « tombe en marche ». L’appel d’une méthode virtuelle pure dans un constructeur est un undefined behavior, comme l’explique Raymon Chen dans cet article C++ corner case: You can implement pure virtual functions in the base class. Ici, ça a un comportement conforme à ce qu’on pourrait attendre mais tout aurait pu arriver.

Vous allez dire « le compilateur prévient quand même, tu l’as bien cherché à ne pas écouter ses alertes ». Et vous auriez raison. À une subtilité prêt. Il faut savoir qu’il peut y avoir bien qu’un double drame n’est pas loin. Pour vous montrer cela, voici une double modification du code. La première est d’enlever l’implémentation par défaut de la fonction virtuelle pure ; la seconde est dans le constructeur pour masquer l’appel direct à cette fonction virtuelle pure en appelant une fonction non virtuelle qui appelle à son tour la fonction qu’on ne devrait pas appeler :

class Base
{
public:
    Base()
    {
        std::cout << "Base::Base()" << std::endl;
        indirection();
    }

    void indirection()
    {
        std::cout << "Base::indirection()" << std::endl;
        log();
    }

    virtual ~Base()
    {
    }

    virtual void log() = 0;
};

// NOTE: ON ENLÈVE L’IMPLÉMENTATION !

Ce code compile sans warning (premier drame) et à l’exécution, on a (deuxième drame) :

Base::Base()
Base::indirection()
pure virtual method called

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
terminate called without an active exception

Le programme se termine brutalement. Désolé. Remarquez, si ça crashe on s’en rend compte rapidement… mais on espère que ça crashe avant d’arriver en prod ! Ici, la ficelle est une grosse mais par le jeu de la maintenance, avec plusieurs développeurs, au fil du temps, avec des fonctions qui appellent des fonctions qui appellent d’autres fonctions, vous pouvez vous retrouver dans une situation inconfortable où votre constructeur appelle une fonction virtuelle, pure ou pas, définie ou pas.

Conclusion : méfiez-vous des appels de fonctions virtuelles dans les constructeurs (et les destructeurs, ça a le même effet). Méfiez-vous de toute fonction que vous appelez dans vos constructeurs ou vos destructeurs car elles pourraient appeler des fonction virtuelles (aujourd’hui ou demain). Enfin, si vous rendez une fonction virtuelle ou virtuelle pure, faites attention à ce quelle ne soit pas déjà appelée (directement ou indirectement) depuis un constructeur ou un destructeur.

En complément, vous pouvez lire cet article, Be Careful with Virtual Method Calls from the Constructor (and Destructor) of your Classes in C++/C#/Java!. L’auteur montre ce que je viens de vous montrer en C++ mais aussi ce qui se passe en Java et C#.


Activer la compilation multicœur dans Eclipse CDT

Mon premier article sur ce blog expliquait comment dire à make d’utiliser plusieurs cœurs lors de la compilation. Il suffit d’utiliser l’option -j pour spécifier le nombre de jobs pouvant s’exécuter en parallèle. Si vous utilisez Eclipse CDT et son builder interne, il existe aussi une option activer la compilation multicœur. Je l’ai trouvé par hasard aujourd’hui, elle est dans les propriétés du projet puis C/C++ Build et Behavior :

eclipse-compilation-multicoeur

J’ai étonné que cette option ne soit pas cochée par défaut et j’ai bien sûr immédiatement testé ça. Mon projet n’est pas encore très gros, il n’a que 78 fichiers. En l’activant, je suis passé de 31s.792 ms à 13s.114 ms pour le builder. Wouhou !