Auteur Sujet: Réseau Dominique  (Lu 263337 fois)

DDEFF

  • Hero Member
  • *****
  • Messages: 760
    • Voir le profil
Re : Projet Dominique
« Réponse #45 le: décembre 31, 2017, 12:14:06 pm »
Bonne Année à toi aussi. ;D

Magnifique programme, testé, qui contient plein d'astuces bien utiles.
A lire à tête reposée... ;)

Denis
"Ce n'est pas le puits qui est trop profond, c'est ta corde qui est trop courte" (proverbe chinois)

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3039
  • 100% Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #46 le: décembre 31, 2017, 03:39:42 pm »
Merci Denis,

C'est justement pour t'aider à construire ton réseau que je publies tous ces détails, car tu as choisis aussi le bus CAN et tu vas aussi partir sur DCCpp.

Une des pièces maitresses d'un réseau est justement la grosse fonction qu'il faut mettre entre la capture des informations de rétrosignalisation et le pilotage des trains = le Gestionnaire de réseau. J'explique d'abord tout ce que j'ai mis autour.

Evidemment tu es en plein là dedans !
http://forum.locoduino.org/index.php?topic=211

La grande majorité des modélistes ne voient pas d'autre solution que d'acheter une centrale du commerce (et hop : 500€ !) et d'y ajouter un logiciel sur PC comme RRTC (et hop, encore : 500€ !).

Tu sais bien que nous, à Locoduino, on souhaite expliquer comment faire autrement, en retroussant les manches et avec peu d'huile de coude ? ... si, un peu quand même ! Avec un poil de maitrise de l'environnement Arduino et quelques séances de lecture de Locoduino où on commence à pouvoir trouver l'essentiel pour faire tout soi-même (DIY). Un bon fer à souder et quelques composants et hop !



Mon exemple n'est pas le seul, loin s'en faut. J'ai choisi cette architecture, maintenant je continue sur la lancée, mais je sais qu'elle reste évolutive (module par module), ce dont je ne vais pas me priver.

Donc il y aura plein d'autres épisodes à suivre ...

Dominique
Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3039
  • 100% Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #47 le: décembre 31, 2017, 05:15:47 pm »
Voici maintenant la gestion du clavier tactile 12 touches.

J'ai utilisé un composant comme celui-ci :
https://www.ebay.fr/itm/1Pcs-Mpr121-Capacitive-Touch-Keypad-Shield-Module-Sensitive-Key-Keyboard-New-I-P/252906299459?hash=item3ae263e043:g:JrAAAOSwx2dYIPLs

Il a l'avantage d'avoir sa puce MPR121 du coté opposé aux touches tactiles et 4 trous de fixation, ce qui permet de l'installer avec discrétion et élégance. J'ai réalisé un cache plastifié qui est posé dessus et tient par les même vis de fixation. Ce cache me permet de reorganiser les touches autrement.

Dans les globales j'ai ajouté :
///////////// Touchpad à l'adresse I2C = 0x5A ///////
Adafruit_MPR121 cap = Adafruit_MPR121();
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
int Key;
// Attach the MPR121's IRQ pin to digital pin 4
const int PIN_TOUCH_IRQ = 4;

Pour lire ce clavier tactile, j'ai écrit une fonction Read_Keypad qui retourne un numéro de 0 à 11, ou 12 si rien (car le 0 est déjà affecté à une touche).

///////////////////////////////////////////////////////////////////////
// LECTURE CLAVIER TACTILE
///////////////////////////////////////////////////////////////////////

byte Read_Keypad(void)
{
  uint8_t i;
  bool keydown = false;
  // Get the currently touched pads
  currtouched = cap.touched();
  for (i=0; i<12; i++) {
    // if it *is* touched and *wasnt* touched before => key down
    if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) ) {
      keydown = true;
      //Serial.print(i); Serial.println(" touched");
      break;
    }
    // if it *was* touched and now *isnt* => key up (ignored)
    if (!(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) {
      keydown = false;
      //Serial.print(i); Serial.println(" released");
      break;
    }
  } 
  lasttouched = currtouched;
  switch (i) {
    case 0:  i=9;  break;
    case 1:  i=6;  break;
    case 2:  i=3;  break;
    case 3:  i=11; break;
    case 4:  i=8;  break;
    case 5:  i=5;  break;
    case 6:  i=2;  break;
    case 7:  i=0;  break;
    case 8:  i=7;  break;
    case 9:  i=4;  break;
    case 10: i=1;  break;
    case 11: i=10; break;   
  }
  if (keydown) {
    return (i);
  } else {
    return (i+12);
  }
}

