Extension d'entrées-sorties Arduino par I2C

Dans un montage conçu autour d'Arduino, il est possible que le nombre d'entrées-sorties disponibles ne suffisent pas pour réaliser notre projet.
Que faire si on se retrouve dans un tel cas de figure? Des solutions existent.

On peut créer des entrées-sorties supplémentaires avec d'autres types de composants électroniques, puis dialoguer avec ceux-ci à l'aide d'un bus de communication série. Arduino sait communiquer par la liaison série RS232, par SPI ou par I2C.

Dans ce nouvel article on se propose de montrer qu'on peut augmenter le nombre d'entrées-sorties de la carte Arduino, à l'aide du bus I2C.

Comme exemple de mise en situation, on va réaliser une serrure codée dotée d'un module clavier-afficheur à cristaux liquides.

Pour finir, on essayera de mettre en évidence le côté pratique de ce type de montage avec une carte à microcontrôleurs à peine plus grande qu'un timbre poste dont la puissance est inversement proportionnelle à la taille: le module Seeeduino XIAO SAM21. Cette mini carte peut être achétée sur Amazon, Aliexpress, ou sur le site Gotronic, pour moins de 10Euros.

Dans cet article, on commencera par présenter le PCF8574, puis le schéma du montage et de ses sous-ensembles, ensuite le programme  et pour finir le test de fonctionnement.

Composants utilisés:

De quoi avons nous besoin pour ce montage:

- Arduino Uno, Seeeduino XIAO SAM21;
- Un clavier matriciel 16 touches;
- Un afficheur à cristaux liquides deux lignes 16 caractères retroéclairé;
- Un buzzer 5V;
- Deux PCF8574 Interfaces d'entrées-sorties I2C tout ou rien 8 bits;
- Un transiator BC547B;
- Une diode LED verte et une diode LED rouge de 3mm les deux;
- Deux résistances de 1Kohm;
- Trois résistances de 10KOhms;
- Une résistance de 100KOhms:
- Une résistance de 100Ohms;
- Deux connecteurs RJ12 (6P6C);
- Une carte à trous pastillée 120 x 85mm environ;
- Du câble plat de téléphonie 6 Conducteurs 500mm de longueur.

 

Présentation du PCF8574:


C'est une interface d'entrées-sorties I2C, qui fonctionne en tout ou rien. Tout d'abord, un bref aperçu du bus I2C.

Le bus I2C a été crée par la société PHILIPS Semiconductors, le côté vraiment pratique de ce type de bus réside dans le fait que tous les composants peuvent dialoguer avec un microprocesseur ou un microcontrôleur par l'intermédaire de deux fils reférencés par une masse. Ceci a eu pour conséquences la réduction drastique du nombres de pistes sur le circuit imprimé et la réduction du coût de fabrication pour les équipementiers.

Finalement l'I2C s'est enrichi en terme de diversité des commposants; on peut citer par exemple: des converstisseurs analogiques-numériques ou numériques-analogiques, des mémoires,divers décodeurs, des circuits audio,...etc; pour finir par devenir une norme de facto. Avec un débit de base de 100 kbits/s.

Au départ le Bus I2C avait été conçu pour des composants électroniques localisés à l'intérieur d'un appareil. Il est même possible à l'heure actuelle d'étendre sa portée à une distance théorique avoisinnant quelques centaines de mètres avec l'ajout d'un circuit d'interface dédié à cet effet.

Dans un bus I2C, tous les composants à l'exeption du maître possèdent une adresse unique codée sur 7 bits. Ce sont les 7 bits de poids fort, de l'octet que le maître devra envoyer aux esclaves. Voir tableau ci-dessous:

OctetBits
7(MSB) 6 5 4 3 2 1 0(LSB)
Adresse I2C L H L L A2 A1 A0 R/W
L = état bas = niveau logique 0;  H = état haut = niveau logique 1. 

Dans ce tableau, les bits  7, 6, 5, et 4 sont figés pour cet exemple, ils sont à 0100, il reste à l'utilisateur de configurer les bits A2, A1, et A0, en les reliant soit à un niveau logique 1, soit à niveau logique 0. Le dernier bit représente le bit d'écriture/lecture; lorsqu'il est à 1 le maître demande une opération de lecture, et lorsqu'il est à 0 c'est une opération d'écriture.

