├── src ├── CommInterface │ ├── DCCEX-commands.xlsx │ ├── CommInterface.h │ ├── CommInterfaceSerial.h │ ├── CommInterfaceUSB.h │ ├── DCCEXParser.h │ ├── CommInterfaceUSB.cpp │ ├── CommInterfaceSerial.cpp │ ├── CommManager.h │ ├── CommManager.cpp │ └── DCCEXParser.cpp ├── DCC-EX-Lib.h ├── Accessories │ ├── Outputs.h │ ├── Turnouts.h │ ├── Sensors.h │ ├── EEStore.h │ ├── EEStore.cpp │ ├── Turnouts.cpp │ ├── Sensors.cpp │ └── Outputs.cpp ├── DCC │ ├── Waveform.h │ ├── Queue.h │ ├── DCCServiceTimers.cpp │ ├── Railcom.h │ ├── DCCMain.h │ ├── DCCService.h │ ├── DCCMainTimers.cpp │ ├── Railcom.cpp │ ├── DCCService.cpp │ └── DCCMain.cpp └── Boards │ ├── BoardPololuMotorShield.h │ ├── BoardArduinoMotorShield.h │ ├── Board.h │ ├── BoardPololuMotorShield.cpp │ ├── BoardArduinoMotorShield.cpp │ └── AnalogReadFast.h ├── README.md ├── .gitignore ├── library.properties ├── archive └── HardwarePresets.cpp └── LICENSE /src/CommInterface/DCCEX-commands.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DCC-EX/DCC-EX-Lib/master/src/CommInterface/DCCEX-commands.xlsx -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #### Note: This repository is a library. Please use CommandStation-DCC (https://github.com/DCC-EX/CommandStation-EX) if you want to build the code for your device. 2 | 3 | # CommandStation Library 4 | Arduino library for controlling and powering devices on a two-wire bus. For controlling model railroads, including trains and accessories. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=DCC-EX-Lib 2 | version=1.0.0 3 | author=David Cutting, Anthony Williams 4 | maintainer=David Cutting , Anthony Williams 5 | sentence=Arduino library for controlling and powering devices on a two-wire bus. 6 | paragraph=Arduino library for controlling and powering devices on a two-wire bus. Based on DCC++ for Model Railroads. 7 | category=Communication 8 | url=https://github.com/DCC-EX/DCC-EX-Lib 9 | architectures=avr, samd, megaavr 10 | includes=DCC-EX-Lib.h 11 | -------------------------------------------------------------------------------- /src/CommInterface/CommInterface.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CommInterface.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_COMMINTERFACE_COMMINTERFACE_H_ 21 | #define COMMANDSTATION_COMMINTERFACE_COMMINTERFACE_H_ 22 | 23 | #include 24 | 25 | class CommInterface { 26 | public: 27 | virtual void process() = 0; 28 | virtual void showConfiguration() = 0; 29 | virtual void showInitInfo() = 0; 30 | 31 | #if defined(ARDUINO_ARCH_MEGAAVR) 32 | virtual arduino::Print* getStream() = 0; 33 | #else 34 | virtual Print* getStream() = 0; 35 | #endif 36 | 37 | }; 38 | 39 | #endif // COMMANDSTATION_COMMINTERFACE_COMMINTERFACE_H_ -------------------------------------------------------------------------------- /src/DCC-EX-Lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CommandStation.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_COMMANDSTATION_H_ 21 | #define COMMANDSTATION_COMMANDSTATION_H_ 22 | 23 | #include "Accessories/EEStore.h" 24 | #include "CommInterface/CommManager.h" 25 | #include "CommInterface/DCCEXParser.h" 26 | #include "DCC/DCCMain.h" 27 | #include "DCC/DCCService.h" 28 | 29 | #include "CommInterface/CommInterfaceSerial.h" 30 | #if defined (ARDUINO_ARCH_SAMD) 31 | #include "CommInterface/CommInterfaceUSB.h" 32 | #endif 33 | 34 | // Motor board includes 35 | #include "Boards/BoardArduinoMotorShield.h" 36 | #include "Boards/BoardPololuMotorShield.h" 37 | 38 | #define VERSION "1.0.0" 39 | #define BOARD_NAME "DCC++ CommandStation" 40 | 41 | #endif // COMMANDSTATION_COMMANDSTATION_H_ -------------------------------------------------------------------------------- /src/CommInterface/CommInterfaceSerial.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CommInterfaceSerial.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_COMMINTERFACE_COMMINTERFACESERIAL_H_ 21 | #define COMMANDSTATION_COMMINTERFACE_COMMINTERFACESERIAL_H_ 22 | 23 | #include "CommInterface.h" 24 | 25 | #include 26 | #include 27 | 28 | class SerialInterface : public CommInterface { 29 | public: 30 | SerialInterface(HardwareSerial &serial, long baud=115200); 31 | void process(); 32 | void showConfiguration(); 33 | void showInitInfo(); 34 | Print* getStream() { return &serialStream; } 35 | protected: 36 | HardwareSerial &serialStream; 37 | long baud; 38 | String buffer; 39 | bool inCommandPayload; 40 | }; 41 | 42 | #endif // COMMANDSTATION_COMMINTERFACE_COMMINTERFACESERIAL_H_ -------------------------------------------------------------------------------- /src/CommInterface/CommInterfaceUSB.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CommInterfaceUSB.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_COMMINTERFACE_COMMINTERFACEUSB_H_ 21 | #define COMMANDSTATION_COMMINTERFACE_COMMINTERFACEUSB_H_ 22 | 23 | #include "CommInterface.h" 24 | 25 | #include 26 | 27 | class USBInterface : public CommInterface { 28 | public: 29 | USBInterface(Serial_ &serial, long baud=115200); // Baud doesn't really matter but keep it for consistency. 30 | void process(); 31 | void showConfiguration(); 32 | void showInitInfo(); 33 | void send(const char *buf); 34 | Print* getStream() { return &serialStream; } 35 | protected: 36 | Serial_ &serialStream; 37 | long baud; 38 | String buffer; 39 | bool inCommandPayload; 40 | }; 41 | 42 | #endif // COMMANDSTATION_COMMINTERFACE_COMMINTERFACEUSB_H_ -------------------------------------------------------------------------------- /src/Accessories/Outputs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_ACCESSORIES_OUTPUTS_H_ 21 | #define COMMANDSTATION_ACCESSORIES_OUTPUTS_H_ 22 | 23 | #include 24 | 25 | struct OutputData { 26 | uint8_t oStatus; 27 | uint8_t id; 28 | uint8_t pin; 29 | uint8_t iFlag; 30 | }; 31 | 32 | struct Output { 33 | static Output *firstOutput; 34 | int num; 35 | struct OutputData data; 36 | Output *nextOutput; 37 | void activate(Print* stream, int s); 38 | static void parse(Print* stream, const char *c); 39 | static Output* get(int); 40 | static void remove(Print* stream, int); 41 | static void load(Print* stream); 42 | static void store(); 43 | static Output *create(Print* stream, int, int, int, int=0); 44 | static void show(Print* stream, int=0); 45 | }; 46 | 47 | #endif // COMMANDSTATION_ACCESSORIES_OUTPUTS_H_ -------------------------------------------------------------------------------- /src/Accessories/Turnouts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Turnouts.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_ACCESSORIES_TURNOUTS_H_ 21 | #define COMMANDSTATION_ACCESSORIES_TURNOUTS_H_ 22 | 23 | #include 24 | #include "../DCC/DCCMain.h" 25 | 26 | struct TurnoutData { 27 | uint8_t tStatus; 28 | uint8_t subAddress; 29 | int id; 30 | int address; 31 | }; 32 | 33 | struct Turnout{ 34 | static Turnout *firstTurnout; 35 | int num; 36 | struct TurnoutData data; 37 | Turnout *nextTurnout; 38 | void activate(Print* stream, int s, DCCMain* track); 39 | static Turnout* get(int); 40 | static void remove(Print* stream, int); 41 | static void load(Print* stream); 42 | static void store(); 43 | static Turnout *create(Print* stream, int, int, int, int=0); 44 | static void show(Print* stream, int=0); 45 | }; 46 | 47 | #endif // COMMANDSTATION_ACCESSORIES_TURNOUTS_H_ -------------------------------------------------------------------------------- /src/Accessories/Sensors.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Sensors.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_ACCESSORIES_SENSORS_H_ 21 | #define COMMANDSTATION_ACCESSORIES_SENSORS_H_ 22 | 23 | #include "Arduino.h" 24 | 25 | #define SENSOR_DECAY 0.03 26 | 27 | struct SensorData { 28 | int snum; 29 | uint8_t pin; 30 | uint8_t pullUp; 31 | }; 32 | 33 | struct Sensor{ 34 | static Sensor *firstSensor; 35 | SensorData data; 36 | boolean active; 37 | float signal; 38 | Sensor *nextSensor; 39 | static void load(Print* stream); 40 | static void store(); 41 | static Sensor *create(Print* stream, int, int, int, int=0); 42 | static Sensor* get(int); 43 | static void remove(Print* stream, int); 44 | static void show(Print* stream); 45 | static void status(Print* stream); 46 | static void parse(const char *c); 47 | static void check(Print* stream); 48 | }; 49 | 50 | #endif // COMMANDSTATION_ACCESSORIES_SENSORS_H_ 51 | 52 | -------------------------------------------------------------------------------- /src/CommInterface/DCCEXParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCEXParser.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_COMMINTERFACE_DCCEXPARSER_H_ 21 | #define COMMANDSTATION_COMMINTERFACE_DCCEXPARSER_H_ 22 | 23 | #include "../DCC/DCCMain.h" 24 | #include "../DCC/DCCService.h" 25 | 26 | #include 27 | 28 | struct DCCEXParser 29 | { 30 | static DCCMain *mainTrack; 31 | static DCCService *progTrack; 32 | static void init(DCCMain* mainTrack_, DCCService* progTrack_); 33 | static void parse(Print* stream, const char *); 34 | static void cvResponse(Print* stream, serviceModeResponse response); 35 | static void POMResponse(Print* stream, RailcomPOMResponse response); 36 | static void trackPowerCallback(const char* name, bool status); 37 | private: 38 | static int stringParser(const char * com, int result[]); 39 | static const int MAX_PARAMS=10; 40 | static int p[MAX_PARAMS]; 41 | }; 42 | 43 | #endif // COMMANDSTATION_COMMINTERFACE_DCCEXPARSER_H_ -------------------------------------------------------------------------------- /src/Accessories/EEStore.h: -------------------------------------------------------------------------------- 1 | /* 2 | * EEStore.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_ACCESSORIES_EESTORE_H_ 21 | #define COMMANDSTATION_ACCESSORIES_EESTORE_H_ 22 | 23 | #include 24 | 25 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 26 | #include 27 | #endif 28 | 29 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 30 | extern ExternalEEPROM EEPROM; 31 | #endif 32 | 33 | #define EESTORE_ID "DCC++" 34 | 35 | struct EEStoreData{ 36 | char id[sizeof(EESTORE_ID)]; 37 | int nTurnouts; 38 | int nSensors; 39 | int nOutputs; 40 | }; 41 | 42 | struct EEStore{ 43 | static EEStore *eeStore; 44 | EEStoreData data; 45 | static int eeAddress; 46 | static void init(Print* stream); 47 | static void reset(); 48 | static int pointer(); 49 | static void advance(int); 50 | static void store(); 51 | static void clear(); 52 | }; 53 | 54 | #endif // COMMANDSTATION_ACCESSORIES_EESTORE_H_ -------------------------------------------------------------------------------- /src/CommInterface/CommInterfaceUSB.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CommInterfaceUSB.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #if defined(ARDUINO_ARCH_SAMD) 21 | 22 | #include "CommInterfaceUSB.h" 23 | 24 | #include 25 | 26 | #include "CommManager.h" 27 | #include "DCCEXParser.h" 28 | 29 | USBInterface::USBInterface(Serial_ &serial, long baud) : serialStream(serial), baud(baud), buffer(""), inCommandPayload(false) { 30 | serialStream.begin(baud); 31 | serialStream.flush(); 32 | } 33 | 34 | void USBInterface::process() { 35 | while(serialStream.available()) { 36 | char ch = serialStream.read(); 37 | if (ch == '<') { 38 | inCommandPayload = true; 39 | buffer = ""; 40 | } else if (ch == '>') { 41 | DCCEXParser::parse(&serialStream, buffer.c_str()); 42 | buffer = ""; 43 | inCommandPayload = false; 44 | } else if(inCommandPayload) { 45 | buffer += ch; 46 | } 47 | } 48 | } 49 | 50 | void USBInterface::showConfiguration() { 51 | CommManager::send(&serialStream, F("USB")); 52 | } 53 | 54 | void USBInterface::showInitInfo() { 55 | CommManager::broadcast(F("")); 56 | } 57 | 58 | #endif -------------------------------------------------------------------------------- /src/CommInterface/CommInterfaceSerial.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CommInterfaceSerial.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "CommInterfaceSerial.h" 21 | 22 | #include 23 | 24 | #include "CommManager.h" 25 | #include "DCCEXParser.h" 26 | 27 | SerialInterface::SerialInterface(HardwareSerial &serial, long baud) : serialStream(serial), baud(baud), buffer(""), inCommandPayload(false) { 28 | serialStream.begin(baud); 29 | serialStream.flush(); 30 | } 31 | 32 | void SerialInterface::process() { 33 | while(serialStream.available()) { 34 | char ch = serialStream.read(); 35 | if (ch == '<') { 36 | inCommandPayload = true; 37 | buffer = ""; 38 | } else if (ch == '>') { 39 | DCCEXParser::parse(&serialStream, buffer.c_str()); 40 | buffer = ""; 41 | inCommandPayload = false; 42 | } else if(inCommandPayload) { 43 | buffer += ch; 44 | } 45 | } 46 | } 47 | 48 | void SerialInterface::showConfiguration() { 49 | CommManager::send(&serialStream, F("Hardware Serial - Speed: %d"), baud); 50 | } 51 | 52 | void SerialInterface::showInitInfo() { 53 | CommManager::broadcast(F("")); 54 | } -------------------------------------------------------------------------------- /src/CommInterface/CommManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CommManager.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_COMMINTERFACE_COMMMANAGER_H_ 21 | #define COMMANDSTATION_COMMINTERFACE_COMMMANAGER_H_ 22 | 23 | #include "CommInterface.h" 24 | #include "../DCC/DCCMain.h" 25 | #include "../DCC/DCCService.h" 26 | 27 | #if defined(ARDUINO_ARCH_SAMD) 28 | // Some processors use a gcc compiler that renames va_list!!! 29 | #include 30 | #define DIAGSERIAL SerialUSB 31 | #elif defined(ARDUINO_ARCH_SAMC) 32 | #include 33 | #define DIAGSERIAL Serial 34 | #elif defined(ARDUINO_ARCH_AVR) 35 | #define DIAGSERIAL Serial 36 | #elif defined(ARDUINO_ARCH_MEGAAVR) 37 | #define DIAGSERIAL Serial 38 | #define __FlashStringHelper char 39 | #endif 40 | 41 | class CommManager { 42 | public: 43 | static void update(); 44 | static void registerInterface(CommInterface *interface); 45 | static void showConfiguration(); 46 | static void showInitInfo(); 47 | static void broadcast(const __FlashStringHelper* input, ...); 48 | static void print(const __FlashStringHelper* input, ...); 49 | static void send(Print* stream, const __FlashStringHelper* input, ...); 50 | static void printEscapes(Print* stream, char * input); 51 | static void printEscapes(Print* stream, const __FlashStringHelper* input); 52 | static void printEscape(Print* stream, char c); 53 | private: 54 | static void send2(Print* stream, const __FlashStringHelper* format, va_list args); 55 | static CommInterface *interfaces[5]; 56 | static int nextInterface; 57 | }; 58 | 59 | #endif // COMMANDSTATION_COMMINTERFACE_COMMMANAGER_H_ -------------------------------------------------------------------------------- /src/DCC/Waveform.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Waveform.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_DCC_WAVEFORM_H_ 21 | #define COMMANDSTATION_DCC_WAVEFORM_H_ 22 | 23 | #include 24 | 25 | #include "../Boards/Board.h" 26 | 27 | const uint8_t kIdlePacket[] = {0xFF,0x00,0xFF}; 28 | const uint8_t kResetPacket[] = {0x00,0x00,0x00}; 29 | const uint8_t kBitMask[] = {0x00,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01}; 30 | 31 | const uint8_t kPacketMaxSize = 6; 32 | 33 | enum : uint8_t { 34 | ERR_OK = 1, 35 | ERR_BUSY = 2, 36 | }; 37 | 38 | class Waveform { 39 | public: 40 | virtual bool interrupt1() = 0; 41 | virtual void interrupt2() = 0; 42 | 43 | void loop() { 44 | board->checkOverload(); 45 | } 46 | 47 | Board* board; 48 | protected: 49 | // Data that controls the packet currently being sent out. 50 | uint8_t bits_sent; // Bits sent from byte 51 | uint8_t bytes_sent; // Bytes sent from packet 52 | uint8_t currentBit = false; 53 | uint8_t transmitRepeats = 0; // Repeats (does not include initial transmit) 54 | uint8_t remainingPreambles = 0; 55 | uint8_t generateStartBit = false; // Send a start bit for the current byte? 56 | uint8_t transmitPacket[kPacketMaxSize]; 57 | uint8_t transmitLength; 58 | uint16_t transmitID = 0; 59 | 60 | // Interrupt segments, called in interrupt_handler 61 | 62 | uint8_t interruptState = 0; // Waveform generator state 63 | 64 | uint16_t counterID = 1; // Maintains the last assigned packet ID 65 | bool counterWrap = false; 66 | inline void incrementCounterID() { 67 | counterID++; 68 | if(counterID == 0) { 69 | counterID = 1; 70 | counterWrap = true; 71 | } 72 | } 73 | }; 74 | 75 | #endif -------------------------------------------------------------------------------- /src/DCC/Queue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Queue.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_DCC_QUEUE_H_ 21 | #define COMMANDSTATION_DCC_QUEUE_H_ 22 | 23 | #include 24 | 25 | template 26 | class Queue { 27 | private: 28 | int _front, _back, _count; 29 | T _data[S+1]; 30 | int _maxitems; 31 | public: 32 | Queue() { 33 | _front = 0; 34 | _back = 0; 35 | _count = 0; 36 | _maxitems = S; 37 | } 38 | ~Queue() { 39 | // delete[] _data; 40 | } 41 | inline int count(); 42 | inline int front(); 43 | inline int back(); 44 | void push(const T &item); 45 | T peek(); 46 | T pop(); 47 | void clear(); 48 | }; 49 | 50 | template 51 | inline int Queue::count() 52 | { 53 | return _count; 54 | } 55 | 56 | template 57 | inline int Queue::front() 58 | { 59 | return _front; 60 | } 61 | 62 | template 63 | inline int Queue::back() 64 | { 65 | return _back; 66 | } 67 | 68 | template 69 | void Queue::push(const T &item) 70 | { 71 | noInterrupts(); 72 | if(_count < _maxitems) { // Drops out when full 73 | _data[_back++]=item; 74 | ++_count; 75 | // Check wrap around 76 | if (_back > _maxitems) 77 | _back -= (_maxitems + 1); 78 | } 79 | interrupts(); 80 | } 81 | 82 | template 83 | T Queue::pop() { 84 | if(_count <= 0) return T(); // Returns empty 85 | else { 86 | T result = _data[_front]; 87 | _front++; 88 | --_count; 89 | // Check wrap around 90 | if (_front > _maxitems) 91 | _front -= (_maxitems + 1); 92 | return result; 93 | } 94 | } 95 | 96 | template 97 | T Queue::peek() { 98 | if(_count <= 0) return T(); // Returns empty 99 | else return _data[_front]; 100 | } 101 | 102 | template 103 | void Queue::clear() 104 | { 105 | _front = _back; 106 | _count = 0; 107 | } 108 | 109 | #endif // COMMANDSTATION_DCC_QUEUE_H_ 110 | -------------------------------------------------------------------------------- /src/Boards/BoardPololuMotorShield.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BoardPololuMotorShield.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_BOARDS_BOARDPOLOLUMOTORSHIELD_H_ 21 | #define COMMANDSTATION_BOARDS_BOARDPOLOLUMOTORSHIELD_H_ 22 | 23 | #include "Board.h" 24 | 25 | struct BoardConfigPololuMotorShield : public BoardConfig { 26 | // Used to store functionality special to this type of shield 27 | }; 28 | 29 | class BoardPololuMotorShield : public Board 30 | { 31 | public: 32 | BoardConfigPololuMotorShield config; 33 | 34 | BoardPololuMotorShield(BoardConfigPololuMotorShield _config) { 35 | config = _config; 36 | } 37 | 38 | static void getDefaultConfigA(BoardConfigPololuMotorShield& _config) { 39 | _config.track_name = "A"; 40 | _config.signal_a_pin = 7; 41 | _config.signal_b_pin = 9; 42 | _config.enable_pin = 4; 43 | _config.sense_pin = A0; 44 | _config.board_voltage = 5.0; 45 | _config.amps_per_volt = 1.904762; 46 | _config.current_trip = 3000; 47 | _config.current_trip_prog = 250; 48 | _config.prog_trip_time = 100; 49 | _config.main_preambles = 16; 50 | _config.prog_preambles = 22; 51 | _config.track_power_callback = nullptr; // Needs to be set in the main file 52 | } 53 | 54 | static void getDefaultConfigB(BoardConfigPololuMotorShield& _config) { 55 | _config.track_name = "B"; 56 | _config.signal_a_pin = 8; 57 | _config.signal_b_pin = 10; 58 | _config.enable_pin = 4; 59 | _config.sense_pin = A1; 60 | _config.board_voltage = 5.0; 61 | _config.amps_per_volt = 1.904762; 62 | _config.current_trip = 3000; 63 | _config.current_trip_prog = 250; 64 | _config.prog_trip_time = 100; 65 | _config.main_preambles = 16; 66 | _config.prog_preambles = 22; 67 | _config.track_power_callback = nullptr; // Needs to be set in the main file 68 | } 69 | 70 | void setup(); 71 | 72 | const char* getName(); 73 | 74 | void power(bool, bool announce); 75 | void signal(bool); 76 | void cutout(bool); 77 | void progMode(bool); 78 | 79 | uint16_t getCurrentRaw(); 80 | uint16_t getCurrentMilliamps(); 81 | uint16_t getCurrentMilliamps(uint16_t reading); 82 | 83 | uint16_t setCurrentBase(); 84 | uint16_t getCurrentBase(); 85 | 86 | bool getStatus(); 87 | 88 | void checkOverload(); 89 | 90 | uint8_t getPreambles(); 91 | 92 | private: 93 | bool isCurrentLimiting(); 94 | }; 95 | 96 | 97 | #endif -------------------------------------------------------------------------------- /src/Accessories/EEStore.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * EEStore.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "EEStore.h" 21 | 22 | #include "Outputs.h" 23 | #include "Sensors.h" 24 | #include "Turnouts.h" 25 | 26 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 27 | #include 28 | #else 29 | #include 30 | #endif 31 | 32 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 33 | ExternalEEPROM EEPROM; 34 | #endif 35 | 36 | void EEStore::init(Print* stream){ 37 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 38 | // Address for Microchip 24-series EEPROM with all three address pins grounded 39 | // (0b1010000 = 0x50) 40 | EEPROM.begin(0x50); 41 | #endif 42 | 43 | eeStore=(EEStore *)calloc(1,sizeof(EEStore)); 44 | 45 | EEPROM.get(0,eeStore->data); 46 | 47 | // check to see that eeStore contains valid DCC++ ID 48 | if(strncmp(eeStore->data.id,EESTORE_ID,sizeof(EESTORE_ID))!=0){ 49 | // if not, create blank eeStore structure (no turnouts, no sensors) and save 50 | // it back to EEPROM 51 | 52 | // TODO(davidcutting42@gmail.com): replace with EEStore::clear(); 53 | sprintf(eeStore->data.id,EESTORE_ID); 54 | eeStore->data.nTurnouts=0; 55 | eeStore->data.nSensors=0; 56 | eeStore->data.nOutputs=0; 57 | EEPROM.put(0,eeStore->data); 58 | } 59 | 60 | reset(); // set memory pointer to first free EEPROM space 61 | Turnout::load(stream); // load turnout definitions 62 | Sensor::load(stream); // load sensor definitions 63 | Output::load(stream); // load output definitions 64 | } 65 | 66 | void EEStore::clear(){ 67 | // create blank eeStore structure (no turnouts, no sensors) and save it back 68 | // to EEPROM 69 | sprintf(eeStore->data.id,EESTORE_ID); 70 | eeStore->data.nTurnouts=0; 71 | eeStore->data.nSensors=0; 72 | eeStore->data.nOutputs=0; 73 | EEPROM.put(0,eeStore->data); 74 | } 75 | 76 | void EEStore::store(){ 77 | reset(); 78 | Turnout::store(); 79 | Sensor::store(); 80 | Output::store(); 81 | EEPROM.put(0,eeStore->data); 82 | } 83 | 84 | void EEStore::advance(int n){ 85 | eeAddress+=n; 86 | } 87 | 88 | void EEStore::reset(){ 89 | eeAddress=sizeof(EEStore); 90 | } 91 | 92 | int EEStore::pointer(){ 93 | return(eeAddress); 94 | } 95 | 96 | EEStore *EEStore::eeStore=NULL; 97 | int EEStore::eeAddress=0; -------------------------------------------------------------------------------- /src/Boards/BoardArduinoMotorShield.h: -------------------------------------------------------------------------------- 1 | /* 2 | * BoardArduinoMotorShield.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_BOARDS_BOARDARDUINOMOTORSHIELD_H_ 21 | #define COMMANDSTATION_BOARDS_BOARDARDUINOMOTORSHIELD_H_ 22 | 23 | #include "Board.h" 24 | 25 | struct BoardConfigArduinoMotorShield : public BoardConfig { 26 | // Used to store functionality special to this type of shield 27 | }; 28 | 29 | class BoardArduinoMotorShield : public Board 30 | { 31 | public: 32 | BoardConfigArduinoMotorShield config; 33 | 34 | BoardArduinoMotorShield(BoardConfigArduinoMotorShield _config) { 35 | config = _config; 36 | } 37 | 38 | static void getDefaultConfigA(BoardConfigArduinoMotorShield& _config) { 39 | _config.track_name = "A"; 40 | _config.signal_a_pin = 12; 41 | _config.signal_b_pin = 9; 42 | _config.enable_pin = 3; 43 | _config.sense_pin = A0; 44 | _config.board_voltage = 5.0; 45 | _config.amps_per_volt = 0.606061; 46 | _config.current_trip = 1500; 47 | _config.current_trip_prog = 250; 48 | _config.prog_trip_time = 100; 49 | _config.main_preambles = 16; 50 | _config.prog_preambles = 22; 51 | _config.track_power_callback = nullptr; // Needs to be set in the main file 52 | } 53 | 54 | static void getDefaultConfigB(BoardConfigArduinoMotorShield& _config) { 55 | _config.track_name = "B"; 56 | _config.signal_a_pin = 13; 57 | _config.signal_b_pin = 8; 58 | _config.enable_pin = 11; 59 | _config.sense_pin = A1; 60 | _config.board_voltage = 5.0; 61 | _config.amps_per_volt = 0.606061; 62 | _config.current_trip = 1500; 63 | _config.current_trip_prog = 250; 64 | _config.prog_trip_time = 100; 65 | _config.main_preambles = 16; 66 | _config.prog_preambles = 22; 67 | _config.track_power_callback = nullptr; // Needs to be set in the main file 68 | } 69 | 70 | void setup(); 71 | 72 | const char* getName(); 73 | 74 | void power(bool, bool announce); 75 | void signal(bool); 76 | void cutout(bool); 77 | void progMode(bool); 78 | 79 | uint16_t getCurrentRaw(); 80 | uint16_t getCurrentMilliamps(); 81 | uint16_t getCurrentMilliamps(uint16_t reading); 82 | 83 | uint16_t setCurrentBase(); 84 | uint16_t getCurrentBase(); 85 | 86 | bool getStatus(); 87 | 88 | void checkOverload(); 89 | 90 | uint8_t getPreambles(); 91 | 92 | private: 93 | bool isCurrentLimiting(); 94 | }; 95 | 96 | 97 | #endif -------------------------------------------------------------------------------- /src/Boards/Board.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Board.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_BOARDS_BOARD_H_ 21 | #define COMMANDSTATION_BOARDS_BOARD_H_ 22 | 23 | #include 24 | #include "AnalogReadFast.h" 25 | 26 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 27 | #define writePin digitalWrite 28 | #elif defined(ARDUINO_ARCH_AVR) 29 | // Library DIO2.h is only compatible with AVR. 30 | #include 31 | #define writePin digitalWrite2 32 | #endif 33 | 34 | #define ON true 35 | #define OFF false 36 | 37 | // Time between current samples (millis) 38 | const int kCurrentSampleTime = 1; 39 | 40 | // Smoothing constant for exponential current smoothing algorithm 41 | const float kCurrentSampleSmoothing = 0.01; 42 | 43 | // Number of milliseconds between retries when the "breaker" is tripped. 44 | const int kRetryTime = 10000; 45 | 46 | struct BoardConfig 47 | { 48 | const char* track_name; 49 | uint8_t signal_a_pin; 50 | uint8_t signal_b_pin; 51 | uint8_t enable_pin; 52 | uint8_t sense_pin; 53 | float board_voltage; 54 | float amps_per_volt; 55 | uint16_t current_trip; 56 | uint16_t current_trip_prog; 57 | uint16_t prog_trip_time; 58 | uint8_t main_preambles; 59 | uint8_t prog_preambles; 60 | void (*track_power_callback)(const char* name, bool status); 61 | }; 62 | 63 | 64 | class Board 65 | { 66 | public: 67 | virtual const char* getName() = 0; 68 | 69 | virtual void power(bool, bool announce) = 0; 70 | virtual void signal(bool) = 0; 71 | // True to enter a railcom cutout, false to recover 72 | virtual void cutout(bool) = 0; 73 | // True to enter prog mode and limit current 74 | virtual void progMode(bool) = 0; 75 | 76 | // Returns current reading 0-1024 77 | virtual uint16_t getCurrentRaw() = 0; 78 | // Returns current reading in mA 79 | virtual uint16_t getCurrentMilliamps() = 0; 80 | virtual uint16_t getCurrentMilliamps(uint16_t reading) = 0; 81 | 82 | virtual uint16_t setCurrentBase() = 0; 83 | virtual uint16_t getCurrentBase() = 0; 84 | 85 | virtual bool getStatus() = 0; 86 | 87 | virtual void checkOverload() = 0; 88 | 89 | virtual uint8_t getPreambles() = 0; 90 | protected: 91 | // Current reading variables 92 | uint16_t reading; 93 | bool tripped; 94 | long int lastCheckTime; 95 | long int lastTripTime; 96 | 97 | // Programming mode 98 | bool inProgMode; 99 | uint16_t progOverloadTimer; 100 | uint16_t currentBase; 101 | 102 | virtual bool isCurrentLimiting() = 0; 103 | }; 104 | 105 | #endif // COMMANDSTATION_BOARDS_BOARD_H_ -------------------------------------------------------------------------------- /src/Boards/BoardPololuMotorShield.cpp: -------------------------------------------------------------------------------- 1 | #include "BoardPololuMotorShield.h" 2 | 3 | void BoardPololuMotorShield::setup() { 4 | pinMode(config.enable_pin, OUTPUT); 5 | writePin(config.enable_pin, LOW); 6 | 7 | pinMode(config.signal_a_pin, OUTPUT); 8 | writePin(config.signal_a_pin, LOW); 9 | 10 | pinMode(config.signal_b_pin, OUTPUT); 11 | writePin(config.signal_b_pin, HIGH); 12 | 13 | pinMode(config.sense_pin, INPUT); 14 | 15 | tripped = false; 16 | } 17 | 18 | const char * BoardPololuMotorShield::getName() { 19 | return config.track_name; 20 | } 21 | 22 | void BoardPololuMotorShield::progMode(bool on) { 23 | inProgMode = on; // Not equipped with active current limiting, so just enable the programming mode variable 24 | } 25 | 26 | void BoardPololuMotorShield::power(bool on, bool announce) { 27 | if(inProgMode) { 28 | progOverloadTimer = millis(); 29 | } 30 | 31 | writePin(config.enable_pin, on); 32 | 33 | if(announce) { 34 | config.track_power_callback(config.track_name, on); 35 | } 36 | } 37 | 38 | void BoardPololuMotorShield::signal(bool dir) { 39 | writePin(config.signal_a_pin, dir); 40 | } 41 | 42 | void BoardPololuMotorShield::cutout(bool on) { 43 | writePin(config.signal_b_pin, !on); 44 | } 45 | 46 | uint16_t BoardPololuMotorShield::getCurrentRaw() { 47 | return analogReadFast(config.sense_pin); 48 | } 49 | 50 | uint16_t BoardPololuMotorShield::getCurrentMilliamps() { 51 | uint16_t currentMilliamps; 52 | currentMilliamps = getCurrentMilliamps(getCurrentRaw()); 53 | return currentMilliamps; 54 | } 55 | 56 | uint16_t BoardPololuMotorShield::getCurrentMilliamps(uint16_t reading) { 57 | uint16_t currentMilliamps; 58 | currentMilliamps = reading / 1023.0 * config.board_voltage * 1000 * config.amps_per_volt; 59 | return currentMilliamps; 60 | } 61 | 62 | bool BoardPololuMotorShield::getStatus() { 63 | return digitalRead(config.enable_pin); 64 | } 65 | 66 | void BoardPololuMotorShield::checkOverload() { 67 | if(millis() - progOverloadTimer > config.prog_trip_time) config.prog_trip_time = 0; // Protect against wrapping 68 | 69 | if(millis() - lastCheckTime > kCurrentSampleTime) { 70 | lastCheckTime = millis(); 71 | reading = getCurrentRaw() * kCurrentSampleSmoothing + reading * (1.0 - kCurrentSampleSmoothing); 72 | uint16_t current = getCurrentMilliamps(reading); 73 | 74 | uint16_t current_trip = config.current_trip; 75 | if(isCurrentLimiting()) current_trip = 250; 76 | 77 | if(current > current_trip && getStatus()) { 78 | power(OFF, true); // TODO: Add track power notice callback 79 | tripped=true; 80 | lastTripTime=millis(); 81 | } 82 | else if(current < current_trip && tripped) { 83 | if (millis() - lastTripTime > kRetryTime) { 84 | // TODO: Add track power notice callback 85 | power(ON, true); 86 | tripped=false; 87 | } 88 | } 89 | } 90 | } 91 | 92 | bool BoardPololuMotorShield::isCurrentLimiting() { 93 | // If we're in programming mode and it's been less than prog_trip_time since we turned the power on... or if the timeout is set to zero. 94 | if(inProgMode && ((millis() - progOverloadTimer < config.prog_trip_time) || config.prog_trip_time == 0)) 95 | return true; 96 | 97 | return false; 98 | } 99 | 100 | uint16_t BoardPololuMotorShield::setCurrentBase() { 101 | currentBase = getCurrentMilliamps(); 102 | return currentBase; 103 | } 104 | 105 | uint16_t BoardPololuMotorShield::getCurrentBase() { 106 | return currentBase; 107 | } 108 | 109 | uint8_t BoardPololuMotorShield::getPreambles() { 110 | if(inProgMode) return config.prog_preambles; 111 | return config.main_preambles; 112 | } 113 | 114 | -------------------------------------------------------------------------------- /src/Boards/BoardArduinoMotorShield.cpp: -------------------------------------------------------------------------------- 1 | #include "BoardArduinoMotorShield.h" 2 | 3 | void BoardArduinoMotorShield::setup() { 4 | pinMode(config.enable_pin, OUTPUT); 5 | writePin(config.enable_pin, LOW); 6 | 7 | pinMode(config.signal_a_pin, OUTPUT); 8 | writePin(config.signal_a_pin, LOW); 9 | 10 | pinMode(config.signal_b_pin, OUTPUT); 11 | writePin(config.signal_b_pin, LOW); 12 | 13 | pinMode(config.sense_pin, INPUT); 14 | 15 | tripped = false; 16 | } 17 | 18 | const char * BoardArduinoMotorShield::getName() { 19 | return config.track_name; 20 | } 21 | 22 | void BoardArduinoMotorShield::progMode(bool on) { 23 | inProgMode = on; // Not equipped with active current limiting, so just enable the programming mode variable 24 | } 25 | 26 | void BoardArduinoMotorShield::power(bool on, bool announce) { 27 | if(inProgMode) { 28 | progOverloadTimer = millis(); 29 | } 30 | 31 | writePin(config.enable_pin, on); 32 | 33 | if(announce) { 34 | config.track_power_callback(config.track_name, on); 35 | } 36 | } 37 | 38 | void BoardArduinoMotorShield::signal(bool dir) { 39 | writePin(config.signal_a_pin, dir); 40 | } 41 | 42 | void BoardArduinoMotorShield::cutout(bool on) { 43 | writePin(config.signal_b_pin, on); 44 | } 45 | 46 | uint16_t BoardArduinoMotorShield::getCurrentRaw() { 47 | return analogReadFast(config.sense_pin); 48 | } 49 | 50 | uint16_t BoardArduinoMotorShield::getCurrentMilliamps() { 51 | uint16_t currentMilliamps; 52 | currentMilliamps = getCurrentMilliamps(getCurrentRaw()); 53 | return currentMilliamps; 54 | } 55 | 56 | uint16_t BoardArduinoMotorShield::getCurrentMilliamps(uint16_t reading) { 57 | uint16_t currentMilliamps; 58 | currentMilliamps = reading / 1023.0 * config.board_voltage * 1000 * config.amps_per_volt; 59 | return currentMilliamps; 60 | } 61 | 62 | bool BoardArduinoMotorShield::getStatus() { 63 | return digitalRead(config.enable_pin); 64 | } 65 | 66 | void BoardArduinoMotorShield::checkOverload() { 67 | if(millis() - progOverloadTimer > config.prog_trip_time) config.prog_trip_time = 0; // Protect against wrapping 68 | 69 | if(millis() - lastCheckTime > kCurrentSampleTime) { 70 | lastCheckTime = millis(); 71 | reading = getCurrentRaw() * kCurrentSampleSmoothing + reading * (1.0 - kCurrentSampleSmoothing); 72 | uint16_t current = getCurrentMilliamps(reading); 73 | 74 | uint16_t current_trip = config.current_trip; 75 | if(isCurrentLimiting()) current_trip = 250; 76 | 77 | if(current > current_trip && getStatus()) { 78 | power(OFF, true); // TODO: Add track power notice callback 79 | tripped=true; 80 | lastTripTime=millis(); 81 | } 82 | else if(current < current_trip && tripped) { 83 | if (millis() - lastTripTime > kRetryTime) { 84 | // TODO: Add track power notice callback 85 | power(ON, true); 86 | tripped=false; 87 | } 88 | } 89 | } 90 | } 91 | 92 | bool BoardArduinoMotorShield::isCurrentLimiting() { 93 | // If we're in programming mode and it's been less than prog_trip_time since we turned the power on... or if the timeout is set to zero. 94 | if(inProgMode && ((millis() - progOverloadTimer < config.prog_trip_time) || config.prog_trip_time == 0)) 95 | return true; 96 | 97 | return false; 98 | } 99 | 100 | uint16_t BoardArduinoMotorShield::setCurrentBase() { 101 | currentBase = getCurrentMilliamps(); 102 | return currentBase; 103 | } 104 | 105 | uint16_t BoardArduinoMotorShield::getCurrentBase() { 106 | return currentBase; 107 | } 108 | 109 | uint8_t BoardArduinoMotorShield::getPreambles() { 110 | if(inProgMode) return config.prog_preambles; 111 | return config.main_preambles; 112 | } 113 | 114 | -------------------------------------------------------------------------------- /src/DCC/DCCServiceTimers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCServiceTimers.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "DCCService.h" 21 | 22 | bool DCCService::interrupt1() { 23 | switch (interruptState) { 24 | case 0: // start of bit transmission 25 | board->signal(HIGH); 26 | interruptState = 1; 27 | return true; // must call interrupt2 to set currentBit 28 | // Case 1 falls to default case 29 | case 2: // 58us after case 0 30 | if(currentBit) { 31 | board->signal(LOW); 32 | } 33 | interruptState = 3; 34 | break; 35 | case 3: // 87us after case 0 36 | if(currentBit) { 37 | // Return to case zero for start of another bit at 116us 38 | interruptState = 0; 39 | } 40 | else interruptState = 4; 41 | break; 42 | case 4: // 116us after case 0 43 | board->signal(LOW); 44 | interruptState = 5; 45 | break; 46 | // Case 5 and 6 fall to default case 47 | case 7: // 203us after case 0 48 | interruptState = 0; 49 | break; 50 | default: 51 | interruptState++; 52 | break; 53 | } 54 | 55 | return false; // Don't call interrupt2 56 | } 57 | 58 | void DCCService::interrupt2() { 59 | if (remainingPreambles > 0 ) { // If there's more preambles to be sent 60 | currentBit=true; // Send a one bit (preambles are one) 61 | remainingPreambles--; // decrement the number of preambles to send 62 | return; 63 | } 64 | 65 | // beware OF 9-BIT MASK generating a zero to start each byte 66 | currentBit=transmitPacket[bytes_sent] & kBitMask[bits_sent]; 67 | bits_sent++; 68 | 69 | // If this is the last bit of a byte, prepare for the next byte 70 | if (bits_sent==9) { // zero followed by 8 bits of a byte 71 | //end of Byte 72 | bits_sent=0; 73 | bytes_sent++; 74 | // if this is the last byte, prepare for next packet 75 | if (bytes_sent >= transmitLength) { 76 | // end of transmission buffer... repeat or switch to next message 77 | bytes_sent = 0; 78 | remainingPreambles = board->getPreambles() + 1; // Add one for the stop bit 79 | 80 | int pendingCount = packetQueue.count(); 81 | 82 | // Note that the number of repeats does not include the final repeat, so 83 | // the number of times transmitted is nRepeats+1 84 | if (transmitRepeats > 0) { 85 | transmitRepeats--; 86 | } 87 | else if (pendingCount > 0) { 88 | // Copy pending packet to transmit packet 89 | Packet pendingPacket = packetQueue.pop(); 90 | 91 | // Load info about the packet into the transmit variables. 92 | // TODO(davidcutting42@gmail.com): check if this can be done with a 93 | // peek() into packetQueue instead. 94 | for (int b=0;b. 20 | */ 21 | 22 | #ifndef COMMANDSTATION_DCC_ANALOGREADFAST_H_ 23 | #define COMMANDSTATION_DCC_ANALOGREADFAST_H_ 24 | 25 | #include 26 | 27 | int inline analogReadFast(uint8_t ADCpin); 28 | 29 | #if defined(ARDUINO_ARCH_SAMD) 30 | int inline analogReadFast(uint8_t ADCpin) 31 | { ADC->CTRLA.bit.ENABLE = 0; // disable ADC 32 | while( ADC->STATUS.bit.SYNCBUSY == 1 ); // wait for synchronization 33 | 34 | int CTRLBoriginal = ADC->CTRLB.reg; 35 | int AVGCTRLoriginal = ADC->AVGCTRL.reg; 36 | int SAMPCTRLoriginal = ADC->SAMPCTRL.reg; 37 | 38 | ADC->CTRLB.reg &= 0b1111100011111111; // mask PRESCALER bits 39 | ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64; // divide Clock by 64 40 | ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample 41 | ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0 42 | ADC->SAMPCTRL.reg = 0x00; // sampling Time Length = 0 43 | 44 | ADC->CTRLA.bit.ENABLE = 1; // enable ADC 45 | while(ADC->STATUS.bit.SYNCBUSY == 1); // wait for synchronization 46 | 47 | int adc = analogRead(ADCpin); 48 | 49 | ADC->CTRLB.reg = CTRLBoriginal; 50 | ADC->AVGCTRL.reg = AVGCTRLoriginal; 51 | ADC->SAMPCTRL.reg = SAMPCTRLoriginal; 52 | 53 | return adc; 54 | } 55 | 56 | #elif defined(ARDUINO_ARCH_SAMC) 57 | 58 | int inline analogReadFast(uint8_t ADCpin) 59 | { 60 | Adc* ADC; 61 | if ( (g_APinDescription[ADCpin].ulPeripheralAttribute & PER_ATTR_ADC_MASK) == PER_ATTR_ADC_STD ) { 62 | ADC = ADC0; 63 | } else { 64 | ADC = ADC1; 65 | } 66 | 67 | ADC->CTRLA.bit.ENABLE = 0; // disable ADC 68 | while( ADC->SYNCBUSY.bit.ENABLE == 1 ); // wait for synchronization 69 | 70 | int CTRLBoriginal = ADC->CTRLB.reg; 71 | int AVGCTRLoriginal = ADC->AVGCTRL.reg; 72 | int SAMPCTRLoriginal = ADC->SAMPCTRL.reg; 73 | 74 | ADC->CTRLB.reg &= 0b1111100011111111; // mask PRESCALER bits 75 | ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64; // divide Clock by 64 76 | ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample 77 | ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0 78 | ADC->SAMPCTRL.reg = 0x00; // sampling Time Length = 0 79 | 80 | ADC->CTRLA.bit.ENABLE = 1; // enable ADC 81 | while(ADC->SYNCBUSY.bit.ENABLE == 1); // wait for synchronization 82 | 83 | int adc = analogRead(ADCpin); 84 | 85 | ADC->CTRLB.reg = CTRLBoriginal; 86 | ADC->AVGCTRL.reg = AVGCTRLoriginal; 87 | ADC->SAMPCTRL.reg = SAMPCTRLoriginal; 88 | 89 | return adc; 90 | } 91 | 92 | #elif defined(ARDUINO_AVR_UNO_WIFI_REV2) || defined(ARDUINO_AVR_NANO_EVERY) 93 | 94 | int inline analogReadFast(uint8_t ADCpin) 95 | { byte ADC0CTRLCoriginal = ADC0.CTRLC; 96 | ADC0.CTRLC = (ADC0CTRLCoriginal & 0b00110000) + 0b01000011; 97 | int adc = analogRead(ADCpin); 98 | ADC0.CTRLC = ADC0CTRLCoriginal; 99 | return adc; 100 | } 101 | 102 | #else 103 | 104 | int inline analogReadFast(uint8_t ADCpin) 105 | { byte ADCSRAoriginal = ADCSRA; 106 | ADCSRA = (ADCSRA & B11111000) | 4; 107 | int adc = analogRead(ADCpin); 108 | ADCSRA = ADCSRAoriginal; 109 | return adc; 110 | } 111 | #endif 112 | #endif // COMMANDSTATION_DCC_ANALOGREADFAST_H_ -------------------------------------------------------------------------------- /src/Accessories/Turnouts.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Turnouts.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "Turnouts.h" 21 | 22 | #include "../CommInterface/CommManager.h" 23 | #include "EEStore.h" 24 | 25 | #if !defined(ARDUINO_ARCH_SAMD) && !defined(ARDUINO_ARCH_SAMC) 26 | #include 27 | #endif 28 | 29 | void Turnout::activate(Print* stream, int s, DCCMain* track){ 30 | // if s>0 set turnout=ON, else if zero or negative set turnout=OFF 31 | data.tStatus=(s>0); 32 | genericResponse response; 33 | track->setAccessory(data.address, data.subAddress, data.tStatus, response); 34 | if(num>0) 35 | EEPROM.put(num,data.tStatus); 36 | CommManager::send(stream, F(""), data.id, data.tStatus); 37 | } 38 | 39 | Turnout* Turnout::get(int n){ 40 | Turnout *tt; 41 | for(tt=firstTurnout;tt!=NULL && tt->data.id!=n;tt=tt->nextTurnout); 42 | return(tt); 43 | } 44 | 45 | void Turnout::remove(Print* stream, int n){ 46 | Turnout *tt,*pp; 47 | tt=firstTurnout; 48 | pp=tt; 49 | 50 | for( ;tt!=NULL && tt->data.id!=n;pp=tt,tt=tt->nextTurnout); 51 | 52 | if(tt==NULL){ 53 | CommManager::send(stream, F("")); 54 | return; 55 | } 56 | 57 | if(tt==firstTurnout) 58 | firstTurnout=tt->nextTurnout; 59 | else 60 | pp->nextTurnout=tt->nextTurnout; 61 | 62 | free(tt); 63 | 64 | CommManager::send(stream, F("")); 65 | } 66 | 67 | void Turnout::show(Print* stream, int n){ 68 | Turnout *tt; 69 | 70 | if(firstTurnout==NULL){ 71 | CommManager::send(stream, F("")); 72 | return; 73 | } 74 | 75 | for(tt=firstTurnout;tt!=NULL;tt=tt->nextTurnout){ 76 | if(n==1) { 77 | CommManager::send(stream, F(""), tt->data.id, tt->data.address, 78 | tt->data.subAddress, tt->data.tStatus); 79 | } 80 | else { 81 | CommManager::send(stream, F(""), tt->data.id, tt->data.tStatus); 82 | } 83 | } 84 | } 85 | 86 | void Turnout::load(Print* stream){ 87 | struct TurnoutData data; 88 | Turnout *tt; 89 | 90 | for(int i=0;idata.nTurnouts;i++){ 91 | EEPROM.get(EEStore::pointer(),data); 92 | tt=create(stream, data.id, data.address, data.subAddress); 93 | tt->data.tStatus=data.tStatus; 94 | tt->num=EEStore::pointer(); 95 | EEStore::advance(sizeof(tt->data)); 96 | } 97 | } 98 | 99 | void Turnout::store(){ 100 | Turnout *tt; 101 | 102 | tt=firstTurnout; 103 | EEStore::eeStore->data.nTurnouts=0; 104 | 105 | while(tt!=NULL){ 106 | tt->num=EEStore::pointer(); 107 | EEPROM.put(EEStore::pointer(),tt->data); 108 | EEStore::advance(sizeof(tt->data)); 109 | tt=tt->nextTurnout; 110 | EEStore::eeStore->data.nTurnouts++; 111 | } 112 | 113 | } 114 | 115 | Turnout *Turnout::create(Print* stream, int id, int add, int subAdd, int v){ 116 | Turnout *tt; 117 | 118 | if(firstTurnout==NULL){ 119 | firstTurnout=(Turnout *)calloc(1,sizeof(Turnout)); 120 | tt=firstTurnout; 121 | } else if((tt=get(id))==NULL){ 122 | tt=firstTurnout; 123 | while(tt->nextTurnout!=NULL) 124 | tt=tt->nextTurnout; 125 | tt->nextTurnout=(Turnout *)calloc(1,sizeof(Turnout)); 126 | tt=tt->nextTurnout; 127 | } 128 | 129 | if(tt==NULL){ // problem allocating memory 130 | if(v==1) 131 | CommManager::send(stream, F("")); 132 | return(tt); 133 | } 134 | 135 | tt->data.id=id; 136 | tt->data.address=add; 137 | tt->data.subAddress=subAdd; 138 | tt->data.tStatus=0; 139 | if(v==1) 140 | CommManager::send(stream, F("")); 141 | return(tt); 142 | } 143 | 144 | Turnout *Turnout::firstTurnout=NULL; -------------------------------------------------------------------------------- /src/DCC/Railcom.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Railcom.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_DCC_RAILCOM_H_ 21 | #define COMMANDSTATION_DCC_RAILCOM_H_ 22 | 23 | #include 24 | 25 | #include "Queue.h" 26 | 27 | #if defined(ARDUINO_ARCH_AVR) 28 | #include 29 | #endif 30 | 31 | extern const uint8_t railcom_decode[256]; 32 | 33 | // invalid value (not conforming to the 4bit weighting requirement) 34 | const uint8_t INV = 0xFF; 35 | // Railcom ACK; the decoder received the message ok. NOTE: some early 36 | // software versions may have ACK and NACK exchanged. 37 | const uint8_t ACK = 0xFE; 38 | // The decoder rejected the packet. 39 | const uint8_t NACK = 0xFD; 40 | // The decoder is busy; send the packet again. This is typically returned 41 | // when a POM CV write is still pending; the caller must re-try sending the 42 | // packet later. 43 | const uint8_t BUSY = 0xFC; 44 | // Reserved for future expansion. 45 | const uint8_t RESVD1 = 0xFB; 46 | const uint8_t RESVD2 = 0xFA; 47 | const uint8_t RESVD3 = 0xF8; 48 | 49 | enum RailcomInstructionType : uint8_t { 50 | kMOBInstruction, 51 | kSTATInstruction, 52 | kNoInstruction 53 | }; 54 | 55 | enum RailcomMOBID : uint8_t { 56 | kMOB_POM = 0, 57 | kMOB_ADR_HIGH = 1, 58 | kMOB_ADR_LOW = 2, 59 | kMOB_EXT = 3, 60 | kMOB_DYN = 7, 61 | kMOB_SUBID = 12 62 | }; 63 | 64 | enum RailcomSTATID : uint8_t { 65 | kSTAT_POM = 0, 66 | kSTAT_STAT1 = 4, 67 | kSTAT_TIME = 5, 68 | kSTAT_ERROR = 6, 69 | kSTAT_DYN = 7, 70 | kSTAT_STAT2 = 8, 71 | kSTAT_SUBID = 12 72 | }; 73 | 74 | enum PacketType : uint8_t { 75 | kResetType, 76 | kIdleType, 77 | kThrottleType, 78 | kFunctionType, 79 | kAccessoryType, 80 | kPOMByteWriteType, // Railcom is same as standard command for write byte 81 | kPOMBitWriteType, // Railcom is same as standard command for write bit 82 | kPOMReadType, 83 | kPOMLongReadType, 84 | kSrvcByteWriteType, 85 | kSrvcBitWriteType, 86 | kSrvcReadType 87 | }; 88 | 89 | struct RailcomDatagram { 90 | uint8_t identifier; // 4-bit ID, LSB justified 91 | uint8_t channel; // Railcom channel the data came in on, either 1 or 2 92 | uint32_t payload; // LSB justified payload of the datagram, excluding the ID 93 | }; 94 | 95 | struct RailcomPOMResponse { 96 | uint32_t data; 97 | uint16_t transactionID; 98 | }; 99 | 100 | struct RailComConfig { 101 | bool enable; 102 | long int baud; 103 | 104 | uint8_t rx_pin; 105 | uint8_t tx_pin; 106 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 107 | Uart* serial; 108 | SERCOM* sercom; 109 | EPioType rx_mux; 110 | SercomRXPad rx_pad; 111 | SercomUartTXPad tx_pad; 112 | #else 113 | HardwareSerial* serial; 114 | #endif 115 | }; 116 | 117 | class Railcom 118 | { 119 | public: 120 | RailComConfig config; 121 | 122 | static void getDefaultConfig(RailComConfig& _config) { 123 | _config.enable = false; 124 | _config.baud = 250000; 125 | } 126 | 127 | Railcom(RailComConfig _config) { 128 | config = _config; 129 | } 130 | void setup(); 131 | 132 | void enableRecieve(uint8_t on); 133 | void readData(uint16_t dataID, PacketType _packetType, uint16_t _address); 134 | void processData(); 135 | 136 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 137 | Uart* getSerial() { return config.serial; } 138 | #else 139 | HardwareSerial* getSerial() { return config.serial; } 140 | #endif 141 | void setPOMResponseCallback(Print* _stream, void (*_POMResponse)(Print*, RailcomPOMResponse)) { 142 | POMResponse = _POMResponse; 143 | responseStream = _stream; 144 | } 145 | 146 | private: 147 | uint8_t rawData[8]; 148 | uint16_t uniqueID; 149 | uint16_t address; 150 | PacketType type; 151 | bool dataReady = false; 152 | void (*POMResponse)(Print*, RailcomPOMResponse); 153 | Print* responseStream = nullptr; 154 | }; 155 | 156 | #endif // COMMANDSTATION_DCC_RAILCOM_H_ -------------------------------------------------------------------------------- /src/CommInterface/CommManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * CommManager.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "CommManager.h" 21 | 22 | #include 23 | 24 | CommInterface *CommManager::interfaces[5] = {NULL, NULL, NULL, NULL, NULL}; 25 | int CommManager::nextInterface = 0; 26 | 27 | void CommManager::update() { 28 | for(int i = 0; i < nextInterface; i++) { 29 | if(interfaces[i] != NULL) { 30 | interfaces[i]->process(); 31 | } 32 | } 33 | } 34 | 35 | void CommManager::registerInterface(CommInterface *interface) { 36 | if(nextInterface < 5) { 37 | interfaces[nextInterface++] = interface; 38 | } 39 | } 40 | 41 | void CommManager::showConfiguration() { 42 | for(int i = 0; i < nextInterface; i++) { 43 | if(interfaces[i] != NULL) { 44 | interfaces[i]->showConfiguration(); 45 | } 46 | } 47 | } 48 | 49 | void CommManager::showInitInfo() { 50 | for(int i = 0; i < nextInterface; i++) { 51 | if(interfaces[i] != NULL) { 52 | interfaces[i]->showInitInfo(); 53 | } 54 | } 55 | } 56 | 57 | void CommManager::broadcast(const __FlashStringHelper* input, ...) { 58 | for(int i = 0; i < nextInterface; i++) { 59 | if(interfaces[i] != NULL) { 60 | Print* mPrint = interfaces[i]->getStream(); 61 | 62 | va_list args; 63 | va_start(args, input); 64 | send2(mPrint, input, args); 65 | va_end(args); 66 | } 67 | } 68 | } 69 | 70 | void CommManager::print(const __FlashStringHelper* input, ...) { 71 | va_list args; 72 | va_start(args, input); 73 | send2(&DIAGSERIAL, input, args); 74 | va_end(args); 75 | } 76 | 77 | void CommManager::send(Print* stream, const __FlashStringHelper* input, ...) { 78 | va_list args; 79 | va_start(args, input); 80 | send2(stream, input, args); 81 | va_end(args); 82 | } 83 | 84 | void CommManager::send2(Print* stream, const __FlashStringHelper* format, va_list args) { 85 | char* flash = (char*)format; 86 | for(int i=0; ; ++i) { 87 | char c=pgm_read_byte_near(flash + i); 88 | if(c == '\0') return; 89 | if(c != '%') { 90 | stream->print(c); 91 | continue; 92 | } 93 | i++; 94 | c = pgm_read_byte_near(flash + i); 95 | switch(c) { 96 | case '%': stream->print('%'); break; 97 | case 'c': stream->print((char) va_arg(args, int)); break; 98 | case 's': stream->print(va_arg(args, char*)); break; 99 | case 'e': printEscapes(stream,va_arg(args, char*)); break; 100 | case 'E': printEscapes(stream,(const __FlashStringHelper*)va_arg(args, char*)); break; 101 | case 'S': stream->print((const __FlashStringHelper*)va_arg(args, char*)); break; 102 | case 'd': stream->print(va_arg(args, int), DEC); break; 103 | case 'l': stream->print(va_arg(args, long), DEC); break; 104 | case 'b': stream->print(va_arg(args, int), BIN); break; 105 | case 'o': stream->print(va_arg(args, int), OCT); break; 106 | case 'x': stream->print(va_arg(args, int), HEX); break; 107 | case 'f': stream->print(va_arg(args, double), 2); break; 108 | } 109 | } 110 | va_end(args); 111 | } 112 | 113 | void CommManager::printEscapes(Print* stream, char * input) { 114 | for(int i=0; ; ++i) { 115 | char c=input[i]; 116 | printEscape(stream,c); 117 | if (c=='\0') return; 118 | } 119 | } 120 | 121 | void CommManager::printEscapes(Print* stream, const __FlashStringHelper* input) { 122 | char* flash=(char*)input; 123 | for(int i=0; ; ++i) { 124 | char c=pgm_read_byte_near(flash+i); 125 | printEscape(stream,c); 126 | if (c=='\0') return; 127 | } 128 | } 129 | 130 | void CommManager::printEscape(Print* stream, char c) { 131 | switch(c) { 132 | case '\n': stream->print(F("\\n")); break; 133 | case '\r': stream->print(F("\\r")); break; 134 | case '\0': stream->print(F("\\0")); return; 135 | case '\t': stream->print(F("\\t")); break; 136 | case '\\': stream->print(F("\\")); break; 137 | default: stream->print(c); 138 | } 139 | } -------------------------------------------------------------------------------- /src/Accessories/Sensors.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Sensors.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "Sensors.h" 21 | 22 | #include "../CommInterface/CommManager.h" 23 | #include "EEStore.h" 24 | 25 | #if !defined(ARDUINO_ARCH_SAMD) && !defined(ARDUINO_ARCH_SAMC) 26 | #include 27 | #endif 28 | 29 | void Sensor::check(Print* stream){ 30 | Sensor *tt; 31 | 32 | for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){ 33 | tt->signal = tt->signal * (1.0 - SENSOR_DECAY) + digitalRead(tt->data.pin) * SENSOR_DECAY; 34 | 35 | if(!tt->active && tt->signal<0.5){ 36 | tt->active=true; 37 | CommManager::send(stream, F(""), tt->data.snum); 38 | } else if(tt->active && tt->signal>0.9){ 39 | tt->active=false; 40 | CommManager::send(stream, F(""), tt->data.snum); 41 | } 42 | } // loop over all sensors 43 | 44 | } 45 | 46 | Sensor *Sensor::create(Print* stream, int snum, int pin, int pullUp, int v){ 47 | Sensor *tt; 48 | 49 | if(firstSensor==NULL){ 50 | firstSensor=(Sensor *)calloc(1,sizeof(Sensor)); 51 | tt=firstSensor; 52 | } else if((tt=get(snum))==NULL){ 53 | tt=firstSensor; 54 | while(tt->nextSensor!=NULL) 55 | tt=tt->nextSensor; 56 | tt->nextSensor=(Sensor *)calloc(1,sizeof(Sensor)); 57 | tt=tt->nextSensor; 58 | } 59 | 60 | if(tt==NULL){ // problem allocating memory 61 | if(v==1) 62 | CommManager::send(stream, F("")); 63 | return(tt); 64 | } 65 | 66 | tt->data.snum=snum; 67 | tt->data.pin=pin; 68 | tt->data.pullUp=(pullUp==0?LOW:HIGH); 69 | tt->active=false; 70 | tt->signal=1; 71 | pinMode(pin,INPUT); // set mode to input 72 | // Don't use Arduino's internal pull-up resistors for external infrared 73 | // sensors --- each sensor must have its own 1K external pull-up resistor 74 | digitalWrite(pin,pullUp); 75 | 76 | if(v==1) 77 | CommManager::send(stream, F("")); 78 | return(tt); 79 | 80 | } 81 | 82 | Sensor* Sensor::get(int n){ 83 | Sensor *tt; 84 | for(tt=firstSensor;tt!=NULL && tt->data.snum!=n;tt=tt->nextSensor); 85 | return(tt); 86 | } 87 | 88 | void Sensor::remove(Print* stream, int n){ 89 | Sensor *tt,*pp; 90 | tt=firstSensor; 91 | pp=tt; 92 | 93 | for( ;tt!=NULL && tt->data.snum!=n;pp=tt,tt=tt->nextSensor); 94 | 95 | if(tt==NULL){ 96 | CommManager::send(stream, F("")); 97 | return; 98 | } 99 | 100 | if(tt==firstSensor) 101 | firstSensor=tt->nextSensor; 102 | else 103 | pp->nextSensor=tt->nextSensor; 104 | 105 | free(tt); 106 | 107 | CommManager::send(stream, F("")); 108 | } 109 | 110 | void Sensor::show(Print* stream){ 111 | Sensor *tt; 112 | 113 | if(firstSensor==NULL){ 114 | CommManager::send(stream, F("")); 115 | return; 116 | } 117 | 118 | for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){ 119 | CommManager::send(stream, F(""), tt->data.snum, tt->data.pin, tt->data.pullUp); 120 | } 121 | } 122 | 123 | void Sensor::status(Print* stream){ 124 | Sensor *tt; 125 | 126 | if(firstSensor==NULL){ 127 | CommManager::send(stream, F("")); 128 | return; 129 | } 130 | 131 | for(tt=firstSensor;tt!=NULL;tt=tt->nextSensor){ 132 | CommManager::send(stream, F("<%c %d>"), tt->active?'Q':'q', tt->data.snum); 133 | } 134 | } 135 | 136 | void Sensor::load(Print* stream){ 137 | struct SensorData data; 138 | Sensor *tt; 139 | 140 | for(int i=0;idata.nSensors;i++){ 141 | EEPROM.get(EEStore::pointer(),data); 142 | tt=create(stream, data.snum, data.pin, data.pullUp); 143 | EEStore::advance(sizeof(tt->data)); 144 | } 145 | } 146 | 147 | void Sensor::store(){ 148 | Sensor *tt; 149 | 150 | tt=firstSensor; 151 | EEStore::eeStore->data.nSensors=0; 152 | 153 | while(tt!=NULL){ 154 | EEPROM.put(EEStore::pointer(),tt->data); 155 | EEStore::advance(sizeof(tt->data)); 156 | tt=tt->nextSensor; 157 | EEStore::eeStore->data.nSensors++; 158 | } 159 | } 160 | 161 | Sensor *Sensor::firstSensor=NULL; 162 | -------------------------------------------------------------------------------- /src/Accessories/Outputs.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "Outputs.h" 21 | 22 | #include "../CommInterface/CommManager.h" 23 | #include "EEStore.h" 24 | 25 | #if !defined(ARDUINO_ARCH_SAMD) && !defined(ARDUINO_ARCH_SAMC) 26 | #include 27 | #endif 28 | 29 | void Output::activate(Print* stream, int s){ 30 | // if s>0, set status to active, else inactive 31 | data.oStatus=(s>0); 32 | // set state of output pin to HIGH or LOW depending on whether bit zero of 33 | // iFlag is set to 0 (ACTIVE=HIGH) or 1 (ACTIVE=LOW) 34 | digitalWrite(data.pin,data.oStatus ^ bitRead(data.iFlag,0)); 35 | if(num>0) 36 | EEPROM.put(num,data.oStatus); 37 | CommManager::send(stream, F(""), data.id, data.oStatus); 38 | } 39 | 40 | Output* Output::get(int n){ 41 | Output *tt; 42 | for(tt=firstOutput;tt!=NULL && tt->data.id!=n;tt=tt->nextOutput); 43 | return(tt); 44 | } 45 | 46 | void Output::remove(Print* stream, int n){ 47 | Output *tt,*pp; 48 | tt=firstOutput; 49 | pp=tt; 50 | 51 | for( ; tt!=NULL && tt->data.id!=n; pp=tt,tt=tt->nextOutput); 52 | 53 | if(tt==NULL){ 54 | CommManager::send(stream, F("")); 55 | return; 56 | } 57 | 58 | if(tt==firstOutput) 59 | firstOutput=tt->nextOutput; 60 | else 61 | pp->nextOutput=tt->nextOutput; 62 | 63 | free(tt); 64 | 65 | CommManager::send(stream, F("")); 66 | } 67 | 68 | void Output::show(Print* stream, int n){ 69 | Output *tt; 70 | 71 | if(firstOutput==NULL){ 72 | CommManager::send(stream, F("")); 73 | return; 74 | } 75 | 76 | for(tt=firstOutput;tt!=NULL;tt=tt->nextOutput){ 77 | if(n==1) { 78 | CommManager::send(stream, F(""), tt->data.id, tt->data.pin, tt->data.iFlag, tt->data.oStatus); 79 | } else { 80 | CommManager::send(stream, F(""), tt->data.id, tt->data.oStatus); 81 | } 82 | } 83 | } 84 | 85 | void Output::load(Print* stream){ 86 | struct OutputData data; 87 | Output *tt; 88 | 89 | for(int i=0;idata.nOutputs;i++){ 90 | EEPROM.get(EEStore::pointer(),data); 91 | tt=create(stream, data.id, data.pin, data.iFlag); 92 | tt->data.oStatus=bitRead(tt->data.iFlag,1)?bitRead(tt->data.iFlag,2):data.oStatus; // restore status to EEPROM value is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag 93 | digitalWrite(tt->data.pin,tt->data.oStatus ^ bitRead(tt->data.iFlag,0)); 94 | pinMode(tt->data.pin,OUTPUT); 95 | tt->num=EEStore::pointer(); 96 | EEStore::advance(sizeof(tt->data)); 97 | } 98 | } 99 | 100 | void Output::store(){ 101 | Output *tt; 102 | 103 | tt=firstOutput; 104 | EEStore::eeStore->data.nOutputs=0; 105 | 106 | while(tt!=NULL){ 107 | tt->num=EEStore::pointer(); 108 | EEPROM.put(EEStore::pointer(),tt->data); 109 | EEStore::advance(sizeof(tt->data)); 110 | tt=tt->nextOutput; 111 | EEStore::eeStore->data.nOutputs++; 112 | } 113 | 114 | } 115 | 116 | Output *Output::create(Print* stream, int id, int pin, int iFlag, int v){ 117 | Output *tt; 118 | 119 | if(firstOutput==NULL){ 120 | firstOutput=(Output *)calloc(1,sizeof(Output)); 121 | tt=firstOutput; 122 | } else if((tt=get(id))==NULL){ 123 | tt=firstOutput; 124 | while(tt->nextOutput!=NULL) 125 | tt=tt->nextOutput; 126 | tt->nextOutput=(Output *)calloc(1,sizeof(Output)); 127 | tt=tt->nextOutput; 128 | } 129 | 130 | if(tt==NULL){ // problem allocating memory 131 | if(v==1) 132 | CommManager::send(stream, F("")); 133 | return(tt); 134 | } 135 | 136 | tt->data.id=id; 137 | tt->data.pin=pin; 138 | tt->data.iFlag=iFlag; 139 | tt->data.oStatus=0; 140 | 141 | if(v==1){ 142 | tt->data.oStatus=bitRead(tt->data.iFlag,1)?bitRead(tt->data.iFlag,2):0; // sets status to 0 (INACTIVE) is bit 1 of iFlag=0, otherwise set to value of bit 2 of iFlag 143 | digitalWrite(tt->data.pin,tt->data.oStatus ^ bitRead(tt->data.iFlag,0)); 144 | pinMode(tt->data.pin,OUTPUT); 145 | CommManager::send(stream, F("")); 146 | } 147 | 148 | return(tt); 149 | 150 | } 151 | 152 | Output *Output::firstOutput=NULL; 153 | -------------------------------------------------------------------------------- /src/DCC/DCCMain.h: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCMain.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_DCC_DCCMAIN_H_ 21 | #define COMMANDSTATION_DCC_DCCMAIN_H_ 22 | 23 | #include 24 | 25 | #include "Waveform.h" 26 | #include "Railcom.h" 27 | #include "Queue.h" 28 | 29 | struct setThrottleResponse { 30 | uint8_t device; 31 | uint8_t speed; 32 | uint8_t direction; 33 | uint16_t transactionID; 34 | }; 35 | 36 | struct genericResponse { 37 | uint16_t transactionID; 38 | }; 39 | 40 | class DCCMain : public Waveform { 41 | public: 42 | DCCMain(uint8_t numDevices, Board* board, Railcom* railcom); 43 | 44 | void setup() { 45 | // board.setup must be called from the main file 46 | railcom->setup(); 47 | } 48 | 49 | void loop() { 50 | Waveform::loop(); 51 | updateSpeed(); 52 | railcom->processData(); 53 | } 54 | 55 | bool interrupt1(); 56 | void interrupt2(); 57 | 58 | uint8_t setThrottle(uint16_t addr, uint8_t speedCode, setThrottleResponse& response); 59 | uint8_t setFunction(uint16_t addr, uint8_t byte1, genericResponse& response); 60 | uint8_t setFunction(uint16_t addr, uint8_t byte1, uint8_t byte2, genericResponse& response); 61 | uint8_t setAccessory(uint16_t addr, uint8_t number, bool activate, genericResponse& response); 62 | // Writes a CV to a decoder on the main track and calls a callback function 63 | // if there is any railcom response to the request. 64 | uint8_t writeCVByteMain(uint16_t addr, uint16_t cv, uint8_t bValue, 65 | genericResponse& response, Print *stream, void (*POMCallback)(Print*, RailcomPOMResponse)); 66 | // Writes a single bit to the decoder on the main track and calls a callback 67 | // function if there is any railcom response to the request. 68 | uint8_t writeCVBitMain(uint16_t addr, uint16_t cv, uint8_t bNum, 69 | uint8_t bValue, genericResponse& response, Print *stream, 70 | void (*POMCallback)(Print*, RailcomPOMResponse)); 71 | // Reads one byte from the decoder over railcom and calls a callback function 72 | // with the value 73 | uint8_t readCVByteMain(uint16_t addr, uint16_t cv, 74 | genericResponse& response, Print *stream, void (*POMCallback)(Print*, RailcomPOMResponse)); 75 | // Reads four bytes from the decoder over railcom. CV corresponds to the 76 | // first byte, the rest are CV+1, CV+2, and CV+3. Calls a callback function 77 | // with the four values. 78 | uint8_t readCVBytesMain(uint16_t addr, uint16_t cv, 79 | genericResponse& response, Print *stream, void (*POMCallback)(Print*, RailcomPOMResponse)); 80 | 81 | uint8_t numDevices; 82 | 83 | // Holds info about a device's speed and direction. 84 | // TODO(davidcutting42@gmail.com): Make this private 85 | struct Speed { 86 | uint16_t cab; 87 | uint8_t speedCode; 88 | }; 89 | // Speed table holds speed of all devices on the bus that have been set since 90 | // startup. 91 | Speed* speedTable; 92 | 93 | // Railcom object, complements hdw object inherited from Waveform 94 | Railcom* railcom; 95 | 96 | void forgetDevice(uint8_t cab); 97 | void forgetAllDevices(); 98 | 99 | private: 100 | // Queues a packet for the next device in line reminding it of its speed. 101 | void updateSpeed(); 102 | // Holds state for updateSpeed function. 103 | uint8_t nextDev = 0; 104 | 105 | struct Packet { 106 | uint8_t payload[kPacketMaxSize]; 107 | uint8_t length; 108 | uint8_t repeats; 109 | uint16_t transmitID; // Identifier for railcom, etc. 110 | PacketType type; 111 | uint16_t address; 112 | }; 113 | 114 | PacketType transmitType = kIdleType; 115 | uint16_t transmitAddress = 0; 116 | 117 | // Queue of packets, FIFO, that controls what gets sent out next. Size 5. 118 | Queue packetQueue; 119 | 120 | void schedulePacket(const uint8_t buffer[], uint8_t byteCount, 121 | uint8_t repeats, uint16_t identifier, PacketType type, uint16_t address); 122 | 123 | // Railcom cutout variables 124 | // TODO(davidcutting42@gmail.com): Move these to the railcom class 125 | bool generateRailcomCutout = false; // Should we do a railcom cutout? 126 | bool inRailcomCutout = false; // Are we in a cutout? 127 | bool railcomData = false; // Is there railcom data available? 128 | 129 | void updateSpeedTable(uint8_t cab, uint8_t speedCode); 130 | int lookupSpeedTable(uint8_t cab); 131 | }; 132 | 133 | #endif -------------------------------------------------------------------------------- /src/DCC/DCCService.h: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCService.h 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #ifndef COMMANDSTATION_DCC_DCCSERVICE_H_ 21 | #define COMMANDSTATION_DCC_DCCSERVICE_H_ 22 | 23 | #include 24 | 25 | #include "Waveform.h" 26 | #include "Queue.h" 27 | 28 | // Threshold (mA) that a sample must cross to ACK 29 | const uint8_t kACKThreshold = 20; 30 | 31 | enum cv_edit_type : uint8_t { 32 | READCV, 33 | WRITECV, 34 | WRITECVBIT, 35 | }; 36 | 37 | struct serviceModeResponse { 38 | cv_edit_type type; 39 | uint16_t callback; 40 | uint16_t callbackSub; 41 | uint16_t cv; 42 | uint8_t cvBitNum; 43 | int cvValue; // Might be -1, so int works 44 | }; 45 | 46 | enum ackOpCodes { 47 | BASELINE, // ensure enough resets sent before starting and obtain baseline 48 | // current 49 | W0,W1, // issue write bit (0..1) packet 50 | WB, // issue write byte packet 51 | VB, // Issue validate Byte packet 52 | V0, V1, // issue validate bit (0/1) packet 53 | WACK, // wait for ack (or absence of ack) 54 | ITC1, // If True Callback(1) (if prevous WACK got an ACK) 55 | ITC0, // If True callback(0); 56 | ITCB, // If True callback(byte) 57 | NACKFAIL, // if false callback(-1) 58 | FAIL, // callback(-1) 59 | STARTMERGE, // Clear bit and byte settings ready for merge pass 60 | MERGE, // Merge previous wack response with byte value and decrement bit 61 | // number (use for reading CV bytes) 62 | }; 63 | 64 | typedef void (*ACK_CALLBACK)(Print* stream, serviceModeResponse result); 65 | 66 | class DCCService : public Waveform { 67 | public: 68 | DCCService(Board* board); 69 | 70 | void setup() { 71 | // board.setup must be called from the main file 72 | } 73 | 74 | void loop() { 75 | Waveform::loop(); // Checks for overcurrent and manages power 76 | ackManagerLoop(); 77 | } 78 | 79 | bool interrupt1(); 80 | void interrupt2(); 81 | 82 | uint8_t writeCVByte(uint16_t cv, uint8_t bValue, uint16_t callback, 83 | uint16_t callbackSub, Print* stream, ACK_CALLBACK); 84 | uint8_t writeCVBit(uint16_t cv, uint8_t bNum, uint8_t bValue, 85 | uint16_t callback, uint16_t callbackSub, Print* stream, 86 | ACK_CALLBACK); 87 | uint8_t readCV(uint16_t cv, uint16_t callback, uint16_t callbackSub, Print* stream, 88 | ACK_CALLBACK); 89 | 90 | private: 91 | struct Packet { 92 | uint8_t payload[kPacketMaxSize]; 93 | uint8_t length; 94 | uint8_t repeats; 95 | uint16_t transmitID; // Identifier for CV programming 96 | }; 97 | 98 | // Queue of packets, FIFO, that controls what gets sent out next. 99 | Queue packetQueue; 100 | 101 | void schedulePacket(const uint8_t buffer[], uint8_t byteCount, 102 | uint8_t repeats, uint16_t identifier); 103 | 104 | // ACK MANAGER 105 | void ackManagerSetup(uint16_t cv, uint8_t value, ackOpCodes const program[], 106 | cv_edit_type type, uint16_t callbackNum, uint16_t callbackSub, 107 | Print* stream, ACK_CALLBACK callback); 108 | void ackManagerLoop(); 109 | void callback(Print* stream, serviceModeResponse value); 110 | ackOpCodes const * ackManagerProg = NULL; 111 | uint8_t ackManagerByte = 0; 112 | uint8_t ackManagerBitNum = 0; 113 | uint16_t ackManagerCV = 0; 114 | bool ackReceived = false; 115 | const uint8_t kProgRepeats = 8; 116 | const uint8_t kResetRepeats = 8; 117 | ACK_CALLBACK ackManagerCallback = NULL; 118 | uint16_t ackManagerCallbackNum = 0; 119 | uint16_t ackManagerCallbackSub = 0; 120 | cv_edit_type ackManagerType = READCV; 121 | Print* responseStream; 122 | 123 | uint8_t cv1(uint8_t opcode, uint16_t cv) { 124 | cv--; 125 | return (highByte(cv) & (uint8_t)0x03) | opcode; 126 | } 127 | uint8_t cv2(uint16_t cv) { 128 | cv--; 129 | return lowByte(cv); 130 | } 131 | 132 | uint8_t transmitResetCount = 0; // Tracks resets sent since last payload packet 133 | 134 | void setAckPending(); 135 | uint8_t didAck(); 136 | void checkAck(); 137 | uint16_t lastCurrent = 0; 138 | bool ackPending = false; 139 | bool ackDetected = false; 140 | 141 | // NMRA codes # 142 | const uint8_t SET_SPEED = 0x3f; 143 | const uint8_t WRITE_BYTE_MAIN = 0xEC; 144 | const uint8_t WRITE_BIT_MAIN = 0xE8; 145 | const uint8_t WRITE_BYTE = 0x7C; 146 | const uint8_t VERIFY_BYTE = 0x74; 147 | const uint8_t BIT_MANIPULATE = 0x78; 148 | const uint8_t WRITE_BIT = 0xF0; 149 | const uint8_t VERIFY_BIT = 0xE0; 150 | const uint8_t BIT_ON = 0x08; 151 | const uint8_t BIT_OFF = 0x00; 152 | }; 153 | 154 | #endif -------------------------------------------------------------------------------- /src/DCC/DCCMainTimers.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCMainTimers.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "DCCMain.h" 21 | 22 | bool DCCMain::interrupt1() { 23 | switch (interruptState) { 24 | case 0: // start of bit transmission 25 | board->signal(HIGH); 26 | interruptState = 1; 27 | return true; // must call interrupt2 to set currentBit 28 | case 1: // 29us after case 0 29 | if(generateRailcomCutout) { 30 | board->cutout(true); // Start the cutout 31 | inRailcomCutout = true; 32 | railcom->enableRecieve(true); // Turn on the serial port so we can RX 33 | } 34 | interruptState = 2; 35 | break; 36 | case 2: // 58us after case 0 37 | if(currentBit && !generateRailcomCutout) { 38 | board->signal(LOW); 39 | } 40 | interruptState = 3; 41 | break; 42 | case 3: // 87us after case 0 43 | if(currentBit && !generateRailcomCutout) { 44 | // Return to case zero for start of another bit at 116us 45 | interruptState = 0; 46 | } 47 | else interruptState = 4; 48 | break; 49 | case 4: // 116us after case 0 50 | if(!generateRailcomCutout) { 51 | board->signal(LOW); 52 | } 53 | interruptState = 5; 54 | break; 55 | // Case 5 and 6 fall to default case 56 | case 7: // 203us after case 0 57 | if(!generateRailcomCutout) { 58 | // Return to case zero for start of another bit at 232us 59 | interruptState = 0; 60 | } 61 | else interruptState = 8; 62 | break; 63 | // Cases 8-15 are for railcom timing 64 | case 16: 65 | board->cutout(false); // Stop the cutout 66 | board->signal(LOW); // Send out 29us of signal before case 0 flips it 67 | railcom->enableRecieve(false); // Turn off serial so we don't get garbage 68 | // Read the data out and tag it with identifying info 69 | railcom->readData(transmitID, transmitType, transmitAddress); 70 | generateRailcomCutout = false; // Don't generate another railcom cutout 71 | inRailcomCutout = false; // We aren't in a railcom pulse 72 | interruptState = 0; // Go back to start of new bit 73 | break; 74 | default: 75 | interruptState++; 76 | break; 77 | } 78 | 79 | return false; // Don't call interrupt2 80 | } 81 | 82 | void DCCMain::interrupt2() { 83 | if (remainingPreambles > 0 ) { // If there's more preambles to be sent 84 | currentBit=true; // Send a one bit (preambles are one) 85 | 86 | // If we're on the first preamble bit and railcom is enabled, send out a 87 | // railcom cutout. 88 | if((board->getPreambles() - remainingPreambles == 0) && railcom->config.enable) { 89 | generateRailcomCutout = true; 90 | remainingPreambles -= 4; // We're skipping 4 bits in the cutout 91 | return; 92 | } 93 | remainingPreambles--; // decrement the number of preambles to send 94 | return; 95 | } 96 | 97 | // beware OF 9-BIT MASK generating a zero to start each byte 98 | currentBit=transmitPacket[bytes_sent] & kBitMask[bits_sent]; 99 | bits_sent++; 100 | 101 | // If this is the last bit of a byte, prepare for the next byte 102 | if (bits_sent==9) { // zero followed by 8 bits of a byte 103 | //end of Byte 104 | bits_sent=0; 105 | bytes_sent++; 106 | // if this is the last byte, prepare for next packet 107 | if (bytes_sent >= transmitLength) { 108 | // end of transmission buffer... repeat or switch to next message 109 | bytes_sent = 0; 110 | remainingPreambles = board->getPreambles() + 1; // Add one for the stop bit 111 | 112 | int pendingCount = packetQueue.count(); 113 | 114 | // Note that the number of repeats does not include the final repeat, so 115 | // the number of times transmitted is nRepeats+1 116 | if (transmitRepeats > 0) { 117 | transmitRepeats--; 118 | } 119 | else if (pendingCount > 0) { 120 | // Copy pending packet to transmit packet 121 | Packet pendingPacket = packetQueue.pop(); 122 | 123 | // Load info about the packet into the transmit variables. 124 | // TODO(davidcutting42@gmail.com): check if this can be done with a 125 | // peek() into packetQueue intead. 126 | for (int b=0;b. 18 | */ 19 | 20 | #include "DCCMain.h" 21 | #include "DCCService.h" 22 | 23 | #if defined(ARDUINO_ARCH_SAMC) 24 | 25 | // TI DRV8873 on custom board 26 | DCCMain* DCCMain::Create_WSM_FireBox_MK1S_Main(uint8_t numDevices) { 27 | Hardware hdw; 28 | Railcom rcom; 29 | 30 | hdw.config_setChannelName("MAIN"); 31 | 32 | hdw.config_setControlScheme(DRV8873); 33 | 34 | hdw.config_setPinSignalA(21); 35 | hdw.config_setPinSignalB(22); 36 | hdw.config_setPinEnable(24); 37 | hdw.config_setDefaultEnable(DEFAULT_HIGH); 38 | hdw.config_setPinSleep(25); 39 | hdw.config_setDefaultSleep(DEFAULT_LOW); 40 | hdw.config_setPinCurrentSense(23); 41 | 42 | hdw.config_setTriggerValue(5000); 43 | hdw.config_setMaxValue(6500); 44 | hdw.config_setAmpsPerVolt(6.111); 45 | 46 | hdw.config_setPreambleBits(16); 47 | 48 | rcom.config_setEnable(true); 49 | rcom.config_setRxPin(34); 50 | rcom.config_setTxPin(34); 51 | rcom.config_setSerial(nullptr); 52 | rcom.config_setSercom(&sercom0); 53 | rcom.config_setRxMux(PIO_SERCOM); 54 | rcom.config_setRxPad(SERCOM_RX_PAD_0); 55 | rcom.config_setTxPad(UART_TX_PAD_2); 56 | rcom.config_setDACValue(0x4E); 57 | 58 | return new DCCMain(numDevices, hdw, rcom); 59 | } 60 | 61 | // TI DRV8873 on custom board 62 | DCCService* DCCService::Create_WSM_FireBox_MK1S_Prog() { 63 | Hardware hdw; 64 | 65 | hdw.config_setChannelName("PROG"); 66 | 67 | hdw.config_setControlScheme(DRV8873); 68 | 69 | hdw.config_setPinSignalA(27); 70 | hdw.config_setPinSignalB(28); 71 | hdw.config_setPinEnable(30); 72 | hdw.config_setDefaultEnable(DEFAULT_HIGH); 73 | hdw.config_setPinSleep(31); 74 | hdw.config_setDefaultSleep(DEFAULT_LOW); 75 | hdw.config_setPinCurrentSense(29); 76 | 77 | hdw.config_setTriggerValue(250); 78 | hdw.config_setMaxValue(6500); 79 | hdw.config_setAmpsPerVolt(6.111); 80 | 81 | hdw.config_setPreambleBits(22); 82 | 83 | return new DCCService(hdw); 84 | } 85 | 86 | #elif defined(ARDUINO_ARCH_SAMD) 87 | // TI DRV8874 on custom board 88 | DCCMain* DCCMain::Create_WSM_FireBox_MK1_Main(uint8_t numDevices) { 89 | Hardware hdw; 90 | Railcom rcom; 91 | 92 | hdw.config_setChannelName("MAIN"); 93 | 94 | hdw.config_setControlScheme(DUAL_DIRECTION_INVERTED); 95 | 96 | hdw.config_setPinSignalA(6); 97 | hdw.config_setPinSignalB(7); 98 | hdw.config_setPinEnable(3); 99 | hdw.config_setDefaultEnable(DEFAULT_LOW); 100 | hdw.config_setDefaultSleep(DEFAULT_NOT_USED); 101 | hdw.config_setPinCurrentSense(A5); 102 | 103 | hdw.config_setTriggerValue(5500); 104 | hdw.config_setMaxValue(6000); 105 | hdw.config_setAmpsPerVolt(1.998004); 106 | 107 | hdw.config_setPreambleBits(16); 108 | 109 | rcom.config_setEnable(true); 110 | rcom.config_setRxPin(5); 111 | rcom.config_setTxPin(2); 112 | rcom.config_setSerial(nullptr); 113 | rcom.config_setSercom(&sercom4); 114 | rcom.config_setRxMux(PIO_SERCOM_ALT); 115 | rcom.config_setRxPad(SERCOM_RX_PAD_3); 116 | rcom.config_setTxPad(UART_TX_PAD_2); 117 | rcom.config_setDACValue(0x7); 118 | 119 | return new DCCMain(numDevices, hdw, rcom); 120 | } 121 | 122 | // TI DRV8876 on custom board 123 | DCCService* DCCService::Create_WSM_FireBox_MK1_Prog() { 124 | Hardware hdw; 125 | 126 | hdw.config_setChannelName("PROG"); 127 | 128 | hdw.config_setControlScheme(DUAL_DIRECTION_INVERTED); 129 | 130 | hdw.config_setPinSignalA(8); 131 | hdw.config_setPinSignalB(9); 132 | hdw.config_setPinEnable(4); 133 | hdw.config_setDefaultEnable(DEFAULT_LOW); 134 | hdw.config_setDefaultSleep(DEFAULT_NOT_USED); 135 | hdw.config_setPinCurrentSense(A1); 136 | 137 | hdw.config_setTriggerValue(250); 138 | hdw.config_setMaxValue(3500); 139 | hdw.config_setAmpsPerVolt(0.909089); 140 | 141 | hdw.config_setPreambleBits(22); 142 | 143 | return new DCCService(hdw); 144 | } 145 | 146 | #endif 147 | 148 | DCCMain* DCCMain::Create_Arduino_L298Shield_Main(uint8_t numDevices) { 149 | Hardware hdw; 150 | Railcom rcom; 151 | 152 | hdw.config_setChannelName("MAIN"); 153 | 154 | hdw.config_setControlScheme(DIRECTION_BRAKE_ENABLE); 155 | 156 | hdw.config_setPinSignalA(12); 157 | hdw.config_setPinSignalB(9); 158 | hdw.config_setDefaultSignalB(DEFAULT_LOW); 159 | hdw.config_setPinEnable(3); 160 | hdw.config_setDefaultEnable(DEFAULT_LOW); 161 | hdw.config_setDefaultSleep(DEFAULT_NOT_USED); 162 | hdw.config_setPinCurrentSense(A0); 163 | 164 | hdw.config_setTriggerValue(1500); 165 | hdw.config_setMaxValue(2000); 166 | hdw.config_setAmpsPerVolt(0.606061); 167 | 168 | hdw.config_setPreambleBits(16); 169 | 170 | rcom.config_setEnable(false); 171 | 172 | return new DCCMain(numDevices, hdw, rcom); 173 | } 174 | 175 | DCCService* DCCService::Create_Arduino_L298Shield_Prog() { 176 | Hardware hdw; 177 | 178 | hdw.config_setChannelName("PROG"); 179 | 180 | hdw.config_setControlScheme(DIRECTION_BRAKE_ENABLE); 181 | 182 | hdw.config_setPinSignalA(13); 183 | hdw.config_setPinSignalB(8); 184 | hdw.config_setDefaultSignalB(DEFAULT_LOW); 185 | hdw.config_setPinEnable(11); 186 | hdw.config_setDefaultEnable(DEFAULT_LOW); 187 | hdw.config_setDefaultSleep(DEFAULT_NOT_USED); 188 | hdw.config_setPinCurrentSense(A1); 189 | 190 | hdw.config_setTriggerValue(250); 191 | hdw.config_setMaxValue(2000); 192 | hdw.config_setAmpsPerVolt(0.606061); 193 | 194 | hdw.config_setPreambleBits(22); 195 | 196 | return new DCCService(hdw); 197 | } 198 | 199 | //////////////////////////////////////////////////////////////////////////////// 200 | 201 | DCCMain* DCCMain::Create_Pololu_MC33926Shield_Main(uint8_t numDevices) { 202 | Hardware hdw; 203 | Railcom rcom; 204 | 205 | hdw.config_setChannelName("MAIN"); 206 | 207 | hdw.config_setControlScheme(DIRECTION_BRAKE_ENABLE); 208 | 209 | hdw.config_setPinSignalA(7); 210 | hdw.config_setPinSignalB(9); 211 | hdw.config_setDefaultSignalB(DEFAULT_HIGH); 212 | hdw.config_setPinEnable(4); 213 | hdw.config_setDefaultEnable(DEFAULT_LOW); 214 | hdw.config_setDefaultSleep(DEFAULT_NOT_USED); 215 | hdw.config_setPinCurrentSense(A0); 216 | 217 | hdw.config_setTriggerValue(2500); 218 | hdw.config_setMaxValue(3000); 219 | hdw.config_setAmpsPerVolt(1.904762); 220 | 221 | hdw.config_setPreambleBits(16); 222 | 223 | rcom.config_setEnable(false); 224 | 225 | return new DCCMain(numDevices, hdw, rcom); 226 | } 227 | 228 | DCCService* DCCService::Create_Pololu_MC33926Shield_Prog() { 229 | Hardware hdw; 230 | 231 | hdw.config_setChannelName("PROG"); 232 | 233 | hdw.config_setControlScheme(DIRECTION_BRAKE_ENABLE); 234 | 235 | hdw.config_setPinSignalA(8); 236 | hdw.config_setPinSignalB(10); 237 | hdw.config_setDefaultSignalB(DEFAULT_HIGH); 238 | hdw.config_setPinEnable(4); 239 | hdw.config_setDefaultEnable(DEFAULT_LOW); 240 | hdw.config_setDefaultSleep(DEFAULT_NOT_USED); 241 | hdw.config_setPinCurrentSense(A1); 242 | 243 | hdw.config_setTriggerValue(250); 244 | hdw.config_setMaxValue(3000); 245 | hdw.config_setAmpsPerVolt(1.904762); 246 | 247 | hdw.config_setPreambleBits(22); 248 | 249 | return new DCCService(hdw); 250 | } 251 | 252 | -------------------------------------------------------------------------------- /src/DCC/Railcom.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Railcom.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "Railcom.h" 21 | 22 | #include 23 | 24 | #include "../CommInterface/CommManager.h" 25 | 26 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 27 | #include "wiring_private.h" 28 | #endif 29 | 30 | const uint8_t railcom_decode[256] PROGMEM = 31 | { INV, INV, INV, INV, INV, INV, INV, INV, 32 | INV, INV, INV, INV, INV, INV, INV, NACK, 33 | INV, INV, INV, INV, INV, INV, INV, 0x33, 34 | INV, INV, INV, 0x34, INV, 0x35, 0x36, INV, 35 | INV, INV, INV, INV, INV, INV, INV, 0x3A, 36 | INV, INV, INV, 0x3B, INV, 0x3C, 0x37, INV, 37 | INV, INV, INV, 0x3F, INV, 0x3D, 0x38, INV, 38 | INV, 0x3E, 0x39, INV, RESVD3, INV, INV, INV, 39 | INV, INV, INV, INV, INV, INV, INV, 0x24, 40 | INV, INV, INV, 0x23, INV, 0x22, 0x21, INV, 41 | INV, INV, INV, 0x1F, INV, 0x1E, 0x20, INV, 42 | INV, 0x1D, 0x1C, INV, 0x1B, INV, INV, INV, 43 | INV, INV, INV, 0x19, INV, 0x18, 0x1A, INV, 44 | INV, 0x17, 0x16, INV, 0x15, INV, INV, INV, 45 | INV, 0x25, 0x14, INV, 0x13, INV, INV, INV, 46 | 0x32, INV, INV, INV, INV, INV, INV, INV, 47 | INV, INV, INV, INV, INV, INV, INV, RESVD2, 48 | INV, INV, INV, 0x0E, INV, 0x0D, 0x0C, INV, 49 | INV, INV, INV, 0x0A, INV, 0x09, 0x0B, INV, 50 | INV, 0x08, 0x07, INV, 0x06, INV, INV, INV, 51 | INV, INV, INV, 0x04, INV, 0x03, 0x05, INV, 52 | INV, 0x02, 0x01, INV, 0x00, INV, INV, INV, 53 | INV, 0x0F, 0x10, INV, 0x11, INV, INV, INV, 54 | 0x12, INV, INV, INV, INV, INV, INV, INV, 55 | INV, INV, INV, RESVD1, INV, 0x2B, 0x30, INV, 56 | INV, 0x2A, 0x2F, INV, 0x31, INV, INV, INV, 57 | INV, 0x29, 0x2E, INV, 0x2D, INV, INV, INV, 58 | 0x2C, INV, INV, INV, INV, INV, INV, INV, 59 | INV, BUSY, 0x28, INV, 0x27, INV, INV, INV, 60 | 0x26, INV, INV, INV, INV, INV, INV, INV, 61 | ACK, INV, INV, INV, INV, INV, INV, INV, 62 | INV, INV, INV, INV, INV, INV, INV, INV, 63 | }; 64 | 65 | void Railcom::setup() { 66 | if(config.enable) { 67 | 68 | #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_SAMC) 69 | if(config.serial == nullptr) { 70 | config.serial = new Uart(config.sercom, config.rx_pin, config.tx_pin, config.rx_pad, config.tx_pad); 71 | } 72 | #endif 73 | config.serial->begin(config.baud); 74 | } 75 | } 76 | 77 | // TODO(davidcutting42@gmail.com): test on AVR 78 | void Railcom::enableRecieve(uint8_t on) { 79 | if(on) { 80 | while(config.serial->available()) { 81 | config.serial->read(); // Flush the buffer so we don't get a bunch of garbage 82 | } 83 | #if defined(ARDUINO_ARCH_SAMD) 84 | pinPeripheral(config.rx_pin, config.rx_mux); 85 | #else 86 | config.serial->begin(config.baud); 87 | #endif 88 | } 89 | else { 90 | #if defined(ARDUINO_ARCH_SAMD) 91 | pinPeripheral(config.rx_pin, PIO_INPUT); 92 | #else 93 | config.serial->end(); 94 | #endif 95 | } 96 | } 97 | 98 | // This is called from an interrupt routine, so it's gotta be quick. DON'T try 99 | // to write to the serial port here. You'll destroy the waveform. 100 | void Railcom::readData(uint16_t _uniqueID, PacketType _packetType, 101 | uint16_t _address) { 102 | 103 | if(dataReady) return; 104 | 105 | uint8_t bytes = config.serial->available(); 106 | if(bytes > 8) bytes = 8; 107 | config.serial->readBytes(rawData, bytes); 108 | 109 | uniqueID = _uniqueID; 110 | address = _address; 111 | type = _packetType; 112 | 113 | if(bytes > 0) 114 | dataReady = true; 115 | } 116 | 117 | void Railcom::processData() { 118 | if(dataReady) { 119 | // CommManager::printf(F("Railcom RAW %d = %x %x %x %x %x %x %x %x\n\r"), 120 | // uniqueID, 121 | // rawData[0], rawData[1], rawData[2], rawData[3], 122 | // rawData[4], rawData[5], rawData[6], rawData[7] 123 | // ); 124 | 125 | for (size_t i = 0; i < 8; i++) 126 | { 127 | rawData[i] = pgm_read_byte_near(&railcom_decode[rawData[i]]); 128 | // Only throw out the packet if channel 2 is corrupted - channel 1 may be 129 | // corrupted by multiple decoders transmitting at once. 130 | if(i > 1) { 131 | if(rawData[i] == INV || rawData[i] == RESVD1 || rawData[i] == RESVD2 132 | || rawData[i] == RESVD3) { 133 | dataReady = false; 134 | return; 135 | } 136 | } 137 | } 138 | 139 | // Verbose print of decoded railcom data to CommManager 140 | // CommManager::printf(F("Railcom DCD %d = %x %x %x %x %x %x %x %x\n\r"), 141 | // uniqueID, 142 | // rawData[0], rawData[1], rawData[2], rawData[3], 143 | // rawData[4], rawData[5], rawData[6], rawData[7] 144 | // ); 145 | 146 | RailcomDatagram datagrams[4]; // One in ch1 plus up to three in ch2 147 | 148 | // First datagram is always the same format 149 | datagrams[0].channel = 1; 150 | datagrams[0].identifier = (rawData[0] >> 2) & 0x0F; 151 | datagrams[0].payload = (rawData[0] & 0x03) << 6 | (rawData[1] & 0x3F); 152 | 153 | switch (datagrams[0].identifier) 154 | { 155 | case kMOB_ADR_HIGH: 156 | // CommManager::printf(F("ADR HIGH %d (%d)\n\r"), datagrams[0].payload, 157 | // address); 158 | break; 159 | case kMOB_ADR_LOW: 160 | // CommManager::printf(F("ADR LOW %d (%d)\n\r"), datagrams[0].payload, 161 | // address); 162 | break; 163 | } 164 | 165 | // CommManager::printf(F("%d,%d\n\r"), highByte(address), lowByte(address)); 166 | 167 | // For now, return if there's a special bit combination in the first byte of 168 | // channel two. 169 | if(rawData[2] == ACK || rawData[2] == NACK || rawData[2] == BUSY) { 170 | dataReady = false; 171 | return; 172 | } 173 | 174 | RailcomInstructionType instructionType; 175 | if(highByte(address) >= 1 && highByte(address) <= 127) { 176 | instructionType = kMOBInstruction; 177 | } 178 | else if(highByte(address) >= 128 && highByte(address) <= 191) { 179 | instructionType = kSTATInstruction; 180 | } 181 | else if(highByte(address) >= 192 && highByte(address) <= 231) { 182 | instructionType = kMOBInstruction; 183 | } 184 | else { 185 | instructionType = kNoInstruction; 186 | } 187 | 188 | 189 | datagrams[1].channel = 2; 190 | datagrams[1].identifier = (rawData[2] >> 2) & 0x0F; 191 | 192 | if(instructionType == kMOBInstruction) { 193 | switch(datagrams[1].identifier) { 194 | case kMOB_POM: { 195 | switch(type) { // Decode based on what packet was just sent 196 | case kPOMBitWriteType: 197 | case kPOMByteWriteType: 198 | case kPOMReadType: 199 | datagrams[1].payload = 200 | ((rawData[2] & 0x03) << 6) | 201 | (rawData[3] & 0x3F); 202 | break; 203 | case kPOMLongReadType: 204 | datagrams[1].payload = 205 | ((rawData[2] & 0x03) << 30) | 206 | ((rawData[3] & 0x3F) << 24) | 207 | ((rawData[4] & 0x3F) << 18) | 208 | ((rawData[5] & 0x3F) << 12) | 209 | ((rawData[6] & 0x3F) << 6) | 210 | (rawData[7] & 0x3F); 211 | break; 212 | default: 213 | dataReady = false; 214 | return; 215 | } 216 | 217 | RailcomPOMResponse response; 218 | 219 | response.data = datagrams[1].payload; 220 | response.transactionID = uniqueID; 221 | 222 | POMResponse(responseStream, response); 223 | 224 | break; 225 | } 226 | case kMOB_EXT: 227 | case kMOB_DYN: 228 | case kMOB_SUBID: 229 | break; // We will handle these cases in a later revision 230 | } 231 | } 232 | else if(instructionType == kSTATInstruction) { 233 | switch(datagrams[1].identifier) { 234 | case kSTAT_POM: 235 | case kSTAT_STAT1: 236 | case kSTAT_TIME: 237 | case kSTAT_ERROR: 238 | case kSTAT_DYN: 239 | case kSTAT_STAT2: 240 | case kSTAT_SUBID: 241 | break; // We will handle these cases in a later revision 242 | } 243 | } 244 | 245 | dataReady = false; 246 | return; 247 | } 248 | } -------------------------------------------------------------------------------- /src/DCC/DCCService.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCService.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "DCCService.h" 21 | 22 | DCCService::DCCService(Board* settings) { 23 | this->board = settings; 24 | } 25 | 26 | void DCCService::schedulePacket(const uint8_t buffer[], uint8_t byteCount, 27 | uint8_t repeats, uint16_t identifier) { 28 | if(byteCount >= kPacketMaxSize) return; // allow for checksum 29 | 30 | Packet newPacket; 31 | 32 | uint8_t checksum=0; 33 | for (int b=0; b 6) { 165 | // ackCheckDuration = millis() - ackCheckStart; 166 | ackPending = false; 167 | return; 168 | } 169 | 170 | lastCurrent = board->getCurrentMilliamps(); 171 | 172 | // Detect the leading edge of a pulse 173 | if(lastCurrent-board->getCurrentBase() > kACKThreshold) { 174 | ackDetected = true; 175 | ackPending = false; 176 | transmitRepeats = 0; // stop sending repeats 177 | 178 | return; 179 | } 180 | } 181 | 182 | void DCCService::ackManagerLoop() { 183 | if(ackPending) checkAck(); 184 | 185 | while (ackManagerProg) { 186 | 187 | // breaks from this switch will step to next prog entry 188 | // returns from this switch will stay on same entry (typically WACK waiting 189 | // and when all finished.) 190 | uint8_t opcode = pgm_read_byte_near(ackManagerProg); 191 | uint8_t resets = transmitResetCount; 192 | 193 | switch (opcode) { 194 | case BASELINE: 195 | if (resetssetCurrentBase(); 197 | break; 198 | case W0: // write 0 bit 199 | case W1: // write 1 bit 200 | { 201 | if (resets. 18 | */ 19 | 20 | #include "DCCEXParser.h" 21 | 22 | #include 23 | 24 | #include "../Accessories/EEStore.h" 25 | #include "../Accessories/Outputs.h" 26 | #include "../Accessories/Sensors.h" 27 | #include "../Accessories/Turnouts.h" 28 | #include "../DCC-EX-Lib.h" 29 | #include "CommManager.h" 30 | 31 | DCCMain* DCCEXParser::mainTrack; 32 | DCCService* DCCEXParser::progTrack; 33 | 34 | int DCCEXParser::p[MAX_PARAMS]; 35 | 36 | void DCCEXParser::init(DCCMain* mainTrack_, DCCService* progTrack_) { 37 | mainTrack = mainTrack_; 38 | progTrack = progTrack_; 39 | } 40 | 41 | int DCCEXParser::stringParser(const char *com, int result[]) { 42 | byte state=1; 43 | byte parameterCount=0; 44 | int runningValue=0; 45 | const char * remainingCmd=com; // skips the opcode 46 | bool signNegative=false; 47 | 48 | // clear all parameters in case not enough found 49 | for (int i=0;i') return parameterCount; 57 | state=2; 58 | continue; 59 | case 2: // checking sign 60 | signNegative=false; 61 | runningValue=0; 62 | state=3; 63 | if (hot!='-') continue; 64 | signNegative=true; 65 | break; 66 | case 3: // building a parameter 67 | if (hot>='0' && hot<='9') { 68 | runningValue=10*runningValue+(hot-'0'); 69 | break; 70 | } 71 | result[parameterCount] = runningValue * (signNegative ?-1:1); 72 | parameterCount++; 73 | state=1; 74 | continue; 75 | } 76 | remainingCmd++; 77 | } 78 | return parameterCount; 79 | } 80 | 81 | // See documentation on DCC class for info on this section 82 | void DCCEXParser::parse(Print* stream, const char *com) { 83 | int numArgs = stringParser(com+1, p); 84 | 85 | switch(com[0]) { 86 | 87 | /***** SET ENGINE THROTTLES USING 128-STEP SPEED CONTROL ****/ 88 | 89 | case 't': { // 90 | setThrottleResponse throttleResponse; 91 | 92 | int speed=p[2]; 93 | if (speed>126 || speed<-1) break; // invalid JMRI speed code 94 | if (speed<0) speed=1; // emergency stop DCC speed 95 | else if (speed>0) speed++; // map 1-126 -> 2-127 96 | 97 | uint8_t speedCode = (speed & 0x7F) + p[3] * 128; 98 | 99 | if(mainTrack->setThrottle(p[1], speedCode, throttleResponse) == ERR_OK) 100 | // TODO(davidcutting42@gmail.com): move back to throttleResponse struct items instead of p[] 101 | CommManager::send(stream, F(""), throttleResponse.device, p[2], p[3]); 102 | 103 | break; 104 | } 105 | 106 | /***** OPERATE ENGINE DECODER FUNCTIONS F0-F28 ****/ 107 | 108 | case 'f': { // 109 | genericResponse response; 110 | 111 | if(numArgs == 2) 112 | mainTrack->setFunction(p[0], p[1], response); 113 | else 114 | mainTrack->setFunction(p[0], p[1], p[2], response); 115 | 116 | // TODO use response? 117 | 118 | break; 119 | } 120 | 121 | /***** OPERATE STATIONARY ACCESSORY DECODERS ****/ 122 | 123 | case 'a': { // 124 | genericResponse response; 125 | 126 | mainTrack->setAccessory(p[0], p[1], p[2], response); 127 | 128 | break; 129 | } 130 | 131 | /***** CREATE/EDIT/REMOVE/SHOW & OPERATE A TURN-OUT ****/ 132 | 133 | case 'T': // 134 | Turnout *t; 135 | 136 | switch(numArgs){ 137 | 138 | // argument is string with id number of turnout followed by zero (not 139 | // thrown) or one (thrown) 140 | case 2: 141 | t=Turnout::get(p[0]); 142 | if(t!=NULL) 143 | t->activate(stream, p[1], (DCCMain*) mainTrack); 144 | else 145 | CommManager::send(stream, F("")); 146 | break; 147 | 148 | // argument is string with id number of turnout followed by an address and 149 | // subAddress 150 | case 3: 151 | Turnout::create(stream, p[0],p[1],p[2],1); 152 | break; 153 | 154 | case 1: // argument is a string with id number only 155 | Turnout::remove(stream, p[0]); 156 | break; 157 | 158 | case 0: // no arguments 159 | Turnout::show(stream, 1); // verbose show 160 | break; 161 | } 162 | 163 | break; 164 | 165 | /***** CREATE/EDIT/REMOVE/SHOW & OPERATE AN OUTPUT PIN ****/ 166 | 167 | case 'Z': // 168 | Output* o; 169 | 170 | switch(numArgs){ 171 | 172 | // argument is string with id number of output followed by zero (LOW) or 173 | // one (HIGH) 174 | case 2: 175 | o=Output::get(p[0]); 176 | if(o!=NULL) 177 | o->activate(stream, p[1]); 178 | else 179 | CommManager::send(stream, F("")); 180 | break; 181 | 182 | // argument is string with id number of output followed by a pin number and 183 | // invert flag 184 | case 3: 185 | Output::create(stream, p[0],p[1],p[2],1); 186 | break; 187 | 188 | case 1: // argument is a string with id number only 189 | Output::remove(stream, p[0]); 190 | break; 191 | 192 | case 0: // no arguments 193 | Output::show(stream, 1); // verbose show 194 | break; 195 | } 196 | 197 | break; 198 | 199 | /***** CREATE/EDIT/REMOVE/SHOW A SENSOR ****/ 200 | 201 | case 'S': 202 | switch(numArgs){ 203 | 204 | // argument is string with id number of sensor followed by a pin number and 205 | // pullUp indicator (0=LOW/1=HIGH) 206 | case 3: 207 | Sensor::create(stream, p[0],p[1],p[2],1); 208 | break; 209 | 210 | case 1: // argument is a string with id number only 211 | Sensor::remove(stream, p[0]); 212 | break; 213 | 214 | case 0: // no arguments 215 | Sensor::show(stream); 216 | break; 217 | 218 | case 2: // invalid number of arguments 219 | CommManager::send(stream, F("")); 220 | break; 221 | } 222 | 223 | break; 224 | 225 | /***** SHOW STATUS OF ALL SENSORS ****/ 226 | 227 | case 'Q': // 228 | Sensor::status(stream); 229 | break; 230 | 231 | 232 | /***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON MAIN TRACK ****/ 233 | 234 | case 'w': { // 235 | genericResponse response; 236 | 237 | mainTrack->writeCVByteMain(p[0], p[1], p[2], response, stream, POMResponse); 238 | 239 | break; 240 | } 241 | 242 | /***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON MAIN TRACK ****/ 243 | 244 | case 'b': { // 245 | genericResponse response; 246 | 247 | mainTrack->writeCVBitMain(p[0], p[1], p[2], p[3], response, stream, POMResponse); 248 | 249 | break; 250 | } 251 | 252 | /***** WRITE CONFIGURATION VARIABLE BYTE TO ENGINE DECODER ON PROG TRACK ****/ 253 | 254 | case 'W': // 255 | 256 | progTrack->writeCVByte(p[0], p[1], p[2], p[3], stream, cvResponse); 257 | 258 | break; 259 | 260 | /***** WRITE CONFIGURATION VARIABLE BIT TO ENGINE DECODER ON PROG TRACK ****/ 261 | 262 | case 'B': // 263 | 264 | progTrack->writeCVBit(p[0], p[1], p[2], p[3], p[4], stream, cvResponse); 265 | 266 | break; 267 | 268 | /***** READ CONFIGURATION VARIABLE BYTE FROM ENGINE DECODER ON PROG TRACK ****/ 269 | 270 | case 'R': // 271 | progTrack->readCV(p[0], p[1], p[2], stream, cvResponse); 272 | 273 | break; 274 | 275 | /***** READ CONFIGURATION VARIABLE BYTE FROM RAILCOM DECODER ON MAIN TRACK ****/ 276 | 277 | case 'r': { // 278 | genericResponse response; 279 | 280 | mainTrack->readCVByteMain(p[0], p[1], response, stream, POMResponse); 281 | break; 282 | } 283 | 284 | /***** READ 4 CONFIGURATION VARIABLE BYTES FROM RAILCOM DECODER ON MAIN ****/ 285 | 286 | case 'm': { // 287 | genericResponse response; 288 | 289 | mainTrack->readCVBytesMain(p[0], p[1], response, stream, POMResponse); 290 | break; 291 | } 292 | /***** TURN ON POWER FROM MOTOR SHIELD TO TRACKS ****/ 293 | 294 | case '1': // <1> 295 | mainTrack->board->power(true, false); 296 | progTrack->board->power(true, false); 297 | CommManager::broadcast(F("")); 298 | break; 299 | 300 | /***** TURN OFF POWER FROM MOTOR SHIELD TO TRACKS ****/ 301 | 302 | case '0': // <0> 303 | mainTrack->board->power(false, false); 304 | progTrack->board->power(false, false); 305 | CommManager::broadcast(F("")); 306 | break; 307 | 308 | /***** READ MAIN OPERATIONS TRACK CURRENT ****/ 309 | 310 | case 'c': // 311 | // TODO(davidcutting42@gmail.com): When JMRI moves to milliamp reporting, 312 | // fix this. 313 | int currRead; 314 | currRead = mainTrack->board->getCurrentRaw(); 315 | CommManager::send(stream, F(""), currRead); 316 | break; 317 | 318 | /***** READ STATUS OF DCC++ BASE STATION ****/ 319 | 320 | case 's': // 321 | trackPowerCallback(mainTrack->board->getName(), mainTrack->board->getStatus()); 322 | trackPowerCallback(progTrack->board->getName(), progTrack->board->getStatus()); 323 | // TODO(davidcutting42@gmail.com): Add throttle status notifications back 324 | CommManager::send(stream, 325 | F(""), 326 | "CommandStation", BOARD_NAME, VERSION, __DATE__, __TIME__); 327 | CommManager::showInitInfo(); 328 | Turnout::show(stream); 329 | Output::show(stream); 330 | 331 | break; 332 | 333 | /***** STORE SETTINGS IN EEPROM ****/ 334 | 335 | case 'E': // 336 | EEStore::store(); 337 | CommManager::send(stream, F(""), EEStore::eeStore->data.nTurnouts, 338 | EEStore::eeStore->data.nSensors, EEStore::eeStore->data.nOutputs); 339 | break; 340 | 341 | /***** CLEAR SETTINGS IN EEPROM ****/ 342 | 343 | case 'e': // 344 | 345 | EEStore::clear(); 346 | CommManager::send(stream, F("")); 347 | break; 348 | 349 | /***** PRINT CARRIAGE RETURN IN SERIAL MONITOR WINDOW ****/ 350 | 351 | case ' ': // < > 352 | CommManager::send(stream, F("\n")); 353 | break; 354 | } 355 | } 356 | 357 | void DCCEXParser::cvResponse(Print* stream, serviceModeResponse response) { 358 | switch (response.type) 359 | { 360 | case READCV: 361 | case WRITECV: 362 | CommManager::send(stream, F(""), response.callback, 363 | response.callbackSub, response.cv, response.cvValue); 364 | break; 365 | case WRITECVBIT: 366 | CommManager::send(stream, F(""), response.callback, 367 | response.callbackSub, response.cv, response.cvBitNum, response.cvValue); 368 | break; 369 | } 370 | } 371 | 372 | void DCCEXParser::POMResponse(Print* stream, RailcomPOMResponse response) { 373 | CommManager::send(stream, F(""), response.transactionID, response.data); 374 | } 375 | 376 | void DCCEXParser::trackPowerCallback(const char* name, bool status) { 377 | if(status) 378 | CommManager::broadcast(F(""), name); 379 | else 380 | CommManager::broadcast(F(""), name); 381 | } -------------------------------------------------------------------------------- /src/DCC/DCCMain.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * DCCMain.cpp 3 | * 4 | * This file is part of CommandStation. 5 | * 6 | * CommandStation is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * CommandStation is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with CommandStation. If not, see . 18 | */ 19 | 20 | #include "DCCMain.h" 21 | 22 | DCCMain::DCCMain(uint8_t numDevices, Board* board, Railcom* railcom) { 23 | this->board = board; 24 | this->railcom = railcom; 25 | this->numDevices = numDevices; 26 | 27 | // Purge the queue memory 28 | packetQueue.clear(); 29 | 30 | // Allocate memory for the speed table and clear it 31 | speedTable = (Speed *)calloc(numDevices, sizeof(Speed)); 32 | for (int i = 0; i < numDevices; i++) 33 | { 34 | speedTable[i].cab = 0; 35 | speedTable[i].speedCode = 128; 36 | } 37 | } 38 | 39 | void DCCMain::schedulePacket(const uint8_t buffer[], uint8_t byteCount, 40 | uint8_t repeats, uint16_t identifier, PacketType type, uint16_t address) { 41 | 42 | Packet newPacket; 43 | 44 | uint8_t checksum=0; 45 | for (int b=0; b 0) return; // Only update speed if queue is empty 64 | 65 | for (; nextDev < numDevices; nextDev++) { 66 | if (speedTable[nextDev].cab > 0) { 67 | setThrottleResponse response; 68 | setThrottle(speedTable[nextDev].cab, speedTable[nextDev].speedCode, response); 69 | nextDev++; 70 | return; 71 | } 72 | } 73 | for (nextDev = 0; nextDev < numDevices; nextDev++) { 74 | if (speedTable[nextDev].cab > 0) { 75 | setThrottleResponse response; 76 | setThrottle(speedTable[nextDev].cab, speedTable[nextDev].speedCode, response); 77 | nextDev++; 78 | return; 79 | } 80 | } 81 | } 82 | 83 | uint8_t DCCMain::setThrottle(uint16_t addr, uint8_t speedCode, setThrottleResponse& response) { 84 | 85 | uint8_t b[5]; // Packet payload. Save space for checksum byte 86 | uint8_t nB = 0; // Counter for number of bytes in the packet 87 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 88 | 89 | if(addr > 127) { 90 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 91 | railcomAddr = (highByte(addr) | 0xC0) << 8; 92 | } 93 | 94 | b[nB++]=lowByte(addr); 95 | railcomAddr |= lowByte(addr); 96 | b[nB++]=0x3F; // 128-step speed control byte 97 | b[nB++]=speedCode; 98 | 99 | incrementCounterID(); 100 | schedulePacket(b, nB, 0, counterID, kThrottleType, railcomAddr); 101 | 102 | updateSpeedTable(addr, speedCode); 103 | 104 | response.device = addr; 105 | response.speed = speedCode; 106 | response.transactionID = counterID; 107 | 108 | return ERR_OK; 109 | } 110 | 111 | uint8_t DCCMain::setFunction(uint16_t addr, uint8_t byte1, 112 | genericResponse& response) { 113 | 114 | uint8_t b[4]; // Packet payload. Save space for checksum byte 115 | uint8_t nB = 0; // Counter for number of bytes in the packet 116 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 117 | 118 | if(addr > 127) { 119 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 120 | railcomAddr = (highByte(addr) | 0xC0) << 8; 121 | } 122 | 123 | b[nB++] = lowByte(addr); 124 | railcomAddr |= lowByte(addr); 125 | 126 | b[nB++] = (byte1 | 0x80) & 0xBF; 127 | 128 | incrementCounterID(); 129 | // Repeat the packet four times (one plus 3 repeats) 130 | schedulePacket(b, nB, 3, counterID, kFunctionType, railcomAddr); 131 | 132 | response.transactionID = counterID; 133 | 134 | return ERR_OK; 135 | } 136 | 137 | uint8_t DCCMain::setFunction(uint16_t addr, uint8_t byte1, uint8_t byte2, 138 | genericResponse& response) { 139 | 140 | uint8_t b[4]; // Packet payload. Save space for checksum byte 141 | uint8_t nB = 0; // Counter for number of bytes in the packet 142 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 143 | 144 | if(addr > 127) { 145 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 146 | railcomAddr = (highByte(addr) | 0xC0) << 8; 147 | } 148 | 149 | b[nB++] = lowByte(addr); 150 | railcomAddr |= lowByte(addr); 151 | 152 | // for safety this guarantees that first byte will either be 0xDE 153 | // (for F13-F20) or 0xDF (for F21-F28) 154 | b[nB++]=(byte1 | 0xDE) & 0xDF; 155 | b[nB++]=byte2; 156 | 157 | incrementCounterID(); 158 | // Repeat the packet four times (one plus 3 repeats) 159 | schedulePacket(b, nB, 3, counterID, kFunctionType, railcomAddr); 160 | 161 | response.transactionID = counterID; 162 | 163 | return ERR_OK; 164 | } 165 | 166 | uint8_t DCCMain::setAccessory(uint16_t addr, uint8_t number, bool activate, 167 | genericResponse& response) { 168 | 169 | uint8_t b[3]; // Packet payload. Save space for checksum byte 170 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 171 | 172 | // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least 173 | // signifcant bits of accessory address 174 | b[0] = (addr % 64) + 128; 175 | // second byte is of the form 1AAACDDD, where C should be 1, and the least 176 | // significant D represent activate/deactivate 177 | b[1] = ((((addr / 64) % 8) << 4) + (number % 4 << 1) + activate % 2) ^ 0xF8; 178 | railcomAddr = (b[0] << 8) | b[1]; 179 | 180 | incrementCounterID(); 181 | // Repeat the packet four times (one plus 3 repeats) 182 | schedulePacket(b, 2, 3, counterID, kAccessoryType, railcomAddr); 183 | 184 | response.transactionID = counterID; 185 | 186 | return ERR_OK; 187 | } 188 | 189 | uint8_t DCCMain::writeCVByteMain(uint16_t addr, uint16_t cv, uint8_t bValue, 190 | genericResponse& response, Print* stream, void (*POMCallback)(Print*, RailcomPOMResponse)) { 191 | 192 | uint8_t b[6]; // Packet payload. Save space for checksum byte 193 | uint8_t nB = 0; // Counter for number of bytes in the packet 194 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 195 | 196 | cv--; // actual CV addresses are cv-1 (0-1023) 197 | 198 | if(addr > 127) { 199 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 200 | railcomAddr = (highByte(addr) | 0xC0) << 8; 201 | } 202 | 203 | b[nB++] = lowByte(addr); 204 | railcomAddr |= lowByte(addr); 205 | 206 | // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 207 | b[nB++] = 0xEC + (highByte(cv) & 0x03); 208 | b[nB++] = lowByte(cv); 209 | b[nB++] = bValue; 210 | 211 | railcom->setPOMResponseCallback(stream, POMCallback); 212 | 213 | incrementCounterID(); 214 | // Repeat the packet four times (one plus 3 repeats) 215 | schedulePacket(b, nB, 3, counterID, kPOMByteWriteType, railcomAddr); 216 | 217 | response.transactionID = counterID; 218 | 219 | return ERR_OK; 220 | } 221 | 222 | uint8_t DCCMain::writeCVBitMain(uint16_t addr, uint16_t cv, uint8_t bNum, 223 | uint8_t bValue, genericResponse& response, Print *stream, 224 | void (*POMCallback)(Print*, RailcomPOMResponse)) { 225 | 226 | uint8_t b[6]; // Packet payload. Save space for checksum byte 227 | uint8_t nB = 0; // Counter for number of bytes in the packet 228 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 229 | 230 | cv--; // actual CV addresses are cv-1 (0-1023) 231 | 232 | bValue = bValue % 2; 233 | bNum = bNum % 8; 234 | 235 | if(addr > 127) { 236 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 237 | railcomAddr = (highByte(addr) | 0xC0) << 8; 238 | } 239 | 240 | b[nB++] = lowByte(addr); 241 | railcomAddr |= lowByte(addr); 242 | 243 | // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 244 | b[nB++] = 0xE8 + (highByte(cv) & 0x03); 245 | b[nB++] = lowByte(cv); 246 | b[nB++] = 0xF0 + (bValue * 8) + bNum; 247 | 248 | railcom->setPOMResponseCallback(stream, POMCallback); 249 | 250 | incrementCounterID(); 251 | schedulePacket(b, nB, 4, counterID, kPOMBitWriteType, railcomAddr); 252 | 253 | response.transactionID = counterID; 254 | 255 | return ERR_OK; 256 | } 257 | 258 | uint8_t DCCMain::readCVByteMain(uint16_t addr, uint16_t cv, 259 | genericResponse& response, Print *stream, void (*POMCallback)(Print*, RailcomPOMResponse)) { 260 | 261 | uint8_t b[6]; // Packet payload. Save space for checksum byte 262 | uint8_t nB = 0; // Counter for number of bytes in the packet 263 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 264 | 265 | cv--; // actual CV addresses are cv-1 (0-1023) 266 | 267 | if(addr > 127) { 268 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 269 | railcomAddr = (highByte(addr) | 0xC0) << 8; 270 | } 271 | 272 | b[nB++] = lowByte(addr); 273 | railcomAddr |= lowByte(addr); 274 | 275 | // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 276 | b[nB++] = 0xE4 + (highByte(cv) & 0x03); 277 | b[nB++] = lowByte(cv); 278 | b[nB++] = 0; // For some reason the railcom spec leaves an empty byte 279 | 280 | railcom->setPOMResponseCallback(stream, POMCallback); 281 | 282 | incrementCounterID(); 283 | // Repeat the packet four times (one plus 3 repeats) 284 | schedulePacket(b, nB, 3, counterID, kPOMReadType, railcomAddr); 285 | 286 | response.transactionID = counterID; 287 | 288 | return ERR_OK; 289 | 290 | } 291 | 292 | uint8_t DCCMain::readCVBytesMain(uint16_t addr, uint16_t cv, 293 | genericResponse& response, Print *stream, void (*POMCallback)(Print*, RailcomPOMResponse)) { 294 | 295 | uint8_t b[5]; // Packet payload. Save space for checksum byte 296 | uint8_t nB = 0; // Counter for number of bytes in the packet 297 | uint16_t railcomAddr = 0; // For detecting the railcom instruction type 298 | 299 | cv--; // actual CV addresses are cv-1 (0-1023) 300 | 301 | if(addr > 127) { 302 | b[nB++] = highByte(addr) | 0xC0; // convert address to packet format 303 | railcomAddr = (highByte(addr) | 0xC0) << 8; 304 | } 305 | 306 | b[nB++] = lowByte(addr); 307 | railcomAddr |= lowByte(addr); 308 | 309 | // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 310 | b[nB++] = 0xE0 + (highByte(cv) & 0x03); 311 | b[nB++] = lowByte(cv); 312 | 313 | railcom->setPOMResponseCallback(stream, POMCallback); 314 | 315 | incrementCounterID(); 316 | // Repeat the packet four times (one plus 3 repeats) 317 | schedulePacket(b, nB, 3, counterID, kPOMLongReadType, railcomAddr); 318 | 319 | response.transactionID = counterID; 320 | 321 | return ERR_OK; 322 | 323 | } 324 | 325 | void DCCMain::updateSpeedTable(uint8_t cab, uint8_t speedCode) { 326 | if(cab == 0) { 327 | // broadcast to all locomotives 328 | for(int dev = 0; dev < numDevices; dev++) 329 | speedTable[dev].speedCode = speedCode; 330 | return; 331 | } 332 | 333 | int reg = lookupSpeedTable(cab); 334 | if(reg >= 0) speedTable[reg].speedCode = speedCode; 335 | } 336 | 337 | int DCCMain::lookupSpeedTable(uint8_t cab) { 338 | int firstEmpty = numDevices; 339 | int reg; 340 | for(reg = 0; reg < numDevices; reg++) { 341 | if(speedTable[reg].cab == cab) break; // We found the loco, break 342 | 343 | // We didn't find the loco, but this slot is empty so update firstEmpty 344 | if(speedTable[reg].cab == 0 && firstEmpty == numDevices) firstEmpty = reg; 345 | } 346 | 347 | if(reg == numDevices) reg = firstEmpty; // If we got all the way to the end of the search, it isn't in the system 348 | if(reg >= numDevices) return -1; // Not enough locos 349 | 350 | if(reg==firstEmpty) { // If this cab isn't already in the system, add it 351 | speedTable[reg].cab = cab; 352 | speedTable[reg].speedCode = 128; 353 | } 354 | 355 | return reg; 356 | } 357 | 358 | void DCCMain::forgetDevice(uint8_t cab) { // removes any speed reminders for this loco 359 | int reg = lookupSpeedTable(cab); 360 | 361 | if(reg >= 0) speedTable[reg].cab = 0; 362 | } 363 | 364 | void DCCMain::forgetAllDevices() { // removes all speed reminders 365 | for(int i = 0; i < numDevices; i++) speedTable[i].cab = 0; 366 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------