Interface Homme-Machine pour boîtier sur rail DIN

Open-source-hardware-logo.png

Dans cet article nous allons nous intéresser au projet Hmi4DinBox. La partie matérielle de Hmi4DinBox a été conçue par 2 étudiants Lionel et Justine du BTS Systèmes Numériques, Option “Électronique et communication” du Lycée Rouvière de Toulon.

Hmi4DinBox est une interface homme-machine conçue pour être implémentée à l’avant d’un boîtier de rail DIN.

Voilà ce que ça donne une fois implantée dans le boîtier DIN :

et voilà le cahier des charges exprimé en SysML qui résume les spécifications de cette interface :

Requirement Diagram Hmi4DinBox

Caractéristiques

Ses caractéristiques principales sont les suivantes :

  • LCD 2×16 caractères avec rétro-éclairage (basé sur ST7032),
  • un bouton de navigation avec (4 directions + 1 bouton poussoir),
  • 5 leds,
  • piloté par un simple bus I2C et un signal binaire HIRQ qui indique qu’un ou plusieurs actions ont été effectuées sur le bouton de navigation (appui et relâchement de touche dans les 5 directions stockées dans une mémoire tampon),
  • alimenté par 5V ou 3.3V permettant de l’utiliser avec une carte Arduino ou Pi,
  • Interface de programmation USB compatible avec Arduino IDE,
  • Interface JTAG pour la programmation du chargeur de démarrage et le débogage du firmware.

Publié sous licence open-source hardware, il est composé de :

Comment connecter cette IHM à votre carte ?

L’IHM est connectée à votre carte par le connecteur J1 MOLEX KK254 à 5 broches situé à l’arrière du circuit imprimé.

Le brochage du connecteur J1 est le suivant:

# Nom Description
1 VCC Alimentation 5V ou 3,3V, protégée contre les surtensions et les inversions de polarité
2 SCL Horloge I2C, pas de résistance au pull-up sur cette ligne! mettez-en une sur la carte principale !
3 SDA Données I2C, pas de résistance au pull-up sur cette ligne! mettez-en une sur la carte principale !
4 HIRQ Indique qu’une ou plusieurs actions ont été effectuées sur le bouton de navigation
5 GND Masse

HIRQ est à l’état haut tant qu’il y a des actions non lues dans le tampon (capacité de 32 actions). Il passe à l’état bas quand toutes les actions ont été lues avec la méthode key ().

Comment installer la bibliothèque ?

Cette bibliothèque permet le contrôle côté client de Hmi4DinBox connecté par un bus I2C. Elle est destinée à être utilisée dans un environnement Arduino (également compatible avec le piduino sur des cartes Pi telles que Raspberry Pi ou NanoPi).

Pour installer la bibliothèque pour Arduino IDE, téléchargez simplement son fichier zip et l’intégrer dans votre croquis en fonction de explications sur le site Arduino.

Comment utiliser l’IHM dans votre programme?

Au début du fichier Hmi4DinBox.h, nous voyons la définition de constantes décrivant la solution matérielle:

#define LED1 0
#define LED2 1
#define LED3 2
#define LED4 3
#define LED5 4

Nous avons 5 leds, la numérotation commence à 0.

#define LED_RED LED1
#define LED_GREEN1 LED2
#define LED_GREEN2 LED3
#define LED_YELLOW1 LED4
#define LED_YELLOW2 LED5

Nous définissons des constantes plus explicites correspondant aux couleurs des leds : la led N°1 est rouge, les N°2 et 3 vertes…

Ensuite, on trouve les constantes pour la fonction bouton de navigation :

#define KUP 1
#define KDOWN 2
#define KLEFT 3
#define KRIGHT 4
#define KCENTER 5

Nous avons 5 actions (ou touches, keys) correspondant aux 4 directions du bouton de navigation et le bouton central.

Pour utiliser cette IHM dans un programme, vous devez déclarer une variable globale de type Hmi4DinBox:

Hmi4DinBox hmi;