Exemple d'adresse: supposons que les broches A2, A1, et A0 sont toutes reliées à la masse, l'adresse I2C du composant sera : 0100000 ou 0x20 en hexadécimal.

Et si A2 et A1 sont à la masse et A0 à +5 Volts, l'adresse sera : 0100001 ou 0x21 en hexadécimal.

La valeur des 4 bits de poids forts de ce tableau dépend du type de composant, si c'est un PCF8574, ils prennent la valeur 0100, et si c'est un PCF8574A ce sera 0111.

Un PCF8574 n'aura pas la même adresse I2C qu'un PCF8574A, même si la configuration des broches A2, A1, et A0 est la même.
L'extrait ci-dessous tirée de la documentation technique de Texas Instruments, donne les huits adresses possibles du PCF8574 et du PCF8574A:

adresses i2c 8574

Il est donc possible, au vu de ceci, d'étendre à 128 le nombre d'entrées-sorties d'Arduino, si on utilise ces deux composants dans un même montage:

exemple plusieurs 8574 16 es

Les PCF8574 sont munies d'une sortie d'interruption. Lorsqu'ils fonctionnent comme entrées pour capteur, un changement d'état sur l'une des entrées produit un signal d'interruption sur cette sortie.
Le microcontrôleur peut ainsi aller voir ce qui se passe puis entreprendre une action appropriée en conséquence.

Schéma du montage:


Il est composé de deux PCF8574, reliés par I2C à Arduino. Un PCF8574 se chargera de gérer l'afficheur; celui-ci fonctionnera exclusivement en sortie, tandis que l'autre qui s'occupera du clavier; il fonctionnera en entrée et en sortie. Des résistances de Pull-Up, de 10 KOhms sont branchées sur les signaux SCL, SDA et INT. Pour l'écran LCD, une résistance ajustable de 5K à 20K, sert pour régler le contraste, les deux diodes indicatrices d'état, ainsi que le buzzer sont branchés sur les deux broches restantes du PCF8574.

Pour les connexions du clavier:

clavier 1 1 schema

Pour l'écran LCD:

ecran lcd 1 1 schema

Signaux du connecteur RJ12:

rj12 1 1

Le clavier:

principe clavier matrice

Les broches 1, 2, 3, et 4 correspondent respectivement aux colonnes 1, 2, 3, et 4; les broches 5, 6, 7, 8 correspondent respectivement aux lignes 1, 2, 3, et 4.

 

Comment décoder une touche appuyée d'un clavier matricé 16 touches?

On a connecté le clavier au PCF8574 de la façon suivante:

=> Colonne 1 -  E/S 0; Colonne 2 - E/S 1; Colonne 3 - E/S 2; Colonne 4 E/S 3.

=> Ligne 1 - E/S 4; Ligne 2 - E/S 5; Ligne 3 - E/S 6; Ligne 4 - E/S 7

Pour décoder une touche appuyée, on va d'abord commencer par placer les lignes en sorties, puis les colonnes en entrées. On écrira la valeur logique 0000, sur les lignes 1,2,3,et 4. Puisque les colonnes sont considérées comme des entrées, elles occupent toutes un niveau logique 1 (1111).

Par exemple si on appui sur la touche 8:

principe clavier 1 2

La colonne 2 prend la valeur 0. Le passage de l'état initial des colonnes 1111 vers l'état 1101, va provoquer une interruption de la part du PCF8574.

Dans la routine d'interruption, on va d'abord commencer par mémoriser l'état des 8 bits; ici: 00001101; puis mettre les colonnes à en sorties et à 0000, puis les lignes en entrées à 1111, pour obtenir un nouvel état:

principe clavier 1 3

Pendant ce temps la touche 8 est toujours appuyée. La nouvelle valeur du port d'Entrées/Sorties du PCF8574 sera: 10110000

Il suffit de faire un "Ou exclusif" entre les deux octets obtenus, pour obtenir un code unique correspondant à la touche appuyée. Ici 00001101 "Ou exclusif" 10110000 = 10111101.

Donc c'est bien la colonne 2 - ligne 3 qui a été solicitée.

Il suffifit de faire correspondre à chaque code obtenu un chiffre ou une lettre du clavier.

Il est aussi possible de gérér le clavier sans utiliser les interruptions. Dans ce cas il faudra placer lignes dans un état logique 0 par exemple, puis procéder à un balayage des colonnes, afin détecter une touche appuyée.

