Generatore di Impulsi Smart con Arduino

Come nasce il Generatore di Impulsi Smart con Arduino? Come al solito da una reale esigenza del momento in ambito industriale.

Esistono dei sistemi di conteggio di confezioni a bordo di macchine confezionatrici che sono costituiti essenzialmente da una fotocellula focalizzata a guardare le confezioni in transito.

A fronte di interruzione del fascio ottico un sistema a PLC (che gestisce anche l’intera macchina) esegue poi il conteggio della produzione totalizzandone le confezioni. Che c’entra il Generatore di Impulsi? Bene! C’entra perchè esso nasce per colmare essenzialmente due esigenze:

  • testare l’affidabilità del sistema di conteggio e
  • ripristinare il contatore in caso di malfunzionamento della fotocellula di conteggio o del sistema di calcolo

Era necessario costruire un apparecchio portatile che fosse facilmente collegabile ai vari dispositivi di conteggio per le diverse linee di produzione. Così l’idea cadde subito su un Arduino Nano v3.0 alloggiato su una apposita scheda progettata per il caso.

Era indispensabile avere un sistema interattivo attraverso il quale poter impostare il numero di confezioni (o impulsi da generare) e a quale velocità, ottenendo di ritorno anche un tempo stimato per il completamento degli impulsi. L’esigenza veniva dal fatto che ci si poteva trovare di fronte alla situazione di dover generare un numero spropositatamente alto nel caso del test di affidabilità del sistema.

Inoltre il sistema doveva essere alimentato a +24V continua e doveva generare gli impulsi a +24V, standard industriale.

Iniziai col selezionare i componenti:

  • Arduino Nano v3.0
  • Shield Display 16×2 DFRobot dotato di bottoni (su, giu, sinistra, destra, select, reset)
  • DC-DC Regolatore di tensione stabilizzato (regolabile con precisione) per convertire l’alimentazione +24V in +5V
  • Mosfet IRL540 (trattandosi di +24V) per pilotare il segnale di uscita
  • Scheda 1000 fori su cui assemblare il tutto

Lo shield display con tasti ha 6 linee parallele da connettere ad Arduino per pilotare i contenuti sul LCD e un segnale analogico che corrisponde al tasto premuto. Il tutto secondo le seguenti indicationi:

Pin	        Function
Analog 0	Buttons (select, up, right, down and left)
Digital 4	DB4
Digital 5	DB5
Digital 6	DB6
Digital 7	DB7
Digital 8	RS (Data or Signal Display Selection)
Digital 9	Enable

A questo punto iniziai le prove connettendo i componenti tra loro usando la classica basetta di test:

Per poter pilotare la parte display useremo una libreria e la dichiareremo come segue:

//------------------------------------------------------------------------------------------
// include the library code:
#include <Wire.h>
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

Feci subito la prova a pilotare il display.

Dopo la dichiarazione della libreria di cui sopra inserii il seguente codice:

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  //         1234567890123456
  lcd.print("  SMART  PULSE  ");
  lcd.setCursor(0,1);
  lcd.print("   GENERATOR    ");
}

Ottenendo il seguente risultato:

Feci subito un test dei pulsanti ed in pratica premendone uno alla volta cambia il valore del segnale analogico di ingresso. Quindi per usare i tasti è sufficiente “decodificare” il valore di ingresso in analogico. Il valore analogico su un PIN di Arduino può variare, una volta decodificato, da 0 a 1023 ed i bottoni dello shield hanno il seguente valore:

  • sotto il valore di 60: bottone destro
  • sotto il valore di 200: bottone alto
  • sotto il valore di 400: bottone basso
  • sotto il valore di 600: bottone sinistro
  • sotto il valore di 800: bottone select

Ho collegato il Mosfet IRL540 secondo il seguente schema:

Il PIN 10 di Arduino è l’uscita che controlla il Mosfet IRL540.

Non ci resta che assemblare il tutto sulla basetta. Decisi di saldare i componenti su entrambi i lati per fare in modo che l’oggetto finale fosse il più compatto possibile.

