Detecteur de fuite d’eau avec un raspberry pi

Dans cette article nous allons voir comment réaliser un détecteur de fuite d’eau pour le raspberry pi pour moins de 5€.

raspberry-ban

Le montage

Les détecteurs de fuites d’eau disponible pour les solutions domotique commerciales ont un fonctionnement très simple. Il suffit d’appliquer une tension au bout de deux bornes en métal séparer de quelques millimètres. En cas de fuite, comme l’eau est un fluide conducteur, le circuit électrique se ferme et le courant passe.

Ce courant est relativement faible et il est nécessaire de l’amplifier. Pour cela, nous utilisons un transistor Darlington.

Il et alors possible de réaliser un circuit imprimé à partir du montage suivant :

Schematic

ou alors de commander un kit à monter soit même. Personellement, j’ai commandé ce kit pour un peut moins de 6€ ici.

J’ai alors remplacé le buzzer par une sortie vers le raspberry pi (- de buzzer vers le raspberry pi) et l’alimentation 9V par l’alimentation en 3V du raspberry pi. Attention à bien utiliser une alimentation 3V au risque de griller votre raspberry.

J’obtiens le module suivant :

IMG_20150506_195036

Le code

Le code est assez similaire à celui de la détection d’un interrupteur. Afin de profiter au mieux des ressources du raspberry, j’utilise la librairie bcm2835 déjà détaille dans cet article.

Le code est le suivant, à noter que nous utilisons cette fois une résistance de pull-up.

#include <bcm2835.h>
#include <stdio.h>
// Input sur le GPIO22 qui est nommé 15 dans le système de la librairie
#define PIN RPI_GPIO_P1_15
 
int main(int argc, char ** argv) {
    if (!bcm2835_init()) {
        return 1;
    }
    // Fixe le GPIO en entrée
    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT);
    //  avec une résistance de pull down
    bcm2835_gpio_set_pud(PIN, BCM2835_GPIO_PUD_UP);
 
    while (1) {
        // lecture du niveau d'entrée
        uint8_t value = bcm2835_gpio_lev(PIN);
        printf("lecture GPIO22: %d\n", value);
 
        // attendre 2s
        delay(2000);
    }
    //Un brin de toilette
    bcm2835_close();
    return 0;
}

Pour compiler :

$ gcc main.c -lrt -lbcm2835 -o main.o

Vous devriez alors voir apparaitre un 1 lorsque tout va bien et un 0 si vous plongez votre capteur dans l’eau.

Améliorations possibles

  • Ajouter une interface php
  • Ajouter une alerte email / téléphone
  • Ajouter un module RF

Voilà quelques idées pour améliorer ce capteur qui permet pour un coût raisonnable d’améliorer votre installation domotique.

Raspeberry Pi et domotique

raspberry-ban

Dans cet article, nous allons voir comment fabriquer à moindre coût un adaptateur RF433 pour notre Raspberry Pi. L’objectif de ce projet est de faire un émetteur récepteur pour la fréquence de 433.92 Mhz est d’y associer une API php.

Le hardware

Comme vous avez pu le voir dans l’article de présentation du projet ici, nous utilisons un module externe. Vous pouvez trouver les schematics qui permettent de réaliser le module RF à cette adresse.

L’assemblage est assez simple et vous devriez vous retrouver avec le circuit suivant :

IMG_20141122_172059

L’antenne doit mesure 8.3 cm de long et devrait ainsi pouvoir rentrer dans le boîtier plastique du Raspberry Pi histoire d’avoir un package « pro ».

Programmer le micro contrôleur

Les sources pour le micro contrôleur sont disponibles à cette adresse : . Il s’agit directement du projet pour le MPLABX que vous pouvez télécharger librement sur la toile.

L’API php

L’API en php est composée de deux modules :

  • Le code C
  • une classe PHP

Le code C établit la connexion au travers du port série avec le module hardware. Pour rendre cela compatible, vous devez tout d’abord réaliser la manipulation disponible ici. Une fois que ceci est réalisé, vous pouvez utiliser la librairie disponible sur github. Elle dispose de deux fonctions qui vous permettront d’enregistrer les signaux de vos équipements et de les ré-emmètre.

Et mais moi je veux une solution tout en un

