Come far muovere i servo con movimento lento e gestire un quadro sinottico con Arduino

valido per protocolli NMRA DCC a norma come Xpressnet, Loconet etc . usati dalle centrali Roco, Lenz, Hornby, Digitrax, Hornby,ESU, Intellibox ed altre.

progetto realizzato da Gianni Barbotti

Revisioni:

1) 04 Maggio 2019

2) 17 Luglio 2019

3) 30 Luglio 2019

4) 19 Agosto 2019


email: cromaad91@libero.it

Parte I

Servo e salvataggio del loro stato.

N.B.: Solo una centralina DCC a norma NMRA è adatta a questo progetto. Sicuramente tutte le centraline DCC che utilizzano il protocollo XpressNet. Ho verificato il funzionamento relativo al test dell'interfaccia DCC, della funzionalità di una singola, di due PCA9685 concatenate con le centraline DCC LZV100 della Lenz (protocollo Xpressnet) e Digitrax DCS 100 (protocollo Loconet) . Stessa verifica anche con Train Controller.

Da tempo inseguivo l'idea di poter comandare il movimento lento dei deviatoi, visualizzarne lo stato con led raggruppati nell'area in cui si opera sul tracciato e comandare i segnali luminosi, il tutto in ambiente DCC in modalità manuale o semiautomatica o automatica usando software quali Train Controller o simili senza però usare i decoder stazionari per deviatoi , ma utilizzando Arduino e le numerose offerte di accessori presenti per esso.

I punti qualificanti sono dunque:

a) Usare uno sketch (programma) per Arduino UNO R3 e/o Mega 2560 R3 per gestire deviatoi e segnali in un tracciato ferroviario in ambito DCC, potendo muovere in modo lento gli aghi dei deviatoi e sfumando (fade) il cambio di colore dei segnali anche con software come TCO o Rocrail ecc., utilizzando i servi.

Va comunque subito sottolineato che l'uso di Train Controller o software simile è opzionale;

b) controllare la posizione dei deviatoi con LED (ben spiegato più avanti con due immagini);

c) utilizzare i LED come segnali luminosi (semafori) con effetto di luce sfumata in fase d'accensione e spegnimento. Questa parte verrà sviluppata quanto prima , considerando che quanto già implementato, la rende di semplice realizzazione;

d) memorizzare lo stato dei led in modo tale che, una volta spento e quindi riacceso il sistema-plastico, ogni deviatoio mosso dai servo, colore dei led indicanti la posizione dei deviatoi e segnali luminosi gestiti dallo sketch, si disponessero nello stato in cui si trovavano prima dello spegnimento.

Allo scopo ho utilizzato un'interfaccia per Arduino UNO ed anche Arduino Mega 2560 con scheda SD disponendo le connessioni in modo opportuno, come ampiamente descritto in dettaglio più avanti.

Quando parlo di interfacce mi riferisco ad elementi di terze parti di Arduino e le cui dimensioni, per fortuna, risultano sempre di pochi centimetri.

Quindi la discriminante era ed è l'utilizzo del microcontrollore Arduino nella versione UNO R3 o Mega 2560 R3. Il materiale disponibile in rete è notevole, ma non ho trovato nulla che rispondesse alle esigenze di gestione di tanti servi e led da attivare singolarmente e/o a gruppi e che coniugasse tutti i livelli necessari e sufficienti per realizzare l'idea.

Sinteticamente:

  1. i servo muovono i deviatoi;
  2. la potenza necessaria a far ruotare l'asse di un servo è fornita da interfacce o schede specifiche. Con i pin di Arduino sarebbe impossibile collegare tanti servo e Led necessari su un tracciato ferroviario ed attivarli, sia per mancanza di erogazione di potenza necessaria, sia per pochezza numerica dei pin di Arduino;
  3. Le interfacce di cui sopra sono a loro volta attivate da Arduino con un programma (sketch) in esso memorizzato;
  4. la scelta di quale servo attivare è affidata a chi controlla col palmare la centralina DCC , attraverso la selezione dell'indirizzo del servo , oppure mediante l'utilizzo di software come Rocrail, Train Controller e analoghi da usare in modalità automatica o manuale.

Arduino ed il DCC non “parlano” la stessa lingua (protocollo). Il sistema DCC (Lenz,Digitrax od ESU etc) come si può colloquiare con Arduino affinché invii i comandi alle interfacce, che muovono i servo e pilotano i led, in modo corretto e nella sua “lingua”? In ambiente DCC "classico" i deviatoi vengono mossi da motori sotto plancia o da solenoidi attivati da decodificatori stazionari. Questi gestiscono i motori , generalmente, in gruppi di quattro ed a ciascuna coppia motore-deviatoio si associa un numero detto indirizzo. I motori sotto plancia ed i solenoidi, in questo progetto, sono sostituiti dai servi ed i decodificatori da interfacce. Quella che ho utilizzato per comandare i servi è la

16-Cannello 12-bit PWM/Servo Driver -I2C - PCA9685

dove :

16-Channel indica che una sola interfaccia gestisce fino a 16 servo (contro i 4 di dun decodificatore (anche se il Lenz LS 150 ne può gestire 6);

12-bit è la qualità del segnale : in generale più sono i bit e più il segnale è efficace;

PWM modulazione di larghezza d'impulso ( pulse-width modulation); è un tipo di modulazione digitale che permette di ottenere una tensione media variabile, necessaria a far muovere i servo con velocità prestabilita ;

I2C è il protocollo con cui queste interfacce colloquiano con Arduino;

PCA9685 è l'integrato della scheda-interfaccia con bus I2C che pilota i servo.


Il costo unitario di queste interfacce è di molto inferiore , se paragonato a quello di un decoder. Le interfacce si trovano in vendita, in rete, anche con i pin già tutti saldati e pronte per essere collegate ai servi. Peracquistarle è sufficiente digitare 16-Channel 12-bit PWM/Servo . Il costo di una di queste interfacce è, oggi , 1/4di quella di un decoder digitale a quattro vie , ma è in grado di monitorare il quadruplo dei deviatoi, cosicchè il costo di ciascun collegamento è 1/16 di quello effettuato con un decodificatore. Inoltre risulta estremamente vantaggioso il prezzo di un singolo servo rispetto ad un motore sottoplancia che, a mio parere, è davvero spropositato.

I led sono pilotati dall'interfaccia 24-Channel 12-bit PWM/Led. Ciascuna di queste interfacce possiede 24 coppie di pin a ciascuna delle quali si collega un led monocolore. Così ciascuna scheda è in grado di pilotare 24 led monocolore. E' pure possibile gestire led RGB (Red Green Blue) coi quali, miscelando opportunamente i tre colori, possiamo ottenere le varie tonalità di giallo. In questo caso ogni led RGB occupa però tre coppie di pin in successione, cosicché una scheda TLC5947 può gestire non oltre otto led RGB.

Se Arduino colloquia naturalmente con le interfacce I2C - PCA9685, ed SPI-TLC5947 non riesce però a farlo con la centralina DCC. Per questo c'è bisogno di un'ulteriore elemento, che si frapponga tra il sistema DCC ed Arduino. Con questa interfaccia quando dal PC, o col palmare, modifichiamo lo stato di un deviatoio o di un segnale luminoso, questa variazione verrà spedita all'interfaccia che comunicherà ad Arduino ciò che abbiamo deciso di fare ed Arduino , a sua volta, attraverso le istruzioni dello sketch, invierà alle PCA9685 e/o TLC5947 i comandi necessari per muovere il servo e/o cambiare stato al led.

Queste interfacce possono essere schede che si sovrappongono ad Arduino (shield) o schede che si connettono ad Arduino con i classici conduttori (fili o ponticelli o jumper). Qualsiasi DCC shield per Arduino UNO R3 o Mega 2560 va bene. Ho utilizzato alternativamente sia una shield che un'interfaccia DCC connessa con fili. Come interfaccia DCC ho utilizzato un'interfaccia di dimensioni ridottissime 40mm x 24 mm che ho trovato in rete (digitate dcc interface su un motore di ricerca e la troverete). Per aiutarvi allego qui le immagini necessarie al collegamento, prese dal sito nel quale sono in vendita:

https://www.dccinterface.com/product/arduino-model-railway-dcc-interface/

Passaggio 1: iniziare collegando un ponticello (rosso) dal piedino 5v dell'Arduino all'ingresso [+] del connettore tripolare dell'interfaccia DCC .

Passaggio 2 - Collegare un cavo jumper (grigio) da un pin GND di Arduino all'ingresso [-] del connettore tripolare dell'interfaccia DCC.

Passaggio 3 – Collegare un cavo (blu) dal pin D2 di Arduino all'ingresso [Ard] del connettore tripolare dell'interfaccia DCC.

Passaggio 4: collegare un cavo dal connettore bipolare della scheda DCC ad uno dei connettori della centralina digitale (per es. J o K nella Lenz LZV100).

Passaggio 5: collegare un secondo cavo dal connettore bipolare della scheda DCC all'altro dei connettori della centralina digitale (per es. J se prima si è collegato il K, oppure K se si è collegato J nella Lenz LZV100). Le connessioni finali dovrebbero apparire come segue :

Le componenti della shield mi sono state regalate e spedite gratuitamente da Luca Dentella che ringrazio per avermi avviato verso la realizzazione del progetto, affermando che era sicuramente realizzabile visto quanto proposto nel suo sito, dove troverete anche le immagini della shield. Ecco il link:

http://www.lucadentella.it/2017/11/25/dcc-decoder-accessori-per-led/

E' importante sottolineare che con la shield, che si sovrappone ad Arduino UNO R3 o Mega 2560 R3, dovrete solo collegare il connettore bipolare a vite al tracciato o direttamente alla centralina DCC per poter comunicare con questa.

N.B.: Qualsiasi shield e/o interfaccia DCC va bene sia per Arduino UNO R3 che per Arduino Mega 2560 R3.

Nel seguito i termini schede, interfacce e breakout sono usati come sinonimi.

Da non trascurare che utilizzare gli elementi di questo progetto in un plastico comporta un notevole risparmio sull'acquisto del materiale e tanto più numerosi saranno i deviatoi, passaggi a livello, segnali luminosi da gestire tanto più sarà maggiore il risparmio.

Ecco gli elementi che ho utilizzato per realizzare il progetto.

1)'Arduino UNO R3 ed Arduino Mega 2560 R3

2) due interfacce 16-Channel 12-bit PWM/Servo Driver -I2C - PCA9685 . Due per verificare che le interfacce lavorino, collegate in serie ma alimentate in parallelo come vedremo più avanti, senza alcun problema.

3) due interfacce 24-Channel 12-bit PWM LED Driver - SPI Interface – TLC5947 (due per verificare che le interfacce lavorino collegate in serie senza alcun problema).

4) Servi gestibili in numero massimo di 32 con due schede. Pertanto con N schede gestirete un massimo di Nx16 servi. Se ad esempio dovete gestire 49 (quarantanove) deviatoi a due vie avrete bisogno di un numero di schede [49 : 16] -> 4 schede 16-Channel 12-bit dove,se vi sono decimali, il risultato della divisione deve essere arrotondato all'unità superiore. Va fatto poi il necessario aggiustamento per i deviatoi a tre vie od inglesi che richiedono due servi per la loro gestione.

5) Alimentatore 5V DC 3A (output), 220 AC (input) per le due 16 canali 12-bit che vanno assolutamente alimentate separatamente e non con la tensione +5V proveniente da Arduino visto che l'assorbimento di corrente, da parte dei servi, risulterebbe troppo oneroso. Se connetterete solo una scheda il programma (sketch) funzionerà ugualmente. Io ne ho usate due per testare anche il collegamento tra due schede .

6) Alimentatore 5-25 V DC 3A-5A per alimentare le interfacce 24-Channel 12-bit PWM/Led. Valutate voi stessi l'amperaggio in funzione dell'assorbimento previsto dal numero di schede di cui avrete bisogno. Ricordate che la metà dei Led degli indicatori di posizione deviatoi rimarranno sempre accesi , oltre alle luci del plastico che vorrete gestire con le TLC5947 ed essendo le schede collegate in serie, va tenuto conto della caduta di potenziale, per questo la differenza di potenziale dell'alimentatore, misurata in volt, può essre così relativamente alta.

Per le caratteristiche tecniche di queste tre interfacce andate in rete e digitate :

  • 16-Channel 12 bit PWM Servo Motor Driver moduli I2C PCA9685
  • 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947
  • https://www.dccinterface.com/

troverete dei documenti in lingua inglese ed in formato PDF che dissiperanno ogni vostro dubbio e necessità di approfondimento sulle caratteristiche fisiche di questi dispositivi.

Più sotto troverete le immagini in cui viene spiegato come effettuare i collegamenti delle interfacce con Arduino Uno e/o Mega.

7) PC Windows 7/8/10.

8) L' IDE di Arduino (ambiente di sviluppo per scrivere, modificare e memorizzare i programmi (sketch)) sull'Arduino stesso. Si scarica dal sito ufficiale di Arduino ed è gratuito.

9)Alimentatore 9V DC (input) 220V AC output con "jack Japan" (quelli tondi col foro centrale) . Li trovate facilmente in rete , basta digitare "Alimentatore per Arduino".