Si on utilise le pavé de navigation, il est nécessaire lors de cette déclaration de spécifier le numéro de la broche Arduino utilisée par l’IHM pour indiquer que des actions sur le bouton sont dans le tampon interne (il faudra donc les lire …).

const int hirqPin = 7;
Hmi4DinBox hmi (hirqPin);

Ensuite, nous pouvons accéder :

  • à l’afficheur LCD grâce à hmi.lcd,
  • aux leds grâce à hmi.led,
  • au bouton de navigation grâce à hmi.keyb,
  • au rétroéclairage avec hmi.backlight.

Utilisation de l’écran LCD

La classe WireLcd est dérivée de la classe Arduino Print. Vous pouvez donc utiliser les fonctions “print(),println(),write()`:

int i = 5;
hmi.lcd.print("Helloworld ! ");
hmi.lcd.println(i++);
hmi.lcd.write('C');

Voici le croquis HelloWorld.ino :

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

Hmi4DinBox hmi;

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }
}

void loop() {
  static int counter = 0;

  // Write a piece of text on the first line...
  hmi.lcd.setCursor (0, 0); //LINE 1, ADDRESS 0
  hmi.lcd.print ("Hello World");

  // Write the counter on the second line...
  hmi.lcd.setCursor (1, 0);
  hmi.lcd.print (counter / 10, DEC);
  hmi.lcd.write ('.');
  hmi.lcd.print (counter % 10, DEC);
  hmi.lcd.write (' ');
  counter++;
  delay (500);
}

Comme on peut le voir ci-dessus, on fera,

Pour effacer l’écran:

hmi.lcd.clear();

Pour aller au début de la ligne 2:

hmi.lcd.setCursor (1, 0);

Pour revenir en haut à droite:

hmi.lcd.home();

Pour éteindre l’écran:

hmi.lcd.noDisplay();

Pour allumer l’écran:

hmi.lcd.display();

Pour activer le curseur:

hmi.lcd.cursor();

Pour désactiver le curseur:

hmi.lcd.noCursor();

Pour activer le curseur clignotant:

hmi.lcd.blink();

Pour désactiver le clignotement du curseur:

hmi.lcd.noBlink();

Le contraste de l’écran LCD peut être modifié, la valeur peut varier de 0 à 63:

hmi.lcd.setcontrast (24);

Le contraste peut également être lu et ajusté par rapport à la valeur actuelle:

byte c = hmi.lcd.getcontrast();
hmi.lcd.adjcontrast (-1); // decrement the contrast value by 1

Utilisation des leds

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

Hmi4DinBox hmi;
void printLed (int led);

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }
  hmi.lcd.cursor();
  hmi.lcd.blink();
  hmi.lcd.clear();
  hmi.lcd.print ("12345");
}

void loop() {

  // turn on all leds
  hmi.led.writeAll();
  // Pause
  delay (2000);

  // turn off all leds
  hmi.led.writeAll (0);
  // Pause
  delay (2000);

  // turn on the LEDs one by one.
  hmi.led.set (LED1);
  delay (500);
  hmi.led.set (LED2);
  delay (500);
  hmi.led.set (LED3);
  delay (500);
  hmi.led.set (LED4);
  delay (500);
  hmi.led.set (LED5);
  delay (500);
  // Pause
  delay (2000);

  // turn off the LEDs one by one with a for loop
  for (int led = LED1; led <= LED5; led++) {
    hmi.led.clear (led);
    delay (500);
  }
  // Pause
  delay (2000);

  // turn on the LEDs one by with a for loop, using toggle and get value.
  for (int led = LED1; led <= LED5; led++) {
    hmi.led.toggle (led);
    printLed (led);
    delay (500);
  }
  // Pause
  delay (2000);

  // turn off the LEDs one by with a for loop, using toggle() and get()
  for (int led = LED1; led <= LED5; led++) {
    hmi.led.toggle (led);
    printLed (led);
    delay (500);
  }
  // Pause
  delay (2000);
}

void printLed (int led) {
  hmi.lcd.setCursor (1, led);
  if (hmi.led.get (led)) {
    hmi.lcd.write ('O');
  }
  else {

    hmi.lcd.write ('_');
  }
}

Comme on peut le voir dans le croquis LedDemo.ino, ci-dessus :

hmi.led.set (LED1);

permet d’allumer la LED1,

hmi.led.clear (LED1);

permet d’éteindre la LED1,

hmi.led.toggle (LED1);

permet de basculer l’état de LED1,

hmi.led.get (LED1);

permet de lire l’état de la LED1,

La fonction hmi.led.writeAll() vous permet de modifier toutes les leds en même temps. Par défaut, un appel à cette fonction sans paramètre active toutes les leds. Si on lui passe un paramètre, cela correspond à l’état des leds. Le bit 0 de ce paramètre est utilisé pour contrôler LED1, bit 1 pour LED2 …

Utilisation du bouton de navigation

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

const int hirqPin = 7;
Hmi4DinBox hmi (hirqPin);

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }

  hmi.lcd.clear();
  hmi.lcd.cursor();
  hmi.lcd.blink();
}

void loop() {
  static int column = 0;
  static int row = 0;

  if (hmi.keyb.available()) { // check if keys are available
    byte key = hmi.keyb.key(); // get the next key

    if (hmi.keyb.released()) { // this key was released ?

      hmi.lcd.write ('R'); // yes, print R
      hmi.led.clear(LED_GREEN1);
    }
    else {

      hmi.lcd.write ('P'); // no, print P
      hmi.led.set(LED_GREEN1);
    }

    hmi.lcd.print (key); // print the key

    // Move the cursor when you reach the end of the line.
    column += 2;
    if (column >= 16) { // end of line ?
      // yes
      if (++row > 1) { // end of screen ?
        // wait for reading last key and clear the LCD
        delay(500);
        row = 0;
        hmi.lcd.clear();
      }
      column = 0;
      hmi.lcd.setCursor (row, column);
    }

  }
}

Comme on peut le voir dans le croquis KeyboardDemo.ino, ci-dessus :

hmi.keyb.available ()

permet de vérifier si des actions sur le bouton sont disponibles pour la lecture,

key = hmi.keyb.key ();

permet de lire la prochaine action mémorisée,

hmi.keyb.released ()

vous permet de savoir si cette action est un relâchement (et donc pas un appui),

hmi.keyb.pressed ()

vous permet de savoir si cette action est un appui.

Utilisation du rétro-éclairage

#ifdef __unix__
#include <Piduino.h>  // All the magic is here ;-)
#else
// Defines the serial port as the console on the Arduino platform
#define Console Serial
#endif

#include <Hmi4DinBox.h>

const int hirqPin = 7;
Hmi4DinBox hmi (hirqPin);

byte bl;
void printBacklight ();

void setup() {

  Console.begin (115200);
  if (!hmi.begin (24, false)) {

    Console.println("hmi.begin() failed !");
    exit (1); // HMI failed to start !
  }

  // read value stored in EEPROM.
  bl = hmi.backlight.read();

  // print this value
  hmi.lcd.clear();
  hmi.lcd.print ("Backlight");
  printBacklight();
}

void loop() {

  if (hmi.keyb.available()) { // check if keys are available

    if (hmi.keyb.pressed()) {
      byte key = hmi.keyb.key(); // get the next key

      if ( (key == KUP) || (key == KDOWN)) { // if key UP or DOWN

        if (key == KUP) {

          bl += 8; // UP for increase
        }
        else {

          bl -= 8;  // DOWN for decrease
        }

        // change value and print
        hmi.backlight.write (bl);
        printBacklight();
        hmi.led.toggle (LED_GREEN1);
      }
    }
  }
}

void printBacklight () {

  hmi.lcd.setCursor (0, 10);
  hmi.lcd.print (bl);
  hmi.lcd.print ("  ");
}

Comme on peut le voir dans le croquis BacklightDemo.ino ci-dessus,

hmi.backlight.write (bl);

permet de modifier la valeur du rétro-éclairage (entre 0 et 255), comme cette valeur est stockée dans la mémoire EEPROM de l’IHM, il est possible de lire la valeur actuelle grâce à:

bl = hmi.backlight.read ();