├── DCCpp_Controller ├── DCCpp_Controller.pde ├── controllerConfig.pde ├── coreComponents.pde ├── coreConstants.java ├── dCabs.pde ├── dRoutes.pde ├── dSensors.pde ├── dTracks.pde ├── dTurnouts.pde ├── data │ ├── ARCENA-15.vlw │ ├── IrisUPC-36.vlw │ ├── JasmineUPC-36.vlw │ ├── LucidaConsole-18.vlw │ ├── Miriam-36.vlw │ ├── MiriamFixed-26.vlw │ ├── OCRAExtended-26.vlw │ ├── cab-1202.jpg │ ├── cab-1506.jpg │ ├── cab-2004.jpg │ ├── cab-54.jpg │ ├── cab-6021.jpg │ ├── cab-622.jpg │ ├── cab-8601.jpg │ └── helpMenu.jpg ├── dccStatus.xml ├── eventHandlers.pde ├── gButtons.pde ├── gTextBoxes.pde ├── gWindows.pde ├── progComponents.pde └── serialComponents.pde └── README.md /DCCpp_Controller/DCCpp_Controller.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER 3 | // COPYRIGHT (c) 2013-2015 Gregg E. Berman 4 | // 5 | // This program is free software: you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation, either version 3 of the License, or 8 | // (at your option) any later version. 9 | // 10 | // This program is distributed in the hope that it will be useful, 11 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | // GNU General Public License for more details. 14 | // 15 | // You should have received a copy of the GNU General Public License 16 | // along with this program. If not, see http://www.gnu.org/licenses 17 | // 18 | ////////////////////////////////////////////////////////////////////////// 19 | // 20 | // DCC++ CONTROLLER is a Java program written using the 64-bit Processing Library 21 | // and Processing IDE (version 3.01). 22 | // 23 | // DCC++ CONTROLLER provides users with a fully customizeable graphical 24 | // front end for the total control of model trains and model train layouts 25 | // via its companion program, DCC++ BASE STATION. 26 | // 27 | // DCC++ BASE STATION allows a standard Arduino Uno with an Arduino Motor Shield 28 | // to be used as a fully-functioning digital command and control (DCC) base station 29 | // for controlling model train layouts that conform to current National Model 30 | // Railroad Association (NMRA) DCC standards. 31 | // 32 | // DCC++ CONTROLLER communicates with DCC++ BASE STATION using simple text commands sent 33 | // via a standard USB Serial Cord at speeds of up to 115200 Baud. A Bluetooth Wireless 34 | // Connection may be used in place of a USB Serial Cord without any software modification. 35 | // 36 | // This version of DCC++ CONTROLLER supports: 37 | // 38 | // * Multi-Cab / Multi-Throttle configurations using 128-step speed control 39 | // * 2-byte and 4-byte cab addresses 40 | // * Customizable cab function buttons F0-F12 41 | // * User-created multi-layout track plan 42 | // * Customizeable turnouts and crossovers with controls integrated into track plan 43 | // * Customizeable routes with configurable buttons 44 | // * Customizeable routes with route buttons integrated into track plan 45 | // * Master Power Button 46 | // * Customizable key-controls 47 | // * Real-time current monitor 48 | // * Optional track-integrated sensors 49 | // * Optional user-created Auto Pilot routines (when used with track-integrated sensors) 50 | // * Manual activation/de-activation of accessory functions using 512 addresses, each with 4 sub-addresses 51 | // * Programming on the Main Operations Track 52 | // - write configuration variable bytes 53 | // - set/clear specific configuration variable bits 54 | // * Programming on the Programming Track 55 | // - write configuration variable bytes 56 | // - read configuration variable bytes 57 | // 58 | // With the exception of a standard 15V power supply for the Arduino Uno that can 59 | // be purchased in any electronics store, no additional hardware is required. 60 | // 61 | // Neither DCC++ BASE STATION nor DCC++ CONTROLLER use any known proprietary or 62 | // commercial hardware, software, interfaces, specifications, or methods related 63 | // to the control of model trains using NMRA DCC standards. Both programs are wholly 64 | // original, developed by the author, and are not derived from any known commercial, 65 | // free, or open-source model railroad control packages by any other parties. 66 | // 67 | // However, DCC++ BASE STATION and DCC++ CONTROLLER do heavily rely on the IDEs and 68 | // embedded libraries associated with Arduino and Processing. Tremendous thanks to those 69 | // responsible for these terrific open-source initiatives that enable programs like 70 | // DCC++ to be developed and distributed in the same fashion. 71 | // 72 | // REFERENCES: 73 | // 74 | // NMRA DCC Standards: http://www.nmra.org/standards/DCC/standards_rps/DCCStds.html 75 | // Arduino: http://www.arduino.cc/ 76 | // Processing: http://processing.org/ 77 | // GNU General Public License: http://opensource.org/licenses/GPL-3.0 78 | // 79 | ////////////////////////////////////////////////////////////////////////// 80 | 81 | import processing.serial.*; 82 | import processing.net.*; 83 | import java.util.regex.Pattern; 84 | import java.util.regex.Matcher; 85 | import java.util.*; 86 | 87 | final String CONTROLLER_VERSION = "3.0"; 88 | final int BASE_BAUD = 115200; 89 | final int SCREEN_WIDTH = 1366; 90 | final int SCREEN_HEIGHT = 768; 91 | final String STATUS_FILE = "dccStatus.xml"; 92 | 93 | ////////////////////////////////////////////////////////////////////////// 94 | 95 | void settings(){ 96 | size(SCREEN_WIDTH,SCREEN_HEIGHT); 97 | } 98 | 99 | ////////////////////////////////////////////////////////////////////////// 100 | 101 | void setup(){ 102 | Initialize(); 103 | } 104 | 105 | ////////////////////////////////////////////////////////////////////////// 106 | 107 | void draw(){ 108 | 109 | background(backgroundColor); 110 | 111 | for(DccComponent dcc : dccComponents) 112 | dcc.display(); 113 | 114 | if(frameCount==1) // if this is the first frame, just display components and return (otherwise user stare at a blank screen while serial is opening 115 | return; 116 | 117 | if(frameCount==2) // is this is the second frame, open the serial port --- screen will have already been displayed in prior frame 118 | aPort.open(arduinoPortXML.getContent()); 119 | 120 | for(int i=buttonQueue2.size()-1;i>=0;i--){ 121 | buttonQueue2.get(i).init(); 122 | buttonQueue2.remove(i); 123 | } 124 | 125 | for(int i=buttonQueue.size()-1;i>=0;i--){ 126 | buttonQueue2.add(buttonQueue.get(i));; 127 | buttonQueue.remove(i); 128 | } 129 | 130 | if(!mousePressed){ 131 | cursorType=ARROW; 132 | previousComponent=selectedComponent; 133 | selectedComponent=null; 134 | 135 | int nComponents = dccComponents.size(); 136 | 137 | for(int i=nComponents-1;i>=0;i--) 138 | dccComponents.get(i).check(); 139 | 140 | cursor(cursorType); 141 | } 142 | 143 | int m=millis(); 144 | if(m-lastTime>250 && aPort!=null && currentMeter.isOn){ 145 | lastTime=m; 146 | aPort.write(""); 147 | } 148 | 149 | msgBoxClock.setMessage(nf(hour(),2)+":"+nf(minute(),2)+":"+nf(second(),2)); 150 | 151 | if(saveXMLFlag){ 152 | try{ 153 | saveXML(dccStatusXML,STATUS_FILE); 154 | saveXMLFlag=false; 155 | } catch(Exception e){ 156 | println("Couldn't save. Will retry"); 157 | } 158 | } 159 | 160 | autoPilot.safetyCheck(); 161 | 162 | } // draw 163 | 164 | ////////////////////////////////////////////////////////////////////////// 165 | 166 | abstract class DccComponent{ 167 | Window window=null; 168 | int xPos, yPos; 169 | String componentName="NAME NOT DEFINED"; 170 | abstract void display(); 171 | void check(){}; 172 | void pressed(){}; 173 | void rightClick(){}; 174 | void shiftPressed(){}; 175 | void released(){}; 176 | void drag(){}; 177 | void init(){}; 178 | 179 | protected int xWindow(){ 180 | if(window==null) 181 | return 0; 182 | return window.xPos; 183 | } 184 | 185 | protected int yWindow(){ 186 | if(window==null) 187 | return 0; 188 | return window.yPos; 189 | } 190 | } 191 | 192 | ////////////////////////////////////////////////////////////////////////// 193 | 194 | interface CallBack{ 195 | void execute(int n, String c); 196 | } 197 | 198 | ////////////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /DCCpp_Controller/controllerConfig.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Configuration and Initialization 3 | // 4 | // * Defines all global variables and objects 5 | // 6 | // * Reads and loads previous status data from status files 7 | // 8 | // * Implements track layout(s), throttles, track buttons, route buttons, 9 | // cab buttons, function buttons, windows, current meter, 10 | // and all other user-specified components 11 | // 12 | ////////////////////////////////////////////////////////////////////////// 13 | 14 | // DECLARE "GLOBAL" VARIABLES and OBJECTS 15 | 16 | PApplet Applet = this; // Refers to this program --- needed for Serial class 17 | 18 | int cursorType; 19 | String baseID; 20 | boolean keyHold=false; 21 | boolean saveXMLFlag=false; 22 | int lastTime; 23 | PFont throttleFont, messageFont, buttonFont; 24 | color backgroundColor; 25 | XML dccStatusXML, arduinoPortXML, sensorButtonsXML, autoPilotXML, cabDefaultsXML, serverListXML; 26 | 27 | DccComponent selectedComponent, previousComponent; 28 | ArrayList dccComponents = new ArrayList(); 29 | ArrayList cabButtons = new ArrayList(); 30 | ArrayList callBacks = new ArrayList(); 31 | ArrayList buttonQueue = new ArrayList(); 32 | ArrayList buttonQueue2 = new ArrayList(); 33 | HashMap remoteButtonsHM = new HashMap(); 34 | ArrayList msgAutoCab = new ArrayList(); 35 | HashMap sensorsHM = new HashMap(); 36 | HashMap cabsHM = new HashMap(); 37 | HashMap trackButtonsHM = new HashMap(); 38 | 39 | ArduinoPort aPort; 40 | PowerButton powerButton; 41 | AutoPilotButton autoPilot; 42 | CleaningCarButton cleaningCab; 43 | Throttle throttleA; 44 | Layout layout,layout2,layoutBridge; 45 | MessageBox msgBoxMain, msgBoxDiagIn, msgBoxDiagOut, msgBoxClock; 46 | CurrentMeter currentMeter; 47 | Window mainWindow, accWindow, progWindow, portWindow, extrasWindow, opWindow, diagWindow, autoWindow, sensorWindow, ledWindow; 48 | ImageWindow imageWindow; 49 | JPGWindow helpWindow; 50 | MessageBox msgAutoState, msgAutoTimer; 51 | InputBox activeInputBox; 52 | InputBox accAddInput, accSubAddInput; 53 | InputBox progCVInput, progHEXInput, progDECInput, progBINInput; 54 | InputBox opCabInput, opCVInput, opHEXInput, opDECInput, opBINInput, opBitInput; 55 | InputBox shortAddInput, longAddInput; 56 | MessageBox activeAddBox; 57 | MessageBox portBox, portNumBox; 58 | MessageBox ledHueMsg, ledSatMsg, ledValMsg, ledRedMsg, ledGreenMsg, ledBlueMsg; 59 | PortScanButton portScanButton; 60 | LEDColorButton ledColorButton; 61 | 62 | // DECLARE TRACK BUTTONS, ROUTE BUTTONS, and CAB BUTTONS WHICH WILL BE DEFINED BELOW AND USED "GLOBALLY" 63 | 64 | TrackButton tButton1,tButton2,tButton3,tButton4,tButton5; 65 | TrackButton tButton6,tButton7,tButton8,tButton9,tButton10; 66 | TrackButton tButton20,tButton30,tButton40,tButton50; 67 | 68 | RouteButton rButton1,rButton2,rButton3,rButton4,rButton5,rButton6,rButton7; 69 | RouteButton rButton10,rButton11,rButton12,rButton13,rButton14; 70 | RouteButton rButtonR1,rButtonR2,rButton15,rButton16,rButton17,rButtonSpiral,rButtonReset,rButtonBridge; 71 | 72 | CabButton cab8601,cab54,cab1202,cab1506,cab622,cab2004,cab6021; 73 | 74 | //////////////////////////////////////////////////////////////////////// 75 | // Initialize --- configures everything! 76 | //////////////////////////////////////////////////////////////////////// 77 | 78 | void Initialize(){ 79 | colorMode(RGB,255); 80 | throttleFont=loadFont("OCRAExtended-26.vlw"); 81 | messageFont=loadFont("LucidaConsole-18.vlw"); 82 | buttonFont=loadFont("LucidaConsole-18.vlw"); 83 | rectMode(CENTER); 84 | textAlign(CENTER,CENTER); 85 | backgroundColor=color(50,50,60); 86 | 87 | aPort=new ArduinoPort(); 88 | 89 | // READ, OR CREATE IF NEEDED, XML DCC STATUS FILE 90 | 91 | dccStatusXML=loadXML(STATUS_FILE); 92 | if(dccStatusXML==null){ 93 | dccStatusXML=new XML("dccStatus"); 94 | } 95 | 96 | arduinoPortXML=dccStatusXML.getChild("arduinoPort"); 97 | if(arduinoPortXML==null){ 98 | arduinoPortXML=dccStatusXML.addChild("arduinoPort"); 99 | arduinoPortXML.setContent("Emulator"); 100 | } 101 | 102 | serverListXML=dccStatusXML.getChild("serverList"); 103 | if(serverListXML==null){ 104 | serverListXML=dccStatusXML.addChild("serverList"); 105 | serverListXML.setContent("127.0.0.1"); 106 | } 107 | 108 | sensorButtonsXML=dccStatusXML.getChild("sensorButtons"); 109 | if(sensorButtonsXML==null){ 110 | sensorButtonsXML=dccStatusXML.addChild("sensorButtons"); 111 | } 112 | 113 | autoPilotXML=dccStatusXML.getChild("autoPilot"); 114 | if(autoPilotXML==null){ 115 | autoPilotXML=dccStatusXML.addChild("autoPilot"); 116 | } 117 | 118 | cabDefaultsXML=dccStatusXML.getChild("cabDefaults"); 119 | if(cabDefaultsXML==null){ 120 | cabDefaultsXML=dccStatusXML.addChild("cabDefaults"); 121 | } 122 | 123 | saveXMLFlag=true; 124 | 125 | // CREATE THE ACCESSORY CONTROL WINDOW 126 | 127 | accWindow = new Window(500,200,300,160,color(200,200,200),color(200,50,50)); 128 | new DragBar(accWindow,0,0,300,10,color(200,50,50)); 129 | new CloseButton(accWindow,288,0,10,10,color(200,50,50),color(255,255,255)); 130 | new MessageBox(accWindow,150,22,0,0,color(200,200,200),20,"Accessory Control",color(200,50,50)); 131 | new MessageBox(accWindow,20,60,-1,0,color(200,200,200),16,"Acc Address (0-511):",color(200,50,50)); 132 | accAddInput = new InputBox(accWindow,230,60,16,color(200,200,200),color(50,50,200),3,InputType.DEC); 133 | new MessageBox(accWindow,20,90,-1,0,color(200,200,200),16,"Sub Address (0-3):",color(200,50,50)); 134 | accSubAddInput = new InputBox(accWindow,230,90,16,color(200,200,200),color(50,50,200),1,InputType.DEC); 135 | new AccessoryButton(accWindow,90,130,55,25,100,18,"ON",accAddInput,accSubAddInput); 136 | new AccessoryButton(accWindow,210,130,55,25,0,18,"OFF",accAddInput,accSubAddInput); 137 | accAddInput.setNextBox(accSubAddInput); 138 | accSubAddInput.setNextBox(accAddInput); 139 | 140 | // CREATE THE SERIAL PORT WINDOW 141 | 142 | portWindow = new Window(500,200,500,170,color(200,200,200),color(200,50,50)); 143 | new DragBar(portWindow,0,0,500,10,color(200,50,50)); 144 | new CloseButton(portWindow,488,0,10,10,color(200,50,50),color(255,255,255)); 145 | new MessageBox(portWindow,250,22,0,0,color(200,200,200),20,"Select Arduino Port",color(200,50,50)); 146 | portScanButton = new PortScanButton(portWindow,100,60,85,20,100,18,"SCAN"); 147 | new PortScanButton(portWindow,400,60,85,20,0,18,"CONNECT"); 148 | new PortScanButton(portWindow,120,140,15,20,120,18,"<"); 149 | new PortScanButton(portWindow,380,140,15,20,120,18,">"); 150 | portBox = new MessageBox(portWindow,250,100,380,25,color(250,250,250),20,"",color(50,150,50)); 151 | portBox.setMessage("Please press SCAN",color(150,50,50)); 152 | portNumBox = new MessageBox(portWindow,250,140,0,0,color(200,200,200),20,"",color(50,50,50)); 153 | 154 | // CREATE THE PROGRAMMING CVs ON THE PROGRAMMING TRACK WINDOW 155 | 156 | progWindow = new Window(500,100,500,400,color(200,180,200),color(50,50,200)); 157 | new DragBar(progWindow,0,0,500,10,color(50,50,200)); 158 | new CloseButton(progWindow,488,0,10,10,color(50,50,200),color(255,255,255)); 159 | new RectButton(progWindow,250,30,210,30,40,color(0),18,"Programming Track",ButtonType.TI_COMMAND,101); 160 | 161 | new MessageBox(progWindow,20,90,-1,0,color(200,180,200),16,"CV (1-1024):",color(50,50,200)); 162 | new MessageBox(progWindow,20,130,-1,0,color(200,180,200),16,"Value (HEX):",color(50,50,200)); 163 | new MessageBox(progWindow,20,160,-1,0,color(200,180,200),16,"Value (DEC):",color(50,50,200)); 164 | new MessageBox(progWindow,20,190,-1,0,color(200,180,200),16,"Value (BIN):",color(50,50,200)); 165 | progCVInput = new InputBox(progWindow,150,90,16,color(200,180,200),color(200,50,50),4,InputType.DEC); 166 | progHEXInput = new InputBox(progWindow,150,130,16,color(200,180,200),color(200,50,50),2,InputType.HEX); 167 | progDECInput = new InputBox(progWindow,150,160,16,color(200,180,200),color(200,50,50),3,InputType.DEC); 168 | progBINInput = new InputBox(progWindow,150,190,16,color(200,180,200),color(200,50,50),8,InputType.BIN); 169 | progCVInput.setNextBox(progHEXInput); 170 | progHEXInput.setNextBox(progDECInput); 171 | progDECInput.setNextBox(progBINInput); 172 | progDECInput.linkBox(progHEXInput); 173 | progBINInput.setNextBox(progHEXInput); 174 | progBINInput.linkBox(progHEXInput); 175 | new ProgWriteReadButton(progWindow,300,90,65,25,100,14,"READ",progCVInput,progHEXInput); 176 | new ProgWriteReadButton(progWindow,390,90,65,25,0,14,"WRITE",progCVInput,progHEXInput); 177 | 178 | new MessageBox(progWindow,20,240,-1,0,color(200,180,200),16,"ENGINE ADDRESSES",color(50,50,200)); 179 | new MessageBox(progWindow,20,280,-1,0,color(200,180,200),16,"Short (1-127):",color(50,50,200)); 180 | new MessageBox(progWindow,20,310,-1,0,color(200,180,200),16,"Long (0-10239):",color(50,50,200)); 181 | new MessageBox(progWindow,20,340,-1,0,color(200,180,200),16,"Active :",color(50,50,200)); 182 | shortAddInput = new InputBox(progWindow,190,280,16,color(200,180,200),color(200,50,50),3,InputType.DEC); 183 | longAddInput = new InputBox(progWindow,190,310,16,color(200,180,200),color(200,50,50),5,InputType.DEC); 184 | activeAddBox = new MessageBox(progWindow,190,340,-1,0,color(200,180,200),16,"?",color(200,50,50)); 185 | new ProgAddReadButton(progWindow,300,240,65,25,100,14,"READ",shortAddInput,longAddInput,activeAddBox); 186 | new ProgShortAddWriteButton(progWindow,300,280,65,25,0,14,"WRITE",shortAddInput); 187 | new ProgLongAddWriteButton(progWindow,300,310,65,25,0,14,"WRITE",longAddInput); 188 | new ProgLongShortButton(progWindow,300,340,65,25,0,14,"Long",activeAddBox); 189 | new ProgLongShortButton(progWindow,390,340,65,25,0,14,"Short",activeAddBox); 190 | 191 | // CREATE THE PROGRAMMING CVs ON THE MAIN OPERATIONS TRACK WINDOW 192 | 193 | opWindow = new Window(500,100,500,300,color(220,200,200),color(50,50,200)); 194 | new DragBar(opWindow,0,0,500,10,color(50,50,200)); 195 | new CloseButton(opWindow,488,0,10,10,color(50,50,200),color(255,255,255)); 196 | new MessageBox(opWindow,250,30,0,0,color(220,200,200),20,"Operations Programming",color(50,100,50)); 197 | new MessageBox(opWindow,20,90,-1,0,color(220,200,200),16,"Cab Number :",color(50,50,200)); 198 | new MessageBox(opWindow,20,120,-1,0,color(220,200,200),16,"CV (1-1024):",color(50,50,200)); 199 | new MessageBox(opWindow,20,160,-1,0,color(220,200,200),16,"Value (HEX):",color(50,50,200)); 200 | new MessageBox(opWindow,20,190,-1,0,color(220,200,200),16,"Value (DEC):",color(50,50,200)); 201 | new MessageBox(opWindow,20,220,-1,0,color(220,200,200),16,"Value (BIN):",color(50,50,200)); 202 | opCabInput = new InputBox(opWindow,150,90,16,color(220,200,200),color(200,50,50),5,InputType.DEC); 203 | opCVInput = new InputBox(opWindow,150,120,16,color(220,200,200),color(200,50,50),4,InputType.DEC); 204 | opHEXInput = new InputBox(opWindow,150,160,16,color(220,200,200),color(200,50,50),2,InputType.HEX); 205 | opDECInput = new InputBox(opWindow,150,190,16,color(220,200,200),color(200,50,50),3,InputType.DEC); 206 | opBINInput = new InputBox(opWindow,150,220,16,color(220,200,200),color(200,50,50),8,InputType.BIN); 207 | opCVInput.setNextBox(opHEXInput); 208 | opHEXInput.setNextBox(opDECInput); 209 | opDECInput.setNextBox(opBINInput); 210 | opDECInput.linkBox(opHEXInput); 211 | opBINInput.setNextBox(opHEXInput); 212 | opBINInput.linkBox(opHEXInput); 213 | new OpWriteButton(opWindow,300,90,65,25,0,14,"WRITE",opCVInput,opHEXInput); 214 | new MessageBox(opWindow,20,260,-1,0,color(220,200,200),16," Bit (0-7):",color(50,50,200)); 215 | opBitInput = new InputBox(opWindow,150,260,16,color(220,200,200),color(200,50,50),1,InputType.DEC); 216 | new OpWriteButton(opWindow,300,260,65,25,50,14,"SET",opCVInput,opBitInput); 217 | new OpWriteButton(opWindow,390,260,65,25,150,14,"CLEAR",opCVInput,opBitInput); 218 | 219 | // CREATE THE DCC++ CONTROL <-> DCC++ BASE STATION COMMUNICATION DIAGNOSTICS WINDOW 220 | 221 | diagWindow = new Window(400,300,500,120,color(175),color(50,200,50)); 222 | new DragBar(diagWindow,0,0,500,10,color(50,200,50)); 223 | new CloseButton(diagWindow,488,0,10,10,color(50,200,50),color(255,255,255)); 224 | new MessageBox(diagWindow,250,20,0,0,color(175),18,"Diagnostics Window",color(50,50,200)); 225 | new MessageBox(diagWindow,10,60,-1,0,color(175),18,"Sent:",color(50,50,200)); 226 | msgBoxDiagOut=new MessageBox(diagWindow,250,60,0,0,color(175),18,"---",color(50,50,200)); 227 | new MessageBox(diagWindow,10,90,-1,0,color(175),18,"Proc:",color(50,50,200)); 228 | msgBoxDiagIn=new MessageBox(diagWindow,250,90,0,0,color(175),18,"---",color(50,50,200)); 229 | 230 | // CREATE THE AUTOPILOT DIAGNOSTICS WINDOW 231 | 232 | autoWindow = new Window(400,300,500,330,color(175),color(50,200,50)); 233 | new DragBar(autoWindow,0,0,500,10,color(50,200,50)); 234 | new CloseButton(autoWindow,488,0,10,10,color(50,200,50),color(255,255,255)); 235 | new MessageBox(autoWindow,250,20,0,0,color(175),18,"AutoPilot Window",color(50,50,150)); 236 | msgAutoState=new MessageBox(autoWindow,0,180,-1,0,color(175),18,"?",color(50,50,250)); 237 | msgAutoTimer=new MessageBox(autoWindow,55,310,-1,0,color(175),18,"Timer =",color(50,50,250)); 238 | 239 | // CREATE THE SENSORS DIAGNOSTICS WINDOW 240 | 241 | sensorWindow = new Window(400,300,500,350,color(175),color(50,200,50)); 242 | new DragBar(sensorWindow,0,0,500,10,color(50,200,50)); 243 | new CloseButton(sensorWindow,488,0,10,10,color(50,200,50),color(255,255,255)); 244 | new MessageBox(sensorWindow,250,20,0,0,color(175),18,"Sensors Window",color(50,50,150)); 245 | 246 | // CREATE THE HELP WINDOW 247 | 248 | helpWindow=new JPGWindow("helpMenu.jpg",1000,650,100,50,color(0,100,0)); 249 | 250 | // CREATE THE EXTRAS WINDOW: 251 | 252 | extrasWindow = new Window(500,200,500,250,color(255,255,175),color(100,100,200)); 253 | new DragBar(extrasWindow,0,0,500,10,color(100,100,200)); 254 | new CloseButton(extrasWindow,488,0,10,10,color(100,100,200),color(255,255,255)); 255 | new MessageBox(extrasWindow,250,20,0,0,color(175),18,"Extra Functions",color(50,50,200)); 256 | // new RectButton(extrasWindow,260,80,120,50,85,color(0),16,"Sound\nEffects",0); 257 | 258 | // CREATE THE LED LIGHT-STRIP WINDOW: 259 | 260 | ledWindow = new Window(500,200,550,425,color(0),color(0,0,200)); 261 | new DragBar(ledWindow,0,0,550,10,color(0,0,200)); 262 | new CloseButton(ledWindow,538,0,10,10,color(0,0,200),color(200,200,200)); 263 | new MessageBox(ledWindow,275,20,0,0,color(175),18,"LED Light Strip",color(200,200,200)); 264 | ledColorButton=new LEDColorButton(ledWindow,310,175,30,201,0.0,0.0,1.0); 265 | new LEDColorSelector(ledWindow,150,175,100,ledColorButton); 266 | new LEDValSelector(ledWindow,50,330,200,30,ledColorButton); 267 | ledHueMsg = new MessageBox(ledWindow,360,80,-1,0,color(175),18,"Hue: -",color(200,200,200)); 268 | ledSatMsg = new MessageBox(ledWindow,360,115,-1,0,color(175),18,"Sat: -",color(200,200,200)); 269 | ledValMsg = new MessageBox(ledWindow,360,150,-1,0,color(175),18,"Val: -",color(200,200,200)); 270 | ledRedMsg = new MessageBox(ledWindow,360,185,-1,0,color(175),18,"Red: -",color(200,200,200)); 271 | ledGreenMsg = new MessageBox(ledWindow,360,220,-1,0,color(175),18,"Green: -",color(200,200,200)); 272 | ledBlueMsg = new MessageBox(ledWindow,360,255,-1,0,color(175),18,"Blue: -",color(200,200,200)); 273 | 274 | // CREATE TOP-OF-SCREEN MESSAGE BAR AND HELP BUTTON 275 | 276 | msgBoxMain=new MessageBox(width/2,12,width,25,color(200),20,"Searching for Base Station: "+arduinoPortXML.getContent(),color(30,30,150)); 277 | new HelpButton(width-50,12,22,22,150,20,"?"); 278 | 279 | // CREATE CLOCK 280 | 281 | msgBoxClock=new MessageBox(30,700,-100,30,backgroundColor,30,"00:00:00",color(255,255,255)); 282 | 283 | // CREATE POWER BUTTON, QUIT BUTTON, and CURRENT METER 284 | 285 | powerButton=new PowerButton(75,475,100,30,100,18,"POWER"); 286 | new QuitButton(200,475,100,30,250,18,"QUIT"); 287 | currentMeter = new CurrentMeter(25,550,150,100,675,5); 288 | 289 | // CREATE THROTTLE, DEFINE CAB BUTTONS, and SET FUNCTIONS FOR EACH CAB 290 | 291 | int tAx=175; 292 | int tAy=225; 293 | int rX=800; 294 | int rY=550; 295 | 296 | throttleA=new Throttle(tAx,tAy,1.3); 297 | 298 | cab2004 = new CabButton(tAx-125,tAy-150,50,30,150,15,2004,throttleA); 299 | cab2004.setThrottleDefaults(100,50,-50,-45); 300 | cab2004.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 301 | cab2004.setFunction(35,15,60,22,60,10,0,"Headlight",ButtonType.NORMAL,CabFunction.F_LIGHT); 302 | cab2004.setFunction(35,45,60,22,60,10,1,"Tailight",ButtonType.NORMAL,CabFunction.R_LIGHT); 303 | 304 | cab622 = new CabButton(tAx-125,tAy-100,50,30,150,15,622,throttleA); 305 | cab622.setThrottleDefaults(53,30,-20,-13); 306 | cab622.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 307 | cab622.setFunction(35,15,60,22,60,10,0,"Headlight",ButtonType.NORMAL,CabFunction.F_LIGHT); 308 | cab622.setFunction(35,45,60,22,60,10,1,"Tailight",ButtonType.NORMAL,CabFunction.R_LIGHT); 309 | 310 | cab8601 = new CabButton(tAx-125,tAy-50,50,30,150,15,8601,throttleA); 311 | cab8601.setThrottleDefaults(77,46,-34,-30); 312 | cab8601.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 313 | cab8601.setFunction(35,15,60,22,60,10,0,"Lights",ButtonType.NORMAL,CabFunction.F_LIGHT,CabFunction.R_LIGHT); 314 | 315 | cab6021 = new CabButton(tAx-125,tAy,50,30,150,15,6021,throttleA); 316 | cab6021.setThrottleDefaults(50,25,-25,-15); 317 | cab6021.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 318 | cab6021.setFunction(35,15,60,22,60,10,0,"Headlight",ButtonType.NORMAL,CabFunction.F_LIGHT); 319 | cab6021.setFunction(35,45,60,22,60,10,1,"Tailight",ButtonType.NORMAL,CabFunction.R_LIGHT); 320 | 321 | cab54 = new CabButton(tAx-125,tAy+50,50,30,150,15,54,throttleA); 322 | cab54.setThrottleDefaults(34,14,-5,-3); 323 | cab54.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 324 | cab54.setFunction(35,15,60,22,60,10,10,"Radiator\nFan",ButtonType.NORMAL); 325 | cab54.setFunction(35,45,60,22,60,10,11,"Air Fill\n/Release",ButtonType.ONESHOT); 326 | cab54.setFunction(35,75,60,22,60,10,14,"Passenger\nDep/Arr",ButtonType.ONESHOT); 327 | cab54.setFunction(35,105,60,22,60,10,18,"City\nSounds",ButtonType.ONESHOT); 328 | cab54.setFunction(35,135,60,22,60,10,19,"Farm\nSounds",ButtonType.ONESHOT); 329 | cab54.setFunction(35,165,60,22,60,10,21,"Lumber\nMill",ButtonType.ONESHOT); 330 | cab54.setFunction(35,195,60,22,60,10,20,"Industry\nSounds",ButtonType.ONESHOT); 331 | cab54.setFunction(35,225,60,22,60,10,13,"Crossing\nHorn",ButtonType.ONESHOT,CabFunction.S_HORN); 332 | cab54.setFunction(35,255,60,22,60,10,22,"Alternate\nHorn",ButtonType.NORMAL); 333 | cab54.setFunction(35,285,60,22,60,10,8,"Mute",ButtonType.NORMAL); 334 | cab54.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 335 | cab54.setFunction(35,15,60,22,60,10,0,"Headlight",ButtonType.NORMAL,CabFunction.F_LIGHT); 336 | cab54.setFunction(35,45,60,22,60,10,1,"Bell",ButtonType.NORMAL,CabFunction.BELL); 337 | cab54.setFunction(35,75,60,22,60,10,2,"Horn",ButtonType.HOLD,CabFunction.HORN); 338 | cab54.setFunction(35,105,60,22,60,10,3,"MARS\nLight",ButtonType.REVERSE,CabFunction.D_LIGHT); 339 | cab54.setFunction(35,135,16,22,60,10,9,"1",ButtonType.NORMAL); 340 | cab54.setFunction(14,135,16,22,60,10,5,"+",ButtonType.ONESHOT); 341 | cab54.setFunction(56,135,16,22,60,10,6,"-",ButtonType.ONESHOT); 342 | cab54.setFunction(35,165,60,22,60,10,15,"Freight\nDep/Arr",ButtonType.ONESHOT); 343 | cab54.setFunction(35,195,60,22,60,10,16,"Facility\nShop",ButtonType.ONESHOT); 344 | cab54.setFunction(35,225,60,22,60,10,17,"Crew\nRadio",ButtonType.ONESHOT); 345 | cab54.setFunction(35,255,60,22,60,10,7,"Coupler",ButtonType.ONESHOT); 346 | cab54.setFunction(35,285,60,22,60,10,4,"Dynamic\nBrake",ButtonType.NORMAL); 347 | cab54.setFunction(35,315,60,22,60,10,12,"Brake\nSqueal",ButtonType.ONESHOT); 348 | 349 | cab1202 = new CabButton(tAx-125,tAy+100,50,30,150,15,1202,throttleA); 350 | cab1202.setThrottleDefaults(34,25,-24,-18); 351 | cab1202.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 352 | cab1202.setFunction(35,15,60,22,60,10,0,"Headlight",ButtonType.NORMAL,CabFunction.F_LIGHT); 353 | cab1202.setFunction(35,45,60,22,60,10,1,"Tailight",ButtonType.NORMAL,CabFunction.R_LIGHT); 354 | 355 | cab1506 = new CabButton(tAx-125,tAy+150,50,30,150,15,1506,throttleA); 356 | cab1506.setThrottleDefaults(61,42,-30,-22); 357 | cab1506.functionButtonWindow(220,59,70,340,backgroundColor,backgroundColor); 358 | cab1506.setFunction(35,15,60,22,60,10,1,"Headlight",ButtonType.NORMAL,CabFunction.F_LIGHT); 359 | cab1506.setFunction(35,45,60,22,60,10,0,"Tailight",ButtonType.NORMAL,CabFunction.R_LIGHT); 360 | cab1506.setFunction(35,75,60,22,60,10,3,"D-Lights",ButtonType.NORMAL,CabFunction.D_LIGHT); 361 | 362 | // CREATE THE IMAGE WINDOW FOR THROTTLE A (must be done AFTER throttle A is defined above) 363 | 364 | imageWindow=new ImageWindow(throttleA,975,450,200,50,color(200,50,50)); 365 | 366 | // CREATE AUTO PILOT BUTTON and CLEANING CAR BUTTON (must be done AFTER cab buttons are defined above) 367 | 368 | autoPilot=new AutoPilotButton(325,550,100,50,30,18,"AUTO\nPILOT"); 369 | cleaningCab=new CleaningCarButton(extrasWindow,28,80,80,120,50,40,16,"Cleaning\nCar"); 370 | 371 | // CREATE MAIN LAYOUT AND DEFINE ALL TRACKS 372 | 373 | layout=new Layout(325,50,1000,80*25.4,36*25.4); 374 | 375 | Track bridgeA = new Track(layout,20,450,62,90); 376 | Track bridgeB = new Track(bridgeA,1,348,-90); 377 | Track bridgeC = new Track(bridgeB,1,399); 378 | Track t5A = new Track(bridgeC,1,126); 379 | Track loop3A = new Track(t5A,1,682); 380 | Track loop3B = new Track(loop3A,1,381,-180); 381 | Track loop3C = new Track(loop3B,1,124); 382 | Track t20A2 = new Track(loop3C,1,126); 383 | Track t20B2 = new Track(loop3C,1,481,15); 384 | Track t20B1 = new Track(t20B2,1,481,-15); 385 | Track loop2A3A = new Track(t20A2,1,120); 386 | Track t30A1 = new Track(loop2A3A,1,126); 387 | Track t30A2 = new Track(t30A1,1,126); 388 | Track t30B1 = new Track(loop2A3A,1,481,-15); 389 | Track t30B4 = new Track(t30B1,1,481,15); 390 | Track loop2C = new Track(t30A2,1,122); 391 | Track t10A3 = new Track(loop2C,1,126); 392 | Track t10B3 = new Track(loop2C,1,481,15); 393 | Track t10A4 = new Track(t10A3,1,126); 394 | Track t10B2 = new Track(t10B3,1,481,-15); 395 | Track loop2D = new Track(t10A4,1,62); 396 | Track loop2E = new Track(loop2D,1,315,-165); 397 | Track loop2F = new Track(loop2E,1,128); 398 | Track loop2G = new Track(loop2F,1,315,-15); 399 | Track loop2H = new Track(loop2G,1,742); 400 | Track t50A2 = new Track(loop2H,1,126); 401 | Track loop2A = new Track(t50A2,1,315,-180); 402 | Track loop2B = new Track(loop2A,1,308); 403 | Track t30A3 = new Track(loop2B,1,126); 404 | Track t30A4 = new Track(t30A3,1,126); 405 | Track t30B3 = new Track(loop2B,1,481,15); 406 | Track t30B2 = new Track(t30B3,1,481,-15); 407 | Track loop1A2A = new Track(t30A4,1,60); 408 | Track t40A1 = new Track(loop1A2A,1,126); 409 | Track loop1B = new Track(t40A1,1,248); 410 | Track loop1C = new Track(loop1B,1,282,-165); 411 | Track loop1D = new Track(loop1C,1,128); 412 | Track loop1E = new Track(loop1D,1,282,-15); 413 | Track t4A = new Track(loop1E,1,126); 414 | Track t4B = new Track(loop1E,1,481,-15); 415 | Track loop1F = new Track(t4A,1,494); 416 | Track t50A1 = new Track(loop1F,1,126); 417 | Track t50B1 = new Track(loop1F,1,481,15); 418 | Track t50B2 = new Track(t50B1,1,481,-15); 419 | Track loop1G = new Track(t50A1,1,122); 420 | Track loop1H = new Track(loop1G,1,282,-180); 421 | Track loop1I = new Track(loop1H,1,62); 422 | Track t1A = new Track(loop1I,1,126); 423 | Track t1B = new Track(loop1I,1,481,-15); 424 | Track loop1A = new Track(t1A,1,308); 425 | Track t40A2 = new Track(loop1A,1,126); 426 | Track t40B2 = new Track(loop1A,1,481,15); 427 | Track t40B1 = new Track(t40B2,1,481,-15); 428 | Track s1A = new Track(t40A2,1,60); 429 | Track s1B = new Track(s1A,1,481,-15); 430 | Track s1C = new Track(s1B,1,339); 431 | Track s1 = new Track(s1C,1,50); 432 | Track loop3D = new Track(t20B1,1,370); 433 | Track t20A1 = new Track(loop3D,0,126); 434 | Track t10A1 = new Track(loop3D,1,126); 435 | Track t10B1 = new Track(loop3D,1,481,-15); 436 | Track t10B4 = new Track(t10B1,1,481,15); 437 | Track t10A2 = new Track(t10A1,1,126); 438 | Track loop3E = new Track(t10A2,1,62); 439 | Track loop3F = new Track(loop3E,1,381,-180); 440 | Track loop3G = new Track(loop3F,1,124); 441 | Track loop3H = new Track(loop3G,1,481,15); 442 | Track t5B = new Track(loop3H,1,481,-15); 443 | Track s7A = new Track(t20A1,1,337); 444 | Track s7B = new Track(s7A,1,348,90); 445 | Track s7C = new Track(s7B,1,124); 446 | Track s7D = new Track(s7C,1,481,15); 447 | Track s7E = new Track(s7D,1,124); 448 | Track s7 = new Track(s7E,1,62); 449 | Track t2A = new Track(t1B,1,126); 450 | Track t2B = new Track(t1B,1,481,-15); 451 | Track s2_3_4_5A = new Track(t2A,1,64); 452 | Track t3A = new Track(s2_3_4_5A,1,126); 453 | Track t3B = new Track(s2_3_4_5A,1,481,15); 454 | Track s2A = new Track(t3B,1,30); 455 | Track s2B = new Track(s2A,1,481,15); 456 | Track s2C = new Track(s2B,1,481,-30); 457 | Track s2D = new Track(s2C,1,248); 458 | Track s2 = new Track(s2D,1,50); 459 | Track t6A = new Track(t3A,1,126); 460 | Track t6B = new Track(t3A,1,481,-15); 461 | Track s3A = new Track(t6A,1,556); 462 | Track s3 = new Track(s3A,1,50); 463 | Track t9A = new Track(t6B,1,126); 464 | Track t9B = new Track(t6B,1,481,15); 465 | Track s4A = new Track(t9B,1,479); 466 | Track s4 = new Track(s4A,1,50); 467 | Track s5A = new Track(t9A,1,481,15); 468 | Track s5B = new Track(s5A,1,341); 469 | Track s5 = new Track(s5B,1,50); 470 | Track rLoopA = new Track(t4B,1,282,-45); 471 | Track rLoopB = new Track(rLoopA,1,87); 472 | Track t7A = new Track(rLoopB,1,126); 473 | Track t7B = new Track(rLoopB,1,481,15); 474 | Track rLoopC = new Track(t7A,1,481,15); 475 | Track rLoopD = new Track(rLoopC,1,425,15); 476 | Track s6A = new Track(t7B,1,60); 477 | Track s6B = new Track(s6A,1,282,45); 478 | Track s6C = new Track(s6B,1,481,30); 479 | Track s6D = new Track(s6C,1,188); 480 | Track s6 = new Track(s6D,1,50); 481 | Track bridgeD = new Track(bridgeA,0,348,60); 482 | 483 | // CREATE SECOND LAYOUT FOR SKY BRIDGE AND DEFINE TRACKS 484 | 485 | layout2=new Layout(325,500,400,80*25.4,36*25.4); 486 | layoutBridge=new Layout(layout2); 487 | 488 | Track bridgeE = new Track(bridgeD,1,348,60,layoutBridge); 489 | Track bridgeF = new Track(bridgeE,1,248); 490 | Track t8A = new Track(bridgeF,1,200); 491 | Track t8B = new Track(bridgeF,1,400,-35); 492 | Track bridgeG = new Track(t8A,1,618); 493 | Track bridgeH = new Track(bridgeG,1,282,-226); 494 | Track bridgeI = new Track(bridgeH,1,558); 495 | 496 | // DEFINE SENSORS, MAP TO ARDUINO NUMBERS, AND INDICATE THEIR TRACK LOCATIONS 497 | 498 | new TrackSensor(loop3B,1,30,20,20,1,false); // mappings from Sensor numbers (1..N) to Arduino Pins 499 | new TrackSensor(t50A2,1,315,-174,20,20,2,false); 500 | new TrackSensor(loop2D,1,315,-47,20,20,3,false); 501 | new TrackSensor(loop1B,1,282,-45,20,20,4,false); 502 | new TrackSensor(loop3E,1,381,-45,20,20,5,false); 503 | new TrackSensor(bridgeA,1,348,-10,20,20,6,false); 504 | new TrackSensor(s1A,1,481,-5,20,20,7,true); 505 | new TrackSensor(s2B,1,481,-5,20,20,8,true); 506 | new TrackSensor(t6A,1,175,20,20,9,true); 507 | new TrackSensor(s6A,1,282,10,20,20,10,true); 508 | new TrackSensor(loop1G,1,282,-137,20,20,11,false); 509 | new TrackSensor(t9B,1,100,20,20,12,true); 510 | new TrackSensor(s5A,1,30,20,20,13,true); 511 | new TrackSensor(s7A,1,348,50,20,20,14,true); 512 | 513 | // CREATE TURNOUT BUTTONS and ADD TRACKS FOR EACH TURNOUT 514 | 515 | tButton1 = new TrackButton(20,20,1); 516 | tButton1.addTrack(t1A,0); 517 | tButton1.addTrack(t1B,1); 518 | 519 | tButton2 = new TrackButton(20,82,2); 520 | tButton2.addTrack(t2A,0); 521 | tButton2.addTrack(t2B,1); 522 | 523 | tButton3 = new TrackButton(20,20,3); 524 | tButton3.addTrack(t3A,0); 525 | tButton3.addTrack(t3B,1); 526 | 527 | tButton4 = new TrackButton(20,20,4); 528 | tButton4.addTrack(t4A,0); 529 | tButton4.addTrack(t4B,1); 530 | 531 | tButton5 = new TrackButton(20,20,5); 532 | tButton5.addTrack(t5A,0); 533 | tButton5.addTrack(t5B,1); 534 | 535 | tButton6 = new TrackButton(20,20,6); 536 | tButton6.addTrack(t6A,0); 537 | tButton6.addTrack(t6B,1); 538 | 539 | tButton7 = new TrackButton(20,20,7); 540 | tButton7.addTrack(t7A,0); 541 | tButton7.addTrack(t7B,1); 542 | 543 | tButton8 = new TrackButton(20,20,8); 544 | tButton8.addTrack(t8A,0); 545 | tButton8.addTrack(t8B,1); 546 | 547 | tButton9 = new TrackButton(20,20,9); 548 | tButton9.addTrack(t9A,0); 549 | tButton9.addTrack(t9B,1); 550 | 551 | tButton10 = new TrackButton(20,20,10); 552 | tButton10.addTrack(t10A1,0); 553 | tButton10.addTrack(t10A2,0); 554 | tButton10.addTrack(t10A3,0); 555 | tButton10.addTrack(t10A4,0); 556 | tButton10.addTrack(t10B1,1); 557 | tButton10.addTrack(t10B2,1); 558 | tButton10.addTrack(t10B3,1); 559 | tButton10.addTrack(t10B4,1); 560 | 561 | tButton20 = new TrackButton(20,20,20); 562 | tButton20.addTrack(t20A1,0); 563 | tButton20.addTrack(t20A2,0); 564 | tButton20.addTrack(t20B1,1); 565 | tButton20.addTrack(t20B2,1); 566 | 567 | tButton30 = new TrackButton(20,20,30); 568 | tButton30.addTrack(t30A1,0); 569 | tButton30.addTrack(t30A2,0); 570 | tButton30.addTrack(t30A3,0); 571 | tButton30.addTrack(t30A4,0); 572 | tButton30.addTrack(t30B1,1); 573 | tButton30.addTrack(t30B2,1); 574 | tButton30.addTrack(t30B3,1); 575 | tButton30.addTrack(t30B4,1); 576 | 577 | tButton40 = new TrackButton(20,20,40); 578 | tButton40.addTrack(t40A1,0); 579 | tButton40.addTrack(t40A2,0); 580 | tButton40.addTrack(t40B1,1); 581 | tButton40.addTrack(t40B2,1); 582 | 583 | tButton50 = new TrackButton(20,20,50); 584 | tButton50.addTrack(t50A1,0); 585 | tButton50.addTrack(t50A2,0); 586 | tButton50.addTrack(t50B1,1); 587 | tButton50.addTrack(t50B2,1); 588 | 589 | // CREATE ROUTE BUTTONS and ADD TRACKS and TURNOUT BUTTONS 590 | 591 | rButton1 = new RouteButton(s1,20,20); 592 | rButton1.addTrackButton(tButton40,0); 593 | rButton1.addTrackButton(tButton1,0); 594 | rButton1.addTrack(t1A); 595 | rButton1.addTrack(loop1A); 596 | rButton1.addTrack(t40A2); 597 | rButton1.addTrack(s1A); 598 | rButton1.addTrack(s1B); 599 | rButton1.addTrack(s1C); 600 | rButton1.addTrack(s1); 601 | 602 | rButton2 = new RouteButton(s2,20,20); 603 | rButton2.addTrackButton(tButton3,1); 604 | rButton2.addTrackButton(tButton2,0); 605 | rButton2.addTrackButton(tButton1,1); 606 | rButton2.addTrack(t1B); 607 | rButton2.addTrack(t2A); 608 | rButton2.addTrack(s2_3_4_5A); 609 | rButton2.addTrack(t3B); 610 | rButton2.addTrack(s2A); 611 | rButton2.addTrack(s2B); 612 | rButton2.addTrack(s2C); 613 | rButton2.addTrack(s2D); 614 | rButton2.addTrack(s2); 615 | 616 | rButton3 = new RouteButton(s3,20,20); 617 | rButton3.addTrackButton(tButton6,0); 618 | rButton3.addTrackButton(tButton3,0); 619 | rButton3.addTrackButton(tButton2,0); 620 | rButton3.addTrackButton(tButton1,1); 621 | rButton3.addTrack(t1B); 622 | rButton3.addTrack(t2A); 623 | rButton3.addTrack(s2_3_4_5A); 624 | rButton3.addTrack(t3A); 625 | rButton3.addTrack(t6A); 626 | rButton3.addTrack(s3A); 627 | rButton3.addTrack(s3); 628 | 629 | rButton4 = new RouteButton(s4,20,20); 630 | rButton4.addTrackButton(tButton9,1); 631 | rButton4.addTrackButton(tButton6,1); 632 | rButton4.addTrackButton(tButton3,0); 633 | rButton4.addTrackButton(tButton2,0); 634 | rButton4.addTrackButton(tButton1,1); 635 | rButton4.addTrack(t1B); 636 | rButton4.addTrack(t2A); 637 | rButton4.addTrack(s2_3_4_5A); 638 | rButton4.addTrack(t3A); 639 | rButton4.addTrack(t6B); 640 | rButton4.addTrack(t9B); 641 | rButton4.addTrack(s4A); 642 | rButton4.addTrack(s4); 643 | 644 | rButton5 = new RouteButton(s5,20,20); 645 | rButton5.addTrackButton(tButton9,0); 646 | rButton5.addTrackButton(tButton6,1); 647 | rButton5.addTrackButton(tButton3,0); 648 | rButton5.addTrackButton(tButton2,0); 649 | rButton5.addTrackButton(tButton1,1); 650 | rButton5.addTrack(t1B); 651 | rButton5.addTrack(t2A); 652 | rButton5.addTrack(s2_3_4_5A); 653 | rButton5.addTrack(t3A); 654 | rButton5.addTrack(t6B); 655 | rButton5.addTrack(t9A); 656 | rButton5.addTrack(s5A); 657 | rButton5.addTrack(s5B); 658 | rButton5.addTrack(s5); 659 | 660 | rButton6 = new RouteButton(s6,20,20); 661 | rButton6.addTrackButton(tButton7,1); 662 | rButton6.addTrackButton(tButton4,1); 663 | rButton6.addTrack(t7B); 664 | rButton6.addTrack(t4B); 665 | rButton6.addTrack(rLoopA); 666 | rButton6.addTrack(rLoopB); 667 | rButton6.addTrack(s6A); 668 | rButton6.addTrack(s6B); 669 | rButton6.addTrack(s6C); 670 | rButton6.addTrack(s6D); 671 | rButton6.addTrack(s6); 672 | 673 | rButton7 = new RouteButton(s7,20,20); 674 | rButton7.addTrackButton(tButton20,0); 675 | rButton7.addTrackButton(tButton10,0); 676 | rButton7.addTrack(t20A1); 677 | rButton7.addTrack(t10A1); 678 | rButton7.addTrack(t10A2); 679 | rButton7.addTrack(s7A); 680 | rButton7.addTrack(s7B); 681 | rButton7.addTrack(s7C); 682 | rButton7.addTrack(s7D); 683 | rButton7.addTrack(s7E); 684 | rButton7.addTrack(s7); 685 | rButton7.addTrack(loop3D); 686 | 687 | rButton10 = new RouteButton(rX,rY,80,40,"Inner\nLoop"); 688 | rButton10.addTrackButton(tButton50,0); 689 | rButton10.addTrackButton(tButton1,0); 690 | rButton10.addTrackButton(tButton40,1); 691 | rButton10.addTrackButton(tButton4,0); 692 | rButton10.addTrack(t40B1); 693 | rButton10.addTrack(t40B2); 694 | rButton10.addTrack(t4A); 695 | rButton10.addTrack(t50A1); 696 | rButton10.addTrack(t1A); 697 | rButton10.addTrack(loop1A); 698 | rButton10.addTrack(loop1B); 699 | rButton10.addTrack(loop1C); 700 | rButton10.addTrack(loop1D); 701 | rButton10.addTrack(loop1E); 702 | rButton10.addTrack(loop1F); 703 | rButton10.addTrack(loop1G); 704 | rButton10.addTrack(loop1H); 705 | rButton10.addTrack(loop1I); 706 | 707 | rButton11 = new RouteButton(rX+200,rY,80,40,"Middle\nLoop"); 708 | rButton11.addTrackButton(tButton50,0); 709 | rButton11.addTrackButton(tButton30,1); 710 | rButton11.addTrackButton(tButton10,0); 711 | rButton11.addTrack(t50A2); 712 | rButton11.addTrack(t30B3); 713 | rButton11.addTrack(t30B2); 714 | rButton11.addTrack(t10A3); 715 | rButton11.addTrack(t10A4); 716 | rButton11.addTrack(loop2A); 717 | rButton11.addTrack(loop2B); 718 | rButton11.addTrack(loop2C); 719 | rButton11.addTrack(loop2D); 720 | rButton11.addTrack(loop2E); 721 | rButton11.addTrack(loop2F); 722 | rButton11.addTrack(loop2G); 723 | rButton11.addTrack(loop2H); 724 | 725 | rButton12 = new RouteButton(rX+400,rY,80,40,"Outer\nLoop"); 726 | rButton12.addTrackButton(tButton20,1); 727 | rButton12.addTrackButton(tButton5,1); 728 | rButton12.addTrackButton(tButton10,0); 729 | rButton12.addTrack(t20B2); 730 | rButton12.addTrack(t20B1); 731 | rButton12.addTrack(t10A1); 732 | rButton12.addTrack(t10A2); 733 | rButton12.addTrack(t5B); 734 | rButton12.addTrack(loop3A); 735 | rButton12.addTrack(loop3B); 736 | rButton12.addTrack(loop3C); 737 | rButton12.addTrack(loop3D); 738 | rButton12.addTrack(loop3E); 739 | rButton12.addTrack(loop3F); 740 | rButton12.addTrack(loop3G); 741 | rButton12.addTrack(loop3H); 742 | 743 | rButton13 = new RouteButton(rX+100,rY,80,40,"Inner/Mid"); 744 | rButton13.addTrackButton(tButton50,1); 745 | rButton13.addTrackButton(tButton30,0); 746 | rButton13.addTrackButton(tButton40,0); 747 | rButton13.addTrackButton(tButton4,0); 748 | rButton13.addTrack(t40A1); 749 | rButton13.addTrack(loop1B); 750 | rButton13.addTrack(loop1C); 751 | rButton13.addTrack(loop1D); 752 | rButton13.addTrack(loop1E); 753 | rButton13.addTrack(t4A); 754 | rButton13.addTrack(loop1F); 755 | rButton13.addTrack(t50B1); 756 | rButton13.addTrack(t50B2); 757 | rButton13.addTrack(loop2A); 758 | rButton13.addTrack(loop2B); 759 | rButton13.addTrack(t30A3); 760 | rButton13.addTrack(t30A4); 761 | rButton13.addTrack(loop1A2A); 762 | 763 | rButton14 = new RouteButton(rX+300,rY,80,40,"Mid/Outer"); 764 | rButton14.addTrackButton(tButton5,1); 765 | rButton14.addTrackButton(tButton20,0); 766 | rButton14.addTrackButton(tButton30,0); 767 | rButton14.addTrackButton(tButton10,1); 768 | rButton14.addTrack(t5B); 769 | rButton14.addTrack(loop3A); 770 | rButton14.addTrack(loop3B); 771 | rButton14.addTrack(loop3C); 772 | rButton14.addTrack(t20A2); 773 | rButton14.addTrack(loop2A3A); 774 | rButton14.addTrack(t30A1); 775 | rButton14.addTrack(t30A2); 776 | rButton14.addTrack(loop2C); 777 | rButton14.addTrack(t10B3); 778 | rButton14.addTrack(t10B2); 779 | rButton14.addTrack(loop3E); 780 | rButton14.addTrack(loop3F); 781 | rButton14.addTrack(loop3G); 782 | rButton14.addTrack(loop3H); 783 | 784 | rButtonR1 = new RouteButton(rX,rY+60,80,40,"Reverse+"); 785 | rButtonR1.addTrackButton(tButton4,1); 786 | rButtonR1.addTrackButton(tButton7,0); 787 | rButtonR1.addTrackButton(tButton1,0); 788 | rButtonR1.addTrack(t4B); 789 | rButtonR1.addTrack(rLoopA); 790 | rButtonR1.addTrack(rLoopB); 791 | rButtonR1.addTrack(t7A); 792 | rButtonR1.addTrack(t1A); 793 | 794 | rButtonR2 = new RouteButton(rX+100,rY+60,80,40,"Reverse-"); 795 | rButtonR2.addTrackButton(tButton1,1); 796 | rButtonR2.addTrackButton(tButton2,1); 797 | rButtonR2.addTrackButton(tButton4,0); 798 | rButtonR2.addTrack(t4A); 799 | rButtonR2.addTrack(t1B); 800 | rButtonR2.addTrack(t2B); 801 | rButtonR2.addTrack(rLoopC); 802 | rButtonR2.addTrack(rLoopD); 803 | 804 | rButton15 = new RouteButton(rX+200,rY+60,80,40,"Mid+Outer"); 805 | rButton15.addTrackButton(tButton5,1); 806 | rButton15.addTrackButton(tButton10,1); 807 | rButton15.addTrackButton(tButton20,1); 808 | rButton15.addTrackButton(tButton30,1); 809 | rButton15.addTrackButton(tButton50,0); 810 | rButton15.addTrack(t20B2); 811 | rButton15.addTrack(t20B1); 812 | rButton15.addTrack(t10B1); 813 | rButton15.addTrack(t10B2); 814 | rButton15.addTrack(t5B); 815 | rButton15.addTrack(loop3A); 816 | rButton15.addTrack(loop3B); 817 | rButton15.addTrack(loop3C); 818 | rButton15.addTrack(loop3D); 819 | rButton15.addTrack(loop3E); 820 | rButton15.addTrack(loop3F); 821 | rButton15.addTrack(loop3G); 822 | rButton15.addTrack(loop3H); 823 | rButton15.addTrack(t50A2); 824 | rButton15.addTrack(t30B3); 825 | rButton15.addTrack(t30B2); 826 | rButton15.addTrack(t10B3); 827 | rButton15.addTrack(t10B4); 828 | rButton15.addTrack(loop2A); 829 | rButton15.addTrack(loop2B); 830 | rButton15.addTrack(loop2C); 831 | rButton15.addTrack(loop2D); 832 | rButton15.addTrack(loop2E); 833 | rButton15.addTrack(loop2F); 834 | rButton15.addTrack(loop2G); 835 | rButton15.addTrack(loop2H); 836 | 837 | rButton16 = new RouteButton(rX+300,rY+60,80,40,"In+Outer"); 838 | rButton16.addTrackButton(tButton4,0); 839 | rButton16.addTrackButton(tButton5,1); 840 | rButton16.addTrackButton(tButton10,1); 841 | rButton16.addTrackButton(tButton20,0); 842 | rButton16.addTrackButton(tButton30,1); 843 | rButton16.addTrackButton(tButton40,0); 844 | rButton16.addTrackButton(tButton50,1); 845 | rButton16.addTrack(t40A1); 846 | rButton16.addTrack(loop1B); 847 | rButton16.addTrack(loop1C); 848 | rButton16.addTrack(loop1D); 849 | rButton16.addTrack(loop1E); 850 | rButton16.addTrack(t4A); 851 | rButton16.addTrack(loop1F); 852 | rButton16.addTrack(t50B1); 853 | rButton16.addTrack(t50B2); 854 | rButton16.addTrack(loop2A); 855 | rButton16.addTrack(loop2B); 856 | rButton16.addTrack(t30B1); 857 | rButton16.addTrack(t30B2); 858 | rButton16.addTrack(t30B3); 859 | rButton16.addTrack(t30B4); 860 | rButton16.addTrack(loop1A2A); 861 | rButton16.addTrack(t5B); 862 | rButton16.addTrack(loop3A); 863 | rButton16.addTrack(loop3B); 864 | rButton16.addTrack(loop3C); 865 | rButton16.addTrack(t20A2); 866 | rButton16.addTrack(loop2A3A); 867 | rButton16.addTrack(loop2C); 868 | rButton16.addTrack(t10B3); 869 | rButton16.addTrack(t10B2); 870 | rButton16.addTrack(loop3E); 871 | rButton16.addTrack(loop3F); 872 | rButton16.addTrack(loop3G); 873 | rButton16.addTrack(loop3H); 874 | 875 | rButton17 = new RouteButton(rX,rY+120,80,40,"Double X"); 876 | rButton17.addTrackButton(tButton5,0); 877 | rButton17.addTrackButton(tButton20,0); 878 | rButton17.addTrackButton(tButton30,1); 879 | rButton17.addTrackButton(tButton40,0); 880 | rButton17.addTrackButton(tButton50,0); 881 | rButton17.addTrackButton(tButton4,1); 882 | rButton17.addTrackButton(tButton7,0); 883 | rButton17.addTrackButton(tButton1,0); 884 | rButton17.addTrack(t4B); 885 | rButton17.addTrack(rLoopA); 886 | rButton17.addTrack(rLoopB); 887 | rButton17.addTrack(t7A); 888 | rButton17.addTrack(t1A); 889 | rButton17.addTrack(loop1B); 890 | rButton17.addTrack(loop1C); 891 | rButton17.addTrack(loop1D); 892 | rButton17.addTrack(loop1E); 893 | rButton17.addTrack(loop1F); 894 | rButton17.addTrack(loop1G); 895 | rButton17.addTrack(loop1H); 896 | rButton17.addTrack(loop1I); 897 | rButton17.addTrack(t50A1); 898 | rButton17.addTrack(t40A1); 899 | rButton17.addTrack(loop1A2A); 900 | rButton17.addTrack(t30B4); 901 | rButton17.addTrack(t30B1); 902 | rButton17.addTrack(loop3A); 903 | rButton17.addTrack(loop3B); 904 | rButton17.addTrack(loop3C); 905 | rButton17.addTrack(t20A2); 906 | rButton17.addTrack(loop2A3A); 907 | rButton17.addTrack(t5A); 908 | rButton17.addTrack(bridgeA); 909 | rButton17.addTrack(bridgeB); 910 | rButton17.addTrack(bridgeC); 911 | rButton17.addTrack(bridgeD); 912 | rButton17.addTrack(bridgeE); 913 | rButton17.addTrack(bridgeF); 914 | rButton17.addTrack(bridgeG); 915 | rButton17.addTrack(bridgeH); 916 | rButton17.addTrack(bridgeI); 917 | 918 | rButtonSpiral = new RouteButton(rX+100,rY+120,80,40,"Spiral"); 919 | rButtonSpiral.addTrackButton(tButton4,1); 920 | rButtonSpiral.addTrackButton(tButton5,0); 921 | rButtonSpiral.addTrackButton(tButton7,0); 922 | rButtonSpiral.addTrackButton(tButton1,0); 923 | rButtonSpiral.addTrackButton(tButton10,0); 924 | rButtonSpiral.addTrackButton(tButton20,0); 925 | rButtonSpiral.addTrackButton(tButton30,0); 926 | rButtonSpiral.addTrackButton(tButton40,0); 927 | rButtonSpiral.addTrackButton(tButton50,0); 928 | rButtonSpiral.addTrack(t4B); 929 | rButtonSpiral.addTrack(rLoopA); 930 | rButtonSpiral.addTrack(rLoopB); 931 | rButtonSpiral.addTrack(t7A); 932 | rButtonSpiral.addTrack(t1A); 933 | rButtonSpiral.addTrack(loop1B); 934 | rButtonSpiral.addTrack(loop1C); 935 | rButtonSpiral.addTrack(loop1D); 936 | rButtonSpiral.addTrack(loop1E); 937 | rButtonSpiral.addTrack(loop1F); 938 | rButtonSpiral.addTrack(loop1G); 939 | rButtonSpiral.addTrack(loop1H); 940 | rButtonSpiral.addTrack(loop1I); 941 | rButtonSpiral.addTrack(t50A1); 942 | rButtonSpiral.addTrack(t40A1); 943 | rButtonSpiral.addTrack(loop1A2A); 944 | rButtonSpiral.addTrack(t30A4); 945 | rButtonSpiral.addTrack(t30A3); 946 | rButtonSpiral.addTrack(t50A2); 947 | rButtonSpiral.addTrack(loop2A); 948 | rButtonSpiral.addTrack(loop2B); 949 | rButtonSpiral.addTrack(loop2C); 950 | rButtonSpiral.addTrack(loop2D); 951 | rButtonSpiral.addTrack(loop2E); 952 | rButtonSpiral.addTrack(loop2F); 953 | rButtonSpiral.addTrack(loop2G); 954 | rButtonSpiral.addTrack(loop2H); 955 | rButtonSpiral.addTrack(t10A3); 956 | rButtonSpiral.addTrack(t10A4); 957 | rButtonSpiral.addTrack(t30A1); 958 | rButtonSpiral.addTrack(t30A2); 959 | rButtonSpiral.addTrack(loop2A3A); 960 | rButtonSpiral.addTrack(t20A2); 961 | rButtonSpiral.addTrack(loop3A); 962 | rButtonSpiral.addTrack(loop3B); 963 | rButtonSpiral.addTrack(loop3C); 964 | rButtonSpiral.addTrack(t5A); 965 | rButtonSpiral.addTrack(bridgeA); 966 | rButtonSpiral.addTrack(bridgeB); 967 | rButtonSpiral.addTrack(bridgeC); 968 | rButtonSpiral.addTrack(bridgeD); 969 | rButtonSpiral.addTrack(bridgeE); 970 | rButtonSpiral.addTrack(bridgeF); 971 | rButtonSpiral.addTrack(bridgeG); 972 | rButtonSpiral.addTrack(bridgeH); 973 | rButtonSpiral.addTrack(bridgeI); 974 | 975 | rButtonReset = new RouteButton(rX+400,rY+120,80,40,"RESET"); 976 | rButtonReset.addTrackButton(tButton40,0); 977 | rButtonReset.addTrackButton(tButton3,0); 978 | rButtonReset.addTrackButton(tButton2,0); 979 | rButtonReset.addTrackButton(tButton8,0); 980 | rButtonReset.addTrackButton(tButton10,0); 981 | rButtonReset.addTrackButton(tButton20,0); 982 | rButtonReset.addTrackButton(tButton9,0); 983 | rButtonReset.addTrackButton(tButton4,0); 984 | rButtonReset.addTrackButton(tButton1,0); 985 | rButtonReset.addTrackButton(tButton5,0); 986 | rButtonReset.addTrackButton(tButton50,0); 987 | rButtonReset.addTrackButton(tButton6,0); 988 | rButtonReset.addTrackButton(tButton7,0); 989 | rButtonReset.addTrackButton(tButton30,0); 990 | rButtonReset.addTrack(t1A); 991 | rButtonReset.addTrack(t2A); 992 | rButtonReset.addTrack(t3A); 993 | rButtonReset.addTrack(t4A); 994 | rButtonReset.addTrack(t5A); 995 | rButtonReset.addTrack(t6A); 996 | rButtonReset.addTrack(t7A); 997 | rButtonReset.addTrack(t8A); 998 | rButtonReset.addTrack(t9A); 999 | rButtonReset.addTrack(t10A1); 1000 | rButtonReset.addTrack(t10A2); 1001 | rButtonReset.addTrack(t10A3); 1002 | rButtonReset.addTrack(t10A4); 1003 | rButtonReset.addTrack(t20A1); 1004 | rButtonReset.addTrack(t20A2); 1005 | rButtonReset.addTrack(t30A1); 1006 | rButtonReset.addTrack(t30A2); 1007 | rButtonReset.addTrack(t30A3); 1008 | rButtonReset.addTrack(t30A4); 1009 | rButtonReset.addTrack(t40A1); 1010 | rButtonReset.addTrack(t40A2); 1011 | rButtonReset.addTrack(t50A1); 1012 | rButtonReset.addTrack(t50A2); 1013 | 1014 | rButtonBridge = new RouteButton(bridgeA,20,20); 1015 | rButtonBridge.addTrackButton(tButton5,0); 1016 | rButtonBridge.addTrackButton(tButton8,0); 1017 | rButtonBridge.addTrack(t5A); 1018 | rButtonBridge.addTrack(bridgeA); 1019 | rButtonBridge.addTrack(bridgeB); 1020 | rButtonBridge.addTrack(bridgeC); 1021 | rButtonBridge.addTrack(bridgeD); 1022 | rButtonBridge.addTrack(bridgeE); 1023 | rButtonBridge.addTrack(bridgeF); 1024 | rButtonBridge.addTrack(bridgeG); 1025 | rButtonBridge.addTrack(bridgeH); 1026 | rButtonBridge.addTrack(bridgeI); 1027 | rButtonBridge.addTrack(t8A); 1028 | 1029 | cab622.setSidingDefaults(rButton6,4,10); // must set default sidings AFTER rButtons are defined above 1030 | cab6021.setSidingDefaults(rButton1,11,7); 1031 | cab54.setSidingDefaults(rButton2,11,8); 1032 | cab1506.setSidingDefaults(rButton3,11,9); 1033 | cab8601.setSidingDefaults(rButton4,11,12); 1034 | cab1202.setSidingDefaults(rButton5,11,13); 1035 | cab2004.setSidingDefaults(rButton7,5,14); 1036 | 1037 | } // Initialize 1038 | 1039 | ////////////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /DCCpp_Controller/coreComponents.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Core Components 3 | // 4 | // PowerButton - send power on/off command to the DCC++ Base Station 5 | // 6 | // CurrentMeter - monitors main track current draw from the DCC++ Base Station 7 | // - displays scrolling bar chart of current measured 8 | // 9 | // HelpButton - toggles Help Window 10 | // 11 | // QuitButton - quits DCC++ Controller 12 | // - connection to DCC++ Base Station terminated 13 | // - NOTE: track power remains on and trains will continue to operate 14 | // since DCC+ Base Station operates independently! 15 | // 16 | // AccessoryButton - sends a DCC ACCESSORY COMMAND to the DCC++ Base Station 17 | // to either activate or de-activate an accessory depending on 18 | // whether the button is labeled "ON" or "OFF" 19 | // - two pre-specified input boxes are used: one for the user 20 | // to input the desired accessory address, and one for 21 | // accessory number (sub-address) 22 | // - the default configuration of DCC++ Controller defines an 23 | // Accessory Window that includes these two input boxes as well 24 | // as ON and OFF buttons. 25 | // 26 | // CleaningCarButton - sends a DCC THROTTLE COMMAND to the DCC++ Base Station that operates 27 | // a mobile decoder with a pre-specified cab number 28 | // - this decoder drives a motor that spins a cleaning pad in a 29 | // track-cleaning car 30 | // - clicking the button toggles the throttle between either 0 or 126 (max speed) 31 | // - the default configuration of DCC++ Controller defines an 32 | // Extras Window that includes this button 33 | // 34 | // LEDColorButton - provide for interactive control of an LED-RGB Light Strip 35 | 36 | ////////////////////////////////////////////////////////////////////////// 37 | // DCC Component: PowerButton 38 | ////////////////////////////////////////////////////////////////////////// 39 | 40 | class PowerButton extends RectButton{ 41 | 42 | PowerButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 43 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText); 44 | } 45 | 46 | PowerButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 47 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.NORMAL); 48 | } // PowerButton 49 | 50 | ////////////////////////////////////////////////////////////////////////// 51 | 52 | void turnOn(){ 53 | aPort.write("<1>"); 54 | } 55 | 56 | ////////////////////////////////////////////////////////////////////////// 57 | 58 | void shiftPressed(){ 59 | aPort.write(""); 60 | exit(); 61 | } 62 | 63 | ////////////////////////////////////////////////////////////////////////// 64 | 65 | void turnOff(){ 66 | aPort.write("<0>"); 67 | } 68 | 69 | } // PowerButton Class 70 | 71 | ////////////////////////////////////////////////////////////////////////// 72 | // DCC Component: CurrentMeter 73 | ////////////////////////////////////////////////////////////////////////// 74 | 75 | class CurrentMeter extends DccComponent{ 76 | int nSamples, kHeight; 77 | int maxCurrent; 78 | int[] samples; 79 | int sampleIndex; 80 | int nGridLines; 81 | boolean isOn; 82 | 83 | CurrentMeter(int xPos, int yPos, int nSamples, int kHeight, int maxCurrent, int nGridLines){ 84 | this.xPos=xPos; 85 | this.yPos=yPos; 86 | this.nSamples=nSamples; 87 | this.kHeight=kHeight; 88 | this.maxCurrent=maxCurrent; 89 | this.nGridLines=nGridLines; 90 | this.isOn=true; 91 | samples=new int[nSamples]; 92 | sampleIndex=nSamples-1; 93 | dccComponents.add(this); 94 | } // CurrentMeter 95 | 96 | ////////////////////////////////////////////////////////////////////////// 97 | 98 | void display(){ 99 | int i; 100 | rectMode(CORNER); 101 | noFill(); 102 | strokeWeight(1); 103 | textFont(buttonFont,8); 104 | textAlign(LEFT,CENTER); 105 | stroke(200); 106 | rect(xPos,yPos,nSamples+1,kHeight+2); 107 | if(isOn) 108 | stroke(50,200,100); 109 | else 110 | stroke(200,100,100); 111 | for(i=0;i511) 158 | msgBoxMain.setMessage("Error - Accessory Address must be in range 0-511",color(255,30,30)); 159 | else if(accSubAddress>3) 160 | msgBoxMain.setMessage("Error - Accessory Sub Address must be in range 0-3",color(255,30,30)); 161 | else 162 | aPort.write(""); 163 | } 164 | 165 | } // AccessoryButton Class 166 | 167 | ////////////////////////////////////////////////////////////////////////// 168 | // DCC Component: Quit Button 169 | ////////////////////////////////////////////////////////////////////////// 170 | 171 | class QuitButton extends RectButton{ 172 | 173 | QuitButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 174 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText); 175 | } 176 | 177 | QuitButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 178 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.NORMAL); 179 | } // PowerButton 180 | 181 | ////////////////////////////////////////////////////////////////////////// 182 | 183 | void turnOn(){ 184 | super.turnOn(); 185 | exit(); 186 | } 187 | 188 | } // QuitButton Class 189 | 190 | ////////////////////////////////////////////////////////////////////////// 191 | // DCC Component: Help Button 192 | ////////////////////////////////////////////////////////////////////////// 193 | 194 | class HelpButton extends EllipseButton{ 195 | 196 | HelpButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 197 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText); 198 | } 199 | 200 | HelpButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 201 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 202 | } // PowerButton 203 | 204 | ////////////////////////////////////////////////////////////////////////// 205 | 206 | void pressed(){ 207 | super.pressed(); 208 | helpWindow.toggle(); 209 | } 210 | 211 | } // HelpButton Class 212 | 213 | ////////////////////////////////////////////////////////////////////////// 214 | // DCC Component: CleaningCar Button 215 | ////////////////////////////////////////////////////////////////////////// 216 | 217 | class CleaningCarButton extends RectButton{ 218 | int cab; 219 | int reg; 220 | 221 | CleaningCarButton(int cab, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 222 | this(null, cab, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText); 223 | } 224 | 225 | CleaningCarButton(Window window, int cab, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 226 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.NORMAL); 227 | reg=cabButtons.size()+1; 228 | this.cab=cab; 229 | } // PowerButton 230 | 231 | ////////////////////////////////////////////////////////////////////////// 232 | 233 | void turnOn(){ 234 | super.turnOn(); 235 | aPort.write(""); 236 | 237 | } 238 | 239 | ////////////////////////////////////////////////////////////////////////// 240 | 241 | void turnOff(){ 242 | super.turnOff(); 243 | aPort.write(""); 244 | } 245 | 246 | ////////////////////////////////////////////////////////////////////////// 247 | 248 | void shiftPressed(){ 249 | autoPilot.clean(); 250 | } 251 | 252 | } // CleaningCarButton Class 253 | 254 | ////////////////////////////////////////////////////////////////////////// 255 | // DCC Component: LED Color Button 256 | ////////////////////////////////////////////////////////////////////////// 257 | 258 | class LEDColorButton extends DccComponent{ 259 | 260 | int bWidth, bHeight; 261 | float hue; 262 | float sat; 263 | float val; 264 | 265 | LEDColorButton(Window window, int xPos, int yPos, int bWidth, int bHeight, float hue, float sat, float val){ 266 | this.xPos=xPos; 267 | this.yPos=yPos; 268 | this.bWidth=bWidth; 269 | this.bHeight=bHeight; 270 | this.hue=hue; 271 | this.sat=sat; 272 | this.val=val; 273 | this.window=window; 274 | window.windowComponents.add(this); 275 | } 276 | 277 | ////////////////////////////////////////////////////////////////////////// 278 | 279 | void display(){ 280 | rectMode(CENTER); 281 | colorMode(HSB,1.0,1.0,1.0); 282 | fill(hue,sat,val); 283 | rect(xPos+xWindow(),yPos+yWindow(),bWidth,bHeight); 284 | colorMode(RGB,255); 285 | } 286 | 287 | ////////////////////////////////////////////////////////////////////////// 288 | 289 | void update(int s){ 290 | color c; 291 | colorMode(HSB,1.0,1.0,1.0); 292 | c=color(hue,sat,val); 293 | colorMode(RGB,255); 294 | aPort.write(""); 295 | ledHueMsg.setMessage("Hue: "+int(hue*360),color(200,200,200)); 296 | ledSatMsg.setMessage("Sat: "+int(sat*100),color(200,200,200)); 297 | ledValMsg.setMessage("Val: "+int(val*100),color(200,200,200)); 298 | ledRedMsg.setMessage("Red: "+int(red(c)),color(200,200,200)); 299 | ledGreenMsg.setMessage("Green: "+int(green(c)),color(200,200,200)); 300 | ledBlueMsg.setMessage("Blue: "+int(blue(c)),color(200,200,200)); 301 | } 302 | 303 | } // LEDColorButton Class 304 | 305 | ////////////////////////////////////////////////////////////////////////// 306 | // DCC Component: LED Value Selector 307 | ////////////////////////////////////////////////////////////////////////// 308 | 309 | class LEDValSelector extends DccComponent{ 310 | 311 | int bWidth, bHeight; 312 | LEDColorButton cButton; 313 | PImage valBox; 314 | 315 | LEDValSelector(Window window, int xPos, int yPos, int bWidth, int bHeight, LEDColorButton cButton){ 316 | this.xPos=xPos; 317 | this.yPos=yPos; 318 | this.bWidth=bWidth; 319 | this.bHeight=bHeight; 320 | this.cButton=cButton; 321 | valBox = createImage(bWidth+1,bHeight+1,RGB); 322 | this.window=window; 323 | window.windowComponents.add(this); 324 | 325 | colorMode(HSB,1.0,1.0,1.0); 326 | valBox.loadPixels(); 327 | 328 | for(int y=0;y=xPos+xWindow()+cButton.val*float(bWidth)-5 && mouseX<=xPos+xWindow()+cButton.val*float(bWidth)+5 && mouseY>=yPos+yWindow()+bHeight+2 && mouseY<=yPos+yWindow()+bHeight+22){ 368 | cursorType=HAND; 369 | selectedComponent=this; 370 | } 371 | } 372 | 373 | ////////////////////////////////////////////////////////////////////////// 374 | 375 | void drag(){ 376 | cButton.val=constrain(float(mouseX-xPos-xWindow())/bWidth,0.0,1.0); 377 | cButton.update(0); 378 | } 379 | 380 | ////////////////////////////////////////////////////////////////////////// 381 | 382 | void released(){ 383 | cButton.update(1); 384 | } 385 | 386 | } // LEDValSelector Class 387 | 388 | ////////////////////////////////////////////////////////////////////////// 389 | // DCC Component: LED Color Selector 390 | ////////////////////////////////////////////////////////////////////////// 391 | 392 | class LEDColorSelector extends DccComponent{ 393 | 394 | PImage colorWheel; 395 | int radius; 396 | LEDColorButton cButton; 397 | 398 | LEDColorSelector(Window window, int xPos, int yPos, int radius, LEDColorButton cButton){ 399 | float d, h; 400 | 401 | this.xPos=xPos; 402 | this.yPos=yPos; 403 | this.radius=radius; 404 | this.cButton=cButton; 405 | colorWheel=createImage(radius*2+1,radius*2+1,RGB); 406 | this.window=window; 407 | window.windowComponents.add(this); 408 | 409 | colorWheel.loadPixels(); 410 | colorMode(HSB,1.0,1.0,1.0); 411 | 412 | for(int i=0, y=radius;y>=-radius;y--){ 413 | for(int x=-radius;x<=radius;x++){ 414 | d=sqrt(x*x+y*y); 415 | if(d<0.5){ 416 | colorWheel.pixels[i]=color(0.0,0.0,1.0); // center of wheel always has zero saturation (hue does not matter) 417 | } else 418 | if(d>radius){ 419 | colorWheel.pixels[i]=color(0.0,0.0,0.0); // outside of wheel is always fully black (hue and saturation does not matter) 420 | } else { 421 | h=acos(float(x)/d); // find angle in radians 422 | if(y<0) // adjust angle to reflect lower half of wheel 423 | h=TWO_PI-h; 424 | colorWheel.pixels[i]=color(h/TWO_PI,d/float(radius),1.0); // hue is based on angle normalized to 1.0, saturation is based on distance to center normalized to 1.0, brightness is always 1.0 425 | } 426 | i++; 427 | } // x-loop 428 | } // y-loop 429 | 430 | colorMode(RGB,255); 431 | colorWheel.updatePixels(); 432 | } 433 | 434 | ////////////////////////////////////////////////////////////////////////// 435 | 436 | void display(){ 437 | imageMode(CENTER); 438 | colorMode(HSB,1.0,1.0,1.0); 439 | image(colorWheel,xPos+xWindow(),yPos+yWindow()); 440 | colorMode(RGB,255); 441 | 442 | } 443 | 444 | ////////////////////////////////////////////////////////////////////////// 445 | 446 | void check(){ 447 | 448 | if(selectedComponent==null && ((pow(mouseX-xPos-xWindow(),2)+pow(mouseY-yPos-yWindow(),2))<=pow(radius,2))){ 449 | cursorType=CROSS; 450 | selectedComponent=this; 451 | } 452 | 453 | } // check 454 | 455 | ////////////////////////////////////////////////////////////////////////// 456 | 457 | void pressed(){ 458 | drag(); 459 | } 460 | 461 | ////////////////////////////////////////////////////////////////////////// 462 | 463 | void drag(){ 464 | float d,h; 465 | color selectedColor; 466 | 467 | d=sqrt(pow(mouseX-xPos-xWindow(),2)+pow(mouseY-yPos-yWindow(),2)); 468 | if(d<0.5){ 469 | h=0.0; 470 | } else { 471 | h=acos(float(mouseX-xPos-xWindow())/d); 472 | if(mouseY>(yPos+yWindow())) 473 | h=TWO_PI-h; 474 | cButton.hue=h/TWO_PI; 475 | cButton.sat=constrain(d/float(radius),0.0,1.0); 476 | } 477 | 478 | cButton.update(0); 479 | 480 | } 481 | 482 | ////////////////////////////////////////////////////////////////////////// 483 | 484 | void released(){ 485 | cButton.update(1); 486 | } 487 | 488 | } // LEDColorSelector Class 489 | 490 | ////////////////////////////////////////////////////////////////////////// 491 | -------------------------------------------------------------------------------- /DCCpp_Controller/coreConstants.java: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Constants 3 | ////////////////////////////////////////////////////////////////////////// 4 | 5 | enum ButtonType{ 6 | NORMAL, 7 | ONESHOT, 8 | HOLD, 9 | REVERSE, 10 | T_COMMAND, 11 | TI_COMMAND, 12 | Z_COMMAND 13 | } 14 | 15 | enum InputType{ 16 | BIN ("[01]"), 17 | DEC ("[0-9]"), 18 | HEX ("[A-Fa-f0-9]"); 19 | 20 | final String regexp; 21 | InputType(String regexp){ 22 | this.regexp=regexp; 23 | } 24 | } 25 | 26 | enum CabFunction{ 27 | F_LIGHT, 28 | R_LIGHT, 29 | D_LIGHT, 30 | BELL, 31 | HORN, 32 | S_HORN 33 | } 34 | 35 | enum ThrottleSpeed{ 36 | FULL, 37 | SLOW, 38 | STOP, 39 | REVERSE, 40 | REVERSE_SLOW; 41 | 42 | static ThrottleSpeed index(String findName){ 43 | for(ThrottleSpeed p : ThrottleSpeed.values()){ 44 | if(p.name().equals(findName)) 45 | return(p); 46 | } 47 | return(null); 48 | } 49 | } 50 | 51 | enum AutoProgram{ 52 | NONE ("NONE"), 53 | ALL_CABS_RUN ("ALL CABS RUN"), 54 | ALL_CABS_PARK ("ALL CABS PARK"), 55 | SINGLE_CAB_PARK ("SINGLE CAB PARK"), 56 | AUTO_CLEAN ("AUTO CLEAN"), 57 | SINGLE_CAB_RUN ("SINGLE CAB RUN"); 58 | 59 | String name; 60 | AutoProgram(String name){ 61 | this.name=name; 62 | } 63 | static AutoProgram index(String findName){ 64 | for(AutoProgram p : AutoProgram.values()){ 65 | if(p.name.equals(findName)) 66 | return(p); 67 | } 68 | return(null); 69 | } 70 | 71 | boolean equals(AutoProgram p){ 72 | return(this==p); 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /DCCpp_Controller/dCabs.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Classes for Cab Throttle and Cab Function Controls 3 | // 4 | // Throttle - creates a sliding throttle to set the speed and direction 5 | // of one or more locomotive cabs 6 | // - cabs are selected by clicking any of the cab buttons 7 | // that have been associated with the throttle 8 | // - multiple throttles, each with a distinct set of cab buttons, 9 | // is allowed. It is also possible to define one throttle per 10 | // cab, in which case a visible cab button would not be needed 11 | // since there is nothing to select 12 | // - moving the slider up or down sends a DCC THROTTLE COMMAND to 13 | // the DCC++ Base Station with the cab addres and register number 14 | // specified by the selected can button 15 | // - throttle commands assume mobile decoders are configured for 128-step speed control 16 | // with speeds ranging from a minimum of 0 to a maximum of 126. 17 | // - the throttle command sent to the DCC++ Base Station also codes whether motion 18 | // is forward or backward 19 | // - negative throttle numbers are NOT used to indicate reverse motion 20 | // - a negative throttle number is used to instruct the DCC++ Base Station 21 | // to initiate an immediate emergency stop of the specified cab. 22 | // - this is in contrast to setting the throttle to 0, in which case the 23 | // cab will stop according to any deceleration parameters (which may allow the locomotive 24 | // to coast before stopping) 25 | // - throttle slider can also be controlled with arrows as follows: 26 | // 27 | // * UP ARROW = increase speed by one unit in the forward direction 28 | // (which decreases speed if already moving in the reverse direction) 29 | // * DOWN ARROW = increase speed by one unit in the reverse direction 30 | // (which decreases speed if already moving in the forward direction) 31 | // * LEFT ARROW = set speed to zero (locomotive will coast to stop if configured with deceleration) 32 | // * RIGHT ARROW = emergency stop (locomotive will stop immediately, ignoring any deceleration parameters) 33 | // 34 | // - Note: throttle slider and arrow buttons will not permit single action that causes locomotive 35 | // to stop and then reverse. This allows users to move slider or press arrow keys to slow 36 | // locomotive to zero without worrying about overshooting and reversing direction. Once slider is 37 | // at zero, reclick to start sliding in reverse direction. 38 | // 39 | // CabButton - defines a button to activate a given cab address for a given throttle 40 | // - in addition to the cab address (which can be short or long), the button 41 | // contains: 42 | // 43 | // * informaiton on which register number the DCC++ Base Station 44 | // should use for throttle commands to this cab 45 | // * a data structure indicating which cab functions (lights, horns, etc.) 46 | // are defined for this cab 47 | // 48 | // FunctionButton - sends a CAB FUNCTION COMMMAND to the DCC++ Base Station to 49 | // activate or de-activate any cab function F0-F12 50 | // - function buttons are always associated with a particular throttle, but 51 | // are dynamically configured according to the cab selected 52 | // to be active for that throttle 53 | // - configuration information for each function button is stored in 54 | // a data structure contained within each cab button 55 | // - configuration data includes the name of each button and whether the function 56 | // should: 57 | // 58 | // * be toggled from on to off, or off to on, with each mouse click (e.g. a headlight) 59 | // * be activated upon pressing the mouse button but de-active when the mouse 60 | // button is released (e.g. a horn) 61 | // * be turned on and then immediately off with a single mouse click (e.g. coupler sounds) 62 | 63 | ////////////////////////////////////////////////////////////////////////// 64 | // DCC Component: Throttle 65 | ////////////////////////////////////////////////////////////////////////// 66 | 67 | class Throttle extends DccComponent{ 68 | final int KMAXPOS=126; 69 | final int KMINPOS=-126; 70 | int kWidth=50; 71 | int kHeight=15; 72 | int sPos,sSpeed; 73 | int kMaxTemp, kMinTemp; 74 | float tScale; 75 | CabButton cabButton=null; 76 | 77 | Throttle(int xPos, int yPos, float tScale){ 78 | this.xPos=xPos; 79 | this.yPos=yPos; 80 | this.tScale=tScale; 81 | dccComponents.add(this); 82 | } // Throttle 83 | 84 | ////////////////////////////////////////////////////////////////////////// 85 | 86 | void display(){ 87 | int i; 88 | 89 | rectMode(CENTER); 90 | ellipseMode(CENTER); 91 | strokeWeight(1); 92 | noStroke(); 93 | fill(255); 94 | rect(xPos,yPos,kWidth/2.0,(KMAXPOS-KMINPOS)*tScale); 95 | fill(0); 96 | rect(xPos,yPos,kWidth/4.0,(KMAXPOS-KMINPOS)*tScale); 97 | 98 | stroke(0); 99 | for(i=0;iKMINPOS*tScale;i-=10*tScale) 102 | line(xPos-kWidth/4.0,yPos-i,xPos+kWidth/4.0,yPos-i); 103 | 104 | if(cabButton==null) 105 | return; 106 | 107 | noStroke(); 108 | for(i=kWidth;i>0;i--){ 109 | fill(230-(i*2),230-(i*2),255-(i*3)); 110 | ellipse(xPos,yPos-cabButton.speed*tScale,i,i/2); 111 | } 112 | 113 | } // display 114 | 115 | ////////////////////////////////////////////////////////////////////////// 116 | 117 | void check(){ 118 | 119 | if(cabButton==null) 120 | return; 121 | 122 | if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-(yPos-cabButton.speed*tScale))*(mouseY-(yPos-cabButton.speed*tScale))/(kWidth*kWidth/16.0)<=1){ 123 | cursorType=HAND; 124 | selectedComponent=this; 125 | } 126 | } // check 127 | 128 | ////////////////////////////////////////////////////////////////////////// 129 | 130 | void pressed(){ 131 | sPos=mouseY; 132 | sSpeed=cabButton.speed; 133 | 134 | if(sSpeed>0){ 135 | kMaxTemp=KMAXPOS; 136 | kMinTemp=0; 137 | } 138 | else if(sSpeed<0){ 139 | kMaxTemp=0; 140 | kMinTemp=KMINPOS; 141 | } 142 | else{ 143 | kMaxTemp=KMAXPOS; 144 | kMinTemp=KMINPOS; 145 | } 146 | 147 | noCursor(); 148 | } // pressed 149 | 150 | ////////////////////////////////////////////////////////////////////////// 151 | 152 | void drag(){ 153 | int tPos; 154 | 155 | tPos=constrain(int((sPos-mouseY)/tScale)+sSpeed,kMinTemp,kMaxTemp); 156 | 157 | if(tPos>0) 158 | kMinTemp=0; 159 | else if(tPos<0) 160 | kMaxTemp=0; 161 | 162 | cabButton.setThrottle(tPos); 163 | } // drag 164 | 165 | ////////////////////////////////////////////////////////////////////////// 166 | 167 | void keyControl(int m){ 168 | int tPos; 169 | 170 | if(m==0){ // emergency stop 171 | tPos=0; 172 | cabButton.throttleSpeed=ThrottleSpeed.STOP; 173 | } else { 174 | tPos=constrain(sSpeed+=m,kMinTemp,kMaxTemp); 175 | } 176 | 177 | if(tPos>0) 178 | kMinTemp=0; 179 | else if(tPos<0) 180 | kMaxTemp=0; 181 | 182 | cabButton.setThrottle(tPos); 183 | 184 | } // keyControl 185 | 186 | } // Throttle Class 187 | 188 | ////////////////////////////////////////////////////////////////////////// 189 | // DCC Component: CabButton 190 | ////////////////////////////////////////////////////////////////////////// 191 | 192 | class CabButton extends RectButton{ 193 | int reg, cab; 194 | int speed=0; 195 | String name; 196 | RouteButton sidingRoute; 197 | int sidingSensor=0; 198 | int parkingSensor=0; 199 | XML speedXML, cabDefaultXML; 200 | XML throttleDefaultsXML; 201 | ThrottleSpeed throttleSpeed=ThrottleSpeed.STOP; 202 | Window fbWindow; 203 | ArrayList windowList = new ArrayList(); 204 | int[] fStatus = new int[29]; 205 | HashMap functionsHM = new HashMap(); 206 | Throttle throttle; 207 | PImage cabImage; 208 | String cabFile; 209 | Window editWindow; 210 | InputBox cabNumInput; 211 | 212 | CabButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, int cab, Throttle throttle){ 213 | super(null, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, str(cab), ButtonType.NORMAL); 214 | this.cab=cab; 215 | this.throttle=throttle; 216 | cabButtons.add(this); 217 | reg=cabButtons.size(); 218 | cabFile=("cab-"+cab+".jpg"); 219 | cabImage=loadImage(cabFile); 220 | name="Cab"+cab; 221 | cabsHM.put(name,this); 222 | colorMode(HSB,255); 223 | editWindow = new Window(xPos-(bWidth/2),yPos-(bHeight/2),bWidth,bHeight,color(baseHue,255,255),color(baseHue,255,125)); 224 | cabNumInput = new InputBox(this); 225 | 226 | speedXML=autoPilotXML.getChild(name); 227 | if(speedXML==null){ 228 | speedXML=autoPilotXML.addChild(name); 229 | speedXML.setContent(ThrottleSpeed.STOP.name()); 230 | } 231 | 232 | cabDefaultXML=cabDefaultsXML.getChild(name); 233 | if(cabDefaultXML==null){ 234 | cabDefaultXML=cabDefaultsXML.addChild(name); 235 | } 236 | 237 | } // CabButton 238 | 239 | ////////////////////////////////////////////////////////////////////////// 240 | 241 | void display(){ 242 | super.display(); 243 | 244 | imageMode(CENTER); 245 | fill(30); 246 | rect(xPos+bWidth/2+30,yPos,42,20); 247 | stroke(backgroundColor); 248 | line(xPos+bWidth/2+23,yPos-10,xPos+bWidth/2+23,yPos+10); 249 | line(xPos+bWidth/2+37,yPos-10,xPos+bWidth/2+37,yPos+10); 250 | textFont(throttleFont,22); 251 | if(speed>0) 252 | fill(0,255,0); 253 | else if(speed<0) 254 | fill(255,0,0); 255 | else 256 | fill(255,255,0); 257 | text(nf(abs(speed),3),xPos+bWidth/2+30,yPos); 258 | 259 | } // display 260 | 261 | ////////////////////////////////////////////////////////////////////////// 262 | 263 | void functionButtonWindow(int xPos, int yPos, int kWidth, int kHeight, color backgroundColor, color outlineColor){ 264 | if(windowList.size()==1) // there is already one defined window and another is requested -- add a NextFunctionsButton to the original window 265 | new NextFunctionsButton(fbWindow, this, kWidth/2, kHeight+5, 40, 15, 60, 8, "More..."); 266 | 267 | fbWindow=new Window(xPos,yPos,kWidth,kHeight,backgroundColor,outlineColor); 268 | windowList.add(fbWindow); 269 | 270 | if(windowList.size()>1) // there are at least two defined windows -- add a NextFunctionsButton to this window 271 | new NextFunctionsButton(fbWindow, this, kWidth/2, kHeight+5, 40, 15, 60, 8, "More..."); 272 | } 273 | 274 | ////////////////////////////////////////////////////////////////////////// 275 | 276 | void setFunction(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, int fNum, String name, ButtonType buttonType, CabFunction ... cFunc){ 277 | new FunctionButton(fbWindow,xPos,yPos,bWidth,bHeight,baseHue,fontSize,this,fNum,name,buttonType,cFunc); 278 | } 279 | 280 | ////////////////////////////////////////////////////////////////////////// 281 | 282 | void activateFunction(CabFunction cFunc, boolean s){ 283 | if(functionsHM.containsKey(cFunc)) 284 | functionsHM.get(cFunc).activateFunction(s); 285 | } 286 | 287 | ////////////////////////////////////////////////////////////////////////// 288 | 289 | void turnOn(){ 290 | if(throttle.cabButton!=null){ 291 | throttle.cabButton.fbWindow.close(); 292 | throttle.cabButton.turnOff(); 293 | } 294 | 295 | super.turnOn(); 296 | fbWindow.show(); 297 | throttle.cabButton=this; 298 | opCabInput.setIntValue(cab); 299 | } 300 | 301 | ////////////////////////////////////////////////////////////////////////// 302 | 303 | void turnOff(){ 304 | super.turnOff(); 305 | fbWindow.close(); 306 | throttle.cabButton=null; 307 | } 308 | 309 | ////////////////////////////////////////////////////////////////////////// 310 | 311 | void shiftPressed(){ 312 | autoPilot.parkCab(this); 313 | } 314 | 315 | ////////////////////////////////////////////////////////////////////////// 316 | 317 | void rightClick(){ 318 | editWindow.open(); 319 | } 320 | 321 | ////////////////////////////////////////////////////////////////////////// 322 | 323 | void setThrottle(int tPos){ 324 | aPort.write("0)+">"); 325 | 326 | if(throttleSpeed!=ThrottleSpeed.STOP) 327 | throttleDefaultsXML.setInt(throttleSpeed.name(),tPos); 328 | 329 | } 330 | 331 | ////////////////////////////////////////////////////////////////////////// 332 | 333 | void setThrottle(ThrottleSpeed throttleSpeed){ 334 | this.throttleSpeed=throttleSpeed; 335 | setThrottle(throttleDefaultsXML.getInt(throttleSpeed.name())); 336 | speedXML.setContent(throttleSpeed.name()); 337 | activateFunction(CabFunction.F_LIGHT,true); 338 | activateFunction(CabFunction.R_LIGHT,true); 339 | activateFunction(CabFunction.D_LIGHT,true); 340 | } 341 | 342 | ////////////////////////////////////////////////////////////////////////// 343 | 344 | void setThrottleDefaults(int fullSpeed, int slowSpeed, int reverseSpeed, int reverseSlowSpeed){ 345 | 346 | throttleDefaultsXML=cabDefaultXML.getChild("throttleDefaults"); 347 | 348 | if(throttleDefaultsXML==null){ 349 | throttleDefaultsXML=cabDefaultXML.addChild("throttleDefaults"); 350 | throttleDefaultsXML.setInt(ThrottleSpeed.FULL.name(),fullSpeed); 351 | throttleDefaultsXML.setInt(ThrottleSpeed.SLOW.name(),slowSpeed); 352 | throttleDefaultsXML.setInt(ThrottleSpeed.REVERSE.name(),reverseSpeed); 353 | throttleDefaultsXML.setInt(ThrottleSpeed.REVERSE_SLOW.name(),reverseSlowSpeed); 354 | throttleDefaultsXML.setInt(ThrottleSpeed.STOP.name(),0); 355 | } 356 | 357 | } 358 | 359 | ////////////////////////////////////////////////////////////////////////// 360 | 361 | void setSidingDefaults(RouteButton sidingRoute, int parkingSensor, int sidingSensor){ 362 | this.sidingRoute=sidingRoute; 363 | this.parkingSensor=parkingSensor; 364 | this.sidingSensor=sidingSensor; 365 | } 366 | 367 | ////////////////////////////////////////////////////////////////////////// 368 | 369 | void stopThrottle(){ 370 | aPort.write(""); 371 | throttleSpeed=ThrottleSpeed.STOP; 372 | } 373 | 374 | ////////////////////////////////////////////////////////////////////////// 375 | 376 | String toString(){ 377 | return(name); 378 | } 379 | 380 | } // CabButton Class 381 | 382 | ////////////////////////////////////////////////////////////////////////// 383 | // DCC Component: FunctionButton 384 | ////////////////////////////////////////////////////////////////////////// 385 | 386 | class FunctionButton extends RectButton{ 387 | int fNum; 388 | CabButton cabButton; 389 | String name; 390 | int oneShotCount; 391 | int fPolarity; 392 | 393 | FunctionButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, CabButton cabButton, int fNum, String name, ButtonType buttonType, CabFunction[] cFunc){ 394 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, name, buttonType); 395 | this.fNum=abs(fNum)%29; // ensures fNum is always between 0 and 28, inclusive 396 | this.cabButton=cabButton; 397 | this.name=name; 398 | for(int i=0;i0){ 409 | oneShotCount--; 410 | isOn=true; 411 | } 412 | else 413 | isOn=(cabButton.fStatus[fNum]!=fPolarity); 414 | 415 | super.display(); 416 | 417 | } // display 418 | 419 | ////////////////////////////////////////////////////////////////////////// 420 | 421 | void turnOn(){ 422 | activateFunction(true); 423 | } 424 | 425 | ////////////////////////////////////////////////////////////////////////// 426 | 427 | void turnOff(){ 428 | activateFunction(false); 429 | } 430 | 431 | ////////////////////////////////////////////////////////////////////////// 432 | 433 | void released(){ 434 | if(buttonType==ButtonType.HOLD) 435 | turnOff(); 436 | } 437 | 438 | ////////////////////////////////////////////////////////////////////////// 439 | 440 | void activateFunction(boolean s){ 441 | int f=0; 442 | int e=0; 443 | 444 | if(s){ 445 | cabButton.fStatus[fNum]=1-fPolarity; 446 | if(buttonType==ButtonType.ONESHOT){ 447 | fPolarity=1-fPolarity; 448 | oneShotCount=1; 449 | } 450 | } else{ 451 | cabButton.fStatus[fNum]=fPolarity; 452 | } 453 | 454 | if(fNum<5){ // functions F0-F4 are single byte instructions of form 1-0-0-F0-F4-F3-F2-F1 455 | f=(1<<7) 456 | +(cabButton.fStatus[0]<<4) 457 | +(cabButton.fStatus[4]<<3) 458 | +(cabButton.fStatus[3]<<2) 459 | +(cabButton.fStatus[2]<<1) 460 | +cabButton.fStatus[1]; 461 | } else if(fNum<9){ // functions F5-F8 are single byte instructions of form 1-0-1-1-F8-F7-F6-F5 462 | f=(1<<7) 463 | +(1<<5) 464 | +(1<<4) 465 | +(cabButton.fStatus[8]<<3) 466 | +(cabButton.fStatus[7]<<2) 467 | +(cabButton.fStatus[6]<<1) 468 | +cabButton.fStatus[5]; 469 | } else if(fNum<13){ // functions F9-F12 are single byte instructions of form 1-0-1-0-F12-F11-F10-F9 470 | f=(1<<7) 471 | +(1<<5) 472 | +(cabButton.fStatus[12]<<3) 473 | +(cabButton.fStatus[11]<<2) 474 | +(cabButton.fStatus[10]<<1) 475 | +cabButton.fStatus[9]; 476 | } else if(fNum<21){ // functions F13-F20 are two-byte instructions of form 0xDE followed by F20-F19-F18-F17-F16-F15-F14-F13 477 | f=222; // 0xDE 478 | e=(cabButton.fStatus[20]<<7) 479 | +(cabButton.fStatus[19]<<6) 480 | +(cabButton.fStatus[18]<<5) 481 | +(cabButton.fStatus[17]<<4) 482 | +(cabButton.fStatus[16]<<3) 483 | +(cabButton.fStatus[15]<<2) 484 | +(cabButton.fStatus[14]<<1) 485 | +cabButton.fStatus[13]; 486 | } else if(fNum<29){ // functions F21-F28 are two-byte instructions of form 0xDF followed by F28-F27-F26-F25-F24-F23-F22-F21 487 | f=223; // 0xDF 488 | e=(cabButton.fStatus[28]<<7) 489 | +(cabButton.fStatus[27]<<6) 490 | +(cabButton.fStatus[26]<<5) 491 | +(cabButton.fStatus[25]<<4) 492 | +(cabButton.fStatus[24]<<3) 493 | +(cabButton.fStatus[23]<<2) 494 | +(cabButton.fStatus[22]<<1) 495 | +cabButton.fStatus[21]; 496 | } 497 | 498 | if(fNum<13) 499 | aPort.write(""); 500 | else 501 | aPort.write(""); 502 | 503 | } // activateFunction 504 | 505 | } // FunctionButton Class 506 | 507 | ////////////////////////////////////////////////////////////////////////// 508 | // DCC Component: NextFunctionsButton 509 | ////////////////////////////////////////////////////////////////////////// 510 | 511 | class NextFunctionsButton extends RectButton{ 512 | CabButton cButton; 513 | 514 | NextFunctionsButton(Window window, CabButton cButton, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 515 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.ONESHOT); 516 | this.cButton=cButton; 517 | } // PowerButton 518 | 519 | ////////////////////////////////////////////////////////////////////////// 520 | 521 | void pressed(){ 522 | super.pressed(); 523 | cButton.fbWindow.close(); 524 | cButton.fbWindow=throttleA.cabButton.windowList.get((throttleA.cabButton.windowList.indexOf(throttleA.cabButton.fbWindow)+1)%throttleA.cabButton.windowList.size()); 525 | cButton.fbWindow.open(); 526 | } 527 | 528 | } // NextFunctionsButton Class -------------------------------------------------------------------------------- /DCCpp_Controller/dRoutes.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Class for Route Button 3 | // 4 | // RouteButton - creates a button to activate one or more Track Buttons 5 | // that in turn set one or more TURNOUTS or CROSSOVERS to either an 6 | // open or closed position representing a specific track route 7 | // - tracks may also be added to a route button so that they are highlighted 8 | // on the screen when the route button is first selected 9 | // - track highlights will be color-coded to indicate whether each 10 | // turnout or crossover that in in the route is already set properly, 11 | // or needs to be toggled if that route is activiated 12 | // 13 | // - two types of route buttons are supported: 14 | // 15 | // * large stand-alone button with a text label indicated the name of the route 16 | // * small button placed on a track where the route is obvious and does 17 | // not require a name (such as at the end of a siding) 18 | // 19 | ////////////////////////////////////////////////////////////////////////// 20 | 21 | class RouteButton extends DccComponent{ 22 | int xPos, yPos; 23 | int kWidth, kHeight; 24 | String label=""; 25 | boolean routeOn=false; 26 | ArrayList aTrackButtons = new ArrayList(); 27 | ArrayList bTrackButtons = new ArrayList(); 28 | ArrayList rTracks = new ArrayList(); 29 | 30 | RouteButton(Track refTrack, int kWidth, int kHeight){ 31 | this.xPos=int((refTrack.x[0]+refTrack.x[1])/2.0*refTrack.layout.sFactor+refTrack.layout.xCorner); 32 | this.yPos=int((refTrack.y[0]+refTrack.y[1])/2.0*refTrack.layout.sFactor+refTrack.layout.yCorner); 33 | this.kWidth=kWidth; 34 | this.kHeight=kHeight; 35 | dccComponents.add(this); 36 | } 37 | 38 | RouteButton(int xPos, int yPos, int kWidth, int kHeight, String label){ 39 | this.xPos=xPos; 40 | this.yPos=yPos; 41 | this.kWidth=kWidth; 42 | this.kHeight=kHeight; 43 | this.label=label; 44 | dccComponents.add(this); 45 | 46 | } // RouteButton 47 | 48 | ////////////////////////////////////////////////////////////////////////// 49 | 50 | void addTrackButton(TrackButton trackButton, int tPos){ 51 | if(tPos==0){ // specifies that this track button should be set to A when route selected 52 | aTrackButtons.add(trackButton); 53 | trackButton.aRouteButtons.add(this); 54 | } else if (tPos==1) { // specifies that this track button should be set to B when route selected 55 | bTrackButtons.add(trackButton); 56 | trackButton.bRouteButtons.add(this); 57 | } 58 | } 59 | 60 | ////////////////////////////////////////////////////////////////////////// 61 | 62 | void addTrack(Track track){ 63 | rTracks.add(track); 64 | } 65 | 66 | ////////////////////////////////////////////////////////////////////////// 67 | 68 | void display(){ 69 | if(label.equals("")){ 70 | ellipseMode(CENTER); 71 | if(routeOn) 72 | fill(color(0,255,0)); 73 | else 74 | fill(color(0,150,0)); 75 | noStroke(); 76 | ellipse(xPos,yPos,kWidth/2,kHeight/2); 77 | } else{ 78 | ellipseMode(CENTER); 79 | if(routeOn) 80 | fill(color(0,200,200)); 81 | else 82 | fill(color(0,100,100)); 83 | noStroke(); 84 | ellipse(xPos,yPos,kWidth,kHeight); 85 | textFont(buttonFont,12); 86 | textAlign(CENTER,CENTER); 87 | fill(color(0)); 88 | text(label,xPos,yPos); 89 | } 90 | } // display 91 | 92 | ////////////////////////////////////////////////////////////////////////// 93 | 94 | void check(){ 95 | if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-yPos)*(mouseY-yPos)/(kHeight*kHeight/4.0)<=1){ 96 | cursorType=HAND; 97 | selectedComponent=this; 98 | for(Track track : rTracks){ 99 | track.hStatus=1; 100 | } 101 | } 102 | 103 | else if(previousComponent==this){ 104 | for(Track track : rTracks){ 105 | track.hStatus=0; 106 | } 107 | } 108 | 109 | } // check 110 | 111 | ////////////////////////////////////////////////////////////////////////// 112 | 113 | void pressed(){ 114 | for(TrackButton trackButton : aTrackButtons){ 115 | if(trackButton.rEnabled) 116 | trackButton.pressed(0); 117 | } 118 | for(TrackButton trackButton : bTrackButtons){ 119 | if(trackButton.rEnabled) 120 | trackButton.pressed(1); 121 | } 122 | routeOn=true; 123 | } // pressed 124 | 125 | ////////////////////////////////////////////////////////////////////////// 126 | 127 | void shiftPressed(){ 128 | for(TrackButton trackButton : aTrackButtons){ 129 | if(trackButton.rEnabled) 130 | trackButton.pressed(1); 131 | } 132 | for(TrackButton trackButton : bTrackButtons){ 133 | if(trackButton.rEnabled) 134 | trackButton.pressed(0); 135 | } 136 | routeOn=false; 137 | } // shiftPressed 138 | 139 | } // RouteButton Class -------------------------------------------------------------------------------- /DCCpp_Controller/dSensors.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Classes for Sensors and AutoPilot Control 3 | // 4 | // AutoPilot Button - automaticaly operates three cabs in a pattern 5 | // by which each cab travels out to the sky bridge, 6 | // reverses course, and comes back into the inner 7 | // reversing loop after passing through the crossover 8 | // - status is saved between session 9 | // - clicking this button RESUMES a previous session 10 | // or stops the current session 11 | // - resumption implies all cabs, turnouts, and sensors are in 12 | // the exact same position as before the session was halted 13 | // - shift-clicking this button STARTS a new session 14 | // - starting a new seesion assume all cabs are in their start position 15 | // but sensors and turnouts will be automatically reset 16 | // 17 | // TrackSensor - defines a track sensor that triggers when the first car of a train passes, and 18 | // then again when the last car of that same train passes. 19 | // - creates a track sensor button on the track layout where ther sensor is located 20 | // - a given track sensor is defined to be "on" once an initial trigger is received from passage 21 | // of first the car of a train, and defined to be "off" once a second trigger is received from 22 | // passage of last car of that same train 23 | // - if the on/off status of a track sensor button seems out of sync with the actual train, 24 | // user can manually toggle the sensor "on" or "off" by clicking the appropriate sensor button 25 | // 26 | ////////////////////////////////////////////////////////////////////////// 27 | 28 | class AutoPilotButton extends RectButton{ 29 | int[] cabs={8601,6021,1506,622,1202,54}; // list of all cabs to be included in autoPilot - order does not matter since it will be randomized 30 | ArrayList cabList = new ArrayList(); 31 | int phase=0; 32 | int tCount=0; 33 | int crossOver=0; 34 | AutoProgram program=AutoProgram.NONE; 35 | XML cabListXML, phaseXML, tCountXML, crossOverXML, programXML; 36 | int safetyTimer; 37 | 38 | AutoPilotButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 39 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText); 40 | } 41 | 42 | AutoPilotButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 43 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(0), fontSize, bText, ButtonType.NORMAL); 44 | 45 | phaseXML=autoPilotXML.getChild("Phase"); 46 | if(phaseXML==null){ 47 | phaseXML=autoPilotXML.addChild("Phase"); 48 | phaseXML.setContent(str(phase)); 49 | } else{ 50 | phase=int(phaseXML.getContent()); 51 | } 52 | 53 | tCountXML=autoPilotXML.getChild("TCount"); 54 | if(tCountXML==null){ 55 | tCountXML=autoPilotXML.addChild("TCount"); 56 | tCountXML.setContent(str(tCount)); 57 | } else{ 58 | tCount=int(tCountXML.getContent()); 59 | } 60 | 61 | crossOverXML=autoPilotXML.getChild("CrossOver"); 62 | if(crossOverXML==null){ 63 | crossOverXML=autoPilotXML.addChild("CrossOver"); 64 | crossOverXML.setContent(str(crossOver)); 65 | } else{ 66 | crossOver=int(crossOverXML.getContent()); 67 | } 68 | 69 | programXML=autoPilotXML.getChild("Program"); 70 | if(programXML==null){ 71 | programXML=autoPilotXML.addChild("Program"); 72 | programXML.setContent(program.name); 73 | } else{ 74 | program=AutoProgram.index(programXML.getContent()); 75 | } 76 | 77 | cabListXML=autoPilotXML.getChild("CabList"); 78 | if(cabListXML==null){ 79 | cabListXML=autoPilotXML.addChild("CabList"); 80 | cabListXML.setContent(join(nf(cabs,0)," ")); 81 | } 82 | 83 | for(int i: int(split(trim(cabListXML.getContent())," "))) 84 | cabList.add(cabsHM.get("Cab"+i)); 85 | 86 | updateDiagBox(); 87 | 88 | } // AutoButton 89 | 90 | ////////////////////////////////////////////////////////////////////////// 91 | 92 | void display(){ 93 | super.display(); 94 | 95 | textAlign(CENTER,CENTER); 96 | textFont(messageFont,12); 97 | fill(color(255)); 98 | text(program.name,xPos+xWindow(),yPos+yWindow()+bHeight/2+10); 99 | 100 | } 101 | 102 | ////////////////////////////////////////////////////////////////////////// 103 | 104 | void pressed(){ 105 | 106 | if(isOn){ 107 | turnOff(); 108 | return; 109 | } 110 | 111 | if(program==AutoProgram.NONE){ 112 | msgBoxMain.setMessage("Can't resume Auto Pilot until program specified!",color(50,50,200)); 113 | return; 114 | } 115 | 116 | for(CabButton cb : cabList) // set throttles of all cabs specified in current program to prior values 117 | cb.setThrottle(ThrottleSpeed.index(cb.speedXML.getContent())); 118 | 119 | if(program.equals(AutoProgram.AUTO_CLEAN)) 120 | cleaningCab.turnOn(); 121 | 122 | msgBoxMain.setMessage("Auto Pilot Resuming",color(50,50,200)); 123 | safetyTimer=millis(); 124 | turnOn(); 125 | } // pressed 126 | 127 | ////////////////////////////////////////////////////////////////////////// 128 | 129 | void init(){ 130 | phase=0; 131 | tCount=0; 132 | crossOver=0; 133 | 134 | for(TrackSensor ts : sensorsHM.values()) 135 | ts.reset(); 136 | 137 | cabList.clear(); 138 | for(int i:cabs) 139 | cabList.add(cabsHM.get("Cab"+i)); 140 | 141 | for(int i=0;i<3;i++) // randomize list 142 | cabList.add(0,cabList.remove(int(random(i,cabList.size())))); 143 | 144 | updateCabList(); 145 | 146 | for(CabButton cb : cabList) // halt all cabs specified in full autopilot program 147 | cb.setThrottle(ThrottleSpeed.STOP); 148 | 149 | rButtonReset.pressed(); 150 | cabList.get(0).sidingRoute.pressed(); // set siding turnouts so they are aligned for first cab 151 | rButtonSpiral.pressed(); 152 | rButton12.pressed(); 153 | 154 | cabList.get(0).setThrottle(ThrottleSpeed.FULL); // start first cab! 155 | 156 | msgBoxMain.setMessage("Auto Pilot Engaged",color(50,50,200)); 157 | updateDiagBox(); 158 | 159 | } // init() 160 | 161 | 162 | ////////////////////////////////////////////////////////////////////////// 163 | 164 | void clean(){ 165 | 166 | if(isOn){ 167 | msgBoxMain.setMessage("Auto Pilot already engaged!",color(50,50,200)); 168 | return; 169 | } 170 | 171 | cleaningCab.turnOn(); // turn on cleaning car 172 | cabList.clear(); 173 | cabList.add(cabsHM.get("Cab"+2004)); // assumes cab 2004 is pulling cleaning car 174 | updateCabList(); 175 | phase=100; 176 | phaseXML.setContent(str(phase)); 177 | tButton10.pressed(0); 178 | 179 | for(TrackSensor ts : sensorsHM.values()) 180 | ts.reset(); 181 | 182 | cabList.get(0).setThrottle(ThrottleSpeed.FULL); // start throttle for cab 183 | msgBoxMain.setMessage("Auto Clean Engaged",color(50,50,200)); 184 | setProgram(AutoProgram.AUTO_CLEAN); 185 | safetyTimer=millis(); 186 | turnOn(); 187 | 188 | } // clean 189 | 190 | ////////////////////////////////////////////////////////////////////////// 191 | 192 | void parkCab(CabButton selectedCab){ 193 | 194 | if(selectedCab.parkingSensor==0){ 195 | msgBoxMain.setMessage("Auto Park not available for Cab "+selectedCab.cab,color(50,50,200)); 196 | return; 197 | } 198 | 199 | if(isOn){ 200 | msgBoxMain.setMessage("Auto Pilot already engaged!",color(50,50,200)); 201 | return; 202 | } 203 | 204 | cabList.clear(); 205 | cabList.add(selectedCab); 206 | updateCabList(); 207 | phase=42; 208 | phaseXML.setContent(str(phase)); 209 | cabList.get(0).setThrottle(ThrottleSpeed.FULL); // start throttle for cab selected --- do not modify throttles for any other cabs 210 | msgBoxMain.setMessage("Auto Park Engaged for Cab "+selectedCab.cab,color(50,50,200)); 211 | setProgram(AutoProgram.SINGLE_CAB_PARK); 212 | safetyTimer=millis(); 213 | turnOn(); 214 | 215 | } // parkCab 216 | 217 | ////////////////////////////////////////////////////////////////////////// 218 | 219 | void shiftPressed(){ 220 | 221 | if(!isOn){ 222 | setProgram(AutoProgram.ALL_CABS_RUN); 223 | safetyTimer=millis(); 224 | turnOn(); 225 | msgBoxMain.setMessage("Starting Auto Pilot...",color(50,50,200)); 226 | buttonQueue.add(this); 227 | } else 228 | if(program==AutoProgram.ALL_CABS_RUN){ 229 | msgBoxMain.setMessage("Switching to Auto Park",color(50,50,200)); 230 | setProgram(AutoProgram.ALL_CABS_PARK); 231 | } else{ 232 | msgBoxMain.setMessage("Auto Park or other program already engaged!",color(50,50,200)); 233 | } 234 | } // shiftPressed 235 | 236 | ////////////////////////////////////////////////////////////////////////// 237 | 238 | void turnOff(){ 239 | super.turnOff(); 240 | 241 | msgBoxMain.setMessage("Auto Pilot Disengaged",color(50,50,200)); 242 | 243 | for(CabButton cb : cabList) // halt (but without updating XML) all cabs specified in current program only 244 | cb.stopThrottle(); 245 | 246 | if(program.equals(AutoProgram.AUTO_CLEAN)) 247 | cleaningCab.turnOff(); 248 | 249 | if(program.equals(AutoProgram.SINGLE_CAB_RUN)){ 250 | aPort.write(""); 251 | setProgram(AutoProgram.NONE); 252 | } 253 | } 254 | 255 | ////////////////////////////////////////////////////////////////////////// 256 | 257 | void updateCabList(){ 258 | 259 | cabListXML.setContent(""); 260 | for(CabButton cb : cabList) 261 | cabListXML.setContent(cabListXML.getContent()+cb.cab+" "); 262 | 263 | cabListXML.setContent(trim(cabListXML.getContent())); 264 | } 265 | 266 | ////////////////////////////////////////////////////////////////////////// 267 | 268 | void process(int s, boolean isActive){ 269 | 270 | int lastPhase; 271 | 272 | if(!isOn || program.equals(AutoProgram.SINGLE_CAB_RUN)) 273 | return; 274 | 275 | if(!isActive) 276 | s=-s; 277 | 278 | lastPhase=phase; 279 | 280 | switch(s){ 281 | 282 | case 1: 283 | if(phase==3){ 284 | rButtonBridge.pressed(); 285 | phase=4; 286 | } else 287 | if(phase==4){ 288 | phase=5; 289 | } else 290 | if(phase==5){ 291 | phase=6; 292 | } else 293 | if(phase==10){ 294 | crossOver++; 295 | if(crossOver==2){ 296 | cabList.get(0).stopThrottle(); 297 | // cabList.get(0).activateFunction(CabFunction.HORN,true); 298 | // cabList.get(0).activateFunction(CabFunction.HORN,false); 299 | } else{ 300 | cabList.get(0).activateFunction(CabFunction.S_HORN,true); 301 | } 302 | } else 303 | if(phase==11){ 304 | phase=12; 305 | } else 306 | if(phase==13){ 307 | phase=14; 308 | } else 309 | if(phase==40){ 310 | tButton20.pressed(0); 311 | phase=41; 312 | } 313 | break; 314 | 315 | case -1: 316 | if(phase==2){ 317 | tCount++; 318 | } else 319 | if(phase==120){ 320 | phase=42; 321 | tButton20.pressed(1); 322 | tButton10.pressed(0); 323 | } 324 | break; 325 | 326 | case 2: 327 | if(phase==0){ 328 | tButton40.routeDisabled(); 329 | cabList.get(1).sidingRoute.pressed(); 330 | tButton40.routeEnabled(); 331 | cabList.get(1).setThrottle(ThrottleSpeed.FULL); 332 | phase=1; 333 | // } else 334 | // if(phase==10 || phase==11){ 335 | // cabList.get(2).setFunction(CabFunction.HORN,false); 336 | } else 337 | if(phase==30){ 338 | tButton50.pressed(1); 339 | phase=31; 340 | } 341 | break; 342 | 343 | case -2: 344 | if(phase==2){ 345 | tCount++; 346 | if(tCount==1) 347 | cabList.get(1).setThrottle(ThrottleSpeed.STOP); 348 | } else 349 | if(phase==9){ 350 | tButton30.pressed(1); 351 | tCount++; 352 | } else 353 | if(phase==10){ 354 | if(crossOver>0){ 355 | crossOver--; 356 | } 357 | if(crossOver==1){ 358 | cabList.get(0).setThrottle(ThrottleSpeed.FULL); 359 | // cabList.get(0).activateFunction(CabFunction.S_HORN,true); 360 | } 361 | } 362 | break; 363 | 364 | case 3: 365 | if(phase==7){ 366 | tButton30.pressed(0); 367 | phase=8; 368 | } else 369 | if(phase==10){ 370 | crossOver++; 371 | if(crossOver==2){ 372 | cabList.get(2).stopThrottle(); 373 | // cabList.get(2).activateFunction(CabFunction.HORN,true); 374 | // cabList.get(2).activateFunction(CabFunction.HORN,false); 375 | } else{ 376 | cabList.get(2).activateFunction(CabFunction.S_HORN,true); 377 | } 378 | } 379 | break; 380 | 381 | case -3: 382 | if(phase==110){ 383 | phase++; 384 | tButton30.pressed(1); 385 | } else 386 | if(phase==111||phase==112){ 387 | phase++; 388 | } else 389 | if(phase==113){ 390 | phase++; 391 | tButton30.pressed(0); 392 | tButton40.pressed(1); 393 | tButton1.pressed(0); 394 | } 395 | break; 396 | 397 | case 4: 398 | if(phase==1){ 399 | tButton40.routeDisabled(); 400 | cabList.get(2).sidingRoute.pressed(); 401 | tButton40.routeEnabled(); 402 | cabList.get(2).setThrottle(ThrottleSpeed.FULL); 403 | phase=2; 404 | } else 405 | if(phase==8){ 406 | tButton40.pressed(0); 407 | phase=9; 408 | // } else 409 | // if(phase==10){ 410 | // cabList.get(0).setFunction(CabFunction.HORN,false); 411 | } else 412 | if(phase==12){ 413 | tButton4.pressed(1); // set reversing loop 414 | tButton7.pressed(0); 415 | phase=20; // start "parking" phase, then resume pattern 416 | } else 417 | if((phase==21 || phase==31 || phase==42) && cabList.get(0).parkingSensor==4){ 418 | cabList.get(0).setThrottle(ThrottleSpeed.SLOW); 419 | phase++; 420 | } else 421 | if(phase==41){ 422 | tButton50.pressed(0); 423 | tButton4.pressed(1); // set reversing loop 424 | tButton7.pressed(0); 425 | phase=42; 426 | } 427 | break; 428 | 429 | case -4: 430 | if(phase==10){ 431 | if(crossOver>0){ 432 | crossOver--; 433 | } 434 | if(crossOver==1){ 435 | cabList.get(2).setThrottle(ThrottleSpeed.FULL); 436 | // cabList.get(2).activateFunction(CabFunction.S_HORN,true); 437 | } 438 | cabList.get(1).setThrottle(ThrottleSpeed.FULL); // just in case cab-1 was stopped on bridge 439 | tButton40.pressed(1); 440 | tButton1.pressed(0); 441 | tButton20.pressed(1); 442 | crossOver=0; 443 | phase=11; 444 | } else 445 | if((phase==22 || phase==32 || phase==43) && cabList.get(0).parkingSensor==4){ 446 | phase++; 447 | cabList.get(0).setThrottle(ThrottleSpeed.STOP); 448 | cabList.get(0).sidingRoute.shiftPressed(); 449 | delay(500); 450 | cabList.get(0).sidingRoute.pressed(); 451 | sensorsHM.get(cabList.get(0).sidingSensor).pressed(false); 452 | cabList.get(0).setThrottle(ThrottleSpeed.REVERSE); 453 | } 454 | break; 455 | 456 | case 5: 457 | if(phase==6){ 458 | cabList.get(0).setThrottle(ThrottleSpeed.SLOW); 459 | } else 460 | if(phase==14){ 461 | cabList.get(1).setThrottle(ThrottleSpeed.SLOW); 462 | } else 463 | if(phase==42 && cabList.get(0).parkingSensor==5){ 464 | cabList.get(0).setThrottle(ThrottleSpeed.SLOW); 465 | phase++; 466 | } 467 | break; 468 | 469 | case -5: 470 | if(phase==6){ 471 | cabList.get(0).setThrottle(ThrottleSpeed.STOP); 472 | phase=7; 473 | } else 474 | if(phase==14){ 475 | cabList.get(1).setThrottle(ThrottleSpeed.STOP); 476 | cabList.add(cabList.remove(0)); // move cab-0 to end of list 477 | updateCabList(); 478 | phase=7; // start next cycle 479 | } else 480 | if(phase==43 && cabList.get(0).parkingSensor==5){ 481 | phase++; 482 | cabList.get(0).setThrottle(ThrottleSpeed.STOP); 483 | cabList.get(0).sidingRoute.shiftPressed(); 484 | delay(500); 485 | cabList.get(0).sidingRoute.pressed(); 486 | sensorsHM.get(cabList.get(0).sidingSensor).pressed(false); 487 | delay(100); 488 | cabList.get(0).setThrottle(ThrottleSpeed.REVERSE); 489 | } else 490 | if(phase==100){ 491 | phase++; 492 | tButton10.pressed(1); 493 | tButton30.pressed(1); 494 | tButton50.pressed(1); 495 | tButton4.pressed(0); 496 | tButton20.pressed(0); 497 | } else 498 | if(phase==101||phase==102){ 499 | phase++; 500 | } else 501 | if(phase==103){ 502 | phase++; 503 | tButton20.pressed(1); 504 | tButton50.pressed(0); 505 | } else 506 | if(phase==104||phase==105){ 507 | phase++; 508 | } else 509 | if(phase==106){ 510 | phase++; 511 | tButton10.pressed(0); 512 | } else 513 | if(phase==107||phase==108){ 514 | phase++; 515 | } else 516 | if(phase==109){ 517 | phase++; 518 | tButton20.pressed(0); 519 | tButton30.pressed(0); 520 | } 521 | break; 522 | 523 | case 6: 524 | if(phase==10){ 525 | cabList.get(1).stopThrottle(); // wait on bridge until cab-0 clears sensor 4 526 | } 527 | break; 528 | 529 | case -6: 530 | if(phase==9){ 531 | tCount++; 532 | tButton8.pressed(); 533 | } 534 | break; 535 | 536 | case 7: 537 | case 8: 538 | case 9: 539 | case 10: 540 | case 12: 541 | case 13: 542 | case 14: 543 | if(phase==23 || phase==33 || phase==44){ 544 | phase++; 545 | cabList.get(0).setThrottle(ThrottleSpeed.REVERSE_SLOW); 546 | } 547 | break; 548 | 549 | case -7: 550 | case -8: 551 | case -9: 552 | case -10: 553 | case -12: 554 | case -13: 555 | case -14: 556 | if(phase==24 || phase==34 || phase==45){ 557 | cabList.get(0).setThrottle(ThrottleSpeed.STOP); 558 | sensorsHM.get(cabList.get(0).parkingSensor).pressed(false); 559 | tButton40.pressed(1); 560 | if(program==AutoProgram.SINGLE_CAB_PARK||program==AutoProgram.AUTO_CLEAN){ 561 | phase=51; // phase must have previously been 45 562 | turnOff(); 563 | } else 564 | if(program==AutoProgram.ALL_CABS_PARK){ 565 | cabList.add(0,cabList.remove(2)); // move cab-2 to beginning of list, making it cab-0 566 | updateCabList(); 567 | phase+=6; // start parking routine at either phase=30, or if second cab just parked then phase=40, or if third cab finished parking phase=51 568 | if(phase==51){ 569 | turnOff(); 570 | } 571 | } else{ 572 | cabList.add(3,cabList.remove(int(random(3,cabList.size())))); // pick random cab to be next to leave siding 573 | updateCabList(); 574 | tButton40.routeDisabled(); 575 | cabList.get(3).sidingRoute.pressed(); 576 | tButton40.routeEnabled(); 577 | cabList.get(3).setThrottle(ThrottleSpeed.FULL); 578 | phase=25; 579 | } 580 | } 581 | break; 582 | 583 | case 11: 584 | if(phase==20){ 585 | phase=21; 586 | } else 587 | if((phase==21 || phase==31 || phase==42) && cabList.get(0).parkingSensor==11){ 588 | cabList.get(0).setThrottle(ThrottleSpeed.SLOW); 589 | phase++; 590 | } else 591 | if(phase==25){ 592 | phase=26; 593 | } else 594 | if(phase==26){ 595 | phase=13; 596 | } 597 | break; 598 | 599 | case -11: 600 | if((phase==22 || phase==32 || phase==43) && cabList.get(0).parkingSensor==11){ 601 | phase++; 602 | cabList.get(0).setThrottle(ThrottleSpeed.STOP); 603 | cabList.get(0).sidingRoute.shiftPressed(); 604 | delay(500); 605 | cabList.get(0).sidingRoute.pressed(); 606 | sensorsHM.get(cabList.get(0).sidingSensor).pressed(false); 607 | delay(100); 608 | cabList.get(0).setThrottle(ThrottleSpeed.REVERSE); 609 | } else 610 | if(phase==114||phase==115){ 611 | phase++; 612 | } else 613 | if(phase==116){ 614 | phase++; 615 | tButton4.pressed(1); 616 | tButton7.pressed(0); 617 | tButton5.pressed(0); 618 | tButton20.pressed(0); 619 | tButton8.pressed(0); 620 | } else 621 | if(phase==117){ 622 | phase++; 623 | tButton40.pressed(0); 624 | } else 625 | if(phase==118||phase==119){ 626 | phase++; 627 | } 628 | break; 629 | 630 | } // switch t 631 | 632 | if(phase==2 && tCount==2){ 633 | cabList.get(1).setThrottle(ThrottleSpeed.FULL); // just in case cab-1 was previously stopped to wait for cab-0 to catch up 634 | rButton10.pressed(); 635 | rButton11.pressed(); 636 | tCount=0; 637 | phase=3; 638 | } else 639 | 640 | if(phase==9 && tCount==2){ 641 | cabList.get(0).setThrottle(ThrottleSpeed.FULL); 642 | tButton20.pressed(0); 643 | tButton4.pressed(0); 644 | tCount=0; 645 | crossOver=0; 646 | phase=10; 647 | } 648 | 649 | phaseXML.setContent(str(phase)); 650 | tCountXML.setContent(str(tCount)); 651 | crossOverXML.setContent(str(crossOver)); 652 | 653 | updateDiagBox(); 654 | 655 | if(phase!=lastPhase) // there was an update of the phase 656 | safetyTimer=millis(); // reset timer 657 | 658 | 659 | } // process 660 | 661 | ////////////////////////////////////////////////////////////////////////// 662 | 663 | void setProgram(AutoProgram p){ 664 | program=p; 665 | programXML.setContent(program.name); 666 | updateDiagBox(); 667 | saveXMLFlag=true; 668 | } 669 | 670 | ////////////////////////////////////////////////////////////////////////// 671 | void updateDiagBox(){ 672 | 673 | String s=""; 674 | 675 | for(XML xml: autoPilotXML.getChildren()){ 676 | if(!xml.getName().equals("#text")) 677 | s=s+(String.format("%10s",xml.getName())+" = "+xml.getContent()+"\n"); 678 | } 679 | 680 | msgAutoState.setMessage(s); 681 | 682 | } // updateDiagBox 683 | 684 | ////////////////////////////////////////////////////////////////////////// 685 | 686 | void safetyCheck(){ 687 | 688 | int countDown; 689 | 690 | if(!isOn || program.equals(AutoProgram.SINGLE_CAB_RUN)) 691 | return; 692 | 693 | countDown=120-int((millis()-safetyTimer)/1000); 694 | 695 | msgAutoTimer.setMessage("Timer = "+countDown); 696 | 697 | if(countDown<=0){ 698 | powerButton.turnOff(); 699 | turnOff(); 700 | } 701 | 702 | } // safetyCheck 703 | 704 | } // AutoPilot Class 705 | 706 | ////////////////////////////////////////////////////////////////////////// 707 | // DCC Component: TrackSensor 708 | ////////////////////////////////////////////////////////////////////////// 709 | 710 | class TrackSensor extends Track{ 711 | boolean isActive=false; 712 | boolean sensorDefault; 713 | int xPos, yPos; 714 | int mTime; 715 | int kWidth, kHeight; 716 | String sensorName; 717 | int sensorNum; 718 | XML sensorButtonXML; 719 | MessageBox msgBoxSensor; 720 | 721 | TrackSensor(Track refTrack, int trackPoint, float tLength, int kWidth, int kHeight, int sensorNum, boolean sensorDefault){ 722 | super(refTrack,trackPoint,tLength); 723 | this.kWidth=kWidth; 724 | this.kHeight=kHeight; 725 | this.xPos=int(x[1]*layout.sFactor+layout.xCorner); 726 | this.yPos=int(y[1]*layout.sFactor+layout.yCorner); 727 | this.sensorNum=sensorNum; 728 | sensorName="Sensor"+sensorNum; 729 | componentName=sensorName; 730 | this.sensorDefault=sensorDefault; 731 | sensorButtonXML=sensorButtonsXML.getChild(sensorName); 732 | if(sensorButtonXML==null){ 733 | sensorButtonXML=sensorButtonsXML.addChild(sensorName); 734 | sensorButtonXML.setContent(str(isActive)); 735 | } else{ 736 | isActive=boolean(sensorButtonXML.getContent()); 737 | } 738 | sensorsHM.put(sensorNum,this); 739 | msgBoxSensor=new MessageBox(sensorWindow,0,sensorNum*22+22,-1,0,color(175),18,"S-"+nf(sensorNum,2)+":",color(50,50,250)); 740 | } 741 | 742 | TrackSensor(Track refTrack, int trackPoint, float curveRadius, float curveAngleDeg, int kWidth, int kHeight, int sensorNum, boolean sensorDefault){ 743 | super(refTrack,trackPoint,curveRadius,curveAngleDeg); 744 | this.kWidth=kWidth; 745 | this.kHeight=kHeight; 746 | this.xPos=int(x[1]*layout.sFactor+layout.xCorner); 747 | this.yPos=int(y[1]*layout.sFactor+layout.yCorner); 748 | this.sensorNum=sensorNum; 749 | this.sensorDefault=sensorDefault; 750 | sensorName="Sensor"+sensorNum; 751 | componentName=sensorName; 752 | sensorButtonXML=sensorButtonsXML.getChild(sensorName); 753 | if(sensorButtonXML==null){ 754 | sensorButtonXML=sensorButtonsXML.addChild(sensorName); 755 | sensorButtonXML.setContent(str(isActive)); 756 | } else{ 757 | isActive=boolean(sensorButtonXML.getContent()); 758 | } 759 | sensorsHM.put(sensorNum,this); 760 | msgBoxSensor=new MessageBox(sensorWindow,0,sensorNum*22+22,-1,0,color(175),18,"S-"+nf(sensorNum,2)+":",color(50,50,250)); 761 | } 762 | 763 | ////////////////////////////////////////////////////////////////////////// 764 | 765 | void display(){ 766 | ellipseMode(CENTER); 767 | 768 | strokeWeight(1); 769 | stroke(color(255,255,0)); 770 | noFill(); 771 | 772 | if(isActive) 773 | fill(color(50,50,200)); 774 | 775 | ellipse(xPos,yPos,kWidth/2,kHeight/2); 776 | } // display() 777 | 778 | ////////////////////////////////////////////////////////////////////////// 779 | 780 | void pressed(){ 781 | pressed(!isActive); 782 | } 783 | 784 | ////////////////////////////////////////////////////////////////////////// 785 | 786 | void pressed(boolean isActive){ 787 | this.isActive=isActive; 788 | autoPilot.process(sensorNum,isActive); 789 | sensorButtonXML.setContent(str(isActive)); 790 | saveXMLFlag=true; 791 | if(isActive){ 792 | msgBoxSensor.setMessage("S-"+nf(sensorNum,2)+": "+nf(hour(),2)+":"+nf(minute(),2)+":"+nf(second(),2)+" - "+nf((millis()-mTime)/1000.0,0,1)+" sec"); 793 | mTime=millis(); 794 | } 795 | 796 | } // pressed 797 | 798 | ////////////////////////////////////////////////////////////////////////// 799 | 800 | void reset(){ 801 | pressed(sensorDefault); 802 | 803 | } // reset 804 | 805 | ////////////////////////////////////////////////////////////////////////// 806 | 807 | void check(){ 808 | if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-yPos)*(mouseY-yPos)/(kHeight*kHeight/4.0)<=1){ 809 | cursorType=HAND; 810 | selectedComponent=this; 811 | } 812 | 813 | } // check 814 | 815 | } // TrackSensor Class -------------------------------------------------------------------------------- /DCCpp_Controller/dTracks.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Classes for Layouts and Tracks 3 | // 4 | // Layout - defines a scaled region on the screen into which tracks 5 | // will be place using scaled coordinates 6 | // 7 | // Track - defines a curved or straight piece of track. 8 | // - placement on layout can be in absolute scaled coordinates 9 | // or linked to one end of a previously-defined track. 10 | // - tracks can be linked even across separate layouts 11 | // - define multiple overlapping tracks to create any type 12 | // of turnout, crossover, or other complex track 13 | ////////////////////////////////////////////////////////////////////////// 14 | 15 | class Layout{ 16 | int xCorner, yCorner; 17 | float sFactor; 18 | 19 | Layout(int xCorner, int yCorner, int frameWidth, float layoutWidth, float layoutHeight){ 20 | this.xCorner=xCorner; 21 | this.yCorner=yCorner; 22 | sFactor=float(frameWidth)/layoutWidth; // frameWidth in pixels, layoutWidth in mm, inches, cm, etc. 23 | } // Layout 24 | 25 | Layout(Layout layout){ 26 | this.xCorner=layout.xCorner; 27 | this.yCorner=layout.yCorner; 28 | this.sFactor=layout.sFactor; 29 | } // Layout 30 | 31 | void copy(Layout layout){ 32 | this.xCorner=layout.xCorner; 33 | this.yCorner=layout.yCorner; 34 | this.sFactor=layout.sFactor; 35 | } // copy 36 | 37 | boolean equals(Layout layout){ 38 | return((this.xCorner==layout.xCorner)&&(this.yCorner==layout.yCorner)&&(this.sFactor==layout.sFactor)); 39 | } // equals 40 | 41 | } // Layout Class 42 | 43 | ////////////////////////////////////////////////////////////////////////// 44 | 45 | class Track extends DccComponent{ 46 | float[] x = new float[2]; 47 | float[] y = new float[2]; 48 | float[] a = new float[2]; 49 | color tColor; 50 | float xR, yR; 51 | float r; 52 | float aStart, aEnd; 53 | int tStatus=1; // specfies current track status (0=off/not visible, 1=on/visible) 54 | int hStatus=0; // specifies if current track is highlighted (1) or normal (0) 55 | Layout layout; 56 | 57 | Track(Layout layout, float x, float y, float tLength, float angleDeg){ 58 | this.x[0]=x; 59 | this.y[0]=y; 60 | this.a[1]=angleDeg/360.0*TWO_PI; 61 | this.a[0]=this.a[1]+PI; 62 | if(this.a[0]>=TWO_PI) 63 | this.a[0]-=TWO_PI; 64 | this.x[1]=this.x[0]+cos(this.a[1])*tLength; 65 | this.y[1]=this.y[0]-sin(this.a[1])*tLength; 66 | this.layout=layout; 67 | this.tColor=color(255,255,0); 68 | dccComponents.add(this); 69 | } // Track - straight, absolute 70 | 71 | ////////////////////////////////////////////////////////////////////////// 72 | 73 | Track(Track track, int trackPoint, float tLength, Layout layout){ 74 | this.x[0]=track.x[trackPoint%2]; 75 | this.y[0]=track.y[trackPoint%2]; 76 | this.a[1]=track.a[trackPoint%2]; 77 | this.a[0]=this.a[1]+PI; 78 | if(this.a[0]>=TWO_PI) 79 | this.a[0]-=TWO_PI; 80 | this.x[1]=this.x[0]+cos(this.a[1])*tLength; 81 | this.y[1]=this.y[0]-sin(this.a[1])*tLength; 82 | this.layout=layout; 83 | this.tColor=color(255,255,0); 84 | dccComponents.add(this); 85 | } // Track - straight, relative, Layout specified 86 | 87 | ////////////////////////////////////////////////////////////////////////// 88 | 89 | Track(Track track, int trackPoint, float tLength){ 90 | this.x[0]=track.x[trackPoint%2]; 91 | this.y[0]=track.y[trackPoint%2]; 92 | this.a[1]=track.a[trackPoint%2]; 93 | this.a[0]=this.a[1]+PI; 94 | if(this.a[0]>=TWO_PI) 95 | this.a[0]-=TWO_PI; 96 | this.x[1]=this.x[0]+cos(this.a[1])*tLength; 97 | this.y[1]=this.y[0]-sin(this.a[1])*tLength; 98 | this.layout=track.layout; 99 | this.tColor=color(255,255,0); 100 | dccComponents.add(this); 101 | } // Track - straight, relative, no Layout specified 102 | 103 | ////////////////////////////////////////////////////////////////////////// 104 | 105 | Track(Layout layout, float x, float y, float curveRadius, float curveAngleDeg, float angleDeg){ 106 | float thetaR, thetaA; 107 | int d; 108 | 109 | thetaR=curveAngleDeg/360.0*TWO_PI; 110 | thetaA=angleDeg/360.0*TWO_PI; 111 | d=(thetaR>0)?1:-1; 112 | 113 | this.x[0]=x; 114 | this.y[0]=y; 115 | 116 | this.a[0]=thetaA+PI; 117 | if(this.a[0]>=TWO_PI) 118 | 119 | this.a[0]-=TWO_PI; 120 | this.a[1]=thetaA+thetaR; 121 | if(this.a[1]>=TWO_PI) 122 | this.a[1]-=TWO_PI; 123 | if(this.a[1]<0) 124 | this.a[1]+=TWO_PI; 125 | 126 | this.r=curveRadius; 127 | 128 | this.xR=this.x[0]-d*this.r*sin(thetaA); 129 | this.yR=this.y[0]-d*this.r*cos(thetaA); 130 | 131 | this.x[1]=this.xR+d*this.r*sin(thetaA+thetaR); 132 | this.y[1]=this.yR+d*this.r*cos(thetaA+thetaR); 133 | 134 | if(d==1){ 135 | this.aEnd=PI/2-thetaA; 136 | this.aStart=this.aEnd-thetaR; 137 | }else{ 138 | this.aStart=1.5*PI-thetaA; 139 | this.aEnd=this.aStart-thetaR; 140 | } 141 | 142 | this.layout=layout; 143 | this.tColor=color(255,255,0); 144 | dccComponents.add(this); 145 | } // Track - curved, absolute 146 | 147 | ////////////////////////////////////////////////////////////////////////// 148 | 149 | Track(Track track, int trackPoint, float curveRadius, float curveAngleDeg, Layout layout){ 150 | float thetaR, thetaA; 151 | int d; 152 | 153 | thetaR=curveAngleDeg/360.0*TWO_PI; 154 | thetaA=track.a[trackPoint%2]; 155 | d=(thetaR>0)?1:-1; 156 | 157 | this.x[0]=track.x[trackPoint%2]; 158 | this.y[0]=track.y[trackPoint%2]; 159 | 160 | this.a[0]=thetaA+PI; 161 | if(this.a[0]>=TWO_PI) 162 | 163 | this.a[0]-=TWO_PI; 164 | this.a[1]=thetaA+thetaR; 165 | if(this.a[1]>=TWO_PI) 166 | this.a[1]-=TWO_PI; 167 | if(this.a[1]<0) 168 | this.a[1]+=TWO_PI; 169 | 170 | this.r=curveRadius; 171 | 172 | this.xR=this.x[0]-d*this.r*sin(thetaA); 173 | this.yR=this.y[0]-d*this.r*cos(thetaA); 174 | 175 | this.x[1]=this.xR+d*this.r*sin(thetaA+thetaR); 176 | this.y[1]=this.yR+d*this.r*cos(thetaA+thetaR); 177 | 178 | if(d==1){ 179 | this.aEnd=PI/2-thetaA; 180 | this.aStart=this.aEnd-thetaR; 181 | }else{ 182 | this.aStart=1.5*PI-thetaA; 183 | this.aEnd=this.aStart-thetaR; 184 | } 185 | 186 | this.layout=layout; 187 | this.tColor=color(255,255,0); 188 | dccComponents.add(this); 189 | } // Track - curved, relative, Layout specified 190 | 191 | ////////////////////////////////////////////////////////////////////////// 192 | 193 | Track(Track track, int trackPoint, float curveRadius, float curveAngleDeg){ 194 | float thetaR, thetaA; 195 | int d; 196 | 197 | thetaR=curveAngleDeg/360.0*TWO_PI; 198 | thetaA=track.a[trackPoint%2]; 199 | d=(thetaR>0)?1:-1; 200 | 201 | this.x[0]=track.x[trackPoint%2]; 202 | this.y[0]=track.y[trackPoint%2]; 203 | 204 | this.a[0]=thetaA+PI; 205 | if(this.a[0]>=TWO_PI) 206 | 207 | this.a[0]-=TWO_PI; 208 | this.a[1]=thetaA+thetaR; 209 | if(this.a[1]>=TWO_PI) 210 | this.a[1]-=TWO_PI; 211 | if(this.a[1]<0) 212 | this.a[1]+=TWO_PI; 213 | 214 | this.r=curveRadius; 215 | 216 | this.xR=this.x[0]-d*this.r*sin(thetaA); 217 | this.yR=this.y[0]-d*this.r*cos(thetaA); 218 | 219 | this.x[1]=this.xR+d*this.r*sin(thetaA+thetaR); 220 | this.y[1]=this.yR+d*this.r*cos(thetaA+thetaR); 221 | 222 | if(d==1){ 223 | this.aEnd=PI/2-thetaA; 224 | this.aStart=this.aEnd-thetaR; 225 | }else{ 226 | this.aStart=1.5*PI-thetaA; 227 | this.aEnd=this.aStart-thetaR; 228 | } 229 | 230 | this.layout=track.layout; 231 | this.tColor=color(255,255,0); 232 | dccComponents.add(this); 233 | } // Track - curved, relative, no Layout specified 234 | 235 | ////////////////////////////////////////////////////////////////////////// 236 | 237 | void display(){ 238 | 239 | if(tStatus==1){ // track is visible 240 | if(hStatus==1) // track is highlighted 241 | stroke(color(0,255,0)); 242 | else 243 | stroke(color(255,255,0)); 244 | } else{ // track is not visible 245 | if(hStatus==1) // track is highlighted 246 | stroke(color(255,0,0)); 247 | else 248 | stroke(color(80,80,0)); 249 | } 250 | 251 | strokeWeight(3); 252 | ellipseMode(RADIUS); 253 | noFill(); 254 | if(r==0){ 255 | line(x[0]*layout.sFactor+layout.xCorner,y[0]*layout.sFactor+layout.yCorner,x[1]*layout.sFactor+layout.xCorner,y[1]*layout.sFactor+layout.yCorner); 256 | } 257 | else{ 258 | arc(xR*layout.sFactor+layout.xCorner,yR*layout.sFactor+layout.yCorner,r*layout.sFactor,r*layout.sFactor,aStart,aEnd); 259 | } 260 | } // display() 261 | 262 | } // Track Class 263 | 264 | ////////////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /DCCpp_Controller/dTurnouts.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Class for Track Button 3 | // 4 | // TrackButton - creates a TURNOUT or CROSSOVER by grouping two sets of 5 | // of pre-specified tracks 6 | // - one set of tracks defines the state of the turnout 7 | // or crossover in the "open" position 8 | // - the other set of tracks defines the state of the turnout 9 | // or crossover in the "closed" position 10 | // - a clickable but otherwise invisible button (the Track Button) 11 | // located near the center of the turnout or crossover 12 | // toggles between the closed and open positions 13 | // 14 | // 15 | // - when toggled, TrackButton will: 16 | // 17 | // * reset the colors of each set of tracks to 18 | // indicate whether the turnour or crossover 19 | // is "open" or "closed" 20 | // 21 | // * reset the color of any route buttons that use this 22 | // track button 23 | // 24 | // * send a DCC ACCESSORY COMMAND to the DCC++ Base Station 25 | // using the Accessory Address and Accessory Number 26 | // specified for this Track Button 27 | // 28 | // In accordance with NMRA DCC Standards, accessory decoders 29 | // are controlled using 12 bits messages. The first 11 form 30 | // a main address (9 bits) and a sub address (2 bits). Depending 31 | // on the specifics of a particular manufacturers decoder, these 32 | // 11 bits can be interpreted as a single address (0-2047) or 33 | // as a main address (0-511) with 4 sub addresses (0-3). Some decoders 34 | // may respond to any address matching the first 9 bits; others may 35 | // also consider the two sub address bits. In any case, Track Button 36 | // can be used to send the correct combination of 11 bits to sucessfully 37 | // communicate with the decoder. 38 | // 39 | // The 12th bit is generally considered to be the data bit that is used 40 | // to toggle the accessory either on or off. In the case of a decoder 41 | // driving a turnout or crossover, this data bit is used to toggle between 42 | // the open and closed positions. 43 | // 44 | ////////////////////////////////////////////////////////////////////////// 45 | 46 | class TrackButton extends DccComponent{ 47 | int xPos, yPos; 48 | int kWidth, kHeight; 49 | int buttonStatus=0; 50 | int id; 51 | boolean rEnabled=true; 52 | ArrayList aTracks = new ArrayList(); 53 | ArrayList bTracks = new ArrayList(); 54 | ArrayList aRouteButtons = new ArrayList(); 55 | ArrayList bRouteButtons = new ArrayList(); 56 | 57 | TrackButton(int kWidth, int kHeight, int id){ 58 | this.kWidth=kWidth; 59 | this.kHeight=kHeight; 60 | this.id=id; 61 | this.componentName="T"+id; 62 | trackButtonsHM.put(id,this); 63 | dccComponents.add(this); 64 | } // FunctionButton 65 | 66 | ////////////////////////////////////////////////////////////////////////// 67 | 68 | void addTrack(Track track, int tPos){ 69 | int n=aTracks.size()+bTracks.size(); 70 | this.xPos=int((this.xPos*n+(track.x[0]+track.x[1])/2.0*track.layout.sFactor+track.layout.xCorner)/(n+1.0)); 71 | this.yPos=int((this.yPos*n+(track.y[0]+track.y[1])/2.0*track.layout.sFactor+track.layout.yCorner)/(n+1.0)); 72 | 73 | if(tPos==0){ // specifies that this track should be considered part of aTracks 74 | track.tStatus=1-buttonStatus; 75 | aTracks.add(track); 76 | } else if (tPos==1) { // specifies that this track should be considered part of bTracks 77 | track.tStatus=buttonStatus; 78 | bTracks.add(track); 79 | } 80 | 81 | } 82 | 83 | ////////////////////////////////////////////////////////////////////////// 84 | 85 | void display(){ 86 | if(buttonStatus==0){ 87 | for(Track track : bTracks) 88 | track.display(); 89 | for(Track track : aTracks) 90 | track.display(); 91 | } else { 92 | for(Track track : aTracks) 93 | track.display(); 94 | for(Track track : bTracks) 95 | track.display(); 96 | } 97 | } // display 98 | 99 | ////////////////////////////////////////////////////////////////////////// 100 | 101 | void check(){ 102 | if(selectedComponent==null && (mouseX-xPos)*(mouseX-xPos)/(kWidth*kWidth/4.0)+(mouseY-yPos)*(mouseY-yPos)/(kHeight*kHeight/4.0)<=1){ 103 | cursorType=HAND; 104 | selectedComponent=this; 105 | } 106 | } // check 107 | 108 | ////////////////////////////////////////////////////////////////////////// 109 | 110 | void routeEnabled(){ 111 | rEnabled=true; 112 | } 113 | ////////////////////////////////////////////////////////////////////////// 114 | 115 | void routeDisabled(){ 116 | rEnabled=false; 117 | } 118 | 119 | ////////////////////////////////////////////////////////////////////////// 120 | 121 | void pressed(){ 122 | pressed(1-buttonStatus); 123 | } 124 | 125 | ////////////////////////////////////////////////////////////////////////// 126 | 127 | void pressed(int buttonStatus){ 128 | aPort.write(""); 129 | delay(50); 130 | } 131 | 132 | ////////////////////////////////////////////////////////////////////////// 133 | 134 | void update(int buttonStatus){ 135 | 136 | this.buttonStatus=buttonStatus; 137 | 138 | for(Track track : aTracks) 139 | track.tStatus=1-buttonStatus; 140 | for(Track track : bTracks) 141 | track.tStatus=buttonStatus; 142 | 143 | if(buttonStatus==0){ 144 | for(RouteButton routeButton : bRouteButtons) 145 | routeButton.routeOn=false; 146 | } else { 147 | for(RouteButton routeButton : aRouteButtons) 148 | routeButton.routeOn=false; 149 | } 150 | 151 | } // update 152 | 153 | } // TrackButton Class -------------------------------------------------------------------------------- /DCCpp_Controller/data/ARCENA-15.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/ARCENA-15.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/IrisUPC-36.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/IrisUPC-36.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/JasmineUPC-36.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/JasmineUPC-36.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/LucidaConsole-18.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/LucidaConsole-18.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/Miriam-36.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/Miriam-36.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/MiriamFixed-26.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/MiriamFixed-26.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/OCRAExtended-26.vlw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/OCRAExtended-26.vlw -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-1202.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-1202.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-1506.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-1506.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-2004.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-2004.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-54.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-6021.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-6021.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-622.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-622.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/cab-8601.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/cab-8601.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/data/helpMenu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DccPlusPlus/Controller/2dd4a752ff581fbdcd23f49df85fb7865dd11c3c/DCCpp_Controller/data/helpMenu.jpg -------------------------------------------------------------------------------- /DCCpp_Controller/dccStatus.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Emulator 4 | 5 | false 6 | false 7 | true 8 | false 9 | false 10 | false 11 | true 12 | true 13 | false 14 | false 15 | true 16 | false 17 | true 18 | false 19 | 20 | 21 | FULL 22 | FULL 23 | STOP 24 | STOP 25 | STOP 26 | FULL 27 | STOP 28 | 42 29 | 0 30 | 0 31 | SINGLE CAB PARK 32 | 622 33 | STOP 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | /dev/tty.usbmodem1431 62 | 192.168.1.169 63 | 64 | -------------------------------------------------------------------------------- /DCCpp_Controller/eventHandlers.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Event Handlers 3 | // 4 | // Top-level processing of mouse, keyboard, and serial events. 5 | // Most of the real functionality is contained in other methods, 6 | // functions, and classes called by these handlers 7 | // 8 | ////////////////////////////////////////////////////////////////////////// 9 | 10 | void mouseDragged(){ 11 | if(selectedComponent!=null) 12 | selectedComponent.drag(); 13 | } 14 | 15 | ////////////////////////////////////////////////////////////////////////// 16 | 17 | void mousePressed(){ 18 | 19 | if(activeInputBox!=null){ 20 | for(InputBox inputBox : activeInputBox.linkedBoxes) 21 | inputBox.setIntValue(activeInputBox.getIntValue()); 22 | } 23 | 24 | activeInputBox=null; 25 | if(selectedComponent!=null){ 26 | if (keyPressed == true && key == CODED){ 27 | if(keyCode == SHIFT){ 28 | selectedComponent.shiftPressed(); 29 | } else if(keyCode == CONTROL){ 30 | msgBoxMain.setMessage("Component Name: "+selectedComponent.componentName,color(30,30,150)); 31 | } 32 | } 33 | else if(mouseButton==LEFT){ 34 | selectedComponent.pressed(); 35 | } else { 36 | selectedComponent.rightClick(); 37 | } 38 | } 39 | 40 | } 41 | 42 | ////////////////////////////////////////////////////////////////////////// 43 | 44 | void mouseReleased(){ 45 | if(selectedComponent!=null) 46 | selectedComponent.released(); 47 | } 48 | 49 | ////////////////////////////////////////////////////////////////////////// 50 | 51 | void keyPressed(){ 52 | keyCommand(key, keyCode); 53 | } 54 | 55 | ////////////////////////////////////////////////////////////////////////// 56 | 57 | void keyReleased(){ 58 | keyCommandReleased(key, keyCode); 59 | } 60 | 61 | ////////////////////////////////////////////////////////////////////////// 62 | 63 | void serialEvent(Serial p){ 64 | receivedString(p.readString()); 65 | } 66 | 67 | ////////////////////////////////////////////////////////////////////////// 68 | 69 | void clientEvent(Client c){ 70 | String s; 71 | s=c.readStringUntil('>'); 72 | if(s!=null) 73 | receivedString(s); 74 | } 75 | 76 | ////////////////////////////////////////////////////////////////////////// 77 | 78 | void receivedString(String s){ 79 | if(s.charAt(0)!='<') 80 | return; 81 | 82 | String c=s.substring(2,s.length()-1); 83 | 84 | switch(s.charAt(1)){ 85 | 86 | case 'i': 87 | baseID=c; 88 | msgBoxMain.setMessage("Found "+baseID,color(0,150,0)); 89 | break; 90 | 91 | case '*': 92 | msgBoxDiagIn.setMessage(c,color(30,30,150)); 93 | break; 94 | 95 | case 'r': 96 | String[] cs=splitTokens(c,"|"); 97 | callBacks.get(int(cs[0])).execute(int(cs[1]),cs[2]); 98 | break; 99 | 100 | case 'T': 101 | int[] n=int(splitTokens(c)); 102 | if(n[0]>cabButtons.size()) 103 | break; 104 | CabButton t=cabButtons.get(n[0]-1); 105 | if(n[2]==1) 106 | t.speed=n[1]; 107 | else 108 | t.speed=-n[1]; 109 | break; 110 | 111 | case 'Q': 112 | if(sensorsHM.get(int(c))!=null){ 113 | sensorsHM.get(int(c)).pressed(); 114 | } 115 | break; 116 | 117 | case 'Y': 118 | int[] h1=int(splitTokens(c)); 119 | if(remoteButtonsHM.get(h1[0])!=null){ 120 | if(h1[1]==1) 121 | remoteButtonsHM.get(h1[0]).turnOn(); 122 | else 123 | remoteButtonsHM.get(h1[0]).turnOff(); 124 | } 125 | break; 126 | 127 | case 'H': 128 | int[] h=int(splitTokens(c)); 129 | 130 | if(trackButtonsHM.get(h[0])!=null){ 131 | trackButtonsHM.get(h[0]).update(h[1]); 132 | } else if(remoteButtonsHM.get(h[0])!=null){ 133 | if(h[1]==((remoteButtonsHM.get(h[0]).buttonType==ButtonType.T_COMMAND)?1:0)) 134 | remoteButtonsHM.get(h[0]).turnOn(); 135 | else 136 | remoteButtonsHM.get(h[0]).turnOff(); 137 | } 138 | 139 | break; 140 | 141 | case 'L': 142 | int[] z=int(splitTokens(c)); 143 | color tempColor; 144 | tempColor=color(z[0],z[1],z[2]); 145 | colorMode(HSB,1.0,1.0,1.0); 146 | ledColorButton.hue=hue(tempColor); 147 | ledColorButton.sat=saturation(tempColor); 148 | ledColorButton.val=brightness(tempColor); 149 | ledColorButton.update(0); 150 | colorMode(RGB,255); 151 | break; 152 | 153 | case 'U': 154 | autoPilot.cabList.clear(); 155 | autoPilot.setProgram(AutoProgram.SINGLE_CAB_RUN); 156 | autoPilot.turnOn(); 157 | break; 158 | 159 | case 'p': 160 | if(c.equals("1")){ 161 | powerButton.isOn=true; 162 | msgBoxMain.setMessage("Track Power On",color(30,30,150)); 163 | } else if(c.equals("0")){ 164 | powerButton.isOn=false; 165 | msgBoxMain.setMessage("Track Power Off",color(30,30,150)); 166 | } else if(c.equals("2")){ 167 | msgBoxMain.setMessage("MAIN Track Current Overload - Power Off",color(200,30,30)); 168 | powerButton.isOn=false; 169 | } else if(c.equals("3")){ 170 | msgBoxMain.setMessage("PROG Track Current Overload - Power Off",color(200,30,30)); 171 | powerButton.isOn=false; 172 | } 173 | break; 174 | 175 | case 'a': 176 | currentMeter.addSample(int(c)); 177 | break; 178 | 179 | } 180 | } // receivedString 181 | 182 | ////////////////////////////////////////////////////////////////////////// 183 | 184 | void keyCommand(char k, int kC){ 185 | 186 | if(activeInputBox!=null){ 187 | activeInputBox.keyStroke(k, kC); 188 | return; 189 | } 190 | 191 | if(k==CODED){ 192 | switch(kC){ 193 | case UP: 194 | if(throttleA.cabButton!=null){ 195 | if(!keyHold) 196 | throttleA.pressed(); 197 | throttleA.keyControl(1); 198 | } 199 | break; 200 | case DOWN: 201 | if(throttleA.cabButton!=null){ 202 | if(!keyHold) 203 | throttleA.pressed(); 204 | throttleA.keyControl(-1); 205 | } 206 | break; 207 | case LEFT: 208 | if(throttleA.cabButton!=null){ 209 | throttleA.keyControl(0); 210 | } 211 | break; 212 | case RIGHT: 213 | if(throttleA.cabButton!=null){ 214 | throttleA.cabButton.stopThrottle(); 215 | } 216 | break; 217 | } 218 | } // key is coded 219 | 220 | else{ 221 | switch(k){ 222 | case 'P': 223 | powerButton.turnOn(); 224 | break; 225 | 226 | case 'F': 227 | aPort.write("<3>"); 228 | break; 229 | 230 | case 'f': 231 | aPort.write("<2>"); 232 | break; 233 | 234 | case ' ': 235 | powerButton.turnOff(); 236 | break; 237 | 238 | case 'a': 239 | accWindow.toggle(); 240 | break; 241 | 242 | case 'c': 243 | currentMeter.isOn=!currentMeter.isOn; 244 | break; 245 | 246 | case 'e': 247 | extrasWindow.toggle(); 248 | break; 249 | 250 | case 'x': 251 | autoWindow.toggle(); 252 | break; 253 | 254 | case 'S': 255 | sensorWindow.toggle(); 256 | break; 257 | 258 | case 'l': 259 | ledWindow.toggle(); 260 | break; 261 | 262 | case 's': 263 | portWindow.toggle(); 264 | break; 265 | 266 | case 'h': 267 | helpWindow.toggle(); 268 | break; 269 | 270 | case 'q': 271 | imageWindow.toggle(); 272 | break; 273 | 274 | case 'd': 275 | diagWindow.toggle(); 276 | break; 277 | 278 | case 'i': 279 | if(layoutBridge.equals(layout2)) 280 | layoutBridge.copy(layout); 281 | else 282 | layoutBridge.copy(layout2); 283 | break; 284 | 285 | case 'p': 286 | progWindow.toggle(); 287 | break; 288 | 289 | case 'o': 290 | opWindow.toggle(); 291 | break; 292 | 293 | case 'n': 294 | if(throttleA.cabButton!=null){ 295 | throttleA.cabButton.fbWindow.close(); 296 | throttleA.cabButton.fbWindow=throttleA.cabButton.windowList.get((throttleA.cabButton.windowList.indexOf(throttleA.cabButton.fbWindow)+1)%throttleA.cabButton.windowList.size()); 297 | throttleA.cabButton.fbWindow.open(); 298 | } 299 | break; 300 | 301 | case '1': 302 | case '2': 303 | case '3': 304 | case '4': 305 | case '5': 306 | case '6': 307 | case '7': 308 | cabButtons.get(int(k)-int('1')).pressed(); 309 | break; 310 | 311 | } 312 | } // key not coded 313 | 314 | keyHold=true; 315 | } // keyCommand 316 | 317 | ////////////////////////////////////////////////////////////////////////// 318 | 319 | void keyCommandReleased(char k, int kC){ 320 | 321 | keyHold=false; 322 | 323 | if(k==CODED){ 324 | switch(kC){ 325 | } 326 | } // key is coded 327 | 328 | else{ 329 | switch(k){ 330 | } 331 | } // key not coded 332 | 333 | } // keyCommandReleased 334 | 335 | 336 | ////////////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /DCCpp_Controller/gButtons.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Generic Ellipse and Rectangle Buttons 3 | // 4 | // EllipseButton - base class for creating simple buttons 5 | // - operating buttons that extend EllipseButton should 6 | // over-ride these methods with functionality specific 7 | // to that button 8 | // 9 | // RectButton - variant of EllipseButton that define a rectanglular button 10 | // 11 | ////////////////////////////////////////////////////////////////////////// 12 | 13 | class EllipseButton extends DccComponent{ 14 | int bWidth, bHeight; 15 | int baseHue; 16 | color textColor; 17 | int fontSize; 18 | String bText; 19 | ButtonType buttonType; 20 | int remoteCode; 21 | boolean isOn=false; 22 | 23 | EllipseButton(){ 24 | this(width/2,height/2,80,50,100,color(0),16,"Button",ButtonType.NORMAL); 25 | } 26 | 27 | EllipseButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){ 28 | this(null, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType); 29 | } 30 | 31 | EllipseButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){ 32 | this.xPos=xPos; 33 | this.yPos=yPos; 34 | this.bWidth=bWidth; 35 | this.bHeight=bHeight; 36 | this.bText=bText; 37 | this.fontSize=fontSize; 38 | this.baseHue=baseHue; 39 | this.textColor=textColor; 40 | this.window=window; 41 | this.buttonType=buttonType; 42 | if(window==null) 43 | dccComponents.add(this); 44 | else 45 | window.windowComponents.add(this); 46 | } // EllipseButton 47 | 48 | ////////////////////////////////////////////////////////////////////////// 49 | 50 | void display(){ 51 | colorMode(HSB,255); 52 | ellipseMode(CENTER); 53 | noStroke(); 54 | fill(color(baseHue,255,isOn?255:125)); 55 | ellipse(xPos+xWindow(),yPos+yWindow(),bWidth,bHeight); 56 | fill(textColor); 57 | textFont(buttonFont,fontSize); 58 | textAlign(CENTER,CENTER); 59 | text(bText,xPos+xWindow(),yPos+yWindow()); 60 | if(buttonType==ButtonType.ONESHOT && isOn) 61 | turnOff(); 62 | colorMode(RGB,255); 63 | } // display 64 | 65 | ////////////////////////////////////////////////////////////////////////// 66 | 67 | void check(){ 68 | if(selectedComponent==null && (mouseX-xPos-xWindow())*(mouseX-xPos-xWindow())/(bWidth*bWidth/4.0)+(mouseY-yPos-yWindow())*(mouseY-yPos-yWindow())/(bHeight*bHeight/4.0)<=1){ 69 | cursorType=HAND; 70 | selectedComponent=this; 71 | } 72 | } // check 73 | 74 | ////////////////////////////////////////////////////////////////////////// 75 | 76 | void turnOn(){ 77 | isOn=true; 78 | } 79 | 80 | ////////////////////////////////////////////////////////////////////////// 81 | 82 | void turnOff(){ 83 | isOn=false; 84 | } 85 | 86 | ////////////////////////////////////////////////////////////////////////// 87 | 88 | void pressed(){ 89 | 90 | if(buttonType==ButtonType.T_COMMAND){ 91 | aPort.write("":"1>")); 92 | return; 93 | } 94 | 95 | if(buttonType==ButtonType.TI_COMMAND){ 96 | aPort.write("":"0>")); 97 | return; 98 | } 99 | 100 | if(buttonType==ButtonType.Z_COMMAND){ 101 | aPort.write("":"1>")); 102 | return; 103 | } 104 | 105 | if(isOn) 106 | turnOff(); 107 | else 108 | turnOn(); 109 | } 110 | 111 | ////////////////////////////////////////////////////////////////////////// 112 | 113 | void released(){ 114 | if(buttonType==ButtonType.HOLD) 115 | turnOff(); 116 | } 117 | 118 | } // EllipseButton Class 119 | 120 | ////////////////////////////////////////////////////////////////////////// 121 | 122 | class RectButton extends EllipseButton{ 123 | 124 | RectButton(){ 125 | super(width/2,height/2,80,50,100,color(0),16,"Button",ButtonType.NORMAL); 126 | } 127 | 128 | RectButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){ 129 | super(null, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType); 130 | } 131 | 132 | RectButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType){ 133 | super(window, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType); 134 | } 135 | 136 | RectButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, color textColor, int fontSize, String bText, ButtonType buttonType, int remoteCode){ 137 | super(window, xPos, yPos, bWidth, bHeight, baseHue, textColor, fontSize, bText, buttonType); 138 | this.remoteCode=remoteCode; 139 | remoteButtonsHM.put(remoteCode,this); 140 | } // RectangleButton 141 | 142 | ////////////////////////////////////////////////////////////////////////// 143 | 144 | void display(){ 145 | colorMode(HSB,255); 146 | rectMode(CENTER); 147 | noStroke(); 148 | fill(color(baseHue,255,isOn?255:125)); 149 | rect(xPos+xWindow(),yPos+yWindow(),bWidth,bHeight); 150 | fill(textColor); 151 | textFont(buttonFont,fontSize); 152 | textAlign(CENTER,CENTER); 153 | text(bText,xPos+xWindow(),yPos+yWindow()); 154 | if(buttonType==ButtonType.ONESHOT && isOn) 155 | turnOff(); 156 | colorMode(RGB,255); 157 | } // display 158 | 159 | ////////////////////////////////////////////////////////////////////////// 160 | 161 | void check(){ 162 | if(selectedComponent==null && (mouseX>xPos+xWindow()-bWidth/2)&&(mouseXyPos+yWindow()-bHeight/2)&&(mouseY linkedBoxes = new ArrayList(); 98 | 99 | InputBox(CabButton cb){ 100 | this(cb.editWindow,4,cb.bHeight/2,cb.fontSize,color(255,0,255),color(0,0,0),4,InputType.DEC); 101 | this.cb=cb; 102 | setIntValue(cb.cab); 103 | } 104 | 105 | InputBox(int xPos, int yPos, int fontSize, color boxColor, color msgColor, int maxChars, InputType inputType){ 106 | this(null, xPos, yPos, fontSize, boxColor, msgColor, maxChars, inputType); 107 | } 108 | 109 | InputBox(Window window, int xPos, int yPos, int fontSize, color boxColor, color msgColor, int maxChars, InputType inputType){ 110 | this.xPos=xPos; 111 | this.yPos=yPos; 112 | this.fontSize=fontSize; 113 | this.msgColor=msgColor; 114 | this.boxColor=boxColor; 115 | this.window=window; 116 | this.maxChars=maxChars; 117 | textFont(messageFont,fontSize); 118 | String s="0"; 119 | for(int i=0;ixPos+xWindow())&&(mouseXyPos+yWindow()-kHeight/2)&&(mouseY0){ 223 | inputText=inputText.substring(0,inputText.length()-1); 224 | } else if(k==ENTER || k==RETURN){ 225 | activeInputBox=null; 226 | for( InputBox inputBox : linkedBoxes) 227 | inputBox.setIntValue(getIntValue()); 228 | if(cb!=null){ 229 | cb.cab=getIntValue(); 230 | cb.bText=str(cb.cab); 231 | cb.cabFile=("cab-"+cb.cab+".jpg"); 232 | cb.cabImage=loadImage(cb.cabFile); 233 | cb.name="Cab"+cb.cab; 234 | cabsHM.put(cb.name,cb); 235 | cb.editWindow.close(); 236 | } 237 | } else if(k==TAB){ 238 | if(nextBox!=null) 239 | nextBox.pressed(); 240 | else 241 | activeInputBox=null; 242 | for( InputBox inputBox : linkedBoxes) 243 | inputBox.setIntValue(getIntValue()); 244 | if(cb!=null){ 245 | setIntValue(cb.cab); 246 | cb.editWindow.close(); 247 | } 248 | } 249 | } // kc!=CODED 250 | } // keyStroke 251 | 252 | } // InputBox Class 253 | 254 | ////////////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /DCCpp_Controller/gWindows.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Generic Windows 3 | // 4 | // Window - creates a window box of a specified size, color, and 5 | // initial position into which other components can be placed 6 | // such as buttons, message boxes, and text boxes 7 | // 8 | // DragBar - creates a drag bar on window to allow it to be dragged 9 | // across screen 10 | // 11 | // CloseButton - creates close button on window that closes window box 12 | // - windows are normally opened by other buttons or key commands 13 | // defined elsewhere 14 | // 15 | // ImageWindow - extends Window to create a window bx into which 16 | // a single cab image tied to a specified throttle will be displayed 17 | // 18 | // JPGWindow - extends Window to create a generic window box for diplaying a single jpg image 19 | // 20 | ////////////////////////////////////////////////////////////////////////// 21 | 22 | class Window extends DccComponent{ 23 | int xPos, yPos; 24 | int kWidth, kHeight; 25 | color backgroundColor; 26 | color outlineColor; 27 | 28 | ArrayList windowComponents = new ArrayList(); 29 | 30 | Window(int xPos, int yPos, int kWidth, int kHeight, color backgroundColor, color outlineColor){ 31 | this.xPos=xPos; 32 | this.yPos=yPos; 33 | this.kWidth=kWidth; 34 | this.kHeight=kHeight; 35 | this.backgroundColor=backgroundColor; 36 | this.outlineColor=outlineColor; 37 | } // Window 38 | 39 | ////////////////////////////////////////////////////////////////////////// 40 | 41 | void display(){ 42 | 43 | rectMode(CORNER); 44 | fill(backgroundColor); 45 | strokeWeight(3); 46 | stroke(outlineColor); 47 | rect(xPos,yPos,kWidth,kHeight); 48 | } // display 49 | 50 | ////////////////////////////////////////////////////////////////////////// 51 | 52 | void check(){ 53 | if(selectedComponent==null && (mouseX>xPos)&&(mouseXyPos)&&(mouseYxPos+window.xPos)&&(mouseXyPos+window.yPos)&&(mouseYxPos+window.xPos)&&(mouseXyPos+window.yPos)&&(mouseY1024){ 82 | msgBoxMain.setMessage("Error - CV must be in range 1-1024",color(255,30,30)); 83 | } else if(bText=="WRITE"){ 84 | aPort.write(""); 85 | } else if(bText=="READ"){ 86 | aPort.write(""); 87 | } 88 | } // pressed 89 | 90 | ////////////////////////////////////////////////////////////////////////// 91 | 92 | void execute(int n, String c){ 93 | String[] cs = splitTokens(c); 94 | 95 | int cv=int(cs[0]); 96 | int val=int(cs[1]); 97 | 98 | progCVInput.setIntValue(cv); 99 | 100 | if(val<0){ 101 | msgBoxMain.setMessage(n==0?"Error - Read Failed":"Error - Write Failed",color(255,30,30)); 102 | progHEXInput.resetValue(); 103 | progBINInput.resetValue(); 104 | progDECInput.resetValue(); 105 | } else{ 106 | msgBoxMain.setMessage(n==0?"Read Succeeded":"Write Succeeded",color(30,150,30)); 107 | progHEXInput.setIntValue(val); 108 | progBINInput.setIntValue(val); 109 | progDECInput.setIntValue(val); 110 | } 111 | 112 | } // execute 113 | 114 | } // progWriteReadButton Class 115 | 116 | ////////////////////////////////////////////////////////////////////////// 117 | // DCC Component: ProgAddReadButton 118 | ////////////////////////////////////////////////////////////////////////// 119 | 120 | class ProgAddReadButton extends EllipseButton implements CallBack{ 121 | InputBox shortAddInput, longAddInput; 122 | MessageBox activeAddBox; 123 | int longAdd; 124 | 125 | ProgAddReadButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox shortAddInput, InputBox longAddInput, MessageBox activeAddBox){ 126 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, shortAddInput, longAddInput, activeAddBox); 127 | } 128 | 129 | ProgAddReadButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox shortAddInput, InputBox longAddInput, MessageBox activeAddBox){ 130 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 131 | this.shortAddInput=shortAddInput; 132 | this.longAddInput=longAddInput; 133 | this.activeAddBox=activeAddBox; 134 | callBacks.add(this); 135 | } // ProgAddReadButton 136 | 137 | ////////////////////////////////////////////////////////////////////////// 138 | 139 | void pressed(){ 140 | super.pressed(); 141 | 142 | aPort.write(""); 143 | 144 | } // pressed 145 | 146 | ////////////////////////////////////////////////////////////////////////// 147 | 148 | void execute(int n, String c){ 149 | String[] cs = splitTokens(c); 150 | 151 | int cv=int(cs[0]); 152 | int val=int(cs[1]); 153 | 154 | switch(cv){ 155 | 156 | case 1: 157 | if(val<0){ 158 | msgBoxMain.setMessage("Error - Reading Short Address Failed",color(255,30,30)); 159 | shortAddInput.resetValue(); 160 | } else{ 161 | shortAddInput.setIntValue(val); 162 | aPort.write(""); 163 | } 164 | break; 165 | 166 | case 17: 167 | if(val<0){ 168 | msgBoxMain.setMessage("Error - Reading First Byte of Long Address Failed",color(255,30,30)); 169 | longAddInput.resetValue(); 170 | } else{ 171 | longAdd=(val&0x3F)*256; 172 | aPort.write(""); 173 | } 174 | break; 175 | 176 | case 18: 177 | if(val<0){ 178 | msgBoxMain.setMessage("Error - Reading Second Byte of Long Address Failed",color(255,30,30)); 179 | longAddInput.resetValue(); 180 | } else{ 181 | longAdd+=val; 182 | longAddInput.setIntValue(longAdd); 183 | aPort.write(""); 184 | } 185 | break; 186 | 187 | case 29: 188 | if(val<0){ 189 | msgBoxMain.setMessage("Error - Reading Second Byte of Long Address Failed",color(255,30,30)); 190 | activeAddBox.setMessage("?",color(200,50,50)); 191 | } else{ 192 | if((val&0x20)==0) 193 | activeAddBox.setMessage("SHORT",color(200,50,50)); 194 | else 195 | activeAddBox.setMessage("LONG",color(200,50,50)); 196 | msgBoxMain.setMessage("Reading Short and Long Addresses Succeeded",color(30,150,30)); 197 | } 198 | break; 199 | 200 | } 201 | 202 | } // execute 203 | 204 | } // ProgAddReadButton Class 205 | 206 | ////////////////////////////////////////////////////////////////////////// 207 | // DCC Component: ProgShortAddWriteButton 208 | ////////////////////////////////////////////////////////////////////////// 209 | 210 | class ProgShortAddWriteButton extends EllipseButton implements CallBack{ 211 | InputBox addInput; 212 | 213 | ProgShortAddWriteButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){ 214 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, addInput); 215 | } 216 | 217 | ProgShortAddWriteButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){ 218 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 219 | this.addInput=addInput; 220 | callBacks.add(this); 221 | } // ProgAddReadButton 222 | 223 | ////////////////////////////////////////////////////////////////////////// 224 | 225 | void pressed(){ 226 | super.pressed(); 227 | 228 | int val=addInput.getIntValue(); 229 | 230 | if(val<1 || val>127){ 231 | msgBoxMain.setMessage("Error - Short Address must be in range 1-127",color(255,30,30)); 232 | } else { 233 | aPort.write(""); 234 | } 235 | 236 | } // pressed 237 | 238 | ////////////////////////////////////////////////////////////////////////// 239 | 240 | void execute(int n, String c){ 241 | String[] cs = splitTokens(c); 242 | 243 | int cv=int(cs[0]); 244 | int val=int(cs[1]); 245 | 246 | if(val<0){ 247 | msgBoxMain.setMessage("Error - Write Short Address Failed",color(255,30,30)); 248 | addInput.resetValue(); 249 | } else{ 250 | msgBoxMain.setMessage("Write Short Address Succeeded",color(30,150,30)); 251 | addInput.setIntValue(val); 252 | } 253 | 254 | } // execute 255 | 256 | } // ProgShortAddWriteButton Class 257 | 258 | ////////////////////////////////////////////////////////////////////////// 259 | // DCC Component: ProgLongAddWriteButton 260 | ////////////////////////////////////////////////////////////////////////// 261 | 262 | class ProgLongAddWriteButton extends EllipseButton implements CallBack{ 263 | InputBox addInput; 264 | int longAddIn, longAddOut; 265 | 266 | ProgLongAddWriteButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){ 267 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, addInput); 268 | } 269 | 270 | ProgLongAddWriteButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox addInput){ 271 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 272 | this.addInput=addInput; 273 | callBacks.add(this); 274 | } // ProgAddReadButton 275 | 276 | ////////////////////////////////////////////////////////////////////////// 277 | 278 | void pressed(){ 279 | super.pressed(); 280 | 281 | longAddIn=addInput.getIntValue(); 282 | 283 | if(longAddIn<0 || longAddIn>10239){ 284 | msgBoxMain.setMessage("Error - Long Address must be in range 0-10239",color(255,30,30)); 285 | } else { 286 | aPort.write(""); 287 | } 288 | 289 | } // pressed 290 | 291 | ////////////////////////////////////////////////////////////////////////// 292 | 293 | void execute(int n, String c){ 294 | String[] cs = splitTokens(c); 295 | 296 | int cv=int(cs[0]); 297 | int val=int(cs[1]); 298 | 299 | switch(cv){ 300 | 301 | case 17: 302 | if(val<0){ 303 | msgBoxMain.setMessage("Error - Writing First Byte of Long Address Failed",color(255,30,30)); 304 | addInput.resetValue(); 305 | } else{ 306 | longAddOut=(val&0x3F)*256; 307 | aPort.write(""); 308 | } 309 | break; 310 | 311 | case 18: 312 | if(val<0){ 313 | msgBoxMain.setMessage("Error - Writing Second Byte of Long Address Failed",color(255,30,30)); 314 | addInput.resetValue(); 315 | } else{ 316 | msgBoxMain.setMessage("Write Long Address Succeeded",color(30,150,30)); 317 | longAddOut+=val; 318 | addInput.setIntValue(longAddOut); 319 | } 320 | break; 321 | } 322 | 323 | } // execute 324 | 325 | } // ProgLongAddWriteButton Class 326 | 327 | ////////////////////////////////////////////////////////////////////////// 328 | // DCC Component: ProgLongShortButton 329 | ////////////////////////////////////////////////////////////////////////// 330 | 331 | class ProgLongShortButton extends EllipseButton implements CallBack{ 332 | MessageBox activeAddBox; 333 | 334 | ProgLongShortButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, MessageBox activeAddBox){ 335 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, activeAddBox); 336 | } 337 | 338 | ProgLongShortButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, MessageBox activeAddBox){ 339 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 340 | this.activeAddBox=activeAddBox; 341 | callBacks.add(this); 342 | } // ProgrWriteReadButton 343 | 344 | ////////////////////////////////////////////////////////////////////////// 345 | 346 | void pressed(){ 347 | super.pressed(); 348 | 349 | if(bText=="Long"){ 350 | aPort.write(""); 351 | } else if(bText=="Short"){ 352 | aPort.write(""); 353 | } 354 | 355 | } // pressed 356 | 357 | ////////////////////////////////////////////////////////////////////////// 358 | 359 | void execute(int n, String c){ 360 | String[] cs = splitTokens(c); 361 | 362 | int val=int(cs[2]); 363 | 364 | switch(val){ 365 | 366 | case -1: 367 | msgBoxMain.setMessage(n==1?"Error - Activating Long Address Failed":"Error - Activating Short Address Failed",color(255,30,30)); 368 | activeAddBox.setMessage("?",color(200,50,50)); 369 | break; 370 | 371 | case 0: 372 | msgBoxMain.setMessage("Activating Short Address Succeeded",color(30,150,30)); 373 | activeAddBox.setMessage("SHORT",color(200,50,50)); 374 | break; 375 | 376 | case 1: 377 | msgBoxMain.setMessage("Activating Long Address Succeeded",color(30,150,30)); 378 | activeAddBox.setMessage("LONG",color(200,50,50)); 379 | break; 380 | } 381 | 382 | } // execute 383 | 384 | } // ProgLongShortButton Class 385 | 386 | ////////////////////////////////////////////////////////////////////////// 387 | // DCC Component: OpWriteButton 388 | ////////////////////////////////////////////////////////////////////////// 389 | 390 | class OpWriteButton extends EllipseButton{ 391 | InputBox opCVInput, opValueInput; 392 | 393 | OpWriteButton(int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox opCVInput, InputBox opValueInput){ 394 | this(null, xPos, yPos, bWidth, bHeight, baseHue, fontSize, bText, opCVInput, opValueInput); 395 | } 396 | 397 | OpWriteButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText, InputBox opCVInput, InputBox opValueInput){ 398 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 399 | this.opCVInput=opCVInput; 400 | this.opValueInput=opValueInput; 401 | } // OpWriteButton 402 | 403 | ////////////////////////////////////////////////////////////////////////// 404 | 405 | void pressed(){ 406 | super.pressed(); 407 | int cab=opCabInput.getIntValue(); 408 | int cv=opCVInput.getIntValue(); 409 | int val=opValueInput.getIntValue(); 410 | 411 | if(cab<1 || cab>10239){ 412 | msgBoxMain.setMessage("Error - Cab must be in range 1-10239",color(255,30,30)); 413 | return; 414 | } 415 | if(cv<1 || cv>1024){ 416 | msgBoxMain.setMessage("Error - CV must be in range 1-1024",color(255,30,30)); 417 | return; 418 | } 419 | 420 | if(bText=="WRITE"){ 421 | aPort.write(""); 422 | return; 423 | } 424 | 425 | if(val>7){ 426 | msgBoxMain.setMessage("Error - Bit must be in range 0-7",color(255,30,30)); 427 | return; 428 | } 429 | 430 | 431 | if(bText=="SET"){ 432 | aPort.write(""); 433 | } else if(bText=="CLEAR"){ 434 | aPort.write(""); 435 | } 436 | 437 | } // pressed 438 | 439 | } // OpWriteButton Class -------------------------------------------------------------------------------- /DCCpp_Controller/serialComponents.pde: -------------------------------------------------------------------------------- 1 | ////////////////////////////////////////////////////////////////////////// 2 | // DCC++ CONTROLLER: Serial Components 3 | // 4 | // All classes and methods related to serial communication to and from 5 | // the DCC++ Base Station 6 | // 7 | // PortScanButton - function depends on button label as follows: 8 | // 9 | // "SCAN" - create a list all serial ports on the computer 10 | // ">" - scroll forward through the list 11 | // "<" - scroll backwards through the list 12 | // "CONNECT" - attempt to connect to a DCC++ Base Station 13 | // 14 | // - the default configuration of DCC++ Controller defines a 15 | // Serial Window that includes all of these components 16 | // 17 | // ArduinoPort - defines a generic port connection to the DCC++ Base Station 18 | // - extends Processing's normal Serial class by adding an 19 | // Ethernet or WiFi Client connection at port 2560 as well as 20 | // a "simulation" function so that DCC++ Controller can be run 21 | // in "emulator" mode without actually establishing a connection 22 | // to the DCC++ Base Station 23 | // - ideal for developing, testing, and demonstrating DCC++ Controller 24 | // without an Arduino 25 | // - also adds functionality that echos to a pre-specified text box all 26 | // text that is written to the DCC++ Base Station 27 | // - the default configuration of DCC++ Controller defines a 28 | // Diagnostic Window that includes this text box and is useful for 29 | // observing the exact commands DCC++ Controller sends to the 30 | // DCC++ Base Station 31 | // 32 | ////////////////////////////////////////////////////////////////////////// 33 | // DCC Component: PortScanButton 34 | ////////////////////////////////////////////////////////////////////////// 35 | 36 | class PortScanButton extends RectButton{ 37 | boolean isComplete; 38 | 39 | PortScanButton(Window window, int xPos, int yPos, int bWidth, int bHeight, int baseHue, int fontSize, String bText){ 40 | super(window, xPos, yPos, bWidth, bHeight, baseHue, color(255), fontSize, bText, ButtonType.ONESHOT); 41 | } // AccessoryButton 42 | 43 | ////////////////////////////////////////////////////////////////////////// 44 | 45 | void pressed(){ 46 | isComplete=false; 47 | super.pressed(); 48 | } 49 | 50 | ////////////////////////////////////////////////////////////////////////// 51 | 52 | void scan(){ 53 | String[] emulator = {"Emulator"}; 54 | String[] serverList=splitTokens(serverListXML.getContent()); 55 | 56 | 57 | aPort.portList=concat(emulator,Serial.list()); 58 | aPort.portList=concat(aPort.portList,serverList); 59 | 60 | aPort.displayedPort=0; 61 | portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200)); 62 | portNumBox.setMessage("Port "+(aPort.displayedPort+1)+" of "+aPort.portList.length,color(50,50,50)); 63 | 64 | } // scan 65 | 66 | ////////////////////////////////////////////////////////////////////////// 67 | 68 | void turnOff(){ 69 | String[] emulator = {"Emulator"}; 70 | 71 | if(isComplete==false){ 72 | isComplete=true; 73 | return; 74 | } 75 | 76 | super.turnOff(); 77 | 78 | if(bText=="SCAN"){ 79 | scan(); 80 | return; 81 | } // SCAN 82 | 83 | if(bText==">" && aPort.portList!=null && aPort.portList.length>0){ 84 | aPort.displayedPort=(aPort.displayedPort+1)%aPort.portList.length; 85 | portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200)); 86 | portNumBox.setMessage("Port "+(aPort.displayedPort+1)+" of "+aPort.portList.length,color(50,50,50)); 87 | return; 88 | } // > 89 | 90 | if(bText=="<" && aPort.portList!=null && aPort.portList.length>0){ 91 | if(--aPort.displayedPort<0) 92 | aPort.displayedPort=aPort.portList.length-1; 93 | portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200)); 94 | portNumBox.setMessage("Port "+(aPort.displayedPort+1)+" of "+aPort.portList.length,color(50,50,50)); 95 | return; 96 | } // < 97 | 98 | if(bText=="CONNECT" && aPort.portList!=null && aPort.portList.length>0){ 99 | arduinoPortXML.setContent(aPort.portList[aPort.displayedPort]); 100 | portBox.setMessage(aPort.portList[aPort.displayedPort],aPort.portList[aPort.displayedPort].equals(arduinoPortXML.getContent())?color(50,150,50):color(50,50,200)); 101 | saveXML(dccStatusXML,STATUS_FILE); 102 | baseID=null; 103 | aPort.open(arduinoPortXML.getContent()); 104 | return; 105 | } // < 106 | 107 | } // pressed 108 | 109 | } // PortScanButton Class 110 | 111 | ////////////////////////////////////////////////////////////////////////// 112 | // ArduinoPort 113 | ////////////////////////////////////////////////////////////////////////// 114 | 115 | class ArduinoPort{ 116 | Serial port; 117 | Client client; 118 | String[] portList; 119 | int displayedPort; 120 | boolean emulate; 121 | String portName; 122 | int baud; 123 | 124 | ArduinoPort(){ 125 | emulate=false; 126 | port=null; 127 | client=null; 128 | } 129 | 130 | ////////////////////////////////////////////////////////////////////////// 131 | 132 | void write(String text){ 133 | msgBoxDiagOut.setMessage(text,color(30,30,150)); 134 | 135 | if(emulate) 136 | simulate(text); 137 | else if(port!=null) 138 | port.write(text); 139 | else if(client!=null) 140 | client.write(text); 141 | 142 | } // write 143 | 144 | ////////////////////////////////////////////////////////////////////////// 145 | 146 | void simulate(String text){ 147 | String c = text.substring(2,text.length()-1); 148 | 149 | switch(text.charAt(1)){ 150 | 151 | case 'c': 152 | if(powerButton.isOn) 153 | receivedString(""); 154 | else 155 | receivedString(""); 156 | break; 157 | 158 | case '0': 159 | receivedString(""); 160 | break; 161 | 162 | case '1': 163 | receivedString(""); 164 | break; 165 | 166 | case 't': 167 | String[] s = splitTokens(c); 168 | if(int(s[2])==-1) 169 | s[2]="0"; 170 | receivedString(""); 171 | break; 172 | 173 | case 'T': 174 | String[] s1 = splitTokens(c); 175 | receivedString(""); 176 | break; 177 | 178 | case 'z': 179 | String[] s2 = splitTokens(c); 180 | receivedString(""); 181 | break; 182 | 183 | } //switch 184 | 185 | } // simulate 186 | 187 | ////////////////////////////////////////////////////////////////////////// 188 | 189 | void open(String portName){ 190 | int t; 191 | this.portName=portName; 192 | 193 | emulate=false; 194 | 195 | if(port!=null) 196 | port.stop(); 197 | 198 | if(client!=null) 199 | client.stop(); 200 | 201 | int[] n=int(splitTokens(portName,".")); 202 | 203 | if(n.length==4 && n[0]>0 && n[0]<=255 && n[1]>=0 && n[1]<=255 && n[2]>=0 && n[2]<=255 && n[3]>=0 && n[3]<=255){ 204 | client=new Client(Applet,portName,2560); 205 | if(client.ip()==null){ 206 | msgBoxMain.setMessage("Can't connect to Server: "+portName,color(200,50,0)); 207 | client=null; 208 | return; 209 | } else if(client!=null){ 210 | msgBoxMain.setMessage("Waiting for Base Station at Server: "+client.ip(),color(200,50,0)); 211 | client.write(""); 212 | return; 213 | } 214 | } 215 | 216 | if(portName.equals("Emulator")){ 217 | emulate=true; 218 | msgBoxMain.setMessage("Using Emulator to Simulate Arduino",color(50,50,200)); 219 | return; 220 | } 221 | 222 | try{ 223 | port=new Serial(Applet,portName,BASE_BAUD); 224 | port.bufferUntil('>'); 225 | } catch(Exception e){ 226 | msgBoxMain.setMessage("Serial Port Busy: "+portName,color(200,50,0)); 227 | port=null; 228 | return; 229 | } 230 | 231 | if(port.port==null){ 232 | msgBoxMain.setMessage("Can't find Serial Port: "+portName,color(200,50,0)); 233 | port=null; 234 | return; 235 | } 236 | 237 | msgBoxMain.setMessage("Waiting for Base Station at Serial Port: "+portName,color(200,50,0)); 238 | 239 | t=millis(); 240 | while(millis()-t<3000); 241 | port.write(""); 242 | 243 | } // open() 244 | 245 | } // Class ArduinoPort 246 | 247 | ////////////////////////////////////////////////////////////////////////// -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | What’s DCC++ 2 | ------------ 3 | 4 | DCC++ is an open-source hardware and software system for the operation of DCC-equipped model railroads. 5 | 6 | The system consists of two parts, the DCC++ Base Station and the DCC++ Controller. 7 | 8 | The DCC++ Base Station consists of an Arduino micro controller fitted with an Arduino Motor Shield that can be connected directly to the tracks of a model railroad. 9 | 10 | The DCC++ Controller provides operators with a customizable GUI to control their model railroad. It is written in Java using the Processing graphics library and IDE and communicates with the DCC++ Base Station via a standard serial connection over a USB cable or wireless over BlueTooth. 11 | 12 | What’s in this Repository 13 | ------------------------- 14 | 15 | This repository, Controller, contains a complete DCC++ Graphical User Interface sketch, written in Java, and designed for use within the Processing IDE environment (www.processing.org). All sketch files are in the folder named DCCpp_Controller. 16 | 17 | To utilize this sketch, simply download a zip file of this repository and open the file DCCpp_Controller.pde within the DCCpp_Controller folder using your Processing IDE. Please do not rename the folder containing the sketch code, nor add any files to that folder. The Processing IDE relies on the structure and name of the folder to properly display and run the code. 18 | 19 | Though this code base is relatively mature and has been tested with the latest version of Processing (3.0.1), it is not as well-commented or documented as the DCC++ Base Station code for the Arduino. 20 | 21 | Before using this code, you may wish to visit my DCC++ YouTube channel (see link on main DCC++ GitHub screen) and watch the demo videos showing all the features and functions of this interface. 22 | 23 | Use and Customization 24 | --------------------- 25 | 26 | DCC++ Controller can be used with or withouth a connection to a DCC++ Base Station, though obviously without a Base Station you won't be able to control a model railroad. However, you would still be able to test out the interface, modify the layout, create turnouts, add and delete cabs, throttles, etc. 27 | 28 | All main operating functions are found on the main screen. Hitting 'h' toggles a help window on and off that contains a list of all other windows that can be opened with similar single-key toggling. You can also toggle the help window via the question mark in the upper right corner of the screen. 29 | 30 | To connect the DCC++ Controller to a DCC++ Arduino Base Station, first connect the Base Station to your PC or Mac via its USB cable. Then open and run the DCC++ Controller within the Processing Environment. Hitting 's' will bring up a serial connection window. Hit the SCAN button and the interface will identify all available serial ports. Use the arrow keys to select which port contains your DCC++ Arduino Base Station and then hit the CONNECT button. After 5 or so seconds, a message should appear at the top of the screen indicating connectivity. If not, please re-check your serial connection and make sure you don't have the Arduino IDE Serial Monitor (or any other serial monitor) opened and connected to the Base Station. This will block the Controller from connecting to the Arduino since only one serial connection to the Arduino can be opened at a time. 31 | 32 | If you do not have an Arduino Base Station, or just want to test out the Controller, you can select "Emulator" from the serial connection window. This will allow the Controller to operate most functions as if it were connected to a Base Station. 33 | 34 | Note that most of the functions on the Controller rely on feedback from the Base Station in order to operate. This is why the imbedded "Emulator" functionality is needed -- to provide emulated feedback to the Controller. 35 | 36 | If you sucessfully connect the Controller to the Base Station, the first thing you may want to test is the "Power" button. This should turn on and off power to the tracks. If the Power button lights up when you press it, this means the Controller is properly communicating with the Base Station since the Power button won't light until it receives a confirm from the Base Station. 37 | 38 | I have pre-programmed 7 cabs and all of their functions into a single throttle. You should be able to select any cab button and control the throttle. However, unless your cab numbers happen to match one of the 7 I have included, you will not be able to operate any of your trains. Almost all of the code you will need to customize for your own layout can be found in the "controllerConfig" tab of the Processing IDE. Definitions of the throttle, the cabs, and the cab buttons can be found starting at line 283. The first cab you'll see defined is #2004 in the following line: 39 | 40 | cab2004 = new CabButton(tAx-125,tAy-150,50,30,150,15,2004,throttleA); 41 | 42 | It's okay to leave the name of the variable as cab2004 -- it could be called anything. The actual cab number is provided in the second-to-last parameter. Change this from 2004 to match the cab number for one of your locomotives. Then restart the program (you don't have to restart Processing itself, just the Controller program). Controller should have remembered your serial settings from before so you wont have to go through the serial scan and connect every time, unless you want to make a change. 43 | 44 | Hit the Power button and verifify that it lights up. Then hit the cab button that now should show the cab number you just modified. Give the throttle a try. If all is well, your train should now be moving. 45 | 46 | Starting at around line 365 in “configController” you'll find all the code that creates the track layout. You should be able to modify these to match your own. The code under the "dTracks" tab should provide some info on the parameters. 47 | 48 | Starting at around line 507 you'll find the code for the turnouts. The routines supporting these functions can be found in the "dRoutes" tab. Note that each turnout has a uniquely defined ID number. These numbers must match the ID number of turnouts you defined in the Arduino DCC++ Base Station sketch. If not, the turnout will not respond on the interface when you click it to switch direction (and obviously will not respond on your layout). You can define a turnout in the Base Station sketch even if it is not really connected to an accessory decoder, if you'd like to simply test the Controller functionality. 49 | 50 | This is a rather complex code base and it's definitely not as clean and tight as the Base Station sketch, but I hope you'll be able to get the gist of things by changing individual parameters and observing the net effect. 51 | 52 | Ideally, if others start to utilize this Controller, it would probably make sense to move the customization of the cabs and layout into an XML or JSON parameters file. A good project for the future... 53 | 54 | Enjoy! 55 | 56 | 57 | 58 | 59 | --------------------------------------------------------------------------------