10) Modulo SD Card per Arduino per scheda SD e scheda SD per memorizzare lo stato dei servo e led. Acquistate la scheda con la quantità di memoria più bassa possibile. Infatti dovrà contenere un unico file di testo che occuperà un solo K-byte. Inutile sprecare SD più capienti. Quella che segue è l'immagine del modulo di lettura/scrittura per la SD card che ho utilizzato.

11) Lettore di schede di memoria USB, lettore di schede SD /micro SD , USB OTG ad adattatore USB 2.0 con connettore standard per PC e notebook smartphone e tablet con funzione OTG. Il lettore è necessario per formattare la scheda SD e successivamente leggere/scrivere la scheda SD sulla quale si memorizza il file con lo stato dei servo e led, durante l'uso con Arduino, ma anche per cancellare il file nel caso esso si corrompa. Ecco un'immagine del lettore da inserire nella porta USB del vostro PC o notebook o tablet

12) Sketch per testare il funzionamento dell'interfaccia e/o shield DCC con la vostra centrale. Collegando come indicato nei passaggi 1) 2) 3) 4) 5) l'interfaccia DCC al tracciato o centralina che sia , caricando lo sketch su Arduino , selezionate il menù Strumenti e scegliete Monitor Seriale. Quindi scegliete la velocità (baud rate) 9600 ed infine avviate lo sketch. Ora col vostro palmare selezionate la funzione gestione switch (nel palmare Lenz LH100 è abbinata al tasto funzione [F5], impostate un indirizzo qualsiasi e quindi selezionate lo stato che volete ,per LH100 si usano i tasti [+] per la posizione in tracciato deviato e [-] per la posizione in tracciato corretto. Sulla finestra del Monitor Seriale dell'IDE di Arduino verrà visualizzato un testo in funzione di quello che nello sketch 2_TEST_INTERFACCIA_DCC sketch . Potete testare lo sketch anche con Train Controller o software simili.

Ecco il listato:

/*

#include <DCC_Decoder.h>

#include <Wire.h>

#include <NmraDcc.h>

NmraDcc Dcc;


// definitions

#define DCC_PIN 2

#define BOARD_ADDRESS 5

#define PAIR_ADDRESS 1


void notifyDccAccState(uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)

{

Serial.println(F("Dentro notify "));

int pairAddress = (OutputAddr >> 1) + 1;

int outputInPair = OutputAddr & 0x01;

int i_dec;

i_dec = BoardAddr;

Serial.print("Indirizzo LH100=");

Serial.println(Addr,DEC);

}


void setup()

{

Serial.begin(9600);

Serial.println(F("Start sketch "));


Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);

Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0); // Serial.println("Interfaccia Dcc inizializzata ");

}


void loop()

{

Dcc.process();


}


Descrizione dell'interfaccia 16-Channel 12 bit PWM Servo Motor Driver I2C PCA9685.

Quando si vede questo chip si comprende subito che può essere un' ottima alternativa ai decoder dcc per deviatoi . Utilizzando solo 4 pin, si possono controllare 16 uscite PWM. È anche possibile concatenare fino 62 di queste schede per controllare fino a 992 uscite PWM.

Si tratta di un driver PWM controllato dal protocollo I2C con un orologio integrato. Ciò significa che, a differenza della famiglia TLC5940, non è necessario inviargli continuamente il segnale di collegamento del microcontrollore, il suo funzionamento è completamente autonomo.

Questa interfaccia può essere utilizzata anche per pilotare i LED con effetto fade (brillantezza crescente o decrescente sino allo spegnimento).

È compatibile 5V, quindi lo potete gestire con un microcontrollore con 3.3V e ancora tranquillamente usare fino alle uscite a 6V . Con Arduino non avrete alcun problema.

Avete sei piazzole per gestire l'indirizzo dell'interfaccia per poter cablare fino a 62 di queste su un unico bus I2C, per un totale di 992 uscite, che sono tanti servo o LED. Frequenza regolabile PWM fino a circa 1.6 KHz.

Risoluzione a 12 bit per ogni uscita, per i servi, che significa circa 4us di risoluzione alla velocità di aggiornamento di 60Hz. Uscita push-pull o open-drain configurabile. Pin di validazione di uscita (pin OE) per disattivare rapidamente tutte le uscite.

Morsettiera per ingresso di alimentazione .

Protezione contro l’inversione di polarità sull’ingresso della morsettiera

LED luminoso verde ( o altro colore a seconda del produttore)

3 connettori a pin in gruppi di 4 in modo che voi possiate collegare 16 servi in una sola volta (spine dei servo sono leggermente più ampia rispetto ai 2.54mm in modo da poterne impilare solo 4 l’uno accanto all’altro su un piedino da 2.54mm).

Concetto ‘a catena’ , per il collegamento di più servo.

Un posto per posizionare un grande condensatore sulla linea V + (in caso di necessità)

Resistenze in serie da 220 ohm su tutte le linee di uscita per proteggere, e per rendere semplice attivare/disattivare i LED .

Usa uno qualsiasi dei lati per collegare l'interfaccia ad Arduino e l'altro per un eventuale collegamento a catena con un'identica interfaccia (collegamento side-by-side) . Vi suggerisco di effettuare il primo collegamento della prima scheda ad Arduino come mostrato nelle figure successive e, per omogeneità, anche le successive tra loro.

Ecco i pin da usare per la connessione ad Arduino.

GND pin della a terra dell'alimentazione - Questo è il pin di messa a terra dell'alimentazione del e del segnale per il PCA9685, deve essere collegato all'omonimo pin di Arduino

VCC Questo è il pin logico per comunicare col PCA9685, collegare questo pin al pin +5V

V + VI SCONSIGLIO ASSOLUTAMENTE DI UTILIZZARLO.

Questo è un pin opzionale di alimentazione che fornirà energia distribuita ai servi. Se non lo usi per i servi puoi lasciarlo disconnesso. Non è usato affatto dal chip. È assolutamente raccomandabile alimentare la scheda dal connettore bipolare con viti di fissaggio dei fili V+ 5V e GND, collocato vicino al condensatore (in genere è di color verde) . Dovresti fornire 5-6 VDC se stai usando i servi. Se devi, puoi andare arrivare a 12V DC, ma NON connettere VCC a V + si potrebbe danneggiare la scheda!

Pin di controllo.

SCL - Pin di clock I2C, collegare alla linea di clock I2C dei microcontrollori. Può usare la logica 3V o 5V e ha un debole pullup a VCC

SDA - Pin dati I2C, collegare alla linea dati I2C dei microcontrollori. Può utilizzare la logica 3V o 5V e ha un pull-up debole a VCC

OE - Abilitazione uscita. Può essere utilizzato per disattivare rapidamente tutte le uscite. Quando questo pin è Basso tutti i pin sono abilitati , quando il pin è alto le uscite sono disabilitate . Di default è Basso, quindi è un pin opzionale!

Porte di uscita.

Ci sono 16 porte di uscita. Ogni porta ha 3 pin: V +, GND e l'uscita PWM. Ogni PWM funziona in modo completamente indipendente, ma devono avere tutti la stessa frequenza PWM. Essa può essere usata anche per gestire i Led realizzando l'effetto FADE - luminosità sfumata in accensione e spegnimento.

Cioè, per i LED probabilmente la frequenza di 1.0 KHz è raccomandabile, ma i servi hanno bisogno di 60 Hz - quindi non è possibile utilizzare una stessa scheda sia per i led che per i servo. Le PCA9685 sono configurate per i servi ma si possono utilizzare anche per per i LED! La corrente massima per pin è 25 mA. Ci sono resistori da 220 ohm in serie a ciascun pin PWM e la logica di uscita è la stessa di Vcc quindi questo è da tenerlo ben presente se si utilizzano queste interfacce per pilotare i LED.

Collegamenti tra Arduino Mega 2560 R3 e 16-Channel 12-Bit PWM/Servo.

Le schede Arduino Mega 2560 R3 hanno pin dedicati SDA e SCL dalla parte opposta alla porta opposta connettore USB e sono rispettivamente i Pin 20 e 21

INTERFACCIA PCA9685 ARDUINO MEGA 2560 R3

Vcc -> +5V

SDA -> SDA (pin 20 )

SCL -> SCL (pin 21)

Collegamenti tra Arduino UNO R3 e

PCA9685 16-Channel 12-Bit PWM/Servo.

INTERFACCIA PCA9685 ARDUINO MEGA 2560 R3

Vcc -> +5V

SDA -> A4

SCL -> A5

Alimentazione dei Servi, aggiunta di un condensatore e collegamento dei servi.

La maggior parte dei servi sono progettati per funzionare a circa 5 o 6v. E' da tenere presente che molti servi si muovono contemporaneamente, quelli potenti in particolare, avranno bisogno di molta corrente. Anche i micro servi assorbiranno diverse centinaia di mA quando sono in movimento. Alcuni servi con coppia elevata assorbono più di 1 A ciascuno sotto carico.

Buone scelte di alimentazione sono:

Alimentatore switching 5v 2A

Alimentatore switching 5v 3A

Alimentatore switching 5v 10A

Porta batteria 4xAA - 6v con celle alcaline. 4.8 v con batterie ricaricabili NiMH.

Pacchi batteria ricaricabili 4.8 o 6v da un negozio di hobby.

Ci sono due piazzole sul PCB per la saldatura dei terminali di un condensatore elettrolitico. In base al tuo utilizzo, potresti avere o no bisogno di un condensatore ( se usi i servo sicuramete si!). Se devi gestire numerosi servi da un alimentatore che eroga molta corrente quando i servi si muovono, scegli una capacità, per il condensatore,pari ad n * 100uF (micro Farad) dove:

n è il numero di servi. Per esempio è un buon punto di partenza 470uF(micro Farad) o più per più per 5 servi. Personalmente ho usato condensatori elettrolitici da 1000uF.

Dal momento che l'assorbimento di corrente è così dipendente dalla coppia che il motore del servo è in grado di fornire , non esiste un unico valore per il condensatore da suggerire.

Assicurarsi di allineare la spina con il cavo di messa a terra (solitamente nero o marrone) sui pin più esterni, quelli confinanti col bordo della scheda.

Ecco una foto del collegamento tra una shield 16-Channel 12-Bit PCA9685. La shield va sovrapposta all'Arduino Mega 2560 R3 e collegata al DCC (tracciato o centralina DCC). Gestisce 16 servo. Per una gestione di servo sino a 32 serve un'altra shield sovrapposta alla precedente da collegare anch'essa al DCC e così via per ulteriori servo. Non avendo a disposizione due shield (ho optato per le interfacce illustrate nelle figure precedenti), non so quanti ostacoli incontrino poi l'inserimento dei connettori dei servi, usando più shield.

N.B.: ricordarsi che la scheda dei primi 16 servo non va programmata , le successive si e l'indirizzo va programmato come spiegato più avanti.

Questa sopra è la figura della connessione di un servo ad una shield PCA9685

La figura sotto mostra come collegare un LED monocromatico alla PCA9685. Ricordo anche che con essa è possibile realizzare l'effetto "fade" , cioè lo spegnimento ed accensione con luminosità progressiva. Utilissima per la gestione dei segnali luminosi di cui mi occuperò in un'altra occasione.

Interfacce concatenate.

Più interfacce (fino a 62) possono essere concatenate per controllare ancora più servi. Il cablaggio è semplice, ed è come collegare un cavo parallelo a 6 pin da una scheda all'altra. Ecco lo schema.

Indirizzamento dell'interfaccia.

Ad ogni interfaccia della catena deve essere assegnato un indirizzo univoco. Questo viene fatto con i ponticelli di indirizzo in alto a destra sul bordo dell'interfaccia. L'indirizzo di base I2C per ciascuna scheda è 0x40. L'indirizzo che si programma con i jumper sono aggiunti all'indirizzo I2C di base. Per programmare l'offset dell'indirizzo, utilizzare una goccia di saldante (stagno) per colmare il jumper di indirizzo corrispondente per ogni "1" binario dell'indirizzo da assegnare. L'immagine seguente spiega bene come procedere per la ponticellatura dei jumper.

Come crimpare i connettori dei servo per collegarli ai deviatoi posti a distanza .

Certamente non si può pensare che le PCA9685 siano vicine a tutti i servo e/o led. Sarà indispensabile provvedere a prolunghe adeguate alfine di arrivare nei punti sottoplancia dove sono collocati i deviatoi.Per questo vi rimando al link seguente che è molto chiaro ed esaustivo.


https://www.youtube.com/watch?v=JUiWTEnvMyM

Per prolungare i cavi elettrici ecco dove trovare i connettori:

https://www.banggood.com/10-Sets-Futaba-Plug-Servo-Plug-Terminal-Gilt-Male-and-Terminal-p-933953.html?rmmds=detail-left-hotproducts__3&cur_warehouse=CN

e per la crimpatrice:

https://www.banggood.com/Multifunctional-Ratchet-Crimping-Tool-Pin-Plug-Spring-Terminals-Clamp-p-1070064.html?rmmds=myorder&cur_warehouse=CN

Sketch per il test di operatività della PCA9685 e dei servo collegati (max 16).

Questo sketch permette di verificare se la scheda PCA9685 ha tutti i 16 canali funzionanti. E' lo schetch che accompagna i driver per la gestione delle schede PWM offerto dalla Adafruit e lo trovate nell'IDE di Arduino in Esempi/Adafruit PWM Servo Driver libary/servo

L'ho leggermente modificato portando il controllo dei canali da 8 a 16 e accorciando gli intervalli dei tempi di ritardo di intervento tra un servo ed il successivo.

La scheda va testata PRIMA di modificare l'indirizzo, come spiegato sopra. Se volete testare una scheda con indirizzo già modificato allora dovrete variare anche la riga :

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

nella quale a 0x40 sostituirete 0x41 oppure 0x42 etc etc

Ecco il codice dello sketch.

/*

Test funzionalità dei 16 canali della PCA9685

*/

#include <Wire.h>

#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

uint8_t servonum = 0;

void setup() {

Serial.begin(9600);

Serial.println("16 channel Servo test!");

pwm.begin();

pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates

delay(10);

}

void setServoPulse(uint8_t n, double pulse) {

double pulselength;

pulselength = 1000000; // 1,000,000 us per second

pulselength /= 60; // 60 Hz

Serial.print(pulselength); Serial.println(" us per period");

pulselength /= 4096; // 12 bits of resolution

Serial.print(pulselength); Serial.println(" us per bit");

pulse *= 1000000; // convert to us

pulse /= pulselength;

Serial.println(pulse);

pwm.setPWM(n, 0, pulse);

}

void loop() {

Serial.println(servonum);

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {

pwm.setPWM(servonum, 0, pulselen);

}

delay(10);

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {

pwm.setPWM(servonum, 0, pulselen);

}

delay(10);

servonum ++;

if (servonum > 15) servonum = 0;

}

Sketch per la gestione dei 64 servi.

/*

Designed by Gianni Barbotti (ITALY) July 15 2018

Aggiornato il 16 Agosto 2018 per la gestione Led quadro sinottico.

revisione 04 Maggio 2019

revisione del 21 Luglio 2019

revisione del 13 Agosto2019


In questo sketch gli indirizzi dei deviatoi devono essere compresi tra l' 1 al il 64.

Per modificare il loro range modificare le due variabili:

int start_address = 0; int start_address = mio indirizzo iniziale;

int end_address = 63; int end_address = mio indirizzo finale;

Con queste impostazioni i servo ruotano il loro asse di circa 60°- 70° con una velocità

non eccessivamente lenta.

Per modificare la velocità di rotazione, modificare il parametro

#define ritardo 5

Valori maggiori rallentano il movimento, valori minori lo velocizzano

Se volete decrementare/incrementare il numero dei servi gestiti dovrete apportare allo

sketch le opportune modifiche. Nel caso di un decremento rispetto

al numero previsto (64), potete anche lasciare tutto così come'è.

Se volete gestire oltre 64 servi


Se nel vostro plastico avete già installato decodificatori DCC

e di conseguenza avete una serie di indirizzi già occupate, ma volete

ampliare il vostro plastico aggiungendo altri deviatoi od elementi

che possono essere attivato coi servo (passaggi a livello),allora:

1) ponticellate (cioè unite con una saldatura la coppia delle piazzole)

della prima scheda 16-Channell 12-bit PCA096 che userete

in modo tale che il suo indirizzo fisico sia coerente con lo schema sotto indicato.

Posto che il simbolo "*" significa che la coppia di piazzole sono ponticellate,

il simbolo "o" significa che la coppia di piazzole non sono poncicellate.

La scheda PCA9685 arriva con questa configurazione base


0 0 0 0 0 0

0 0 0 0 0 0

Se, per esempio, l'ultimo indirizzo DCC dei dispositivi stazionari come

deviatoi, segnali luminosi, quindi esclusi gli indirizzi DCC

assegnati alle locomotive, è 51 allora il primo indirizzo successivo a 51 è

ovviamente 52.

Le schede PCA9685 gestiscono gli indirizzi in questo modo:

dal 1° al 16° servo, scheda PCA9685 con configurazione base, cioè:

0 0 0 0 0 0

0 0 0 0 0 0


con istruzione

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40);

dal 17° al 32° servo, scheda PCA9685 con configurazione

0 0 0 0 0 *

0 0 0 0 0 *

con istruzione:

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41);

dal 33° al 48° servo, scheda PCA9685 con configurazione


0 0 0 0 * 0

0 0 0 0 * 0

con istruzione:

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42);


dal 49° al 64° servo, scheda PCA9685 con configurazione


0 0 0 0 * *

0 0 0 0 * *

con istruzione:

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);


dal 65° al 80° servo, scheda PCA9685 con configurazione

0 0 0 * 0 0

0 0 0 * 0 0

con istruzione:

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire, 0x44);


dal 81° al 96° servo, scheda PCA9685 con configurazione

0 0 0 * 0 *

0 0 0 * 0 *

con istruzione:

Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire, 0x45);


dal 97° al 112° servo, scheda PCA9685 con configurazione

0 0 0 * * *

0 0 0 * * *

con istruzione:

Adafruit_PWMServoDriver pwm7 = Adafruit_PWMServoDriver(&Wire, 0x46);

e così via. Pertanto dovrete scegliere la confgurazione:

dal 49° al 64° servo, scheda PCA9685 con configurazione


0 0 0 0 * *

0 0 0 0 * *

con istruzione:

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);

Guardando la PCA965 in modo tale che i pin dei collegamenti coi

servi siano a sinistra, dovete collegare il primo servo alla

quarta terna di pin dall'alto verso il basso.

Le figure, descrizioni e didascalie che trovate sul sito web:


https://sites.google.com/view/dcc-arduino-micro-servo/home

sono esplicative.


Inoltre dovrete sostituire la riga: int start_address = 0;

con la riga int start_address = N;

con N = (Maggiore degli indirizzi gestiti con i decodificatori dei deviatoi e/o segnali) + 1.


Per esempio avete impegnati gli indirizzi per i deviatoi e/o segnali luminosi,

sino al numero 51, dovrete:

1) scrivere:

int start_address = 52

anzichè

int_star_address = 0.


2) scrivere:

int start_address = 52

anzichè

int_star_address = 0.




Se si vogliono gestire un numero superiore di servo a 64,supponendo che il numero sia S

sostituite l'istruzione :

int end_address = 63

con la :

int end_address = (S - 1)


e l'istruzione :

#define i_max_servo 64

con la :

#define i_max_servo S

Dichiarate inoltre le istruzioni:

Adafruit_PWMServoDriver pwmNN = Adafruit_PWMServoDriver(&Wire, 0xYY);

con NN numero progressivo di scheda ed YY indirizzo scheda PCA9685.

seguite questa operazione su tutte le dichiarazioni

dello sketch (v. all'interno della void setup() e della CHEC_SERVO)

*/



#include <NmraDcc.h>

NmraDcc Dcc;

//#include <DCC_Decoder.h>

#include <Adafruit_PWMServoDriver.h>

// you can also call it with a different address you want

//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

// you can also call it with a different address and I2C interface

//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(&Wire, 0x40);

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40);

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41);

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42);

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);

