En ce qui me concerne, c'est pour mettre au fond du jardin afin d'avoir de la lumière.
Voici la suite, le schéma et les plans mécaniques
et le code
/* Programme Pansol1
syph60 02/2025
Ce programme assure le fonctionnement d’un support pivotant pour panneau solaire.
Le panneau est fixé sur un support pivoté avec une roue dentée mue par une courroie et un moteur pas à pas.
Le moteur pas à pas est commandé par le programme et utilise une carte d’interface dédiée.
Le driver du moteur pas à pas est câblé de manière à ce qu'une impulsion génère 1/32eme de tour, et
l'ensemble poulies-courroie assure une démultiplication x2.8. Il faut donc 17920 impulsion pour un tour complet,
et 50 impulsions pour tourner le panneau de un degré.
La synchonisation avec la rotation du soleil est pilotée via une carte horloge RTC interfacée en I2C.
Enfin, le montage est doté d’un afficheur LCD 20x4 avec une carte d’interface I2C. Cet afficheur permet
d’indiquer la date et l’heure, et certains paramètres du système.
Un bouton poussoir permet d’effectuer la mise à l’heure du système (solution de secours). Sinon, la carte est équipée d'un
module bluetooth HC05 qui permet via un terminal bluetooth depuis un smartphone, d'envoyer quelque commandes :
Date : JJ/MM/AAAA - HH:MM:SS -> Effectue la mise à l'heure de l'horloge RTC"
Init : -> Lance la séquence d'initialisation
GoAng : Valeur -> Positionne le panneau à l'angle indiqué
Rapport : -> Affiche un rapport d'état du système
Reboot : -> Redemarre le contrôleur
Au démarrage du système on effectue un balayage droite-gauche pour retrouver le point central au moyen d'un fin de course optique
positionné vers le SUD (90°) ensuite le panneau est positionné en fonction de la position du soleil correspondant à l'heure du moment.
Le suivi du soleil est actualisé toutes les quinze minute entre 6H00 et 22H00. En dehors de cette plage le panneau est positionné
à l'Est (gauche).
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h> // Bibliothèque contrôle afficheur LCD via liaison I2C
#include <stdio.h>
#include <stdarg.h>
#include <RTClib.h> // Librairie horloge temps réèl
# include <SoftwareSerial.h> // Librairie pour liaison série
// Constantes
const uint8_t CstepPin = 2; // Définition sortie commande STEP
const uint8_t CdirPin = 3; // Définition sortie commande DIR
const uint8_t CEnPin = 4; // Définition sortie Enable
const uint8_t CEstate = 9; // Entrée D9 pour état module Bluetooth
const uint8_t CErx = 11; // Entrée D11 réception Bluetooth coté controleur
const uint8_t CEtx = 10; // Entrée D10 émission Bluetooth coté controleur
const int Cangmin = 0; // Angle mini (le plus à gauche) avec le Sud = 90°
const int Cangmax = 225; // Angle maxi (le plus à droite)
const int CCpto = 5; // Définition entrée FDC optique
const int CPBut = 6; // N° entrée du bouton poussoir mise à l'heure
const int VD13 = 13; // N° entrée builtin diode
const int Ctpexit = 5000; // Valeur tempo de sortie du mode réglage (5s)
const int Ctpuls = 1500; // Durée impulsion pour temps long
const int CCliRythm = 300; // Rythme du clignotement affichage mise à l'heure
const unsigned long CTOlcd = 300000; // Time out pour éteindre le LCD = 5 minutes
const int CEbat = A3 ; // Entrée analogique A3 pour tension batterie
const float CKu = 0.012389666; // 5*(7850 + 4656) / (4656 * 1024), Coefficient pour lecture tension batterie
// Fin section constantes
// Variables
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4); // Declaration écran LCD
int Vsteps = 17920; // Variable nombre de pas pour 180 degrés (ex 19200)
int Vstepdeg = 50; // Nombre de pas pour tourner d'un angle de 1,004464 degrés
int VmsDelay = 500; // Variable durée impulsion moteur
int Vangcur = 90; // Valeur angle courant d'apès les déplacements du moteur
uint16_t VHrs = 0; // Heure courante
uint16_t VMin = 0; // Minutes courantes
uint16_t VSec = 0; // Secondes courantes
uint16_t VMois = 1; // Mois courant
uint16_t VJour = 1; // Jour courant
uint16_t VAnnee = 2025; // Année courante
unsigned long VExit =0; // Timeout pour sortie du mode réglage
char VBuff[20]; // Buffer de texte de la ligne affichée (date + heure)
int VBuffidx = 0; // Index buffer
int VResuBP = 0; // Retour fonction test du bouton poussoir
bool VReglage = false; // Fonction réglage date et heure active
byte VStpReg = 0; // Step réglage 1 = Jour, 2 = mois, 3 = année, 4 = heure...
byte Vprem = true; // Flag utilisé pour détecter le premier passage dans une boucle
byte V4print = false; // Flag utilisé pour déclencher l'impression du buffer texte
byte VCliRythm = false; // Flag utilisé pour le clignotement affichage réglage date/heure
byte VcurX = 0; // Position curseur sur la ligne
byte VcurY = 0; // Numéro de ligne
int Vparam = 0;
byte Vtemps = false;
RTC_DS3231 Vrtc;
DateTime VHrcour;
int VStatut = 0; // Variable statut général 0 = Ok, 1 = défaut
bool VFinjour = false; // Variable utilisée pour le retour à gauche après 22H
bool VStlcd = true; // Etat allumé lcd
int VResSerial = 0; // Resultat fonction F_HeureSerie
int VAng = 0; // Angle pour commande moteur à partir d'un ordre passé par la liaison série
float VUbat = 0.0; // Tension batterie
SoftwareSerial VBt(CErx, CEtx); // Bluetooth Emission, Réception coté controleur
int VEtatBT; // Valeur entrée état module Bluetooth
// Fin section variables
void setup() {
Serial.begin(9600);
VBt.begin(9600); // initialisation Bluetooth
VBt.println("");
lcd.init(); // Initialisation afficheur
lcd.clear(); // Effacement afficheur
lcd.backlight(); // Allumage Afficheur
pinMode(CstepPin, OUTPUT); // Sortie STEP
pinMode(CdirPin, OUTPUT); // Sortie sens de rotation
pinMode(CEnPin, OUTPUT); // Sortie Enable
digitalWrite(CEnPin, LOW); // On éteint le moteur
pinMode(CCpto, INPUT); // Entrée FDC optique
pinMode(CEstate, INPUT); // Entrée état module Bluetooth
Vrtc.begin(); // Démarrage horloge temps réèl
delay(500);
F_Serprintln("* Fonction setup *");
F_Serprintln(" Commandes disponibles :");
F_Serprintln("Date : JJ/MM/AAAA - HH:MM:SS -> Effectue la mise à l'heure de l'horloge RTC");
F_Serprintln("Init : -> Lance la séquence d'initialisation");
F_Serprintln("GoAng : Valeur -> Positionne le panneau à l'angle indiqué");
F_Serprintln("Rapport : -> Affiche un rapport d'état du système");
F_Serprintln("Reboot : -> Redemarre le contrôleur");
VEtatBT = digitalRead(CEstate); // lecture état module Bluetooth (haut = connecté, bas = non connecté)
F_Serprint("Etat Bluetooth = ");
if (VEtatBT == HIGH) {F_Serprintln("connecté");} else {F_Serprintln("non connecté");}
VHrcour = Vrtc.now(); // lecture date et heure courante
VAnnee = VHrcour.year(); // initialisation avec la date courante
VJour = VHrcour.day();
VMois = VHrcour.month();
VHrs = VHrcour.hour();
VMin = VHrcour.minute();
VSec = VHrcour.second();
F_Serprint("Date : ");
F_Serprint(String(VAnnee));
F_Serprint("/");
F_Serprint(String(VMois));
F_Serprint("/");
F_Serprint(String(VJour));
F_Serprint(" Heure : ");
F_Serprint(String(VHrs));
F_Serprint(":");
F_Serprint(String(VMin));
F_Serprint(":");
F_Serprintln(String(VSec));
lcd.setCursor(0,1); // Positionnement curseur en haut
lcd.print("Angle : ");
lcd.setCursor(0,2);
lcd.print("Statut : ");
lcd.setCursor(14,2);
VUbat = F_Ubat();
F_Serprintln("Tension batterie = " + String(VUbat) + "V");
lcd.print(VUbat,2);
lcd.setCursor(0,3);
lcd.print("Temperature : ");
lcd.print(Vrtc.getTemperature());
F_Faitligne();
lcd.setCursor(0,0);
lcd.print(VBuff);
F_init();
F_Position();
F_RefreshLCD();
F_Serprintln("Statut : " + String(VStatut));
} // Fin fonction setup
void loop()
{
F_Timer2(900); // (délais 15 minutes entre deux lectures)
if (Vtemps)
{
F_Serprint("Vtemps");
Vtemps = false;
F_Position(); // Positionnement panneau
}
F_gestheure();
VResSerial = F_HeureSerie();
if (VResSerial == 1) // Mise à l'heure
{
Vrtc.adjust(DateTime(VAnnee, VMois, VJour, VHrs, VMin, VSec)); // Mise a l'heure saisie sur term USB ou Bluetooth
F_Serprintln("Nouvelle date : " + String(VJour) + "/" + String(VMois) +"/" + String(VAnnee) + " - " + String(VHrs)+ ":" + String(VMin) + ":" + String(VSec) + " - Statut = " + String(VStatut));
F_Position(); // Repositionnement panneau selon nouvelle heure
}
if (VResSerial == 2) // Commande balayage d'initialisation
{
F_Serprintln("Commande Init :");
F_init();
}
if (VResSerial == 3) // Commande Go Left, positionnement panneau à un angle donné
{
F_Serprintln("Commande GoAng Left :");
F_Gole(VAng);
}
if (VResSerial == 4) // Commande Go Right, positionnement panneau à un angle donné
{
F_Serprintln("Commande GoAng Right :");
F_Gori(VAng);
}
if (VResSerial == 5) // Impression du rapport sur terminal USB ou Bluetooth
{
F_Serprintln("Rapport :");
F_Serprintln("Heure :" + String(VHrs) + ":" + String(VMin) + ":" + String(VSec) + " , angle courant : " + String(Vangcur));
F_Serprintln("Tension batterie = " + String(VUbat,2) + "V");
F_Serprint("Statut : ");
if (VStatut == 0) {F_Serprint("OK ");} else {F_Serprint("NOK");}
F_Serprintln(" Température = " + String(Vrtc.getTemperature(),2));
}
if (VResSerial == 6) // Commande Reboot
{
F_reboot();
}
F_TOlcd(); // Check si pas de timeout pour l'afficheur LCD
if(VReglage) {F_lcdmajreg();}
} // Fin fonction loop
void F_Position() // Routine utilisée pour positionner le panneau en fonction de l'heure courante
{
unsigned long VTjour; // nombre de secondes écoulées depuis minuit
int VNangle; // Nouvelle position
if (VStatut == 0) // On s'assure que le panneau à bien été positionné pendant la séquence init
{
VHrcour = Vrtc.now(); // lecture date et heure courante
VHrs = VHrcour.hour();
VMin = VHrcour.minute();
VSec = VHrcour.second();
VTjour = ((unsigned long) VHrs * 3600) + ((unsigned long) VMin * 60) + (unsigned long) VSec; // Calcul nombre de secondes depuis minuit
if (VTjour > 21600 && VTjour < 79200) // On ne positionne le panneau qu'entre 6H et 22H
{
if (VFinjour == true) // premier mouvement à 6H
{
VFinjour = false;
F_Serprintln("En piste, il est 6H");
F_init;
}
VNangle = int(VTjour * 0.004167) - 90; // Le nouvel angle est calculé relativement à la position zéro (midi) = 90°
F_Serprintln("Heure :" + String(VHrs) + ":" + String(VMin) + ":" + String(VSec) + " , nouvel angle : " + String(VNangle) + " angle courant : " + String(Vangcur));
if (VNangle != Vangcur)
{
if (VNangle > Vangcur)
{
F_Gori(VNangle - Vangcur);
F_Serprintln("Direction droite, nouvel angle : " + String(Vangcur));
delay(1000); // On attend une seconde, puis
//digitalWrite(CEnPin, HIGH); // on éteint le moteur
} else
{
F_Gole(Vangcur - VNangle);
F_Serprintln("Direction gauche, nouvel angle : " + String(Vangcur));
delay(1000); // On attend une seconde, puis
//digitalWrite(CEnPin, HIGH); // on éteint le moteur
}
}
}
if (VTjour > 79200 && VFinjour == false) // Après 22H
{
VFinjour = true; // On renvoie le panneau à gauche
F_Gole(Vangcur-1);
F_Serprintln("Retour au bercail, il est 22H");
}
}
} // Fin fonction F_Position
void F_Gole(int P_angle){ // Commande du panneau vers la gauche d'un angle donné en paramètre
digitalWrite(CEnPin, LOW);
digitalWrite(CdirPin, HIGH);
if((Vangcur - P_angle) > Cangmin)
{
Vangcur -= P_angle;
for(int x = 0; x < (Vstepdeg * P_angle); x++)
{
digitalWrite(CstepPin, HIGH);
delayMicroseconds(VmsDelay);
digitalWrite(CstepPin, LOW);
delayMicroseconds(VmsDelay);
}
}
//digitalWrite(CEnPin, HIGH); // On éteint le moteur
} // Fin fonction F_Gole
void F_Gori(int P_angle){ // Commande du panneau vers la droite d'un angle donné en paramètre
digitalWrite(CEnPin, LOW); // activation moteur
digitalWrite(CdirPin, LOW); // sélection sens de rotation
if((Vangcur + P_angle) < Cangmax)
{
Vangcur += P_angle; // mise à jour de la valeur Angle courant
for(int x = 0; x < (Vstepdeg * P_angle); x++)
{
digitalWrite(CstepPin, HIGH);
delayMicroseconds(VmsDelay);
digitalWrite(CstepPin, LOW);
delayMicroseconds(VmsDelay);
}
}
//digitalWrite(CEnPin, HIGH); // On éteint le moteur
} // Fin fonction F_Gori
/* Cette fonction lit les données dispo sur la liaison série devant avoir la forme :
Date : JJ/MM/AAAA - HH:MM:SS
Les espaces n'ont pas d'importance, ni les caratères séparateurs utilisés, par contre les tous les séparateurs doivent
être présent, et tous les champs doivent avoir 2 caratères sauf l'année qui en a 4
Si des champs sont omis, leur valeur est fixée à zéro */
int F_HeureSerie()
{
String VInbuff; // Buffer entrée liaison série
String VStr; // Chaine temporaire de travail
int VIdx; // Utilisé pour déterminerla position des caratères
int VNbcar = 0; // Nombre de caractères à lire sur la liaison série
int VRet = 0; // Variable de retour
VRet = 0;
VIdx = -1;
if (Serial)
{
if ( (VNbcar = F_Seravailable()) > 0) // Caractères disponibles sur liaison série
{
VInbuff = F_Trimall(F_SerreadString()); // Supression des espaces
VIdx = F_Trouvestr(VInbuff, "Date:", 0); // Vérification syntaxique indiquant une commande de mise à l'heure
if (VIdx != -1)
{
VStr = VInbuff.substring(VIdx+5, VIdx + 7); // Récupération des paramètres de réglage en format numérique
VJour = VStr.toInt();
VStr = VInbuff.substring(VIdx+8, VIdx + 10);
VMois = VStr.toInt();
VStr = VInbuff.substring(VIdx+11, VIdx + 15);
VAnnee = VStr.toInt();
VStr = VInbuff.substring(VIdx+16, VIdx + 18);
VHrs = VStr.toInt();
VStr = VInbuff.substring(VIdx+19, VIdx + 21);
VMin = VStr.toInt();
VStr = VInbuff.substring(VIdx+22, VIdx + 24);
VSec = VStr.toInt();
VRet = 1;
}
VIdx = F_Trouvestr(VInbuff, "Init:", 0); // Vérification syntaxique indiquant une commande d'initialisation
if (VIdx != -1)
{
VRet = 2;
}
VIdx = F_Trouvestr(VInbuff, "GoAng:", 0); // Vérification syntaxique indiquant une commande d'initialisation
if (VIdx != -1)
VAng = VInbuff.substring(VIdx+6, VIdx + 8).toInt(); // Récupération des paramètres de réglage en format numérique
{
if (VAng > 0 && VAng != Vangcur)
{
if (VAng > Vangcur)
{
VAng = VAng - Vangcur;
VRet = 3; // Exécuter commande Gole
} else
{
VAng = Vangcur - VAng;
VRet = 4; // Exécuter commande Gori
}
}
}
VIdx = F_Trouvestr(VInbuff, "Rapport:", 0); // Vérification syntaxique indiquant une commande d'impression du rapport
if (VIdx != -1)
{
VRet = 5;
}
VIdx = F_Trouvestr(VInbuff, "Reboot:", 0); // Vérification syntaxique indiquant une commande Reboot
if (VIdx != -1)
{
VRet = 6;
}
}
}
return VRet;
} // Fin fonction F_HeureSerie
/* Cette fonction recherche la première occurence d'une chaine A, dans une chaine B
en commençant à la position X */
int F_Trouvestr(String PChn, String PChx, int PDeb)
{
int VPos1 = 0;
int VPos2 = 0;
int VLgn = 0;
int VLgx = 0;
int VResu = 0;
String VSubch;
VResu = -1;
VLgn = PChn.length(); // Longueur chaine origine
VLgx = PChx.length(); // Longueur chaine recherchée
VPos1 = PDeb;
do
{
VPos2 = VPos1 + VLgx;
if (VLgn > 0 && VLgx > 0 && VPos2 < VLgn)
{
VSubch = PChn.substring(VPos1, VPos2);
if (VSubch != PChx)
{
VPos1++;
} else {VResu = VPos1;}
}
} while (VResu == -1 && (VPos1+VLgx) < VLgn);
return VResu;
} // Fin fonction F_Trouvestr
/* Cette fonction retourne la chaine passée en paramètre en supprimant tous les espaces */
String F_Trimall(String PChn)
{
String VNewstr = "";
for (int i = 0; i < PChn.length(); i++)
{
if (PChn[i] != ' ') {VNewstr += PChn[i];}
}
return VNewstr;
} // Fin fonction F_Trimall
void F_init() /* Au démarrage on fait un balayage complet à droite puis un retour
à gauche en recherchant la position centrale avec le Fin De Course optique.*/
{
int VCpto = HIGH; // Variable lecture FDC optique , entrée pullup
VCpto = digitalRead(CCpto);
while(Vangcur < (Cangmax-1) && VCpto == HIGH) // On amène le panneau complètement à droite ou FDC optique trouvé
{
F_Gori(1);
VCpto = digitalRead(CCpto);
}
if (VCpto == HIGH)
{
while(Vangcur > (Cangmin+1) && VCpto == HIGH) // On amène le panneau à gauche jusqu'au FDC optique
{
F_Gole(1);
VCpto = digitalRead(CCpto);
}
}
delay(1000); // On attend une seconde, puis
//digitalWrite(CEnPin, HIGH); // on éteint le moteur
if (VCpto == HIGH) {VStatut = 1;} else // Erreur, on a pas détecté le FDC optique, sinon l'angle courant est égal à 90°
{
Vangcur = 90;
VStatut = 0; //Statut OK
}
} // Fin fonction F_init
// Fonction pour effacer les caratères restant sur une ligne de l'écran LCD
void F_ClrEOL(int P_posx, int P_posy, int P_valeur)
{
int V_lgstr = 0;
int V_nb = 0;
String V_val = String(P_valeur);
if(P_posx >= 0 && P_posx < 20 && P_posy >= 0 && P_posy < 5)
{
V_lgstr = V_val.length();
V_nb = 20 - P_posx - V_lgstr;
if(V_nb > 0)
{
lcd.setCursor(P_posx + V_lgstr, P_posy);
for(int i = 1; i <= V_nb; i++) {lcd.print(" ");}
}
}
} // Fin fonction ClrEOL
void F_RefreshLCD() // Mise à jour afficheur LCD
{
lcd.setCursor(10,1);
lcd.print(Vangcur);
F_ClrEOL(10, 1, Vangcur);
lcd.setCursor(10,2);
if (VStatut == 0) {lcd.print("OK ");} else {lcd.print("NOK");}
VUbat = F_Ubat(); // Lecture tension batterie
lcd.setCursor(14,2);
lcd.print(" ");
lcd.setCursor(14,2);
lcd.print(VUbat,2);
lcd.print("V");
lcd.setCursor(14,3);
lcd.print(" ");
lcd.setCursor(14,3);
lcd.print(Vrtc.getTemperature());
} // Fin fonction F_RefreshLCD
/* La fonction F_MajTime lit l'horloge RTC et
effectue la mise à jour de la date et de l'heure à chaque seconde écoulée*/
void F_MajTime()
{
static unsigned long VTempscour = millis();
static unsigned long VTempsprec = VTempscour;
VHrcour = Vrtc.now(); // lecture date et heure courante
VAnnee = VHrcour.year(); // initialisation avec la date courante
VJour = VHrcour.day();
VMois = VHrcour.month();
VHrs = VHrcour.hour();
VMin = VHrcour.minute();
VSec = VHrcour.second();
VTempscour = millis(); // Valeur courante compteur interne en millisecondes
if(VTempscour - VTempsprec > CCliRythm) {VCliRythm = true;} else {VCliRythm = false;}
if(VTempscour - VTempsprec >= 1000) // Si une seconde écoulée
{
VTempsprec = VTempscour;
F_Faitligne(); // Préparation de l'affichage
lcd.setCursor(0,0);
lcd.print(VBuff);
}
} // fin F_MajTime*/
/* Fonction F_in, cette fonction teste si l'argument Vms existe
dans la liste d'arguements variable passée à la fonction, VNbe
indique le nombre d'arguments passés. va_list, va_start, va_arg et va_end
sont des macros définies dans stdarg.h
Cette fonction fonctionne uniquement avec le type integer.
La fonction renvoie un booléen si l'occurence est trouvée, mais on aurait pu
aussi bien retourner le numéro de l'élément dans la liste des arguments*/
bool F_in(int Vms, int VNbe, ...)
{
bool trouve = false;
va_list(L_ms); // initialisation de la variable liste
int Velem;
va_start(L_ms, VNbe);
do
{
Velem = va_arg(L_ms, int); // Retourne le prochain élément de la liste
if(Velem == Vms){
trouve = true;
break;
}
VNbe -=1;
} while(VNbe);
va_end(L_ms);
return(trouve);
} // fin F_in
/* Routine utilisée pour gérer un bouton poussoir, les fonctions sont :
Bouton appuyé puis relâché moins de une seconde (appui court) renvoie 1
Bouton appuyé puis relâché plus d'une seconde (appui long) renvoie 2
Bouton resté appuyé et non relâché après une seconde, renvoie 3
Autres situations renvoie 0
*/
int F_Pbut(int PInput)
{
static int VEtatprec = LOW;
int VEtatcur = LOW;
static bool VFront = false; // Flag permettant de détecter un front montant ou descendant
static bool VLong = false; // Flag indiquant un appui long
int VRetour = 0;
static unsigned long VCurti = millis(); // Valeur courante du compteur interne de l'Arduino
static unsigned long VPrevti = VCurti; // Valeur précédente du compteur interne de l'Arduino
static bool VFirst = true; // Flag permettant de détecter le premier appel à la fonction
static int VNblec = 10; // Nombre de cycles répétitifs, utilisé pour le filtrage des rebonds
int Vbout0 = HIGH;
int Vbout1 = HIGH;
if(VFirst) // Premier passage dans la fonction
{
VFirst = false;
pinMode(PInput, INPUT_PULLUP); // Initialisation entrée bouton poussoir
}
Vbout1 = digitalRead(PInput); // Acquisition entrée du bouton
if (Vbout1 == Vbout0) {VNblec -=1;} else // Filtrage rebonds
{
VNblec = 10;
Vbout0 = Vbout1;
}
if (VNblec <= 0)
{
VNblec = 0;
VEtatcur = Vbout1; // Validation état bouton après filtrage des rebonds
}
// if (VEtatcur == LOW) {digitalWrite(VD13, HIGH);} else {digitalWrite(VD13, LOW);} // Allumage builtin diode
VCurti = millis(); // Actualisation temps courant
if(VEtatcur == LOW) {VEtatcur = HIGH;} else {VEtatcur = LOW;} // Inversion du sens d'action du bouton (à cause de INPUT_PULLUP)
if(VEtatcur != VEtatprec) // Changement d'état du bouton poussoir
{
VEtatprec = VEtatcur;
VFront = true;
if (VStlcd == false) // Si le LCD est éteint on le rallume
{
lcd.backlight();
lcd.display();
VStlcd = true;
}
VPrevti = VCurti;
if (VEtatcur == LOW) // Front descendant
{
if(VLong) {VRetour = 2;} else {VRetour = 1;}
VFront = false;
VLong = false;
}
} else if (VFront) // Pas de changement d'état mais front actif
{
if(VCurti - VPrevti > Ctpuls) // Bouton resté appuyé depuis plus d'une seconde
{
VLong = true;
VRetour = 3;
}
else
{
VRetour = 0;
}
}
else {VRetour = 0;}
return(VRetour);
} // Fin fonction F_Pbut
void F_Faitligne() // Contruction de la première ligne affichée sur l'écran LCD, Date et Heure
{
VBuffidx = 0;
VBuffidx += sprintf(VBuff + VBuffidx, "%02d", VJour);
VBuffidx += sprintf(VBuff + VBuffidx, "%s", "/");
VBuffidx += sprintf(VBuff + VBuffidx, "%02d", VMois);
VBuffidx += sprintf(VBuff + VBuffidx, "%s", "/");
VBuffidx += sprintf(VBuff + VBuffidx, "%2d", VAnnee);
VBuffidx += sprintf(VBuff + VBuffidx, " ");
VBuffidx += sprintf(VBuff + VBuffidx, "%02d", VHrs);
VBuffidx += sprintf(VBuff + VBuffidx, "%s", ":");
VBuffidx += sprintf(VBuff + VBuffidx, "%02d", VMin);
VBuffidx += sprintf(VBuff + VBuffidx, "%s", ":");
VBuffidx += sprintf(VBuff + VBuffidx, "%02d", VSec);
} // Fin fonction F_Faitligne
/* Cette fonction assure un mode de secours pour la mise à l'heure de l'horloge à l'aide du bouton
poussoir. Un appuis long sur le bouton fait entrer dans le mode réglage et affiche les jours en
clignotant sur l'écrans lcd. Un appuis court incrémente la valeur traitée de 1. Un nouvel appuis long
fait passer à la valeur suivante (mois, année, heures, minutes et secondes). Le mode réglage est quitté
si pas d'action sur le bouton pendant 3 secondes, et la nouvelle heure&date sont inscrites dans l'horloge
RTC. NB : l'action sur le bouton poussoir rallume l'écran LCD si celui-ci était éteint suite à un timeout.*/
void F_gestheure()
{
if (VReglage == false) {F_MajTime();} // On ne fait pas la mise à jour de l'heure pendant le réglage
VResuBP = F_Pbut(CPBut); // Acquisition état du bouton poussoir
if(VResuBP == 2 & VReglage == false) // Activation du mode réglage si action longue sur le BP
{
VReglage = true;
VStpReg =0;
F_Serprintln("Réglage activé");
F_Faitligne();
F_Serprintln(VBuff);
V4print = false;
VExit = millis(); // Initialisation timeout pour sortie du mode réglage
VcurX = 0;
VcurY = 0;
Vparam = VJour;
}
if(VResuBP == 0 & VReglage == true & VStpReg == 0) // On avance d'un cran quand le BP est relâché
{
VStpReg +=1;
F_Serprintln("Réglage jours");
}
if(VResuBP == 1 & VReglage == true & VStpReg > 0 & Vprem == true) // Traitement impulsion courte sur le BP
{
Vprem = false;
VExit = millis(); // Raz timeout
switch (VStpReg)
{
case 1: // Jours
VJour +=1;
V4print = true;
if(VJour == 31 & F_in(VMois,4,4, 6, 9, 11)) // cas du 30 pour les mois à 30 jour
{
VJour = 1;
}
else if(VJour == 32 & F_in(VMois,7,1, 3, 5, 7,8, 10,12)) // cas du 31 pour les mois à 31 jours
{
VJour = 1;
if(VMois == 13)
{
VJour = 1;
}
}
else if(VJour == 30 & VMois == 2 & VAnnee % 4 == 0) // cas de février pour les années bissextiles
{
VJour = 1;
}
else if(VJour == 29 & VMois == 2 & VAnnee % 4 > 0) // cas de février pour les années non bissextiles
{
VJour = 1;
}
Vparam = VJour;
break;
case 2: // Mois
VMois += 1;
V4print = true;
if(VMois == 13) {VMois = 1;}
Vparam = VMois;
break;
case 3: // Annee
VAnnee +=1;
V4print = true;
Vparam = VAnnee;
break;
case 4: // Heures
VHrs +=1;
V4print = true;
if(VHrs == 24) {VHrs = 0;}
Vparam = VHrs;
break;
case 5: // Minutes
VMin += 1;
V4print = true;
if(VMin == 60) {VMin = 0;}
Vparam = VMin;
break;
case 6: // Secondes
VSec += 1;
V4print = true;
if(VSec == 60) { VSec = 0;}
Vparam = VSec;
break;
}
}
if(VResuBP == 2 & VReglage == true & VStpReg > 0 & Vprem == true) // Traitement impulsion longue sur le BP
{
VStpReg +=1;
VExit = millis(); // raz timeout
if(VStpReg > 6)
{
VStpReg =1;
}
switch ( VStpReg) // Mise à jour affichage
{
case 1:
F_Serprintln("Réglage jours");
VcurX = 0;
Vparam = VJour;
break;
case 2:
F_Serprintln("Réglage mois");
VcurX =3;
Vparam = VMois;
break;
case 3:
F_Serprintln("Réglage années");
VcurX = 6;
Vparam = VAnnee;
break;
case 4:
F_Serprintln("Réglage heures");
VcurX = 12;
Vparam = VHrs;
break;
case 5:
F_Serprintln("Réglage minutes");
VcurX = 15;
Vparam = VMin;
break;
case 6:
F_Serprintln("Réglage secondes");
VcurX = 18;
Vparam = VSec;
break;
}
Vprem = false;
}
if(VResuBP == 0 & VReglage == true & VStpReg > 0 & Vprem == false) {Vprem = true;}
if (V4print) // Affichage de la ligne en cas de changement d'une valeur
{
V4print = false;
}
if ((millis() - VExit > Ctpexit) & VReglage == true) // Sortie mode réglage après time out
{
VReglage = false;
F_Serprintln("Sortie du mode réglage");
Vrtc.adjust(DateTime(VAnnee, VMois, VJour, VHrs, VMin, VSec));
}
} // Fin fonction V_Gestheure
// Fonction F_lcdmajreg, utilisée pour l'affichage en mode clignotant de la valeur en cours de réglage (mise à l'heure)
void F_lcdmajreg()
{
static unsigned long VTpcur = 0;
static unsigned long VTprec = 0;
VTpcur = millis();
if(VTpcur - VTprec > CCliRythm)
{
VTprec = VTpcur;
if (VCliRythm == false) {VCliRythm = true;} else {VCliRythm = false;}
F_Serprint(String(VCliRythm));
}
lcd.setCursor(VcurX, VcurY);
if (VCliRythm == false) {lcd.print(" ");} else {lcd.print(Vparam);}
} // Fin fonction F_lcdmajreg
// Fonction F_Timer attente d'un délais en ms, sans interruption du programme
void F_Timer(unsigned long Ptemps)
{
static unsigned long VTimeP = millis();
static unsigned long VTimeC;
VTimeC = millis();
if (VTimeC - VTimeP > Ptemps)
{
Vtemps = true;
VTimeP = VTimeC;
}
} // Fin fonction F_Timer
// Fonction F_Timer2 attente d'un délais en s, sans interruption du programme
void F_Timer2(unsigned long Ptemps)
{
unsigned long VTjour; // nombre de secondes écoulées depuis minuit
VHrcour = Vrtc.now(); // lecture date et heure courante
VHrs = VHrcour.hour();
VMin = VHrcour.minute();
VSec = VHrcour.second();
VTjour = (VHrs * 3600) + (VMin * 60) + VSec; // Calcul nombre de secondes depuis minuit
static unsigned long VTimeP = VTjour;
static unsigned long VTimeC;
VTimeC = VTjour;
if (VTjour == 0) { VTimeP =0;} // Passage de minuit
if (VTimeC - VTimeP >= Ptemps)
{
Vtemps = true;
VTimeP = VTimeC;
}
} // Fin fonction F_Timer2
void F_TOlcd() // Fonction time out pour éteindre l'écran LCD
{
static unsigned long VTp = millis(); // Initialisation temps courant et temps actuel
static unsigned long VTc = VTp;
VTc = millis();
if (VTc - VTp > CTOlcd) // Le time out CTOlcd est dépassé
{
if (VStlcd == true)
{
lcd.noDisplay();
lcd.noBacklight();
VStlcd = false;
VTp = VTc;
}
}
} // Fin fonction F_TOlcd
float F_Ubat() // Lecture de la tension d'alimentation VIN (soit, la batterie)
{
int VA3;
VA3 = analogRead(CEbat);
return float(VA3) * CKu; // La valeur de CKu dépend des valeurs exactes du pont diviseur sur l'entrée CEbat
} // Fin fonction F_Ubat
void F_Serprint(String PTxt) // Remplace la fonction print et oriente la sortie soit ves le terminal soit vers Bluetooth
{
VEtatBT = digitalRead(CEstate); // lecture état module Bluetooth (haut = connecté, bas = non connecté)
if (VEtatBT == HIGH)
{
VBt.print(PTxt);
} else
{
Serial.print(PTxt);
}
} // Fin fonction F_Serprint
void F_Serprintln(String PTxt) // Remplace la fonction println et oriente la sortie soit vers le terminal soit vers Bluetooth
{
VEtatBT = digitalRead(CEstate); // lecture état module Bluetooth (haut = connecté, bas = non connecté)
if (VEtatBT == HIGH)
{
VBt.println(PTxt);
} else
{
Serial.println(PTxt);
}
} // Fin fonction F_Serprintln
int F_Seravailable() // Remplace la fonction Serial.available et prend soit l'entrée du terminal soit celle de Bluetooth
{
int VResu = 0;
VEtatBT = digitalRead(CEstate); // lecture état module Bluetooth (haut = connecté, bas = non connecté)
if (VEtatBT == HIGH)
{
VResu = VBt.available();
} else
{
VResu = Serial.available();
}
return VResu;
} // Fin fonction F_Seravailable
String F_SerreadString() // Remplace la fonction Serial.readString et prend soit l'entrée du terminal soit celle de Bluetooth
{
String VResu = "";
VEtatBT = digitalRead(CEstate); // lecture état module Bluetooth (haut = connecté, bas = non connecté)
if (VEtatBT == HIGH)
{
VResu += VBt.readString();
} else
{
VResu += Serial.readString();
}
return VResu;
} // Fin fonction F_SerreadString
void F_reboot() // Fonction reboot redémarre le contrôleur
{
F_Serprintln("Reboot...");
asm volatile ("jmp 0");
} // Fin fonction F_Reboot