/* HopeWorksV4 09/11/22 Servos set and fully tested with controller This sketch adds the DCC Acc controller code. This board is addresses 21-30 range point to engine shed = 21 point to traverser = 22 Traverser at Back (entrance line) = 23 Traverser centre track 24 Traverser front track 25 Unit built and run in. Photointerrupter fitted. Stop locations and indexing working correctly index auto reset working EEPROM settings correct and updating Arduino Uno Traverser Photo Interrupter 2 x adjuster buttons? 2 points EEPROM DCC decoder on pin 2 Power Required 12 DC for Arduino/Stepper DCC track power Pins A0 = photointerrupter 2 - interrupt pin for DCC 7 = enablePin...stepper 8 = stepPin...stepper 9 = dirPin...stepper 10 = Adjuster switch 11 = Adjuster switch 5 servo 6 servo */ //Other Variables int moveToTrackPos = 100;//if 100 do not move traverser...no command issued. int currentTrackPos = 100;//100 means it's lost int indexed = 0;//1 means unit is indexed const int indexStepReset = 2000;//the value steps is set to when indexed. unsigned int currentSteps; //traverser current step position unsigned int targetSteps; //position the traverser is moving to. int traverserDirection; int interrupterState; #include "NmraDcc.h" NmraDcc Dcc ; DCC_MSG Packet ; //EEPROM //system will use built in eeprom as it should not be written to very much...limited lifecycle. #include "EEPROM.h" long eepromUpdatePeriod = 10000;//60 secs between updates unsigned long eepromUpdateTimer; const int eepromOffset = 100;//initial adress that is looked at to find info /* EEPROM data structure First three bytes ensure EEPROM has had proper values installed 100 = 3 101 = 11 102 = 63 103-104 Track 1 step position...to main line 105-106 Track 2 step position...shed centre 107-108 Track 3 step position...shed front 109-110 Track Spare step position...spare */ unsigned int trackPositions[4] = {1000, 19000, 37000, 1000}; //1/4 step default positions if nothing programmed //Servos const int PointAPin = 5;//entrance to traverser const int PointBPin = 6;//seperate works point const int servoLiveTime = 1500;//1.5 secs before reset unsigned long servoTimer; byte servoDetached = 0;//1 means attached #include "Servo.h" Servo PointA; Servo PointB; //Adjuster switch const int adjustBackPin = 10; const int adjustForwardPin = 11; //Stepper Motor const int enablePin = 7;//taken high to disable stepper const int stepPin = 8; const int dirPin = 9; const int poweron = 0; const int poweroff = 1; const int forwards = 0;//moving to front of layout const int backwards = 1;//moving to back of layout...zero step position is at back. byte stepInState = 0;//high or low state for next step unsigned long stepperTimer;//timer until next step in micros(); int stepperSpeedDelay = 1000;//delay in micros() between steps...controls speed, Good max speed void moveTraverser() { unsigned long currentMicros; int photointerruptRead; currentMicros = micros(); if (indexed > 0) { //if it knows where it is if (moveToTrackPos < 100) { if (currentSteps != targetSteps) { if ((currentMicros - stepperTimer) >= stepperSpeedDelay) { //if sufficient time has gone next step stepperTimer = currentMicros; if (stepInState > 0) { stepInState = 0; } else { stepInState = 1; } digitalWrite(stepPin, stepInState);//take a step photointerruptRead = analogRead(A0); if (interrupterState > 0 && photointerruptRead > 100) { currentSteps = indexStepReset; interrupterState = 0; Serial.println("indexed"); } if (interrupterState < 1 && photointerruptRead < 100) { currentSteps = indexStepReset; interrupterState = 1; Serial.println("indexed"); } if (traverserDirection == forwards) { currentSteps++; } else { currentSteps--; } } } else { digitalWrite(enablePin, poweroff);//turn the power off...it's arrived Serial.println("Arrived...power off"); Serial.print("Current Position Steps = "); Serial.println(currentSteps); currentTrackPos = moveToTrackPos; Serial.print("currentTrackPos: "); Serial.println(currentTrackPos); moveToTrackPos = 100; } } } else { //unit is NOT indexed yet photointerruptRead = analogRead(A0); indexed = 1; if (photointerruptRead < 100) { currentSteps = trackPositions[0];//Extreme back track interrupterState = 1; setTraverserTarget(1);//move to centre track } else { currentSteps = trackPositions[2];//Extreme front interrupterState = 0; setTraverserTarget(0);//move to rear track } } } void setTraverserTarget(int moveToTrack) { targetSteps = trackPositions[moveToTrack]; if (targetSteps != currentSteps) { //only turn power on if it's going to move digitalWrite(enablePin, poweron); if (moveToTrack = 0) {//set system to re index if moving to rear track interrupterState = 0; } } if (targetSteps < currentSteps) { traverserDirection = backwards; } else { traverserDirection = forwards; } digitalWrite(dirPin, traverserDirection); Serial.print("Direction = "); Serial.println(traverserDirection); Serial.print("Steps Target = "); Serial.println(targetSteps); } //gets eeprom data and puts in trackpositions[4] array void geteepromdata() { int q; byte getbytes[11];//2 bytes per position + 3 data check bytes Serial.println("getbytes[]: "); //get the data from eeprom and store in array for (q = 0; q < 11; q++) { getbytes[q] = EEPROM.read(q + eepromOffset); Serial.println( getbytes[q]); } //check if the Arduino has had values put in...if not set them if (getbytes[0] == 3 && getbytes[1] == 11 && getbytes[2] == 64) { Serial.println("trackPositions[]: "); //now assemble the data and put into trackpositions[4] for (q = 0; q < 4; q++) { trackPositions[q] = (getbytes[(q * 2) + 3] * 256) + (getbytes[(q * 2) + 4]); Serial.println(trackPositions[q]); } } else { //values not set so needs defaults updateeeprom(); } } //update the eeprom with current trackpositions void updateeeprom() { int q; //put check values in EEPROM.update(eepromOffset, 3); EEPROM.update(eepromOffset + 1, 11); EEPROM.update(eepromOffset + 2, 64); //now for the track values for (q = 0; q < 4; q++) { EEPROM.update((q * 2) + eepromOffset + 3, highByte(trackPositions[q])); EEPROM.update((q * 2) + eepromOffset + 4, lowByte(trackPositions[q])); } geteepromdata(); } // This function is called whenever a normal DCC Turnout Packet is received and we're in Output Addressing Mode void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower) { switch (Addr) { case 21://point to engine shed PointA.attach(PointAPin); servoTimer = millis() + servoLiveTime; servoDetached = 1;//let system know servos are attached delay(30);//shouldn't upset DCC as too fast to press 2nd button if (Direction == 0) { PointA.write(95); //siding } else { PointA.write(57); //main } break; case 22://point to traverser PointB.attach(PointBPin); servoTimer = millis() + servoLiveTime; servoDetached = 1;//let system know servos are attached delay(30);//shouldn't upset DCC as too fast to press 2nd button if (Direction == 0) { PointB.write(87); //traverser } else { PointB.write(115); //main } break; //Traverser have an address for each location, they will activate whatever direction is selected. case 23: // Rear entrance track Serial.println("rear track"); if (moveToTrackPos == 100) { //make sure last movement has stopped before starting a new one moveToTrackPos = 0; setTraverserTarget(0); } break; case 24: // centre track Serial.println("centre track"); if (moveToTrackPos == 100) { //make sure last movement has stopped before starting a new one moveToTrackPos = 1; setTraverserTarget(1); } break; case 25: //front track Serial.println("front track"); if (moveToTrackPos == 100) { //make sure last movement has stopped before starting a new one moveToTrackPos = 2; setTraverserTarget(2); } break; default: break; } Serial.print("notifyDccAccTurnoutOutput: ") ; Serial.print(Addr, DEC) ; Serial.print(','); Serial.println(Direction, DEC) ; } void setup() { Serial.begin(9600); Serial.println("HopeWorksV4"); //Adjuster switches pinMode(adjustBackPin , INPUT); pinMode(adjustForwardPin, INPUT); //stepper set up pins as outputs pinMode(stepPin, OUTPUT); pinMode(dirPin, OUTPUT); pinMode(enablePin, OUTPUT); //turndriver on and off to get rid of the noise digitalWrite(enablePin, poweroff); digitalWrite(dirPin, forwards); // LOW for forwards //get trackposition data from eeprom geteepromdata(); // Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up Dcc.pin(0, 2, 1); // Call the main DCC Init function to enable the DCC Receiver Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 ); Serial.println("Set Up Complete..."); } void adjusterSwitches() { //only move if no track movement has been selected and it's in an indexed track pos if (moveToTrackPos == 100 && currentTrackPos != 100) { if (digitalRead(adjustForwardPin) == 1) { //forward selected digitalWrite(enablePin, poweron); digitalWrite(dirPin, forwards); if (stepInState > 0) { stepInState = 0; } else { stepInState = 1; } digitalWrite(stepPin, stepInState);//take a step //delayMicroseconds used as it will help to block any incoming DCC requests delayMicroseconds(stepperSpeedDelay * 10); currentSteps++; trackPositions[currentTrackPos] = currentSteps;//update the location array } else { //done this way so that it doesn't try to process touching both buttons at once if (digitalRead(adjustBackPin) == 1) { //forward selected digitalWrite(enablePin, poweron); digitalWrite(dirPin, backwards); if (stepInState > 0) { stepInState = 0; } else { stepInState = 1; } digitalWrite(stepPin, stepInState);//take a step delayMicroseconds(stepperSpeedDelay * 10); currentSteps--; trackPositions[currentTrackPos] = currentSteps;//update the location array }else{ //if no buttons being pressed check if EEPROM needs updating if(millis() > eepromUpdateTimer){ eepromUpdateTimer = millis() + eepromUpdatePeriod; updateeeprom(); } } } } } void loop() { //this is some test code that auto selects a destination for the traverser //Used for testing and running in /* if (moveToTrackPos == 100) { //temp delay code and track position selection delay(4000);// a wait between movements so it's clear it stopped. moveToTrackPos = random(0, 3); //pick a random position setTraverserTarget(moveToTrackPos); } */ Dcc.process();//process incoming DCC Commands moveTraverser();//deals with traverser control adjusterSwitches(); if (servoDetached > 0 && millis() > servoTimer) { servoDetached = 0; PointA.detach();//detach all the point servos PointB.detach(); } }