Gen 2 808 Posté(e) février 26, 2018 Partager Posté(e) février 26, 2018 (modifié) re: Pinpin.. il n'y a rien de personnel dans ce que j'ai écrit plus haut. Pas de jugement, pas de reproche. Je suis dans le partage, je vois que ça merde, je viens vous aider.. rien de plus, rien de moins C'est juste que ce code est mal foutu. Je vais te le réécrire et le documenter, ensuite tu pourras le modifier comme bon te semble. Là tu apprendras plus vite Pour le moment je me monte un Uno avec LCD 4x20 en I2c , DHT22 et boutons poussoirs histoire d'avoir le même matos que vous. Je pense que le code sera bouclé pour fin de semaine en fonction de mon emploi du temps. ++ GEN Modifié février 26, 2018 par Gen 1 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 26, 2018 Auteur Partager Posté(e) février 26, 2018 (modifié) Re @Gen Pinpin !! Alors excuse moi de la véracité de ma réponse ! Ton message m'as un poil cassé le moral .. je code comme un fou ( avec mes pieds certes ) a trouver des bouts de code que je peux rajouter , quand je trouve , faut que je les rajoutes , un fois rajouté y'as toujours une c***lles quelque part que je passe 4 heures a corrigés ... Je suis partis de ce code , car je n'ai aucun moyen de créer le miens par mes propres moyen et c'était le seul aussi lisible que facile a modifier pour une personne n'ayant jamais touché au langage C , et le matos requis est plus que minimaliste d'ou l’intérêt de partir de cette source pour un partage futur Ne te prend pas la tête a ré-écrire le code avec tes projets en cours , j'imagine bien que tu n'as pas que ça as faire .. après si tu comme dis un de ces 4 tu le ponds .. je t'en remercie d'avance , car je pense qu'un bon code , bien fait et bien commenté ne pourras que être bénéfique a la compréhension du langage C ou même mieux aider a comprendre le code du MiniGrowDuino pour touts les novice du forum Encore désolé du quiproquo @Gen, merci d'avoir rectifié ! Passe une bonne journée ! Modifié février 26, 2018 par Omgprod 1 Lien à poster Partager sur d’autres sites
Gen 2 808 Posté(e) février 26, 2018 Partager Posté(e) février 26, 2018 (modifié) Pas de problème.. Mais je vais quand même ré-écrire le code. Car j'aime me faire mal PS : Je n'ai pas de boulot, car les circuits imprimés commandés n'arrivent que la semaine prochaine Et en plus j'ai préparé le matos, et je me suis fait une 'cht'it' boîte à boutons ++ GEN Modifié février 26, 2018 par Gen 1 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 26, 2018 Auteur Partager Posté(e) février 26, 2018 Ahhh ! bah je pense que tu vas faire des heureux y compris moi ! entre Papy qui est très libertin et toi qui fait dans Sado-masochisme je pense que je me suis peut être trompé dans le titre du site internet ou je me trouve .. je me permet de te poser une ou deux questions ? vue que je t'ai sous la main Sur ce code que tu vas pondre y'aura t'il un exemple du fonctionnement RTC , car ce code fonctionne avec la lib DS1307 .. mais je n'ai que des horloges 3132 ( sa marche, mais la lib date et elle affiche la date et l'heure mais ne contient pas grand chose d'autre ) je voulais savoir aussi , si avec l'eeprom de l'horloge 3132 l'on pourrait automatiser une sauvegarde des datas de la DHT pour faire une page comme toi temp & humidité minimum, maximum suivis de la date a laquelle ces datas on était sauvegardés ?? Je sais pas si je me suis bien exprimé , je m'excuse d'avance pour le maux de tête occasionné Lien à poster Partager sur d’autres sites
Gen 2 808 Posté(e) février 26, 2018 Partager Posté(e) février 26, 2018 yop. Sur ce code que tu vas pondre y'aura t'il un exemple du fonctionnement RTC , car ce code fonctionne avec la lib DS1307 .. mais je n'ai que des horloges 3132 ( sa marche, mais la lib date et elle affiche la date et l'heure mais ne contient pas grand chose d'autre ) ?? bizarre ce que tu me racontes, lors des test de la 3132 pour mon GRCC, je n'ai rencontré aucun problème de ce coté là, car justement je voulais mettre un circuit RTC qui contient un oscillateur à l'inverse du DS1307 je voulais savoir aussi , si avec l'eeprom de l'horloge 3132 l'on pourrait automatiser une sauvegarde des datas de la DHT pour faire une page comme toi temp & humidité minimum, maximum suivis de la date a laquelle ces datas on était sauvegardés ?? Une EEPROM à une durée de vie limitée en terme de lectures et écriture, l'utiliser pour stocker autre chose que des parametres de setup est une mauvaise idée Dans mon logiciel, les données sont stockées dans un array Maintenant si tu veux que les données climatiques soient enregistrées de manière non volatile, 2 possibilités s'offrent à toi 1 - une sd card (perso je n'aime pas, on rencontre vite des problèmes) 2 - utiliser une ram avec maintient des données tel qu'un circuit 23LCV1024 (1Mb RAM) la solution que j'ai perso retenue pour mon shield DUE Voila ++ GEN 1 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 26, 2018 Auteur Partager Posté(e) février 26, 2018 Re @Gen Laisse tombé pour la lib j'ai dis des conne***s comme d'hab ^^ j'ai melangé mon plus est mon moins ( pour changer ) oui j'ai lu ça a propos de sa durée de vie limité a force d'écrire dessus , c'est pour ça que je venais a toi ...sur cette question , une pichenette derrière les oreilles ... et ou est ce que tu stock ces données volatile ? PS : What the F... , tu connectes l'I2C de l’écran sur les ports de l'horloge ... ( pourquoi je trouve pas ça moi !!! ) Spoiler ce genre de partage aussi est très appréciable !! Thanks ! Lien à poster Partager sur d’autres sites
Gen 2 808 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 (modifié) LOL Je stocke les données volatile dans une table mémoire (Array) ci dessous un exemple les lignes 0 et 1 temperature Min Max les lignes 2 et 3 humidité Min Max Exemples d'utilisation void resetMinMax() { int t = temperature; int h = humidity; for(int i = 0; i<=1 ; i++) { minMaxTable[0] = t; minMaxTable[1] = now.day(); minMaxTable[2] = now.month(); minMaxTable[3] = now.hour(); minMaxTable[4] = now.minute(); } for(int i = 2; i<=3 ; i++) { minMaxTable[0] = h; minMaxTable[1] = now.day(); minMaxTable[2] = now.month(); minMaxTable[3] = now.hour(); minMaxTable[4] = now.minute(); } } void fillMinMaxTable(int indice, int newValue) { minMaxTable[indice][0] = newValue; minMaxTable[indice][1] = now.day(); minMaxTable[indice][2] = now.month(); minMaxTable[indice][3] = now.hour(); minMaxTable[indice][4] = now.minute(); } void recordMinMax() { int newt = temperature; int newh = humidity; if(newt < minMaxTable[0][0]){fillMinMaxTable(0,newt);} if(newt > minMaxTable[1][0]){fillMinMaxTable(1,newt);} if(newh < minMaxTable[2][0]){fillMinMaxTable(2,newh);} if(newh > minMaxTable[3][0]){fillMinMaxTable(3,newh);} } ++ GEN Modifié février 27, 2018 par Gen 1 Lien à poster Partager sur d’autres sites
tizi59 17 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 salut les gars ! Pas de soucis Omgprod Citation heure = RTC.get(DS1307_HR, true); minut = RTC.get(DS1307_MIN, false); seconde = RTC.get(DS1307_SEC, false); date = RTC.get(DS1307_DATE, false); mois = RTC.get(DS1307_MTH, false); annee = RTC.get(DS1307_YR, false); temps_perso = (heure * 100) + minut; //creation temps_perso l"erreur est ici non ? minut = RTC.get(DS1307_MIN, false); temps_perso = (heure * 100) + minut; regarde bien ta oublié le "e" sur minute peace 1 Lien à poster Partager sur d’autres sites
Gen 2 808 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 re: Cette librairie c'est du concentré de daube en bouteille. Faut trouver une librairie avec l'utilisation du DateTime now ++ GEN 1 Lien à poster Partager sur d’autres sites
tizi59 17 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 re ! il y a 1 minute, Gen a dit: re: Cette librairie c'est du concentré de daube en bouteille. Faut trouver une librairie avec l'utilisation du DateTime now ++ GEN oui c'est le mieux à faire Lien à poster Partager sur d’autres sites
tizi59 17 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 (modifié) re perso j' ais fais ça pour la tmax/hmax et tmin/hmin et sa fonctionne la tempMax et Min est "sauvegarder ?" pendant 24h int tmin=100; int tmax=0; int hmin=100; int hmax=0; float t = dht.readTemperature(); //dht22 float h = dht.readHumidity(); int TabEvt [1][3] = {{9,6,00},}; // grow int TabEvt1 [1][3] = {{11,00,00},}; // flow if(t >= tmax) { tmax=t; } if(t <= tmin) { tmin=t; } if(h >= hmax) { hmax=h; } if(h <= hmin) { hmin=h; } / je réinitialise la tmax/hmax et tmin/hmin a l'allumage à l'aide d'un tableau d' événement if ((digitalRead(A2) == true) && now.hour() == TabEvt [0][0] && now.minute() == TabEvt [0][1] && now.second() == TabEvt [0][2] ) {tmax=0;tmin=100;hmax=0;hmin=100;} if ((digitalRead(A2) == false) && now.hour() == TabEvt1 [0][0] && now.minute() == TabEvt1 [0][1] && now.second() == TabEvt1 [0][2] ) {tmax=0; tmin=100;hmax=0;hmin=100;} a++ Modifié février 27, 2018 par tizi59 1 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 27, 2018 Auteur Partager Posté(e) février 27, 2018 Yo @tizi59 & @Gen Déjà je vous remercie pour les exemples et les bouts de code avec explications ! la table mémoire Array c'est vraiment sympa de votre part ! Et oui c'est ce que je voulais dire hier a propos de la lib je parlais pas de la 3132 mais de la DS1307 qui apars afficher l'heure ne fait pas grand chose T'es chaud coquin de @tizi59 Lien à poster Partager sur d’autres sites
tizi59 17 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 lol pourquoi ça ! il y a 3 minutes, Omgprod a dit: T'es chaud coquin de @tizi59 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 27, 2018 Auteur Partager Posté(e) février 27, 2018 PTDRRR , par ce que tu codes et tu te débrouilles plus que bien a ce que je vois !! donc oui t'es chaud , mon gars ! Lien à poster Partager sur d’autres sites
tizi59 17 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 (modifié) il y a 7 minutes, Omgprod a dit: PTDRRR , par ce que tu codes et tu te débrouilles plus que bien a ce que je vois !! donc oui t'es chaud , mon gars ! je suis dans le même cas que toi j'ais commencer en bidouillant le code à windaub pour finalement le changer entièrement gràce aux infos de gen et de google mais je suis novice avec peut-etre quelque moi d'avance sur toi lol. j 'ai "codé" un automate pour mon aqua au passage lol peace Modifié février 27, 2018 par tizi59 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 27, 2018 Auteur Partager Posté(e) février 27, 2018 Ah bah sa fait plaisir a entendre , je comprend mieux les j’aimes sur des commentaires GG a toi maggle ! Partit de rien , mais tu gères pas mal la fougère Peace Tizi 1 Lien à poster Partager sur d’autres sites
tizi59 17 Posté(e) février 27, 2018 Partager Posté(e) février 27, 2018 il y a 13 minutes, Omgprod a dit: Ah bah sa fait plaisir a entendre , je comprend mieux les j’aimes sur des commentaires GG a toi maggle ! Partit de rien , mais tu gères pas mal la fougère Peace Tizi merci pour le moment mon code gère uniquement croissance et floraison donc la prochaine étape seras l'ajout de la gestion du séchage peace. Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) février 27, 2018 Auteur Partager Posté(e) février 27, 2018 Spoiler il y a 33 minutes, tizi59 a dit: merci pour le moment mon code gère uniquement croissance et floraison donc la prochaine étape seras l'ajout de la gestion du séchage peace. ça va le plus dur est fait alors bonne journée @tizi59 Lien à poster Partager sur d’autres sites
Omgprod 80 Posté(e) mars 1, 2018 Auteur Partager Posté(e) mars 1, 2018 (modifié) Salut les loulous Vu que @Gen m'as provisoirement mis au chômage technique Je suis tombé sur une source qui date un peu mais un truc vraiment sympa , qui m'intéresse assez vu le coté WIFI et le coût hardware ( wemos + dht = 7.50euro ) je le modifie pour ma box en ce moment même , j'aimerais avoir vos avis si cela pourrait vous intéressez tout n'est pas encore fonctionnel , et pour le moment c'est héberger sur le wifi ( réseau local ) je vais voir pour rediriger le port et d'avoir un accès a l’interface ou que l'on soit Je vous montre : Spoiler pour le moment rien de très dur un montage prototype et le résultat : Les GPIOS ne sont pas fonctionnel pas grand chose ne l’était , peut être que sa viendras J'attend vos avis et si intéressés je vous ferais un beau tuto avec tout les liens d’origines (y) Bonne Aprem ! Modifié mars 1, 2018 par Omgprod Lien à poster Partager sur d’autres sites
Gen 2 808 Posté(e) mars 1, 2018 Partager Posté(e) mars 1, 2018 (modifié) Il y a 2 heures, Omgprod a dit: Vu que @Gen m'as provisoirement mis au chômage technique Re : La récré est terminée, fini de te tourner les pouces !! Le code est restructuré et les fioritures ont été supprimées J'ai mis en remarque la création des Char car la définition n'en était pas correcte, on créé des array de 8 bytes et on n'en défini que 7 .. Cherchez l'erreur Vous trouverez dans l'archive, toutes les bonnes librairies pour faire tourner ce projet Dorénavant, vous pouvez utiliser ce que vous voulez comme RTC, toutes les définitions se trouvent dans le code, suffit de mettre en remarque ce que vous n'utilisez pas. Je n'ai bien entendu pas fait le programme à ta place, j'ai assez de taf pour le moment, mais le squelette est complet et cohérent. Il ne te reste plus qu'à arranger les écran à ta sauce et de faire un setup si nécessaire. Certaines personnes n'ont pas besoin de setup et mettent les valeurs dans le code en 'dur', chacun fera comme il lui plaira. Attention, l'assignation des pin a été modifiée !! le programme fait 330 lignes au lieu de 700.. à l'origine, ce qui fait que vous avez encore plein de place pour faire ce que vous voulez voici le récap de compil Device: atmega328p Program: 11706 bytes (35.7% Full) (.text + .data + .bootloader) Data: 741 bytes (36.2% Full) (.data + .bss + .noinit) J'ai codé de façon à ce que l'écran principal change toutes les 5 secondes sans utiliser la fonction millis() Supprimez le si vous voulez, c'est juste fourni à titre d'exemple A vous de gérer la manière de changer de mode (croissance, floraison, fin de floraison) via un setup et du code, ou à l'aide d'un interrupteur 3 positions, ou automatiquement en fonction des jours écoulés (c'est vous qui voyez) Par défaut il est mis à croissance voici le code Spoiler //#include <SPI.h> #include <LiquidCrystal_I2C.h> #include <Wire.h> #include <Arduino.h> #include <RTClib.h> #include <DHT.h> #include <DailyTimer.h> #include <CyclicTimer.h> #define DHTTYPE DHT22 #define CROISSANCE 0 #define FLORAISON 1 #define SECHAGE 2 #define DAY_DURATION_ON 0 #define DAY_DURATION_OFF 1 #define NIGHT_DURATION_ON 2 #define NIGHT_DURATION_OFF 3 #define PotPIN A0 // pas utilisé #define cyclicPin 12 #define hygroPin 11 #define extractPin 10 #define chauffagePin 9 #define lampePin 8 #define dhtPin 6 #define MIN 0 #define MAX 1 // Activez la ligne correspondante à votre type de RTC //DS1307 rtc; //RTC_Millis rtc; DS3231 rtc; //PCF8563 rtc; //PCF8583 rtc; //DS1302 rtc(CE_PIN,SCK_PIN,IO_PIN); //DS1302 rtc;// CE_PIN 4, SCK_PIN 5, IO_PIN 6 as default DailyTimer lampe(lampePin); CyclicTimer brasseur(cyclicPin); LiquidCrystal_I2C lcd(0x3F, 20, 4); DHT dht(dhtPin, DHTTYPE); DateTime now; const int UP = 1; const int DOWN = 2; const int ENTER = 3; //------------- Reglage Temperature , Hygro , Extracteur ------------ bool hygroFlag = false; bool chauffageFlag = false; bool ventilFlag = false; float t = 0.0; float h = 0.0; float chauffage_on = 23.00; float chauffage_off = 24.00; float extracteur_on = 70.00; float extracteur_off = 15.00; // MIN MAX float hygro[3][2] = {{ 20.00, 80.00}, // CROISSANCE { 20.00, 60.00}, // FLORAISON { 20.00, 50.00}}; // FIN DE floraison //les temps sont à définir en secondes (valeurs ci-dessous pour test) int brasseur_duree_jour_ON = 2; int brasseur_duree_jour_OFF = 2; int brasseur_duree_nuit_ON = 1; int brasseur_duree_nuit_OFF = 1; bool firstScreen = true; bool displayed = true; int sol = 0; int GROWMODE = 0 ; // CROISSANCE tu pourras modifier le status via un interrupteur 3 pos ou via le code ou menu etc.. void initializeLamp() { lampe.setValue(GROW_START,00,00); // 05:00 lampe.setValue(GROW_STOP ,18,00); // 23:00 lampe.setValue(FLO_START ,11,20); // 11:20 lampe.setValue(FLO_STOP ,23,20); // 23:20 } void initializeIO() { pinMode(UP, INPUT); pinMode(DOWN, INPUT); pinMode(ENTER, INPUT); for(int i = 9 ; i <= 11 ; i++) { pinMode(i, OUTPUT); digitalWrite(i, LOW); } } void charCreation() { //lcd.createChar(coeur, COEUR); //lcd.createChar(carre, CARRE); //lcd.createChar(rond, ROND); //lcd.createChar(cel, CEL); //lcd.createChar(temp, TEMP); //lcd.createChar(canna, CANNA); //lcd.createChar(goutte, GOUTTE); //lcd.createChar(ampoule, AMPOULE); } void initializeLCD() { lcd.init(); lcd.backlight(); } void initializeBrasseur() { brasseur.initialise(DAY_DURATION_ON,brasseur_duree_jour_ON); brasseur.initialise(DAY_DURATION_OFF,brasseur_duree_jour_OFF); brasseur.initialise(NIGHT_DURATION_ON,brasseur_duree_nuit_ON); brasseur.initialise(NIGHT_DURATION_OFF,brasseur_duree_nuit_OFF); } void intro() { lcd.clear(); lcd.setCursor(0,0); lcd.print(" GrowMyBox Bitch's "); lcd.setCursor(0, 1); lcd.print(" Thank's to "); lcd.setCursor(0, 2); lcd.print(" Windaube & Gen "); lcd.setCursor(2, 3); lcd.print("OMGPROD Edition "); delay(3000); lcd.clear(); } int convertTimeInMinute(int hour, int minute) { return (hour * 60) + minute; } void setup() { initializeLamp(); initializeIO(); initializeLCD(); initializeBrasseur(); //charCreation(); à activer quand la definition des bytes char sera correcte Serial.begin(9600); // a utiliser pour debugger sur le terminal dht.begin(); rtc.begin(); // rtc.adjust(DateTime(__DATE__, __TIME__)); intro(); } void catchData() { now = rtc.now(); float temporaryVal; delay(10); // ce code permet de garder la dernière valeur correcte temporaryVal = dht.readTemperature(); if(!isnan(temporaryVal)){t = temporaryVal;} delay(10); temporaryVal = dht.readHumidity(); if(!isnan(temporaryVal)){h = temporaryVal;} delay(10); //setMinMaxToMemory(); } void hygroProcess() { if(h < hygro[GROWMODE][MIN] && !hygroFlag) {hygroFlag = true;} if(h > hygro[GROWMODE][MAX] && hygroFlag){hygroFlag = false;} digitalWrite(hygroPin, hygroFlag); } void heatProcess() { if(t >= chauffage_off){chauffageFlag = false;} if(t >= chauffage_on){chauffageFlag = true;} digitalWrite(chauffagePin,chauffageFlag); } void ventilProcess() { if(t >= extracteur_on){ventilFlag = true;} if(t <= extracteur_off){ventilFlag = false;} digitalWrite(extractPin,ventilFlag); } void executeProcess() { lampe.run(convertTimeInMinute(now.hour(),now.minute()),GROWMODE); // lampe hygroProcess(); // hygro brasseur.run(now.unixtime(),lampe.isWorking()); // brasseur Serial.println(now.unixtime()); ventilProcess(); // extracteur heatProcess(); // chauffage } String formatIntNumberInXdigits(int number, int nbDigit) { String stringNumber = String(number); while(stringNumber.length() < nbDigit) { stringNumber = "0"+stringNumber; } return stringNumber; } String getTimeFormated() { return formatIntNumberInXdigits(now.hour() , 2) + ":" + formatIntNumberInXdigits(now.minute(), 2) + ":" + formatIntNumberInXdigits(now.second(), 2); } String getDateFormated() { return formatIntNumberInXdigits(now.day() , 2) + "/" + formatIntNumberInXdigits(now.month(), 2) + "/" + formatIntNumberInXdigits(now.year(), 2); } void displayMainScreen() { lcd.setCursor(0,0); lcd.print(getTimeFormated() + " " + getDateFormated()); lcd.setCursor(0,1); lcd.print("TEMP:"); lcd.print(t); lcd.setCursor(11,1); lcd.print("HR:"); lcd.print(h); lcd.setCursor(0,2); lcd.print("MODE: "); switch (GROWMODE) { case 0: lcd.print("CROISSANCE ");break; case 1: lcd.print("FLORAISON ");break; case 2: lcd.print("FIN FLORAISON");break; } lcd.setCursor(0,3); if(lampe.isWorking()) { lcd.print("LAMPE : ALLUMEE "); } else { lcd.print("LAMPE : ETEINTE "); } } String return_ON_OFF(bool value) { String label[2] = {"OFF","ON "}; return label[value]; } void displaySecondaryScreen() { lcd.setCursor(0,0); lcd.print("Chauffage :"); lcd.print(return_ON_OFF(chauffageFlag)); lcd.setCursor(0,1); lcd.print("Ventilation:"); lcd.print(return_ON_OFF(ventilFlag)); lcd.setCursor(0,2); lcd.print("Brumisateur:"); lcd.print(return_ON_OFF(hygroFlag)); lcd.setCursor(0,3); lcd.print("Brasseur :"); lcd.print(return_ON_OFF(brasseur.isWorking())); } void loop() { catchData(); // a partir d'ici les variable sont assignées ( t h et now) if(now.unixtime()%5 == 0 && displayed == false ) { lcd.clear(); firstScreen = !firstScreen; } if(firstScreen) // changement d'écran toutes les 5 secondes (exemple) { displayMainScreen(); } else { displaySecondaryScreen(); } displayed = true; if(now.unixtime()%5 !=0 && displayed == true) {displayed = false;} executeProcess(); //************************************************************************ //** A partir d'ici on peut mettre le traitement d'appuis sur un bouton pour //** rentrer dans un setup etc.. //************************************************************************ // buttonState1 = digitalRead(bp1); // buttonState2 = digitalRead(bp2); // buttonState3 = digitalRead(bp3); } l'archive complète en Zip et renommée en pdf pour pouvoir la télécharger : 180228-192511-uno.pdf edit : vous pouvez supprimer la ligne 208 Serial.print(now.unixtime()); elle sert uniquement pour faire des tests ++ GEN Modifié mars 1, 2018 par Gen 1 Lien à poster Partager sur d’autres sites
Messages recommandés