In questo articolo, vedremo come utilizzare la piccola scheda Arduino Nano per gestire i deviatoi tramite servocomandi di un tipico plastico ferroviario con funzionamento analogico.
L’idea di questo progetto parte da uno schema che l’amico Salvatore Pluchino del Gruppo Appassionati Scala TT mi ha inviato tempo fa e che serve per gestire l’azionamento con servocomando di un deviatoio sfruttando la logica programmabile di Arduino.
Il suo schema è molto semplice e sfrutta una porta di uscita per comandare un servocomando e una porta di ingresso per leggere lo stato di un pulsante con dei led per leggere lo stato del deviatoio.
Descrizione dello schema elettrico:
Il progetto riprende lo schema di Salvatore e lo espande per poter sfruttare tutte le porte di I/O di Arduino Nano. Non solo per la gestione dei servocomandi e dei deviatoi, ma anche di altri dispositivi generici.
Schema elettrico:
Lo schema elettrico del circuito è distinto sostanzialmente in tre parti:
– circuito di uscita per il controllo dei servocomandi (6), porte D3, D5, D6, D9, D10 e D11;
– circuito di ingresso per il comando dei servocomandi tramite pulsanti (12, tastierino numerico), porte A0, A1, A2, A3, A6 e A7;
– circuito di uscita e di controllo digitale tramite transistor (relè, led, ecc. 6), porte D2, D4, D7, D8, D12 e D13.
Gli ingressi analogici facenti capo alle porte A4 (SDA) e A5 (SCL), verranno usati per futuri utilizzi tramite bus I2c.
Vi sono dei morsetti per il collegamento dei fili per l’alimentazione esterna dei servocomandi a 5 VCC, per un bus seriale di tipo I2C (vedi sopra) e per l’alimentazione di Arduino VIN.
Il circuito di ingresso con i 6 pulsanti può essere separato dal circuito principale tramite un pratico connettore per agevolarne il montaggio dello stesso su una plancia.
Inoltre, con questo sistema è possibile collegare sensori di vario tipo al posto del tastierino numerico, come reed, sensori di infrarosso, sensori PIR ecc.
Una opzione non esclude l’altra.
Il funzionamento del tastierino numerico a 12 tasti si basa sul principio del partitore resistivo.
In pratica, per ogni porta di ingresso analogica di Arduino, abbiamo due resistenze da 1000 OHM collegate come partitore resistivo con due pulsanti normalmente aperti.
In questo modo, se non premiamo nessun tasto, avremo 5 Volt in uscita, se premiamo il tasto 1 avremo 2,5 Volt e se premiamo il tasto 2 avremo 0 Volt.
Questo schema ci permette di comandare le funzioni di movimento di un servo montato sottoplancia per la gestione di un deviatoio.
Arduino Nano dispone di 8 porte di ingresso analogiche, ma noi ne utilizzeremo 6: A0, A1, A2, A3, A6 e A7.
Le porte A4 e A5 verranno usate in futuro per un sistema di gestione dei servi tramite bus seriale I2C.
In ultima analisi, abbiamo la possibilità di azionare 6 relè utili per gestire diverse funzioni del plastico, come polarizzazione del cuore, gestione segnali, sezionamenti sui binari, ecc.
Come dicevo sopra, una funzione di questa scheda non esclude l’altra, quindi il limite di funzionamento sta solo nella nostra fantasia e capacità di programmazione.
Qui sopra il tastierino a 12 pulsanti per il comando del sistema.
E l’output seriale delle funzioni dei tasti:
Il circuito in fase di preparazione:
Il software del solo tastierino:
// Tastiera per 6 servocomandi su porte A0, A1, A2, A3, A6 e A7 su Arduino Nano int pservo1 = A0; int pservo2 = A1; int pservo3 = A2; int pservo4 = A3; int pservo5 = A6; int pservo6 = A7; int pval1 = 0; int pval2 = 0; int pval3 = 0; int pval4 = 0; int pval5 = 0; int pval6 = 0; void setup() { Serial.begin(9600); } void loop() { pval1=analogRead(pservo1); pval2=analogRead(pservo2); pval3=analogRead(pservo3); pval4=analogRead(pservo4); pval5=analogRead(pservo5); pval6=analogRead(pservo6); if (pval1 > 700) { Serial.print("Valore tasto per servo 1 = Non premuto = "); Serial.println(pval1); } else if(pval1 < 700 && pval1 > 300) { Serial.print("Valore tasto per servo 1 = Premuto 2 = "); Serial.println(pval1); } else if(pval1 < 300) { Serial.print("Valore tasto per servo 1 = Premuto 1 = "); Serial.println(pval1); } if (pval2 > 700) { Serial.print("Valore tasto per servo 2 = Non premuto = "); Serial.println(pval2); } else if(pval2 < 700 && pval2 > 300) { Serial.print("Valore tasto per servo 2 = Premuto 2 = "); Serial.println(pval2); } else if(pval2 < 300) { Serial.print("Valore tasto per servo 2 = Premuto 1 = "); Serial.println(pval2); } if (pval3 > 700) { Serial.print("Valore tasto per servo 3 = Non premuto = "); Serial.println(pval3); } else if(pval3 < 700 && pval3 > 300) { Serial.print("Valore tasto per servo 3 = Premuto 2 = "); Serial.println(pval3); } else if(pval3 < 300) { Serial.print("Valore tasto per servo 3 = Premuto 1 = "); Serial.println(pval3); } if (pval4 > 700) { Serial.print("Valore tasto per servo 4 = Non premuto = "); Serial.println(pval4); } else if(pval4 < 700 && pval4 > 300) { Serial.print("Valore tasto per servo 4 = Premuto 2 = "); Serial.println(pval4); } else if(pval4 < 300) { Serial.print("Valore tasto per servo 4 = Premuto 1 = "); Serial.println(pval4); } if (pval5 > 700) { Serial.print("Valore tasto per servo 5 = Non premuto = "); Serial.println(pval5); } else if(pval5 < 700 && pval5 > 300) { Serial.print("Valore tasto per servo 5 = Premuto 2 = "); Serial.println(pval5); } else if(pval5 < 300) { Serial.print("Valore tasto per servo 5 = Premuto 1 = "); Serial.println(pval5); } if (pval6 > 700) { Serial.print("Valore tasto per servo 6 = Non premuto = "); Serial.println(pval6); } else if(pval6 < 700 && pval6 > 300) { Serial.print("Valore tasto per servo 6 = Premuto 2 = "); Serial.println(pval6); } else if(pval6 < 300) { Serial.print("Valore tasto per servo 6 = Premuto 1 = "); Serial.println(pval6); } delay(1000); Serial.println(); } |
Collegamento dei servi:
Una volta che abbiamo verificato il funzionameto dei 12 pulsanti, possiamo collegare i servocomandi tramite l’apposito connetore a sinistra di Arduino.
Per alimentare i servi è necessario collegare un alimentatore da 5 Volt sul morsetto GND e 5VCC posto a sinistra del connettore dei servi.
Qui sopra la serigrafia del PCB con tutti componenti e le indicazioni peri cablaggi.
I morsetti a destra della scheda Arduino servono per alimentare la scheda stessa tramite il pin VIN e GND con un alimentatore che eroghi una tensione compresa tra 7 e 12 VCC.
Il morsetto SDA e SCL serve per applicazioni future,
Tutto intorno alla scheda ci sono altri morsetti per collegare direttamente le bobine di 6 relè a 5 VCC.
Funzionamento:
Premendo i tasti della fila inferiore del tastierino, azioneremo il movimento delle squadrette dei servi di 30 gradi in senso antiorario. Contemporaneamente si azioneranno i led rossi ed i relè associati ai tasto premuto. Premendo i tasti della fila superiore otterremo un movimento di 30 gradi in senso orario e si disattiveranno i relè relativi.
Cablaggi:
Il software definitivo:
// Controllo servi su Arduino Nano, Edgardo Rosatti 2020 #include <Servo.h> // Includiamo la libreria Servo.h pa la gestione pratica dei servocomandi // Creazione degli oggetti servo Servo mioservo1; Servo mioservo2; Servo mioservo3; Servo mioservo4; Servo mioservo5; Servo mioservo6; // Creazione delle variabili "pos" per definire la posizione iniziale dei servocomandi int pos1 = 0; int pos2 = 0; int pos3 = 0; int pos4 = 0; int pos5 = 0; int pos6 = 0; // Definizione delle variabili "pservo" sulle porte analogiche di ingresso A0, A1, A2, A3, A6 e A7, le porte analogiche di ingresso A4 e A5 vengono utilizzate per l'interfaccia seriale I2C int pservo1 = A0; int pservo2 = A1; int pservo3 = A2; int pservo4 = A3; int pservo5 = A6; int pservo6 = A7; // Definizione delle variabili sDA e sCL sulle porte analogiche A4 e A5 int sDA = A4; int sCL = A5; // Definizione delle variabili di lettura dello stato di A0, A1, A2, A3, A6 e A7 int pval1 = 0; int pval2 = 0; int pval3 = 0; int pval4 = 0; int pval5 = 0; int pval6 = 0; // Definizione delle variabili di assegnazione ai relè su porte digitali int rele1 = 2; int rele2 = 4; int rele3 = 7; int rele4 = 8; int rele5 = 12; int rele6 = 13; void setup() { Serial.begin(9600); // Inizializzazione della porta seriale a 9600 baud // Imposta i pin di gestione relè su uscite digitali pinMode(rele1, OUTPUT); pinMode(rele2, OUTPUT); pinMode(rele3, OUTPUT); pinMode(rele4, OUTPUT); pinMode(rele5, OUTPUT); pinMode(rele6, OUTPUT); // Assegnazione dei servi alle porte relative D3, D5, D6, D9, D10 e D11 mioservo1.attach(3); mioservo2.attach(5); mioservo3.attach(6); mioservo4.attach(9); mioservo5.attach(10); mioservo6.attach(11); // Posizionamento a 0 di tutti i servi mioservo1.write(0); mioservo2.write(0); mioservo3.write(0); mioservo4.write(0); mioservo5.write(0); mioservo6.write(0); } void loop() { pval1=analogRead(pservo1); pval2=analogRead(pservo2); pval3=analogRead(pservo3); pval4=analogRead(pservo4); pval5=analogRead(pservo5); pval6=analogRead(pservo6); if (pval1 > 700) { Serial.print("Valore tasto per servo 1 = Non premuto = "); Serial.println(pval1); } else if(pval1 < 700 && pval1 > 300) { Serial.print("Valore tasto per servo 1 = Premuto 2 = Spegni relè 1 = "); Serial.println(pval1); digitalWrite(rele1, LOW); // Spegni il relè 1 for(pos1 = 30; pos1 >= 1; pos1 -= 1) // Aziona il servo 1 antiorario { mioservo1.write(pos1); delay(15); } } else if(pval1 < 300) { Serial.print("Valore tasto per servo 1 = Premuto 1 = Accendi relè 1 = "); Serial.println(pval1); digitalWrite(rele1, HIGH); // Accendi il relè 1 for(pos1 = 0; pos1 < 30; pos1 += 1) // Aziona il servo 1 orario { mioservo1.write(pos1); delay(15); } } if (pval2 > 700) { Serial.print("Valore tasto per servo 2 = Non premuto = "); Serial.println(pval2); } else if(pval2 < 700 && pval2 > 300) { Serial.print("Valore tasto per servo 2 = Premuto 2 = Spegni relè 2 = "); Serial.println(pval2); digitalWrite(rele2, LOW); // Spegni il relè 2 for(pos2 = 30; pos2 >= 1; pos2 -= 1) // Aziona il servo 2 antiorario { mioservo2.write(pos2); delay(15); } } else if(pval2 < 300) { Serial.print("Valore tasto per servo 2 = Premuto 1 = Accendi relè 2 = "); Serial.println(pval2); digitalWrite(rele2, HIGH); // Accendi il relè 2 for(pos2 = 0; pos2 < 30; pos2 += 1) // Aziona il servo 2 orario { mioservo2.write(pos2); delay(15); } } if (pval3 > 700) { Serial.print("Valore tasto per servo 3 = Non premuto = "); Serial.println(pval3); } else if(pval3 < 700 && pval3 > 300) { Serial.print("Valore tasto per servo 3 = Premuto 2 = Spegni relè 3 = "); Serial.println(pval3); digitalWrite(rele3, LOW); // Spegni il relè 3 for(pos3 = 30; pos3 >= 1; pos3 -= 1) // Aziona il servo 3 antiorario { mioservo3.write(pos3); delay(15); } } else if(pval3 < 300) { Serial.print("Valore tasto per servo 3 = Premuto 1 = Accendi relè 3 = "); Serial.println(pval3); digitalWrite(rele3, HIGH); // Accendi il relè 3 for(pos3 = 0; pos3 < 30; pos3 += 1) // Aziona il servo 3 orario { mioservo3.write(pos3); delay(15); } } if (pval4 > 700) { Serial.print("Valore tasto per servo 4 = Non premuto = "); Serial.println(pval4); } else if(pval4 < 700 && pval4 > 300) { Serial.print("Valore tasto per servo 4 = Premuto 2 = Spegni il relè 4 = "); Serial.println(pval4); digitalWrite(rele4, LOW); // Spegni il relè 4 for(pos4 = 30; pos4 >= 1; pos4 -= 1) // Aziona il servo 4 antiorario { mioservo4.write(pos4); delay(15); } } else if(pval4 < 300) { Serial.print("Valore tasto per servo 4 = Premuto 1 = Accendi il relè 4 = "); Serial.println(pval4); digitalWrite(rele4, HIGH); // Accendi il relè 4 for(pos4 = 0; pos4 < 30; pos4 += 1) // Aziona il servo 4 orario { mioservo4.write(pos4); delay(15); } } if (pval5 > 700) { Serial.print("Valore tasto per servo 5 = Non premuto = "); Serial.println(pval5); } else if(pval5 < 700 && pval5 > 300) { Serial.print("Valore tasto per servo 5 = Premuto 2 = Spegni il relè 5 = "); Serial.println(pval5); digitalWrite(rele5, LOW); // Spegni il relè 5 for(pos5 = 30; pos5 >= 1; pos5 -= 1) // Aziona il servo 5 antiorario { mioservo5.write(pos5); delay(15); } } else if(pval5 < 300) { Serial.print("Valore tasto per servo 5 = Premuto 1 = Accendi il relè 5 = "); Serial.println(pval5); digitalWrite(rele5, HIGH); // Accendi il relè 5 for(pos5 = 0; pos5 < 30; pos5 += 1) // Aziona il servo 5 orario { mioservo5.write(pos5); delay(15); } } if (pval6 > 700) { Serial.print("Valore tasto per servo 6 = Non premuto = "); Serial.println(pval6); } else if(pval6 < 700 && pval6 > 300) { Serial.print("Valore tasto per servo 6 = Premuto 2 = Spegni il relè 6 = "); Serial.println(pval6); digitalWrite(rele6, LOW); // Spegni il relè 6 for(pos6 = 30; pos6 >= 1; pos6 -= 1) // Aziona il servo 6 antiorario { mioservo6.write(pos6); delay(15); } } else if(pval6 < 300) { Serial.print("Valore tasto per servo 6 = Premuto 1 = Accendi il relè 6 = "); Serial.println(pval6); digitalWrite(rele6, HIGH); // Accendi il relè 6 for(pos6 = 0; pos6 < 30; pos6 += 1) // Aziona il servo 6 orario { mioservo6.write(pos6); delay(15); } } delay(500); // Imposta un ritardo di 1 secondo tra una scansioe e l'altra Serial.println(); // stampa una linea vuota } |
Note:
Il software è abbondantemente commentato e non dovrebbe essere un problema interpretarlo per chi ha già esperienza con Arduino. Tuttavia, mi sento di dare qualche indicazione pratica per agevolare eventuali adattamenti in merito.
Il programma utilizza la porta seriale standard ad una velocità di 9600 baud che è molto utile quando si scrive il codice. Se si ritiene che il programma fuzioni bene, tutti i comandi relativi ad essa possono essere eliminati.
Alla fine della procedura di “setup” ci sono 6 comandi in serie per portare a 0 gradi la posizione di tutti i servi al momento dell’accensione del sistema.
// Posizionamento a 0 di tutti i servi mioservo1.write(0); mioservo2.write(0); mioservo3.write(0); mioservo4.write(0); mioservo5.write(0); mioservo6.write(0); |
Se si desidera è possibile cambiare questi valori con un numero in gradi compreso tra 0 e 180. Il numero è tra parentesi tonde alla fine di ogni comado.
Questo vale anche per tutti gli altri comandi che azionano il movimento dei servi.
Alla fine di ogni routine di azionamento dei servi c’è un comando “delay” che ci permette di modificare la velocità di movimento dei servi. Qui è impostata a 15 millisecondi e serve per regolare il conteggio dei cicli “for”:
if (pval1 > 700) { Serial.print("Valore tasto per servo 1 = Non premuto = "); Serial.println(pval1); } else if(pval1 < 700 && pval1 > 300) { Serial.print("Valore tasto per servo 1 = Premuto 2 = Spegni relè 1 = "); Serial.println(pval1); digitalWrite(rele1, LOW); // Spegni il relè 1 for(pos1 = 30; pos1 >= 1; pos1 -= 1) // Aziona il servo 1 antiorario { mioservo1.write(pos1); delay(15); } |
Qui sopra è evidenziata una delle 12 routine decisionali dedicata al primo tasto della pulsantiera.
Funzioni I2c:
La scheda prevede l’utilizzo del bus seriale I2C tramite i morsetti ad esso dedicato e sono posizionati sotto i morsetti GND e VIN sulla destra sella scheda Arduino.
Tale protocollo per ora non viene usato, ma in futuro sarà possibile sfruttarlo per collegare insieme diverse schede di questo tipo per avere così il controllo di molti più deviatoi contemporaneamente. Ad esempio con 4 schede sarà possibile gestire 24 deviatoi e con 8 schede 48 deviatoi.
Il tutto sarà comandato da una scheda madre dotata di display LCD e tastierino alfanumerico a 16 tasti.
Elenco componenti:
5VCC AK500/2-H AK500/2-H CONNECTOR
D1 1N4148DO35-7 1N4148DO35-7 DO35-7 DIODE 58
D2 1N4148DO35-7 1N4148DO35-7 DO35-7 DIODE 58
D3 1N4148DO35-7 1N4148DO35-7 DO35-7 DIODE 58
D4 1N4148DO35-7 1N4148DO35-7 DO35-7 DIODE 58
D5 1N4148DO35-7 1N4148DO35-7 DO35-7 DIODE 58
D6 1N4148DO35-7 1N4148DO35-7 DO35-7 DIODE 58
I2C AK500/2-H AK500/2-H CONNECTOR
LED1 ROSSO LED3MM LED3MM LED 97
LED2 ROSSO LED3MM LED3MM LED 97
LED3 ROSSO LED3MM LED3MM LED 97
LED4 ROSSO LED3MM LED3MM LED 97
LED5 ROSSO LED3MM LED3MM LED 97
LED6 ROSSO LED3MM LED3MM LED 97
MODUL1 ARDUINO_NANO
R1 4700 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R2 10k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R3 330 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R4 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R5 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R6 4700 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R7 10k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R8 330 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R9 4700 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R10 10k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R11 330 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R12 4700 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R13 10k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R14 330 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R15 4700 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R16 10k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R17 330 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R18 4700 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R19 10k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R20 330 R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R21 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R22 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R23 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R24 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R25 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R26 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R27 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R28 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R29 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
R30 1k R-EU_R1206 R1206 RESISTOR, European symbol 41 R
S1 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S2 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S3 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S4 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S5 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S6 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S7 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S8 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S9 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S10 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S11 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
S12 10-XX B3F-10XX OMRON SWITCH B3F-1000 176432 36M3542 72
SV1 MA03-2 MA03-2 PIN HEADER unknown unknown 15
SV2 MA03-2 MA03-2 PIN HEADER unknown unknown 15
SV3 MA03-2 MA03-2 PIN HEADER unknown unknown 15
SV4 MA08-1 MA08-1 PIN HEADER unknown unknown 10
SV5 MA08-1 MA08-1 PIN HEADER unknown unknown 10
T1 BC547 BC547 TO92 NPN TRANSISTOR 43
T2 BC547 BC547 TO92 NPN TRANSISTOR 43
T3 BC547 BC547 TO92 NPN TRANSISTOR 43
T4 BC547 BC547 TO92 NPN TRANSISTOR 43
T5 BC547 BC547 TO92 NPN TRANSISTOR 43
T6 BC547 BC547 TO92 NPN TRANSISTOR 43
X1 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
X2 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
X3 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
X4 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
X5 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
X6 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
X7 AK500/2-H AK500/2-H CONNECTOR unknown unknown 3
Aggiornamento (30/09/2022)
Sono state realizzate nuove schede per questo progetto, in particolare è stata inserita la scheda di telecomando per utilizzare più schede di gestione dei servi contemporaneamente, fino ad un massimo di 128 tramite l’interfaccia I2C di Arduino.
Con questa scheda è possibile gestire fino a 768 servocomandi contemporaneamente.
Brevemente, il protocollo I2C è stato inventato da Philips negli anni 80 e serve per far comunicare tra loro i circuiti integrati tramite una porta seriale asincrona.
I fili necessari per il funzionamento di questa interfaccia sono 3:
– SDA (SerialData);
– SCL (SerialClock);
– GND (ground o polo negativo).
Ai pin SDA ed SCL, va applicata una resistenza verso il positivo di alimentazione di 5 o 3,3 Volt CC.
Tale resistenza ha un valore nel nostro caso di 1500 ohm.
Qui sotto potete vedere uno schema a blocchi di come è collegata la scheda telecomando con due schede di gestione dei servi, per avere il controllo di un totale di 12 servi.
Le schede “slave” hanno indirizzi numerici 1 e 2:
Schema elettrico di una scheda da 6 servi (slave) aggiornata:
Come possibile notare, la nuova scheda ha anche un relè abbinato ad ogniuno dei 6 servi.
Ogni relè gestisce due deviatori.
Schema elettrico della una scheda “master” del controller:
La scheda “master”, contiene un microcontrollore Arduino Nano, un display LCD da 16 caratteri per 2 linee e un tastierino composto da 16 pulsanti (4 x 4).
La sua funzione è quella di inviare comandi a tutte le schede “slave” ad essa collegata, cioè a tutte le schede da 6 servocomandi ciascuna:
Tramite il tastierino numerico è possibile digitare un codice che verrà inviato a tutte le schede “slave”.
Solo la scheda corrispondente al codice digitato eseguirà il comando ed azionerà il servo richiesto.
La sintassi del codice è molto semplice ed è composta da 5 caratteri.
Tre numeri per l’indirizzo della scheda (001/128), un numero per il numero del servo (1/6) ed infine una lettera per indicare la posizione del deviatoio D o C.
D sta per “deviato” o “non corretto tracciato”, mentre C sta per “corretto tracciato”.
É molto semplice; se per esempio voglio azionare il deviatoio numero 5 della scheda numero 2, il codice sarà: 0025D.
Se voglio riportare alla posizione iniziale lo stesso deviatoio scriverò: 0025C,
In pratica, le prime 3 cifre identificano l’indirizzo I2C della scheda che è compreso tra 001 e 128.
La quarta cifra identifica il numero del deviatoio da azionare ed è compreso tra 1 e 6.
Infine l’ultimo carattere può essere una lettera D o una lettera C per indicare la posizione del deviatoio:
– D deviata o non corretto tracciaot;
– C corretto tracciato.
La matrice dei caratteri del tastierino numerico è la seguente:
123A
456B
789C
*0#D
In pratica abbiamo tutti i numeri decimali da 0 a 9, le lettere A, B, C e D ed i simboli asterisco e cancelletto.
Qui una foto del sistema con la scheda master e due schede slave con 12 servi:
Qui la scheda master con il codice inserito :
PCB delle schede:
Software:
Il nuovo software della scheda servi, ora è in grado di memorizzare nella EEPROM di Arduino l’ultima posizione impostata così da caricarla alla sua accensione.
// Controllo servi su Arduino Nano, Edgardo Rosatti 2022. Scheda con indirizzo I2C 001. #include <Wire.h> // Includo la libreria Wire.h per la gestione di I2C sui pin A4 e A5 #include <Servo.h> // Includo la libreria Servo.h pa la gestione pratica dei servocomandi #include <EEPROM.h> // Includo la libreria EEPROM.h per memorizzare lo stato dei servi anche a circuito spento // Creazione degli oggetti servo Servo mioservo1; Servo mioservo2; Servo mioservo3; Servo mioservo4; Servo mioservo5; Servo mioservo6; // Creazione delle variabili "pos" per definire la posizione iniziale dei servocomandi int pos1 = 0; int pos2 = 0; int pos3 = 0; int pos4 = 0; int pos5 = 0; int pos6 = 0; // Definizione delle variabili "pservo" sulle porte analogiche di ingresso A0, A1, A2, A3, A6 e A7, le porte analogiche di ingresso A4 e A5 vengono utilizzate per l'interfaccia seriale I2C int pservo1 = A0; int pservo2 = A1; int pservo3 = A2; int pservo4 = A3; int pservo5 = A6; int pservo6 = A7; // Definizione delle variabili sDA e sCL sulle porte analogiche A4 e A5 int sDA = A4; int sCL = A5; // Definizione delle variabili di lettura dello stato di A0, A1, A2, A3, A6 e A7 int pval1 = 0; int pval2 = 0; int pval3 = 0; int pval4 = 0; int pval5 = 0; int pval6 = 0; // Definizione delle variabili di assegnazione ai relè su porte digitali int rele1 = 2; int rele2 = 4; int rele3 = 7; int rele4 = 8; int rele5 = 12; int rele6 = 13; void setup() { Serial.begin(9600); // Inizializzazione della porta seriale a 9600 baud Wire.begin(1); // Inizializzazione I2C con indirizzo 1 slave (7 bit) Wire.onReceive(riceviDati); // Imposta i pin di gestione relè su uscite digitali pinMode(rele1, OUTPUT); pinMode(rele2, OUTPUT); pinMode(rele3, OUTPUT); pinMode(rele4, OUTPUT); pinMode(rele5, OUTPUT); pinMode(rele6, OUTPUT); // Assegnazione dei servi alle porte relative D3, D5, D6, D9, D10 e D11 mioservo1.attach(3); mioservo2.attach(5); mioservo3.attach(6); mioservo4.attach(9); mioservo5.attach(10); mioservo6.attach(11); // Recupero della posizione dei servi e dei relè dalla memoria EEPROM int val1 = EEPROM.read(0); if (val1 == 0) { digitalWrite(rele1, LOW); // Spegni il relè 1 mioservo1.write(0); } else { digitalWrite(rele1, HIGH); // Accendii il relè 1 mioservo1.write(30); } int val2 = EEPROM.read(1); if (val2 == 0) { digitalWrite(rele2, LOW); // Spegni il relè 2 mioservo2.write(0); } else { digitalWrite(rele2, HIGH); // Accendii il relè 2 mioservo2.write(30); } int val3 = EEPROM.read(2); if (val3 == 0) { digitalWrite(rele3, LOW); // Spegni il relè 3 mioservo3.write(0); } else { digitalWrite(rele3, HIGH); // Accendii il relè 2 mioservo3.write(30); } int val4 = EEPROM.read(3); if (val4 == 0) { digitalWrite(rele4, LOW); // Spegni il relè 4 mioservo4.write(val4); } else { digitalWrite(rele4, HIGH); // Accendii il relè 4 mioservo4.write(30); } int val5 = EEPROM.read(4); if (val5 == 0) { digitalWrite(rele5, LOW); // Spegni il relè 5 mioservo5.write(0); } else { digitalWrite(rele5, HIGH); // Accendii il relè 5 mioservo5.write(30); } int val6 = EEPROM.read(5); if (val6 == 0) { digitalWrite(rele6, LOW); // Spegni il relè 6 mioservo6.write(0); } else { digitalWrite(rele6, HIGH); // Accendii il relè 6 mioservo6.write(30); } } void loop() { pval1=analogRead(pservo1); pval2=analogRead(pservo2); pval3=analogRead(pservo3); pval4=analogRead(pservo4); pval5=analogRead(pservo5); pval6=analogRead(pservo6); if (pval1 > 700) { // Serial.print("Valore tasto per servo 1 = Non premuto = "); // Serial.println(pval1); } else if(pval1 < 700 && pval1 > 300) { // Serial.print("Valore tasto per servo 1 = Premuto 2 = Spegni relè 1 = "); // Serial.println(pval1); digitalWrite(rele1, LOW); // Spegni il relè 1 for(pos1 = 30; pos1 >= 1; pos1 -= 1) // Aziona il servo 1 antiorario { mioservo1.write(pos1); delay(10); } EEPROM.write(0,0); // Memorizza OFF servo 1 } else if(pval1 < 300) { // Serial.print("Valore tasto per servo 1 = Premuto 1 = Accendi relè 1 = "); // Serial.println(pval1); digitalWrite(rele1, HIGH); // Accendi il relè 1 for(pos1 = 0; pos1 < 30; pos1 += 1) // Aziona il servo 1 orario { mioservo1.write(pos1); delay(10); } EEPROM.write(0,1); // Memorizza ON servo 1 } if (pval2 > 700) { // Serial.print("Valore tasto per servo 2 = Non premuto = "); // Serial.println(pval2); } else if(pval2 < 700 && pval2 > 300) { // Serial.print("Valore tasto per servo 2 = Premuto 2 = Spegni relè 2 = "); // Serial.println(pval2); digitalWrite(rele2, LOW); // Spegni il relè 2 for(pos2 = 30; pos2 >= 1; pos2 -= 1) // Aziona il servo 2 antiorario { mioservo2.write(pos2); delay(10); } EEPROM.write(1,0); // Memorizza OFF servo 2 } else if(pval2 < 300) { // Serial.print("Valore tasto per servo 2 = Premuto 1 = Accendi relè 2 = "); // Serial.println(pval2); digitalWrite(rele2, HIGH); // Accendi il relè 2 for(pos2 = 0; pos2 < 30; pos2 += 1) // Aziona il servo 2 orario { mioservo2.write(pos2); delay(10); } EEPROM.write(1,1); // Memorizza ON servo 2 } if (pval3 > 700) { // Serial.print("Valore tasto per servo 3 = Non premuto = "); // Serial.println(pval3); } else if(pval3 < 700 && pval3 > 300) { // Serial.print("Valore tasto per servo 3 = Premuto 2 = Spegni relè 3 = "); // Serial.println(pval3); digitalWrite(rele3, LOW); // Spegni il relè 3 for(pos3 = 30; pos3 >= 1; pos3 -= 1) // Aziona il servo 3 antiorario { mioservo3.write(pos3); delay(10); } EEPROM.write(2,0); // Memorizza OFF servo 3 } else if(pval3 < 300) { // Serial.print("Valore tasto per servo 3 = Premuto 1 = Accendi relè 3 = "); // Serial.println(pval3); digitalWrite(rele3, HIGH); // Accendi il relè 3 for(pos3 = 0; pos3 < 30; pos3 += 1) // Aziona il servo 3 orario { mioservo3.write(pos3); delay(10); } EEPROM.write(2,1); // Memorizza ON servo 3 } if (pval4 > 700) { // Serial.print("Valore tasto per servo 4 = Non premuto = "); // Serial.println(pval4); } else if(pval4 < 700 && pval4 > 300) { // Serial.print("Valore tasto per servo 4 = Premuto 2 = Spegni il relè 4 = "); // Serial.println(pval4); digitalWrite(rele4, LOW); // Spegni il relè 4 for(pos4 = 30; pos4 >= 1; pos4 -= 1) // Aziona il servo 4 antiorario { mioservo4.write(pos4); delay(10); } EEPROM.write(3,0); // Memorizza OFF servo 4 } else if(pval4 < 300) { // Serial.print("Valore tasto per servo 4 = Premuto 1 = Accendi il relè 4 = "); // Serial.println(pval4); digitalWrite(rele4, HIGH); // Accendi il relè 4 for(pos4 = 0; pos4 < 30; pos4 += 1) // Aziona il servo 4 orario { mioservo4.write(pos4); delay(10); } EEPROM.write(3,1); // Memorizza ON servo 4 } if (pval5 > 700) { // Serial.print("Valore tasto per servo 5 = Non premuto = "); // Serial.println(pval5); } else if(pval5 < 700 && pval5 > 300) { // Serial.print("Valore tasto per servo 5 = Premuto 2 = Spegni il relè 5 = "); // Serial.println(pval5); digitalWrite(rele5, LOW); // Spegni il relè 5 for(pos5 = 30; pos5 >= 1; pos5 -= 1) // Aziona il servo 5 antiorario { mioservo5.write(pos5); delay(10); } EEPROM.write(4,0); // Memorizza OFF servo 5 } else if(pval5 < 300) { // Serial.print("Valore tasto per servo 5 = Premuto 1 = Accendi il relè 5 = "); // Serial.println(pval5); digitalWrite(rele5, HIGH); // Accendi il relè 5 for(pos5 = 0; pos5 < 30; pos5 += 1) // Aziona il servo 5 orario { mioservo5.write(pos5); delay(10); } EEPROM.write(4,1); // Memorizza ON servo 5 } if (pval6 > 700) { // Serial.print("Valore tasto per servo 6 = Non premuto = "); // Serial.println(pval6); } else if(pval6 < 700 && pval6 > 300) { // Serial.print("Valore tasto per servo 6 = Premuto 2 = Spegni il relè 6 = "); // Serial.println(pval6); digitalWrite(rele6, LOW); // Spegni il relè 6 for(pos6 = 30; pos6 >= 1; pos6 -= 1) // Aziona il servo 6 antiorario { mioservo6.write(pos6); delay(10); } EEPROM.write(5,0); // Memorizza OFF servo 6 } else if(pval6 < 300) { // Serial.print("Valore tasto per servo 6 = Premuto 1 = Accendi il relè 6 = "); // Serial.println(pval6); digitalWrite(rele6, HIGH); // Accendi il relè 6 for(pos6 = 0; pos6 < 30; pos6 += 1) // Aziona il servo 6 orario { mioservo6.write(pos6); delay(10); } EEPROM.write(5,1); // Memorizza ON servo 6 } // delay(200); // Imposta un ritardo di 0.2 secondi tra una scansioe e l'altra // Serial.println(); // stampa una linea vuota } void riceviDati() { // Ricevi dati da i2C e stampali sul monitor seriale int x = Wire.read(); // Numero servo char s = Wire.read(); // Posizione (C = corretto tracciato, D = deviata) Serial.println(x); Serial.println(s); if (x == 1) { Serial.println("Deviatoio 1"); if ( s == 'C') { Serial.println("Corretto tracciato"); digitalWrite(rele1, LOW); // Spegni il relè 1 for(pos1 = 30; pos1 >= 1; pos1 -= 1) // Aziona il servo 1 antiorario { mioservo1.write(pos1); delay(10); } EEPROM.write(0,0); // Memorizza OFF servo 1 } else if ( s == 'D') { Serial.println("Deviata"); digitalWrite(rele1, HIGH); // Accendi il relè 1 for(pos1 = 0; pos1 < 30; pos1 += 1) // Aziona il servo 1 orario { mioservo1.write(pos1); delay(10); } EEPROM.write(0,1); // Memorizza ON servo 1 } } else if (x == 2) { Serial.println("Deviatoio 2"); if ( s == 'C') { Serial.println("Corretto tracciato"); digitalWrite(rele2, LOW); // Spegni il relè 2 for(pos2 = 30; pos2 >= 1; pos2 -= 1) // Aziona il servo 2 antiorario { mioservo2.write(pos2); delay(10); } EEPROM.write(1,0); // Memorizza OFF servo 2 } else if ( s == 'D') { Serial.println("Deviata"); digitalWrite(rele2, HIGH); // Accendi il relè 2 for(pos2 = 0; pos2 < 30; pos2 += 1) // Aziona il servo 2 orario { mioservo2.write(pos2); delay(10); } EEPROM.write(1,1); // Memorizza ON servo 2 } } else if (x == 3) { Serial.println("Deviatoio 3"); if ( s == 'C') { Serial.println("Corretto tracciato"); digitalWrite(rele3, LOW); // Spegni il relè 3 for(pos3 = 30; pos3 >= 1; pos3 -= 1) // Aziona il servo 3 antiorario { mioservo3.write(pos3); delay(10); } EEPROM.write(2,0); // Memorizza OFF servo 3 } else if ( s == 'D') { Serial.println("Deviata"); digitalWrite(rele3, HIGH); // Accendi il relè 3 for(pos3 = 0; pos3 < 30; pos3 += 1) // Aziona il servo 3 orario { mioservo3.write(pos3); delay(10); } EEPROM.write(2,1); // Memorizza ON servo 3 } } else if (x == 4) { Serial.println("Deviatoio 4"); if ( s == 'C') { Serial.println("Corretto tracciato"); digitalWrite(rele4, LOW); // Spegni il relè 4 for(pos4 = 30; pos4 >= 1; pos4 -= 1) // Aziona il servo 4 antiorario { mioservo4.write(pos4); delay(10); } EEPROM.write(3,0); // Memorizza OFF servo 4 } else if ( s == 'D') { Serial.println("Deviata"); digitalWrite(rele4, HIGH); // Accendi il relè 4 for(pos4 = 0; pos4 < 30; pos4 += 1) // Aziona il servo 4 orario { mioservo4.write(pos4); delay(10); } EEPROM.write(3,1); // Memorizza ON servo 4 } } else if (x == 5) { Serial.println("Deviatoio 5"); if ( s == 'C') { Serial.println("Corretto tracciato"); digitalWrite(rele5, LOW); // Spegni il relè 5 for(pos5 = 30; pos5 >= 1; pos5 -= 1) // Aziona il servo 5 antiorario { mioservo5.write(pos5); delay(10); } EEPROM.write(4,0); // Memorizza OFF servo 5 } else if ( s == 'D') { Serial.println("Deviata"); digitalWrite(rele5, HIGH); // Accendi il relè 5 for(pos5 = 0; pos5 < 30; pos5 += 1) // Aziona il servo 5 orario { mioservo5.write(pos5); delay(10); } EEPROM.write(4,1); // Memorizza ON servo 5 } } else if (x == 6) { Serial.println("Deviatoio 6"); if ( s == 'C') { Serial.println("Corretto tracciato"); digitalWrite(rele6, LOW); // Spegni il relè 6 for(pos6 = 30; pos6 >= 1; pos6 -= 1) // Aziona il servo 6 antiorario { mioservo6.write(pos6); delay(10); } EEPROM.write(5,0); // Memorizza OFF servo 6 } else if ( s == 'D') { Serial.println("Deviata"); digitalWrite(rele6, HIGH); // Accendi il relè 6 for(pos6 = 0; pos6 < 30; pos6 += 1) // Aziona il servo 6 orario { mioservo6.write(pos6); delay(10); } EEPROM.write(5,1); // Memorizza ON servo 6 } } } |
Qui sotto invece il software del telecomando:
// Controller di comando per servi basato su bus I2C, Edgardo Rosatti (c) 2022 // Libreria I2C #include <Wire.h> // Libreria display LCD #include <LiquidCrystal.h> // Libreria keypad #include <Keypad.h> char keyInsert[5]; // Variabile alfanumerica indicizzata per codice immesso // Queste variabili servono come verifica del corretto inserimento del comando int i = 0; int j = 0; int s = 0; int x = 0; const byte ROWS = 4; // Quattro righe const byte COLS = 4; // Quattro colonne char keys[ROWS][COLS] = { {'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; // Definizione mappa della tastiera byte rowPins[ROWS] = { 8, 9, 10, 11 }; // Riga 0,1,2,3 byte colPins[COLS] = { 13, 12, 15 ,14 }; // Colonna 0,1,2,3 // Creazione della tastiera Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); // Definizione collegamenti display LiquidCrystal lcd(2, 3, 4, 5, 6, 7); void setup() { // Setup protocollo I2C (master), porta seriale e display LCD Wire.begin(); Serial.begin(9600); lcd.begin(16, 2); } void loop(){ lcd.blink(); // Cursore lampeggiante char key = kpd.getKey(); // Lettura del tasto premuto if (i==0){ // Stampa "Codice:" sul display Serial.println("Codice: "); lcd.setCursor(0,0); lcd.print("Codice: "); i++; } if (key != NO_KEY && j<6){ // Visualizza i tasti premuti Serial.println(key); lcd.print(key); keyInsert[j]=key; j++; } if(j==5){ // Dopo il quinto numero visualizza il codice intero lcd.setCursor(0,1); // Posiziona il cursore sulla seconda riga // lcd.print("Codifica: "); // Stampa intestazione // Serial.println("Codifica..."); // Serial.print(keyInsert[0]); // lcd.print (keyInsert[0]); String numInsert0; // Definisci string numInsert0 = keyInsert[0]; // Assegna char a string int val0 = (atoi(numInsert0.c_str())); // Converti string in un numero intero dentro val0 // lcd.print (val0); // Stampa val0 sul display // Serial.println(val0); // Stampa val0 su seriale // Serial.print(keyInsert[1]); // lcd.print (keyInsert[1]); String numInsert1; // Definisci string numInsert1 = keyInsert[1]; // Assegna char a string int val1 = (atoi(numInsert1.c_str())); // Converti string in un numero intero dentro val1 // lcd.print (val1); // Stampa val1 sul display // Serial.println(val1); // Stampa val1 su seriale // Serial.print(keyInsert[2]); // lcd.print (keyInsert[2]); String numInsert2; // Definisci string numInsert2 = keyInsert[2]; // Assegna char a string int val2 = (atoi(numInsert2.c_str())); // Converti string in un numero intero dentro val2 // lcd.print (val2); // Stampa val2 sul display // Serial.println(val2); // Stampa val2 su seriale int address=(val0*100)+(val1*10)+(val2); Serial.print("Indirizzo I2C: "); Serial.println(address); lcd.print("Ind: "); lcd.print (address); // Serial.println(keyInsert[3]); // lcd.print (keyInsert[3]); String numInsert3; // Definisci string numInsert3 = keyInsert[3]; // Assegna char a string int deviatoio = (atoi(numInsert3.c_str())); // Converti string in un numero intero dentro deviatoio lcd.print(" Dev: "); lcd.print(deviatoio); // Stampa deviatoio sul display Serial.print("Deviatoio numero: "); Serial.println(deviatoio); // Stampa deviatoio su seriale Serial.print("Posizione (C = corretto tracciato, D = deviato): "); // Stampa posizione del deviatoio su seriale Serial.println(keyInsert[4]); lcd.print (keyInsert[4]); // Stampa posizione del deviatoio sul display i=0; // azzera i contatori j=0; Wire.beginTransmission(address); // Inizializzo trasmissione dati con indirizzo I2C contenuto in address Wire.write(deviatoio); // Scrivo il numero del deviatoio su I2C contenuto in deviatoio Wire.write(keyInsert[4]); // Scrivo lo stato di azionamento del deviatoio (C o D) su I2C contenuto in keyInsert[4] Wire.endTransmission(); // Termino la trasmissione I2C delay (3000); // Imposta un ritardo di 3 secondi lcd.clear(); // Pulisci lo schermo LCD } } |