L’aticolo è stato aggiornato in data 2016/Ottobre/12 dopo la sua pubblicazione iniziale.
Premessa:
Questo progetto si basa su un articolo che ho trovato in rete scritto dal Sig. David G. Bodnar e spiega come realizzare una piccola centrale DCC con Arduino e un telecomando a raggi infrarossi.
L’articolo originale può essere consultato a questo link e fa parte del sito Train electronics.
Il progetto elencato di seguito non vuole essere un semplice copia e incolla dell’articolo originale e nemmeno una traduzione dello stesso. Ho voluto realizzare il suo progetto per curiosità e per passione.
Alcuni aspetti di questo lavoro trattano anche dispositivi che non hanno una apparente attinenza con il DCC, come i sensori a raggi infrarossi e i display LCD.
Tuttavia, tali dispositivi sono usati in questo progetto e mi è sembrato opportuno parlare del loro funzionamento tecnico con l’uso Arduino cogliendo l’occasione di spiegare, nel limite del possibile come usarli.
Ringrazio personalmente Dave per il suo lavoro e per avermi permesso di divulgarlo qui.
Thanks Dave!
Dave ha realizzato anche una centrale DCC un po’ più complessa di quella mostrata nell’articolo e che è possibile vedere a questo link del suo sito: A Hight Power DCC Option che ha chiamato DCC++ e che mi auguro di poter realizzare in futuro. Questo progetto è la logica evoluzione di quello presentato qui e comprende la possibilità di gestire molte locomotive (fino a 40 Ampere di corrente contro i 3 di questo progetto), di includere il supporto vocale per la ripetizione dei comandi DCC e di poter essere usato con altri software.
Il progetto attuale
Questo sistema usa un telecomando a raggi infrarossi per inviare comandi ad una centrale DCC realizzata con Arduino Uno ed è in grado di visualizzare tutte le sue funzioni su un display LCD da 16 caratteri x 2 linee. E’ un progetto relativamente semplice che consente di far circolare un massimo di 2 o 3 locomotive contemporaneamente e le funzioni DCC sono molto limitate.
Il circuito elettrico è composto da 4 sezioni distinte che possono essere identificate in:
– sensore IR (infrared o dispositivo di ricezione a raggi infrarossi a 38KHz). Sensore TSOP4838;
– telecomando IR (qualsiasi telecomando, anche quello comunemente usato per la TV di casa);
– display LCD da 16 caratteri x 2 linee tipo TWI-1602 con interfaccia seriale I2C;
– circuito integrato LMD 18200; un controller per motori in CC.
Infine, il software che ci permetterà di gestire tutti i dispositivi elencati sopra ed una scheda Arduino Uno.
Tale software è completo di librerie per la gestione del sensore IR, del display LCD e dei pacchetti DCC usati in questa centrale.
Vedremo in seguto come scaricare nell’IDE di Arduino tali librerie. Alcune sono già presenti di defalult e vengono usate in modo trasparente, mentre altre, per essere usate vanno prima installate manualmente.
Iniziamo con la gestione del telecomando a raggi infrarossi.
Abbiamo detto che nel progetto viene utilizzato un sensore come ricevitore dei comandi inviati da un telecomando IR chiamato TSOP 4838.
Tale sensore è in grado di leggere praticamente i codici di tutti i tipi di telecomandi, anche quelli comunemente usati per la tv o per i dispositivi multimediali come lettori DVD, BluRay o videoregistratori e di visualizzare tali codici sullo schermo del nostro pc..
Questa operazione è necessaria, in quanto ci permette di leggere i codici dei tasti di un telecomando IR, di trascriverli e successivamente di memorizzarli nel programma che gestirà la centrale DCC.
Può sembrare complicato, ma vedremo che in realtà è molto semplice, basta collegare il sensore IR ad Arduino e usare un software che restituisce sul monitor seriale i codici dei tasti del telecomando.
Tali codici verranno visualizzati in numerazione esadecimale.
Lo schema elettrico prevede il collegamento del sensore ad una scheda Arduino Uno tramite tre fili conduttori.
Il sensore ha 3 piedini così definiti:
– pin1 OUTPUT, da collegare alla porta digitale numero 11 di Arduino per la lettura dei codici tasti del telecomando. Tale porta viene configurata via software come INPUT digitale;
– pin2 GND, polo negativo di alimentazione del sensore da collegare al pin GND di Arduino;
– pin3 +5Vcc, polo positivo di alimentazione del sensore da collegare al pin 5V di Arduino.
Dopo aver effettuato i collegamenti richiesti (una comune bredboard per iniziare va benissimo), possiamo scrivere il programma e caricarlo su Arduino. Il programma si chiama “IR_fincode” e usa la libreria “IRemote.h”.
Programma lettura codici IR “IR_fincode”
#include <IRremote.h> // use the library int receiver = 11; // pin 1 of IR receiver to Arduino digital pin 11 IRrecv irrecv(receiver); // create instance of 'irrecv' decode_results results; void setup() { Serial.begin(9600); // for serial monitor output irrecv.enableIRIn(); // Start the receiver pinMode(9, OUTPUT); // Pin 9 output } void loop() { if (irrecv.decode(&results)) // have we received an IR signal? { Serial.println(results.value, HEX); // display it on serial monitor in hexadecimal irrecv.resume();// receive the next value } } |
La libreria “IRemote” può essere scaricata qui.
Lanciamo il programma e apriamo il monitor seriale di Arduino per leggere i codici dei tasti telecomando; basta puntare il telecomando verso il sensore e iniziare a premere i tasti.
Quello che verrà visualizzato sul monitor seriale saranno i codici in formato esadecimale da trascrivere su un foglio per utilizzarli in futuro nel programma vero e proprio della centrale DCC.
In questo modo avremo ottenuto una mappatura dei tasti del nostro telecomando.
Nota: la porta seriale va impostata con un baud rate di 9600.
In questa foto è possibile vedere il tipo di telecomando che ho usato nel progetto e un foglio con gli appunti dei codici tasto esadecimali rilevati dal software “IR_findcode”.
Fatta questa prima operazione vediamo come gestire il display lcd con Arduino.
Come citato sopra, il display usato in questo progetto è del tipo con interfaccia seriale I2C; un particolare tipo di interfaccia seriale che consente la comunicazione dei dati tra dispositivi.
In questa foto è possibile vedere la parte posteriore del display e a destra i collegamenti da effettuare che sono 4:
– GND, polo negativo di alimentazione;
– VCC, polo positivo di alimentazione (5volt);
– SDA, Serial DAta per il trasferimento dei dati;
– SCL, Serial CLock per la sincronizzazione.
Chi volesse approfondire l’argomento I2C può consultare questo wiki.
Collegamenti verso Arduino:
Anche qui è possibile usare una bredboard per fare qualche test, quindi colleghiamo i 4 fili del display ad Arduino nel modo seguente:
– GND al pin GND di Arduino;
– VCC al pin 5V di Arduino;
– SDA al pin A4 di Arduino (ingresso analogico A4);
– SCL al pin A5 di Arduino (ingresso analogico A5).
Schema elettrico di collegamento di un display I2C con Arduino:
Lo schema visualizza il sensore IR e il display LDC I2C collegati ad Arduino Uno
Ora possiamo provare il display, ma prima dobbiamo trovare l’indirizzo di memoria dove il display è allocato. Questo è necessario per poter gestire il display stesso. Un po’ come abbiamo fatto per leggere i codici tasti del telecomando IR.
Tale indirizzo può essere diverso da display a display e per trovarlo basta usare il programma seguente.
I2C_scanner
// -------------------------------------- // i2c_scanner // // Version 1 // This program (or code that looks like it) // can be found in many places. // For example on the Arduino.cc forum. // The original author is not know. // Version 2, Juni 2012, Using Arduino 1.0.1 // Adapted to be as simple as possible by Arduino.cc user Krodal // Version 3, Feb 26 2013 // V3 by louarnold // Version 4, March 3, 2013, Using Arduino 1.0.3 // by Arduino.cc user Krodal. // Changes by louarnold removed. // Scanning addresses changed from 0...127 to 1...119, // according to the i2c scanner by Nick Gammon // http://www.gammon.com.au/forum/?id=10896 // Version 5, March 28, 2013 // As version 4, but address scans now to 127. // A sensor seems to use address 120. // // // This sketch tests the standard 7-bit addresses // Devices with higher bit address might not be seen properly. // #include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); Serial.println("\nI2C Scanner"); } void loop() { byte error, address; int nDevices; Serial.println("Scanning..."); nDevices = 0; for(address = 1; address < 127; address++ ) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire.beginTransmission(address); error = Wire.endTransmission(); if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !"); nDevices++; } else if (error==4) { Serial.print("Unknow error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); } } if (nDevices == 0) Serial.println("No I2C devices found\n"); else Serial.println("done\n"); delay(5000); // wait 5 seconds for next scan } |
Il risultato dell’output del programma è visibile nel monitor seriale (9600 baud) che nel mio caso riporta un indirizzo 0x3F del display.
Tale valore è molto importante e può cambiare in base al display LCD utilizzato. Va annotato per inserirlo successivamente nel software della centrale DCC.
Ora possiamo fare qualche test per visualizzare dei caratteri sul display utilizzando un’altro programma e la libreria “LiquidCrystal_I2C.h”.
Il programma si chiama “New_liquid:I2c”:
New_liquid:I2c
/* YourDuino.com Example Software Sketch 16 character 2 line I2C Display Backpack Interface labelled "A0 A1 A2" at lower right. ..and Backpack Interface labelled "YwRobot Arduino LCM1602 IIC V1" MOST use address 0x27, a FEW use 0x3F [email protected] */ /*-----( Import needed libraries )-----*/ #include <Wire.h> // Comes with Arduino IDE // Get the LCD I2C Library here: // https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads // Move any other LCD libraries to another folder or delete them // See Library "Docs" folder for possible commands etc. #include <LiquidCrystal_I2C.h> /*-----( Declare Constants )-----*/ /*-----( Declare objects )-----*/ // set the LCD address to 0x27 for a 16 chars 2 line display // A FEW use address 0x3F // Set the pins on the I2C chip used for LCD connections: // addr, en,rw,rs,d4,d5,d6,d7,bl,blpol LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address /*-----( Declare Variables )-----*/ //NONE void setup() /*----( SETUP: RUNS ONCE )----*/ { Serial.begin(9600); // Used to type in characters lcd.begin(16,2); // initialize the lcd for 16 chars 2 lines, turn on backlight // ------- Quick 3 blinks of backlight ------------- for(int i = 0; i< 3; i++) { lcd.backlight(); delay(250); lcd.noBacklight(); delay(250); } lcd.backlight(); // finish with backlight on //-------- Write characters on the display ------------------ // NOTE: Cursor Position: (CHAR, LINE) start at 0 lcd.setCursor(0,0); //Start at character 4 on line 0 lcd.print("Hello, world!"); delay(1000); lcd.setCursor(0,1); lcd.print("HI!YourDuino.com"); delay(8000); // Wait and then tell user they can start the Serial Monitor and type in characters to // Display. (Set Serial Monitor option to "No Line Ending") lcd.clear(); lcd.setCursor(0,0); //Start at character 0 on line 0 lcd.print("Use Serial Mon"); lcd.setCursor(0,1); lcd.print("Type to display"); }/*--(end setup )---*/ void loop() /*----( LOOP: RUNS CONSTANTLY )----*/ { { // when characters arrive over the serial port... if (Serial.available()) { // wait a bit for the entire message to arrive delay(100); // clear the screen lcd.clear(); // read all the available characters while (Serial.available() > 0) { // display each character to the LCD lcd.write(Serial.read()); } } } }/* --(end main loop )-- */ /* ( THE END ) */ |
La libreria LiquidCrystal_I2C.h va scaricata da qui.
Questo programma effettua un test sul display lcd facendo lampeggiare per 3 volte la retroilluminazione del display e ci consente di visualizzare un testo digitato da tastiera.
Fare riferimento alla sezione Come installare una libreria nell’IDE di Arduino del sito per installare correttamente una libreria. (sezione in costruzione futura)..
Se tutto funziona correttamente dovreste essere in grado di visualizzare sul display lcd un testo scritto tramite la tastiera del vostro pc.
Cicuito di controllo della centrale DCC:
Qui possiamo vedere lo schema elettrico definitivo del progetto.
Oltre ai controlli del sensore IR e del display, il circuito viene competato dall’integrato LMD 18200.
Tramite l’integrato LMD 18200 gestiamo anche tutti i comandi DCC per il controllo delle locomotive.
Qui una foto dello shield montato:
E qui lo shield visto da sotto:
Le uscite digitali D8 e D9 di Arduino, sono collegate rispettivamente al pin 4 (BREAK) e al pin 3 (DIRECTION) dell’integrato LMD 18200 per la trasmissione dei pacchetti DCC.
Altri collegamenti dei pin dell’integrato LMD 18200 sono:
- pin 5 (PWM) collegato a +5V di Arduino;
- pin 7 (GND) collegato a GND di Arduino;
- pin 6 (VS) alimentazione a 15 VCC;
- pin 9 (THERMAL) diodo led per la segnalazione di temperatura elevata;
- pin 8 (SENSE) uscita predisposta per usi futuri (PAD 1 e PAD 2);
- pin 2 (OUTPUT 1) uscita verso le rotaie;
- pin 10 (OUTPUT 2) uscita verso le rotaie;
- pin 1 (BOOTSTRAP 1) verso C1 e pin OUTPUT 1;
- pin 11 (BOOTSTRAP 2) verso C2 e pin OUTPUT 2.
Al pin 7 (GND) vi sono 2 pad per realizzare un ponticello tramite uno spezzone di filo elettrico che dovrà avere una sezione di almeno 1,5 mmq. Il ponticello si è reso necessario in quanto il circuito stampato è di tipo monofaccia e non è stato possibile realizzare tale collegamento sul lato rame dello stesso.
Importante: l’integrato LM 18200 è alimentato con una tensione in corrente continua di 15 Volt tramite un alimentatore in grado di erogare un minimo di 3 Ampere. Il polo negativo di tale alimentatore deve essere collegato al polo negativo di Arduino per avere un corretto funzionamento del circuito. Un alimentatore per PC portatili va più che bene per tale scopo e si trova facilmente in qualsiasi negozio di elettronica o nei supermercati.
Circuito Stampato:
Elenco componenti:
- R1, R2 = 2700 ohm 1/4 watt;
- C1, C2 = 10000 picoFarad ceramico;
- LED1 = led 5mm rosso;
- IC1 = integrato LMD 18200;
- X1, X2 = connettore AK500 a 2 poli (5mm, fissaggio cavi a vite);
- SV1 = pin strip femmina a 4 poli (passo 2,54mm), collegamento display;
- SV2 = pin strip femmina a 3 poli (passo 2,54mm), collegamento sensore IR;
- SV3, SV4 = pin strip femmina a 6 poli (passo 2,54mm per Arduino);
- SV5, SV6 = pin strip femmina a 8 poli (passo 2,54mm per Arduino).
Note: LED1 può essere montato sul pannello frontale del mobiletto che andrà collegato tramite una coppia di fili a un pin strip da 2 poli saldato sulla scheda. Il led 13 (D13) presente sulla scheda Arduino lampeggia ogni 2 secondi per indicare l’attività del circuito; se si dedidera è possibile montare un led giallo sul pannello frontale collegato al pin D13 e GND di Arduino tramite una resistenza da 270 ohm 1/4 di watt.
Il circuito stampato è di tipo monofaccia e contiene tutti i componenti necessari per realizzare lo shield, inoltre è predisposto per il classico montaggio a “sandwitch” con una normale scheda Arduino Uno tramite delle comuni strip. Permette anche una comoda installazione di un piccolo radiatore per favorire lo smaltimento di calore dell’integrato LMD 18200.
Software DCC controller:
Di seguito è possibile vedere il software utilizzato per questa centrale che andrà modificato con i codici telecomando rilevati e con il corretto indirizzo del display lcd come indicato nell’articolo.
Note: per programmare Arduino con questo software si consiglia vivamente di usare l’IDE versione 1.6.5 scaricabile da qui.
Le librerie DCC necessarie per questo software possono essere scaricate dal repository github in un unico file zip.
/* d. bodnar 9-23-2014 Uses 2 line LCD for display Uses IR remote control for throttle & functions (functions not working well yet) Uses CmdArduino library for DCC output UP = faster - 13 - hold to repeat DN = slower - 17 -hold to repeat * = STOP - 11 # = Disable / enable DCC (pin 8) -12 < = - 14 > = -16 OK = Menu choices -15 */ // the following defines the codes for Keyes brand Sony and Nec IR remote controls //#define KeyesUp 0xFF629D #define SonyUp 0x90 #define NecUp 0xFFA857 //#define KeyesLeft 0xFF22DD #define SonyLeft 0xC90 #define NecLeft 0xFF22DD //#define KeyesOK 0xFF02FD #define SonyMenu 0x70 #define NecEQ 0xFF906F //#define KeyesRight 0xFFC23D #define SonyRight 0x490 #define NecRight 0xFF02FD //#define KeyesDown 0xFFA857 #define SonyDown 0x890 #define NecDown 0xFFE01F //#define Keyes1 0xFF6897 #define Sony1 0x10 #define Nec1 0xFF30CF //#define Keyes2 0xFF9867 #define Sony2 0x810 #define Nec2 0xFF18E7 //#define Keyes3 0xFFB04F #define Sony3 0x410 #define Nec3 0xFF7A85 //#define Keyes4 0xFF30CF #define Sony4 0xC10 #define Nec4 0xFF10EF //#define Keyes5 0xFF18E7 #define Sony5 0x210 #define Nec5 0xFF38C7 //#define Keyes6 0xFF7A85 #define Sony6 0xA10 #define Nec6 0xFF5AA5 //#define Keyes7 0xFF10EF #define Sony7 0x610 #define Nec7 0xFF42BD //#define Keyes8 0xFF38C7 #define Sony8 0xE10 #define Nec8 0xFF4Ab5 //#define Keyes9 0xFF5AA5 #define Sony9 0x110 #define Nec9 0xFF52AD //#define Keyes0 0xFF4AB5 #define Sony0 0x910 #define Nec0 0xFF6897 //#define KeyesStar 0xFF42BD #define SonyExit 0xC70 #define NecExit 0xFFC23D //#define KeyesPound 0xFF52AD #define SonyPower 0xA90 #define NecCh 0xFF629D #include<EEPROM.h> int irButton; int LED = 13; // LED to blink when DCC packets are sent in loop #include <IRremote.h> int RECV_PIN = 11; int Enable_PIN = 8; //low to enable DCC, high to stop IRrecv irrecv(RECV_PIN); decode_results results; #include <DCCPacket.h> #include <DCCPacketQueue.h> #include <DCCPacketScheduler.h> DCCPacketScheduler dps; //unsigned int analog_value=0; char speed_byte, old_speed = 0; char temp; byte Fx = 0; byte DCCAddress = 3; int irCode = 0; int inMenu = 0; // keeps track of being in the menu area 0=out, 1=in int digits = 0; int upFlag = 0; // trying to get keys to repeat! int dnFlag = 0; #include <Wire.h> #include <LCD.h> #include <LiquidCrystal_I2C.h> #define I2C_ADDR 0x3F // <<----- Add your address here. Find it from I2C Scanner #define BACKLIGHT_PIN 3 #define En_pin 2 #define Rw_pin 1 #define Rs_pin 0 #define D4_pin 4 #define D5_pin 5 #define D6_pin 6 #define D7_pin 7 byte fn0to4 = 0; // DCC function variables byte fn5to8 = 0; byte fn9to12 = 0; LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin); unsigned long previousMillis = 0; // last time update long interval = 2000; // interval at which to do refresh (milliseconds) void setup() { pinMode(LED, OUTPUT); DCCAddress = EEPROM.read(0); if(DCCAddress >=100){ // set defalut as 3 if not in proper range (0-99) DCCAddress = 3; } pinMode(Enable_PIN, OUTPUT); lcd.begin (16,2); // LCD is 16 characters x 2 lines lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); lcd.setBacklight(HIGH); // Switch on the backlight lcd.home (); // go home Serial.begin(115200); lcd.setCursor(0,0); lcd.print("Version 2.7 "); lcd.setCursor(0,1); lcd.print("9-23-2014 "); delay(1500); lcd.clear(); irrecv.enableIRIn(); // Start the receiver dps.setup(); dps.setFunctions0to4(DCCAddress,DCC_SHORT_ADDRESS, B00000000); //clear functions dps.setFunctions5to8(DCCAddress,DCC_SHORT_ADDRESS, B00000000); } void loop() { //this section sends DCC updates every 2 seconds (interval) // not sure if it is necessary but the functions are slow to respond // at times - may be due to the DCC library setting priorities as the // speed controls always work unsigned long currentMillis = millis(); if(currentMillis - previousMillis > interval) { previousMillis = currentMillis; dps.setSpeed128(DCCAddress,DCC_SHORT_ADDRESS,speed_byte); dps.setFunctions0to4(DCCAddress,DCC_SHORT_ADDRESS, fn0to4); //clear all functions dps.setFunctions5to8(DCCAddress,DCC_SHORT_ADDRESS, fn5to8); digitalWrite(LED, !digitalRead(LED)); } digitalWrite(Enable_PIN, LOW);// HIGH = disable DCC lcd.setCursor(0,0); lcd.print("Speed="); lcd.print(speed_byte, DEC); lcd.print(" "); lcd.setCursor(11,0); lcd.print("ad="); if(DCCAddress <=9){ lcd.print("0"); // add leading zero for single digit addresses } lcd.print(DCCAddress, DEC); lcd.setCursor (14,1); // go to end of 2nd line // lcd.print("IR code "); lcd.print(irButton, DEC); lcd.print(" "); lcd.setCursor(5,1);// start of 2nd line // pad with leading zeros String temp = "0000" + String(fn0to4,BIN); int tlen= temp.length() - 5; lcd.print(temp.substring(tlen)); temp = "000" + String(fn5to8,BIN); tlen= temp.length() - 4; lcd.setCursor(0,1);// start of 2nd line lcd.print(temp.substring(tlen)); if (irrecv.decode(&results)) { translateIR(); Serial.println(irButton,DEC); irrecv.resume(); // Receive the next value } byte button_state = digitalRead(4); //high == not pushed; low == pushed if ((irButton >0 && irButton <13) | (irButton ==14 | irButton == 16)){ upFlag=0; dnFlag=0; ////MAY NEED TO PUT THIS ON EACH KEY } if(irButton >=1 && irButton <=10){ //Funtions done with numbers 1-9 - clear all with 10 doFunction(); irButton=0; } // MENU SECTION - stays here to get DCC address if (irButton==15 ) //OK key for menu choices { Serial.println("found OK (menu)"); inMenu=1; lcd.clear(); // blank screen lcd.setCursor(0,0); lcd.print("MENU"); lcd.setCursor(0,1); lcd.print("Ent 2 digit add"); for (int i =0; i = 1; i++){ // do twice if (irrecv.decode(&results)) { translateIR(); irrecv.resume(); // Receive the next value } if (irButton == 15 && inMenu == 0){ Serial.print("BREAK OUT "); Serial.println(inMenu, DEC); inMenu=0; irButton=0; digits=0; lcd.clear(); break; } if (irButton >=1 && irButton <=10) { if (digits==1){ DCCAddress = DCCAddress * 10; Serial.print("x10 address "); Serial.println(DCCAddress, DEC); if(irButton != 10){ // only add it not zero (zero button or remote returns a 10) DCCAddress = DCCAddress + irButton; } lcd.setCursor(0,1); if(DCCAddress <=9){ lcd.print("0"); // add leading zero for single digit addresses } lcd.print(DCCAddress, DEC); lcd.setCursor(0,0); lcd.print("OK to Exit Menu:"); digits = 3; inMenu=0; EEPROM.write(0,DCCAddress); } if (digits ==0){ DCCAddress = irButton; if (DCCAddress==10){ DCCAddress = 0; // "0" button returns 10 so make it zero } Serial.print("dig 1 address "); Serial.println(DCCAddress, DEC); lcd.clear(); // blank screen lcd.setCursor(0,0); lcd.print("New Address"); lcd.setCursor(0,1); lcd.print(DCCAddress, DEC); digits = 1; delay(500); irButton=0; } Serial.print("new address "); Serial.println(DCCAddress, DEC); } irButton=0; } irButton=0; } //END MENU SECTION if (irButton==13 | (irButton==99 && upFlag==1)) // repeat key (99) { Serial.println("found UP"); speed_byte++; upFlag=1; dnFlag=0; irButton=0; } if (irButton==16) { Serial.println("found UP "); speed_byte++; irButton=0; } if (irButton ==17 | (irButton==99 && dnFlag==1)) { Serial.println("found DN"); speed_byte--; dnFlag=1; upFlag = 0; irButton=0; } if (irButton ==14) { Serial.println("found DN "); speed_byte--; irButton=0; } if (irButton==11) //* key - does STOP { Serial.println("found ***"); speed_byte=0; irButton=0; } //Functions will not work without this to limit speed command to only new speeds if(speed_byte != old_speed) { speed_byte = constrain(speed_byte, -127, 127); dps.setSpeed128(DCCAddress,DCC_SHORT_ADDRESS,speed_byte); old_speed = speed_byte; } dps.update(); } //END LOOP int translateIR() // takes action based on IR code received // describing KEYES Remote IR codes (first) and Sony IR codes (second) { Serial.println(results.value, HEX); switch(results.value) { //case KeyesUp: //Keyes remote code for UP case SonyUp: //Sony remote code for UP case NecUp: //Nec remote code for UP Serial.println(" UP"); irButton = 13; break; //case KeyesLeft: case SonyLeft: case NecLeft: Serial.println(" LEFT"); irButton = 14; break; //case KeyesOK: case SonyMenu: case NecEQ: Serial.println(" -MENU-"); irButton = 15; break; //case KeyesRight: case SonyRight: case NecRight: Serial.println(" RIGHT"); irButton = 16; break; //case KeyesDown: case SonyDown: case NecDown: Serial.println(" DOWN"); irButton = 17; break; //case Keyes1: case Sony1: case Nec1: Serial.println(" 1"); irButton = 1; break; //case Keyes2: case Sony2: case Nec2: Serial.println(" 2"); irButton = 2; break; //case Keyes3: case Sony3: case Nec3: Serial.println(" 3"); irButton = 3; break; //case Keyes4: case Sony4: case Nec4: Serial.println(" 4"); irButton = 4; break; //case Keyes5: case Sony5: case Nec5: Serial.println(" 5"); irButton = 5; break; //case Keyes6: case Sony6: case Nec6: Serial.println(" 6"); irButton = 6; break; //case Keyes7: case Sony7: case Nec7: Serial.println(" 7"); irButton = 7; break; //case Keyes8: case Sony8: case Nec8: Serial.println(" 8"); irButton = 8; break; //case Keyes9: case Sony9: case Nec9: Serial.println(" 9"); irButton = 9; break; //case Keyes0: case Sony0: case Nec0: Serial.println(" 0"); irButton = 10; break; //case KeyesStar: case NecExit: Serial.println(" *"); irButton = 11; break; case SonyExit: //EXIT Serial.println(" *"); irButton = 11; break; //case KeyesPound: case NecCh: Serial.println(" #"); irButton = 12; break; case SonyPower: //POWER Serial.println(" #"); irButton = 12; break; case 0xFFFFFFFF: Serial.println(" REPEAT"); irButton = 99; break; default: Serial.println(" other button "); irButton = 99; }// End Case delay(100); // Do not get immediate repeat } //END translateIR //START DO FUNCTION BUTTONS int doFunction(){ int irTemp= irButton-1; lcd.setCursor (14,1); // go to end of 2nd line /// lcd.print("FN code "); lcd.print(irButton, DEC); Serial.print("got a keypad button "); Serial.println(irButton,DEC); if (irTemp<=4){ if(bitRead(fn0to4,irTemp)== 0 ){ bitWrite(fn0to4,irTemp,1); } else { if(bitRead(fn0to4,irTemp)== 1 ){ bitWrite(fn0to4,irTemp,0); } } dps.setFunctions0to4(DCCAddress,DCC_SHORT_ADDRESS,fn0to4); Serial.print(fn0to4,BIN); Serial.println(" fn0to4"); } if (irTemp>=5 && irTemp<=8){ irTemp=irTemp-5; if(bitRead(fn5to8,irTemp)== 0 ){ bitWrite(fn5to8,irTemp,1); } else { if(bitRead(fn5to8,irTemp)== 1 ){ bitWrite(fn5to8,irTemp,0); } } dps.setFunctions5to8(DCCAddress,DCC_SHORT_ADDRESS,fn5to8); Serial.print(fn5to8,BIN); Serial.println(" fn5to8"); } if (irButton == 10) { lcd.setCursor (14,1); // go to end of 2nd line /// lcd.print("FN code "); lcd.print(irButton, DEC); Serial.println("got a keypad button 0 (reads as 10)"); dps.setFunctions0to4(DCCAddress,DCC_SHORT_ADDRESS, B00000000); dps.setFunctions5to8(DCCAddress,DCC_SHORT_ADDRESS, B00000000); irButton = 0; fn0to4 = B00000000; //clear variables for which functions are set fn5to8 = B00000000; delay(500); irButton=0; } irButton = 0; delay(500); } //END DO FUNCTION BUTTONS |
Inserimento o modifica dei codici tasto del telecomando:
Il software consente di inserire i codici tasto per più titpi di telecomandi IR in modo da poterli usare contemporaneamente. Tuttavia, si consiglia di inserire solo quelli strettamente necessari del tipo di telecomando usato per evitare conflitti. In questo esempio sono stati inseriti i codici di tre telecomandi; Sony, Nec e Keyes.
I codici Sony e Keyes sono stati inseriti dall’autore Dave, mentre quelli Nec da me e sono compresi tra le linee 15 e 66 del listato nella seguente forma:
//#define KeyesUp 0xFF629D #define SonyUp 0x90 #define NecUp 0xFFA857 |
Qui sopra è possibile vedere un esempio per la definizione del tasto “UP” del telecomando che si trova alle linee 16, 17 e 18. Tutte le altre definizioni dei tasti fino alla linea 66 sono simili e si ripetono nello stesso modo.
La linea 16 //#define KeyesUP 0xFF629D è stata resa commento con una doppia “slash” posta all’inizio per disattivarla, in quanto ho notato che il telecomando Keyes ha codici simili a quello usato da me che è di tipo Nec e mi creava dei conflitti. Con lo stesso sistema (doppia “slash”) è possibile escludere i codici indesiderati senza cancellare le linee del programma.
La linea 17 definisce il tasto “UP” di un telecomando Sony avente codice 0x90.
La linea 18 definisce il tasto “UP” di un telecomando Nec avente codice 0xFFA857.
Il nome dopo #define, è scelto arbitrariamente dal programmatore per assegnarvi il codice esadecimale ricavato tramite il software “IR_findcode” visto sopra:
- KeyesUp per un telecomando Keyes;
- SonyUp per un telecomando Sony;
- NecUp per un telecomando Nec.
Completata la fase della definizione dei codici è necessario dire al programma quando usarli e per fare questo ci spostiamo alla linea 307 dove troviamo il comando switch(results.value)
Questo comando è seguito da una serie di linee comprese tra 308 e 424 che contengono a loro volta le istruzioni per far capire al programma quale tasto è stato premuto sul telecomando IR.
Anche in questo caso le linee si ripetono per tutti i tipi di tasti e vanno scritte nel seguente modo; linee da 309 a 314 del listato:
//case KeyesUp: //Keyes remote code for UP case SonyUp: //Sony remote code for UP case NecUp: //Nec remote code for UP Serial.println(" UP"); irButton = 13; break; |
Qui sopra è evidenziato sempre il tasto “UP” del telecomando e le possibili opzioni case per i tipi usati, cioè Keyes, Sony e Nec. Le istruzioni eseguite in “caso” di pressione del tasto “UP” dei telecomandi descritti sono:
Serial.println(” UP”); stampa sul monitor seriale di Arduino il testo ” UP” e torna a capo.
irButton = 13; assegna il numero decimale 13 alla variabile di sistema irButton per il funzionamento del programma.
break; riprendi la lettura dei codici telecomando avviati da switch.
Anche in questa routine, il telecomando tipo Keyes è stato escluso con la doppia “slash”.
Impostazione dell’indirizzo del display lcd I2C:
La linea del software preposta alla definizione di tale indirizzo è la 92.
#define I2C_ADDR 0x3F // <<----- Add your address here. Find it from I2C Scanner |
Sostituire il codice esadecimale 0x3F con quello rilevato tramite il software “I2C_scanner” visto sopra.
A questo punto è possibile caricare il programma su Arduino.
Nota: aprire il monitor seriale di Arduino e impostare il baud rate a 115200 per vedere correttamente l’output del programma.
Assemblaggio finale:
Il circuito è stato inserito in un pratico mobiletto in plastica con i pannelli fronale e posteriore in alluminio dalle dimensioni di 13x7x18 cm circa. L’alimentatore usato dispone anche di una uscita USB a 5 VCC comoda per alimentare Arduino; in alternativa usare un alimentaore separato da collegare alla presa 6-15 VCC.
Sul pannello posteriore ho montato la presa a 15VCC per l’alimentazione dell’integrato LMD 18200 e 2 boccole per spine a “banana” per collegare i binari alla centrale.
Sul pannello anteriore ho montato il display LCD, il sensore IR, i 2 led (Thermal warning e DCC send) e ho praticato le sedi per l’accesso alle prese USB e 6-15VCC per l’alimentazione di Arduino.
Istruzioni per l’uso della centrale:
Collegare 2 fili ai binari tramite l’uscita posteriore DCC OUT.
Alimentare Arduino tramite la presa frontale USB o la presa 6-15 VCC con un alimentatore stabilizzato. Arduino si accende e sul display compaiono una serie di caratteri come nella figura sottostante:
Collegare un alimentatore da 15 VCC sulla presa posteriore per alimentare l’integrato LMD 18200.
Usare il telecomando IR per inviare comandi alla centrale.
L’indirizzo di default per il controllo di una locomotova è 03.
Per modificare tale indirizzo usare il pulsante “Enter” che nel mio caso corrisponde al pulsanre “EQ” del mio telecomando.
Premendo “Enter”sul telecomando, il display visualizza un messaggio di richiesta per il cambio dell’indirizzo locomotiva (0-99). Usare i tasti numerici del telecomando per impostare un nuovo indirizzo. Premere nuovamente “Enter” per confermare la scelta.
Nota: L’ultimo indirizzo di locomotiva impostato viene memorizzato nella EEPROM di Arduino e verrà reso disponibile al successivo riavvio del sistema.
Regolazione della velocità di una locomotiva:
Usare i tasti + e – per aumentare o diminuire la velocità della locomotiva; i tasti + e – consentono di avere un incremento o decremento della velocità compreso tra -127 e +127 in modalità “autorepeat”, cioè tenendo premuto uno dei tasti + o – l’impostazione della velocità sarà progressiva.
Premendo invece i tasti >> e << del telecomando, la velocità della locomotiva potrà essere gestita in modo graduale e sarà necessario premere di continuo i tasti >> e << per una gestione più raffinata della velocità.
Gestione delle funzioni ausiliarie, luci e suoni:
Nel caso di utilizzo di locomotive dotate di decoder con funzioni ausiliare è possibile attivarle tramite i tasti numerici del telecomando.
Luci; premere il tasto 1 del telecomando per accendere le luci della locomotiva, sul display della centrale verrà visualizzato “1” nella seconda linea e le luci si accenderanno in base al senso di marcia impostato.
I tasti da 2 a 9 attivano e disattivano altre funzioni del decoder, come il suono se previsto.
Il tasto “Cancel” blocca la marcia della locomotiva portando tutti i comandi di trazione a zero.
Aggiornamento:
In questa immagine è possibile vedere la forma d’onda del segnale DCC presente all’uscita della centrale:
Link correlati:
PDF circuito stampato in scala 1:1
I commenti non appaiono subito, in quanto è attiva la moderazione dei messaggi. Si chiede gentilmente di non inserire listati di programmi nel testo per problemi di visualizzazzione dei post, grazie.
Fantastico Edgardo!
Peccato che l’ho visto questo post dopo più di un mese che è stato pubblicato!
Ho tutto in casa per smanettare e crcare di replicarti tranne un decoder da mettere su una locomotiva…
Tra l’altro vanno bene tutti i decoder?
Ciao Marco
Grazie Marco,
vanno bene tutti i decoder DCC a norme NEM a interfaccia unificata, fai riferimento partendo dalla NEM 650 in poi.
A questo link trovi tutte le norme NEM/FIMF
http://www.fimf.it/fimf_prec/norme_nem.html
Intanto complimenti per la realizzazione (me ne avevi parlato all’hobby model show di Novegro a primavera al platico GasTT).
A scanso di equivoci, il treno deve montare un decoder dcc (tipo Lenz o Esu, ecc.) ?
Se volessi comandare la centrale non con telecomando IR ma con altri dispositivi, quale parte dello sketch devo tenere buona e quale cancellare? Grazie per la cortese risposta.
Fabiano
Grazie Fabiano,
per i decoder da utilizzare vale lo stesso discorso in risposta al post di Marco qui sopra.
Fai riferimento sempre alle norme NEM/FIMF a e verifica che i decoder che hai indicato siano compatibili con tali norme partendo dalla 650:
http://www.fimf.it/fimf_prec/norme_nem.html
Per le mie prove ho usato un decoder Roco a norma 651 su una loco Roco (BR80) in scala TT.
Ho provato tale centrale anche con altre loco con decoder a norme NEM senza problemi.
Se non vuoi utilizzare un telecomando IR, puoi benissimo realizzare una pulsantiera composta da 17 tasti.
17 tasti è il numero richiesto per gestire tutte le funzioni previste dal programma, tuttavia, l’unico modo pratico che mi sento di sugerire è quello del partitore resistivo sugli ingressi analogici di Arduino.
Ti consiglio di dare un’occhiata a questo link che spiega come utilizzare più pulsanti con un partitore resistivo:
http://www.danielealberti.it/2015/01/come-collegare-molti-pulsanti-usando.html
Per le modifiche che chiedi, i punti del programma su cui intervenire sono 2.
Il primo è su una serie di definizioni dei tasti posti alle linee comprese tra la 17 e la 66.
Consiglio di caricare il programma sull’IDE di Arduino e di abilitare i numeri di linea, in quanto WordPress sballa la visualizzazione di tali numeri sul blog, quindi li ho eliminati.
In tali linee sono espressi i codici esadecimali dei tasti usati dal telecomando IR.
Basterebbe commentare con // tutte le linee e inserire nuovi comandi di definizione relativi ai tasti usati nel partitore resistivo.
La seconda parte da modificare è compresa tra le linee 307 e 426.
Questi comandi di tipo switch/case usano le definizioni dei tasti indicate in precedenza per capire quale tasto è premuto.
In pratica viene associato un numero alla variabile “irButton” in base al tasto premuto.
Gentile Edgardo,
ho realizzato tutto il sistema come da istruzioni di Bodnar utilizzando il comado IR Keyes. Riesco a comandare il display aumentando o diminuendo la velocità, invertendo il segno, attivo le funzioni e le disattivo (tasti numerici), azzero i comandi e imposto l’idirizzo a due cifre del decoder della loco. Tuttavia se metto sui binari alimentati attraverso la shield costruita (uso un alimentatore DC 12V 1A) una loco questa non da segni di vita (non si muove e non accende le luci). Lo stesso accade con un’altra loco dove ho ricontrollato il cablaggio.
Non ho idea di cosa possa non funzionare. Tu cosa ne pensi?
Ciao Fabiano,
innanzitutto ti consiglio di fare la cosa più ovvia anche se può sembrare banale; ricontrolla bene tutti i collegamenti elettrici.
Non so se hai utilizzato un pcb come quello da me proposto nell’articolo o se hai usato un altro metodo (per es. scheda millefori).
Questo non fa differenza, ma è possibile che qualche saldatura o collegamento non sia stato fatto in modo corretto.
Poi controlla la tensione sulle rotaie; se alle rotaie viene inviato correttamente un segnale codificato in DCC dovresti essere in grado di leggere un valore prossimo a 12-15 volt con un comune tester.
Se la loco non si muove può dipendere da 2 cose:
– l’alimentazione codificata DCC non arriva alle rotaie;
– l’indirizzo della loco sulla centrale non è corretto.
Di default, tutti i decoder per loco dovrebbero avere l’indirizzo 03, ma se hai tensione sulle rotaie, probabilmente è solo l’indirizzo della loco che è sbagliato.
Se non arriva tensione alle rotaie il problema è sulla scheda e devi verificare i collegamenti.
Controlla che il GND dell’alimentatore di potenza sia collegato al GND di Arduno.
Io ho usato un alimentatore di potenza da 15 VCC e 3 Ampere che consiglio vivamente.
Lo trovi come alimentatore per pc portatili come ricambio per laptop in qualsiasi negozio di elettronica.
Ricontrolla tutto e fammi sapere.
Edgardo
Ciao Edgardo.
Ho ricontrollato tutti i collegamenti sulla shield prototipale di Arduino che ho costruito. Ho trovato un errato collegamento della thermal (piedino 9 del LMD18200) e relativa resistenza con il GRD (non ci doveva essere!). Messo a posto questo ho poi misurato la tensione di alimentazione continua in ingresso che risulta correttamente 12V. In uscita DCC inizialmente ho rilevato 12V alternata, ma quando ho collegato ad un tratto di un metro di binario, li ho misurato 4V ?!? Ho scollegato i binari e ancora 4V ????
P.S. sui binari ho provato la loco digitale nonostante i 4V per vedere se almeno si muoveva (la speranza era forte, quasi un miraggio perchè ad un certo punto mi sono immaginato che si potesse muovere 🙂 ): inchiodata completamente. Ho provato anche i tasti numerici per vedere se le luci led si accendevano ( a proposito con quale tasti numerici le accendi?), ancora nulla.
Vorrei provare con un alimentatore da 15 Vcc e 3 A ma temo di poter friggere il decoder della loco che al massimo regge 1A. Potrebbe accadere questo secondo te?
Ciao e grazie per il tuo aiuto.
Fabiano
Ciao Fabiano,
le connessioni tra Arduino e shield DCC sono corrette? Contollale. A me è successo che nel mio primo test i pin non erano collegati correttamente e Arduino non inviava correttamente i dati DCC all’integrato LMD18200 perchè usavo dei pin strip troppo corti. Poi ho rimediato con pin strip più lunghi.
Il pin 9 di Arduino collegato al “thermal” è solo un indicatore di elevata temperatura dell’integrato LMD 18200 e non è critico.
Mi sembra strano che tu legga 4 volt sulle rotaie, sembrerebbe che tu abbia una caduta di tensione.
Con un alimentatore da 15 VCC 3 Ampere non friggi il decoder DCC, io lo uso così. Sono i dispositivi elettronici ad assorbire corrente in base alle loro esigenze. Noi umani dobbiamo solo fornirgli l’energia necessaria al loro corretto funzionamento.
Magari il tuo alimentatore da 12 volt 1 ampere non è sufficiente a fornire energia sufficiente al funzionamento del sistema.
Aumenta a 15 Volt e 3 ampere di alimentazione come suggerito e fammi sapere.
Edgardo
Ciao Edgardo.
E’ incredibile, all’uscita dei morsetti della centrale ho in AC 13 V e rotti, poi vado a collegare i binari e trovo ina AC 4 V.
Ho pulito bene col disossidante spray i morsetti dei binari dove collego i due cavetti provenienti dalla centrale ma continuo ad avere 4 V. Sui binari non c’é nulla. Cosa può essere?
Mi sembra strano anche a me. Ricontrolla tutti i collegamenti sul circuito dell’integrato LMD 18200.
Il problema deve essere lì.
Con un oscilloscopio puoi verificare se il segnale codificado DCC arriva alle rotaie.
Appena posso aggiorno l’articolo e inserisco una foto del segnale DCC presente sulle rotaie.
Ricontrolla anche l’indirizzo della loco; come dicevo nell’articolo di default è impostato a 03, ma puoi cambiarlo.
poi con i tasti da 1 a 9 puoi attivare/disattivare le funzioni ausiliarie (luci, sound, ecc.)
L’indirizzo della loco è impostato su 03 poichè cosi dicono le istruzioni del decoder circa le impostazioni di fabbrica.
Purtroppo non ho un oscilloscopio e quindi non posso verificare l’onda del segnale della centrale.
Il fatto che il binario sia un rettilineo non chiuso ad anello ma aperto alle estremità potrebbe influire (sono cinque binari piko rettilinei innestati tra loro) ?
Ma se a vuoto ai capi dei cavetti connessi al morsetto DCC (ho copiato esattamente la shield di Bodnar con gli stessi componenti o quasi) ho la tensione giusta, perchè per il solo fatto di collegare i due cavetti ai binari ottengo 1/3 circa della tensione di partenza? Il resto dove va a finire? Se ci fosse un problema sui collegamenti dell’integrato dovrei averli sempre anche in assenza dei binari e non trovarmi 12 Volts (a proposito, la resistenza ai capi dei binari o rispetto ai suoi morsettini, a vuoto la misuro con valori di 4 o 5 Ohm).
Comunque domani ricontrollo per la 100ma volta i collegamenti anche se dubito di trovare qualche cosa di diverso rispetto allo schema teorico di Bodnar.
Grazie mille e buona notte.
Ok, l’indirizzo della loco se è impostata di fabbrica è 03, ma sarebbe saggio verificare che sia effettivamente così.
IL binario può essere lasciato aperto e non influisce sul sistema anche se lo si chiude ad ovale.
La caduta di tensione è effettivamente strana e non dovrebbe esserci. Prova a sostituire i cavetti di collegamento da centrale a rotaie. Se la caduta persiste il problema è sull’integrato LMD18200.
Da quello che dici sembra che vi sia un corto circuito e l’integrato LMD18200 va in protezione.
Domanda: come hai assemblato il circuito? Hai usato una basetta millefori o hai fatto il pcb da me postato nell’articolo?
Te lo dico perchè con l’accoppiata Arduino e PCB fatto da me il tutto funziona, te lo assicuro.
Se vuoi posso fornire il PCB fotoinciso. Io non ci guadagno nulla e lo faccio a prezzo si costo, però almeno ti puoi togliere il dubbio.
MI spiace per tutto, ma più di così non posso fare.
Ciao Edgardo,
finalmento ce l’ho fatta! 🙂
C’era una frase tua che continuava a girarmi per la testa: “Controlla che il GND dell’alimentatore di potenza sia collegato al GND di Arduno.”
Banale dirai, si ma siccome avevo interpretato male lo schema elettrico il ground di potenza lo avevo collegato al piedino 8 current sense! Ora invece ho acquistato dietro tuo suggerimento un trasformatore con output 15 V 4 A e la loco funziona!
Sinceramente mi aspettavo un funzionamento più “reattivo” ai comandi del telecomando, comunque il suo dovere lo fa. Ora proverò anche le funzioni come l’illuminazione dei fari.
Grazie tante per il tuo aiuto fondamentale.
Ciao, Fabiano
Mi fa molto piacere cha hai risolto Fabiano.
Questo circuito , come indicato nell’articolo è una centrale DCC molto modesta ed è normale aspettarsi un funzionamento limitato. Se hai letto l’articolo originale del sig Bodnar troverai conferma.
Comunque è interessante e ci permette di sperimentare.
Se ti va di condividere il tuo lavoro puoi inviarmi qualche foto con commento e sarò felicissimo di pubblicarli in calce all’articolo.
Per le funzioni ausiliarie basta premere i tasti da 1 a 9 del telecomando, ON/OFF
Grandissimo…
Bel tutorial, e spiegato veramente in dettaglio.
Avrei una domanda: ma se volessi replicare con Arduino solo la parte del controller? ovvero inviare i comandi ai treni ma utilizzando la centrale DCC già in mio possesso?
Grazie Andrea, buona parte del merito va all’amico Dave che ha progettato il tutto.
Se ho ben capito vorresti usare il solo telecomando a raggi infrarossi per inviare i comandi alla tua centrale DCC.
In questo caso si potrebbe eliminare tutta la parte relativa all’integrato LMD18200 che è l’alimentatore vero e proprio e sfruttare il dato contenuto nella variabile “irButton”.
La variabile “irButton” restituisce un numero decimale intero in base al tasto premuto sul telecomando IR.
Da qui è possibile scrivere una routine per azionare qualsiasi apparecchiatura esterna collegata alle porte di I/O su Arduino.