A questo punto vi allego l’intero codice del Generatore di Impulsi con Arduino:

//------------------------------------------------------------------------------------------
// Smart Pulse Generator 
// by Michele Ardito
// 07.03.2015
//------------------------------------------------------------------------------------------

/*
Pin	        Function
Analog 0	Buttons (select, up, right, down and left)
Digital 4	DB4
Digital 5	DB5
Digital 6	DB6
Digital 7	DB7
Digital 8	RS (Data or Signal Display Selection)
Digital 9	Enable
*/ 

#define LED      13
#define OUTPULSE 10

//------------------------------------------------------------------------------------------
// include the library code:
#include <Wire.h>
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
//------------------------------------------------------------------------------------------
// gestione display
#define PULSEPOS         13
#define FREQUENCYPOS     10
String strWhiteString = "                ";
String strCursor = (String)(char)255; 
unsigned long timeBlink = 0;
bool          blink     = false;
bool          blink2    = false;
int x = 0;
//------------------------------------------------------------------------------------------
// gestione stati
unsigned long time = 0;
int           state = 0;
#define       STATE_INITIAL           0
#define       STATE_SETTING_PULSE     1
#define       STATE_SETTING_FREQUENCY 2
#define       STATE_START             3
#define       STATE_GENERATING        4
bool          returnEvent = false;
//------------------------------------------------------------------------------------------
// gestione dei tasti del keypad
int           keyPress          = 0;
int           keyAcquired       = 0;
int           keyAcquiredInibit = 0;
#define       KEY_RIGHT     1
#define       KEY_LEFT      2
#define       KEY_UP        3
#define       KEY_DOWN      4
#define       KEY_SELECT    5

//------------------------------------------------------------------------------------------
// gestione dei valori pulse e frequency
unsigned long pulse = 0; // il massimo è 4,294,967,295
unsigned int  frequency = 0;
int           cursorPos = 0;
unsigned long unitIncDec = 0;
//------------------------------------------------------------------------------------------
// gestione dei valori per le generazione
unsigned long pulseGenerated = 0;
unsigned long timeHz = 0;
unsigned long timeHzCalc = 0;
bool blinkHz = false;
unsigned long secondsRemain = 0;
unsigned int  seconds = 0;
unsigned int  minutes = 0;
unsigned int  hours   = 0;
void setup() {
  pinMode(LED,      OUTPUT);
  pinMode(OUTPULSE, OUTPUT);
  digitalWrite(OUTPULSE, true); 
  // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  //         1234567890123456
  lcd.print("     SMART      ");
  lcd.setCursor(0,1);
  lcd.print("PULSE  GENERATOR");
}

