diff --git a/NmraDcc.cpp b/NmraDcc.cpp index 82d70b7..ff9a80d 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -888,8 +888,17 @@ void execDccProcessor( DCC_MSG * pDccMsg ) if(pDccMsg->Data[1] & 0b10000000) { + uint8_t direction = OutputAddress & 0x01; + uint8_t outputPower = (pDccMsg->Data[1] & 0b00001000) >> 3; + if( notifyDccAccState ) notifyDccAccState( Address, BoardAddress, OutputAddress, pDccMsg->Data[1] & 0b00001000 ) ; + + if( notifyDccAccTurnoutBoard ) + notifyDccAccTurnoutBoard( BoardAddress, OutputIndex, direction, outputPower ); + + if( notifyDccAccTurnoutOutput ) + notifyDccAccTurnoutOutput( Address, direction, outputPower ); } else diff --git a/NmraDcc.h b/NmraDcc.h index 6a0f08f..208aa8c 100644 --- a/NmraDcc.h +++ b/NmraDcc.h @@ -223,6 +223,8 @@ extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Ra extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak)); extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak)); +extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); +extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak)); diff --git a/examples/NmraDccExample_1/NmraDccExample_1.ino b/examples/NmraDccAccessoryDecoder_1/NmraDccAccessoryDecoder_1.ino similarity index 79% rename from examples/NmraDccExample_1/NmraDccExample_1.ino rename to examples/NmraDccAccessoryDecoder_1/NmraDccAccessoryDecoder_1.ino index 56cecf3..c6ed4d1 100644 --- a/examples/NmraDccExample_1/NmraDccExample_1.ino +++ b/examples/NmraDccAccessoryDecoder_1/NmraDccAccessoryDecoder_1.ino @@ -69,6 +69,30 @@ void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, u Serial.println(State, HEX) ; } +// This function is called whenever a normal DCC Turnout Packet is received +void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) +{ + Serial.print("notifyDccAccTurnoutBoard: ") ; + Serial.print(BoardAddr,DEC) ; + Serial.print(','); + Serial.print(OutputPair,DEC) ; + Serial.print(','); + Serial.print(Direction,DEC) ; + Serial.print(','); + Serial.println(OutputPower, HEX) ; +} + +// This function is called whenever a normal DCC Turnout Packet is received +void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) +{ + Serial.print("notifyDccAccTurnoutOutput: ") ; + Serial.print(Addr,DEC) ; + Serial.print(','); + Serial.print(Direction,DEC) ; + Serial.print(','); + Serial.println(OutputPower, HEX) ; +} + // This function is called whenever a DCC Signal Aspect Packet is received void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) { diff --git a/examples/NmraDccAccessoryDecoder_Pulsed_8/NmraDccAccessoryDecoder_Pulsed_8.ino b/examples/NmraDccAccessoryDecoder_Pulsed_8/NmraDccAccessoryDecoder_Pulsed_8.ino new file mode 100644 index 0000000..cf0b7a7 --- /dev/null +++ b/examples/NmraDccAccessoryDecoder_Pulsed_8/NmraDccAccessoryDecoder_Pulsed_8.ino @@ -0,0 +1,214 @@ +#include +#include "PinPulser.h" +// This Example shows how to use the library as a DCC Accessory Decoder to drive 8 Pulsed Turnouts + +// 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 15 // This is A1 on the Iowa Scaled Engineering ARD-DCCSHIELD DCC Shield + +#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 + +#define DCC_DECODER_VERSION_NUM 11 // Set the Decoder Version - Used by JMRI to Identify the decoder + +struct CVPair +{ + uint16_t CV; + uint8_t Value; +}; + +#define CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME 2 // CV for the Output Pulse ON ms +#define CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME 3 // CV for the delay in ms to allow a CDU to recharge +#define CV_ACCESSORY_DECODER_ACTIVE_STATE 4 // CV to define the ON Output State + +// 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, 1}, // CV 1 Board Address (lower 6 bits) + {CV_ACCESSORY_DECODER_ADDRESS_MSB, 0}, // CV 9 Board Address (Upper 3 bits) + {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}, +}; + +uint8_t FactoryDefaultCVIndex = 0; + +// This is the Arduino Pin Mapping to Turnout Addresses with 2 pins per turnout +// A1 is missing in the sequence as it is used for the DCC ACK +// The Pins are defined in Pairs T=Thrown, C=Closed (Digitrax Notation) +// base address 1T 1C 2T 2C 3T 3C 4T 4C 5T 5C 6T 6C 7T 7C 8T 8C +byte outputs[] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19}; +// pins D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 A0 A2 A3 A4 A5 + +NmraDcc Dcc ; +DCC_MSG Packet ; +PinPulser pinPulser; +uint16_t BaseTurnoutAddress; + +// This function is called whenever a normal DCC Turnout Packet is received +void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) +{ +#ifdef NOTIFY_TURNOUT_MSG + Serial.print("notifyDccAccTurnoutOutput: Turnout: ") ; + Serial.print(Addr,DEC) ; + Serial.print(" Direction: "); + Serial.print(Direction ? "Closed" : "Thrown") ; + Serial.print(" Output: "); + Serial.print(OutputPower ? "On" : "Off") ; +#endif + if(( Addr >= BaseTurnoutAddress ) && ( Addr < (BaseTurnoutAddress + NUM_TURNOUTS )) && OutputPower ) + { + uint16_t pinIndex = ( (Addr - BaseTurnoutAddress) << 1 ) + Direction ; + pinPulser.addPin(outputs[pinIndex]); +#ifdef NOTIFY_TURNOUT_MSG + Serial.print(" Pin Index: "); + Serial.print(pinIndex,DEC); + Serial.print(" Pin: "); + Serial.print(outputs[pinIndex],DEC); +#endif + } +#ifdef NOTIFY_TURNOUT_MSG + Serial.println(); +#endif +} + +void initPinPulser(void) +{ + BaseTurnoutAddress = (((Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_MSB) * 64) + Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_LSB) - 1) * 4) + 1 ; + + uint16_t onMs = Dcc.getCV(CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME) * 10; + uint16_t cduRechargeMs = Dcc.getCV(CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME) * 10; + uint8_t activeOutputState = Dcc.getCV(CV_ACCESSORY_DECODER_ACTIVE_STATE); + +#ifdef DEBUG_MSG + Serial.print("initPinPulser: DCC Turnout Base Address: "); Serial.print(BaseTurnoutAddress, DEC); + Serial.print(" Active Pulse: "); Serial.print(onMs); + Serial.print("ms CDU Recharge: "); Serial.print(cduRechargeMs); + Serial.print("ms Active Output State: "); Serial.println(activeOutputState ? "HIGH" : "LOW" ); +#endif + + // Step through all the Turnout Driver pins setting them to OUTPUT and NOT Active State + for(uint8_t i = 0; i < (NUM_TURNOUTS * 2); i++) + { + digitalWrite(outputs[i], !activeOutputState); // Set the Output Inactive before the direction so the + pinMode( outputs[i], OUTPUT ); // Pin doesn't momentarily pulse the wrong state + } + + // Init the PinPulser with the new settings + pinPulser.init(onMs, cduRechargeMs, activeOutputState); +} + +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); + + // Call the main DCC Init function to enable the DCC Receiver + Dcc.init( MAN_ID_DIY, DCC_DECODER_VERSION_NUM, CV29_ACCESSORY_DECODER, 0 ); + +#ifdef DEBUG_MSG + Serial.print("\nNMRA DCC 8-Turnout Accessory Decoder. Ver: "); Serial.println(DCC_DECODER_VERSION_NUM,DEC); +#endif + +#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 + initPinPulser(); +} + +void loop() +{ + // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation + Dcc.process(); + + pinPulser.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 + initPinPulser(); + } +} + +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_ACCESSORY_DECODER_OUTPUT_PULSE_TIME) || (CV == CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME) || (CV == CV_ACCESSORY_DECODER_ACTIVE_STATE)) + initPinPulser(); // 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 diff --git a/examples/NmraDccAccessoryDecoder_Pulsed_8/PinPulser.cpp b/examples/NmraDccAccessoryDecoder_Pulsed_8/PinPulser.cpp new file mode 100644 index 0000000..222e2da --- /dev/null +++ b/examples/NmraDccAccessoryDecoder_Pulsed_8/PinPulser.cpp @@ -0,0 +1,92 @@ +#include "PinPulser.h" + +#define PIN_PULSER_SLOT_EMPTY 255 + +void PinPulser::init(uint16_t onMs, uint16_t cduRechargeMs, uint8_t activeOutputState) +{ + this->onMs = onMs; + this->cduRechargeMs = cduRechargeMs; + this->activeOutputState = activeOutputState; + state = PP_IDLE; + targetMs = 0; + memset(pinQueue, PIN_PULSER_SLOT_EMPTY, PIN_PULSER_MAX_PINS + 1); +} + +uint8_t PinPulser::addPin(uint8_t Pin) +{ +// Serial.print(" PinPulser::addPin: "); Serial.print(Pin,DEC); + + for(uint8_t i = 0; i < PIN_PULSER_MAX_PINS; i++) + { + if(pinQueue[i] == Pin) + { +// Serial.print(" Already in Index: "); Serial.println(i,DEC); + return i; + } + + else if(pinQueue[i] == PIN_PULSER_SLOT_EMPTY) + { +// Serial.print(" pinQueue Index: "); Serial.println(i,DEC); + pinQueue[i] = Pin; + process(); + return i; + } + } + +// Serial.println(); + return PIN_PULSER_SLOT_EMPTY; +} + +PP_State PinPulser::process(void) +{ + unsigned long now; + + switch(state) + { + case PP_IDLE: + if(pinQueue[0] != PIN_PULSER_SLOT_EMPTY) + { +// Serial.print(" PinPulser::process: PP_IDLE: Pin: "); Serial.println(pinQueue[0],DEC); + + digitalWrite(pinQueue[0], activeOutputState); + targetMs = millis() + onMs; + state = PP_OUTPUT_ON_DELAY; + } + break; + + case PP_OUTPUT_ON_DELAY: + now = millis(); + if(now >= targetMs) + { +// Serial.print(" PinPulser::process: PP_OUTPUT_ON_DELAY: Done Deactivate Pin: "); Serial.println(pinQueue[0],DEC); + + digitalWrite(pinQueue[0], !activeOutputState); + targetMs = now + cduRechargeMs; + memmove(pinQueue, pinQueue + 1, PIN_PULSER_MAX_PINS); + state = PP_CDU_RECHARGE_DELAY; + } + break; + + case PP_CDU_RECHARGE_DELAY: + now = millis(); + if(now >= targetMs) + { + if(pinQueue[0] != PIN_PULSER_SLOT_EMPTY) + { +// Serial.print(" PinPulser::process: PIN_PULSER_SLOT_EMPTY: Done Deactivate Pin: "); Serial.println(pinQueue[0],DEC); + + digitalWrite(pinQueue[0], activeOutputState); + targetMs = now + onMs; + state = PP_OUTPUT_ON_DELAY; + } + else + { +// Serial.println(" PinPulser::process: PP_CDU_RECHARGE_DELAY - Now PP_IDLE"); + state = PP_IDLE; + } + } + break; + } + return state; +} + diff --git a/examples/NmraDccAccessoryDecoder_Pulsed_8/PinPulser.h b/examples/NmraDccAccessoryDecoder_Pulsed_8/PinPulser.h new file mode 100644 index 0000000..747fd18 --- /dev/null +++ b/examples/NmraDccAccessoryDecoder_Pulsed_8/PinPulser.h @@ -0,0 +1,27 @@ +#include + +#define PIN_PULSER_MAX_PINS 16 + +enum PP_State +{ + PP_IDLE = 0, + PP_OUTPUT_ON_DELAY, + PP_CDU_RECHARGE_DELAY, +}; + +class PinPulser +{ + private: + uint16_t onMs; + uint16_t cduRechargeMs; + PP_State state = PP_IDLE; + unsigned long targetMs = 0; + uint8_t activeOutputState = HIGH; + uint8_t pinQueue[PIN_PULSER_MAX_PINS + 1]; + + public: + void init(uint16_t onMs, uint16_t cduRechargeMs, uint8_t activeOutputState); + uint8_t addPin(uint8_t pin); + PP_State process(void); +}; + diff --git a/examples/NmraDccMultiFunctionDecoder_1/NmraDccMultiFunctionDecoder_1.ino b/examples/NmraDccMultiFunctionDecoder_1/NmraDccMultiFunctionDecoder_1.ino new file mode 100644 index 0000000..443d646 --- /dev/null +++ b/examples/NmraDccMultiFunctionDecoder_1/NmraDccMultiFunctionDecoder_1.ino @@ -0,0 +1,195 @@ +#include + +#define This_Decoder_Address 3 + +struct CVPair +{ + uint16_t CV; + uint8_t Value; +}; + +CVPair FactoryDefaultCVs [] = +{ + // The CV Below defines the Short DCC Address + {CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address}, + + // These two CVs define the Long DCC Address + {CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0}, + {CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, This_Decoder_Address}, + +// ONLY uncomment 1 CV_29_CONFIG line below as approprate +// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps + {CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps +// {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long Address 28/128 Speed Steps +}; + +NmraDcc Dcc ; + +uint8_t FactoryDefaultCVIndex = 0; + +// Uncomment this line below to force resetting the CVs back to Factory Defaults +// FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair); + +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); +}; + +// Uncomment the #define below to print all Speed Packets +#define NOTIFY_DCC_SPEED +#ifdef NOTIFY_DCC_SPEED +void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) +{ + Serial.print("notifyDccSpeed: Addr: "); + Serial.print(Addr,DEC); + Serial.print( (AddrType == DCC_ADDR_SHORT) ? "-S" : "-L" ); + Serial.print(" Speed: "); + Serial.print(Speed,DEC); + Serial.print(" Steps: "); + Serial.print(SpeedSteps,DEC); + Serial.print(" Dir: "); + Serial.println( (Dir == DCC_DIR_FWD) ? "Forward" : "Reverse" ); +}; +#endif + +// Uncomment the #define below to print all Function Packets +#define NOTIFY_DCC_FUNC +#ifdef NOTIFY_DCC_FUNC +void notifyDccFunc(uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) +{ + Serial.print("notifyDccFunc: Addr: "); + Serial.print(Addr,DEC); + Serial.print( (AddrType == DCC_ADDR_SHORT) ? 'S' : 'L' ); + Serial.print(" Function Group: "); + Serial.print(FuncGrp,DEC); + + switch( FuncGrp ) + { +#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE + case FN_0: + Serial.print(" FN0: "); + Serial.println((FuncState & FN_BIT_00) ? "1 " : "0 "); + break; +#endif + + case FN_0_4: + if(Dcc.getCV(CV_29_CONFIG) & CV29_F0_LOCATION) // Only process Function 0 in this packet if we're not in Speed Step 14 Mode + { + Serial.print(" FN 0: "); + Serial.print((FuncState & FN_BIT_00) ? "1 ": "0 "); + } + + Serial.print(" FN 1-4: "); + Serial.print((FuncState & FN_BIT_01) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_02) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_03) ? "1 ": "0 "); + Serial.println((FuncState & FN_BIT_04) ? "1 ": "0 "); + break; + + case FN_5_8: + Serial.print(" FN 5-8: "); + Serial.print((FuncState & FN_BIT_05) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_06) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_07) ? "1 ": "0 "); + Serial.println((FuncState & FN_BIT_08) ? "1 ": "0 "); + break; + + case FN_9_12: + Serial.print(" FN 9-12: "); + Serial.print((FuncState & FN_BIT_09) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_10) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_11) ? "1 ": "0 "); + Serial.println((FuncState & FN_BIT_12) ? "1 ": "0 "); + break; + + case FN_13_20: + Serial.print(" FN 13-20: "); + Serial.print((FuncState & FN_BIT_13) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_14) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_15) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_16) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_17) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_18) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_19) ? "1 ": "0 "); + Serial.println((FuncState & FN_BIT_20) ? "1 ": "0 "); + break; + + case FN_21_28: + Serial.print(" FN 21-28: "); + Serial.print((FuncState & FN_BIT_21) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_22) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_23) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_24) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_25) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_26) ? "1 ": "0 "); + Serial.print((FuncState & FN_BIT_27) ? "1 ": "0 "); + Serial.println((FuncState & FN_BIT_28) ? "1 ": "0 "); + break; + } +} +#endif + +// Uncomment the #define below to print all DCC Packets +#define NOTIFY_DCC_MSG +#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 + +// 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 + +const int DccAckPin = 15 ; + +void notifyCVAck(void) +{ + Serial.println("notifyCVAck") ; + + digitalWrite( DccAckPin, HIGH ); + delay( 8 ); + digitalWrite( DccAckPin, LOW ); +} + +void setup() +{ + Serial.begin(115200); + Serial.println("NMRA Dcc Multifunction Decoder Demo 1"); + + // Configure the DCC CV Programing ACK pin for an output + pinMode( DccAckPin, OUTPUT ); + 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); + + // 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 ); + + Dcc.init( MAN_ID_DIY, 10, FLAGS_MY_ADDRESS_ONLY, 0 ); + + // Uncomment to force CV Reset to Factory Defaults + notifyCVResetFactoryDefault(); +} + +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 + Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value); + } +} + diff --git a/examples/SMA/Decoder_10Serv_7LED_4Function/Decoder_10Serv_7LED_4Function.ino b/examples/SMA/Decoder_10Serv_7LED_4Function/Decoder_10Serv_7LED_4Function.ino index 57efba5..27bbf41 100755 --- a/examples/SMA/Decoder_10Serv_7LED_4Function/Decoder_10Serv_7LED_4Function.ino +++ b/examples/SMA/Decoder_10Serv_7LED_4Function/Decoder_10Serv_7LED_4Function.ino @@ -68,7 +68,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -407,7 +406,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 diff --git a/examples/SMA/Decoder_13Serv_4LED_4Function/Decoder_13Serv_4LED_4Function.ino b/examples/SMA/Decoder_13Serv_4LED_4Function/Decoder_13Serv_4LED_4Function.ino index cb6a608..049f60d 100755 --- a/examples/SMA/Decoder_13Serv_4LED_4Function/Decoder_13Serv_4LED_4Function.ino +++ b/examples/SMA/Decoder_13Serv_4LED_4Function/Decoder_13Serv_4LED_4Function.ino @@ -68,7 +68,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -407,7 +406,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 diff --git a/examples/SMA/Decoder_15_Servos_1LED_4Function/Decoder_15_Servos_1LED_4Function.ino b/examples/SMA/Decoder_15_Servos_1LED_4Function/Decoder_15_Servos_1LED_4Function.ino index 524d77f..5d38f9b 100755 --- a/examples/SMA/Decoder_15_Servos_1LED_4Function/Decoder_15_Servos_1LED_4Function.ino +++ b/examples/SMA/Decoder_15_Servos_1LED_4Function/Decoder_15_Servos_1LED_4Function.ino @@ -68,7 +68,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -407,7 +406,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 diff --git a/examples/SMA/Decoder_17LED_1Function/DecoderNmraDcc_4.ino b/examples/SMA/Decoder_17LED_1Function/DecoderNmraDcc_4.ino index 0159dbf..57ad538 100755 --- a/examples/SMA/Decoder_17LED_1Function/DecoderNmraDcc_4.ino +++ b/examples/SMA/Decoder_17LED_1Function/DecoderNmraDcc_4.ino @@ -33,7 +33,6 @@ DCC_MSG Packet ; #define This_Decoder_Address 17 -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -89,7 +88,7 @@ void loop() } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { diff --git a/examples/SMA/Decoder_17LED_4Function/Decoder_17LED_4Function.ino b/examples/SMA/Decoder_17LED_4Function/Decoder_17LED_4Function.ino index ea23b1b..ae27ca6 100755 --- a/examples/SMA/Decoder_17LED_4Function/Decoder_17LED_4Function.ino +++ b/examples/SMA/Decoder_17LED_4Function/Decoder_17LED_4Function.ino @@ -68,7 +68,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -407,7 +406,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 diff --git a/examples/SMA/Decoder_17LED_5FTN_Fade/Decoder_17LED_5FTN_Fade.ino b/examples/SMA/Decoder_17LED_5FTN_Fade/Decoder_17LED_5FTN_Fade.ino index 169e87c..1e68653 100755 --- a/examples/SMA/Decoder_17LED_5FTN_Fade/Decoder_17LED_5FTN_Fade.ino +++ b/examples/SMA/Decoder_17LED_5FTN_Fade/Decoder_17LED_5FTN_Fade.ino @@ -67,7 +67,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -416,7 +415,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, uint8_t FuncNum, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { if (FuncNum==1) { //Function Group 1 F0 F4 F3 F2 F1 exec_function( 0, FunctionPin0, (FuncState&0x10)>>4 ); exec_function( 1, FunctionPin1, (FuncState&0x01 )); diff --git a/examples/SMA/Decoder_17Pulsed_5Function/Decoder_17Pulsed_5Function.ino b/examples/SMA/Decoder_17Pulsed_5Function/Decoder_17Pulsed_5Function.ino index c82dc2a..2fd1c81 100755 --- a/examples/SMA/Decoder_17Pulsed_5Function/Decoder_17Pulsed_5Function.ino +++ b/examples/SMA/Decoder_17Pulsed_5Function/Decoder_17Pulsed_5Function.ino @@ -71,7 +71,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -420,7 +419,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 diff --git a/examples/SMA/Decoder_2MotDrive_12LED_1Servo/Decoder_2MotDrive_12LED_1Servo.ino b/examples/SMA/Decoder_2MotDrive_12LED_1Servo/Decoder_2MotDrive_12LED_1Servo.ino index 60c6a73..db64c10 100755 --- a/examples/SMA/Decoder_2MotDrive_12LED_1Servo/Decoder_2MotDrive_12LED_1Servo.ino +++ b/examples/SMA/Decoder_2MotDrive_12LED_1Servo/Decoder_2MotDrive_12LED_1Servo.ino @@ -85,7 +85,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -470,7 +469,7 @@ void gobwd2(int bcnt,int bcycle) { icnt++; } } -extern void notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, uint8_t MaxSpeed ) { +void notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, uint8_t MaxSpeed ) { if (Function13_value==1) { Motor1Speed = Speed; Motor1ForwardDir = ForwardDir; @@ -482,7 +481,7 @@ extern void notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, ui Motor2MaxSpeed = MaxSpeed; } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 @@ -669,4 +668,5 @@ void detach_servo (int servo_num) { default: break; } -} +} + diff --git a/examples/SMA/Decoder_7Servos_10LED_4Function/Decoder_7Servos_10LED_4Function.ino b/examples/SMA/Decoder_7Servos_10LED_4Function/Decoder_7Servos_10LED_4Function.ino index 904291a..33e221a 100755 --- a/examples/SMA/Decoder_7Servos_10LED_4Function/Decoder_7Servos_10LED_4Function.ino +++ b/examples/SMA/Decoder_7Servos_10LED_4Function/Decoder_7Servos_10LED_4Function.ino @@ -68,7 +68,6 @@ struct QUEUE }; QUEUE *ftn_queue = new QUEUE[16]; -extern uint8_t Decoder_Address = This_Decoder_Address; struct CVPair { uint16_t CV; @@ -407,7 +406,7 @@ void loop() //**************************************************************** } } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { switch(FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 diff --git a/examples/SMA/Decoder_Dir_and_Fade/Decoder_Dir_and_Fade.ino b/examples/SMA/Decoder_Dir_and_Fade/Decoder_Dir_and_Fade.ino index f18c087..1637cfe 100755 --- a/examples/SMA/Decoder_Dir_and_Fade/Decoder_Dir_and_Fade.ino +++ b/examples/SMA/Decoder_Dir_and_Fade/Decoder_Dir_and_Fade.ino @@ -101,7 +101,8 @@ void loop() // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation Dcc.process(); } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { + +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { int f_index; switch (FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 @@ -145,7 +146,8 @@ void exec_function (int f_index, int FuncState) { Set_LED (f_index,false); } } -extern void notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, uint8_t MaxSpeed ) { + +void notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, uint8_t MaxSpeed ) { Last_Decoder_direction = Decoder_direction; Decoder_direction = ForwardDir; if ( Decoder_direction==Last_Decoder_direction) return; @@ -196,4 +198,5 @@ void Switch_LED (int Function) { delayMicroseconds (1000.*time_fraction); } led_last_state[Function] = end_state; -} +} + diff --git a/examples/SMA/Decoder_SMA12_LED_Groups/Decoder_SMA12_LED_Groups.ino b/examples/SMA/Decoder_SMA12_LED_Groups/Decoder_SMA12_LED_Groups.ino index ab1191a..7bc5fe6 100755 --- a/examples/SMA/Decoder_SMA12_LED_Groups/Decoder_SMA12_LED_Groups.ino +++ b/examples/SMA/Decoder_SMA12_LED_Groups/Decoder_SMA12_LED_Groups.ino @@ -123,7 +123,7 @@ void loop() Dcc.process(); } -extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) { +void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) { int f_index; switch (FuncGrp) { case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1 @@ -172,4 +172,5 @@ void exec_function (int f_index, int FuncState) { Last_Function_State[f_index] = false; } } - + + diff --git a/keywords.txt b/keywords.txt index c61a74c..72f070c 100755 --- a/keywords.txt +++ b/keywords.txt @@ -6,54 +6,57 @@ # Datatypes (KEYWORD1) ####################################### -DCC_MSG KEYWORD1 -NmraDcc KEYWORD1 +DCC_MSG KEYWORD1 +NmraDcc KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -NmraDcc KEYWORD2 -pin KEYWORD2 -init KEYWORD2 -process KEYWORD2 -getCV KEYWORD2 -setCV KEYWORD2 -isSetCVReady KEYWORD2 -notifyDccReset KEYWORD2 -notifyDccIdle KEYWORD2 -notifyDccSpeed KEYWORD2 -notifyDccFunc KEYWORD2 -notifyDccAccState KEYWORD2 -notifyDccSigState KEYWORD2 -notifyDccMsg KEYWORD2 -notifyCVValid KEYWORD2 -notifyCVRead KEYWORD2 -notifyCVWrite KEYWORD2 -notifyIsSetCVReady KEYWORD2 -notifyCVChange KEYWORD2 -notifyCVAck KEYWORD2 -notifyCVResetFactoryDefault KEYWORD2 +NmraDcc KEYWORD2 +pin KEYWORD2 +init KEYWORD2 +process KEYWORD2 +getCV KEYWORD2 +setCV KEYWORD2 +isSetCVReady KEYWORD2 +notifyDccReset KEYWORD2 +notifyDccIdle KEYWORD2 +notifyDccSpeed KEYWORD2 +notifyDccSpeedRaw +notifyDccFunc KEYWORD2 +notifyDccAccState KEYWORD2 +notifyDccAccTurnoutBoard +notifyDccAccTurnoutOutput +notifyDccSigState KEYWORD2 +notifyDccMsg KEYWORD2 +notifyCVValid KEYWORD2 +notifyCVRead KEYWORD2 +notifyCVWrite KEYWORD2 +notifyIsSetCVReady KEYWORD2 +notifyCVChange KEYWORD2 +notifyCVAck KEYWORD2 +notifyCVResetFactoryDefault KEYWORD2 ####################################### # Constants (LITERAL1) -MAN_ID_JMRI LITERAL1 -MAN_ID_DIY LITERAL1 -MAN_ID_SILICON_RAILWAY LITERAL1 -FLAGS_MY_ADDRESS_ONLY LITERAL1 -FLAGS_OUTPUT_ADDRESS_MODE LITERAL1 -FLAGS_DCC_ACCESSORY_DECODER LITERAL1 +MAN_ID_JMRI LITERAL1 +MAN_ID_DIY LITERAL1 +MAN_ID_SILICON_RAILWAY LITERAL1 +FLAGS_MY_ADDRESS_ONLY LITERAL1 +FLAGS_OUTPUT_ADDRESS_MODE LITERAL1 +FLAGS_DCC_ACCESSORY_DECODER LITERAL1 -CV_ACCESSORY_DECODER_ADDRESS_LSB LITERAL1 -CV_ACCESSORY_DECODER_ADDRESS_MSB LITERAL1 +CV_ACCESSORY_DECODER_ADDRESS_LSB LITERAL1 +CV_ACCESSORY_DECODER_ADDRESS_MSB LITERAL1 -CV_MULTIFUNCTION_PRIMARY_ADDRESS LITERAL1 -CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB LITERAL1 -CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB LITERAL1 +CV_MULTIFUNCTION_PRIMARY_ADDRESS LITERAL1 +CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB LITERAL1 +CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB LITERAL1 -CV_VERSION_ID LITERAL1 -CV_MANUFACTURER_ID LITERAL1 -CV_29_CONFIG LITERAL1 -CV_OPS_MODE_ADDRESS_LSB LITERAL1 +CV_VERSION_ID LITERAL1 +CV_MANUFACTURER_ID LITERAL1 +CV_29_CONFIG LITERAL1 +CV_OPS_MODE_ADDRESS_LSB LITERAL1 ####################################### diff --git a/library.properties b/library.properties index e8eb40a..e190e6b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=NmraDcc -version=1.1.0 +version=1.2.0 author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky maintainer=Alex Shepherd sentence=Enables NMRA DCC Communication