Nous avons aussi réalisé une interface graphique et le script d’installation est en cours de finalisation. Nous posterons le script permettant de tout installer avec un tutoriel très rapidement.

Raspberry Pi et domotique

raspberry-ban
Afin d’appareiller nos maisons respectives, nous cherchions une solution domotique abordable.
Après avoir fait le tour du net et des magasins de bricolage, nous nous sommes fixés sur les équipements avec une fréquence de 433Mhz. Ces équipements sont économiques, les protocoles utilisés sont statiques et il est très simple de trouver de quoi communiquer avec.

Le projet

Nous avons profité d’un projet lancé par l’ENSICAEN en partenariat avec Radiospare. Nous souhaitons d’ailleurs les remercier tous les deux pour nous avoir accompagner tant financièrement que techniquement durant ce projet.

La base du projet est la réalisation d’un projet domotique avec Raspberry Pi.

Nous avons choisi de réaliser un petit module à greffer sur les ports du Raspberry Pi afin de pouvoir éteindre et allumer des équipements en RF433 (volets roulants, prises, lumières, etc…).
Une fois ce module réalisé, nous avons développé une interface web afin de pouvoir utiliser et scénariser les équipements.

L’architecture

Nous avons développé notre module à l’aide d’un composant permettant de moduler et démoduler les signaux RF433, le RTX MID 3V.

Afin de capturer les signaux, nous avions besoin d’un système temps réel. La première solution consiste à installer un OS temps réel sur le Raspberry Pi. La seconde consiste à utiliser un micro-contrôleur. Nous sommes partis sur la seconde solution et avons développé sur un PIC18F24k50. Il présente l’avantage d’être petit, économique et de disposer de suffisamment de RAM.

architexture_hard

Les signaux

Les signaux qui permettent de commander les équipements sont statiques. Ceci implique que pour chaque commande allumer et éteindre, le signal émit est identique. La figure ci dessous représente un signal quelconque.

signal

L’objectif de notre module est donc de faire un enregistreur de signal capable de le réémettre. Nous avons remarqué que le signal de commande était réémis plusieurs fois de suite par les émetteurs du commerce. Ceci est dû au fait que le protocole est à sens unique. Il n’y a donc pas de validation (ACK) une fois le signal reçu. Ces espaces vides sont cependant présents sur plusieurs protocoles RF433 que nous avons observé à l’oscilloscope (équipements des marques DIO et Carrefour).

Afin d’acquérir correctement le signal émis à l’intention de ces équipements, nous utilisons l’algorithme suivant :

  1. Lire état (haut/bas)
  2. Si état haut continuer, sinon revenir à 1
  3. Enregistrer le plus longtemps possible
  4. Chercher la plus longue série d’états bas consécutifs
  5. Chercher la plus proche série d’états bas consécutifs de longueur équivalente
  6. Le signal se trouve entre les deux séries d’états bas

Il suffit ensuite d’émettre plusieurs fois ce signal pour allumer ou éteindre un équipement. Cet algorithme a été implémenté en C. L’enregistrement fonctionne sur le module et la recherche du signal (étape 4 à 6) tourne sur le Raspberry Pi.

Le module

Le module se connecte sur les PIN du Raspberry Pi. La communication entre le Raspberry Pi et le PIC18F24k50 est assuré au travers du port série. Pour cela, il a été nécessaire d’utiliser une astuce pour retirer la console du port série d’un Raspberry Pi.
Le module tient dans un boîtier de Raspberry Pi avec une antenne de 8.175 cm de long.
IMG_20141122_172059

Les prochains travaux consisterons à améliorer l’antenne afin d’optimiser la porté en réception afin de permettre l’ajout de nouveaux modules.

Interface Web

Afin de mettre en place l’interface web, nous avons installé un serveur Nginx. La base php et les interfaces ont été développés par Antoine Choquet.

home

equipements

Quand est ce que ça sort

Nous réalisons encore quelques tests avec l’antenne, ce qui peut entraîner quelques changements sur le PCB. En parallèle, nous testons encore un peu le scripts d’installation. Tout ça devrait être prêt dans le courant de décembre, avant noël. Comme toujours, les sources et les schematics seront libres.

Open data, les calendriers

android

