voici la dernière mouture du programme
//*************************************************************************
// GESTION DE 8 AIGUILLAGES PAR SERVOMOTEURS
//*************************************************************************
// le 04/01/2021 à 11:09
// fonctionnement testé le 07/01/2021 14:23 => OK
//=========================================================================
// I) Détection du mode de fonctionnement par BP permanent connecté sur A0
// I-a le BP est enfoncé en position basse => mode "REGLAGE"
// - sélection du servomoteur concerné sur A2
// - positionnement du servo concerné au point milieu
// - sélection de la butée à régler sur A4
// - BP enregistrement sur eeprom sur A5
// I-b le BP reste en position haute => mode "COMMANDE"
// - sélection du BP enfoncé sur A1
// - connection du servomoteur concerné au pin + 2
// - lecture des données de ce servomoteur
// - basculement de la position
// - enregistrement en eeprom
// II) Gestion des 16 leds (bicolores) concernées
// Une grande partie du code provient de http://modelleisenbahn.triskell.org/spip.php?rubrique22
//=========================================================================
#include
#include
#include
#include
LiquidCrystal_PCF8574 lcd(0x27); // set the LCD address to 0x27 for a 16 chars and 2 line display
const byte SERVO_A_ANGLE_MIN = 0;
const byte SERVO_A_ANGLE_MAX = 1;
const byte SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX = 2;
const byte SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN = 3;
const int limitAngleMin = 1300;
const int limitAngleMax = 1700;
const int PointMilieuT = 1500;
struct DescripteurServo {
Servo objetServo;
int vitesse;
int angleMin;
int angleMax;
int angleIntermediaire;
int DernierePositionServo;
int pinServo;
byte etatServo;
};
struct DescripteurServo servoMoteur[8];
const byte NON_PRESSE = 0;
const byte ENFONCE = 1;
const byte PRESSE = 2;
byte etatAutomate = NON_PRESSE;
int etatPoussoir = -1;
const byte AUCUN_EVENEMENT = 0;
const byte EVENEMENT_PRESSE = 1;
const byte EVENEMENT_RELACHE = 2;
const byte temporisation = 3; // règle la vitesse de déplacment
int valeurLectureButee; // valeur de la résistance variable transcrite en mS
int validationReglageButee; // état du bouton de validation des données
const byte pinDepartServo = 2; // les servos sont connectés à partir de D2
const byte pinBuzzer = 12; // Le buzzer est connecté sur D12
const byte pinBPChoixMode = A0; // BP choix mode sur A0
const byte pinBPAiguillages = A1; // BP aiguillages sur A1
const byte pinSwitchSelecteurServo = A2; // les 8 switches de sélection du servo à régler sur A2
const byte pinSelecteurButee = A3; // switche de sélection de la butée (gauche/droite) en cours de réglage sur A3
const byte pinRVButees = A6; // la résistance variable de réglage des butées sur A4 => A6 en production
const byte pinBPValidationReglage = A7; // le BP de validation des réglages sur A5 => A7 en production
// les drapeaux
int dernierServoReglage = -1;
byte Y = 0; // flag affichage mode REGLAGE
byte Z = 0; // flag affichage mode COMMANDE
byte X = 0; // flag affichage butée droite (angle max)
byte W = 0; // flag affichage butée gauche (angle min))
void setup()
{
Serial.begin(9600);
/* Initialisation de l'écran LCD */
Wire.begin();
Wire.beginTransmission(0x27);
int error = Wire.endTransmission();
Serial.print("Error: ");
Serial.print(error);
if (error == 0) {
Serial.println(": LCD found.");
} else {
Serial.println(": LCD not found.");
} // fin de if (error == 0)
lcd.begin(16, 2); // initialize the lcd
lcd.setBacklight(255);lcd.clear();lcd.home();
/* =========================== */
/* Initialisation des servos */
int numServo;
for (numServo = 0; numServo < 8; numServo++) {
/* lecture des derniers réglages dans l'eeprom */
/* Initialisation si première utilisation */
/* les informations du servo 1 sont dans les bytes 0 à 9, ceux du servo 2 de 10 à 19 ... and so on ... */
servoMoteur[numServo].angleMin = EEPROM.read(numServo * 10) | ((int)EEPROM.read((numServo * 10) + 1)) << 8; // lecture des 2 bytes pour int angleMin[]
if (servoMoteur[numServo].angleMin > 1500 || servoMoteur[numServo].angleMin < limitAngleMin){ // si incohérence de valeur ou initialisation (=255)
servoMoteur[numServo].angleMin = limitAngleMin; // on enregistre la limite pour angleMin
EEPROM.update((numServo * 10), servoMoteur[numServo].angleMin & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((numServo * 10) + 1, servoMoteur[numServo].angleMin >> 8); // écrit le second byte de la valeur int
} // fin de if (servoMoteur[numServo].angleMin > 1500 || servoMoteur[numServo].angleMin < limitAngleMin)
servoMoteur[numServo].angleMax = EEPROM.read((numServo * 10) + 2) | ((int)EEPROM.read((numServo * 10) + 3)) << 8;// lecture des 2 bytes pour int angleMax[]
if (servoMoteur[numServo].angleMax < 1500 || servoMoteur[numServo].angleMax > limitAngleMax){ // si incohérence de valeur ou initialisation (=255)
servoMoteur[numServo].angleMax = limitAngleMax; // on enregistre la limite pour angleMax
EEPROM.update((numServo * 10) + 2, servoMoteur[numServo].angleMax & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((numServo * 10) + 3, servoMoteur[numServo].angleMax >> 8); // écrit le second byte de la valeur int
} // fin de if (servoMoteur[numServo].angleMax == 0)
servoMoteur[numServo].DernierePositionServo = EEPROM.read((numServo * 10) + 4) | ((int)EEPROM.read((numServo * 10) + 5)) << 8;
if (servoMoteur[numServo].DernierePositionServo < limitAngleMin || servoMoteur[numServo].DernierePositionServo > limitAngleMax){
servoMoteur[numServo].DernierePositionServo = PointMilieuT;
EEPROM.update((numServo * 10) + 4, servoMoteur[numServo].DernierePositionServo & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((numServo * 10) + 5, servoMoteur[numServo].DernierePositionServo >> 8); // écrit le second byte de la valeur int
} // if (servoMoteur[numServo].DernierePositionServo < ...
/* fin de lecture des données et d'initialisation de l'eeprom */
// Serial.println(servoMoteur[numServo].DernierePositionServo);
/* positionnement à la derniere poition */
servoMoteur[numServo].angleIntermediaire = servoMoteur[numServo].DernierePositionServo;
servoMoteur[numServo].vitesse = 0;
if (servoMoteur[numServo].DernierePositionServo < PointMilieuT)
{
servoMoteur[numServo].etatServo = SERVO_A_ANGLE_MIN;
} else {
servoMoteur[numServo].etatServo = SERVO_A_ANGLE_MAX;
}
servoMoteur[numServo].pinServo = numServo + pinDepartServo;
servoMoteur[numServo].objetServo.attach(servoMoteur[numServo].pinServo);
}
}
int lirePoussoirs()
{
int resultat;
int numPoussoir = (analogRead(pinBPAiguillages) + 64) / 128;
int nouvelEtatPoussoir = etatPoussoir; /* à priori rien ne change */
switch (etatAutomate) {
case NON_PRESSE:
if (numPoussoir < 8)
etatAutomate = ENFONCE;
break;
case ENFONCE:
if (numPoussoir < 8) {
etatAutomate = PRESSE;
nouvelEtatPoussoir = numPoussoir;
}
else {
etatAutomate = NON_PRESSE;
}
break;
case PRESSE:
if (numPoussoir == 8) {
etatAutomate = NON_PRESSE;
nouvelEtatPoussoir = -1;
}
break;
}
return nouvelEtatPoussoir;
}
/*
* construction d'un événement en comparant
* le nouvel état des poussoirs avec l'état précédent.
*/
byte lireEvenement(int *numPoussoir)
{
byte evenement;
int nouvelEtatPoussoir = lirePoussoirs();
if (nouvelEtatPoussoir == etatPoussoir)
evenement = AUCUN_EVENEMENT;
if (nouvelEtatPoussoir >= 0 && etatPoussoir == -1)
evenement = EVENEMENT_PRESSE;
if (nouvelEtatPoussoir == -1 && etatPoussoir >= 0)
evenement = EVENEMENT_RELACHE;
etatPoussoir = nouvelEtatPoussoir;
*numPoussoir = etatPoussoir;
return evenement;
}
/*
* Gestion des témoins en Charlieplexing
* Cf. http://modelleisenbahn.triskell.org/spip.php?article66
*/
enum { OUT0, OUT1, INZ };
const byte etatSortiePourLED[16][5] = {
{ OUT0, OUT1, INZ, INZ, INZ }, /* D0 */
{ OUT1, OUT0, INZ, INZ, INZ }, /* D1 */
{ INZ, OUT0, OUT1, INZ, INZ }, /* D2 */
{ INZ, OUT1, OUT0, INZ, INZ }, /* D3 */
{ INZ, INZ, OUT0, OUT1, INZ }, /* D4 */
{ INZ, INZ, OUT1, OUT0, INZ }, /* D5 */
{ INZ, INZ, INZ, OUT0, OUT1 }, /* D6 */
{ INZ, INZ, INZ, OUT1, OUT0 }, /* D7 */
{ OUT0, INZ, OUT1, INZ, INZ }, /* D8 */
{ OUT1, INZ, OUT0, INZ, INZ }, /* D9 */
{ INZ, INZ, OUT0, INZ, OUT1 }, /* D10 */
{ INZ, INZ, OUT1, INZ, OUT0 }, /* D11 */
{ INZ, OUT0, INZ, OUT1, INZ }, /* D12 */
{ INZ, OUT1, INZ, OUT0, INZ }, /* D13 */
{ OUT0, INZ, INZ, INZ, OUT1 }, /* D14 */
{ OUT1, INZ, INZ, INZ, OUT0 } /* D15 */
};
enum { ALLUME, ETEINT };
byte etatLED[16] = {
ETEINT, ETEINT, ETEINT, ETEINT,
ETEINT, ETEINT, ETEINT, ETEINT,
ETEINT, ETEINT, ETEINT, ETEINT,
ETEINT, ETEINT, ETEINT, ETEINT
};
void programmeBroche(int numBroche, byte etat)
{
switch (etat) {
case OUT0:
digitalWrite(numBroche,LOW);
pinMode(numBroche,OUTPUT);
break;
case OUT1:
digitalWrite(numBroche,HIGH);
pinMode(numBroche,OUTPUT);
break;
case INZ:
pinMode(numBroche,INPUT);
digitalWrite(numBroche,LOW); /* pas de pullup */
break;
}
}
void gereLED(int num)
{
if (etatLED[num] == ALLUME) {
programmeBroche(8,etatSortiePourLED[num][0]);
programmeBroche(9,etatSortiePourLED[num][1]);
programmeBroche(10,etatSortiePourLED[num][2]);
programmeBroche(11,etatSortiePourLED[num][3]);
programmeBroche(12,etatSortiePourLED[num][4]);
}
else {
programmeBroche(8,INZ);
programmeBroche(9,INZ);
programmeBroche(10,INZ);
programmeBroche(11,INZ);
programmeBroche(12,INZ);
}
}
void gereServo(int numServo)
{
servoMoteur[numServo].objetServo.writeMicroseconds(servoMoteur[numServo].angleIntermediaire);
servoMoteur[numServo].angleIntermediaire += servoMoteur[numServo].vitesse;
// on arrive en butée donc on enregistre la dernière position
if (servoMoteur[numServo].angleIntermediaire > limitAngleMax) {
servoMoteur[numServo].angleIntermediaire = limitAngleMax;
servoMoteur[numServo].vitesse = 0;
servoMoteur[numServo].etatServo = SERVO_A_ANGLE_MAX;
servoMoteur[numServo].objetServo.detach();
EEPROM.update((numServo * 10) + 4, servoMoteur[numServo].angleIntermediaire & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((numServo * 10) + 5, servoMoteur[numServo].angleIntermediaire >> 8); // écrit le premier byte de la valeur int
Serial.print("Servo N° ");
Serial.print(numServo);
Serial.print(" positionné à ");
Serial.println(servoMoteur[numServo].angleIntermediaire);
lcd.setBacklight(255);lcd.clear();lcd.home();
lcd.setCursor(0, 0);
lcd.print("Servo ");
lcd.setCursor(9, 0);
lcd.print(numServo);
lcd.setCursor(0, 1);
lcd.print("position ");
lcd.setCursor(12,1);
lcd.print(servoMoteur[numServo].angleIntermediaire);
}
else if (servoMoteur[numServo].angleIntermediaire < limitAngleMin) {
servoMoteur[numServo].angleIntermediaire = limitAngleMin;
servoMoteur[numServo].vitesse = 0;
servoMoteur[numServo].etatServo = SERVO_A_ANGLE_MIN;
servoMoteur[numServo].objetServo.detach();
EEPROM.update((numServo * 10) + 4, servoMoteur[numServo].angleIntermediaire & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((numServo * 10) + 5, servoMoteur[numServo].angleIntermediaire >> 8); // écrit le premier byte de la valeur int
Serial.print("Servo N° ");
Serial.print(numServo);
Serial.print(" positionné à ");
Serial.println(servoMoteur[numServo].angleIntermediaire);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Servo ");
lcd.setCursor(9, 0);
lcd.print(numServo);
lcd.setCursor(0, 1);
lcd.print("position ");
lcd.setCursor(12,1);
lcd.print(servoMoteur[numServo].angleIntermediaire);
}
}
void evenementServo(int numServo)
{
switch (servoMoteur[numServo].etatServo) {
case SERVO_A_ANGLE_MIN:
servoMoteur[numServo].objetServo.attach(servoMoteur[numServo].pinServo);
case SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN:
servoMoteur[numServo].vitesse = 1;
servoMoteur[numServo].etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX;
break;
case SERVO_A_ANGLE_MAX:
servoMoteur[numServo].objetServo.attach(servoMoteur[numServo].pinServo);
case SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX:
servoMoteur[numServo].vitesse = -1;
servoMoteur[numServo].etatServo = SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN;
break;
}
}
void loop()
{
if (analogRead(pinBPChoixMode) < 512) {
//************************************
// Bouton MODE enfoncé => mode REGLAGE
//************************************
delay(25);
if (Y == 0){
//Serial.print("Bouton : "); // Debug
//Serial.print(pinBPChoixMode); // Debug
//Serial.print(" en mode REGLAGE : "); // Debug
//Serial.println(analogRead(pinBPChoixMode)); // Debug
//tone(pinBuzzer,5000,500);
Serial.println("MODE REGLAGE : ");
lcd.setBacklight(255);lcd.clear();lcd.home();
lcd.print("MODE REGLAGE");
tone(pinBuzzer,2000,300);
Y += 1; // flag choix REGLAGE
Z = 0; // RAZ flag choix COMMANDE
} // fin de if (Y == 0)
// Détermination de l'aiguillage en cours de réglage
byte ServoEnCours = (analogRead(pinSwitchSelecteurServo) + 64) / 128; // retourne le n° du servo à régler sélectionné par switches
delay(25); // pour éviter les rebonds ... utilité à vérifie
ServoEnCours = 0; //pour les tests
if (ServoEnCours < 8) {
if (ServoEnCours != dernierServoReglage){
Serial.print("Réglage servo : "); // Debug
Serial.print(ServoEnCours); // Debug
Serial.println(". "); // Debug
servoMoteur[ServoEnCours].objetServo.attach(pinDepartServo + ServoEnCours); // on connecte le servo à régler au pin correspondant à partir du pin 2
dernierServoReglage = ServoEnCours;
X = 0; // RAZ flag affichage butée droite (angle max)
W = 0; // RAZ flag affichage butée gauche (angle min)
} else { // else de if (ServoEnCours != dernierServoRegle)
// Détermination de la butée (angleMin / angleMax) en cours de réglage
// ButeeEnCours = 0 => bouton appuyé => butée droite (angleMax)
// ButeeEnCours = 1023 => bouton relaché => butée gauche (angleMin)
int ButeeEnCours = analogRead(pinSelecteurButee);
delay(25);
if (ButeeEnCours < 512) {
// bouton = enfoncé => on définit que le réglage concerne angleMax
if (X == 0){
Serial.print("Bouton enfoncé :");
Serial.print(ButeeEnCours);
Serial.println(". Réglage butée DROITE (angle maxi)."); // Debug
servoMoteur[ServoEnCours].objetServo.writeMicroseconds(PointMilieuT); // positionne le servo au point milieu
delay(temporisation);
X += 1;
W = 0; // RAZ flag affichage butée gauche (angle min))
} // fin de if (X == 0)
// Lecture de l'angle
valeurLectureButee = analogRead(pinRVButees); // Lecture de la résistance variable => valeurs de 0 à 1023)
delay(25);
valeurLectureButee = map(valeurLectureButee, 0, 1023, PointMilieuT, limitAngleMax); // conversion en microsecondes
servoMoteur[ServoEnCours].objetServo.writeMicroseconds(valeurLectureButee); // positionne le servomoteur entre le point milieu et angle Max
delay(temporisation);
//if (valeurLectureButee == limitAngleMax) tone(pinBuzzer,6000,75);
// Enregistrement du réglage par l'action du bouton validation
validationReglageButee = analogRead(pinBPValidationReglage);
delay(25);
if ( validationReglageButee > 512) {
// le bouton poussoir de validation a été appuyé
digitalWrite(LED_BUILTIN, HIGH);
servoMoteur[ServoEnCours].angleMax = valeurLectureButee;
servoMoteur[ServoEnCours].angleIntermediaire = servoMoteur[ServoEnCours].angleMax;
Serial.print("Angle maxi N° "); //Débug
Serial.println(ServoEnCours); //Débug
Serial.print("réglé à "); //Débug
Serial.println(servoMoteur[ServoEnCours].angleMax); //Débug
lcd.setBacklight(255);lcd.clear();lcd.home();
lcd.setCursor(0, 0);
lcd.print("Angle maxi ");
lcd.setCursor(12, 0);
lcd.print(ServoEnCours);
lcd.setCursor(0, 1);
lcd.print("position ");
lcd.setCursor(12,1);
lcd.print(servoMoteur[ServoEnCours].angleMax);
EEPROM.update((ServoEnCours * 10) + 2, servoMoteur[ServoEnCours].angleMax & 0xFF); // écrit le premier byte de la valeur int angleMax
EEPROM.update((ServoEnCours * 10) + 3, servoMoteur[ServoEnCours].angleMax >> 8); // écrit le second byte de la valeur int angleMax
servoMoteur[ServoEnCours].DernierePositionServo = servoMoteur[ServoEnCours].angleMax;
EEPROM.update((ServoEnCours * 10) + 4, servoMoteur[ServoEnCours].DernierePositionServo & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((ServoEnCours * 10) + 5, servoMoteur[ServoEnCours].DernierePositionServo >> 8); // écrit le second byte de la valeur int
tone(pinBuzzer,1000,75);
digitalWrite(LED_BUILTIN, LOW);
} // fin de if ( validationReglageButee > 512)
} else { // else de if (ButeeEnCours < 512)
// bouton = relaché => on définit que le réglage concerne angleMin
if (W == 0){
Serial.print("Bouton relaché :");
Serial.print(ButeeEnCours);
Serial.println(". Réglage butée GAUCHE (angle min)."); // Debug
servoMoteur[ServoEnCours].objetServo.writeMicroseconds(PointMilieuT); // positionne le servo au point milieu
delay(25);
W += 1;
X = 0; // RAZ flag affichage butée droite (angle max)
} // fin de if (W == 0)
// Lecture de l'angle
valeurLectureButee = analogRead(pinRVButees); // Lecture de la résistance variable sur le pin A2 => valeurs de 0 à 1023)
delay(25);
valeurLectureButee = map(valeurLectureButee, 0, 1023, limitAngleMin, PointMilieuT);// conversion en microsecondes
servoMoteur[ServoEnCours].objetServo.writeMicroseconds(valeurLectureButee); // positionne le servomoteur
delay(temporisation);
// if (valeurLectureButee == limitAngleMin) tone(pinBuzzer,6000,75);
// Enregistrement du réglage par l'action du bouton validation
validationReglageButee = analogRead(pinBPValidationReglage);
delay(50);
if (validationReglageButee > 512) { // on détecte si le bouton poussoir de validation a été appuyé
// le bouton poussoir de validation a été appuyé
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
servoMoteur[ServoEnCours].angleMin = valeurLectureButee;
servoMoteur[ServoEnCours].angleIntermediaire = servoMoteur[ServoEnCours].angleMin;
Serial.print("Angle mini N° "); //Débug
Serial.println(ServoEnCours); //Débug
Serial.print("réglé à "); //Débug
Serial.println(servoMoteur[ServoEnCours].angleMin); //Débug
lcd.setBacklight(255);lcd.clear();lcd.home();
lcd.setCursor(0, 0);
lcd.print("Angle mini ");
lcd.setCursor(12, 0);
lcd.print(ServoEnCours);
lcd.setCursor(0, 1);
lcd.print("position ");
lcd.setCursor(12,1);
lcd.print(servoMoteur[ServoEnCours].angleMin);
EEPROM.update((ServoEnCours * 10) + 0, servoMoteur[ServoEnCours].angleMin & 0xFF); // écrit le premier byte de la valeur int angleMax
EEPROM.update((ServoEnCours * 10) + 1, servoMoteur[ServoEnCours].angleMin >> 8); // écrit le second byte de la valeur int angleMax
servoMoteur[ServoEnCours].DernierePositionServo = servoMoteur[ServoEnCours].angleMin;
EEPROM.update((ServoEnCours * 10) + 4, servoMoteur[ServoEnCours].DernierePositionServo & 0xFF); // écrit le premier byte de la valeur int
EEPROM.update((ServoEnCours * 10) + 5, servoMoteur[ServoEnCours].DernierePositionServo >> 8); // écrit le second byte de la valeur int
tone(pinBuzzer,1000,75);
digitalWrite(LED_BUILTIN, LOW);
} // fin de if (validationReglageButee > 512)
} // fin de if (ButeeEnCours < 512)
} // if (ServoEnCours != dernierServoRegle
} // fin de if (ServoEnCours < 8)
} else { // else de if(analogRead(pinBPChoixMode) < 512)
//*************************************
// Bouton MODE relaché => mode COMMANDE
//*************************************
if (Z == 0){
//Serial.print("Bouton : "); // Debug
//Serial.print(pinBPChoixMode); // Debug
//Serial.print(" en mode COMMANDE : "); // Debug
//Serial.println(analogRead(pinBPChoixMode)); // Debug
Serial.println("MODE COMMANDE : ");
lcd.setBacklight(255);
lcd.home(); lcd.clear();
lcd.print("MODE COMMANDE");
tone(pinBuzzer,2500,100);
Z += 1; // flag choix COMMANDE
Y = 0; // RAZ flag choix REGLAGE
X = 0; // RAZ flag affichage butée droite (angle max)
W = 0; // RAZ flag affichage butée gauche (angle min)
dernierServoReglage = -1;
} // if (byte Z = 0)
int numServo;
for (numServo = 0; numServo < 8; numServo++) gereServo(numServo);
byte evenement = lireEvenement(&numServo);
if (evenement == EVENEMENT_PRESSE) {
evenementServo(numServo);
}
delay(temporisation);
} // (analogRead(pinBPMode) < 512)
et la dernière version du PCB incluant un écran de contrôle LCD et la gestion des leds de positionnement.
Ajout d'un écran LCD 16-2 connecté en I2C pour visualiser le fonctionnement
Ce qui me donne l'idée d'ajouter un bouton pour scanner l'état des aiguillages.
Pages vues depuis 15/01/2020 : 341 464