// you can also call it with a different address you want

//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);

// you can also call it with a different address and I2C interface

//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(&Wire, 0x40);


#define DCC_PIN 2 // definizione Pin comunicazione Arduino con interfaccia DCC

#define BOARD_ADDRESS 5

#define PAIR_ADDRESS 1

//#define kDCC_INTERRUPT 0

//#define DCC_decoder


int start_address = 0; // Non modificare

int end_address = 64; // Non modificare

#define ritardo 5

#define L_ritardo 10

#define min_servo 1 // Non modificare

#define max_servo 64 // Non modificare

#define SERVOMIN 150 // Valore iniziale rotazione.Modificare a seconda dell'ampiezza della rotazione

// desiderata per l'asse del servo

#define SERVOMAX 310 // Valore finale rotazione - Modificare a seconda dell'ampiezza della rotazione

// desiderata per l'asse del servo

#define i_max_servo 64

char status_servo[i_max_servo];

#define num_ch_srv 16

#define freq_srv 64 // Frequenza funzionamento del servo - NON MODIFICARE -

int servonum; // variabile in cui si memorizza il numero progressivo del servo

// rispetto alla scheda che lo gestisce (da 0 a 15)

int servo; // è il numero progressivo del servo del range degli indirizzi e

// quindi è compreso tra:

// start_address ed end_address

int scheda; // numero della scheda calcolato in base all'indirizzo del deviatoio

void notifyDccAccState(uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)

{

int pairAddress = (OutputAddr >> 1) + 1;

int outputInPair = OutputAddr & 0x01;

int i_dec;

int indirizzo;

indirizzo = Addr;

i_dec = BoardAddr;

servo = abs(Addr - start_address);

if (indirizzo > end_address)

{

Serial.print(F(" Indirizzo selezionato = "));

Serial.println(indirizzo, DEC);

Serial.println(F("Indirizzo non accettato perchè supera l'indirizzo massimo che è 64"));

}

else

{

performAction(Addr, outputInPair);

}

}

void performAction(int Addr, int outputInPair)

{

int stato;

stato = outputInPair;

int scheda;

int indirizzo;

indirizzo = Addr;

scheda = ((indirizzo - 1) / num_ch_srv) +1 ;

servonum = (servo - num_ch_srv * (scheda - 1)) -1 ;

Serial.print(F(" scheda = "));

Serial.print(scheda, DEC);

Serial.print(F(" servo = "));

Serial.print(servo, DEC);

Serial.print(F(" N.ro servo interno alla SK = "));

Serial.println(servonum, DEC);

Serial.print(F("stato = "));

Serial.print(stato,DEC);

if (outputInPair == 0)

{

Serial.println(F(" ==> deviatoio su tracciato corretto"));

}


if (outputInPair == 1)

{

Serial.println(F(" ==> deviatoio su tracciato deviato"));

}

if (outputInPair == 1)

{

switch (scheda)

{

case 1:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm1.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;


case 2:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm2.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;


case 3:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm3.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 4:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm4.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

/*

case 5:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm5.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 6:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm6.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

*/

}

}

if (outputInPair == 0)

{

switch (scheda)

{

case 1:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm1.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;


case 2:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm2.setPWM(servonum, 0, pulselen);

delay(ritardo);

}


break;


case 3:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm3.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 4:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm4.setPWM(servonum, 0, pulselen);

delay(ritardo);

}


break;


/*

case 5:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm5.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 6:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm6.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

*/


}

}

}


void setup()

{

Serial.begin(9600);

Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);

Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0);

pwm1.begin();

pwm1.setPWMFreq(freq_srv);

pwm2.begin();

pwm2.setPWMFreq(freq_srv);

pwm3.begin();

pwm3.setPWMFreq(freq_srv);

pwm4.begin();

pwm4.setPWMFreq(freq_srv);

}



void loop()

{


// DCC.loop();

Dcc.process();

}

Collegamento modulo SD con Arduino e connessione modulo lettura/scrittura su scheda SD

Poiché il plastico sarà "spento" ed "acceso" più e più volte, ogni volta che lo si spegne e riaccende, i servo,all'avvio, potrebbero tornano in posizione di "riposo" e si perde lo stato al quale erano impostati. Sicuramente i Led (segnali luminosi e posizione sul quadro sinottico dei deviatoi lo saranno. Perciò è molto utile conservare la posizione finale dei deviatoi al momento dello spegnimento memorizzando il loro stato su un supporto di memoria. Ho usato un modulo che contiene una scheda SD sulla quale poter effettuare questa operazione. Ecco come connettere i pin del modulo r/w scheda SD con Arduino. Alla loro gestione ci pensa il software che gestisce l'intero progetto e che è pubblicato più avanti. Sulle due ultime righe della figura sono indicati a quali input delle schede Arduino vanno connessi i pin +5V, GND, 3.3V, CS etc etc.

TABELLA DELLE SCHEDE PCA9685 e TLC5947 NECESSARIE IN FUNZIONE DEI SERVI E LED UTILIZZATI.

FOGLIO 2.xls

Sketch per il test di funzionalità della scheda SD

/*

SD card read/write


This example shows how to read and write data to and from an SD card file

The circuit:

* SD card attached to SPI bus as follows:

** MOSI - pin 11 - ARDUINO UNO

** MISO - pin 12 - ARDUINO UNO

** CLK - pin 13 - ARDUINO UNO

** CS - pin digitali da 3 a 10 (for MKRZero SD: SDCARD_SS_PIN)

* scegliendo il pin 10 si hanno su ARDUINO UNO tutti i cavetti jumper

* in sequenza : 10,11,12,13,GND accanto al pin 13.

* Quest'ultima connessione è importane, perchè

* se si utilizza il GND accanto al pin +5V la scheda

* SD non è accessibile!!!

* CS - PIN 53 PER MEGA 2560


created Nov 2010

by David A. Mellis

modified 9 Apr 2012

by Tom Igoe


This example code is in the public domain.


*/


#include <SPI.h>

#include <SD.h>


File myFile;