Dans les deux cas de figures ce sont des algorithmes difficiles à mettre en oeuvre.
Il existe dans l'IDE Arduino une librairie permettant de gérer un clavier I2C; c'est cette librairie qu'on va utiliser dans notre programme de démonstration.

L'afficheur LCD:


Il peut être piloté soit en 8 bits ou en 4 bits. On va choisir de le piloter sur 4 bits, avec en plus les broches de commande de l'afficheur RS et EN, celui-ci occupera en tout 6 broches d'entrées-sorties du PCF8574. Il nous reste donc 2 broches pour connecter le buzzer et les deux diodes.

Pour gérer l'écran LCD par un PCF8574; on va utiliser aussi une librairie disponible elle aussi dans l'IDE Arduino.

Voici le schéma du montage: réalisé à l'aide de ...

Pour cet essai, on a réalisé les connexions sur une plaque à trous pastillés:

carte pastillee lcd clavier

On peut maintenant procéder à la pose de l'écran LCD, puis du clavier.

La connexion avec la carte Arduino sera assurée par du câble 6 conducteur, et des connecteur RJ12
6 Pôles 6 Conducteurs.

rj12 1 2

Différentes connexions:  1 => SDA; 2 => SCL; 3 => INT; 5 => GND; 6 => +5V

Du côté d'Arduino, SDA correspond à la broche A4; SCL correspond à la broche A5. Le +5V et la masse de Arduino serviront pour alimenter le clavier et l'afficheur.

L'entrée d'interruption se fera sur la broche d'entrées-sorties n°2 de Arduino, c'est l'interruption INT0 qui est utilisée ici.


Les connecteurs RJ12, ont été récupérés, c'est le pourquoi ils sont un peu différents de ceux du commerce, mais le brochge reste le même. On peut aussi en trouver sur le net ou sur des sites marchands.
Il faudra aussi avoir en sa possession une pince à sertir adaptée pour assembler les câbles sur les connecteurs mâles.

Pour finir, seules trois broches d'entées-sorties ont été mises à contribution sur Arduino.

Voici ce qu'on obtient après avoir assemblé tous les composants sur le module clavier-afficheur:

ensemble clavier afficheur

Exemple de programme Arduino:

Le programme d'essai consiste à piloter une serrure codée sur 5 caractères, avec une possibilité de corriger un caractère erroné.

On va donc utiliser deux librairies de l'IDE Arduino: la librairie I2CKeypad.h pour la gestion du clavier, et la librairie LiquidCrystal_PCF8574.h pour l'afficheur LCD 2 x 16 caractères.

Le programme débute avec les directives:

#include "Wire.h"
#include "I2CKeyPad.h"
#include <LiquidCrystal_PCF8574.h>

Puis les constantes d'adresses I2C du clavier et de l'afficheur LCD:

const uint8_t KEYPAD_ADDRESS = 0x20;
const uint8_t LCD_ADDRESS = 0x21;

Ensuite les instances clavier et afficheur LCD sont déclarées comme suit:

I2CKeyPad keyPad(KEYPAD_ADDRESS);
LiquidCrystal_PCF8574 lcd(LCD_ADDRESS, LCD_EN, LCD_RS, LCD_P4, LCD_P5, LCD_P6, LCD_P7);

Pour l'afficheur on va définir où seront connectées toutes les broches, pour son fonctionnement.

Ensuite on définit le tableau de caractères du clavier. A noter dans ce tableau les constantes N et F qui pourraient s'afficher en cas de défaut de frappe; par exemple deux touches appuyées en même temps.

Puis, les constantes d'interruption, à savoir la broche et la constante volatile utilisée dans la routine d'interruption appelée keyChange.

Ensuite vient le sous-programme d'initialisation setup()

Juste une petite remarque; dans cette routine setup() on a ajouté l'instruction

uint8_t index = keyPad.getKey();

Cette instruction permet de forcer l'état du PCF8574, de façon à ce que celui-ci puisse générer l'interruption nécessaire si on veut faire fonctionner la clavier en interruption. Toutes fois si on choisit le mode polling au lieu des interruptions pour gérer le clavier, il n'y aura pas besoin de rajouter cette instruction dès le départ dans la rubrique setup().