On remarque le switch qui permet de changer les numéros de touches, car l'ordre des touches du produit de base n'est pas bien adapté à ce que je recherche.

Dans la loop, la lecture des appuis sur le clavier tactile est très simple :

   // gestion du clavier tactile
   if (digitalRead(PIN_TOUCH_IRQ) == LOW) {
    int Key = Read_Keypad();
   }

Dans mon programme, les entrées sur ce clavier tactile sont très nombreuses et dépendent de plusieurs indicateurs, par exemple :

- Hors configuration, l'appui sur une touche sélectionne une loco dans la table et affiche ses variables courantes (nom, vitesse, direction, ..)

    // choix registre/loco hors configuration et saisie
    if ((!gConfig)&&(!gSaisie)&&(Key < 12)) {
      gId = Key;
      gCurrentLoco = gConfigLocos[gId];
      gChange = true;
      MajLeds();
    }

- En configuration, le clavier sert à saisir des valeurs sur 1, 2 ou 3 chiffres significatifs, avec affichage sur l'écran LCD, possibilité de correction avec la touche 'c' (numéro 10)  et validation avec la touche 11.

Par exemple, pour la configuration de l'adresse DCC :

        //--- saisie adresse DCC
        if ((Key >= 0)&&(Key <=9)) {
          gSaisieValeur[gSaisieValeurIndex] = Key;
          if (gSaisieValeurIndex < 3) {
            gSaisieValeurIndex++;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 10) { // back
          if (gSaisieValeurIndex > 0) {
            gSaisieValeurIndex--;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 11) { // enter
          gChange = true;
          switch (gSaisieValeurIndex) {
            case 0:
            gChange = false; // saisie vide
            break;
            case 1:
            gCurrentLoco.dccAddress = gSaisieValeur[0];
            break;
            case 2:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 10) + gSaisieValeur[1];
            break;
            case 3:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 100) + (gSaisieValeur[1] * 10) + gSaisieValeur[2];
            break;           
          }
          lcd.setCursor(14,1);lcd.print(gCurrentLoco.dccAddress);lcd.print("  ");
          if (gChange) {
            gConfigLocos[gId] = gCurrentLoco;   // sauvegarde dans la table des locos
            EnregistreLocoEEProm(gId);          // enregistre gCurrentLoco dans l'EEPROM
          }
          gSaisie=false; gConfig=false; gChange = true;
        }

Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3039
  • 100% Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #48 le: décembre 31, 2017, 05:24:57 pm »
Et maintenant le mesure de vitesse des trains !

Très simplement j'ai tiré 2 fils (signal et masse) entre 1 capteur de consommation d'une zone droite et horizontale d'une longueur de 172 cm sur mon réseau et une entrée capt1 de l'Arduino.

Quand une loco entre dans cette zone, capt1 passe à l'état bas. Quand elle sort de la zone, capt1 passe à l'état haut.

Le code pour gérer cela est :

void mesureV1() {
  int v1 = digitalRead(capt1);
  if (v1 != gOldcapt1) {    // changement d'état capt1
    gOldcapt1 = v1;
    if (v1 == 0) {          // zone occupée
      if (gModeMV1 == 0) {  // entree dans zone de mesure 1
        gModeMV1 = 1;
        gTempsMV1 = millis();
        lcd.setCursor(0,1);
        lcd.print("Vitesse 1          ");
      }
    } else {                // v1 = 1 : zone libérée
      if (gModeMV1 == 1) {  // sortie et calcul vitesse
        gModeMV1 = 0;
        gTempsMV1 = millis() - gTempsMV1;
        gVitesse1 = calcul(gTempsMV1);
        lcd.setCursor(0,1);
        lcd.print("Vitesse 1 = ");
        lcd.print(gVitesse1);lcd.print(" km/h ");
        lcd.backlight();
        retrotime = millis();
      }
    }
  }
}

Le principe est simple : On enregistre le temps système (millis()) à l'entrée dans la zone, puis à la sortie de la zone. Ensuite on utilise la différence de temps et la longueur de la zone pour calculer la vitesse à l'échelle N et on l'affiche sur le LCD. On pourrait configurer cette longueur dans le configurateur.

Le calcul de la vitesse est :