void setup() {

// Open serial communications and wait for port to open:

Serial.begin(9600);

while (!Serial) {

; // wait for serial port to connect. Needed for native USB port only

}



Serial.print("Initializing SD card...");


if (!SD.begin(53)) {

Serial.println("initialization failed!");

while (1);

}

Serial.println("initialization done.");


// open the file. note that only one file can be open at a time,

// so you have to close this one before opening another.

myFile = SD.open("test.txt", FILE_WRITE);


// if the file opened okay, write to it:

if (myFile) {

Serial.print("Scrivo il file test.txt...");

myFile.println("testing 11, 12, 13.");

// close the file:

myFile.close();

Serial.println("Chiusura file eseguita il file.");

} else {

// if the file didn't open, print an error:

Serial.println("Non posso aprire il file test.txt");

}


// re-open the file for reading:

myFile = SD.open("test.txt");

if (myFile) {

Serial.println("Ho riaperto il file test.txt:");


// read from the file until there's nothing else in it:

while (myFile.available()) {

Serial.write(myFile.read());

}

// close the file:

myFile.close();

Serial.println("RI-chiusura file test.txt eseguita il file.");

} else {

// if the file didn't open, print an error:

Serial.println("errore nella Riapertura file test.txt");

}

}


void loop() {

// nothing happens after setup

}

Ed ecco lo sketch della gestione dei deviatoi mossi dai servi con indirizzo da 1 a 64 e memorizzazione della loro posizione.

N.B: Prima di avviare lo sketch DOVETE inserire nella scheda SD un file che editerete col Blocco note di Windows ,quindi un file di testo con estensione .txt, contenete una stringa di 64 cifre tutte uguali a due come la seguente:

222222222222222222 ... 2222 (64 cifre 2 di fila)

/*

Designed by Gianni Barbotti (ITALY) 15 Luglio 2018

Aggiornato il 16 Agosto 2018 per la gestione Led quadro sinottico.

revisione 04 Maggio 2019

revisione del 21 Luglio 2019

revisione del 13 Agosto 2019

Lo sketch precedente gestisce il movimento dei servo, ma non memorizzando il loro stato,

quando un servo si attiva non sempre si sistema subito nella posizione desiderata.

Se gli spostamento sono alternati da tracciato corretto a deviato o viceversa,

il "difetto" non si manifesta.

Provate però ad impostare il deviatoio in posizione corretta e poi,

a ciclo concluso, ripetete il posizionamento in tracciato corretto.

Il servo si comporterà come descritto sopra. Poichè questo è molto "spiacevole",

soprattutto quando il tutto viene spento e si torna magari

il giorno dopo od anche più, non sapete nulla dei deviatoi che non

siano a vista e/o distanti da dove operate. Ecco allora si rende

necessario memorizzare le posizioni in modo tale che ripartendo

se ne possa tener conto.

Questo sketch gestisce il movimento dei servo e memorizzando il loro stato,

quando col palmare o via software si vuol far muovere un deviatoio

in una posizione nella quale già si trova,questo rimane nella stessa

posizione.

Lo sketch accetta indirizzi da 1 a 64 (0-63).


OSSERVAZIONE:

Ho scoperto che collegando il solo polo contrassegnato con 'K'

del decoder ai poli 'K' della LZV100 e della shield DCC o scheda

DCC per Arduino,potete gestire ugualmente i deviatoi connessi

al decoder stazionario.

Fate voi, anche se fino a poco tempo fa collegavo entrambi i poli "J"

e "K" del segnale DCC.

Se nel vostro plastico avete già installato decodificatori DCC

e di conseguenza avete una serie di indirizzi già occupati, ma volete

ampliare il vostro plastico aggiungendo altri deviatoi od elementi

che possono essere attivati coi servo (passaggi a livello),allora

dovrete utilizzare le schede PCA9685 attribuendo loro il giusto indirizzo.

Le schede PCA9685 hanno due file parallele di sei quadratini con superficie

metallica, dette piazzole.

Dovrete quindi ponticellare, cioè unire con una saldatura

la coppia delle piazzole, delle schede 16-Channell 12-bit PCA9685

che userete in modo tale che il suo indirizzo fisico sia coerente con

lo schema sotto indicato.


Assumiamo che che il simbolo "*" significa che la piazzola è

ponticellata e che il simbolo "o" significa che la piazzola

non è ponticellata.

La scheda PCA9685 arriva con nessuna delle piazzole

ponticellata e quindi in questa configurazione base


0 0 0 0 0 0

0 0 0 0 0 0


Le schede PCA9685 gestiscono gli indirizzi in questo modo:

dal 1° al 16° servo, scheda PCA9685 con configurazione base, cioè:

0 0 0 0 0 0

0 0 0 0 0 0


associata all'istruzione

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40);

dal 17° al 32° servo, scheda PCA9685 con configurazione

0 0 0 0 0 *

0 0 0 0 0 *

associata all'istruzione:

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41);

dal 33° al 48° servo, scheda PCA9685 con configurazione


0 0 0 0 * 0

0 0 0 0 * 0

associata all'istruzione:

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42);


dal 49° al 64° servo, scheda PCA9685 con configurazione


0 0 0 0 * *

0 0 0 0 * *

associata all'istruzione:

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);


dal 65° all' 80° servo, scheda PCA9685 con configurazione

0 0 0 * 0 0

0 0 0 * 0 0

associata all'istruzione:

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire, 0x44);


dal 81° al 96° servo, scheda PCA9685 con configurazione

0 0 0 * 0 *

0 0 0 * 0 *

associata all'istruzione:

Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire, 0x45);


dal 97° al 112° servo, scheda PCA9685 con configurazione

0 0 0 * * *

0 0 0 * * *

associata all'istruzione:

Adafruit_PWMServoDriver pwm7 = Adafruit_PWMServoDriver(&Wire, 0x46);

e così via.


Se, per esempio, l'ultimo indirizzo DCC dei dispositivi stazionari come

deviatoi, segnali luminosi, quindi esclusi gli indirizzi DCC

assegnati alle locomotive, è 51 allora il primo indirizzo libero

successivo è ovviamente 52. Pertanto questa è la configurazione

che dovrete assegnare:

dal 49° al 64° servo, scheda PCA9685 con configurazione


0 0 0 0 * *

0 0 0 0 * *

associata all'istruzione:

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);


Inoltre nello sketch sostituite l'istruzione:

int start_address = 52

con la:

int_star_address = 0.

e sostituite l'istruzione:



int_end_address = ultimo indirizzo che stabilisco di gestire.

ovvero se voglio gestire oltre ai deviatoi coi decodificatori anche

deviatoi coi servo, diciamo altri 20 elementi, allora :


sostituite l'istruzione:


int end_address = 63;

con la:

int end_address = 71;

occupando così i successivi 20 indirizzi DCC.

In tal caso vi servirà una successiva scheda PCA9685

con questa configurazione :


0 0 0 * 0 0

0 0 0 * 0 0

associata all'istruzione:

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire, 0x44);


Inoltre dovrete sostituire la riga: int start_address = 0;

con la riga int start_address = N;

con N = (Maggiore degli indirizzi gestiti con i decodificatori dei deviatoi e/o segnali) + 1.


Per esempio avete impegnati gli indirizzi per i deviatoi e/o segnali luminosi,

sino al numero 51, dovrete:

1) scrivere:

int start_address = 52

anzichè

int_star_address = 0.


2) scrivere:

int start_address = 52

anzichè

int_star_address = 0.




Se si vogliono gestire un numero superiore di servo a 64,supponendo che il numero sia S

sostituite l'istruzione :

int end_address = 63

con la :

int end_address = (S - 1)


e l'istruzione :

#define i_max_servo 64

con la :

#define i_max_servo S


Dichiarate inoltre le istruzioni:

Adafruit_PWMServoDriver pwmNN = Adafruit_PWMServoDriver(&Wire, 0xYY);

con NN numero progressivo di scheda ed YY indirizzo scheda PCA9685.

seguite questa operazione su tutte le dichiarazioni

dello sketch (v. all'interno della void setup() e della CHEC_SERVO)

*/

// *** Definizione libreria NMRA per DCC

#include <NmraDcc.h>

NmraDcc Dcc;

#define DCC_PIN 2 // definizione Pin comunicazione Arduino con interfaccia DCC

#define BOARD_ADDRESS 5

#define PAIR_ADDRESS 1


// Definizione Libreria e parametri scheda SD

#include <SD.h>

/* int CS_pin = ; // CS_pin = 3 o 4 o 10 con Arduino UNO;

//CS_pin = 53 con Arduino MEGA 2560;

*/

int CS_pin = 53;

int j;

char logfile[] = {"SERVI.txt"};

File file;

// **** Librerie e PARAMETRI RELATIVI AI SERVO *****

#include <Adafruit_PWMServoDriver.h> // Libreria per poter utilizzare i micro servo con Arduino

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire,0x40); // to manage servos from 1° to the 16°(addresses 0-15)

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire,0x41); // to manage servos from 1° to the 16°(addresses 16-31)

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire,0x42); // to manage servos from 1° to the 16°(addresses 32-47)

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire,0x43); // to manage servos from 1° to the 16°(addresses 48-63)

// Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire,0x44); // to manage servos from 1° to the 16°(addresses 64-79)

// Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire,0x45); // to manage servos from 1° to the 16°(addresses 80-95)

/* Per ogni scheda Adafruit16-Channel aggiuntiva inserire la riga:

Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);

dove N è il numero progressivo della scheda aggiunta.

For each additional Adafruit16-Channel add the statement:

Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);

where N is the progressive of the Adafruit breakout added

*/


int start_address = 0; // Non modificare

int end_address = 63; // Non modificare

#define ritardo 5

#define L_ritardo 10

#define min_servo 1 // Non modificare

#define max_servo 64 // Non modificare

#define SERVOMIN 150 // Valore iniziale rotazione.Modificare a seconda dell'ampiezza della rotazione

// desiderata per l'asse del servo

#define SERVOMAX 310 // Valore finale rotazione - Modificare a seconda dell'ampiezza della rotazione

// desiderata per l'asse del servo

#define i_max_servo 64


char status_servo[i_max_servo];

#define num_ch_srv 16

#define freq_srv 60 // Frequenza funzionamento del servo - NON MODIFICARE -

int servonum; // variabile in cui si memorizza il numero progressivo del servo

// rispetto alla scheda che lo gestisce (da 0 a 15)

int servo; // è il numero progressivo del servo del range degli indirizzi e

// quindi è compreso tra:

// start_address ed end_address

int scheda; // numero della scheda calcolato in base all'indirizzo del deviatoio


void notifyDccAccState(uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)

{


int pairAddress = (OutputAddr >> 1) + 1;

int outputInPair = OutputAddr & 0x01;

servo = abs(Addr - start_address );

if (Addr > max_servo)

{

Serial.print(F(" Indirizzo selezionato = "));

Serial.println(Addr, DEC);

Serial.println(F("Indirizzo non accettato perchè supera l'indirizzo massimo che è 64"));

}

else

{

performAction(Addr, outputInPair);

}

// performAction(Addr, outputInPair);

remove_file();

create_servi(logfile);

open2File(logfile);

while (file.available())

{

readLine();

}

closeFile();

update_status(logfile);

}

void performAction(int Addr, int outputInPair)

{

Serial.println(F("perform action"));

int scheda;

scheda = ((Addr - 1) / num_ch_srv) + 1;

servonum = (Addr -1) - num_ch_srv * (scheda -1);

Serial.print(F("Indirizzo DCC scelto = "));

Serial.print(Addr, DEC);

Serial.print(F(" scheda= "));

Serial.print(scheda, DEC);

// Serial.print(F(" servo = "));

// Serial.print(servo, DEC);

Serial.print(F(" servonum= "));

Serial.print(servonum, DEC);

Serial.print(F(" outputInPair= "));

Serial.println(outputInPair);


if (outputInPair == 1) // indirizzo servo nel range controllo schede

{

if ((min_servo <= Addr <= max_servo) && (outputInPair == 1)) // indirizzo servo nel range controllo schede

// con selezione "SPOSTA AGHI IN TRACCIATO DEVIATO"

{

if ((status_servo[servo] == char(48)) || (status_servo[servo] == char(50))) // il servo è in posizione [-] tracciato corretto oppure mai attivato

// va ACCESO il LED ROSSO e SPENTO il LED VERDE


{

status_servo[servo] = char(49);

status_servo[servo] = char(49);

switch (scheda)

{

case 1:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm1.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 2:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm2.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 3:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm3.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 4:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm4.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

/*

case 5:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm5.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 6:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm6.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

*/

}

}

}

}


if ((min_servo <= Addr <= max_servo) && (outputInPair == 0))

{

if (outputInPair == 0)

{

if ((status_servo[servo] == char(49)) || (status_servo[servo] == char(50))) // il servo è in posizione [+] oppure mai attivato

{

status_servo[servo] = char(48);

switch (scheda)

{

case 1:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm1.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

Serial.println(F("Esco da Case 1 "));

break;

case 2:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm2.setPWM(servonum, 0, pulselen);

delay(ritardo);

}


break;

case 3:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm3.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 4:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm4.setPWM(servonum, 0, pulselen);

delay(ritardo);

}


break;

/*

case 5:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm5.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 6:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm6.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

*/

}

}

}

}

}


void setup()