Le programme principal loop() se chargera d'abord de vérifier si la varaiable keyChange est positionnée à "1". Cet état est positionné par la routine d'interruption, qui à chaque fois qu'une touche est appuyée. Le programme principal va donc appeler les fonctions de gestion du clavier pour voir la frappée, puis va effectuer le traitement en conséquence. Notre code secret ici est égal à 1256B.

Ci-joint le listing complet du programme:

///////////////////////////////////////////////////////////////
//
// Ver:
// Date:
// By JtBB #include "Wire.h" //By Brian T.Park #include "I2CKeyPad.h" //By Rob Tillaart #include <LiquidCrystal_PCF8574.h> //By Matthias Hertel const uint8_t KEYPAD_ADDRESS = 0x20; const uint8_t LCD_ADDRESS = 0x21; I2CKeyPad keyPad(KEYPAD_ADDRESS); char keys[] = {'1','2','3','A', // '4','5','6','B', // '7','8','9','C', // '*','0','#','D', // 'N','F'}; // N = NoKey, F = Fail //LCD constants pins are connected on pcf8574 IO pins #define LCD_RS 0 #define LCD_EN 1 #define LCD_P4 4 #define LCD_P5 5 #define LCD_P6 6 #define LCD_P7 7 // // set the LCD address to 0x21 for a 16 chars and 2 line display LiquidCrystal_PCF8574 lcd(LCD_ADDRESS, LCD_EN, LCD_RS, LCD_P4, LCD_P5, LCD_P6, LCD_P7); //IRQ pin const int IRQPIN = 2; // volatile flag used in IRQ routine volatile bool keyChange = false; //variables for password #define pwd_length 6 char Data[pwd_length]; //input password is stored here pwd_length char Password[pwd_length] = "1256B"; byte data_count = 0; //counter for characters entry char custom_key; //caracter to hold key input //Here we have define also 2 custom characters byte dotOff[] = { 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110, 0b00000, 0b00000 }; byte dotOn[] = { 0b00000, 0b01110, 0b11111, 0b11111, 0b11111, 0b01110, 0b00000, 0b00000 }; //Interrupt subroutine void keyChanged() { keyChange = true; } // void setup() { Serial.begin(115200); Wire.begin(); //Call the connection wire lcd.begin(16, 2); // initialize the lcd //Character 1 --> dot Off; character 2 --> dot On lcd.createChar(1, dotOff); lcd.createChar(2, dotOn); // NOTE: PCF8574 will generate an interrupt on key press and release. pinMode(IRQPIN, INPUT); // attachInterrupt(digitalPinToInterrupt(IRQPIN), keyChanged, FALLING); keyChange = false; uint8_t index = keyPad.getKey(); //set initial state of pcf8574 IO lcd.home(); lcd.clear(); lcd.setCursor(3, 0); lcd.print("Hello And"); lcd.setCursor(3, 1); lcd.print("Wellcome!"); delay(2000); lcd.clear(); lcd.print("code:"); lcd.setCursor(5, 1); //cursor to lcd's 2nd line lcd.print("\01\01\01\01\01 ");//clear data on display and //print empty dots } // //Write data to pcf8574 to turn on/off led or buzzer void write_data(byte val) { Wire.beginTransmission(0x21); Wire.write(val); Wire.endTransmission(); } // void sound(void) { for (unsigned char a = 10; a > 0; a--) { delayMicroseconds(800); write_data(0b00000100); delayMicroseconds(800); write_data(0x00000000); } } //clear data from Data array void clearData(void){ while (data_count !=0){ Data[data_count--] = 0; } return; } // void loop(void) { if (keyChange) { delay(30); //delay for debouncing if (keyChange) { uint8_t index = keyPad.getKey(); // only after keyChange is handled it is time reset the flag keyChange = false; if (index != 16) //More than 2 keys are pressed at the same time { if (keys[index] != '*'){ Data[data_count] = keys[index]; lcd.setCursor(5 + data_count, 1); lcd.print("\02"); //comment for debug sound(); data_count++; }else {if (keys[index] == '*'){ if(data_count !=0){ sound(); data_count--; lcd.setCursor(data_count + 5, 1); lcd.print("\01"); //comment for debug lcd.setCursor(data_count + 5, 1); } } } } } } //Password length is reached? if (data_count == pwd_length-1){ lcd.setCursor(5, 1); if(!strcmp(Data, Password)){//Password //As the password is correct lcd.print("Unlocked!"); write_data(0b00001000); //add here other IO pin to drive relay delay(4000); write_data(0b00000000); //disable also the pin previously added }else{ //As the password is not correct lcd.print("Incorrect"); delay(3000); } lcd.setCursor(5, 1); //cursor to lcd's 2nd line lcd.print("\01\01\01\01\01 ");//clear data on display clearData(); } } // // -- END OF FILE --