Dans cet article nous verrons comment récupérer les données d’un google agenda afin de le rendre exploitable par une application android ou java. Je l’utilise moi même pour le système d’intelligence artificiel qui contrôle ma maison. Je peux ainsi prévoir mes prochains rendez vous et connaitre mes rendez vous de la journée lorsque je me lève. Afin de réaliser le parsing des données, j’utilise la librairie iCal4j qui est assez bien documenté et pour lequel le net regorge d’exemple.

Le format iCalendar

Le format est iCal est un format qui permet de représenter les agendas. Il s’agit d’un format normalisé. La page Wikipedia à ce sujet est assez bien réalisée et je vous laisse le soin de la visualiser.

Google propose d’exporter vos agendas dans ce format. Vous ne devriez pas rencontrez de difficulté si vous suivez les instructions des services de google à cette adresse. Attention à bien lire jusqu’au bout et à partager votre agenda (le rendre public) pour pouvoir accèder aux données.

Vous devez maintenant avoir un lien iCal qui ressemble à celui ci : « https://www.google.com/calendar/ical/mon.compte%40gmail.com/public/basic.ics »

Quelques exemples

Si vous avez pris le temps de lire la page Wikipedia, vous connaissez maintenant les différents composants que l’ont peux récupérer. Si vous ne l’avez pas encore fait je vous invite à le faire, c’est très rapide et ceci vous permettra de comprendre comment fonctionne le standard.

J’aimerai récupérer mon prochain rendez vous afin de l’afficher par exemple. Si celui ci a lieu dans plus de 7 jours, alors je n’en prends pas compte et indique à l’utilisateur qu’il n’a pas de rendez vous prévu dans les 7 prochains jours.

Pour cela il est nécessaire de :

  1. Récupérer un flux vers l’url de mon google agenda au format iCal
  2. Parser ce flux de données
  3. Filtrer les données pour récupérer les VEVENTs qui ont lieu dans les 7 prochains jours
  4. Afficher le premier VEVENT ou bien un message si il n’y a pas de rendez vous dans les 7 prochains jours

ce qui donne le code correspondant :

//1 Récupérer le flux de icalendar à partir de mon google agenda
URL googleCalendar = new URL("https://www.google.com/calendar/ical/mon.compte%40gmail.com/public/basic.ics");
BufferedReader in = new BufferedReader(new InputStreamReader(googleCalendar.openStream()));
 
//2 Parser le flux de données
CalendarBuilder builder = new CalendarBuilder();
Calendar calendar = builder.build(in);
 
//3 Filtrer les données pour récupérer les VEVENTs qui ont lieu dans les 7 prochains jours
Period period = new Period(new DateTime(today.getTime()), new Dur(7, 0, 0, 0));
Filter filter = new Filter(new PeriodRule(period));
Collection<?> eventsToday = filter.filter(calendar.getComponents(Component.VEVENT));
 