{

Serial.begin(9600);

pinMode(CS_pin, OUTPUT);

initializeSD();

openFile(logfile);

while (file.available())

{

readLine();

}

closeFile();

Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);

Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0);

pwm1.begin();

pwm1.setPWMFreq(freq_srv);

pwm2.begin();

pwm2.setPWMFreq(freq_srv);

pwm3.begin();

pwm3.setPWMFreq(freq_srv);

pwm4.begin();

pwm4.setPWMFreq(freq_srv);

/*

pwm5.begin();

pwm5.setPWMFreq(freq_srv);

pwm6.begin();

pwm6.setPWMFreq(freq_srv);

*/

CHECK_SERVO();

}


void loop()

{

Dcc.process();

}


String readLine()

{

int j;

j = start_address;

String received = "";

char ch;

while (file.available())

{

ch = file.read();

if (ch == '\n')

{

return String(received);

}

else

{

status_servo[j] = ch;

received += ch;

j++;

}

}

return "";

}


void initializeSD()

{


if (SD.begin())

{

}

else

{

Serial.println(F("SD card non disponibile. USCITA!!"));

return;

}

}


void remove_file()

{

if (SD.remove("SERVI.txt"))

{

}

else

{

Serial.println(F("file SERVI.txt NON eliminato. NON ESISTE! USCITA!!!"));

return;

}

}


int openFile(char filename[])

{

file = SD.open(filename);

if (file)

{

return 1;

}

else

{

default_status(logfile);

return 0;

}


}


int open2File(char filename[])

{

file = SD.open(logfile);

if (file)

{

for (j = start_address; j <= end_address; j++)

{

file.print(status_servo[j]);

}

return 1;

}

else

{

Serial.println(F(" File SERVI.txt inesistente! USCITA!!!!"));

return 0;

}

}



int default_status(char filename[])

{

file = SD.open("SERVI.txt", FILE_WRITE);

if (file)

{


for (j = start_address; j <= end_address; j++)

{

file.print(char(50));

status_servo[j] = char(50);

}

file.close();

}

else

{

Serial.println(F("Non è stato possibile scrivere il file SERVI.txt! USCITA!!"));


return;

}

}

int CHECK_SERVO()

{

// j = start_address;

// do

for (j = start_address; j <= end_address; j++)

{

scheda = (j / num_ch_srv) + 1;

servonum = j - (num_ch_srv * (scheda - 1));

if (status_servo[j] == char(48))

{

switch (scheda)

{

case 1:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm1.setPWM(servonum, 0, pulselen);

}


break;

case 2:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm2.setPWM(servonum, 0, pulselen);

}

break;

case 3:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm3.setPWM(servonum, 0, pulselen);

}

break;

case 4:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm4.setPWM(servonum, 0, pulselen);

}

break;

/*

case 5:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm5.setPWM(servonum, 0, pulselen);

}

break;

case 6:

for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)

{

pwm6.setPWM(servonum, 0, pulselen);

}

break;

*/

}

}


if (status_servo[j] == char(49))

{


switch (scheda)

{

case 1:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm1.setPWM(servonum, 0, pulselen);

}


break;

case 2:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm2.setPWM(servonum, 0, pulselen);

}

break;

case 3:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm3.setPWM(servonum, 0, pulselen);

}

break;

case 4:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm4.setPWM(servonum, 0, pulselen);

}

break;

/*

case 5:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm5.setPWM(servonum, 0, pulselen);

}

break;

case 6:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm6.setPWM(servonum, 0, pulselen);

}

break;

*/

}

}

} // CHIUDE IL CICLO FOR

// j++;

// while (j < end_address);

}

int create_servi(char filename[])

{

file = SD.open("SERVI.txt", FILE_WRITE);

if (file)

{

file.close();

}

else

{

Serial.println(F("Non è stato possibile creare il file SERVI.txt! USCITA!!"));

return;

}

}


int update_status(char filename[])

{

file = SD.open("SERVI.txt", FILE_WRITE);

if (file)

{

j = start_address;

for (j = start_address; j <= end_address; j++)

{

file.print(status_servo[j]);

}

file.close();

}

else

{

Serial.println(F("Non è stato possibile AGGIORNARE il file SERVI.txt! USCITA!!"));

return;

}

}

void closeFile()

{

if (file)

{

file.close();

}

}

Parte II

Quadro sinottico dello stato dei deviatoi indicato da Led

In questa seconda parte affrontiamo l'argomento della gestione di led monocromatici, utilizzando l'interfaccia :

24-Channel 12-bit PWM LED Driver - SPI Interface – TLC5947

Il tipo di quadro sinottico che si propone non prevede pulsanti od interruttori che modifichino la posizione dei deviatoi gestiti coi servo o lo stato dei led. Ecco perchè.

Supponiamo di affidare ad un pulsante o ad un interruttore a leva o basculante la variazione della posizione di in deviatoio. Lo sketch può benissimo contenere istruzioni che interpretino il movimento della leva dell'interruttore come cambio della posizione del deviatoio. Gli aghi si spostano, i led associati al cambio posizione degli aghi cambiano colore e … siamo a posto?

Ora supponiamo di operare sullo stesso deviatoio da Train Controller o direttamente col palmare: gli aghi si spostano, i colori dei led associati cambiano, ma la leva dell'interruttore chi la muove dalla parte opposta? Questo spostamento dovrebbe essere effettuato manualmente, ma allora che senso ha comandare il cambiamento di stato da PC o col palmare se poi devo agire manualmente sulla leva dell'interruttore?

Ecco perchè mi sono limitato ad una gestione minimale ed automatizzata di quello che, forse impropriamente, ho chiamato quadro sinottico. In effetti è molto più appropriato definirlo

Schema grafico dello stato dei deviatoi

Lo schema si basa sul criterio di associare ad ogni ramo di un deviatoio a due vie una coppia di led , verde e rosso ma i colori sono arbitrari, col significato che il led di colore verde acceso su di un ramo significa ramo percorribile, viceversa ramo non percorribile ed in tal caso si avrà il led rosso acceso.

Questo schema grafico di controllo mi sembra molto utile per il controllo della posizione dei deviatoi ovunque, ma in particolare nelle stazioni nascoste ed in tutti quei punti di non facile accesso visivo diretto.


DESCRIZIONE Adafruit 24 channel 12 bit.

Se volete controllare 24 led (monocromatici) in modalità PWM questa è la scheda adatta. Il chip driver PWM TLC5947 può controllare 24 canali separati PWM a 12 bit.

Per inviare i dati sono necessari solo tre pin "SPI" (DIN, CLK,LAT). La scheda è concatenabile con altre finché la potenza dell'alimentatore soddisfa le esigenze delle schede connesse.

Ciascuno dei 24 output è a corrente costante e scarico aperto. La scheda è alimentata con un'alimentazione al V+ fino a 30V (segue lo schema di alimentazione esterna).

Se si desidera pilotare qualcosa che richiede un ingresso digitale, è necessario utilizzare una resistenza di pullup dal pin dell'unità al livello logico per creare la forma d'onda completa. Un resistore viene utilizzato per impostare la corrente per ciascuna uscita, la corrente costante indica che la luminosità del LED non varia in caso di caduta dell'alimentazione. Nella scheda è implementato un resistore da 3,3 K per circa 15 mA, ma è possibile saldare un resistore thru-hole su di esso se si desidera modificare tale valore. Controllare la scheda tecnica TLC5947 per dettagli sui valori resistenza-corrente.

La connessione tra la scheda ed Arduino UNO R3 è la seguente:

TLC5947 Arduino UNO R3

DIN -> Digital 4

CLK -> Digital 5

LAT -> Digital 6

GND → GND

V+ -> Vin

Nell'immagine sottostante V + collegato al pin Vin di Arduino. Questo alimenterà la scheda TCL5947 e il LED direttamente dall'alimentazione fornita ad Arduino. Se Arduino è connesso tramite porta USB al PC esso potrà gestire pochi led. Consiglio di non caricare la scheda TCL5947 con troppi Led senza avere un buon alimentatore (>= 3A e >= 5V) connesso ad Arduino.

La scheda TLC5947 può accettare un V + da 5v a +30v. Tensioni più elevate, entro questo range, consentono di pilotare più LED in serie da ciascun canale.

Personalmente ho connesso alla scheda un alimentatore a V+ con 26 V ed ho notato un notevole surriscaldamento. Verificherò di nuovo.

Il progetto prevede l'utilizzo contemporaneo di non pochi led , saranno concatenate diverse schede TLC5947 e potrebbe essere raccomandabile, anche se l'assorbimento di corrente dei led è modesto, alimentare le schede con un alimentatore esterno.

Se si decide di aver bisogno di un alimentatore esterno per i LED, allora procedete come indicato qui di seguito:


1) Rimuovere la connessione tra V + della TLC5947 e VIN di Arduino.

2) Assicurarsi di mantenere la connessione tra GND della TLC5947 e Arduino GND.

3) Collegare il GND della TCL5947 al terminale negativo dell'alimentatore.

4) Collegare il V+ della TCL5947 e al terminale positivo dell'alimentatore.


Rimuovere innanzitutto la connessione tra V + e VIN da Arduino.

Assicurati di mantenere la connessione tra GND e Arduino GND.

Quindi collega il terminale negativo dell'aimentatore al GND della

scheda

Infine, collega il terminale positivo della tua alimentazione a V + sul

breakout. Dal link :

https://learn.adafruit.com/tlc5947-tlc59711-pwm-led-driver-breakout/power-and-leds

Choosing a Supply Voltage

Since these are constant current drivers, the voltage selection is not so critical. It just needs to be slightly higher than the forward votage (Vf )of your LEDs.


Supply Voltage Range:

  • TLC5947 - 5v to 30v
  • TLC59711 - 5v to 17v

Typical LED Vf by Color:

  • Red 2.1v
  • Yellow 2.2v
  • Green 3.2v
  • Blue 3.2v
  • White 3.2v

Connecting an External Supply

If you do decide you need an external supply for your LEDs:

  • First remove the connection between V+ and VIN from the Arduino.
  • Be sure to keep the connection between GND and the Arduino GND.
  • Next connect the negative terminal of your supply to GND on the breakout.
  • Finally, connect the positive terminal of your supply to V+ on the breakout.

I pin DIN / CLK / LAT possono essere collegati con pin diversi da pin digitali 4,5,6 purché in sequenza con valori successivi al 4 per es : 10,11,12 va comunque sempre verificata la funzionalità.

Le schede TLC5947 possono essere collegate in cascata come indicato in figura.

Connettendo la TLC5947 ad Arduino Mega 2560 R3,

i pin DIN,CLK e LAT della scheda vanno connessi ad Arduino Mega 2560

nel seguente modo:

TLC5947 Arduino Mega 2560 R3

DIN → Digital 17 / Digital 6/ Digital N

CLK → Digital 18 / Digital 5/Digital N+1

LAT → Digital 19/ Digital 4/Digital N+2

GND → GND

V+ → Vin

Con N un pin digitale e quindi si avrà una tripla di pin digitali consecutivi.

Se con una sequenza dei tre pin digitali non si ha successo, provarne un'altra.

Volendo utilizzare un alimentatore esterno, seguire la stessa procedura usata per Arduino UNO R3.

Ed ora ecco come inserire nella TLC5947 un led monocromatico ed un led RGB.

Ed ora un esempio di collegamento di una TCL5947 con Arduino Mega 2560 R3 per gestire manualmente l'intensità luminosa dei led RGB con potenziometri.

Verifica funzionalità scheda TLC5947

Vi suggerisco di provare la scheda inserendovi i 24 led, prima di farla diventare parte del vostro progetto. Questo per evitare rompicapi successivi ed incertezze sull'elemento del progetto che non “funziona”.

Per verificarla dovete disporre di:

  1. una scheda Arduino (UNO o Mega)
  2. una scheda TLC5947
  3. Da 1 a 24 led monocromatici
  4. lo sketch per il test.

Dopo aver effettuato i collegamenti ed inserito i led nella scheda TLC5947 in modo opportuno (l'anodo è sempre rivolto verso l'esterno della scheda su entrambi i lati), caricate sul vostro Arduino lo sketch seguente, attivando il Monitor seriale di Arduino per seguire le fasi di spegnimento ed accensione dei 24 led.

Verificate inoltre la funzionalità di due o più schede connesse tra loro.

A verifica avvenuta potrete far diventare parte del progetto le vostre TLC5947.

N.B.: Ho valutato la qualità dell'alimentazione dei led collegando un'estremità di un cavo, lungo 50 metri (due bobine da 25 mt) a due conduttori, ad un canale di una TLC5947 e l'altra estremità ai terminali di un led. Il risultato è stato eccellente.

Questa verifica è utile per sviluppare, come ho in mente, la gestione dei segnali luminosi lungo il tracciato usando, appunto, queste schede.

Segue lo sketch di verifica. Leggete bene i commento inseriti.

Sketch per il test della TLC5947