Test de fonctionnement sur Arduino et Seeeduino:

 

Avant de poursuivre, la figure ci-dessous donne le brochage de ce module Seeeduino:

seeeduino broches

La carte Seeeduino est compatible avec les programmes Arduino, pour peu que certains registres spéciaux de l'Atmega328P ne soient pas mis à contribution pour être utilisés par le contrôleur ARM Cortex M0 de Seeeduino.

Il faudra aussi noter que cette carte délivre, et ne supporte qu'une tension de 3V3 sur chacune de ses entrées-sorties. D'où la présence nécessaire d'un adaptateur réversible 3V3 => 5 Volts dans ce montage. On peut aussi alimenter l'ensemble par une alimentation +5V externe, au cas où l'alimentation délivrée par l'USB ne fournit pas assez de puissance.

La carte à base de Seeeduino a été aussi montée sur plaque à trous pastillés.

 

On n'a pas pu résister à la tentation d'ajouter un connecteur pour un petit module relais fait maison, doté d'un relais de 10A/250V, de résistance de bobine 60 Ohms, commandée en +5V; pour simuler jusqu'au bout l'interface de puissance avec une gâche ou un autre dispositif de puissance. Le signal de commande est délivré par la sortie "Sig" du connecteur.

carte relais 2

On peut aussi opter pour un module relais du commerce comme celui de la photo ci-dessous; il faudra bien tenir compte du pouvoir de coupure du relais, si on veut commander des charges de puissance et de la tension de la bobine:

carte relais du commerce

Pour ce qui est du programme, quelques modifications mineures ont été apportées. La broche d'interruption se trouve désormais sur la broche d'entrées-sorties numéro 1 au lieu de la numéro 2 sur Arduino reservée pour l'interruption INT0. Ce choix a été fait au hasard car toutes les broches de Seeeduino peuvent fonctionner en interruption. La sortie "Sig" est commandée par la broche d'entrées-sorties 2 de Seeeduino.

Dans le programme précédent, les lignes suivantes ont été modifiées:

//IRQ pin
const int IRQPIN = 1;

Puis la ligne d'entrées-sorties destinée au relais a été rajoutée:

#define RELAY 2

Puis dans le setup, il ne faudra pas oublier de déclarer cette broche en sortie:

pinMode(RELAY, OUTPUT);

Exemple de programme pour Seeeduino :

