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

valido per protocollo sia Xpressnet che Loconet

progetto realizzato da Gianni Barbotti

ultima revisione 04/05/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 (quadro sinottico) e comandare i segnali luminosi, il tutto in ambiente DCC in modalità manuale o semiautomatica o automatica usando software quali Train Controller , Rocrail o simili senza però usare i decoder per deviatoi e segnali , ma utilizzando Arduino e le numerose offerte di accessori presenti per Arduino.

I punti qualificanti sono dunque:

a) Usare uno sketch (programma) per Arduino UNO R3 e 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 micro-servo. 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 un'immagine

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 mossi dai servo 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 e 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. Ho scelto di impiegare l'Arduino Mega 2560 R3, perchè l'Arduino UNO ha limiti di memoria e numero di connessioni (pin) inferiore al Mega e non permette di realizzare interamente il progetto. Il materiale disponibile in rete è notevole, ma non ho trovato nulla 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;
  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 Rocrail, Train Controller e analoghi da usare in modalità automatica o manuale.

Arduino ed il DCC non “parlano” la stessa lingua (protocollo). Il nostro sistema DCC (Lenz,Roco 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 attivati da decoder. Questi gestiscono i motori , generalmente, in gruppi di quattro ed a ciascuna coppia motore-deviatoio si associa un numero progressivo detto indirizzo. I motori sotto plancia, in questo progetto, sono sostituiti dai micro-servo ed i decoder da interfacce. Quella che ho utilizzato per comandare i servo è la

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

dove :

16-Channel indica che una sola interfaccia gestisce fino a 16 servo

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 all'uso Per trovarle è sufficiente digitare 16-Channel 12-bit PWM/Servo . Il costo di una di queste interfacce è la metà di quella di un decoder digitale a quattro vie , ma è in grado di monitorare il quadruplo dei deviatoi . Inoltre risulta estremamente vantaggioso il prezzo di un singolo micro 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ì per ciascuna scheda è in grado di pilotare 24 led monocolore. E' pure possibile gestire led RGB (Red Green Blue) coi quali, miscelandoli opportunamente 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, detto interfaccia, 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 del programma detto 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). 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/

Al link trovate le immagini della shield. E' importante sottolineare che con la shield, che si sovrappone ad Arduino Mega 2560 R3, dovrete solo collegare il connettore bipolare 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 servo e/o i led da gestire tanto più sarà maggiore il risparmio.

Ecco gli elementi che ho utilizzato per realizzare il progetto.

1) Arduino Mega 2560 R3 in sostituzione dell'Arduino UNO R3, perchè l'uso delle interfacce per servo, per led, per il DCC e la scheda SD dove memorizzare lo stato dei dispositivi e l'intero programma (sketch) che gestisce il tutto , sull'Arduino UNO non ha memoria né accessi sufficienti per operare con le librerie (programmi che fanno “funzionare” le interfacce). Le foto dei collegamenti delle interfacce con Arduino UNO R3 presenti , sono valide anche per effettuare i collegamenti di esse con Arduino Mega 2560 R3 e, dove necessario, sono indicate le eventuali variazioni di connessione.

2) due interfacce 16-Channel 12-bit PWM/Servo Driver -I2C - PCA9685 (due per verificare che le interfacce lavorino, collegate in serie, 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) Micro Servo (gestibili in numero massimo di 32 con due schede. Pertanto con N schede gestirete un massimo di Nx16 micro servo, quindi 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 è arrotondato all'unità superiore. Va fatta poi il necessario aggiustamento per i deviatoi a tre vie od inglesi che richiedono due micro servo 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, altrimenti i micro servo non saranno alimentati e non si muoveranno. Se connetterete solo una scheda il programma (sketch) funzionerà ugualmente. Io ne ho usate due per testare anche il collegamento tra le schede stesse.

6) Alimentatore 5-25 V DC 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 e che i led dei segnali luminosi 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, è 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) Arduino Mega 2560 R3 o suo clone, 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 Mega 2560 R3 , 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 è [F5], impostate un indirizzo qualsiasi e quindi selezionate lo stato che volete (per LH100 si usano i tasti [+] e [-]). Sulla finestra del Monitor Seriale dell'IDE di Arduino verrà visualizzato un testo di questo tipo:

Test interfaccia DCC

0 --> tracciato deviato 1 --> tracciato correttoIndirizzo deviatoio

