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.