//4 Afficher le premier VEVENT ou bien un message si il n'y a pas de rendez vous dans les 7 prochains jours
Iterator<?> i = eventsToday.iterator();
if (!i.hasNext()) {
   System.out.println("Pas de rendez vous dans les 7 prochains jours");
} else {
   Component component = (Component) i.next();
 
   DateTime dateStart = new DateTime(component.getProperty("DTSTART").getValue());
   //DateTime dateEnd = new DateTime(component.getProperty("DTEND").getValue());
   String description = component.getProperty("DESCRIPTION").getValue();
   String location = component.getProperty("LOCATION").getValue();
   String name = component.getProperty("SUMMARY").getValue();
DateFormat formater = new SimpleDateFormat("dd MMMM yyyy 'à' hh 'heure et' mm 'minutes'",new Locale("FR", "fr"));
 
sentence = "Vous avez rendez-vous " + name + " le " + formater.format(dateStart) + ".";
if(null != location && !location.isEmpty()) {
   sentence = sentence + " Il se tiendra à " + location + ".";
}
 
if(null != description && !description.isEmpty()) {
   sentence = sentence + " La description asocié est " + description + ".";
}
 
System.out.println(sentence);

Afin d’obtenir d’avantage d’exemples, je vous invite à parcourir la page du wiki de iCal4j : http://wiki.modularity.net.au/ical4j/index.php?title=Examples
Vous y trouverez des exemples pour ajouter / récupérer des VEVENT, VCARD, générer un iCal etc…

Open data, parsing XML

android

On entend beaucoup parler de l’open data. Ce concept qui veut ouvrir les données afin que tout le monde puisse les réutiliser est en théorie une grande idée qui permet au plus créatif de réaliser l’application de leur rêve.

On voit fleurir un peu partout des hackathons qui proposent d’utiliser ces données et lorsqu’il s’agit de s’en servir, les développeurs déchantent assez rapidement. Le principe de l’open data consiste bien souvent à balancer les données dans un format difficile à utiliser.

Pour cet article, nous allons réaliser un convertisseur de devises à l’aide de données accessibles publiquement. Vous pouvez trouver le convertisseur sur le playstore à cette adresse
Vous pouvez aussi accèder directement aux sources sur github

Trouver les données

Sur internet les sites qui proposent les taux de change en temps réels sont assez nombreux. Il s’agit même d’un véritable business utilisé par les traders. Pour notre convertisseur, nous souhaitons avoir une source fiable et gratuite. Le format est aussi asez important, il faut si possible utiliser un format courant comme le XML ou le json. Afin de s’assurer de la fiabilité des données, il n’y a je pense qu’une seule chose à faire, avoir une source de confiance. Afin de trouver des données publiques, je commence toujours par lister l’ensemble des organismes publiques qui me viennent à l’idée et qui pourraient mettre cette donnée en ligne.

Dans notre cas nous cherchons les taux de change, j’ai donc choisi d’orienter mes recherches vers :

  • Les banques centrales
  • Les banques d’état
  • Tout les services de l’état qui pourraient avoir à faire de prés ou de loin à de la monnaie (ministère des affaires étrangères, etc)

A cette liste, je conseille d’ajouter systèmatiquement les organismes de normalisation tel que l’ISO ou l’ANSI. Ces organismes regorgent de données publiques qui sont dans des formats très faciles à gérer (xml ou csv).

Utiliser la donnée

Après ces recherches je suis rapidement tombé sur le site de la banque centrale européenne qui offre un accès en XML aux taux de changede la journée.

La source est fiable et offre en plus de cela des exemples pour les développeurs en php. Je souhaite réaliser une application android, mais les exemples sont souvent un gage de qualité (ou laisse penser que les données on au moins étaient testé).

Voici le fichier XML proposé :

<gesmes:Envelope>
<gesmes:subject>Reference rates</gesmes:subject>
<gesmes:Sender><gesmes:name>European Central Bank</gesmes:name></gesmes:Sender>
<Cube>
<Cube time="2014-08-07">
<Cube currency="USD" rate="1.3368"/>
<Cube currency="JPY" rate="136.76"/>
<Cube currency="BGN" rate="1.9558"/>
<Cube currency="CZK" rate="27.853"/>
...
</Cube>
</Cube>
</gesmes:Envelope>

La banque centrale européenne propose les taux de change par rapport à l’Euro. Afin d’obtenir les autres taux de change, nous réalisons une simple règle de trois.

Pour parser le xml, j’utilise la librairie jdom. Le code proposé sur le github est assez parlant, si vous rencontrez des problèmes n’hésitez pas à poster un commentaire. Le problème suivant est que nous souhaiterions avoir le nom de la monnaie plutôt que le symbole. Pour cela nous retournons à l’étape précèdente et récupérons une autre source.

La source proviens de l’iso (http://www.currency-iso.org/dam/downloads/table_a1.xml), de cette manière je récupére les données suivantes :

<ISO_4217 Pblshd="2014-03-28">
<CcyTbl>
<CcyNtry>
<CtryNm>AFGHANISTAN</CtryNm>
<CcyNm>Afghani</CcyNm>
<Ccy>AFN</Ccy>
<CcyNbr>971</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
<CcyNtry>
<CtryNm>ÅLAND ISLANDS</CtryNm>
<CcyNm>Euro</CcyNm>
<Ccy>EUR</Ccy>
<CcyNbr>978</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
<CcyNtry>
<CtryNm>ALBANIA</CtryNm>
<CcyNm>Lek</CcyNm>
<Ccy>ALL</Ccy>
<CcyNbr>008</CcyNbr>
<CcyMnrUnts>2</CcyMnrUnts>
</CcyNtry>
...

Afin de réaliser l’application j’aurai souhaité avoir un fichier xml comme celui ci :

<?xml version="1.0" encoding="UTF-8"?>
<CurrenciesCode>
...
  <Currency code="CNY">Yuan Renminbi</Currency>
  <Currency code="JPY">Yen</Currency>
  <Currency code="XBD">Bond Markets Unit European Unit of Account 17 (E.U.A.-17)</Currency>
  <Currency code="UGX">Uganda Shilling</Currency>
  <Currency code="RON">New Romanian Leu</Currency>
  <Currency code="UYI">Uruguay Peso en Unidades Indexadas (URUIURUI)</Currency>
  <Currency code="TTD">Trinidad and Tobago Dollar</Currency>
  <Currency code="SHP">Saint Helena Pound</Currency>
  <Currency code="MOP">Pataca</Currency>
...

Pour ce faire, j’ai donc choisi de réaliser une conversion des données et d’inclure directement le fichier dans mon projet android. Je ne pense pas que les devises changent en permanence de nom et dans le cas de l’apparition d’une nouvelle devise, il suffira de mettre à jour l’application avec un fichier mis à jour. Si vous souhaitez récupérer ce fichier, il est présent dans les sources github dans le fichier assets.

Conclusion

Cet article reprend les grand principes pour travailler avec des données publiques. La méthodologie est celle que j’utilise, le choix des données est très important, je vous recommande de toujours choisir des grands organismes qui souhaitent garder à jour les données (et qui on un interêt à le faire).
N’hésitez pas lorsque cela est nécessaire à retraiter la donnée. Ce traitement permet d’alléger votre application mais ne peut se faire que sur les données statiques.

It’s oversimple isn’t it?

Créer une enceinte programable pour Raspberry pi

electro

Dans cet article, nous allons voir comment modifier une enceinte que l’on trouve dans le commerce afin de l’utiliser avec le raspberry pi.

Pour une application, je souhaitais ajouter la voix à mon raspberry et je devais changer l’enceinte de mon réveil. Pour cela, j’ai utilisé une enceinte autoalimentée que l’on peut recharger en USB. Cette solution a deux inconvénients majeurs :

  • L’enceinte grésille lorsqu’elle est alimentée mais qu’aucun son n’est envoyé sur la prise jack
  • On passe notre temps à recharger l’enceinte si elle n’est pas connectée au pi ou alors on occupe un port usb.

La solution serait donc de pouvoir programmer l’allumage et l’extinction de notre enceinte.

Piloter l’enceinte

Afin de rendre l’enceinte pilotable, j’ai choisi d’agir directement sur l’alimentation.
L’idée est d’utiliser le raspberry pi pour alimenter l’enceinte. Afin de pouvoir faire celà, il faut supprimer la batterie de l’enceinte. Si votre enceinte n’a pas de batterie, vous pouvez passer cette étape.

En démontant l’enceinte j’obtiens ce que l’on voit sur l’image suivante :
IMG_20140820_202701

J’ai donc coupé les deux fils reliant la batterie. Je l’ai fait de mainière à pouvoir les ressouder si dans le futur je souhaite réutiliser l’enceinte en mode portable.

Après cela, j’ai remonté l’enceinte et je me suis attaqué à la réalisation d’un cable USB-mini USB comme ceux qui alimentent nos téléphones portables. Choississez un câble assez gros, le soudage n’en sera que plus facile. Il faut maintenant permettre au pi de contrôler cette alimentation. L’alimentation USB est en 5V. Les GPIO du raspberry pi ne sont que en 3V, il va donc falloir faire un petit montage afin d’effectuer cette conversion. Pour cela, j’utilise le composant SFH6206. Voici le schéma :

Screenshot from 2014-08-22 09:48:10

Une fois assemblé, j’obtiens le montage suivant :

IMG_20140821_085943

La programation

Nous allons utiliser les GPIO, pour cela j’ai développé un petit programme en C à l’aide de la librairie bcm2835. Vous pouvez retrouver un exemple d’installation et de compilation dans cet article.

#include <bcm2835.h>
#include <stdio.h>
#include <stdlib.h>
 
#define PIN_ALIM_SPEAKER RPI_GPIO_P1_07
 
int main(int argc, char ** argv) {
   if (!bcm2835_init()) {
        return EXIT_FAILURE;
   }
 
   //On alimente l'enceinte
   bcm2835_gpio_fsel(PIN_ALIM_SPEAKER, BCM2835_GPIO_FSEL_OUTP);
   bcm2835_gpio_write(PIN_ALIM_SPEAKER, HIGH);
 
   //... Jouer un son
 
   //On éteint l'enceinte
   bcm2835_gpio_write(PIN_ALIM_SPEAKER, LOW);
   bcm2835_close();
   return EXIT_SUCCESS;
}

Rendu final

Afin de cacher un peu le montage, j’ai utilisé un vieux boitier de disque dur externe.


OVERSIMPLE : Un réveil avec un raspberry pi par oversimple

Le stockage des mots de passes

android

Il y a quelques jours, je regardai le projet lancé par hackaday de faire un password keeper qui soit offline : Mooltipass

Je me suis dis que c’était quand même bien pratique et que j’en aurai bien besoin sur mon téléphone mobile. Comme ça faisait pas mal de temps que je n’avais pas développé sur Android, c’était l’occasion de réfléchir au plus gros problème que l’on retrouve sur les mobiles, ils ne disposent pas d’un espace sécurisé.

Vous pouvez récupérer l’application sur google play directement à cette adresse : Téléchargement

Le mécanisme de protection

Afin de réaliser ce projet, j’ai supposé que le mobile n’était pas vérolé à priori et qu’aucun touchlogger n’était présent sur celui ci.

En plus de cela, j’ai supposé que l’utilisateur été suffisamment doué pour choisir un mot de passe solide dont on peut sauvegarder le hash sans qu’il soit casser rapidement.

L’idée est donc de chiffrer l’ensemble des mots de passe de l’utilisateur en utilisant un secret que seul l’utilisateur connait.

Pour cela nous allons utiliser les API java cryptographiques et utiliser l’algorithme AES.

Afin de générer le secret pré partagé, nous allons utiliser une phrase secrete. Ce secret consiste en une phrase que seul l’utilisateur connait. Nous utiliserons alors une fonction de hashage pour générer la clé de chiffrement à partir de la phrase secrète de l’utilisateur. A cela nous ajoutons un salt, ceci permet de diversifier le secret pré partagé en fonction de son usage.

La fonction de diversification a été codé comme suis :

private byte[] diversify(String password, String salt) {
	password = password + salt;
	byte[] key = new byte[16];
	MessageDigest sha1 = null;
	try {
		sha1 = MessageDigest.getInstance("SHA");
	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
	}
	sha1.update(password.getBytes());
	byte[] hash = sha1.digest();
	System.arraycopy(hash, 0, key, 0, 16);
	return key;
}

Le stockage des données

Afin de stocker les données, nous allons transporter la clé dérivée pour la session courante et utiliser une base de données Android pour stocker les mots de passe. L’ensemble du contenu de la base de données est bien entendu chiffré avant insertion.

Chaque entrée est identifiée par les champs suivants :

public class PasswordReference {
 
	private int id;
	private String description;
	private String login;
	private String password;

L’id est stocké en clair, pour les autres champs, nous utilisons un salt différents pour chacun.

Attaques possibles

La première attaque possible est de regarder par dessus l’épaule. Ainsi l’utilisateur récupère le secret pré partagé.
Une seconde attaque serait d’utiliser un key logguer. Afin de palier à ce genre d’attaque, il faudrait ajouter un deuxième paramètre dans la génération du secret pré partagé. Ce deuxième paramètre serait un code pin avec clavier dynamique. Ainsi un touchlogger deviendrait inefficace pour récupérer le code pin.

Cependant cette technique n’empêcherait pas de bruteforcer les solutions restantes et avec un code pin à 4 chiffres, ça ne fait que 10 000 essais.
L’attaque par bruteforce reste la dernière possible et celle ci peut être évitée à condition de choisir un mot de passe sure et de le changer régulièrement.

Sources

L’ensemble des sources de ce petit programme peut être télécharger sur github à l’adresse suivante : https://github.com/julien14/secretKeeper
Bien sur le programme est libre et sous licence GPL. Enjoy !