selezionato: 62

Stato : 0

Indirizzo deviatoio selezionato: 62

Stato : 1

Indirizzo deviatoio selezionato: 21

Stato : 0

Indirizzo deviatoio selezionato: 21

Stato : 1

Indirizzo deviatoio selezionato: 21

Stato : 1



Fatelo prima di collegare qualsiasi altra interfaccia ad Arduino!!!!!!

Potete testare lo sketch anche con Train Controller

Una centraline DCC che utilizzano il protocollo XpressNet e Loconet è adatta a questa interfaccia.

Qui sotto ho inserito il listato dello sketch.

Per utilizzare lo sketch dovrete fare, com'è palese dal listato, la download delle librerie:

  • NmraDCC.h

In caso contrario lo sketch segnalerà gli errori connessi a questa mancanza



#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)

{

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.print(F("Start sketch "));


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

Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0); // Serial.println("- decoder ready");


}


void loop()

{

Dcc.process();

}

Descrizione scheda 16-Channel 12 bit PWM Servo Motor Driver moduli I2C PCA9685.

Quando si vede questo chip si comprende subito che può essere un' ottima alternativa ai decoder dcc per deviatoi . Utilizzando solo due 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 controllare da un microcontrollore con 3.3V e ancora tranquillamente guidare 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 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 la condotta dei LED semplice.

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) . 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 ad Arduino nell'omonimo pin.

VCC - Questo è il pin logico per comunicare col PCA9685, collegare questo al livello logico che si desidera . Utilizzato come uscita PCA9685, dovrebbe essere alimentato 3 - 5 V max! Viene anche utilizzato per i pullup 10K su SCL / SDA quindi, a meno che tu non abbia i tuoi pullup, devi abbinarli anche al livello logico del microcontrollore!

V + VI SCONSIGLIO ASSUTAMENTE 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 collocato vicino al condensatore . 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 indipendentemente, ma devono avere tutti la stessa frequenza PWM. Essa può essere usata anche per gestire i Led sfruttando l'effetto FADE.

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 ii 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) con la riga inferiore e il filo del segnale

(di solito giallo o bianco) in alto.

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 micro servi.

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 il test di operatività di una coppia di PCA9685 concatete e dei servo collegati sino ad un massimo di 32 ( sedici per scheda).

/*

Test funzionalità due servo concatenati

*/

#include <Wire.h>

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

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); // to manage servos from 1° to the 16°(addresses 100-115)

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41); // to manage servos from 1° to the 16°(addresses 116-131)

#define SERVOMIN 150

#define SERVOMAX 600

#define freq_srv 60

uint8_t servonum = 0;

void setup()

{

Serial.begin(9600);

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

pwm1.begin();

// setto la frequenza di funzionamento per i servi della scheda 1

pwm1.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 2

pwm2.begin();

// setto la frequenza di funzionamento per i servi della scheda 2

pwm2.setPWMFreq(freq_srv);; // Frequenza fissata a 60 Hz

}

// you can use this function if you'd like to set the pulse length in seconds

// e.g. setServoPulse(0, 0.001) is a ~1 millisecond pulse width. its not precise!

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);

}

void loop()

{

Serial.println(servonum);

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

{

{

if (servonum <= 15)

{

pwm1.setPWM(servonum, 0, pulselen);

}

else

if (servonum >= 15 && servonum < 32)

{

pwm2.setPWM((servonum -15), 0, pulselen);

}

}

}

delay(5);

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

{

{

if (servonum <= 15)

{

pwm1.setPWM(servonum, 0, pulselen);

}

else

if (servonum >= 15 && servonum < 32)

{

pwm2.setPWM((servonum - 15), 0, pulselen);

}

}

}

servonum ++;

if (servonum >32) servonum = 0;

}

Sketch di gestione dei solo servi utilizzati nel plastico. Lavora sia con XpressNet che con LocoNet

Designed by Gianni Barbotti (ITALY) July 15 2018

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

Ultima revisione 04/05/2019

Lo sketch gestisce il movimento dei servo con indirizzo assegnato senza vincoli, se non quello della scelta di chi opera direttamente sul tracciato

*/

// *** Definizione libreria NMRA per DCC

#include <NmraDcc.h>

#include <Adafruit_PWMServoDriver.h>

NmraDcc Dcc;



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