void loop() {
  //------------------------------------------------------------------------------------------
  // blink
  //------------------------------------------------------------------------------------------
  if (timeBlink < millis()) { timeBlink = (millis() + 500); blink = !blink; blink2 = true; }
  //------------------------------------------------------------------------------------------
  // legge il valore del tasto premuto
  //------------------------------------------------------------------------------------------
  keyPress = analogRead (0);
  if (keyPress > 1000) { keyAcquiredInibit = 0; }
  if (keyAcquiredInibit == 0 ) {
  if (keyPress < 60) {
    // right
    keyAcquired = KEY_RIGHT;
    keyAcquiredInibit = 1;
    time = millis();
  }
  else if (keyPress < 200) {
    // up
    keyAcquired = KEY_UP;
    keyAcquiredInibit = 1;
    time = millis();
  }
  else if (keyPress < 400){
    // down
    keyAcquired = KEY_DOWN;
    keyAcquiredInibit = 1;
    time = millis();
  }
  else if (keyPress < 600){
    // left
    keyAcquired = KEY_LEFT;
    keyAcquiredInibit = 1;
    time = millis();
  }
  else if (keyPress < 800){
    // select
    keyAcquired = KEY_SELECT;
    keyAcquiredInibit = 1;
    time = millis();
  }
  }
  //------------------------------------------------------------------------------------------
  // discrima i comportamenti a seconda degli stati
  //------------------------------------------------------------------------------------------
  unitIncDec = 1;
  for (int x = 0; x < cursorPos; x++) { unitIncDec *= 10; }
  if (keyAcquiredInibit == 1 || blink2) {
  blink2 = false;
  switch (state) {
    case STATE_INITIAL:
      if (keyAcquired == KEY_SELECT || returnEvent) {
        state = STATE_SETTING_PULSE;
        returnEvent = false;
        lcd.clear();  
        lcd.setCursor(0,0);
        //         1234567890123456
        lcd.print("Imposta Impulsi:");
        cursorPos = 0;
      }
      break;
    case STATE_SETTING_PULSE:
      if (keyAcquired == KEY_UP) {
        if (pulse < (4294967294-unitIncDec) ) { pulse += unitIncDec; } else { pulse = 4294967294; }
      } else if (keyAcquired == KEY_DOWN) {
        if (pulse > unitIncDec)  { pulse -= unitIncDec; } else { pulse = 0; }
      } else if (keyAcquired == KEY_LEFT) {
        if (cursorPos < 9) { cursorPos++; }
      } else if (keyAcquired == KEY_RIGHT) {
        if (cursorPos > 0) { cursorPos--; }
      } else if (keyAcquired == KEY_SELECT) {
        state = STATE_SETTING_FREQUENCY;
        lcd.clear();  
        lcd.setCursor(0,0);
        //         1234567890123456
        lcd.print("  Imposta Hz:   ");
        cursorPos = 0;
        break;
      }
      // decodifica la lunghezza del numero
      x = d(pulse);
      // cancella i caratteri del display a sinistra
      lcd.setCursor(0,1);
      lcd.print(strWhiteString.substring(0,PULSEPOS-11));
      lcd.setCursor(PULSEPOS-10,1);
      lcd.print(strWhiteString.substring(0,10-x));
      // apertura lunghezza campo
      lcd.setCursor(PULSEPOS-11,1);
      lcd.print("[");
      // stampa il valore
      lcd.setCursor(PULSEPOS-x,1);
      lcd.print(pulse);
      // chiusura lunghezza campo
      lcd.setCursor(PULSEPOS,1);
      lcd.print("]");
      // visualizza il cursore
      if (blink) { lcd.setCursor(PULSEPOS-1-cursorPos,1); lcd.print(strCursor); }
      break;
    case STATE_SETTING_FREQUENCY:
      if (keyAcquired == KEY_UP) {
        if (frequency < (250-unitIncDec) ) { frequency += unitIncDec; } else { frequency = 250; }
      } else if (keyAcquired == KEY_DOWN) {
        if (frequency > unitIncDec)  { frequency -= unitIncDec; } else { frequency = 0; }
      } else if (keyAcquired == KEY_LEFT) {
        if (cursorPos < 2) { cursorPos++; }
      } else if (keyAcquired == KEY_RIGHT) {
        if (cursorPos > 0) { cursorPos--; }
      } else if (keyAcquired == KEY_SELECT) {
        state = STATE_START;
        lcd.clear();  
        lcd.setCursor(0,0);
        //         1234567890123456
        lcd.print("Genera Impulsi:");
        lcd.setCursor(0,1);
        //         1234567890123456
        lcd.print(" [OK]  |  [DX]");
        cursorPos = 0;
        break;
      }
      // decodifica la lunghezza del numero
      x = d(frequency);
      // cancella i caratteri del display a sinistra
      lcd.setCursor(0,1);
      lcd.print(strWhiteString.substring(0,FREQUENCYPOS-6));
      lcd.setCursor(FREQUENCYPOS-5,1);
      lcd.print(strWhiteString.substring(0,5-x));
      // apertura lunghezza campo
      lcd.setCursor(FREQUENCYPOS-6,1);
      lcd.print("[");
      // stampa il valore
      lcd.setCursor(FREQUENCYPOS-x,1);
      lcd.print(frequency);
      // chiusura lunghezza campo
      lcd.setCursor(FREQUENCYPOS,1);
      lcd.print("]");
      // visualizza il cursore
      if (blink) { lcd.setCursor(FREQUENCYPOS-1-cursorPos,1); lcd.print(strCursor); }
      break;
    case STATE_START:
      if (keyAcquired == KEY_RIGHT) {
        state = STATE_INITIAL;
        returnEvent = true;
      } else if (keyAcquired == KEY_SELECT) {
        state = STATE_GENERATING;
        lcd.clear();  
        lcd.setCursor(0,0);
        //         1234567890123456
        lcd.print("GENERA  --:--:--");
        lcd.setCursor(0,1);
        //         1234567890123456
        lcd.print("[OK]            ");
        cursorPos = 0;
        pulseGenerated = pulse; // + 1;
        if (frequency < 1) { frequency = 1; }
        timeHzCalc = 500 / frequency;
      }
      break;
    case STATE_GENERATING:
      if (keyAcquired == KEY_SELECT) {
          state = STATE_INITIAL;
          pulse = pulseGenerated;
          cursorPos = 0;
      }
      // decodifica la lunghezza del numero
      x = d(pulseGenerated);
      // cancella i caratteri del display a sinistra
      lcd.setCursor(PULSEPOS-10+2,1);
      lcd.print(strWhiteString.substring(0,10-x));
      // apertura lunghezza campo
      lcd.setCursor(PULSEPOS-11+2,1);
      lcd.print("[");
      // stampa il valore
      lcd.setCursor(PULSEPOS-x+2,1);
      lcd.print(pulseGenerated);
      // chiusura lunghezza campo
      lcd.setCursor(PULSEPOS+2,1);
      lcd.print("]");
      // calcola il tempo rimasto
      secondsRemain = pulseGenerated / frequency;
      seconds = secondsRemain % 60;
      minutes = (secondsRemain / 60) % 60;
      hours   = (secondsRemain / 3600);
      lcd.setCursor(8,0);
      String tempo = intToString(hours); 
      tempo += ":";
      tempo += intToString(minutes);
      tempo += ":";
      tempo += intToString(seconds);
      lcd.print(tempo);
      break;
  }  
  }
  if (state != STATE_GENERATING) {  
    if ((time + 100) > millis()) { digitalWrite(LED, HIGH); } else { digitalWrite(LED, LOW); }
  }
  
  //------------------------------------------------------------------------------------------
  // generazione impulsi
  //------------------------------------------------------------------------------------------
  if (state == STATE_GENERATING && timeHz < millis()) { 
    timeHz = millis() + timeHzCalc;
    blinkHz = !blinkHz;
    if (blinkHz) { pulseGenerated--; }
    if (pulseGenerated <=0) { 
      state = STATE_INITIAL; 
      pulse = 0; 
      blink2 = true; 
      lcd.clear();
      //         1234567890123456
      lcd.print("impulsi generati");
      lcd.setCursor(0,1);
      lcd.print("   ---------    ");
   }
    digitalWrite(LED, blinkHz); 
    digitalWrite(OUTPULSE, !blinkHz); 
  }

  keyAcquired = 0;
  
}

String intToString (int valore) {
  String appoggio = "00" + String(valore, DEC);
  int    lunghezza = appoggio.length() - 2;
  return appoggio.substring(lunghezza, lunghezza + 2);
}

int d(unsigned long valore) {
  String myString = String(valore);
  return (myString.length());
}

Dopo aver caricato il programma in Arduino è possibile accedere a tutte le funzioni di generazione tra cui impostare il numero di impulsi da generare, grazie ai tasti di spostamento è possibile impostare facilmente il valore e successivamente passare ad impostare la velocità di generazione in Hz (impulsi al secondo)

Dopo di chè è possibile confermare la generazione oppure tornare indietro per cambiare i parametri:

Una volta avviata la generazione sarà visualizzato il tempo stimato alla fine.

Con una stampante 3D riuscii a creare il case del dispositivo:

Ed infine il test di generazione finale:

Durante la generazione, col pulsante OK (enter) è possibile mettere in pausa il generatore.

Di seguito un video su questo dispositivo.