//////////////////////////////////////////////////////////// // // // Horloge Parlante MP3 avec module RTC // // Et DFPlayer Mini pour lecture mp3 // // Mise à l'Heure + Choix sonneries // // // // Arnaud Houllemare 2017 // // // // This code is released under GPLv2 license. // // // //////////////////////////////////////////////////////////// #include // Librairie du module horloge #include // Librairie du lecteur DFPlayer #include #include // Librairie de gestion de l'EEProm pour sauvegarde des options LiquidCrystal lcd(12, 11, 5, 4, 3, 2); #define SayIt 6 // Pin de lecture forcée #define mahH 9 // Pin MàH Heures #define mahM 8 // Pin MàH Minutes #define cloche 10 // Pin Choix Sonnerie #define Lec 7 // Pin Affichage #define tempo 650 // Tempo d'attente lors de la lecture DS3231 rtc(SDA, SCL); // Initialise le module RTC // DS3231: SDA pin -> Arduino Analog 4 or the dedicated SDA pin // SCL pin -> Arduino Analog 5 or the dedicated SCL pin String TimeStr; // Chaîne reçue du ùodule RTC int unitsOut; // Numéro du message à lire pour les unités int tensOut; // Numéro du message à lire pour les dizaines int units; // unités issues de la décomposition d'un nombre int tens; // dizaines issues de la décomposition d'un nombre int HeuDec; // Heures en nombre récupéré du RTC int MinDec; // Minutes en nombre récupéré du RTC int SecDec; // Secondes en nombre récupéré du RTC boolean Lit; // Switch Heure parlée/Sonnerie. Par défaut: Sonnerie int TypSon; // Type Sonnerie index 0 à 3. Par défaut: 0 int Sonnerie; // Numéro de fichier de Sonnerie void setup() { if(EEPROM.read(0)==255) EEPROM.put(0,0); // initialise le TypSon à 0, la première fois TypSon = EEPROM.read(0); // Récupère le type de sonnerie dans l'EEProm if(EEPROM.read(1)==255) EEPROM.put(1,0); // initialise le toggle Lit à 0, la première fois Lit = EEPROM.read(1); // Récupère le toggle lecture dans l'EEprom if(EEPROM.read(2)==255) EEPROM.put(2,29); // initialise le fichier sonnerie à 29 (Reveil Officier), la première fois Sonnerie = EEPROM.read(2); // Récupère le toggle lecture dans l'EEprom lcd.begin(16, 2); // Initialise le LCD 16 Chars 2 Lignes Serial.begin(9600); // Ouverture d'une communication série mp3_set_serial (Serial); // DFPlayer assigné à la connection série mp3_set_volume (25); // Volume de lecture (0 à 30) rtc.begin(); // Initialise l'objet RTC // rtc.setTime(23, 13, 0); // Mise à l'heure logicielle au moins une fois initialement. (HH,MM,SS format 24h) pinMode(SayIt, INPUT); // Pin de lecture de l'heure en mode INPUT pinMode(mahH, INPUT); // Pin Mise à L'Heure Heures en mode INPUT pinMode(mahM, INPUT); // Pin Mise à L'Heure Minutes en mode INPUT pinMode(cloche, INPUT); // Pin choix sonnerie en mode INPUT pinMode(Lec, INPUT); // Pin heure parlée/sonnerie en mode INPUT } void loop() { TimeStr=(rtc.getTimeStr()); // Récupération de la chaîne de l'heure du module RTC MinDec = TimeStr.substring(3,5).toInt(); // On extrait et convertit en int les minutes HeuDec = TimeStr.substring(0,2).toInt(); // On extrait et convertit en int les heures SecDec = TimeStr.substring(6,8).toInt(); // On extrait et convertit en int les secondes AfficheHeure(); // Affichage de l'Heure if ((MinDec == 0) && (SecDec < 1)) // Si c'est l'heure pleine et dans la première seconde // Astuce pour ne sonner qu'une seule fois { if (HeuDec == 0) // il est minuit { if (Lit) LitHeure(HeuDec,MinDec); // Lit l'heure si actif else for (int i=1; i <= 12; i++) Sonne(Sonnerie); // on sonne douze fois } else if (HeuDec <= 12) // entre minuit et midi inclus { if (Lit) LitHeure(HeuDec,MinDec); // Lit l'heure si actif else for (int i=1; i <= HeuDec; i++) Sonne(Sonnerie); // on sonne l'heure } else if (HeuDec < 24) // si c'est l'après midi { if (Lit) LitHeure(HeuDec,MinDec); // Lit l'heure si actif else for (int i=1; i <= (HeuDec % 12); i++) Sonne(Sonnerie); // on enlève 12 (pour éviter les 24 coups à minuit) et son sonne l'heure } } if ((MinDec == 30) && (SecDec < 1)) { if (Lit) LitHeure(HeuDec,MinDec); // Lit l'heure si actif else Sonne(Sonnerie); // Si c'est la demie, on sonne une fois } if (digitalRead(mahH) == HIGH) ajusteH(); // Ajuste l'Heure if (digitalRead(mahM) == HIGH) ajusteM(); // Ajuste Les Minutes if (digitalRead(cloche) == HIGH) ChoixSonnerie(); // Choix de la sonnerie if (digitalRead(Lec) == HIGH) LitAuto(); // Allume ou eteind l'affichage (la lumiere reste allumée !) if (digitalRead(SayIt) == HIGH) // Si le bouton de demande d'heure est activé, on dit l'heure et les minutes { Sonne(Sonnerie); // Pour faire joli, on sonne une fois avant de dire l'heure LitHeure(HeuDec,MinDec); } lcd.setCursor(0,1); // 1er char 2eme Ligne lcd.print(" "); // Efface la deuxième ligne } void Parse(int nombre) // Décomposition d'un nombre en unités et dizaines { units = nombre % 10; // les unités sont le nombre modulo 10 tens = (nombre - units) /10; // le nombre de dizaine est le nombre moins les unités divisé par 10 // Traitement des unités unitsOut = units; // Pour les unités, pas de soucis, la variable est juste là pour l'élégance du script // Traitement des dizaines if (tens == 1 && units > 0) // Gestion du cas particulier de la langue française 11,12,13...19 { tensOut = 10 + units; // met dans tensOut le numéro destiné à dire 11,12,13 etc unitsOut = 11; // met dans unitsOut un numéro de cas qui ne fait rien } else { tensOut = tens; // met dans tensOut le numéro destiné à dire 20,30,40 ou 50 if (units == 0) unitsOut = 11; // si unités=0, met dans unitsOut un numéro de cas qui ne fait rien } } void SayUnit(int n) { switch(n) { case 0: Parle(1); break; // Dit 'Zéro' case 1: Parle(2); break; // Dit 'Une' case 2: Parle(3); break; // Dit 'Deux' case 3: Parle(4); break; // Dit 'Trois' case 4: Parle(5); break; // Dit 'Quatre' case 5: Parle(6); break; // Dit 'Cinq' case 6: Parle(7); break; // Dit 'Six' case 7: Parle(8); break; // Dit 'Sept' case 8: Parle(9); break; // Dit 'Huit' case 9: Parle(10); break; // Dit 'Neuf' case 10: Parle(11); break; // Dit 'Dix' case 11: break; } } void SayTens(int n) { switch(n) { case 1: Parle(11); break; // Dit 'Dix' case 2: Parle(21); break; // Dit 'Vingt' case 3: Parle(22); break; // Dit 'Trente' case 4: Parle(23); break; // Dit 'Quarante' case 5: Parle(24); break; // Dit 'Cinquante' case 11: Parle(12); break; // Dit 'Onze' case 12: Parle(13); break; // Dit 'Douze' case 13: Parle(14); break; // Dit 'Treize' case 14: Parle(15); break; // Dit 'Quatorze' case 15: Parle(16); break; // Dit 'Quinze' case 16: Parle(17); break; // Dit 'Seize' case 17: Parle(18); break; // Dit 'Dix-Sept' case 18: Parle(19); break; // Dit 'Dix-Huit' case 19: Parle(20); break; // Dit 'Dix-Neuf' } } void LitHeure(int HeuDec,int MinDec) { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Je dis l'heure..."); Parle(27); // 'Il est...' Parse(HeuDec); // décompose les heures en dizaines d'heures et unités d'heures LitNombre(); // Lit la décomposition Parle(25); // Dit 'heures' (sans liaison...) if (MinDec != 0) // Si les minutes sont différentes de 0, lit les minutes { Parse(MinDec); // décompose les minutes en dizaines de minutes et unités de minutes LitNombre(); // Lit la décomposition Parle(26); // Dit 'minutes' } } void LitNombre() { SayTens(tensOut); // Dit les dizaines if (tensOut == 0 && unitsOut == 11) Parle(1); // Dit 'Zéro' si nécessaire (dizaines = 0 et unités = 0 par le pointeur 11) if (tensOut > 1 && unitsOut == 1) Parle(28); // Ajoute si nécessaire 'Et' SayUnit(unitsOut); // Sinon dit les unités } void Parle(int n) // séquence de lecture du mp3 et sa temporisation nécessaire ! { mp3_play(n); delay(tempo); } void ajusteH() { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Mise a l'heure"); // Affiche ce qu'il fait HeuDec++; // Incrémente l'Heure HeuDec = HeuDec % 24; // modulo 24 pour être entre 0 et 23 rtc.setTime(HeuDec, MinDec, 0); // Met le RTC à l'heure AfficheHeure(); // Affiche l'Heure (nécessaire ?) delay(500); // Courte pause } void ajusteM() { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Mise a l'heure"); // Affiche ce qu'il fait MinDec++; // Incrémente les Minutes MinDec = MinDec % 60; // modulo 60 pour être entre 0 et 59 rtc.setTime(HeuDec, MinDec, 0); // Met le RTC à l'heure AfficheHeure(); // Affiche l'Heure (nécessaire ?) delay(500); // Courte pause } void AfficheHeure() { lcd.setCursor(4, 0); // 1er char 1eme ligne lcd.print(TimeStr.substring(0,2)); // Affiche l'Heure lcd.print(':'); // Affiche les char de séparation lcd.print(TimeStr.substring(3,5)); // Affiche les Minutes lcd.print(':'); // Affiche les char de séparation lcd.print(TimeStr.substring(6,8)); // Affiche les Secondes } void LitAuto() // Choix de la lecture ou pas de l'heure à l'heure pleine { Lit = !Lit; // switch booléen if (Lit) // si 'vrai' { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Lecture Heure"); // Affiche l'état du switch delay(500); // courte pause } else // si 'faux' { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Sonnerie"); // Affiche l'état du switch delay(500); // courte pause } EEPROM.put(1,Lit); // Mémorise dans l'EEProm le toggle de Lecture } void ChoixSonnerie() { TypSon++; // TypSon est le choix de sonnerie de 0 à 4 TypSon = TypSon % 5; // Prend le modulo 5 switch(TypSon) // Selon le numéro du choix de sonnerie { case 0: { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Reveil Officier"); // Affiche le type de Sonnerie (Un ding enregistré du réveil d'officier de petite Mamie !) Sonnerie = 29; // Numéro mp3 correspondant delay(500); // Pause EEPROM.put(2,Sonnerie); // Mémorise dans l'EEProm le numéro de fichier de sonnerie EEPROM.put(0,TypSon); // Mémorise dans l'EEProm le type de sonnerie break; // Fin du traitement } case 1: { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Carillon"); // Affiche le type de Sonnerie (Un dong enregistré chez Guilhem !) Sonnerie = 30; // Numéro mp3 correspondant delay(500); // Pause EEPROM.put(2,Sonnerie); // Mémorise dans l'EEProm le numéro de fichier de sonnerie EEPROM.put(0,TypSon); // Mémorise dans l'EEProm le type de sonnerie break; // Fin du traitement } case 2: { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Coucou"); // Affiche le type de Sonnerie (Un coucou téléchargé !) Sonnerie = 31; // Numéro mp3 correspondant delay(500); // Pause EEPROM.put(2,Sonnerie); // Mémorise dans l'EEProm le numéro de fichier de sonnerie EEPROM.put(0,TypSon); // Mémorise dans l'EEProm le type de sonnerie break; // Fin du traitement } case 3: { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Poule"); // Affiche le type de Sonnerie (Une Poule téléchargée !) Sonnerie = 32; // Numéro mp3 correspondant delay(500); // Pause EEPROM.put(2,Sonnerie); // Mémorise dans l'EEProm le numéro de fichier de sonnerie EEPROM.put(0,TypSon); // Mémorise dans l'EEProm le type de sonnerie break; // Fin du traitement } case 4: { lcd.setCursor(0, 1); // 1er char 2eme Ligne lcd.print("Vache"); // Affiche le type de Sonnerie (Une Vache téléchargée !) Sonnerie = 33; // Numéro mp3 correspondant delay(500); // Pause EEPROM.put(2,Sonnerie); // Mémorise dans l'EEProm le numéro de fichier de sonnerie EEPROM.put(0,TypSon); // Mémorise dans l'EEProm le type de sonnerie break; // Fin du traitement } } } void Sonne(int Sonnerie) { mp3_play(Sonnerie); // Joue la sonnerie sélectionnée delay(2 * tempo); // Une tempo une peu plus longue entre chaque Sonnerie }