#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 = 53; // Pin CS = 10 con Ardiono UNO e pin 53 con Arduino MEGA 2560

int j;

int a_Led;

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

File file;



int passo_ON = 25; // Step con cui si incrementa/decrementa luminosità dei Led per

// l'effetto di brillantezza sfumata (FADE) in fase di accensione

int passo_OFF = 15; // Step con cui si incrementa/decrementa luminosità dei Led per

// l'effetto di brillantezza sfumata (FADE) in fase di spegnimento

// della variazione di stato di un deviatoio.


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


Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); // to manage servos from 1° to the 16°

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41); // to manage servos from 17° to the 32°

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42); // to manage servos from 33° to the 48°

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x43); // to manage servos from 49° to the 64°

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(0x44); // to manage servos from 65° to the 80°

Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(0x45); // to manage servos from 81° to the 96°

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

Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);

dove N è il numero progressivo della scheda aggiunta.

For each additional Adafruit16-Channell add the statement:

Adafruit_PWMServoDriver pwmN = Adafruit_PWMServoDriver(0x4N);

where N is the progressive of the Adafruit breakout added

*/


int start_address = 0;

#define ritardo 5

#define SERVOMIN 200

#define SERVOMAX 300

#define i_max_servo 96


char status_servo[i_max_servo];

#define num_ch_srv 16

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

int servonum;

int servo;

int scheda;

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

{

// Serial.println("notify");

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

int outputInPair = OutputAddr & 0x01;

servo = abs(Addr);

// a_Led = 4 * servo;

performAction(Addr, outputInPair);

}

void performAction(int Addr, int outputInPair)

{

Serial.println("perform action");

int scheda;

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

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

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

Serial.print(Addr, DEC);

Serial.print(" scheda= ");

Serial.print(scheda, DEC);

Serial.print(" servo = ");

Serial.print(servo, DEC);

Serial.print(" servonum= ");

Serial.print(servonum, DEC);

Serial.print(" outputInPair= ");

Serial.println(outputInPair);

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);

pwm5.begin();

pwm5.setPWMFreq(freq_srv);

pwm6.begin();

pwm6.setPWMFreq(freq_srv);

// 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);

}


void 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 sicuramente. 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 Mega 2560. 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.

La gestione di ciascun servo è associata ad un indirizzo DCC, ho pensato che in un plastico medio-piccolo, i primi novantanove indirizzi (escluso lo zero) possano essere sufficienti la loro assegnazione al parco locomotive. Quindi ecco che nella gestione del plastico gli indirizzi dei servo partiranno dal valore cento. Chi avesse un parco locomotive più numeroso, può assegnare un indirizzo iniziale più adatto a sua scelta, salvo poi aggiungere la differenza a ciascuna cella della colonna INDIRIZZO SERVO. Le altre colonne DEVONO RIMANERE intatte.

Le tabelle contengono anche i dati riferiti alla gestione dei led per il quadro sinottico. Tratterò questo argomento nella seconda parte dl progetto. Lascio perciò inalterate le tabelle e le immagini riguardanti la gestione dei led.

Ecco indicato nelle seguenti tabelle, in base al numero dei servi utilizzati, quante schede

  • 16-Channel 12 bit PWM Servo Motor Driver moduli I2C PCA9685
  • 24-Channel 12-bit PWM LED Driver - SPI Interface – TLC5947

sono necessarie per la loro gestione e quella dei led.

Come si può osservare dall'ultima riga con solo sei schede PCA9685 si possono gestire ben 96 servo (si inizia dall'indirizzo 100 compreso!).

Ed ecco, infine, lo sketch della gestione completa servi+scheda SD.


/*

Designed by Gianni Barbotti (ITALY) July 15 2018

aggiornamento 07 Apr 2019

ultimo aggiornamento del 04/05/2019

Lo sketch gestisce il movimento dei servo con indirizzi bloccati dal 100 (indi=

rizzo iniziale, al 196 indirizzo finale. Questa scelta è dovuta al fatto che l'abbinamento servo-posizione deviatoio andrà memorizzata in un file su scheda SD.

*/

// *** 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

// Definizione Libreria e parametri scheda SD

#include <SD.h>

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

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(0x40); // to manage servos from 1° to the 16°(addresses 100-115)

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41); // to manage servos from 1° to the 16°(addresses 116-131)

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42); // to manage servos from 1° to the 16°(addresses 132-147)

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x43); // to manage servos from 1° to the 16°(addresses 148-163)

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(0x44); // to manage servos from 1° to the 16°(addresses 164-179)

Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(0x45); // to manage servos from 1° to the 16°(addresses 180-195)