int calcul(unsigned long temps) {         // exemple 100 cm en 10 s => 57,6 km/h
  unsigned long VKM;
  if (temps>0) { //VKM=(gDistanceMV*5760)/temps; // 172*5760 / 10000 = 57,60 km/h
    VKM=990720/temps; // ici 172*5760 / 10000 = 57,60 km/h
    Serial.println(VKM);
  }
  return ((int)VKM);
}
« Modifié: décembre 31, 2017, 05:27:40 pm par Dominique »
Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3039
  • 100% Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #49 le: janvier 03, 2018, 03:22:44 pm »
Maintenant il faut assembler tous les morceaux pour que l'Arduino fasse toutes ces tâches séquentiellement (à l'échelle humaine on ne s'en rend pas compte et cela semble simultané) et sans blocage. Ce qui compte c'est que toutes les commandes de l'interface utilisateur soit les plus instantanées possibles.

On va donc placer dans le loop() des séquences de code, les unes après les autres, chacune se chargeant d'une tâche simple. Aucune de ces séquence ne doit bloquer l'Arduino :

1- récupérer un message CAN et le traiter (on l'a vu ici http://forum.locoduino.org/index.php?topic=290.msg4319#msg4319)
2- détecter un appui sur le bouton poussoir de l'encodeur quadratique
---> le 1er appui démarre le choix d'une séquence de configuration
---> le 2ème appui démarre la modification d'un paramètre
3- lire le clavier tactile (vu ici http://forum.locoduino.org/index.php?topic=290.msg4328#msg4328)
---> en mode configuration c'est une saisie de paramètre
---> hors mode configuration, c'est une sélection de loco
4- lire la position de l'encodeur quadratique
---> en configuration, c'est une sélection de paramètre
---> hors configuration, c'est une variation de vitesse de la loco sélectionnée
5- lire les potentiomètres de vitesse (vu ici http://forum.locoduino.org/index.php?topic=290.msg4320#msg4320)
6- gérer les boutons et leds de direction (idem)
7- gérer les boutons "panic", F0 (FL), F1 et F2
8- mesurer la vitesse des trains dans 2 zones distinctes (vu ici http://forum.locoduino.org/index.php?topic=290.msg4328#msg4328)
9- gérer les clignotements des leds
10- gérer le rétro-éclairage de l'écran LCD
11- gérer la mise à jour des affichages de l'écran LCD (état de la loco sélectionnée)
12- surveiller le courant pour couper le DCC en cas de court-circuits
13- gérer l'interface avec un petit moniteur de debugging sur l'écran terminal

OUF !!!!

Il en fait des choses cet Arduino  (MEGA). Mais certaines des fonctions listées ci-dessus sont conditionnées par des variables qui permettent d'exécuter certains parties du code et pas d'autres.

C'est ce que je vais appeler pompeusement ici "la logique de décision".

Dans un tel projet, c'est très important. Définir les variables globales d'état de ces séquences conditionne la réussite du projet.
Cela demande un long moment de réflexion avant d'écrire le code. Tout au plus, on peut écrire des petits programmes pour tester séparément les différentes parties du code, puis on assemble le tout à la fin. C'est ce que j'ai fait et je pense que je modifierai encore le code pour l'optimiser si cela est nécessaire.

« Modifié: janvier 03, 2018, 04:07:41 pm par Dominique »
Cordialement,
Dominique

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3039
  • 100% Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #50 le: janvier 03, 2018, 04:50:57 pm »
Maintenant abordons la logique de décision, c'est à dire l'ordonnancement des tâches dans la loop() en fonction d'un certain nombre de variables d'état globales.

1- Traitement des messages CAN
C'est le compteur d'octets non lus dans le buffer circulaire Ncan qui conditionne cette tâche. Si Ncan > 2 il y a forcément un message (la taille minimum est 3) et dans ce cas on le traite.

2- Détection d'appui sur le bouton de l'encodeur.
C'est un bouton que l'on teste à chaque tour de loop
//--- Detection d'appui sur SWenc :
  SWenc.update();
  if ( SWenc.fell() ) {     
    gSWState = !gSWState;
    gSW = true;     // appui effectif
    gConfig = true; // mode global configuration
  }

Ce bouton poussoir est géré par la bibliothèque Bounce2 comme n'importe quel bouton poussoir.
Chaque appui met à true une variable gConfig (mode configuration) et gSW (appui détecté) et inverse la variable gSWState pour différencier les cas du 1er et du 2ème appui.

Le premier appui sert à afficher le choix de la variable à configurer en fonction de la position de l'encodeur qui permet, en tournant le bouton dans un sens ou un autre, de choisir parmi 10 variables de configuration, avec une variable gMode :
  // premier appui : mode choix parametre de configuration
  if (gSWState && gConfig ) { // passage en choix de mode
    if (gSW) {
      afficheTitre();
      gSW=false;
    }
    long newPosition = myEnc.read()/4;
    if (newPosition != oldPosition) { 
      if ( newPosition > oldPosition) {
        if (gMode < 9) gMode ++;
      } else {
        if (gMode > 0) gMode--;
      }
      oldPosition = newPosition;   
      afficheTitre();
      MajLeds();
    }
   }


La fonction afficheTitre est ici :
    // affichage titre
    void afficheTitre() {
      lcd.clear();
      lcd.print(gMode); 
      if (gMode < 10) lcd.print(' ');
      switch (gMode) {
      case 0:
        lcd.print("Inverser sens");
      break;
      case 1:
        lcd.print("Choix adresse");
      break;
      case 2:
        lcd.print("Mesure vitesse");
      break;
      case 3:
        lcd.print("Choix cran 30");
      break;
      case 4:
        lcd.print("Choix cran 60");
      break;
      case 5:
        lcd.print("Choix cran 90");
      break;
      case 6:
        lcd.print("Choix Vit Max");
      break;
      case 7:
        lcd.print("Choix Vit Min");
      break;
      case 8:
        lcd.print("Distance M/V cm");
      break;
      case 9:
        lcd.print("Erase EEProm ");
      break;
      default:
      break;   
      }
    }

Le deuxième appui sert à dérouler le code de configuration en fonction de la variable gMode :
  // Deuxieme appui : passage en modification de parametre
  if (!gSWState && gConfig && gSW) { // sortie du choix de mode
    gSW=false;
    switch (gMode) {
      case 0: // inverser sens
        lcd.setCursor(0,1);
        lcd.print("Normal  : taper 0 ");
        lcd.setCursor(0,2);
        lcd.print("Inverse : taper 1 ");
        gSaisie = true;
      break;
      case 1: // choix @ DCC
        lcd.setCursor(0,1);
        lcd.print("adresse DCC : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 2:
        lcd.setCursor(0,1);
        lcd.print("Attente... ");
        gMesureVitesse = true;
        gSaisie = true;
        //gSaisieValeurIndex = 0;
      break;
      case 3:
        lcd.setCursor(0,1);
        lcd.print("Cran vit 30 : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 4:
        lcd.setCursor(0,1);
        lcd.print("Cran vit 60 : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 5:
        lcd.setCursor(0,1);
        lcd.print("Cran vit 90 : ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 6:
        lcd.setCursor(0,1);
        lcd.print("Cran vit max: ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 7:
        lcd.setCursor(0,1);
        lcd.print("Cran vit min: ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 8:
        lcd.setCursor(0,1);
        lcd.print("Distance M/V: ");
        gSaisie = true;
        gSaisieValeurIndex = 0;
      break;
      case 9:
        lcd.setCursor(0,2);
        lcd.print("Erase & Reset");
        EraseEEProm();
        reset();
      break;
      default:
        gConfig=false;
      break;   
    }
    gChange = true;
  }

Ca a l'air simple comme cela, mais ce switch sert à autoriser la saisie de valeurs sur le clavier tactile.

3- lecture du clavier tactile
Donc, en plus de gMode, d'autres variables viennent autoriser la gestion du clavier tactile dont la bonne exécution se terminera par la modification d'un paramètre de configuration qui sera sauvegardé en EEPROM.

Voici la séquence d'entrée dans ce code : elle réactive le rétro-éclairage du LCD
   // gestion du clavier tactile
   if (digitalRead(PIN_TOUCH_IRQ) == LOW) {
    int Key = Read_Keypad();
    lcd.backlight();
    retrotime = millis();

Deux cas se présentent :

A- Hors configuration on ne fait que choisir une des 12 locos
    // choix registre/loco hors configuration et saisie
    if ((!gConfig)&&(!gSaisie)&&(Key < 12)) {
      gId = Key;
      gCurrentLoco = gConfigLocos[gId];
      gChange = true;
      MajLeds();
    }

gId est le numéro de loco en cours, qui est utilisé partout pour paramètrer cette loco.

B- En configuration, on modifie un paramêtre, fonction de gId et il y a autant de programme que de paramêtre.

Cas de gMode = 0 (inversion de sens de la loco)
    // mode saisie en configuration
    if ((gSaisie)&&(Key < 12)) {
      switch (gMode) {
       
        //--- INVERSION de SENS
        case 0:
        lcd.setCursor(0,1);
        gChange = false;
        if (gCurrentLoco.inverse) {
          lcd.print("Sens Inverse       ");
        } else {
          lcd.print("Sens Normal        ");
        }
        if (Key==0) {
          gCurrentLoco.inverse = 0; 
          gChange = true;       
        }
        if (Key==1) {
          gCurrentLoco.inverse = 1;
          gChange = true;         
          }
        // toute autre touche ne change pas le sens
        if (gChange) {
          lcd.setCursor(0,1);
          if (gCurrentLoco.inverse) {
            lcd.print("Sens Inverse       ");
          } else {
            lcd.print("Sens Normal        ");
          }
          gConfigLocos[gId] = gCurrentLoco;   // sauvegarde dans la table des locos
          EnregistreLocoEEProm(gId);          // enregistre gCurrentLoco dans l'EEPROM
        }
        gSaisie=false; gConfig=false; gChange = true;
        break;

Ce code affiche des choses sur l'écran LCD, modifie le paramètre CurrentLoco.inverse dans l'objet loco et sauvegarde cet objet dans l'EEPROM, puis re-initialise les variables globales d'état pour terminer le processus.

Je donne le cas gMode = 1 : saisie de l'adresse DCC de la loco sélectionnée car il peut être intéressant à réutiliser dans votre code :
        //--- saisie adresse DCC
        case 1:
        if ((Key >= 0)&&(Key <=9)) {
          gSaisieValeur[gSaisieValeurIndex] = Key;
          if (gSaisieValeurIndex < 3) {
            gSaisieValeurIndex++;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 10) { // back
          if (gSaisieValeurIndex > 0) {
            gSaisieValeurIndex--;
            lcd.setCursor(14,1);
            for (int c=0;c<gSaisieValeurIndex;c++) {
              lcd.print(gSaisieValeur[c]);
            }
            lcd.print("  ");
          }
        }
        if (Key == 11) { // enter
          gChange = true;
          switch (gSaisieValeurIndex) {
            case 0:
            gChange = false; // saisie vide
            break;
            case 1:
            gCurrentLoco.dccAddress = gSaisieValeur[0];
            break;
            case 2:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 10) + gSaisieValeur[1];
            break;
            case 3:
            gCurrentLoco.dccAddress = (gSaisieValeur[0] * 100) + (gSaisieValeur[1] * 10) + gSaisieValeur[2];
            break;           
          }
          lcd.setCursor(14,1);lcd.print(gCurrentLoco.dccAddress);lcd.print("  ");
          if (gChange) {
            gConfigLocos[gId] = gCurrentLoco;   // sauvegarde dans la table des locos
            EnregistreLocoEEProm(gId);          // enregistre gCurrentLoco dans l'EEPROM
          }
          gSaisie=false; gConfig=false; gChange = true;
        }
        break;

Là le but est d'afficher la valeur initiale, de pouvoir taper sur les touches du clavier, corriger une erreur avec la touche "c" et de valider la saisie puis l'enregistrer.
J'ai mis du temps à la mettre au point donc si cela peut vous éviter d'en perdre, j'en serais ravi !

On retrouvera le même principe pour toutes les autres saisies (cran des vitesses 30, 60, 90, choix des vitesses Mini et Maxi, de la distance de mesure de vitesse).

Et pour terminer, le plus simple, c'est la gestion du temps système avec millis() pour gérer les cligotements, le rétro-éclairage, la surveillance du courant et la mise à jour des lignes d'affichage de l'écran LCD :
  //clignotements Led Rouge
  if (gblinkR) {
    if ((millis() - gClignoteR) > 250) {
      gClignoteR = millis();
      digitalWrite(LedModeR,!digitalRead(LedModeR));
    }
  }
  //clignotements Led Jaune
  if (gblinkJ) {
    if ((millis() - gClignoteJ) > 500) {
      gClignoteJ = millis();
      digitalWrite(LedModeJ,!digitalRead(LedModeJ));
    }
  }

  // retro-eclairage
  if ((millis() - retrotime) > eclairage) {
    lcd.noBacklight();     
  }

  // mise à jour de la ligne d'etat en L3
  if ((gChange)&&(!gSaisie)) {
    StatutL3();
  }

  // surveillance courant MAXIMUM 2 A
  // affichage courant = la valeur maxi pendant 500 ms
  if ((millis() - gCurrentSampleTime) > 500) {
    gCurrentSampleTime = millis();
    lcd.setCursor(16,3);lcd.print(gCurrent*5);lcd.print(' ');
    gCurrent = 0;
  }
  int iCurrent = analogRead(A0);
  if (iCurrent > gCurrent) {
    gCurrent = iCurrent;
    if (gCurrent > CurrentMax) { // 400 * 5 = 2000 mA
    stop_DCC();
    }
  }

Je passe l'écran terminal pour le debugging et j'aurais aimé faire quelques mesures des temps passés dans chaque taches pour connaitre l'état de saturation du Mega2560, cela viendra plus tard. En tout cas je suis satisfait de ce code qui mériterait quand même d'être amélioré car on peut mtoujours faire mieux, avec du temps..

A suivre...
« Modifié: janvier 03, 2018, 05:04:53 pm par Dominique »
Cordialement,
Dominique

francisch

  • Newbie
  • *
  • Messages: 24
  • Bricoleur Linux, Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #51 le: janvier 06, 2019, 09:42:21 pm »
Tout d'abord je vous adresse tous mes meilleurs vœux de santé et de réussite dans vos projets.

Je lu et essayé de suivre le Projet de Dominique, mais ma culture de la programmation est trop légère beaucoup de choses m'ont échappé.

Etant sur un la réalisation d'une centrale pour 5 locos sans PC, je cherchais dans les programme de Dominique la commande pour l'arrêt d'urgence d'une seule loco (pour toutes pas de problème).
En TextCommand on envoi -1 pour la vitesse est ça marche, mais avec  DCCpp::setSpeedMain comme la vitesse est mappée dans DCCpp.cpp pas question de lui envoyé -1.
Je pensais utilisé TextCommand avec un message créé par le programme mais je ne vois pas comment faire sans PC.

Cordialement.
Francis

Dominique

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 3039
  • 100% Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #52 le: janvier 07, 2019, 09:11:11 am »
La doocumentation de DCCpp dit qu'il faut mettre "1" dans le parametre inNewSpeed :

Voir le dossier de la bibliothèque / extras / doc / index.html

static bool DCCpp::setSpeedMain ( int nReg,
int inLocoId,
int inStepsNumber,
int inNewSpeed,
bool inForward
)
inlinestatic
For the given decoder id, set the speed and the direction on the main track.

Parameters
nReg Register number. Avoid register 0, used for one shot commands like accessories or CV programming.
inLocoId Decoder address in short or long format.
inStepsNumber According to the decoder configuration, set it to 14, 28 or 128 .
inNewSpeed Speed of the loco, between 2 and the steps number - 1 (13, 27 or 127). 0 means normal complete stop. 1 means emergency stop.
inForward True means forward move, false means backward.
Cordialement,
Dominique

francisch

  • Newbie
  • *
  • Messages: 24
  • Bricoleur Linux, Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #53 le: janvier 07, 2019, 01:51:50 pm »
Effectivement j'avais lu (trop vite) mais pas compris.
J'applique et cela va marcher.
Merci

francisch

  • Newbie
  • *
  • Messages: 24
  • Bricoleur Linux, Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #54 le: janvier 10, 2019, 02:23:23 pm »
Bon, j'applique et cela ne marche
j’envoie
 DCCpp::setSpeedMain(1, 3, 28, 0, 1);
en retour j'ai
DCCpp SetSpeed 0/28 (in Dcc 0 )
 cela va tres bien
mais si j'envoie
DCCpp::setSpeedMain(1, 3, 28, 0, 1);
en retour j'ai
DCCpp SetSpeed 1/28 (in Dcc 6 )

La valeur 1 est transformée en 6 par la fonction map  dans DCCpp.cpp
 /***************************** Driving functions */
 
 bool DCCpp::setThrottle(volatile RegisterList *inpRegs, int nReg,  int inLocoId, int inStepsNumber, int inNewSpeed, bool inForward)
 {
   int val = 0;
 
   if (panicStopped)
     val = 1;
   else
     if (inNewSpeed > 0)
       val = map(inNewSpeed, 0, inStepsNumber, 2, 127);
 
 #ifdef DCCPP_DEBUG_MODE
   Serial.print(F("DCCpp SetSpeed "));
   Serial.print(inForward?inNewSpeed:-inNewSpeed);
   Serial.print(F("/"));
   Serial.print(inStepsNumber);
   Serial.print(F(" (in Dcc "));
   Serial.print(val);
   Serial.println(F(" )"));
 #endif


D'autre part je n'ai pas retrouve les lignes de code citées dans la reponse de Dominique.
Est ce que j'ai sauté une etape ?
« Modifié: janvier 11, 2019, 11:20:28 am par francisch »

Thierry

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 810
    • Voir le profil
Re : Projet Dominique
« Réponse #55 le: janvier 11, 2019, 11:34:26 am »
C'est une incohérence de DCCpp. Vu le code, le Map est là pour permettre à l'utilisateur de setSpeedMain de ne pas se préoccuper des valeurs spéciales : il peut utiliser des valeurs de vitesse de 0 à 127, ce sera transformé pour exclure 0 et 1. La doc explique bien la norme pour la vitesse mais ne dit pas que setSpeedMain n'aura pas ce comportement... On peut contourner ça en appelant directement la bonne fonction de Greeg via les registres :

DCCpp::mainRegs.setThrottle(1, 3, 0, 1);  // fourni sans test !

sachant que cette fonction impose une valeur de vitesse réglée pour 128 pas pour une vitesse normale. Les vitesses spéciales 0 et 1 ne sont pas concernées par le nombre de pas.

francisch

  • Newbie
  • *
  • Messages: 24
  • Bricoleur Linux, Arduino et N
    • Voir le profil
Re : Projet Dominique
« Réponse #56 le: janvier 11, 2019, 01:18:01 pm »
Merci de cette réponse, elle m'amène une autre question. Est ce que cela veut dire que DCCpp n'envoie que des ordres de vitesse en 128crans ?

Tony04

  • Hero Member
  • *****
  • Messages: 569
    • Voir le profil
Re�: Projet Dominique
« Réponse #57 le: janvier 11, 2019, 01:41:04 pm »
Bonjour francish,

non, DCCpp gere tres bien aussi les 28 crans mais je n'ai trouve aucune doc la dessus alors que le 28 crans est tres utile pour certaines locos.

Voici comment je procede dans ma souris/centrale sans fil.

Pour le 128 crans registre=1 par defaut mais peut etre 1 chiffre defini pour chaque loco, adresse_loco=adresse DCC, vitesse 0 a 127, sens=0 ou 1  :
DCCpp::setSpeedMain(registre, adresse_loco, 128, vitesse, sens); 
Pour le 28 crans idem sauf le 28 et vitesse de 0 a 27 :
DCCpp::setSpeedMain(registre, adresse_loco, 28, vitesse, sens); 

Cordialement
Antoine

PS: J'ai enleve les accents
« Modifié: janvier 11, 2019, 03:41:23 pm par Tony04 »

Thierry

  • Global Moderator
  • Hero Member
  • *****
  • Messages: 810
    • Voir le profil
Re : Projet Dominique
« Réponse #58 le: janvier 11, 2019, 04:10:02 pm »
DCC++ en interne ne gère que 128 crans, mais le Map de DCCpp.cpp est là pour permettre de gérer n'importe quel nombre de crans. L'utilisateur de setSpeedMain met une valeur entre 0 et 27 pour une loco 28 crans, et c'est le setThrottle avec son Map qui va donner une vitesse connue en 128 crans à DCC++ . Par contre effectivement, c'est un ordre 128 crans qui sera envoyé au décodeur de la loco... Je n'ai pas l'impression qu'autre chose est possible avec DCC++. Je ne suis pas rentré dans le détail, je suis au boulot...

Tony04

  • Hero Member
  • *****
  • Messages: 569
    • Voir le profil
Re : Projet Dominique
« Réponse #59 le: janvier 11, 2019, 05:20:28 pm »
Bonsoir Thierry,

de toute facon sur les decodeurs tu ne peux pas choisir entre 28 et 128 (a ma connaissance), c'est toujours 28/128 ou autre chose, donc l'ordre de DCCpp doit effectivement etre en 128 crans mais c'est transparent pour l'utilisateur qui peux lui utiliser une plage de 28 crans.