/*

SD card read/write


This example shows how to read and write data to and from an SD card file

The circuit:

* SD card attached to SPI bus as follows:

** MOSI - pin 11 - ARDUINO UNO

** MISO - pin 12 - ARDUINO UNO

** CLK - pin 13 - ARDUINO UNO

** CS - pin digitali da 3 a 10 (for MKRZero SD: SDCARD_SS_PIN)

* scegliendo il pin 10 si hanno su ARDUINO UNO tutti i cavetti jumper

* in sequenza : 10,11,12,13,GND accanto al pin 13.

* Quest'ultima connessione è importane, perchè

* se si utilizza il GND accanto al pin +5V la scheda

* SD non è accessibile!!!

* CS - PIN 53 PER MEGA 2560


created Nov 2010

by David A. Mellis

modified 9 Apr 2012

by Tom Igoe


This example code is in the public domain.


*/


#include <SPI.h>

#include <SD.h>


File myFile;


void setup() {

// Open serial communications and wait for port to open:

Serial.begin(9600);

while (!Serial) {

; // wait for serial port to connect. Needed for native USB port only

}



Serial.print("Initializing SD card...");


if (!SD.begin(53)) {

Serial.println("initialization failed!");

while (1);

}

Serial.println("initialization done.");


// open the file. note that only one file can be open at a time,

// so you have to close this one before opening another.

myFile = SD.open("test.txt", FILE_WRITE);


// if the file opened okay, write to it:

if (myFile) {

Serial.print("Scrivo il file test.txt...");

myFile.println("testing 11, 12, 13.");

// close the file:

myFile.close();

Serial.println("Chiusura file eseguita il file.");

} else {

// if the file didn't open, print an error:

Serial.println("Non posso aprire il file test.txt");

}


// re-open the file for reading:

myFile = SD.open("test.txt");

if (myFile) {

Serial.println("Ho riaperto il file test.txt:");


// read from the file until there's nothing else in it:

while (myFile.available()) {

Serial.write(myFile.read());

}

// close the file:

myFile.close();

Serial.println("RI-chiusura file test.txt eseguita il file.");

} else {

// if the file didn't open, print an error:

Serial.println("errore nella Riapertura file test.txt");

}

}


void loop() {

// nothing happens after setup

}

Osservazioni importanti.

E' raccomandabile attivare il monitor seriale, almeno le prime volte, per verificare le scelte effettuate sui servi e le risposte ottenute dallo sketch.

Questo sketch assegna ai servi gli indirizzi da 1 a 64. Questa modifica è stata attuata il 16 GIUGNO 2019. Leggete bene i commenti all'inizio degli schetch. Sono preziosi per capire come modificare il codice quando si vogliono incrementare oltre il 64 il numero dei servo da gestire od abbassare tale numero.

Attenzione!!! Sulle schede di retroazione/occupazione binario, in genere, si assegnano indirizzi DCC 65,66,67,...

Inoltre per far funzionare lo sketch anche con un solo servo è sufficiente disporre di una sola scheda PCA9685 e TLC5947.

Ecco lo schetck che gestisce Servo,SD e Led indicanti la posizione dei deviatoi sul tracciato utilizzando due led per ciascuno d'essi.

/*

Designed by Gianni Barbotti (ITALY) 15 Luglio 2018

Aggiornato il 16 Agosto 2018 per la gestione Led quadro sinottico.

revisione 11 Aprile 2019

REVISIONE 16 GIUGNO 2019

revisione 19 Agosto 2019

In questo sketch è stata aggiunta la possibilità di avere

un quadro della posizione dei deviatoi sul tracciato, anche

di quei deviatoi nascosti alla vista. Servi e led sono

automaticamente coordinati tramite uno specifico indicatore

memorizzato su scheda SD


a) ad ogni deviatoio sono associati due led ai quali,

nello sketch, ci si riferisce rispettivamente con le variabili

L0 per il tracciato corretto e L1 per il tracciato deviato


b) quando il deviatoio è posizionato su tracciato corretto

il led associato a questa posizione è acceso, mentre quello

associato alla posizione deviatoio deviato è spento.

.

c) quando il deviatoio è posizionato in tracciato deviato

il led associato a questa posizione è acceso, mentre quello

associato alla posizione deviatoio corretto è spento.


d) poiché per ogni deviatoio si gestiscono due led, la gestione delle schede

TLC5947 che gestiscono i led ed associate ai deviatoi necessitano di:


e-1) fino a 16 servi: 1 x PCA9685 + 2 x TLC5947

e-2) fino a 32 servi: 2 x PCA9685 + 3 x TLC5947

e-3) fino a 48 servi: 3 x PCA9685 + 4 x TLC5947

e-4) fino a 64 servi: 3 x PCA9685 + 6 x TLC5947


e così via. Per calcolare il numero delle TCL5947 basta dividere il numero dei servi

per dodici ed arrotondare per eccesso all'unità successiva nel caso sia presente

la parte decimale diversa da zero.


Se nel vostro plastico avete già installato decodificatori DCC

e di conseguenza avete una serie di indirizzi già occupate, ma volete

ampliare il vostro plastico aggiungendo altri deviatoi od elementi

che possono essere attivato coi servo (passaggi a livello),allora:

1) ponticellate (cioè unite con una saldatura la coppia delle piazzole)

della prima scheda 16-Channell 12-bit PCA096 che userete

in modo tale che il suo indirizzo fisico sia coerente con lo schema sotto indicato.


Posto che il simbolo "*" significa che la coppia di piazzole sono ponticellate,

il simbolo "o" significa che la coppia di piazzole non sono poncicellate.

La scheda PCA9685 arriva con questa configurazione base


0 0 0 0 0 0

0 0 0 0 0 0

Se, per esempio, l'ultimo indirizzo DCC dei dispositivi stazionari come

deviatoi, segnali luminosi, quindi esclusi gli indirizzi DCC

assegnati alle locomotive, è 51 allora il primo indirizzo successivo a 51 è

ovviamente 52.

Le schede PCA9685 gestiscono gli indirizzi in questo modo:

dal 1° al 16° servo, scheda PCA9685 con configurazione base, cioè:


0 0 0 0 0 0

0 0 0 0 0 0


con istruzione

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40);


dal 17° al 32° servo, scheda PCA9685 con configurazione

0 0 0 0 0 *

0 0 0 0 0 *

con istruzione:

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41);


dal 33° al 48° servo, scheda PCA9685 con configurazione


0 0 0 0 * 0

0 0 0 0 * 0

con istruzione:

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42);


dal 49° al 64° servo, scheda PCA9685 con configurazione


0 0 0 0 * *

0 0 0 0 * *

con istruzione:

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);


dal 65° al 80° servo, scheda PCA9685 con configurazione


0 0 0 * 0 0

0 0 0 * 0 0

con istruzione:

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire, 0x44);



dal 81° al 96° servo, scheda PCA9685 con configurazione


0 0 0 * 0 *

0 0 0 * 0 *

con istruzione:

Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire, 0x45);


dal 97° al 112° servo, scheda PCA9685 con configurazione


0 0 0 * * *

0 0 0 * * *

con istruzione:

Adafruit_PWMServoDriver pwm7 = Adafruit_PWMServoDriver(&Wire, 0x46);


e così via. Pertanto dovrete scegliere la confgurazione:

dal 49° al 64° servo, scheda PCA9685 con configurazione


0 0 0 0 * *

0 0 0 0 * *

con istruzione:

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);

Guardando la PCA965 in modo tale che i pin dei collegamenti coi

servi siano a sinistra, dovete collegare il primo servo alla

quarta terna di pin dall'alto verso il basso.

Le figure, descrizioni e didascalie che trovate sul sito web:


https://sites.google.com/view/dcc-arduino-micro-servo/home


sono esplicative.


Inoltre dovrete sostituire la riga: int start_address = 0;

con la riga int start_address = N;

con N = (Maggiore degli indirizzi gestiti con i decodificatori dei deviatoi e/o segnali) + 1.


Per esempio avete impegnati gli indirizzi per i deviatoi e/o segnali luminosi,

sino al numero 51, dovrete:

1) scrivere:

int start_address = 52

anzichè

int_star_address = 0.


2) scrivere:

int start_address = 52

anzichè

int_star_address = 0.




Se si vogliono gestire un numero superiore di servo a 64,supponendo che il numero sia S

sostituite l'istruzione :

int end_address = 63

con la :

int end_address = (S - 1)


e l'istruzione :

#define i_max_servo 64

con la :

#define i_max_servo S


Dichiarate inoltre le istruzioni:

Adafruit_PWMServoDriver pwmnn = Adafruit_PWMServoDriver(&Wire, 0xYY);

con nn numero progressivo di scheda ed YY indirizzo scheda PCA9685.

seguite questa operazione su tutte le dichiarazioni

dello sketch (v. all'interno della void setup() e della CHEC_SERVO)

*/


// *** Definizione libreria NMRA per DCC


#include <NmraDcc.h>


NmraDcc Dcc;


#define DCC_PIN 2 // definizione Pin comunicazione Arduino con interfaccia DCC


#define BOARD_ADDRESS 5


#define PAIR_ADDRESS 1



// Definizione Libreria e parametri scheda SD


#include <SD.h>


#include <SPI.h>



int CS_pin = 53; // CS = 3 O 4 Pin CS = 10 con Arduino UNO e pin 53 con Arduino MEGA 2560


int i;


int j;


char logfile[] = {"SERVI.txt"};


File file;



// DEfinizione Libreria e parametri per scheda Led TCL5947


// DEfinizione Libreria e parametri per scheda Led TCL5947


#include <Adafruit_TLC5947.h>


int N_SK_LED = 6; // Numero schede Adafruit 24-Channel 12-bit PWM LED Driver

// 6 è il numero di schede per gestire i 128 Led

// associati allo stato dei deviatoi mssi dai serv, infatti:

// 64 servi x 2 led a servo = 128 Led

// (128 Led : 24 Led per scheda) = 5,33333 che arrotondato

// all'unità superiore, fa 6.

// AssegnaTe un valore coerentemente al n.ro


// di TCL5947 necessarie



#include "Adafruit_TLC5947.h"


#define data 17 // Arduino Mega 17 - Uno 4


#define clock 18 // Arduino Mega 18 - Uno 5


#define latch 19 // Arduino Mega 19 - Uno 6


#define oe -1 // set to -1 to not use the enable pin (its optional)


#define NUM_TLC5974 N_SK_LED


Adafruit_TLC5947 tlc = Adafruit_TLC5947(NUM_TLC5974, clock, data, latch);

#define NUM_TLC5974 6 // 6 è il numero di schede per gestire i 128 Led

// associati allo stato dei deviatoi mossi dai servi, infatti:

// 64 servi x 2 led a servo = 128 Led

// 128 Led : 24 Led per scheda = 5,33333 che arrotondato

// all'unità superiore fa 6.




int step_ON = 180; // Step con cui si incrementa/decrementa luminosità dei Led per

int step_OFF = 175; // Step con cui si incrementa/decrementa luminosità dei Led per


int intensity = 0; // parametro per variazione intensità luminosa


int min_lux = 0; // valore minima intensità lumminosa


int max_lux = 4000; // valore massima intensità lumminosa


int L0 = 0;


int L1 = 0;



// **** Librerie e PARAMETRI RELATIVI AI SERVO *****


#include <Adafruit_PWMServoDriver.h> // Libreria per poter utilizzare i micro servo con Arduino

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40); // to manage servos from 1° to the 16°(addresses 0-15)


Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41); // to manage servos from 1° to the 16°(addresses 16-31)


Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42); // to manage servos from 1° to the 16°(addresses 32-47)


Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43); // to manage servos from 1° to the 16°(addresses 48-63)


// Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire,0x44); // to manage servos from 1° to the 16°(addresses 64-79)


// Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire,0x45); // to manage servos from 1° to the 16°(addresses 80-95)


/* Per ogni scheda Adafruit16-Channel aggiuntiva inserire la riga:


Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);


dove N è il numero progressivo della scheda aggiunta.



For each additional Adafruit16-Channel add the statement:


Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);


where N is the progressive of the Adafruit breakout added


*/



int start_address = 0;


int end_address = 63;


#define ritardo 5


#define L_ritardo 10


#define min_servo 1 // Non modificare


#define max_servo 64 // Non modificare


#define SERVOMIN 150 // Valore iniziale rotazione.Modificare a seconda dell'ampiezza della rotazione


// desiderata per l'asse del servo


#define SERVOMAX 310 // Valore finale rotazione - Modificare a seconda dell'ampiezza della rotazione


// desiderata per l'asse del servo


#define i_max_servo 64


int stato = 0;


char status_servo[i_max_servo];


#define num_ch_srv 16


#define freq_srv 60 // Frequenza funzionamento del servo - NON MODIFICARE -


int servonum; // variabile in cui si memorizza il numero progressivo del servo


// rispetto alla scheda che lo gestisce (da 0 a 15)


int servo; // è il numero progressivo del servo del range degli indirizzi e


// quindi è compreso tra:


// start_address +1 ed end_address + 1



int scheda; // numero della scheda calcolato in base all'indirizzo del deviatoio


int ind_status = 0;



int flag_SD = 0;



void notifyDccAccState(uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)