int start_address = 100;

int end_address = 195;


#define ritardo 15

#define min_servo 100

#define max_servo 195

#define SERVOMIN 200

#define SERVOMAX 280

#define i_max_servo 96 // NUMERO MASSIMO DEI SERVO GESTITI DA QUESTO SKETCH

char status_servo[i_max_servo];

#define num_ch_srv 16 // NON MODIFICARE

#define freq_srv 60 // NON MODIFICARE -

int servonum;

int servo;

int scheda;

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);

performAction(Addr, outputInPair);

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 - start_address) / num_ch_srv) + 1;

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

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

Serial.print(Addr, DEC);

Serial.print(" scheda= ");

Serial.print(scheda, DEC);

Serial.print(" servo = ");

Serial.print(servo, DEC);

Serial.print(" servonum= ");

Serial.print(servonum, DEC);

Serial.print(" outputInPair= ");

Serial.println(outputInPair);

if ((start_address <= Addr <= end_address) && (outputInPair == 1))

{

if ((status_servo[servo] == char(48)) || (status_servo[servo] == char(50)))

{

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 ((start_address <= Addr <= end_address) && (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);

}

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: // attiva i servo con Addr dal 148 al 163

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); // Serial.println("- decoder ready");

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 1

pwm1.begin();

// setto la frequenza di funzionamento per i servi della scheda 1

pwm1.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 2

pwm2.begin();

// setto la frequenza di funzionamento per i servi della scheda 2

pwm2.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 1

pwm3.begin();

// setto la frequenza di funzionamento per i servi della scheda 1

pwm3.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 2

pwm4.begin();

// setto la frequenza di funzionamento per i servi della scheda 2

pwm4.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 1

pwm5.begin();

// setto la frequenza di funzionamento per i servi della scheda 1

pwm5.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

// apro la comunicazione con la scheda per la gestione dei servo connessi alla scheda 2

pwm6.begin();

// setto la frequenza di funzionamento per i servi della scheda 2

pwm6.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz


// CHECK_LED(); FUNZIONE INGLOBATA NELLA ***** CHECK_SERVO *****

CHECK_SERVO();


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

Dcc.init(MAN_ID_DIY, 1, FLAGS_DCC_ACCESSORY_DECODER, 0); // Serial.println("- decoder ready");

pwm1.begin();

pwm1.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

pwm2.begin();

pwm2.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

pwm3.begin();

pwm3.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

pwm4.begin();

pwm4.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

pwm5.begin();

pwm5.setPWMFreq(freq_srv); // Frequenza fissata a 60 Hz

pwm6.begin();

pwm6.setPWMFreq(freq_srv);

}

void loop()

{

Dcc.process();

}

String readLine()