///////////////////////////////////////////////////////////////
//
// Ver:
// Date:
// By JtBB #include "Wire.h" //By Brian T.Park #include "I2CKeyPad.h" //By Rob Tillaart #include <LiquidCrystal_PCF8574.h> //By Matthias Hertel const uint8_t KEYPAD_ADDRESS = 0x20; const uint8_t LCD_ADDRESS = 0x21; I2CKeyPad keyPad(KEYPAD_ADDRESS); char keys[] = {'1','2','3','A', // '4','5','6','B', // '7','8','9','C', // '*','0','#','D', // 'N','F'}; // N = NoKey, F = Fail //LCD constants pins are connected on pcf8574 IO pins #define LCD_RS 0 #define LCD_EN 1 #define LCD_P4 4 #define LCD_P5 5 #define LCD_P6 6 #define LCD_P7 7 // // set the LCD address to 0x21 for a 16 chars and 2 line display LiquidCrystal_PCF8574 lcd(LCD_ADDRESS, LCD_EN, LCD_RS, LCD_P4, LCD_P5, LCD_P6, LCD_P7); //IRQ pin const int IRQPIN = 1; //pin 2 for Arduino Uno // volatile flag used in IRQ routine volatile bool keyChange = false; //Relay pin #define RELAY 2 //Output for relay //variables for password #define pwd_length 6 char Data[pwd_length]; //input password is stored here pwd_length char Password[pwd_length] = "1256B"; byte data_count = 0; //counter for characters entry char custom_key; //caracter to hold key input //Here we have define also 2 custom characters byte dotOff[] = { 0b00000, 0b01110, 0b10001, 0b10001, 0b10001, 0b01110, 0b00000, 0b00000 }; byte dotOn[] = { 0b00000, 0b01110, 0b11111, 0b11111, 0b11111, 0b01110, 0b00000, 0b00000 }; //Interrupt subroutine void keyChanged() { keyChange = true; } // void setup() { //Serial.begin(115200); Wire.begin(); //Call the connection wire lcd.begin(16, 2); // initialize the lcd //Character 1 --> dot Off; character 2 --> dot On lcd.createChar(1, dotOff); lcd.createChar(2, dotOn); // NOTE: PCF8574 will generate an interrupt on key press and release. pinMode(IRQPIN, INPUT); // attachInterrupt(digitalPinToInterrupt(IRQPIN), keyChanged, FALLING); keyChange = false; pinMode(RELAY,OUTPUT); //to control relay digitalWrite(RELAY,LOW); uint8_t index = keyPad.getKey(); //set initial state of pcf8574 IO lcd.home(); lcd.clear(); lcd.setCursor(3, 0); lcd.print("Hello And"); lcd.setCursor(3, 1); lcd.print("Wellcome!"); delay(2000); lcd.clear(); lcd.print("code:"); lcd.setCursor(5, 1); //cursor to lcd's 2nd line lcd.print("\01\01\01\01\01 ");//clear data on display and //print empty dots } // //Write data to pcf8574 to turn on/off led or buzzer void write_data(byte val) { Wire.beginTransmission(0x21); Wire.write(val); Wire.endTransmission(); } // void sound(void) { for (unsigned char a = 10; a > 0; a--) { delayMicroseconds(800); //enough for me to hear buzzer sound write_data(0b00000100); delayMicroseconds(800); write_data(0x00000000); } } //clear data from Data array void clearData(void){ while (data_count !=0){ Data[data_count--] = 0; } return; } // void loop(void) { if (keyChange) { delay(30); //delay for debouncing if (keyChange) { uint8_t index = keyPad.getKey(); // only after keyChange is handled it is time reset the flag keyChange = false; if (index != 16) //More than 2 keys are pressed at the same time { if (keys[index] != '*'){ Data[data_count] = keys[index]; lcd.setCursor(5 + data_count, 1); lcd.print("\02"); //comment for debug sound(); data_count++; }else {if (keys[index] == '*'){ if(data_count !=0){ sound(); data_count--; lcd.setCursor(data_count + 5, 1); lcd.print("\01"); //comment for debug lcd.setCursor(data_count + 5, 1); } } } } } } //Password length is reached? if (data_count == pwd_length-1){ lcd.setCursor(5, 1); if(!strcmp(Data, Password)){//Password //As the password is correct lcd.print("Unlocked!"); write_data(0b00001000); //add here other IO pin to drive relay digitalWrite(RELAY, HIGH); //drive relay on delay(4000); write_data(0b00000000); //desactivate also the pin previously added digitalWrite(RELAY, LOW); //drive relay on }else{ //As the password is not correct lcd.print("Incorrect"); delay(3000); } lcd.setCursor(5, 1); //cursor to lcd's 2nd line lcd.print("\01\01\01\01\01 ");//clear data on display clearData(); } } // // -- END OF FILE --

On peut aussi voir le résumé de cet article en visualisant le lien ci-dessous:

Conclusion:

On peut dire que l'interface I2C constitue une bonne alternative pour augmenter le nombre d'entrées-sorties de Arduino. On a aussi vu qu'on peut profiter de la puissance de Seeeduino en lui ajoutant aussi des entrées-sorties suplémentaires avec la même interface.

Jusqu'à 128 entrées-sorties tout ou rien de plus grâce à l' I2C peut être considéré comme un compromis intéressant pour ce type d'amélioration. Comparativement si on avait choisi un bus SPI, il aurait fallut aussi 16 lignes supplémentaires pour la fonction Chip Select de chaque composant, et l'économie des broches escomptée aurait été réduite.

JtBB

Ce site web utilise des cookies

Certains d’entre eux sont essentiels pour son fonctionnement et d’autres nous aident à améliorer l’expérience utilisateur (cookies traceurs). Vous pouvez décider vous-même si vous autorisez ou non ces cookies. Merci de noter que, si vous les rejetez, certaines fonctionnalités du site pourront être défaillantes.