{



int pairAddress = (OutputAddr >> 1) + 1;


int outputInPair = OutputAddr & 0x01;


servo = abs(Addr - start_address );



if (Addr > i_max_servo)


{


Serial.println(F(" INDIRIZZO selezionato = "));


Serial.print(Addr, DEC);


Serial.println(F("Indirizzo non accettato perchè supera l'indirizzo massimo che è 64"));


}



if (Addr <= i_max_servo)


{


performAction(Addr, outputInPair);


}



if (flag_SD == 1)


{



remove_file();


create_servi(logfile);


open2File(logfile);


while (file.available())


{


readLine();


}


closeFile();


update_status(logfile);



}


}

void performAction(int Addr, int outputInPair)

{

int scheda;

scheda = ((Addr - 1) / num_ch_srv) + 1;

servonum = (Addr - 1) - num_ch_srv * (scheda - 1);

L0 = 2 * (Addr - 1);

L1 = L0 + 1;

flag_SD = 0;

if (outputInPair == 1) // è stata attivata posizione tracciato deviato

{

if ((status_servo[Addr - 1] == char(48)) || (status_servo[Addr - 1] == char(50)))

{

status_servo[Addr - 1] = char(49);

switch (scheda)

{

case 1:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm1.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 2:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm2.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 3:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm3.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

case 4:

for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)

{

pwm4.setPWM(servonum, 0, pulselen);

delay(ritardo);

}

break;

/*


case 5:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm5.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 6:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm6.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


*/


}




tlc.setPWM(L0, 0);

tlc.write();

tlc.setPWM(L1, max_lux);

tlc.write();

flag_SD = 1;


}


}


if (outputInPair == 0) // è stata attivata posizione tracciato corretto


{


if ((status_servo[Addr - 1] == char(49)) || (status_servo[Addr - 1] == char(50)))

// il servo è in posizione [+] oppure mai attivato

// va acceso il led L0 e spento il led L1


{


status_servo[Addr - 1] = char(48);



switch (scheda)


{


case 1:



for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm1.setPWM(servonum, 0, pulselen);


delay(ritardo);


}



break;


case 2:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm2.setPWM(servonum, 0, pulselen);


delay(ritardo);


}



break;


case 3:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm3.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 4:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm4.setPWM(servonum, 0, pulselen);


delay(ritardo);


}



break;


/*


case 5:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm5.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 6:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm6.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


*/


}



tlc.setPWM(L1, 0);


tlc.write();


tlc.setPWM(L0, max_lux);


tlc.write();


flag_SD = 1;



}


}


}



void setup()


{


Serial.begin(9600);


pinMode(CS_pin, OUTPUT);


initializeSD();



openFile(logfile);


while (file.available())


{



readLine();


}


closeFile();


flag_SD = 0;


tlc.begin();


Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);


Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0);


pwm1.begin();


pwm1.setPWMFreq(freq_srv);


pwm2.begin();


pwm2.setPWMFreq(freq_srv);


pwm3.begin();


pwm3.setPWMFreq(freq_srv);


pwm4.begin();


pwm4.setPWMFreq(freq_srv);


/*


pwm5.begin();


pwm5.setPWMFreq(freq_srv);


pwm6.begin();


pwm6.setPWMFreq(freq_srv);


*/


CHECK_SERVO();


}



void loop()


{


Dcc.process();


}




String readLine()


{



int i;


j = start_address;


String received = "";


char ch;


while (file.available())


{


ch = file.read();


if (ch == '\n')


{


return String(received);


}


else


{


status_servo[j] = ch;


received += ch;


j++;


}


}



return "";


}



void initializeSD()


{


if (!SD.begin(10))


{


Serial.println(F(" SD card non disponibile. USCITA!!"));


}


return;



}



void remove_file()


{



if (SD.remove("SERVI.txt"))


{



}


else


{


Serial.println(F("file SERVI.txt NON eliminato. NON ESISTE! USCITA!!!"));


return;


}


}



int openFile(char filename[])


{



file = SD.open(filename);


if (file)


{


return 1;


}


else


{


default_status(logfile);


return 0;


}



}



int open2File(char filename[])


{


file = SD.open(logfile);


if (file)


{


for (j = start_address; j <= end_address; j++)


{


file.print(status_servo[j]);


}


return 1;


}


else


{


Serial.println(F(" File SERVI.txt inesistente! USCITA!!!!"));


return 0;


}


}




int default_status(char filename[])


{



file = SD.open("SERVI.txt", FILE_WRITE);


if (file)


{



for (j = start_address; j <= end_address; j++)


{


file.print(char(50));


status_servo[j] = char(50);


}


file.close();



}


else


{


Serial.println(F("Non è stato possibile scrivere il file SERVI.txt! USCITA!!"));



return;


}


}


int CHECK_SERVO()


{



{


for (j = start_address; j <= end_address; j++)


{


L0 = 2 * j; // scrivete a_Led = (2 * j) - 1;


scheda = (j / num_ch_srv) + 1;


servonum = j - (num_ch_srv * (scheda - 1));


if (status_servo[j] == char(48))


{


tlc.setPWM(L1, 0); // OFF posizione corretta [-]


tlc.write();


tlc.setPWM(L0, max_lux); // Green - ON posizione corretta [-]


tlc.write();


switch (scheda)


{


case 1:



for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm1.setPWM(servonum, 0, pulselen);


}



break;


case 2:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm2.setPWM(servonum, 0, pulselen);


}


break;


case 3:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm3.setPWM(servonum, 0, pulselen);


}


break;


case 4:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm4.setPWM(servonum, 0, pulselen);


}


break;


/*


case 5:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm5.setPWM(servonum, 0, pulselen);


}


break;


case 6:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm6.setPWM(servonum, 0, pulselen);


}


break;


*/


}



}




if (status_servo[j] == char(49))


{

tlc.setPWM(L0, 0); // OFF posizione corretta [-]


tlc.write();


tlc.setPWM(L1, max_lux); // ON posizione deviata [+]


tlc.write();


switch (scheda)


{


case 1:



for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm1.setPWM(servonum, 0, pulselen);


}



break;


case 2:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm2.setPWM(servonum, 0, pulselen);


}


break;


case 3:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm3.setPWM(servonum, 0, pulselen);


}


break;


case 4:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm4.setPWM(servonum, 0, pulselen);


}


break;


/*


case 5:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm5.setPWM(servonum, 0, pulselen);


}


break;


case 6:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm6.setPWM(servonum, 0, pulselen);


}


break;


*/


}


}


// j++;


}


// while (j < i_max_servo);


}


}


int create_servi(char filename[])


{




file = SD.open("SERVI.txt", FILE_WRITE);


if (file)


{


file.close();


}


else


{


Serial.println(F("Non è stato possibile creare il file SERVI.txt! USCITA!!"));


return;


}



}


int update_status(char filename[])


{



file = SD.open("SERVI.txt", FILE_WRITE);


if (file)


{


for (j = start_address; j <= end_address; j++)


{


file.print(status_servo[j]);


}


file.close();


}


else


{


Serial.println(F("Non è stato possibile AGGIORNARE il file SERVI.txt! USCITA!!"));


return;


}



}


void closeFile()


{



if (file)


{



file.close();


}


}

Schetck che gestisce Servo,SD e Led indicanti la posizione dei deviatoi sul tracciato utilizzando quattro led per ciascuno d'essi.

/*


Designed by Gianni Barbotti (ITALY) 15 Luglio 2018


Aggiornato il 16 Agosto 2018 per la gestione Led quadro sinottico.


revisione 11 Aprile 2019


REVISIONE 16 GIUGNO 2019



revisione 13 Agosto 2019


In questo sketch è stata aggiunta la possibilità di avere


un quadro della posizione dei deviatoi sul tracciato, anche


di quei deviatoi nascosti alla vista. Servi e led sono


automaticamente coordinati tramite uno specifico indicatore


memorizzato su scheda SD



Lo sketch precedente gestisce il movimento dei servo e due led per servo,


led memorizzando il loro stato. Lo sketch accetta gli indirizzi da 1 a 64 (0-63).


Questo sketch offre la possibilità di avere un quadro della posizione dei deviatoi


sul tracciato utilizzando quattro led di colore V, R, V, R.



Ecco come sono gestiti i led associati ai deviatoi per avere lo stato


della loro posizione su un quadro di controllo visivo.




Ecco come sono gestiti i led associati ai deviatoi per avere lo stato


della loro posizione su un quadro di controllo visivo.



Ecco come sono gestiti i led associati ai deviatoi per avere lo stato


della loro posizione su un quadro di controllo visivo.


a) A ciascun deviatoio sono associati quattro led.


Ad ogni posizione del deviatoio sono associati due led



b) ai quattro diodi led , nello sketch, ci si riferisce


rispettivamente con le variabili :


L0 ed L2 per il tracciato corretto;


L1 ed L3 per il tracciato deviato;



c) quando il deviatoio è posizionato su tracciato corretto


i due led associati a tale posizione risulteranno:


- Verde acceso (L0)


- Rosso spento (L2)



mentre i led associati alla posizione tracciato corretto risulteranno:


- Verde spento(L1)


- Rosso acceso (L3)



d) quando il deviatoio è posizionato in tracciato deviato


i due led associati a tale posizione risulteranno:


- Verde acceso (L2)


- Rosso spento (L3)



mentre i led associati alla posizione tracciato corretto risulteranno:


- Verde spento(L0)


- Rosso acceso (L1)




e) poiché per ogni deviatoio si gestiscono 4 led, la gestione delle schede


per TLC5947 vecessarie ai servo che associate ai deviatoi necessitano di:



e-1) fino a 16 servi: 1 x PCA9685 + 3 x TLC5947


e-2) fino a 32 servi: 2 x PCA9685 + 6 x TLC5947


e-3) fino a 48 servi: 3 x PCA9685 + 8 x TLC5947


e-4) fino a 64 servi: 3 x PCA9685 + 11 x TLC5947



e così via. Per calcolare il numero delle TCL5947 basta dividere il numero dei servi


per dodici ed arrotondare per eccesso all'unità successiva nel caso siano presenti


cifre decimali diverse da zero



Se nel vostro plastico avete già installato decodificatori DCC


e di conseguenza avete una serie di indirizzi già occupate, ma volete


ampliare il vostro plastico aggiungendo altri deviatoi od elementi


che possono essere attivato coi servo (passaggi a livello),allora:


1) ponticellate (cioè unite con una saldatura la coppia delle piazzole)


della prima scheda 16-Channell 12-bit PCA096 che userete


in modo tale che il suo indirizzo fisico sia coerente con lo schema sotto indicato.



Posto che il simbolo "*" significa che la coppia di piazzole sono ponticellate,


il simbolo "o" significa che la coppia di piazzole non sono poncicellate.


La scheda PCA9685 arriva con questa configurazione base



0 0 0 0 0 0


0 0 0 0 0 0


Se, per esempio, l'ultimo indirizzo DCC dei dispositivi stazionari come


deviatoi, segnali luminosi, quindi esclusi gli indirizzi DCC


assegnati alle locomotive, è 51 allora il primo indirizzo successivo a 51 è


ovviamente 52.


Le schede PCA9685 gestiscono gli indirizzi in questo modo:


dal 1° al 16° servo, scheda PCA9685 con configurazione base, cioè:



0 0 0 0 0 0


0 0 0 0 0 0



con istruzione


Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40);



dal 17° al 32° servo, scheda PCA9685 con configurazione


0 0 0 0 0 *


0 0 0 0 0 *


con istruzione:


Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41);



dal 33° al 48° servo, scheda PCA9685 con configurazione



0 0 0 0 * 0


0 0 0 0 * 0


con istruzione:


Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42);



dal 49° al 64° servo, scheda PCA9685 con configurazione



0 0 0 0 * *


0 0 0 0 * *


con istruzione:


Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);



dal 65° al 80° servo, scheda PCA9685 con configurazione



0 0 0 * 0 0


0 0 0 * 0 0


con istruzione:


Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire, 0x44);




dal 81° al 96° servo, scheda PCA9685 con configurazione



0 0 0 * 0 *


0 0 0 * 0 *


con istruzione:


Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire, 0x45);



dal 97° al 112° servo, scheda PCA9685 con configurazione



0 0 0 * * *


0 0 0 * * *


con istruzione:


Adafruit_PWMServoDriver pwm7 = Adafruit_PWMServoDriver(&Wire, 0x46);



e così via. Pertanto dovrete scegliere la confgurazione:


dal 49° al 64° servo, scheda PCA9685 con configurazione



0 0 0 0 * *


0 0 0 0 * *


con istruzione:


Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43);


Guardando la PCA965 in modo tale che i pin dei collegamenti coi


servi siano a sinistra, dovete collegare il primo servo alla


quarta terna di pin dall'alto verso il basso.


Le figure, descrizioni e didascalie che trovate sul sito web:



https://sites.google.com/view/dcc-arduino-micro-servo/home



sono esplicative.




Inoltre dovrete sostituire la riga: int start_address = 0;


con la riga int start_address = N;


con N = (Maggiore degli indirizzi gestiti con i decodificatori dei deviatoi e/o segnali) + 1.



Per esempio avete impegnati gli indirizzi per i deviatoi e/o segnali luminosi,


sino al numero 51, dovrete:


1) scrivere:


int start_address = 52


anzichè


int_star_address = 0.



2) scrivere:


int start_address = 52


anzichè


int_star_address = 0.





Se si vogliono gestire un numero superiore di servo a 64,supponendo che il numero sia S


sostituite l'istruzione :


int end_address = 63


con la :


int end_address = (S - 1)



e l'istruzione :


#define i_max_servo 64


con la :


#define i_max_servo S


Dichiarate inoltre le istruzioni:


Adafruit_PWMServoDriver pwmNN = Adafruit_PWMServoDriver(&Wire, 0xYY);


con NN numero progressivo di scheda ed YY indirizzo scheda PCA9685.


seguite questa operazione su tutte le dichiarazioni


dello sketch (v. all'interno della void setup() e della CHEC_SERVO)



*/



