Jump to content
 

antogar

Members
  • Posts

    11
  • Joined

  • Last visited

antogar's Achievements

11

Reputation

  1. Hello to everyone... due to COVID lockdown constraint (I am in Italy!), I am refreshing this project. I designed a PCB and mounted the board of the Servo decoder. If someone is interested, I can share the gerber files (or EAGLE project) I sent to the HK manufacturer to receive the boards. For the first order (5 boards), it is very cheap. Bye
  2. Hello to everyone and Happy Christmas! I received two Arduino Nano w/328 and I mounted one of them on a PCB breadboard for the SERVO DCC decoder: the board is 50x58mm and works fine! ciao, Antonino
  3. Thanks Dave! You are right about the EEprom, I was thinkng to my layout (260x130cm... small!) with a few number of turnout and really small number of switch movements because it is a "walkway" layout. For the detach function, I got the suggestion from the web and an italian modeler who developed an accessory decoder for servo based on PIC platform. Anyway, it seems to work properly. Robin, the picture is just to show the board and main connections (also the pull-ups are missing). For my experiments, the Arduino board gets the power from an external 2Amps power supply at 12V, looking to the Arduino UNO scheme, there is a 5v regulator that can support up to 1Amp current, it is enough to debug the software. Anyway, for the layout application of the decoder, you are right: the servo power source must be external. Regarding the pull-up, I used a +5V pull-up just because also Arduino as internal pullup resistors but they are not connected since the board launch the software and in the meantime the servos move! Anyway, it is important that at the start-up the command line it is not in a floating state. I am sure that also retarding the power supply of servos will fix the problem. ciao! antogar
  4. Hi to all I would share my version of DCC Servo decoder starting form the Dave code, the sketch includes some new functions. The EEprom management: all the servoo settings are stored as the angle position and the last position and read at reset; typing 'r' on the monitor console the initial values are restored at the next restart ofthe board. Another function is the decoder address set by the command station: pushing the button after a reset, the led is on and sending an accessory command form DCC command station the decoder stores the address as "base address" of the decoder, the servo addresses will be the base value plus 1,2,3,4. After a servo command, the button has the initial function for servo position setting. This function is availableonly in DCC-mode. Futhermore, during the servo position setting, the led flashes with differet times based on the "status": pushing the first time the led flashes slowly, for the next two phases (close and thrown setting) the led flashes quicker, this will help when serial monitor isnot available... Each servo should be connected by a pull-up resistor to the control pin, I used 10k but 4k7 should be better. This should avoid the initial "glitch" at power-up. Anyway, to avoid the servo "buzzing" (for cheap motors) the code "detach" the servo after a movement, this means that the control pin is disconnected. Of course the code is still on debug phase, I didn't test on a real layout but just on the table! I used an Arduino UNO R3 board, with two cheap servo (Tower PRO SG-90) and optocoupler circuit to interface the DCC command station (see mynabay.com for circuit as principle, I used CNX83 instead 6N137). The push buttonis connected to A0 pin (analog port) and this require an external pull-up resistor, 10k are ok. I used also an external power supply instead USB power from PC. ciao! antogar //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // DCC_Accessory_Decoder // // Author: Dave Renshaw // Modified: Antonino Gargiulo 8th dec 2014 // // This sketch provides basic accessory decoder capabilites for operating servos or Tortoise pont machines. It uses // the DCC_Decoder library created by Mynabay (http://www.mynabay.com/arduino/2-uncategorised/14-arduino-dcc-monitor) // // BEWARE This version is far from complete! // // http://www.rmweb.co.uk/community/index.php?/topic/70389-arduino-accessory-decoder-experiments/ #include <DCC_Decoder.h> #include <Servo.h> #include <EEPROM.h> ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // The configuration of the decoder is done via a string that is either retrieved from the persistent storage or // sent via the serial interface. The format is as follows... // // - DCC address terminated by a '#' character // For a servo device... // - Device type character 's' // - PWM output port to use // - Switch input port to use // - Closed angular displacement in degrees terminated by a '#' character // - Thrown angular displacement in degrees terminated by a '#' character // For a motor driven point machine // - Device type character 'm' // - DCC accessory address // - Digital output port to which 5volts is delivered to close terminated by a '#' character // - Digital output port to which 5volts is delivered to throw terminated by a '#' character // - Switch input port to use // // Arduino Uno, Nano and ProMini config data: // PWM output ports: 3,5,6,9,10,11 // Interrupt ports: 2,3 // char configData[] = "61#s03#04#060#120#62#s05#07#060#120#63#s06#08#060#120#64#s09#12#060#120#65#s10#11#060#120#!"; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Defines and structures // #define MAX_DEVICES 5 #define SET_UP A0 //pin button #define kDCC_INTERRUPT 0 // interrupt 0, pin 2 selected for DCC input int DEBOUNCE_PERIOD = 500; // the debounce time; increase if things get twitchy! int TYPE_SERVO = 1; int TYPE_TORTOISE = 2; boolean dccMode = false; unsigned long lastPacketMillis = 0; int lastServoReferenced = -1; boolean configOK = false; int eepromaddr = 0; int timeServoMove = 250; int ServoDetachTime = 1200; int currentAddr; boolean addressStored =0; int ledLamp = 0; boolean ledState = 0; int long ledTime; typedef struct { int address; // Address to respond to int type; // Type of accessory (servo, Tortoise, etc.) int outputPin; // Arduino output pin to drive int switchPin; // Arduino output pin to read for switch input boolean thrown; // true=thrown, false=closed. int closedSetting; // Angular displacement for closed position int thrownSetting; // Angular displacement for thrown position Servo servo; int oldSwitchState; long lastSwitchChangeTime; } DCCAccessoryAddress; DCCAccessoryAddress devices[MAX_DEVICES]; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Decoder Init // void configureDecoder() { // Assume the config is bad... configOK = false; // parse the config string... int nextDevice = 0; int nextChar = 0; while (nextChar < sizeof(configData)) { // If the next character is the terminator we are all done and things are OK (maybe)... if (configData[nextChar] == '!') { configOK = true; break; } int addr = 0; char c = configData[nextChar++] ; while (c != '#') { if (c < '0' || c > '9') { Serial.print("**Error in config string - DCC address invalid at character "); Serial.println(--nextChar); nextChar = 9999; break; } else { addr *= 10; addr += c - '0'; c = configData[nextChar++] ; } } Serial.print("Received DCC address "); Serial.println(addr); char type; if (nextChar < sizeof(configData)) { type = configData[nextChar++]; } else { Serial.print("**Error in config string - Expected device type at character "); Serial.println(nextChar); } Serial.print("Received type character '"); Serial.print(type); Serial.println("'"); if (type == 's') { // servo... pick out the output port number... int port = 0; c = configData[nextChar++] ; while (c != '#') { if (c < '0' || c > '9') { Serial.print("**Error in config string - output port invalid character '"); Serial.print(c); Serial.println("'"); nextChar = 9999; break; } else { port *= 10; port += c - '0'; c = configData[nextChar++] ; } } Serial.print("Received port "); Serial.println(port); // ..then the switch port number... int sport = 0; c = configData[nextChar++] ; while (c != '#') { if (c < '0' || c > '9') { Serial.print("**Error in config string - switch port invalid character '"); Serial.print(c); Serial.println("'"); nextChar = 9999; break; } else { sport *= 10; sport += c - '0'; c = configData[nextChar++] ; } } Serial.print("Received switch port "); Serial.println(sport); // ...then the closed and thrown angles... int closed = 0; // angular position for closed int thrown = 0; // angular position for thrown c = configData[nextChar++] ; while (c != '#') { if (c < '0' || c > '9') { Serial.print("**Error in config string - closed offset angle invalid character '"); Serial.print(c); Serial.println("'"); nextChar = 9999; break; } else { closed *= 10; closed += c - '0'; c = configData[nextChar++] ; } } if (closed > 180) { Serial.println("**Error in config string - closed offset too big!"); nextChar = 9999; break; } else { Serial.print("Received closed offset "); Serial.println(closed); } c = configData[nextChar++] ; while (c != '#') { if (c < '0' || c > '9') { Serial.print("**Error in config string - thrown offset angle invalid character '"); Serial.print(c); Serial.println("'"); nextChar = 9999; break; } else { thrown *= 10; thrown += c - '0'; c = configData[nextChar++] ; } } if (thrown > 180) { Serial.println("**Error in config string - thrown offset too big!"); nextChar = 9999; break; } else { Serial.print("Received thrown offset "); Serial.println(thrown); } // Device configuration complete fora Servo Motor... store in eeprom! devices[nextDevice].address = addr; devices[nextDevice].type = TYPE_SERVO; devices[nextDevice].outputPin = port; devices[nextDevice].switchPin = sport; devices[nextDevice].thrown = false; devices[nextDevice].closedSetting = closed; devices[nextDevice].thrownSetting = thrown; devices[nextDevice].oldSwitchState = 1; devices[nextDevice].lastSwitchChangeTime = millis(); eepromaddr = nextDevice * 7; EEPROM.write(eepromaddr,addr); EEPROM.write(eepromaddr+1,type); EEPROM.write(eepromaddr+2,port); EEPROM.write(eepromaddr+3,sport); EEPROM.write(eepromaddr+4,closed); EEPROM.write(eepromaddr+5,thrown); EEPROM.write(eepromaddr+6,0); //state of the switch, innitialized as closed Serial.println ("******************************"); Serial.print ("Stored data for Device "); Serial.println (nextDevice); Serial.print (addr); Serial.print ("\t"); Serial.print (type); Serial.print ("\t"); Serial.print (port); Serial.print ("\t"); Serial.print (sport); Serial.print ("\t"); Serial.print (closed); Serial.print ("\t"); Serial.println (thrown); Serial.println ("******************************"); nextDevice++; } else if (type == 'm') { // motor driven point machine... } else { // duff type... Serial.print("**Error in config string - Unrecognised device type '"); Serial.print(type); Serial.print(" at character "); Serial.println(nextChar); nextChar = 9999; break; } } // while nextChar } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Basic accessory packet handler // void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data) { // Convert NMRA packet address format to human address address -= 1; address *= 4; address += 1; address += (data & 0x06) >> 1; Serial.print("Acc Packet "); Serial.println(address, DEC); currentAddr = address; boolean closed = (data & 0x01) ? 1 : 0; for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) { if( address == devices[i].address ) { if (devices[i].type == TYPE_SERVO) { lastServoReferenced = i; devices[i].thrown = !closed; devices[i].servo.attach(devices[i].outputPin); devices[i].lastSwitchChangeTime = millis(); EEPROM.write(i*7+6,devices[i].thrown); //store the switch status in EEprom // Serial.print ("Servo "); // Serial.print (i); // Serial.print (" "); // Serial.print (devices[i].lastSwitchChangeTime); // Serial.print (" "); // Serial.println (closed); if (closed) { devices[i].servo.write(devices[i].closedSetting); } else { devices[i].servo.write(devices[i].thrownSetting); } } else if (devices[i].type == TYPE_TORTOISE ) { if (closed) { digitalWrite(devices[i].outputPin + 1, LOW); digitalWrite(devices[i].outputPin, HIGH); } else { digitalWrite(devices[i].outputPin, LOW); digitalWrite(devices[i].outputPin + 1, HIGH); } } } } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Basic accessory packet handler // boolean RawPacket_Handler(byte count, byte* packet) { lastPacketMillis = millis(); if (!dccMode) { dccMode = true; Serial.println("DCC mode"); } return false; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Setup // void setup() { Serial.begin(9600); DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true); DCC.SetRawPacketHandler(RawPacket_Handler); DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT ); // Enable the set-up/adjustment pushbutton... pinMode(SET_UP, INPUT); //digitalWrite(SET_UP, HIGH); // Set-up pin 13 so we can illuminate the LED for info... pinMode(13, OUTPUT); digitalWrite(13, ledState); // Do we have a saved configuration? if (EEPROM.read(0) != 255) { // Yes so read it back in... for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) { eepromaddr = i * 7; //current servo base-address in eeprom devices[i].address = EEPROM.read(eepromaddr); if (EEPROM.read(eepromaddr+1)=='s') devices[i].type = TYPE_SERVO; else devices[i].type = TYPE_TORTOISE; devices[i].outputPin = EEPROM.read(eepromaddr+2); devices[i].switchPin = EEPROM.read(eepromaddr+3); devices[i].thrown = EEPROM.read(eepromaddr+6); devices[i].closedSetting = EEPROM.read(eepromaddr+4); devices[i].thrownSetting = EEPROM.read(eepromaddr+5); devices[i].oldSwitchState = 1; devices[i].lastSwitchChangeTime = millis(); Serial.println ("******************************"); Serial.print ("Read data for Device "); Serial.println (i); Serial.print (devices[i].address); Serial.print ("\t"); Serial.print (devices[i].type); Serial.print ("\t"); Serial.print (devices[i].outputPin); Serial.print ("\t"); Serial.print (devices[i].switchPin); Serial.print ("\t"); Serial.print (devices[i].closedSetting); Serial.print ("\t"); Serial.print (devices[i].thrownSetting); Serial.print ("\t"); Serial.println (devices[i].thrown); } Serial.println ("EEprom read!"); configOK = true; } else { // Read the configuration string and set up the relavent input and putput ports... configureDecoder(); } // Do we have a valid config? if (configOK) { Serial.println("Config OK"); // Setup output pins for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) { // if the device address is defined then we can try and set things up... if (devices[i].address != 0) { if ( devices[i].type == TYPE_SERVO ) { pinMode( devices[i].outputPin, OUTPUT ); devices[i].servo.attach(devices[i].outputPin); if (!devices[i].thrown) { devices[i].servo.write(devices[i].closedSetting); } else { devices[i].servo.write(devices[i].thrownSetting); } delay (timeServoMove); devices[i].servo.detach(); pinMode(devices[i].switchPin, INPUT); digitalWrite(devices[i].switchPin, HIGH); Serial.print("Servo addr: "); Serial.print(devices[i].address,DEC); Serial.print(" ready on pin: "); Serial.print(devices[i].outputPin,DEC); Serial.print(" and switchpin: "); Serial.println(devices[i].switchPin,DEC); } else if (devices[i].type == TYPE_TORTOISE ) { pinMode( devices[i].outputPin, OUTPUT ); pinMode( devices[i].outputPin + 1, OUTPUT ); digitalWrite(devices[i].outputPin, HIGH); digitalWrite(devices[i].outputPin + 1 , LOW); } } } } } int oldSetUpButtonState = 1; long lastSetUpButtonChangeTime = millis(); int setUpMode = 0; long lastSetUpStep; int setUpStep; int STEP_INTERVAL = 300; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // Main loop // void loop() { //////////////////////////////////////////////////////////////// // Loop DCC library DCC.loop(); if (dccMode && (millis() - lastPacketMillis > 1000)) { dccMode = false; Serial.println("DC mode"); } for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) { if (( setUpMode == 0) && devices[i].servo.attached() && (millis() - devices[i].lastSwitchChangeTime > ServoDetachTime)) { devices[i].servo.detach(); Serial.print("Servo "); Serial.print(i); Serial.println(" detached"); } } // See if any command data is coming in... while (Serial.available()) { char cmd = Serial.read(); // Are we waiting for a command? if (cmd == 'r') // Reset of the configuration, write 255 at first position of EEprom { EEPROM.write(0,255); Serial.println ("Reset Configuration!"); } } // look at the set_up push button... int newState = digitalRead(SET_UP); if (newState != oldSetUpButtonState) { long timeNow = millis(); if ((timeNow - lastSetUpButtonChangeTime) > DEBOUNCE_PERIOD){ lastSetUpButtonChangeTime = timeNow; // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current state: // Push Button pressed after reset if (newState == LOW && lastServoReferenced < 0 && dccMode) { if (addressStored) { for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) { devices[i].address = currentAddr+i; EEPROM.write(i*7,devices[i].address); } Serial.print("currentAcc Packet "); Serial.println(currentAddr, DEC); digitalWrite (13, LOW); addressStored = 0; } else { Serial.println ("...aspetto indirizzo"); digitalWrite (13, HIGH); addressStored = 1; } } // Push Button pressed after servo movement if (newState == LOW && lastServoReferenced >= 0) { setUpMode++; Serial.print("set_up mode="); Serial.println(setUpMode); Serial.print("Setting Servo = "); Serial.println(lastServoReferenced); devices[lastServoReferenced].servo.attach(devices[lastServoReferenced].outputPin); if (setUpMode == 1) { lastSetUpStep = timeNow; // starting set up so move servo to middle position... setUpStep = 90; devices[lastServoReferenced].servo.write(setUpStep); } else if (setUpMode == 3) { devices[lastServoReferenced].closedSetting = setUpStep; Serial.print ("New value for close setting for device "); Serial.println (lastServoReferenced); Serial.println (setUpStep); EEPROM.write(((lastServoReferenced*7)+4),setUpStep); setUpStep = 90; devices[lastServoReferenced].servo.write(setUpStep); } else if (setUpMode == 4) { devices[lastServoReferenced].thrownSetting = setUpStep; Serial.print ("New value for thrown setting for device "); Serial.println (lastServoReferenced); Serial.println (setUpStep); EEPROM.write(((lastServoReferenced*7)+5),setUpStep); devices[lastServoReferenced].servo.write(setUpStep); setUpMode = 0; Serial.println ("State 0"); devices[lastServoReferenced].servo.detach(); } } oldSetUpButtonState = newState; } } // do the servo set up thing... if (setUpMode > 0) { if ((millis() - lastSetUpStep) > STEP_INTERVAL) { if (setUpMode == 2) { setUpStep--; devices[lastServoReferenced].servo.write(setUpStep); lastSetUpStep = millis(); } else if (setUpMode == 3) { setUpStep++; devices[lastServoReferenced].servo.write(setUpStep); lastSetUpStep = millis(); } } } // look at the switches if we are in DC mode... if (!dccMode && ( setUpMode == 0)) { for(int i=0; i<(int)(sizeof(devices)/sizeof(devices[0])); i++) { int newStateSwitch = digitalRead(devices[i].switchPin); if (newStateSwitch != devices[i].oldSwitchState) { long timeNow = millis(); if ((timeNow - devices[i].lastSwitchChangeTime) > DEBOUNCE_PERIOD) { devices[i].lastSwitchChangeTime = timeNow; // whatever the reading is at, it's been there for longer // than the debounce delay, so take it as the actual current state: devices[i].servo.attach(devices[i].outputPin); Serial.print("devices["); Serial.print(i); Serial.print("] new switch state="); if (newStateSwitch == LOW) { Serial.println("LOW"); devices[i].servo.write(devices[i].closedSetting); } else { Serial.println("HIGH"); devices[i].servo.write(devices[i].thrownSetting); } devices[i].oldSwitchState = newStateSwitch; lastServoReferenced = i; } } } } // Led on pin 13 management if (setUpMode > 0) { switch(setUpMode){ case 1: ledLamp=700; break; case 2: ledLamp=300; break; case 3: ledLamp=100; break; default: ledLamp=0; } if (millis()-ledTime > ledLamp) { ledTime = millis(); ledState = !ledState; digitalWrite(13, ledState); } } else if (lastServoReferenced >= 0) { digitalWrite(13, LOW); } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  5. Hi, I am trying CNX83 as alternative to 6N137. It has a transistor instead a cmos port, it works well putting a 10k resistance on the base to ground. ciao.
×
×
  • Create New...