Compare commits
19 Commits
GBIDEC
...
Accessory-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b52381fa4 | ||
|
|
5f1e271f2c | ||
|
|
6f91d4f7d4 | ||
|
|
b35c1fbbd0 | ||
|
|
f59048383f | ||
|
|
6333abf691 | ||
|
|
afe488d792 | ||
|
|
61a730bf82 | ||
|
|
b0b27ce0cc | ||
|
|
c3dc28479a | ||
|
|
a05c12ce95 | ||
|
|
0a72fc610b | ||
|
|
ad5ce96253 | ||
|
|
1e81b95044 | ||
|
|
84fdf45dce | ||
|
|
037070b899 | ||
|
|
eb15c25491 | ||
|
|
681b362811 | ||
|
|
f5d7e9b8c3 |
55
.astylerc
Normal file
55
.astylerc
Normal file
@@ -0,0 +1,55 @@
|
||||
# http://astyle.sourceforge.net/astyle.html#_style=allman
|
||||
# use the allman style for braces
|
||||
--style=allman
|
||||
|
||||
# http://astyle.sourceforge.net/astyle.html#_lineend
|
||||
# newlines for all line endings
|
||||
--lineend=linux
|
||||
|
||||
# http://astyle.sourceforge.net/astyle.html#_indent=spaces
|
||||
# 4 space indents
|
||||
--indent=spaces=4
|
||||
|
||||
# http://astyle.sourceforge.net/astyle.html#_add-braces
|
||||
# don't leave any unbraced one-line blocks by changing:
|
||||
# if (thing)
|
||||
# statement;
|
||||
#
|
||||
# to
|
||||
# if (thing)
|
||||
# {
|
||||
# statement;
|
||||
# }
|
||||
#
|
||||
# --add-braces
|
||||
|
||||
# http://astyle.sourceforge.net/astyle.html#_indent-preproc-cond
|
||||
# line up #if/#endif with the code
|
||||
#
|
||||
# statement;
|
||||
# #if SOMETHING
|
||||
# statement;
|
||||
# #endif
|
||||
#
|
||||
--indent-preproc-cond
|
||||
|
||||
# http://astyle.sourceforge.net/astyle.html#_indent-preproc-block
|
||||
# indent top level preprocessor block contents
|
||||
#
|
||||
# #ifdef __ARDUINO__
|
||||
# #include "Arduino.h"
|
||||
# #endif
|
||||
#
|
||||
--indent-preproc-block
|
||||
|
||||
# normalize spacing around parenthese
|
||||
# http://astyle.sourceforge.net/astyle.html#_unpad-paren
|
||||
# first, remove all whitespace on either side of a paren
|
||||
--unpad-paren
|
||||
# http://astyle.sourceforge.net/astyle.html#_pad-paren-out
|
||||
# then add a space on the "outer" side of a paren
|
||||
--pad-paren-out
|
||||
|
||||
# http://astyle.sourceforge.net/astyle.html#_max-code-length
|
||||
# we've got big monitors these days....
|
||||
--max-code-length=180
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
||||
.development
|
||||
*.zip
|
||||
*.orig
|
||||
*~
|
||||
*.txt
|
||||
|
||||
959
NmraDcc.cpp
959
NmraDcc.cpp
File diff suppressed because it is too large
Load Diff
152
NmraDcc.h
152
NmraDcc.h
@@ -49,9 +49,9 @@
|
||||
//#define NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#include "Arduino.h"
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
||||
#ifndef NMRADCC_IS_IN
|
||||
@@ -81,6 +81,10 @@ typedef struct
|
||||
#define MAN_ID_DIY 0x0D
|
||||
#define MAN_ID_SILICON_RAILWAY 0x21
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
#define EEPROM_COMMIT_DELAY_MS 3000
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// This section contains the Product/Version Id Codes for projects
|
||||
//
|
||||
@@ -94,6 +98,10 @@ typedef struct
|
||||
|
||||
// Product/Version Id Codes allocated under: MAN_ID_DIY
|
||||
|
||||
#define DEFAULT_MULTIFUNCTION_DECODER_ADDRESS 3
|
||||
#define DEFAULT_ACCESSORY_DECODER_ADDRESS 1
|
||||
|
||||
|
||||
// Standard CV Addresses
|
||||
#define CV_ACCESSORY_DECODER_ADDRESS_LSB 1
|
||||
#define CV_ACCESSORY_DECODER_ADDRESS_MSB 9
|
||||
@@ -102,9 +110,14 @@ typedef struct
|
||||
#define CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB 17
|
||||
#define CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB 18
|
||||
|
||||
#define CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(x) (x & 0xFF)
|
||||
#define CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(x) (((x>>8) & 0x7F) + 192)
|
||||
|
||||
#define CV_VERSION_ID 7
|
||||
#define CV_MANUFACTURER_ID 8
|
||||
#define CV_29_CONFIG 29
|
||||
#define CV_MANUFACTURER_START 33
|
||||
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <esp_spi_flash.h>
|
||||
@@ -118,11 +131,15 @@ typedef struct
|
||||
#undef ALLOW_NESTED_IRQ // This is done with NVIC on STM32
|
||||
#define PRIO_DCC_IRQ 9
|
||||
#define PRIO_SYSTIC 8 // MUST be higher priority than DCC Irq
|
||||
#elif defined(ARDUINO_ARCH_RP2040)
|
||||
#define MAXCV 256 // todo: maybe somebody knows a good define for it
|
||||
|
||||
#else
|
||||
#define MAXCV E2END // the upper limit of the CV value currently defined to max memory.
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
CV29_LOCO_DIR = 0b00000001, /** bit 0: Locomotive Direction: "0" = normal, "1" = reversed */
|
||||
CV29_F0_LOCATION = 0b00000010, /** bit 1: F0 location: "0" = bit 4 in Speed and Direction instructions, "1" = bit 4 in function group one instruction */
|
||||
CV29_APS = 0b00000100, /** bit 2: Alternate Power Source (APS) "0" = NMRA Digital only, "1" = Alternate power source set by CV12 */
|
||||
@@ -133,20 +150,23 @@ typedef enum {
|
||||
CV29_ACCESSORY_DECODER = 0b10000000, /** bit 7: "0" = Multi-Function Decoder Mode "1" = Accessory Decoder Mode */
|
||||
} CV_29_BITS;
|
||||
|
||||
typedef enum {
|
||||
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
typedef enum
|
||||
{
|
||||
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
SPEED_STEP_14 = 15, /**< ESTOP=0, 1 to 15 */
|
||||
#endif
|
||||
#endif
|
||||
SPEED_STEP_28 = 29, /**< ESTOP=0, 1 to 29 */
|
||||
SPEED_STEP_128 = 127 /**< ESTOP=0, 1 to 127 */
|
||||
} DCC_SPEED_STEPS;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
DCC_DIR_REV = 0, /** The locomotive to go in the reverse direction */
|
||||
DCC_DIR_FWD = 1, /** The locomotive should move in the forward direction */
|
||||
} DCC_DIRECTION;
|
||||
|
||||
typedef enum {
|
||||
typedef enum
|
||||
{
|
||||
DCC_ADDR_SHORT, /** Short address is used. The range is 0 to 127. */
|
||||
DCC_ADDR_LONG, /** Long Address is used. The range is 1 to 10239 */
|
||||
} DCC_ADDR_TYPE;
|
||||
@@ -158,9 +178,9 @@ typedef enum
|
||||
FN_9_12,
|
||||
FN_13_20,
|
||||
FN_21_28,
|
||||
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
FN_0 /** function light is controlled by base line package (14 speed steps) */
|
||||
#endif
|
||||
#endif
|
||||
} FN_GROUP;
|
||||
|
||||
#define FN_BIT_00 0x10
|
||||
@@ -197,33 +217,40 @@ typedef enum
|
||||
#define FN_BIT_27 0x40
|
||||
#define FN_BIT_28 0x80
|
||||
|
||||
//#define DCC_DBGVAR
|
||||
// #define DCC_DBGVAR
|
||||
#ifdef DCC_DBGVAR
|
||||
typedef struct countOf_t {
|
||||
typedef struct countOf_t
|
||||
{
|
||||
unsigned long Tel;
|
||||
unsigned long Err;
|
||||
}countOf_t ;
|
||||
} countOf_t ;
|
||||
|
||||
extern struct countOf_t countOf;
|
||||
#endif
|
||||
|
||||
class NmraDcc
|
||||
{
|
||||
private:
|
||||
private:
|
||||
DCC_MSG Msg ;
|
||||
|
||||
public:
|
||||
public:
|
||||
NmraDcc();
|
||||
|
||||
// Flag values to be logically ORed together and passed into the init() method
|
||||
#define FLAGS_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address
|
||||
#define FLAGS_AUTO_FACTORY_DEFAULT 0x02 // Call notifyCVResetFactoryDefault() if CV 7 & 8 == 255
|
||||
#define FLAGS_SETCV_CALLED 0x10 // only used internally !!
|
||||
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
||||
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
||||
#define FLAGS_EXTENDED_ADDRESS_MODE 0x20 // CV 29/541 bit 5 0 = 9-bit Board or Decoder Addressing mode, 1 = 11-bit Extended Address using middle 2 DD of the CDDD bits for the extra 2 bits
|
||||
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6 0 = Board or Extended Addressing Mode, 1 = Signal Decoder Output Addressing Mode
|
||||
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7 0 = Multifunction Decoder, 1 = Accessory Decoder
|
||||
|
||||
#define FLAGS_MULTIFUNCTION_DECODER ()
|
||||
#define FLAGS_BASIC_ACCESSORY_DECODER (FLAGS_DCC_ACCESSORY_DECODER)
|
||||
#define FLAGS_EXTENDED_ACCESSORY_DECODER (FLAGS_DCC_ACCESSORY_DECODER | FLAGS_EXTENDED_ADDRESS_MODE)
|
||||
#define FLAGS_SIGNAL_ACCESSORY_DECODER (FLAGS_DCC_ACCESSORY_DECODER | FLAGS_OUTPUT_ADDRESS_MODE)
|
||||
|
||||
// Flag Bits that are cloned from CV29 relating the DCC Accessory Decoder
|
||||
#define FLAGS_CV29_BITS (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
|
||||
#define FLAGS_CV29_BITS (FLAGS_EXTENDED_ACCESSORY_DECODER | FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
|
||||
|
||||
|
||||
/*+
|
||||
@@ -237,7 +264,7 @@ class NmraDcc
|
||||
* Returns:
|
||||
* None.
|
||||
*/
|
||||
void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
void pin (uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
|
||||
/*+
|
||||
* pin() is called from setup() and sets up the pin used to receive DCC packets.
|
||||
@@ -250,9 +277,9 @@ class NmraDcc
|
||||
* Returns:
|
||||
* None.
|
||||
*/
|
||||
#ifdef digitalPinToInterrupt
|
||||
void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
#endif
|
||||
#ifdef digitalPinToInterrupt
|
||||
void pin (uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
#endif
|
||||
|
||||
/*+
|
||||
* init() is called from setup() after the pin() command is called.
|
||||
@@ -275,7 +302,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* Returns:
|
||||
* None.
|
||||
*/
|
||||
void init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
|
||||
void init (uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV);
|
||||
|
||||
/*+
|
||||
* initAccessoryDecoder() is called from setup() for accessory decoders.
|
||||
@@ -292,7 +319,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* Returns:
|
||||
* None.
|
||||
*/
|
||||
void initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
|
||||
void initAccessoryDecoder (uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV);
|
||||
|
||||
/*+
|
||||
* process() is called from loop() to process DCC packets.
|
||||
@@ -318,7 +345,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* since nothing will have been set in that EEPROM position.
|
||||
* Calls notifyCVRead() if it is defined.
|
||||
*/
|
||||
uint8_t getCV( uint16_t CV );
|
||||
uint8_t getCV (uint16_t CV);
|
||||
|
||||
/*+
|
||||
* setCV() sets the value of a CV.
|
||||
@@ -333,7 +360,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* Calls notifyCVWrite() if it is defined.
|
||||
* Calls notifyCVChange() if the value is changed by this call.
|
||||
*/
|
||||
uint8_t setCV( uint16_t CV, uint8_t Value);
|
||||
uint8_t setCV (uint16_t CV, uint8_t Value);
|
||||
|
||||
/*+
|
||||
* setAccDecDCCAddrNextReceived() enables/disables the setting of the board address from the next received turnout command
|
||||
@@ -343,7 +370,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
*
|
||||
* Returns:
|
||||
*/
|
||||
void setAccDecDCCAddrNextReceived(uint8_t enable);
|
||||
void setAccDecDCCAddrNextReceived (uint8_t enable);
|
||||
|
||||
/*+
|
||||
* isSetCVReady() returns 1 if EEPROM is ready to write.
|
||||
@@ -358,7 +385,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* Note: It returns the value returned by notifyIsSetCVReady() if it is defined.
|
||||
* Calls notifyIsSetCVReady() if it is defined.
|
||||
*/
|
||||
uint8_t isSetCVReady( void );
|
||||
uint8_t isSetCVReady (void);
|
||||
|
||||
/*+
|
||||
* getAddr() return the currently active decoder address.
|
||||
@@ -371,7 +398,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* Adr - The current decoder address based on decoder type(Multifunction, Accessory)
|
||||
* and short or long address selection for Multifunction decoders.
|
||||
*/
|
||||
uint16_t getAddr(void);
|
||||
uint16_t getAddr (void);
|
||||
|
||||
/*+
|
||||
* getX() return debugging data if DCC_DEBUG is defined.
|
||||
@@ -393,13 +420,13 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* is not handling packets fast enough or some other error is occurring.
|
||||
*/
|
||||
// #define DCC_DEBUG
|
||||
#ifdef DCC_DEBUG
|
||||
uint8_t getIntCount(void);
|
||||
uint8_t getTickCount(void);
|
||||
uint8_t getBitCount(void);
|
||||
uint8_t getState(void);
|
||||
uint8_t getNestedIrqCount(void);
|
||||
#endif
|
||||
#ifdef DCC_DEBUG
|
||||
uint8_t getIntCount (void);
|
||||
uint8_t getTickCount (void);
|
||||
uint8_t getBitCount (void);
|
||||
uint8_t getState (void);
|
||||
uint8_t getNestedIrqCount (void);
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
@@ -408,7 +435,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
************************************************************************************/
|
||||
|
||||
#if defined (__cplusplus)
|
||||
extern "C" {
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*+
|
||||
@@ -421,7 +448,7 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak));
|
||||
extern void notifyDccReset (uint8_t hardReset) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccIdle() Callback for a DCC idle command.
|
||||
@@ -432,7 +459,7 @@ extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccIdle(void) __attribute__ ((weak));
|
||||
extern void notifyDccIdle (void) __attribute__ ( (weak));
|
||||
|
||||
|
||||
/*+
|
||||
@@ -453,7 +480,7 @@ extern void notifyDccIdle(void) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) __attribute__ ((weak));
|
||||
extern void notifyDccSpeed (uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccSpeedRaw() Callback for a multifunction decoder speed command.
|
||||
@@ -467,7 +494,7 @@ extern void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Sp
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ((weak));
|
||||
extern void notifyDccSpeedRaw (uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccFunc() Callback for a multifunction decoder function command.
|
||||
@@ -489,11 +516,11 @@ extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak));
|
||||
extern void notifyDccFunc (uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccAccTurnoutBoard() Board oriented callback for a turnout accessory decoder.
|
||||
* Most useful when CV29_OUTPUT_ADDRESS_MODE is not set.
|
||||
* Most useful when CV29_OUTPUT_ADDRESS_MODE is NOT set.
|
||||
* Decoders of this type have 4 paired turnout outputs per board.
|
||||
* OutputPower is 1 if the power is on, and 0 otherwise.
|
||||
*
|
||||
@@ -511,11 +538,10 @@ extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP Fu
|
||||
* None
|
||||
*/
|
||||
|
||||
extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
|
||||
extern void notifyDccAccTurnoutBoard (uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower) __attribute__ ( (weak));
|
||||
/*+
|
||||
* notifyDccAccTurnoutOutput() Output oriented callback for a turnout accessory decoder.
|
||||
* Most useful when CV29_OUTPUT_ADDRESS_MODE is not set.
|
||||
* Decoders of this type have 4 paired turnout outputs per board.
|
||||
* Most useful when CV29_OUTPUT_ADDRESS_MODE IS set.
|
||||
* OutputPower is 1 if the power is on, and 0 otherwise.
|
||||
*
|
||||
* Inputs:
|
||||
@@ -530,7 +556,7 @@ extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair,
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
|
||||
extern void notifyDccAccTurnoutOutput (uint16_t Addr, uint8_t Direction, uint8_t OutputPower) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccAccBoardAddrSet() Board oriented callback for a turnout accessory decoder.
|
||||
@@ -546,7 +572,7 @@ extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((weak));
|
||||
extern void notifyDccAccBoardAddrSet (uint16_t BoardAddr) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccAccOutputAddrSet() Output oriented callback for a turnout accessory decoder.
|
||||
@@ -562,7 +588,7 @@ extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((wea
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak));
|
||||
extern void notifyDccAccOutputAddrSet (uint16_t Addr) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccSigOutputState() Callback for a signal aspect accessory decoder.
|
||||
@@ -575,7 +601,7 @@ extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribute__ ((weak));
|
||||
extern void notifyDccSigOutputState (uint16_t Addr, uint8_t State) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyDccMsg() Raw DCC packet callback.
|
||||
@@ -590,7 +616,7 @@ extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribut
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
||||
extern void notifyDccMsg (DCC_MSG * Msg) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyCVValid() Callback to determine if a given CV is valid.
|
||||
@@ -608,7 +634,7 @@ extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
||||
* 1 - CV is valid.
|
||||
* 0 - CV is not valid.
|
||||
*/
|
||||
extern uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((weak));
|
||||
extern uint8_t notifyCVValid (uint16_t CV, uint8_t Writable) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyCVRead() Callback to read a CV.
|
||||
@@ -624,7 +650,7 @@ extern uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((we
|
||||
* Returns:
|
||||
* Value - Value of the CV.
|
||||
*/
|
||||
extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
|
||||
extern uint8_t notifyCVRead (uint16_t CV) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyCVWrite() Callback to write a value to a CV.
|
||||
@@ -641,7 +667,7 @@ extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* Value - Value of the CV.
|
||||
*/
|
||||
extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
extern uint8_t notifyCVWrite (uint16_t CV, uint8_t Value) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyIsSetCVReady() Callback to to determine if CVs can be written.
|
||||
@@ -661,7 +687,7 @@ extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak))
|
||||
* 1 - CV is ready to be written.
|
||||
* 0 - CV is not ready to be written.
|
||||
*/
|
||||
extern uint8_t notifyIsSetCVReady(void) __attribute__ ((weak));
|
||||
extern uint8_t notifyIsSetCVReady (void) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyCVChange() Called when a CV value is changed.
|
||||
@@ -679,8 +705,8 @@ extern uint8_t notifyIsSetCVReady(void) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
extern void notifyDccCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
extern void notifyCVChange (uint16_t CV, uint8_t Value) __attribute__ ( (weak));
|
||||
extern void notifyDccCVChange (uint16_t CV, uint8_t Value) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyCVResetFactoryDefault() Called when CVs must be reset.
|
||||
@@ -697,7 +723,7 @@ extern void notifyDccCVChange( uint16_t CV, uint8_t Value) __attribute__ ((we
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
|
||||
extern void notifyCVResetFactoryDefault (void) __attribute__ ( (weak));
|
||||
|
||||
/*+
|
||||
* notifyCVAck() Called when a CV write must be acknowledged.
|
||||
@@ -710,7 +736,7 @@ extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyCVAck(void) __attribute__ ((weak));
|
||||
extern void notifyCVAck (void) __attribute__ ( (weak));
|
||||
/*+
|
||||
* notifyAdvancedCVAck() Called when a CV write must be acknowledged via Advanced Acknowledgement.
|
||||
* This callback must send the Advanced Acknowledgement via RailComm.
|
||||
@@ -721,7 +747,7 @@ extern void notifyCVAck(void) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyAdvancedCVAck(void) __attribute__ ((weak));
|
||||
extern void notifyAdvancedCVAck (void) __attribute__ ( (weak));
|
||||
/*+
|
||||
* notifyServiceMode(bool) Called when state of 'inServiceMode' changes
|
||||
*
|
||||
@@ -731,12 +757,12 @@ extern void notifyAdvancedCVAck(void) __attribute__ ((weak));
|
||||
* Returns:
|
||||
* None
|
||||
*/
|
||||
extern void notifyServiceMode(bool) __attribute__ ((weak));
|
||||
extern void notifyServiceMode (bool) __attribute__ ( (weak));
|
||||
|
||||
// Deprecated, only for backward compatibility with version 1.4.2.
|
||||
// Don't use in new designs. These functions may be dropped in future versions
|
||||
extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak));
|
||||
extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak));
|
||||
extern void notifyDccAccState (uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State) __attribute__ ( (weak));
|
||||
extern void notifyDccSigState (uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ( (weak));
|
||||
|
||||
#if defined (__cplusplus)
|
||||
}
|
||||
|
||||
11
README.md
11
README.md
@@ -10,3 +10,14 @@ The library currently supports the AVR ATTiny84/85 & ATMega88/168/328/32u4 and T
|
||||
extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State )
|
||||
extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State)
|
||||
|
||||
|
||||
|
||||
Developers:
|
||||
Use of the supplied git pre-commit hook is encouraged. This will require installation of the 'astyle' package for formatting source file.
|
||||
See http://astyle.sourceforge.net for details on this package.
|
||||
|
||||
On Linux or Mac development machines, run the following command after you clone the repository:
|
||||
|
||||
ln -s support/pre-commit .git/hooks/pre-commit
|
||||
|
||||
Reformatting the source code to the preferred style is easy using astyle. Just run 'astyle --options=.astylerc NmraDcc.h NmraDcc.cpp'
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#include <AccelStepper.h>
|
||||
#include <NmraDcc.h>
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
// The lines below define the pins used to connect to the A4988 driver module
|
||||
#define A4988_STEP_PIN 4
|
||||
#define A4988_DIRECTION_PIN 5
|
||||
@@ -244,7 +247,13 @@ void setupDCCDecoder()
|
||||
Serial.println(F("Setting up DCC Decorder..."));
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(0, 2, 1);
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
NmraDcc Dcc ;
|
||||
DCC_MSG Packet ;
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
struct CVPair
|
||||
{
|
||||
uint16_t CV;
|
||||
@@ -15,8 +18,8 @@ struct CVPair
|
||||
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, 1},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, DEFAULT_ACCESSORY_DECODER_ADDRESS & 0xFF},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, DEFAULT_ACCESSORY_DECODER_ADDRESS >> 8},
|
||||
};
|
||||
|
||||
uint8_t FactoryDefaultCVIndex = 0;
|
||||
@@ -92,6 +95,8 @@ void notifyDccSigOutputState( uint16_t Addr, uint8_t State)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
elapsedMillis millisWaitedForUSB = 0;
|
||||
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
|
||||
|
||||
// Configure the DCC CV Programing ACK pin for an output
|
||||
pinMode( DccAckPin, OUTPUT );
|
||||
@@ -99,7 +104,13 @@ void setup()
|
||||
Serial.println("NMRA DCC Example 1");
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(0, 2, 1);
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
// Un-Comment the line below to Enable DCC ACK for Service Mode Programming Read CV Capablilty
|
||||
//#define ENABLE_DCC_ACK 15 // This is A1 on the Iowa Scaled Engineering ARD-DCCSHIELD DCC Shield
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
#define NUM_TURNOUTS 8 // Set Number of Turnouts (Pairs of Pins)
|
||||
#define ACTIVE_OUTPUT_STATE LOW // Set the ACTIVE State of the output to Drive the Turnout motor electronics HIGH or LOW
|
||||
|
||||
@@ -40,8 +43,8 @@ struct CVPair
|
||||
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, 1}, // CV 1 Board Address (lower 6 bits)
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0}, // CV 9 Board Address (Upper 3 bits)
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, DEFAULT_ACCESSORY_DECODER_ADDRESS & 0xFF},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, DEFAULT_ACCESSORY_DECODER_ADDRESS >> 8},
|
||||
{CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME, 50}, // x 10mS for the output pulse duration
|
||||
{CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME, 30}, // x 10mS for the CDU recharge delay time
|
||||
{CV_ACCESSORY_DECODER_ACTIVE_STATE, ACTIVE_OUTPUT_STATE},
|
||||
@@ -119,7 +122,13 @@ void setup()
|
||||
Serial.begin(115200);
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(0, 2, 1);
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, DCC_DECODER_VERSION_NUM, CV29_ACCESSORY_DECODER, 0 );
|
||||
|
||||
@@ -0,0 +1,377 @@
|
||||
// This Example shows how to use the library as a DCC Accessory Decoder to drive 16 Servos to control Turnouts
|
||||
#include <NmraDcc.h>
|
||||
#include <Servo.h>
|
||||
#include <SlowMotionServo.h>
|
||||
#include <elapsedMillis.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
// You can print every DCC packet by un-commenting the line below
|
||||
//#define NOTIFY_DCC_MSG
|
||||
|
||||
// You can print every notifyDccAccTurnoutOutput call-back by un-commenting the line below
|
||||
#define NOTIFY_TURNOUT_MSG
|
||||
|
||||
// You can also print other Debug Messages uncommenting the line below
|
||||
#define DEBUG_MSG
|
||||
|
||||
// Un-Comment the line below to force CVs to be written to the Factory Default values
|
||||
// defined in the FactoryDefaultCVs below on Start-Up
|
||||
//#define FORCE_RESET_FACTORY_DEFAULT_CV
|
||||
|
||||
// Un-Comment the line below to Enable DCC ACK for Service Mode Programming Read CV Capablilty
|
||||
#define ENABLE_DCC_ACK 27 // This is A1 on the Iowa Scaled Engineering ARD-DCCSHIELD DCC Shield
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 26
|
||||
|
||||
#define NUM_SERVOS 16 // Set Number of Servos
|
||||
|
||||
#define DCC_DECODER_VERSION_NUM 12 // Set the Decoder Version - Used by JMRI to Identify the decoder
|
||||
|
||||
#define EEPROM_COMMIT_DELAY_MS 3000
|
||||
|
||||
struct CVPair
|
||||
{
|
||||
uint16_t CV;
|
||||
uint8_t Value;
|
||||
};
|
||||
|
||||
#define CV_VALUE_SERVO_DETACH_MILLIS 0 // CV Default Value for the delay in ms x 10ms to Detach the servo signal to stop chattering
|
||||
#define CV_ADDRESS_SERVO_DETACH_MILLIS CV_MANUFACTURER_START
|
||||
|
||||
#define CV_VALUE_SERVO_MOVE_SPEED 40 // CV Default Value for the Serov movement speed x 10
|
||||
#define CV_ADDRESS_SERVO_MOVE_SPEED (CV_MANUFACTURER_START + 1)
|
||||
|
||||
#define CV_ADDRESS_SERVO_INDIVIDUAL_VALUES (CV_MANUFACTURER_START + 2)
|
||||
|
||||
#define CV_ADDRESS_LAST_POS_LSB (CV_MANUFACTURER_START + (NUM_SERVOS * 2 ) + 3)
|
||||
#define CV_ADDRESS_LAST_POS_MSB (CV_MANUFACTURER_START + (NUM_SERVOS * 2 ) + 4)
|
||||
|
||||
#define CV_VALUE_SERVO_MIN_POS 40 // CV Default Value for the Minimum Servo Position as a % of Full Scale
|
||||
#define CV_ADDRESS_SERVO_MIN_POS(x) (CV_ADDRESS_SERVO_INDIVIDUAL_VALUES + (2 * x) + 1)
|
||||
|
||||
#define CV_VALUE_SERVO_MAX_POS 60 // CV Default Value for the Maximum Servo Position as a % of Full Scale
|
||||
#define CV_ADDRESS_SERVO_MAX_POS(x) (CV_ADDRESS_SERVO_INDIVIDUAL_VALUES + (2 * x) + 2)
|
||||
|
||||
// To set the Turnout Addresses for this board you need to change the CV values for CV1 (CV_ACCESSORY_DECODER_ADDRESS_LSB) and
|
||||
// CV9 (CV_ACCESSORY_DECODER_ADDRESS_MSB) in the FactoryDefaultCVs structure below. The Turnout Addresses are defined as:
|
||||
// Base Turnout Address is: ((((CV9 * 64) + CV1) - 1) * 4) + 1
|
||||
// With NUM_TURNOUTS 8 (above) a CV1 = 1 and CV9 = 0, the Turnout Addresses will be 1..8, for CV1 = 2 the Turnout Address is 5..12
|
||||
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, DEFAULT_ACCESSORY_DECODER_ADDRESS & 0xFF},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, DEFAULT_ACCESSORY_DECODER_ADDRESS >> 8},
|
||||
{CV_ADDRESS_SERVO_DETACH_MILLIS, CV_VALUE_SERVO_DETACH_MILLIS}, // 0 = don't Detach else the Detach will occur after the CV Value x 10ms
|
||||
{CV_ADDRESS_SERVO_MOVE_SPEED, CV_VALUE_SERVO_MOVE_SPEED}, // 0 = don't Detach else the Detach will occur after the CV Value x 10ms
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(0), CV_VALUE_SERVO_MIN_POS}, // Servo 0
|
||||
{CV_ADDRESS_SERVO_MAX_POS(0), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(1), CV_VALUE_SERVO_MIN_POS}, // Servo 1
|
||||
{CV_ADDRESS_SERVO_MAX_POS(1), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(2), CV_VALUE_SERVO_MIN_POS}, // Servo 2
|
||||
{CV_ADDRESS_SERVO_MAX_POS(2), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(3), CV_VALUE_SERVO_MIN_POS}, // Servo 3
|
||||
{CV_ADDRESS_SERVO_MAX_POS(3), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(4), CV_VALUE_SERVO_MIN_POS}, // Servo 4
|
||||
{CV_ADDRESS_SERVO_MAX_POS(4), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(5), CV_VALUE_SERVO_MIN_POS}, // Servo 5
|
||||
{CV_ADDRESS_SERVO_MAX_POS(5), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(6), CV_VALUE_SERVO_MIN_POS}, // Servo 6
|
||||
{CV_ADDRESS_SERVO_MAX_POS(6), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(7), CV_VALUE_SERVO_MIN_POS}, // Servo 7
|
||||
{CV_ADDRESS_SERVO_MAX_POS(7), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(8), CV_VALUE_SERVO_MIN_POS}, // Servo 8
|
||||
{CV_ADDRESS_SERVO_MAX_POS(8), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(9), CV_VALUE_SERVO_MIN_POS}, // Servo 9
|
||||
{CV_ADDRESS_SERVO_MAX_POS(9), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(10), CV_VALUE_SERVO_MIN_POS}, // Servo 10
|
||||
{CV_ADDRESS_SERVO_MAX_POS(10), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(11), CV_VALUE_SERVO_MIN_POS}, // Servo 11
|
||||
{CV_ADDRESS_SERVO_MAX_POS(11), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(12), CV_VALUE_SERVO_MIN_POS}, // Servo 12
|
||||
{CV_ADDRESS_SERVO_MAX_POS(12), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(13), CV_VALUE_SERVO_MIN_POS}, // Servo 13
|
||||
{CV_ADDRESS_SERVO_MAX_POS(13), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(14), CV_VALUE_SERVO_MIN_POS}, // Servo 14
|
||||
{CV_ADDRESS_SERVO_MAX_POS(14), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(15), CV_VALUE_SERVO_MIN_POS}, // Servo 15
|
||||
{CV_ADDRESS_SERVO_MAX_POS(15), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_LAST_POS_LSB, 0},
|
||||
{CV_ADDRESS_LAST_POS_MSB, 0},
|
||||
};
|
||||
|
||||
uint8_t FactoryDefaultCVIndex = 0;
|
||||
|
||||
NmraDcc Dcc ;
|
||||
DCC_MSG Packet ;
|
||||
uint16_t BaseTurnoutAddress;
|
||||
|
||||
uint16_t lastPositionBits;
|
||||
|
||||
SMSSmooth Servos[NUM_SERVOS];
|
||||
|
||||
// This function is called whenever a normal DCC Turnout Packet is received
|
||||
void notifyDccAccTurnoutOutput (uint16_t OutputAddress, uint8_t Direction, uint8_t OutputPower)
|
||||
{
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print("notifyDccAccTurnoutOutput: Output Addr: ") ;
|
||||
Serial.print(OutputAddress,DEC) ;
|
||||
Serial.print(" Direction: ");
|
||||
Serial.print(Direction ? "Closed" : "Thrown") ;
|
||||
Serial.print(" Output: ");
|
||||
Serial.print(OutputPower ? "On" : "Off") ;
|
||||
#endif
|
||||
if(( OutputAddress >= BaseTurnoutAddress ) && ( OutputAddress < (BaseTurnoutAddress + NUM_SERVOS )) && OutputPower ) // Only Drive the Servo on Activation
|
||||
{
|
||||
uint16_t servoIndex = OutputAddress - BaseTurnoutAddress;
|
||||
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print(" Servo Num: ");
|
||||
Serial.println(servoIndex,DEC);
|
||||
#endif
|
||||
|
||||
setServoPos(servoIndex, Direction != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void notifyDccAccTurnoutBoard (uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower)
|
||||
{
|
||||
uint16_t Addr = ((BoardAddr - 1) * 4) + OutputPair + 1;
|
||||
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print("notifyDccAccTurnoutBoard: Board Addr: ") ;
|
||||
Serial.print(BoardAddr,DEC) ;
|
||||
Serial.print(" Output Pair: ");
|
||||
Serial.print(OutputPair,DEC) ;
|
||||
Serial.print(" Turnout Addr: ");
|
||||
Serial.print(Addr,DEC) ;
|
||||
Serial.print(" Base Turnout Addr: ");
|
||||
Serial.print(BaseTurnoutAddress,DEC) ;
|
||||
Serial.print(" Direction: ");
|
||||
Serial.print(Direction ? "Closed" : "Thrown") ;
|
||||
Serial.print(" Output: ");
|
||||
Serial.print(OutputPower ? "On" : "Off") ;
|
||||
|
||||
#endif
|
||||
|
||||
if(( Addr >= BaseTurnoutAddress ) && ( Addr < (BaseTurnoutAddress + NUM_SERVOS )) && OutputPower ) // Only Drive the Servo on Activation
|
||||
{
|
||||
uint16_t servoIndex = Addr - BaseTurnoutAddress;
|
||||
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print(" Servo Num: ");
|
||||
Serial.println(servoIndex,DEC);
|
||||
#endif
|
||||
|
||||
setServoPos(servoIndex, Direction != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setServoPos(uint8_t index, bool position)
|
||||
{
|
||||
if(index < NUM_SERVOS)
|
||||
{
|
||||
Servos[index].goTo( position ? 1.0 : 0.0);
|
||||
|
||||
setLastTurnoutPosition(index, position);
|
||||
}
|
||||
}
|
||||
|
||||
bool getLastTurnoutPosition(uint8_t index)
|
||||
{
|
||||
if(index < NUM_SERVOS)
|
||||
{
|
||||
uint16_t bitMask = 1 << index;
|
||||
return lastPositionBits & bitMask ? true : false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setLastTurnoutPosition(uint8_t index, bool position)
|
||||
{
|
||||
if(index < NUM_SERVOS)
|
||||
{
|
||||
uint16_t bitMask = 1 << index;
|
||||
|
||||
if(position)
|
||||
lastPositionBits |= bitMask;
|
||||
else
|
||||
lastPositionBits &= ~bitMask;
|
||||
|
||||
Dcc.setCV(CV_ADDRESS_LAST_POS_LSB, lastPositionBits & 0x00ff);
|
||||
Dcc.setCV(CV_ADDRESS_LAST_POS_MSB, (lastPositionBits >> 8) & 0x00ff);
|
||||
}
|
||||
}
|
||||
|
||||
void initServos()
|
||||
{
|
||||
BaseTurnoutAddress = (((Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_MSB) * 64) + Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_LSB) - 1) * 4) + 1 ;
|
||||
|
||||
uint16_t servoDetachMillis = Dcc.getCV(CV_ADDRESS_SERVO_DETACH_MILLIS) * 10;
|
||||
SlowMotionServo::setDelayUntilStop(servoDetachMillis); // This is a static class member so only needs to be set once and is used globally
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("initServos: DCC Turnout Base Address: "); Serial.print(BaseTurnoutAddress, DEC);
|
||||
Serial.print(" Detach Servo Signal MilliSeconds: "); Serial.print(servoDetachMillis);
|
||||
#endif
|
||||
|
||||
float servoMoveSpeed = Dcc.getCV(CV_ADDRESS_SERVO_MOVE_SPEED) / 10.0;
|
||||
lastPositionBits = Dcc.getCV(CV_ADDRESS_LAST_POS_LSB) | (Dcc.getCV(CV_ADDRESS_LAST_POS_MSB) << 8);
|
||||
|
||||
for(uint8_t i = 0; i < NUM_SERVOS; i++)
|
||||
{
|
||||
Servos[i].setPin(i);
|
||||
Servos[i].setupMin( map(Dcc.getCV(CV_ADDRESS_SERVO_MIN_POS(i)), 0, 100, 544, 2400));
|
||||
Servos[i].setupMax( map(Dcc.getCV(CV_ADDRESS_SERVO_MAX_POS(i)), 0, 100, 544, 2400));
|
||||
Servos[i].setSpeed(servoMoveSpeed);
|
||||
Servos[i].setDetach(servoDetachMillis != 0);
|
||||
Servos[i].setInitialPosition(getLastTurnoutPosition(i) ? 1.0 : 0.0);
|
||||
}
|
||||
|
||||
SlowMotionServo::update();
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.println("\ninitServos: Done");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
elapsedMillis millisWaitedForUSB = 0;
|
||||
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("\nNMRA DCC 16-Servo Accessory Decoder. Ver: "); Serial.println(DCC_DECODER_VERSION_NUM,DEC);
|
||||
#endif
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 1);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, DCC_DECODER_VERSION_NUM, FLAGS_DCC_ACCESSORY_DECODER|FLAGS_EXTENDED_ADDRESS_MODE, 0);
|
||||
|
||||
#ifdef FORCE_RESET_FACTORY_DEFAULT_CV
|
||||
Serial.println("Resetting CVs to Factory Defaults");
|
||||
notifyCVResetFactoryDefault();
|
||||
#endif
|
||||
|
||||
if( FactoryDefaultCVIndex == 0) // Not forcing a reset CV Reset to Factory Defaults so initPinPulser
|
||||
initServos();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||
Dcc.process();
|
||||
|
||||
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
|
||||
{
|
||||
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||
uint16_t cv = FactoryDefaultCVs[FactoryDefaultCVIndex].CV;
|
||||
uint8_t val = FactoryDefaultCVs[FactoryDefaultCVIndex].Value;
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("loop: Write Default CV: "); Serial.print(cv,DEC); Serial.print(" Value: "); Serial.println(val,DEC);
|
||||
#endif
|
||||
Dcc.setCV( cv, val );
|
||||
|
||||
if( FactoryDefaultCVIndex == 0) // Is this the last Default CV to set? if so re-initPinPulser
|
||||
initServos();
|
||||
}
|
||||
|
||||
SlowMotionServo::update();
|
||||
}
|
||||
|
||||
void notifyCVChange(uint16_t CV, uint8_t Value)
|
||||
{
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("notifyCVChange: CV: ") ;
|
||||
Serial.print(CV,DEC) ;
|
||||
Serial.print(" Value: ") ;
|
||||
Serial.println(Value, DEC) ;
|
||||
#endif
|
||||
|
||||
Value = Value; // Silence Compiler Warnings...
|
||||
|
||||
if((CV == CV_ACCESSORY_DECODER_ADDRESS_MSB) || (CV == CV_ACCESSORY_DECODER_ADDRESS_LSB) ||
|
||||
((CV >= CV_ADDRESS_SERVO_DETACH_MILLIS) && (CV < CV_ADDRESS_LAST_POS_LSB)) && (FactoryDefaultCVIndex == 0))
|
||||
initServos(); // Some CV we care about changed so re-init the PinPulser with the new CV settings
|
||||
}
|
||||
|
||||
void notifyCVResetFactoryDefault()
|
||||
{
|
||||
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
|
||||
// to flag to the loop() function that a reset to Factory Defaults needs to be done
|
||||
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
|
||||
};
|
||||
|
||||
// This function is called by the NmraDcc library when a DCC ACK needs to be sent
|
||||
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
|
||||
#ifdef ENABLE_DCC_ACK
|
||||
void notifyCVAck(void)
|
||||
{
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.println("notifyCVAck") ;
|
||||
#endif
|
||||
// Configure the DCC CV Programing ACK pin for an output
|
||||
pinMode( ENABLE_DCC_ACK, OUTPUT );
|
||||
|
||||
// Generate the DCC ACK 60mA pulse
|
||||
digitalWrite( ENABLE_DCC_ACK, HIGH );
|
||||
delay( 10 ); // The DCC Spec says 6ms but 10 makes sure... ;)
|
||||
digitalWrite( ENABLE_DCC_ACK, LOW );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NOTIFY_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg: ") ;
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <NmraDcc.h>
|
||||
|
||||
#define This_Decoder_Address 3
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
struct CVPair
|
||||
{
|
||||
@@ -11,11 +12,11 @@ struct CVPair
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
// The CV Below defines the Short DCC Address
|
||||
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
|
||||
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, DEFAULT_MULTIFUNCTION_DECODER_ADDRESS},
|
||||
|
||||
// These two CVs define the Long DCC Address
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, This_Decoder_Address},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(DEFAULT_MULTIFUNCTION_DECODER_ADDRESS)},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(DEFAULT_MULTIFUNCTION_DECODER_ADDRESS)},
|
||||
|
||||
// ONLY uncomment 1 CV_29_CONFIG line below as approprate
|
||||
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
|
||||
@@ -170,7 +171,13 @@ void setup()
|
||||
digitalWrite( DccAckPin, LOW );
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(0, 2, 0);
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
//Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
// This section defines the Arduino UNO Pins to use
|
||||
#ifdef __AVR_ATmega328P__
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
#define LED_PIN_FWD 5
|
||||
@@ -42,6 +43,7 @@
|
||||
// This section defines the Arduino ATTiny85 Pins to use
|
||||
#elif ARDUINO_AVR_ATTINYX5
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
#define LED_PIN_FWD 0
|
||||
@@ -89,8 +91,8 @@ CVPair FactoryDefaultCVs [] =
|
||||
{CV_VHIGH, 255},
|
||||
|
||||
// These two CVs define the Long DCC Address
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, DEFAULT_DECODER_ADDRESS},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(DEFAULT_DECODER_ADDRESS)},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(DEFAULT_DECODER_ADDRESS)},
|
||||
|
||||
// ONLY uncomment 1 CV_29_CONFIG line below as approprate
|
||||
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
|
||||
@@ -218,7 +220,13 @@ void setup()
|
||||
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
Dcc.init( MAN_ID_DIY, 10, FLAGS_MY_ADDRESS_ONLY | FLAGS_AUTO_FACTORY_DEFAULT, 0 );
|
||||
|
||||
@@ -292,8 +300,8 @@ void loop()
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.println("LED On");
|
||||
#endif
|
||||
digitalWrite(LED_PIN_FWD, newDirection ? LOW : HIGH);
|
||||
digitalWrite(LED_PIN_REV, newDirection ? HIGH : LOW);
|
||||
digitalWrite(LED_PIN_FWD, newDirection ? HIGH : LOW);
|
||||
digitalWrite(LED_PIN_REV, newDirection ? LOW : HIGH);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#include <NmraDcc.h>
|
||||
|
||||
// This Example shows how to use the library with the Iowa Scaled Engineering ARD-DCCSHIELD
|
||||
// You can find out more about this DCC Interface here: http://www.iascaled.com/store/ARD-DCCSHIELD
|
||||
//
|
||||
@@ -16,6 +14,9 @@
|
||||
// JP6 - Boards without VIO - User Choice
|
||||
// JP7 - Enable Programming ACK - 1-2 ON 3-4 ON
|
||||
//
|
||||
#include <NmraDcc.h>
|
||||
#include <elapsedMillis.h>
|
||||
|
||||
// It is a very basic DCC Accessory Decoder that does nothing except allow CV Read/Write and
|
||||
// you can also print every DCC packet by uncommenting the "#define NOTIFY_DCC_MSG" line below
|
||||
#define NOTIFY_DCC_MSG
|
||||
@@ -26,6 +27,9 @@
|
||||
// Un-Comment the line below to Enable DCC ACK for Service Mode Programming Read CV Capablilty
|
||||
#define ENABLE_DCC_ACK 15 // This is A1 on the Iowa Scaled Engineering ARD-DCCSHIELD DCC Shield
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
NmraDcc Dcc ;
|
||||
|
||||
struct CVPair
|
||||
@@ -36,8 +40,8 @@ struct CVPair
|
||||
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, 1},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, DEFAULT_ACCESSORY_DECODER_ADDRESS & 0xFF},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, DEFAULT_ACCESSORY_DECODER_ADDRESS >> 8},
|
||||
};
|
||||
|
||||
uint8_t FactoryDefaultCVIndex = 0;
|
||||
@@ -98,11 +102,19 @@ void notifyCVChange(uint16_t CV, uint8_t Value)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
elapsedMillis millisWaitedForUSB = 0;
|
||||
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
|
||||
|
||||
Serial.println("NMRA DCC Iowa Scaled Engineering ARD-DCCSHIELD Example");
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(0, 2, 1);
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER, 0 );
|
||||
|
||||
@@ -106,8 +106,8 @@ CVPair FactoryDefaultCVs [] =
|
||||
{CV_MANUF, MANUF_ID }, // Manufacturer ID.
|
||||
|
||||
// These two CVs define the Long DCC Address
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0}, // Extended address MSB.
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, DECODER_ADDR}, // Extended address LSB.
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(DECODER_ADDR)},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(DECODER_ADDR)},
|
||||
|
||||
{CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
|
||||
{CV_MANUF_01, VER_MINOR}, // Minor decoder version.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name=NmraDcc
|
||||
version=2.0.6
|
||||
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner
|
||||
version=2.0.11
|
||||
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner, bugfixes by Jueff
|
||||
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
||||
sentence=Enables NMRA DCC Communication
|
||||
paragraph=This library allows you to interface to a NMRA DCC track signal and receive DCC commands. The library has been tested on AVR ATTiny84/85 & ATMega88/168/328/32u4, ESP8266 and Teensy 3.x using the INT0/1 Hardware Interrupt and micros() ONLY and no longer uses Timer0 Compare Match B, which makes it much more portable to other platforms.
|
||||
|
||||
20
support/pre-commit
Executable file
20
support/pre-commit
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
FORMAT_SOURCES="NmraDcc.h NmraDcc.cpp"
|
||||
|
||||
error=0
|
||||
|
||||
for file in ${FORMAT_SOURCES}
|
||||
do
|
||||
output=$(mktemp -t stylecheckXXXXXXX)
|
||||
astyle --options=.astylerc < $file > $output
|
||||
if ! cmp -s $file $output
|
||||
then
|
||||
echo "Formatting on $file doesn't match expectations"
|
||||
diff -u $file $output
|
||||
error=1
|
||||
fi
|
||||
[ -f $output ] && rm $output
|
||||
done
|
||||
|
||||
exit $error
|
||||
Reference in New Issue
Block a user