// *** Definizione libreria NMRA per DCC


#include <NmraDcc.h>


NmraDcc Dcc;


#define DCC_PIN 2 // definizione Pin comunicazione Arduino con interfaccia DCC


#define BOARD_ADDRESS 5


#define PAIR_ADDRESS 1



// Definizione Libreria e parametri scheda SD


#include <SD.h>


#include <SPI.h>



int CS_pin = 53; // CS = 3 O 4 Pin CS = 10 con Arduino UNO e pin 53 con Arduino MEGA 2560


int i;


int j;


char logfile[] = {"SERVI.txt"};


File file;



// DEfinizione Libreria e parametri per scheda Led TCL5947


// DEfinizione Libreria e parametri per scheda Led TCL5947


#include <Adafruit_TLC5947.h>


int N_SK_LED = 6; // Numero schede Adafruit 24-Channel 12-bit PWM LED Driver

// 6 è il numero di schede per gestire i 128 Led

// associati allo stato dei deviatoi mssi dai serv, infatti:

// 64 servi x 2 led a servo = 128 Led

// (128 Led : 24 Led per scheda) = 5,33333 che arrotondato

// all'unità superiore, fa 6.

// AssegnaTe un valore coerentemente al n.ro


// di TCL5947 necessarie



#include "Adafruit_TLC5947.h"


#define data 17 // Arduino Mega 17 - Uno 4


#define clock 18 // Arduino Mega 18 - Uno 5


#define latch 19 // Arduino Mega 19 - Uno 6


#define oe -1 // set to -1 to not use the enable pin (its optional)


#define NUM_TLC5974 N_SK_LED


Adafruit_TLC5947 tlc = Adafruit_TLC5947(NUM_TLC5974, clock, data, latch);

#define NUM_TLC5974 6 // 6 è il numero di schede per gestire i 128 Led

// associati allo stato dei deviatoi mssi dai serv, infatti:

// 64 servi x 2 led a servo = 128 Led

// 128 Led : 24 Led per scheda = 5,33333 che arrotondato

// all'unità superiore fa 6.




int step_ON = 180; // Step con cui si incrementa/decrementa luminosità dei Led per

int step_OFF = 175; // Step con cui si incrementa/decrementa luminosità dei Led per

int a_Led; // Indirizzo base del gruppo dei led da accendere/spegnere a seguito

// della variazione di stato di un deviatoio.


int intensity = 0; // parametro per variazione intensità luminosa


int min_lux = 0; // valore minima intensità lumminosa


int max_lux = 4000; // valore massima intensità lumminosa


int L0 = 0;


int L1 = 0;


int L2 = 0;


int L3 = 0;



// **** Librerie e PARAMETRI RELATIVI AI SERVO *****


#include <Adafruit_PWMServoDriver.h> // Libreria per poter utilizzare i micro servo con Arduino

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(&Wire, 0x40); // to manage servos from 1° to the 16°(addresses 0-15)


Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(&Wire, 0x41); // to manage servos from 1° to the 16°(addresses 16-31)


Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(&Wire, 0x42); // to manage servos from 1° to the 16°(addresses 32-47)


Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(&Wire, 0x43); // to manage servos from 1° to the 16°(addresses 48-63)


// Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(&Wire,0x44); // to manage servos from 1° to the 16°(addresses 64-79)


// Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(&Wire,0x45); // to manage servos from 1° to the 16°(addresses 80-95)


/* Per ogni scheda Adafruit16-Channel aggiuntiva inserire la riga:


Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);


dove N è il numero progressivo della scheda aggiunta.



For each additional Adafruit16-Channel add the statement:


Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);


where N is the progressive of the Adafruit breakout added


*/



int start_address = 0;


int end_address = 63;


#define ritardo 5


#define L_ritardo 10


#define min_servo 1 // Non modificare


#define max_servo 64 // Non modificare


#define SERVOMIN 150 // Valore iniziale rotazione.Modificare a seconda dell'ampiezza della rotazione


// desiderata per l'asse del servo


#define SERVOMAX 310 // Valore finale rotazione - Modificare a seconda dell'ampiezza della rotazione


// desiderata per l'asse del servo


#define i_max_servo 64


int stato = 0;


char status_servo[i_max_servo];


#define num_ch_srv 16


#define freq_srv 60 // Frequenza funzionamento del servo - NON MODIFICARE -


int servonum; // variabile in cui si memorizza il numero progressivo del servo


// rispetto alla scheda che lo gestisce (da 0 a 15)


int servo; // è il numero progressivo del servo del range degli indirizzi e


// quindi è compreso tra:


// start_address +1 ed end_address + 1



int scheda; // numero della scheda calcolato in base all'indirizzo del deviatoio


int ind_status = 0;



int flag_SD = 0;



void notifyDccAccState(uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)


{



int pairAddress = (OutputAddr >> 1) + 1;


int outputInPair = OutputAddr & 0x01;


servo = abs(Addr - start_address );



if (Addr > i_max_servo)


{


Serial.println(F(" INDIRIZZO selezionato = "));


Serial.print(Addr, DEC);


Serial.println(F("Indirizzo non accettato perchè supera l'indirizzo massimo che è 64"));


}



if (Addr <= i_max_servo)


{


performAction(Addr, outputInPair);


}



if (flag_SD == 1)


{



remove_file();


create_servi(logfile);


open2File(logfile);


while (file.available())


{


readLine();


}


closeFile();


update_status(logfile);



}


}





void performAction(int Addr, int outputInPair)


{



int scheda;


scheda = ((Addr - 1) / num_ch_srv) + 1;


servonum = (Addr - 1) - num_ch_srv * (scheda - 1);


L0 = 4 * (Addr - 1);

L1 = L0 + 1;

L2 = L0 + 2;

L3 = L0 + 3;


flag_SD = 0;



if (outputInPair == 1) // è stata attivata posizione tracciato deviato


{


if ((status_servo[Addr - 1] == char(48)) || (status_servo[Addr - 1] == char(50)))


{



status_servo[Addr - 1] = char(49);



switch (scheda)


{


case 1:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm1.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 2:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm2.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 3:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm3.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 4:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm4.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


/*


case 5:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm5.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 6:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm6.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


*/


}




tlc.setPWM(L0, 0);


tlc.write();


tlc.setPWM(L3, 0);


tlc.write();


tlc.setPWM(L1, max_lux);


tlc.write();


tlc.setPWM(L2, max_lux);


tlc.write();



flag_SD = 1;


}


}


if (outputInPair == 0) // è stata attivata posizione tracciato corretto


{


if ((status_servo[Addr - 1] == char(49)) || (status_servo[Addr - 1] == char(50))) // il servo è in posizione


// [+] oppure mai attivato


// va acceso il led L0 e spento il led L1


{


status_servo[Addr - 1] = char(48);



switch (scheda)


{


case 1:



for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm1.setPWM(servonum, 0, pulselen);


delay(ritardo);


}



break;


case 2:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm2.setPWM(servonum, 0, pulselen);


delay(ritardo);


}



break;


case 3:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm3.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 4:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm4.setPWM(servonum, 0, pulselen);


delay(ritardo);


}



break;


/*


case 5:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm5.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


case 6:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm6.setPWM(servonum, 0, pulselen);


delay(ritardo);


}


break;


*/


}



tlc.setPWM(L1, 0);


tlc.write();


tlc.setPWM(L2, 0);


tlc.write();


tlc.setPWM(L0, max_lux);


tlc.write();


tlc.setPWM(L3, max_lux);


tlc.write();



flag_SD = 1;



}


}


}



void setup()


{


Serial.begin(9600);


pinMode(CS_pin, OUTPUT);


initializeSD();



openFile(logfile);


while (file.available())


{



readLine();


}


closeFile();


flag_SD = 0;


tlc.begin();


Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);


Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0);


pwm1.begin();


pwm1.setPWMFreq(freq_srv);


pwm2.begin();


pwm2.setPWMFreq(freq_srv);


pwm3.begin();


pwm3.setPWMFreq(freq_srv);


pwm4.begin();


pwm4.setPWMFreq(freq_srv);


/*


pwm5.begin();


pwm5.setPWMFreq(freq_srv);


pwm6.begin();


pwm6.setPWMFreq(freq_srv);


*/


CHECK_SERVO();


}



void loop()


{


Dcc.process();


}




String readLine()


{



int i;


j = start_address;


String received = "";


char ch;


while (file.available())


{


ch = file.read();


if (ch == '\n')


{


return String(received);


}


else


{


status_servo[j] = ch;


received += ch;


j++;


}


}



return "";


}



void initializeSD()


{


if (!SD.begin(10))


{


Serial.println(F(" SD card non disponibile. USCITA!!"));


}


return;



}



void remove_file()


{



if (SD.remove("SERVI.txt"))


{



}


else


{


Serial.println(F("file SERVI.txt NON eliminato. NON ESISTE! USCITA!!!"));


return;


}


}



int openFile(char filename[])


{



file = SD.open(filename);


if (file)


{


return 1;


}


else


{


default_status(logfile);


return 0;


}



}



int open2File(char filename[])


{


file = SD.open(logfile);


if (file)


{


for (j = start_address; j <= end_address; j++)


{


file.print(status_servo[j]);


}


return 1;


}


else


{


Serial.println(F(" File SERVI.txt inesistente! USCITA!!!!"));


return 0;


}


}




int default_status(char filename[])


{



file = SD.open("SERVI.txt", FILE_WRITE);


if (file)


{



for (j = start_address; j <= end_address; j++)


{


file.print(char(50));


status_servo[j] = char(50);


}


file.close();



}


else


{


Serial.println(F("Non è stato possibile scrivere il file SERVI.txt! USCITA!!"));



return;


}


}


int CHECK_SERVO()


{



{


for (j = start_address; j <= end_address; j++)


{


L0 = 4 * j; // scrivete a_Led = (2 * j) - 1;


scheda = (j / num_ch_srv) + 1;


servonum = j - (num_ch_srv * (scheda - 1));


if (status_servo[j] == char(48))


{


tlc.setPWM(L1, 0); // Red OFF posizione corretta [-]


tlc.write();


tlc.setPWM(L2, 0); // Green OFF posizione deviata [+]


tlc.write();


tlc.setPWM(L0, max_lux); // Green - ON posizione corretta [-]


tlc.write();


tlc.setPWM(L3, max_lux); // Red ON posizione deviata [+]


tlc.write();



switch (scheda)


{


case 1:



for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm1.setPWM(servonum, 0, pulselen);


}



break;


case 2:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm2.setPWM(servonum, 0, pulselen);


}


break;


case 3:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm3.setPWM(servonum, 0, pulselen);


}


break;


case 4:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm4.setPWM(servonum, 0, pulselen);


}


break;


/*


case 5:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm5.setPWM(servonum, 0, pulselen);


}


break;


case 6:


for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++)


{


pwm6.setPWM(servonum, 0, pulselen);


}


break;


*/


}



}




if (status_servo[j] == char(49))


{

tlc.setPWM(L0, 0); // Green OFF posizione corretta [-]


tlc.write();


tlc.setPWM(L3, 0); // Red OFF posizione deviata [+]


tlc.write();


tlc.setPWM(L1, max_lux); // Red - ON posizione corretta [-]


tlc.write();


tlc.setPWM(L2, max_lux); // Green ON posizione deviata [+]


tlc.write();



switch (scheda)


{


case 1:



for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm1.setPWM(servonum, 0, pulselen);


}



break;


case 2:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm2.setPWM(servonum, 0, pulselen);


}


break;


case 3:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm3.setPWM(servonum, 0, pulselen);


}


break;


case 4:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm4.setPWM(servonum, 0, pulselen);


}


break;


/*


case 5:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm5.setPWM(servonum, 0, pulselen);


}


break;


case 6:


for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--)


{


pwm6.setPWM(servonum, 0, pulselen);


}


break;


*/


}


}


// j++;


}


// while (j < i_max_servo);


}


}


int create_servi(char filename[])


{




file = SD.open("SERVI.txt", FILE_WRITE);


if (file)


{


file.close();


}


else


{


Serial.println(F("Non è stato possibile creare il file SERVI.txt! USCITA!!"));


return;


}



}


int update_status(char filename[])


{



file = SD.open("SERVI.txt", FILE_WRITE);


if (file)


{


for (j = start_address; j <= end_address; j++)


{


file.print(status_servo[j]);


}


file.close();


}


else


{


Serial.println(F("Non è stato possibile AGGIORNARE il file SERVI.txt! USCITA!!"));


return;


}



}


void closeFile()


{



if (file)


{



file.close();


}


}

Con quest'ultimo sketch ha termine la II Parte del progetto.

Parte III

Segnali luminosi.