{

int i;

j = 0;

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()

{

// Serial.println("Initializing SD card...");

if (SD.begin())

{

// Serial.println("SD card disponibile");

}

else

{

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

return;

}

}


void remove_file()

{

// Serial.println("dentro removefile");

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

{

// Serial.println("file SERVI.txt eliminato!");

}

else

{

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

return;

}

}


int openFile(char filename[])

{

// Serial.println("dentro openFile");

file = SD.open(filename);

if (file)

{

// Serial.println(" Open File SERVI OK!");

return 1;

}

else

{

default_status(logfile);

return 0;

}


}


int open2File(char filename[])

{

// Serial.println("dentro open2File");

file = SD.open(logfile);

if (file)

{

// Serial.println(" Open File SERVI OK!");

for (j = 0; j <= i_max_servo; j++)

{

file.print(status_servo[j]);

}

return 1;

}

else

{

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

return 0;

}

}



int default_status(char filename[])

{

// Serial.println("default_status");

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

if (file)

{

j = 0;

for (j = 0; j <= i_max_servo; j++)

{

file.print(char(50));

status_servo[j] = char(50);

}

file.close();

// return 0;

}

else

{

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


return;

}

}



int CHECK_SERVO()

{

j = 0;

do

{

scheda = (j / num_ch_srv) + 1;

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

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

{

switch (scheda)

{

case 1: // attiva i servo con con indirizzo compreso tra 100 e 115


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

{

pwm1.setPWM(servonum, 0, pulselen);

}


break;

case 2: // attiva i servo con Addr dal 116 al 131

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

{

pwm2.setPWM(servonum, 0, pulselen);

}

break;

case 3: // attiva i servo con Addr dal 132 al 147

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

{

pwm3.setPWM(servonum, 0, pulselen);

}

break;

case 4: // attiva i servo con Addr dal 148 al 163

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

{

pwm4.setPWM(servonum, 0, pulselen);

}

break;

case 5: // attiva i servo con Addr dal 164 al 179

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

{

pwm5.setPWM(servonum, 0, pulselen);

}

break;

case 6: // attiva i servo con Addr dal 180 al 195

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

{

pwm6.setPWM(servonum, 0, pulselen);

}

break;

}

}


if (status_servo[j] == char(49)) // il servo era posizionato con deviatoio

// in tracciato deviato tasto[+] LH100

{

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("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 = 0;

for (j = 0; j <= i_max_servo; j++)

{

file.print(status_servo[j]);

}

file.close();

}

else

{

Serial.println("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.

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

CLK -> Digital 18

LAT -> Digital 19

GND -> GND

V+ → Vin

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. 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 di verifica funzionalità TLC5947

// Edit by Gianni Barbotti

// 11/04/2019

//


#include "Adafruit_TLC5947.h"

#define NUM_TLC5974 1

// I tre valori 17/18/19 sono impostati per la scheda Arduino Mega 2560 R3

// Per la verifica con la scheda Arduino UNO R3 usate 4/5/6

#define data 17 // per Arduino UNO impostare a 4

#define clock 18 // per Arduino UNO impostare a 5

#define latch 19 // per Arduino UNO impostare a 6

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

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

void setup() {

Serial.begin(9600);

Serial.println("Sequenza OFF-ON 24 leds");

tlc.begin();

int i = 0;

int intensity= 0;

}

void loop()

{

Serial.println("Spegnimento dei Led");

// ****************************************

//NB NB NB NB

// per testare due schede concatenate usate 47 anzichè 24

// per testare tre schede concatenate usate 71 anzichè 47

// e così via.

//Se però avete già verificato la funzionalità di ciascuna scheda

// direi che è sufficiente verificare la connessione a catena di due schede TLC5947

// impostando, in tal caso il valore i<24 a i<47

// ********************************************************

for(int i=0; i<24; i++)

{

tlc.setPWM(i, 0);

tlc.write();

delay(1000);

}

Serial.println("Accensione led");


// ****************************************

//NB NB NB NB

// per testare due schede connesse usate 47 anzichè 24

// per testare tre schede connesse usate 71 anzichè 47

// e così via.

//Se però avete già verificato la funzionalità di ciascuna scheda

// direi che è sufficiente verificare la connessione a catena di due schede TLC5947

// impostando, in tal caso il valore i<24 ad i<47

// ********************************************************

for(int i=0; i<24; i++)

{

tlc.setPWM(i, 4095);

tlc.write();

delay(1000);

}

}

Ed infine ecco il listato dello sketch completo della gestione del progetto.

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

/*

Designed by Gianni Barbotti (ITALY) July 15 2018

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

revisione 11/04/2019

ultima revisione 04/05/2019

N.B.: gli indirizzi dei servo sono bloccati dal 100 al 196 per permettere

la memorizzazione su scheda SD della posizione dei deviatoi e di conseguenza dei led da accendere o tenere spenti in funzione di questa.

Lo sketch è stato scritto consultando i contenuti dei link indicati ed i dispositivi hardware qui sotto indicati.

1) le librerie NMRAdcc, Adafruit_PWMServoDriver; Adafruit_TLC5947,SD;

2) lo sketch di Luca Dentella pubblicato sul suo sito

3) DCC Command Station Lenz LZV100 + Lenz LH100 ( opzionali: + Lenz LHI101F + LA152) e Digitrax DCS 100

-lo sketch funziona con tutte le centraline DCC compatibili col protocollo

NMRA (Xpressnet e Loconet).

4) adattatore seriale-Usb per connettere LHI101F al PC sulla porta USB (opzionale se non usate TCO o altro software)

6) la scheda DCC Shield per Arduino Uno e Mega 2560 progettata da Luca Dentella,

il quale me l'ha regalata , spedita gratuitamente e che ringrazio nuovamente.

5) 2 x Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685

6) 2 x 24-Channel 12-bit PWM/Led Driver - I2C interface - TCL5947

7) Inoltre lo sketch è stato provato utilizzando, in sostituzione della shield di Luca Dentella,

l'interfaccia DCC che ho acquistato ordinandola in rete (digitate dccinterface e troverete il sito)

Entrambe funzionano magnificamente. Il vantaggio dato dall'utilizzo dell'interfaccia

progettata da Luca Dentella è che è una shield per Arduino Uno o Mega, quindi la monti sull'Arduino

e la colleghi al tracciato usando il connettore a due poli della shield (DCC) anzichè cinque.

NOTA: Questo sketch gestisce per ogni deviatoio quattro led con questa logica:

a) ad ogni posizione del deviatoio sono associati due led: uno verde e l'altro rosso;

b) quando il deviatoio è posizionato in tracciato corretto i due led associati a

tale posizione risulteranno: LED VERDE ACCESO, LED ROSSO SPENTO; mentre i due

led associati alla posizione tracciato deviato risulteranno: LED VERDE SPENTO, LED ROSSO ACCESO.


c) quando il deviatoio è posizionato in tracciato deviato i due led associati a

tale posizione risulteranno: LED VERDE ACCESO, LED ROSSO SPENTO; mentre i due

led associati alla posizione tracciato corretto risulteranno: LED VERDE SPENTO, LED ROSSO ACCESO.


d) ai quattro led , nello sketch, ci si riferisce rispettivamente con le variabili

LED e LED + 1 per gestire l'accensione/spegnimento dei led per la posizione tracciato corretto

LED + 2 e LED + 3 per gestire l'accensione/spegnimento dei led per la posizione tracciato deviato


e) poiché per ogni deviatoio si gestiscono quattro led, la gestione delle sei schede per servo che gestiscono 96 deviatoi necessitano di 96 x 4 connessioni per i led, cioè 384, ovvero 16 schede 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947 . Non è necessario installare tutte le schede subito. Si può usare questo sketch partendo con una sola scheda Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685 e 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947 . Infatti basta ricordare che per ogni deviatoio, e quindi servo, connesso occorrono 4 connessioni per i LED su una scheda 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947 .

1 Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685 -> 2 schede 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947

2 Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685 -> 4 schede 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947

3 Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685 -> 8 schede 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947

4 Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685 -> 11 schede 24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947

e così via. Vedere quanto già pubblicato nelle tabelle al termine della prima parte.

(*) Per "opzionale" si intende che lo sketch non ha bisogno di questi elementi, ma sono però necessari se si vuol usare un software di controllo del tracciato col PC.

*/

// *** 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 = 53; // Pin CS = 10 con Arduino UNO e pin 53 con Arduino MEGA 2560

int j;

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

File file;

// DEfinizione Libreria e parametri per scheda Led TCL5947

#include <Adafruit_TLC5947.h>

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

// - SPI Interface - TLC5947 connesse ad Arduino e tra loro

// Modificare il valore 2 in base al numero effettivo di schede 24-Channel 12-bit PWM/LED

// che sono connesse ad Arduino

#include "Adafruit_TLC5947.h"

#define data 17 // Arduino UNO 4

#define clock 18 // Arduino UNO 5

#define latch 19 // Arduino 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);


int max_lux = 3000;

int min_lux = 0; // Valore minimo luminosità LED (0 = spento ; 4096 luminosità massima)

int passo_ON = 25; // Step con cui si incrementa/decrementa luminosità dei Led per

// l'effetto di brillantezza sfumata (FADE) in fase di accensione

int passo_OFF = 15; // Step con cui si incrementa/decrementa luminosità dei Led per

// l'effetto di brillantezza sfumata (FADE) in fase di spegnimento

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

// della variazione di stato di un deviatoio.


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

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

Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40); // to manage servos from 1° to the 16°(addresses 100-115)

Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41); // to manage servos from 1° to the 16°(addresses 116-131)

Adafruit_PWMServoDriver pwm3 = Adafruit_PWMServoDriver(0x42); // to manage servos from 1° to the 16°(addresses 132-147)

Adafruit_PWMServoDriver pwm4 = Adafruit_PWMServoDriver(0x43); // to manage servos from 1° to the 16°(addresses 148-163)

Adafruit_PWMServoDriver pwm5 = Adafruit_PWMServoDriver(0x44); // to manage servos from 1° to the 16°(addresses 164-179)

Adafruit_PWMServoDriver pwm6 = Adafruit_PWMServoDriver(0x45); // to manage servos from 1° to the 16°(addresses 180-195)

/* 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 = 100;

int end_address = 195;

#define ritardo 15

#define L_ritardo 10

#define min_servo 100

#define max_servo 195

#define SERVOMIN 200

#define SERVOMAX 280

#define i_max_servo 96


char status_servo[i_max_servo];

#define num_ch_srv 16

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

int servonum;

int servo;

int scheda;

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

{

// Serial.println("notify");

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

int outputInPair = OutputAddr & 0x01;

servo = abs(Addr - start_address);

a_Led = 4 * servo;

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("perform action");

int scheda;

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

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

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

Serial.print(Addr, DEC);

Serial.print(" scheda= ");

Serial.print(scheda, DEC);

Serial.print(" servo = ");

Serial.print(servo, DEC);

Serial.print(" servonum= ");

Serial.print(servonum, DEC);

Serial.print(" Addr a_Led V= ");

Serial.print(a_Led, DEC);

Serial.print(" Addr _Led + 1 R = ");

Serial.print(a_Led +1, DEC);

Serial.print(" Addr a_Led + 2 V = ");

Serial.print(a_Led +2, DEC);

Serial.print(" Addr a_Led + 3 R = ");

Serial.print(a_Led +3, DEC);

Serial.print(" outputInPair= ");

Serial.println(outputInPair);

if ((start_address <= Addr <= end_address) && (outputInPair == 1))

{

if ((status_servo[servo] == char(48)) || (status_servo[servo] == char(50)))

{

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;

}

tlc.setPWM(a_Led, 0); // spengo Led Verde tracciato corretto

tlc.write();

tlc.setPWM(a_Led + 2, 4000); // accendo Led Verde tracciato deviato

tlc.write();

tlc.setPWM(a_Led + 1, 4000); // accendo Led Rosso Tracciato corretto

tlc.write();

tlc.setPWM(a_Led + 3, 0); // spengo Led Rosso tracciato deviato

tlc.write();

}

}

if ((start_address <= Addr <= end_address) && (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);

}

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(a_Led, 4000);

tlc.write();

tlc.setPWM(a_Led + 2, 0);

tlc.setPWM(a_Led + 1, 0);

tlc.write();

tlc.setPWM(a_Led + 3, 4000);

tlc.write();

}

}

}


void setup()

{

Serial.begin(9600);

pinMode(CS_pin, OUTPUT);

initializeSD();

openFile(logfile);

while (file.available())

{

readLine();

}

closeFile();

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();

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);

}


void loop()

{

Dcc.process();

}

String readLine()

{

int i;

j = 0;

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("SD card non disponibile. USCITA!!");

return;

}

}


void remove_file()

{

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

{

}

else

{

Serial.println("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 = 0; j <= i_max_servo; j++)

{

file.print(status_servo[j]);

}

return 1;

}

else

{

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

return 0;

}

}



int default_status(char filename[])

{

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

if (file)

{

j = 0;

for (j = 0; j <= i_max_servo; j++)

{

file.print(char(50));

status_servo[j] = char(50);

}

file.close();

}

else

{

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


return;

}

}

int CHECK_SERVO()

{

Serial.println("Dentro CHECK_SERVO");

a_Led = 4 * servo;

j = 0;

do

{

a_Led = 4 * j;

scheda = (j / num_ch_srv) + 1;

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

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

{

tlc.setPWM(a_Led, 4000);

tlc.write();

tlc.setPWM(a_Led + 2, 0);

tlc.write();

tlc.setPWM(a_Led + 1, 0);

tlc.write();

tlc.setPWM(a_Led + 3, 4000);

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(a_Led, 0);

tlc.write();

tlc.setPWM(a_Led + 2, 4000);

tlc.write();

tlc.setPWM(a_Led + 1, 4000);

tlc.write();

tlc.setPWM(a_Led + 3, 0);

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("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 = 0;

for (j = 0; j <= i_max_servo; j++)

{

file.print(status_servo[j]);

}

file.close();

}

else

{

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

return;

}

}

void closeFile()

{

if (file)

{

// Serial.println("dentro closefile");

file.close();

// Serial.println("File closed");

}

}