Merge branch 'AddOutputModeAddressing'
AddOutputModeAddressing: Fixed off-by-one (x4) error with DCC Accessory Output Mode Addressing. Made compatible with the DCC Spec for DCC Accessory Output Mode Addressing CV storage in CV 1 & 9. Its a bit wierd but... Added heaps of DEBUG PRINT to the Accessory Decoder section to follow/test the various test cases through the code and to figure out how to make this stuff work. Added more code to make the existing supported functions to be more selective about which packet bits patterns they take notice of as it was too broad previously Will remove some of the notifyCall-Back functions as some were not well conceived at the time and now need to go Testing is NOT complete as there were issues in JMRI that also need to be resolved in sync with this so we're not quite there yet.. With eeprom_is_ready() for AVR-processors (#13) Fixed some bugs around DCC Accessory Output Mode Addressing and handling of edge cases Add function to set Accessory Decoder Address from next received Accessory Decoder command and new notify call-backs when a new address is set Added new notifyDccSigOutputState() with no OutputIndex parameter Bumped version but have NOT tagged the library as it needs more testing and checking for breakage Added NmraDcc.h documentation changes and logic changes to correct a couple of issue that arose from Ken West performing a standard NMRA DCC Decoder Confirmance test. Added his test sketches and output reports
This commit is contained in:
367
NmraDcc.cpp
367
NmraDcc.cpp
@@ -23,6 +23,8 @@
|
|||||||
// 2016-07-16 handle glitches on DCC line
|
// 2016-07-16 handle glitches on DCC line
|
||||||
// 2016-08-20 added ESP8266 support by Sven (littleyoda)
|
// 2016-08-20 added ESP8266 support by Sven (littleyoda)
|
||||||
// 2017-01-19 added STM32F1 support by Franz-Peter
|
// 2017-01-19 added STM32F1 support by Franz-Peter
|
||||||
|
// 2017-11-29 Ken West (kgw4449@gmail.com):
|
||||||
|
// Minor fixes to pass NMRA Baseline Conformance Tests.
|
||||||
//
|
//
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
@@ -36,6 +38,9 @@
|
|||||||
#include <avr/eeprom.h>
|
#include <avr/eeprom.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Uncomment to print DEBUG messages
|
||||||
|
// #define DEBUG_PRINT
|
||||||
|
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
// DCC Receive Routine
|
// DCC Receive Routine
|
||||||
//
|
//
|
||||||
@@ -80,6 +85,7 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
|
|
||||||
#define MAX_ONEBITFULL 146
|
#define MAX_ONEBITFULL 146
|
||||||
#define MAX_PRAEAMBEL 146
|
#define MAX_PRAEAMBEL 146
|
||||||
#define MAX_ONEBITHALF 82
|
#define MAX_ONEBITHALF 82
|
||||||
@@ -230,6 +236,15 @@ typedef enum
|
|||||||
}
|
}
|
||||||
DccRxWaitState ;
|
DccRxWaitState ;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
OPS_INS_RESERVED = 0,
|
||||||
|
OPS_INS_VERIFY_BYTE,
|
||||||
|
OPS_INS_BIT_MANIPULATION,
|
||||||
|
OPS_INS_WRITE_BYTE
|
||||||
|
}
|
||||||
|
OpsInstructionType;
|
||||||
|
|
||||||
struct DccRx_t
|
struct DccRx_t
|
||||||
{
|
{
|
||||||
DccRxWaitState State ;
|
DccRxWaitState State ;
|
||||||
@@ -252,6 +267,8 @@ typedef struct
|
|||||||
DCC_MSG LastMsg ;
|
DCC_MSG LastMsg ;
|
||||||
uint8_t ExtIntNum;
|
uint8_t ExtIntNum;
|
||||||
uint8_t ExtIntPinNum;
|
uint8_t ExtIntPinNum;
|
||||||
|
int16_t myDccAddress; // Cached value of DCC Address from CVs
|
||||||
|
uint8_t inAccDecDCCAddrNextReceivedMode;
|
||||||
#ifdef DCC_DEBUG
|
#ifdef DCC_DEBUG
|
||||||
uint8_t IntCount;
|
uint8_t IntCount;
|
||||||
uint8_t TickCount;
|
uint8_t TickCount;
|
||||||
@@ -475,10 +492,21 @@ void ExternalInterruptHandler(void)
|
|||||||
SET_TP3;
|
SET_TP3;
|
||||||
}
|
}
|
||||||
else // Get next Byte
|
else // Get next Byte
|
||||||
DccRx.State = WAIT_DATA ;
|
// KGW - Abort immediately if packet is too long.
|
||||||
|
if( DccRx.PacketBuf.Size == MAX_DCC_MESSAGE_LEN ) // Packet is too long - abort
|
||||||
|
{
|
||||||
|
DccRx.State = WAIT_PREAMBLE ;
|
||||||
|
bitMax = MAX_PRAEAMBEL;
|
||||||
|
bitMin = MIN_ONEBITFULL;
|
||||||
|
DccRx.BitCount = 0 ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DccRx.State = WAIT_DATA ;
|
||||||
|
|
||||||
DccRx.BitCount = 0 ;
|
DccRx.BitCount = 0 ;
|
||||||
DccRx.TempByte = 0 ;
|
DccRx.TempByte = 0 ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
CLR_TP1;
|
CLR_TP1;
|
||||||
CLR_TP3;
|
CLR_TP3;
|
||||||
@@ -543,6 +571,15 @@ uint8_t readCV( unsigned int CV )
|
|||||||
|
|
||||||
uint8_t writeCV( unsigned int CV, uint8_t Value)
|
uint8_t writeCV( unsigned int CV, uint8_t Value)
|
||||||
{
|
{
|
||||||
|
switch( CV )
|
||||||
|
{
|
||||||
|
case CV_ACCESSORY_DECODER_ADDRESS_LSB: // Also same CV for CV_MULTIFUNCTION_PRIMARY_ADDRESS
|
||||||
|
case CV_ACCESSORY_DECODER_ADDRESS_MSB:
|
||||||
|
case CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB:
|
||||||
|
case CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB:
|
||||||
|
DccProcState.myDccAddress = -1; // Assume any CV Write Operation might change the Address
|
||||||
|
}
|
||||||
|
|
||||||
if( notifyCVWrite )
|
if( notifyCVWrite )
|
||||||
return notifyCVWrite( CV, Value ) ;
|
return notifyCVWrite( CV, Value ) ;
|
||||||
|
|
||||||
@@ -558,24 +595,30 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
|||||||
|
|
||||||
uint16_t getMyAddr(void)
|
uint16_t getMyAddr(void)
|
||||||
{
|
{
|
||||||
uint16_t Addr ;
|
|
||||||
uint8_t CV29Value ;
|
uint8_t CV29Value ;
|
||||||
|
|
||||||
|
if( DccProcState.myDccAddress != -1 ) // See if we can return the cached value
|
||||||
|
return( DccProcState.myDccAddress );
|
||||||
|
|
||||||
CV29Value = readCV( CV_29_CONFIG ) ;
|
CV29Value = readCV( CV_29_CONFIG ) ;
|
||||||
|
|
||||||
if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder?
|
if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder?
|
||||||
Addr = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 6 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) ;
|
{
|
||||||
|
if( CV29Value & CV29_OUTPUT_ADDRESS_MODE )
|
||||||
|
DccProcState.myDccAddress = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 8 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) - 1;
|
||||||
|
else
|
||||||
|
DccProcState.myDccAddress = ( ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) & 0b00000111) << 6 ) | ( readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) & 0b00111111) ;
|
||||||
|
}
|
||||||
else // Multi-Function Decoder?
|
else // Multi-Function Decoder?
|
||||||
{
|
{
|
||||||
if( CV29Value & CV29_EXT_ADDRESSING ) // Two Byte Address?
|
if( CV29Value & CV29_EXT_ADDRESSING ) // Two Byte Address?
|
||||||
Addr = ( ( readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB ) - 192 ) << 8 ) | readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB ) ;
|
DccProcState.myDccAddress = ( ( readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB ) - 192 ) << 8 ) | readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB ) ;
|
||||||
|
|
||||||
else
|
else
|
||||||
Addr = readCV( 1 ) ;
|
DccProcState.myDccAddress = readCV( 1 ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Addr ;
|
return DccProcState.myDccAddress ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
||||||
@@ -802,12 +845,12 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
|
|||||||
case 0b11000000: // Feature Expansion Instruction
|
case 0b11000000: // Feature Expansion Instruction
|
||||||
switch(Cmd & 0b00011111)
|
switch(Cmd & 0b00011111)
|
||||||
{
|
{
|
||||||
case 0B00011110:
|
case 0b00011110:
|
||||||
if( notifyDccFunc )
|
if( notifyDccFunc )
|
||||||
notifyDccFunc( Addr, AddrType, FN_13_20, Data1 ) ;
|
notifyDccFunc( Addr, AddrType, FN_13_20, Data1 ) ;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0B00011111:
|
case 0b00011111:
|
||||||
if( notifyDccFunc )
|
if( notifyDccFunc )
|
||||||
notifyDccFunc( Addr, AddrType, FN_21_28, Data1 ) ;
|
notifyDccFunc( Addr, AddrType, FN_21_28, Data1 ) ;
|
||||||
break;
|
break;
|
||||||
@@ -902,6 +945,24 @@ void clearDccProcState(uint8_t inServiceMode)
|
|||||||
memset( &DccProcState.LastMsg, 0, sizeof( DCC_MSG ) ) ;
|
memset( &DccProcState.LastMsg, 0, sizeof( DCC_MSG ) ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
void SerialPrintPacketHex(const __FlashStringHelper *strLabel, DCC_MSG * pDccMsg)
|
||||||
|
{
|
||||||
|
Serial.print( strLabel );
|
||||||
|
|
||||||
|
for( uint8_t i = 0; i < pDccMsg->Size; i++ )
|
||||||
|
{
|
||||||
|
if( pDccMsg->Data[i] <= 9)
|
||||||
|
Serial.print('0');
|
||||||
|
|
||||||
|
Serial.print( pDccMsg->Data[i], HEX );
|
||||||
|
Serial.write( ' ' );
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void execDccProcessor( DCC_MSG * pDccMsg )
|
void execDccProcessor( DCC_MSG * pDccMsg )
|
||||||
{
|
{
|
||||||
if( ( pDccMsg->Data[0] == 0 ) && ( pDccMsg->Data[1] == 0 ) )
|
if( ( pDccMsg->Data[0] == 0 ) && ( pDccMsg->Data[1] == 0 ) )
|
||||||
@@ -965,61 +1026,238 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||||||
if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER )
|
if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER )
|
||||||
{
|
{
|
||||||
uint16_t BoardAddress ;
|
uint16_t BoardAddress ;
|
||||||
uint8_t OutputAddress ;
|
uint16_t OutputAddress ;
|
||||||
uint8_t OutputIndex ;
|
uint8_t TurnoutPairIndex ;
|
||||||
uint16_t Address ;
|
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
SerialPrintPacketHex(F( "execDccProcessor: Accessory Decoder Command: "), pDccMsg);
|
||||||
|
#endif
|
||||||
|
|
||||||
BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ;
|
BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ;
|
||||||
|
|
||||||
// If we're filtering was it my board address Our or a broadcast address
|
#ifdef DEBUG_PRINT
|
||||||
if( ( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) && ( BoardAddress != getMyAddr() ) && ( BoardAddress != 511 ) )
|
Serial.print(F("execDccProcessor: Board Addr: "));
|
||||||
return;
|
Serial.println(BoardAddress);
|
||||||
|
#endif
|
||||||
OutputAddress = pDccMsg->Data[1] & 0b00000111 ;
|
// First check for Legacy Accessory Decoder Configuration Variable Access Instruction
|
||||||
|
// as it's got a different format to the others
|
||||||
OutputIndex = OutputAddress >> 1;
|
if((pDccMsg->Size == 5) && ((pDccMsg->Data[1] & 0b10001100) == 0b00001100))
|
||||||
|
|
||||||
Address = ( ( ( BoardAddress - 1 ) << 2 ) | OutputIndex ) + 1 ;
|
|
||||||
|
|
||||||
if( pDccMsg->Size == 6 && (pDccMsg->Data[2] & 0b11100000) == 0b11100000 && (pDccMsg->Data[1] & 0b00001111) == 0 )
|
|
||||||
{
|
{
|
||||||
// Accessory CV programming, program entire decoder
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.println(F( "execDccProcessor: Legacy Accessory Decoder CV Access Command"));
|
||||||
// Process only if it is our board address or a broadcast
|
#endif
|
||||||
// (even if we have more than one address on this decoder,
|
// Check if this command is for our address or the broadcast address
|
||||||
// CV programming is allowed only for address defined in CV1/CV9)
|
if((BoardAddress != getMyAddr()) && ( OutputAddress < 511 ))
|
||||||
if( BoardAddress == getMyAddr() || BoardAddress == 511 )
|
|
||||||
{
|
{
|
||||||
uint16_t CVAddr = ( ( ( pDccMsg->Data[2] & 0x03 ) << 8 ) | pDccMsg->Data[3] ) + 1 ;
|
#ifdef DEBUG_PRINT
|
||||||
uint8_t Value = pDccMsg->Data[4] ;
|
Serial.println(F("execDccProcessor: Board Address Not Matched"));
|
||||||
|
#endif
|
||||||
processDirectOpsOperation( pDccMsg->Data[2] & 0b00001100, CVAddr, Value ) ;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t cvAddress = ((pDccMsg->Data[1] & 0b00000011) << 8) + pDccMsg->Data[2] + 1;
|
||||||
|
uint8_t cvValue = pDccMsg->Data[3];
|
||||||
|
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: CV: "));
|
||||||
|
Serial.print(cvAddress);
|
||||||
|
Serial.print(F(" Value: "));
|
||||||
|
Serial.println(cvValue);
|
||||||
|
#endif
|
||||||
|
if(validCV( cvAddress, 1 ))
|
||||||
|
writeCV(cvAddress, cvValue);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
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
|
TurnoutPairIndex = (pDccMsg->Data[1] & 0b00000110) >> 1;
|
||||||
{
|
|
||||||
if( notifyDccSigState )
|
OutputAddress = (((BoardAddress - 1) << 2 ) | TurnoutPairIndex) + 1 ;
|
||||||
notifyDccSigState( Address, OutputIndex, pDccMsg->Data[2] ) ;
|
|
||||||
}
|
if( DccProcState.inAccDecDCCAddrNextReceivedMode)
|
||||||
}
|
{
|
||||||
|
if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE )
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Set Output Addr: "));
|
||||||
|
Serial.println(OutputAddress);
|
||||||
|
#endif
|
||||||
|
uint16_t storedOutputAddress = OutputAddress + 1; // The value stored in CV1 & 9 for Output Addressing Mode is + 1
|
||||||
|
writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(storedOutputAddress % 256));
|
||||||
|
writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(storedOutputAddress / 256));
|
||||||
|
|
||||||
|
if( notifyDccAccOutputAddrSet )
|
||||||
|
notifyDccAccOutputAddrSet(OutputAddress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Set Board Addr: "));
|
||||||
|
Serial.println(BoardAddress);
|
||||||
|
#endif
|
||||||
|
writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(BoardAddress % 64));
|
||||||
|
writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(BoardAddress / 64));
|
||||||
|
|
||||||
|
if( notifyDccAccBoardAddrSet )
|
||||||
|
notifyDccAccBoardAddrSet(BoardAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
DccProcState.inAccDecDCCAddrNextReceivedMode = 0; // Reset the mode now that we have set the address
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're filtering addresses, does the address match our address or is it a broadcast address? If NOT then return
|
||||||
|
if( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY )
|
||||||
|
{
|
||||||
|
if( ( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) && ( OutputAddress != getMyAddr() ) && ( OutputAddress < 2045 ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
else if( ( BoardAddress != getMyAddr() ) && ( BoardAddress < 511 ) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.println(F("execDccProcessor: Address Matched"));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if((pDccMsg->Size == 4) && ((pDccMsg->Data[1] & 0b10001001) == 1)) // Extended Accessory Decoder Control Packet Format
|
||||||
|
{
|
||||||
|
uint8_t state = pDccMsg->Data[2] & 0b00011111;
|
||||||
|
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Output Addr: "));
|
||||||
|
Serial.print(OutputAddress);
|
||||||
|
Serial.print(F(" Extended State: "));
|
||||||
|
Serial.println(state);
|
||||||
|
#endif
|
||||||
|
if( notifyDccSigOutputState )
|
||||||
|
notifyDccSigOutputState(OutputAddress, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if(pDccMsg->Size == 3) // Basic Accessory Decoder Packet Format
|
||||||
|
{
|
||||||
|
uint8_t direction = pDccMsg->Data[1] & 0b00000001;
|
||||||
|
uint8_t outputPower = (pDccMsg->Data[1] & 0b00001000) >> 3;
|
||||||
|
|
||||||
|
if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE )
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Output Addr: "));
|
||||||
|
Serial.print(OutputAddress);
|
||||||
|
Serial.print(F(" Turnout Dir: "));
|
||||||
|
Serial.print(direction);
|
||||||
|
Serial.print(F(" Output Power: "));
|
||||||
|
Serial.println(outputPower);
|
||||||
|
#endif
|
||||||
|
if( notifyDccAccTurnoutOutput )
|
||||||
|
notifyDccAccTurnoutOutput( OutputAddress, direction, outputPower );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Turnout Pair Index: "));
|
||||||
|
Serial.print(TurnoutPairIndex);
|
||||||
|
Serial.print(F(" Dir: "));
|
||||||
|
Serial.print(direction);
|
||||||
|
Serial.print(F(" Output Power: "));
|
||||||
|
Serial.println(outputPower);
|
||||||
|
#endif
|
||||||
|
if( notifyDccAccTurnoutBoard )
|
||||||
|
notifyDccAccTurnoutBoard( BoardAddress, TurnoutPairIndex, direction, outputPower );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(pDccMsg->Size == 6) // Accessory Decoder OPS Mode Programming
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.println(F("execDccProcessor: OPS Mode CV Programming Command"));
|
||||||
|
#endif
|
||||||
|
// Check for unsupported OPS Mode Addressing mode
|
||||||
|
if(((pDccMsg->Data[1] & 0b10001001) != 1) && ((pDccMsg->Data[1] & 0b10001111) != 0x80))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.println(F("execDccProcessor: Unsupported OPS Mode CV Addressing Mode"));
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this command is for our address or the broadcast address
|
||||||
|
if(DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Check Output Address: "));
|
||||||
|
Serial.println(OutputAddress);
|
||||||
|
#endif
|
||||||
|
if((OutputAddress != getMyAddr()) && ( OutputAddress < 2045 ))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.println(F("execDccProcessor: Output Address Not Matched"));
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Check Board Address: "));
|
||||||
|
Serial.println(BoardAddress);
|
||||||
|
#endif
|
||||||
|
if((BoardAddress != getMyAddr()) && ( BoardAddress < 511 ))
|
||||||
|
{
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.println(F("execDccProcessor: Board Address Not Matched"));
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t cvAddress = ((pDccMsg->Data[2] & 0b00000011) << 8) + pDccMsg->Data[3] + 1;
|
||||||
|
uint8_t cvValue = pDccMsg->Data[4];
|
||||||
|
|
||||||
|
OpsInstructionType insType = (OpsInstructionType)((pDccMsg->Data[2] & 0b00001100) >> 2) ;
|
||||||
|
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: OPS Mode Instruction: "));
|
||||||
|
Serial.println(insType);
|
||||||
|
#endif
|
||||||
|
switch(insType)
|
||||||
|
{
|
||||||
|
case OPS_INS_RESERVED:
|
||||||
|
case OPS_INS_VERIFY_BYTE:
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: Unsupported OPS Mode Instruction: "));
|
||||||
|
Serial.println(insType);
|
||||||
|
#endif
|
||||||
|
break; // We only support Write Byte or Bit Manipulation
|
||||||
|
|
||||||
|
case OPS_INS_WRITE_BYTE:
|
||||||
|
#ifdef DEBUG_PRINT
|
||||||
|
Serial.print(F("execDccProcessor: CV: "));
|
||||||
|
Serial.print(cvAddress);
|
||||||
|
Serial.print(F(" Value: "));
|
||||||
|
Serial.println(cvValue);
|
||||||
|
#endif
|
||||||
|
if(validCV( cvAddress, 1 ))
|
||||||
|
writeCV(cvAddress, cvValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// 111CDBBB
|
||||||
|
// Where BBB represents the bit position within the CV,
|
||||||
|
// D contains the value of the bit to be verified or written,
|
||||||
|
// and C describes whether the operation is a verify bit or a write bit operation.
|
||||||
|
// C = "1" WRITE BIT
|
||||||
|
// C = "0" VERIFY BIT
|
||||||
|
case OPS_INS_BIT_MANIPULATION:
|
||||||
|
// Make sure its a Write Bit Manipulation
|
||||||
|
if((cvValue & 0b00010000) && validCV(cvAddress, 1 ))
|
||||||
|
{
|
||||||
|
uint8_t currentValue = readCV(cvAddress);
|
||||||
|
uint8_t newValueMask = 1 << (cvValue & 0b00000111);
|
||||||
|
if(cvValue & 0b00001000)
|
||||||
|
writeCV(cvAddress, cvValue | newValueMask);
|
||||||
|
else
|
||||||
|
writeCV(cvAddress, cvValue & ~newValueMask);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1082,11 +1320,13 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
|||||||
|
|
||||||
DccProcState.Flags = Flags ;
|
DccProcState.Flags = Flags ;
|
||||||
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
|
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
|
||||||
|
DccProcState.myDccAddress = -1;
|
||||||
|
DccProcState.inAccDecDCCAddrNextReceivedMode = 0;
|
||||||
|
|
||||||
// Set the Bits that control Multifunction or Accessory behaviour
|
// Set the Bits that control Multifunction or Accessory behaviour
|
||||||
// and if the Accessory decoder optionally handles Output Addressing
|
// and if the Accessory decoder optionally handles Output Addressing
|
||||||
uint8_t cv29Mask = Flags & (CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE) ; // peal off the top two bits
|
uint8_t cv29Mask = CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE ; // peal off the top two bits
|
||||||
writeCV( CV_29_CONFIG, ( readCV( CV_29_CONFIG ) & ~cv29Mask ) | Flags ) ;
|
writeCV( CV_29_CONFIG, ( readCV( CV_29_CONFIG ) & ~cv29Mask ) | (Flags & ~FLAGS_MY_ADDRESS_ONLY) ) ; // KGW: Don't write bit 0 to CV.
|
||||||
|
|
||||||
writeCV( 7, VersionId ) ;
|
writeCV( 7, VersionId ) ;
|
||||||
writeCV( 8, ManufacturerId ) ;
|
writeCV( 8, ManufacturerId ) ;
|
||||||
@@ -1143,6 +1383,11 @@ uint8_t NmraDcc::getBitCount(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void NmraDcc::setAccDecDCCAddrNextReceived(uint8_t enable)
|
||||||
|
{
|
||||||
|
DccProcState.inAccDecDCCAddrNextReceivedMode = enable;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t NmraDcc::process()
|
uint8_t NmraDcc::process()
|
||||||
{
|
{
|
||||||
if( DccProcState.inServiceMode )
|
if( DccProcState.inServiceMode )
|
||||||
|
457
NmraDcc.h
457
NmraDcc.h
@@ -19,6 +19,8 @@
|
|||||||
// 2015-11-06 Martin Pischky (martin@pischky.de):
|
// 2015-11-06 Martin Pischky (martin@pischky.de):
|
||||||
// Experimental Version to support 14 speed steps
|
// Experimental Version to support 14 speed steps
|
||||||
// and new signature of notifyDccSpeed and notifyDccFunc
|
// and new signature of notifyDccSpeed and notifyDccFunc
|
||||||
|
// 2017-11-29 Ken West (kgw4449@gmail.com):
|
||||||
|
// Added method and callback headers.
|
||||||
//
|
//
|
||||||
//------------------------------------------------------------------------
|
//------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
@@ -196,24 +198,167 @@ class NmraDcc
|
|||||||
NmraDcc();
|
NmraDcc();
|
||||||
|
|
||||||
// Flag values to be logically ORed together and passed into the init() method
|
// 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_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address
|
||||||
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
||||||
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
||||||
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.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* ExtIntNum - Interrupt number of the pin. Use digitalPinToInterrupt(ExtIntPinNum).
|
||||||
|
* ExtIntPinNum - Input pin number.
|
||||||
|
* EnablePullup - Set true to enable the pins pullup resistor.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None.
|
||||||
|
*/
|
||||||
|
void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* init() is called from setup() after the pin() command is called.
|
||||||
|
* It initializes the NmDcc object and makes it ready to process packets.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* ManufacturerId - Manufacturer ID returned in CV 8.
|
||||||
|
* Commonly MAN_ID_DIY.
|
||||||
|
* VersionId - Version ID returned in CV 7.
|
||||||
|
* Flags - ORed flags beginning with FLAGS_...
|
||||||
|
* FLAGS_MY_ADDRESS_ONLY - Only process packets with My Address.
|
||||||
|
* FLAGS_DCC_ACCESSORY_DECODER - Decoder is an accessory decoder.
|
||||||
|
* FLAGS_OUTPUT_ADDRESS_MODE - This flag applies to accessory decoders only.
|
||||||
|
* Accessory decoders normally have 4 paired outputs
|
||||||
|
* and a single address refers to all 4 outputs.
|
||||||
|
* Setting FLAGS_OUTPUT_ADDRESS_MODE causes each
|
||||||
|
* address to refer to a single output.
|
||||||
|
* OpsModeAddressBaseCV - Ops Mode base address. Set it to 0?
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* It calls init() with FLAGS_DCC_ACCESSORY_DECODER ORed into Flags.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* ManufacturerId - Manufacturer ID returned in CV 8.
|
||||||
|
* Commonly MAN_ID_DIY.
|
||||||
|
* VersionId - Version ID returned in CV 7.
|
||||||
|
* Flags - ORed flags beginning with FLAGS_...
|
||||||
|
* FLAGS_DCC_ACCESSORY_DECODER will be set for init() call.
|
||||||
|
* OpsModeAddressBaseCV - Ops Mode base address. Set it to 0?
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* It must be called very frequently to keep up with the packets.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 1 - Packet succesfully parsed on this call to process().
|
||||||
|
* 0 - Packet not ready or received packet had an error.
|
||||||
|
*/
|
||||||
uint8_t process();
|
uint8_t process();
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* getCV() returns the selected CV value.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number. It must point to a valid CV.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Value - CV value. Invalid CV numbers will return an undefined result
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number. It must point to a valid CV.
|
||||||
|
* Value - CV value.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Value - CV value set by this call.
|
||||||
|
* since nothing will have been set in that EEPROM position.
|
||||||
|
* 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);
|
||||||
uint8_t isSetCVReady( void );
|
|
||||||
uint16_t getAddr(void);
|
/*+
|
||||||
|
* setAccDecDCCAddrNextReceived() enables/disables the setting of the board address from the next received turnout command
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* enable- boolean to enable or disable the mode
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
*/
|
||||||
|
void setAccDecDCCAddrNextReceived(uint8_t enable);
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* isSetCVReady() returns 1 if EEPROM is ready to write.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number. It must point to a valid CV.
|
||||||
|
* Value - CV value.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* ready - 1 if ready to write, 0 otherwise. AVR processor will block
|
||||||
|
* for several ms. for each write cycle so you should check this to avoid blocks.
|
||||||
|
* Note: It returns the value returned by notifyIsSetCVReady() if it is defined.
|
||||||
|
* Calls notifyIsSetCVReady() if it is defined.
|
||||||
|
*/
|
||||||
|
uint8_t isSetCVReady( void );
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* getAddr() return the currently active decoder address.
|
||||||
|
* based on decoder type and current address size.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Adr - The current decoder address based on decoder type(Multifunction, Accessory)
|
||||||
|
* and short or long address selection for Multifunction decoders.
|
||||||
|
*/
|
||||||
|
uint16_t getAddr(void);
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* getX() return debugging data if DCC_DEBUG is defined.
|
||||||
|
* You would really need to be modifying the library to need them.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* getIntCount - Init to 0 and apparently never incremented?
|
||||||
|
* getTickCount - Init to 0 and incremented each time interrupt handler
|
||||||
|
* completes without an error.
|
||||||
|
* getBitCount - Bit count of valid packet, 0 otherwise. Only valid until
|
||||||
|
* start of the next packet.
|
||||||
|
* getState - Current WAIT_... state as defined by DccRxWaitState in NmraDcc.cpp.
|
||||||
|
* getNestedIrqCount - Init to 0 and incremented each time the interrupt handler
|
||||||
|
* is called before the previous interrupt was complete.
|
||||||
|
* This is an error indication and may indicate the system
|
||||||
|
* is not handling packets fast enough or some other error is occurring.
|
||||||
|
*/
|
||||||
// #define DCC_DEBUG
|
// #define DCC_DEBUG
|
||||||
#ifdef DCC_DEBUG
|
#ifdef DCC_DEBUG
|
||||||
uint8_t getIntCount(void);
|
uint8_t getIntCount(void);
|
||||||
uint8_t getTickCount(void);
|
uint8_t getTickCount(void);
|
||||||
uint8_t getBitCount(void);
|
uint8_t getBitCount(void);
|
||||||
uint8_t getState(void);
|
uint8_t getState(void);
|
||||||
uint8_t getNestedIrqCount(void);
|
uint8_t getNestedIrqCount(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -227,29 +372,301 @@ class NmraDcc
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak));
|
/*+
|
||||||
extern void notifyDccIdle(void) __attribute__ ((weak));
|
* notifyDccReset(uint8_t hardReset) Callback for a DCC reset command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* hardReset - 0 normal reset command.
|
||||||
|
* 1 hard reset command.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak));
|
||||||
|
|
||||||
extern void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) __attribute__ ((weak));
|
/*+
|
||||||
extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ((weak));
|
* notifyDccIdle() Callback for a DCC idle command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccIdle(void) __attribute__ ((weak));
|
||||||
|
|
||||||
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));
|
* notifyDccSpeed() Callback for a multifunction decoder speed command.
|
||||||
extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
|
* The received speed and direction are unpacked to separate values.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Active decoder address.
|
||||||
|
* AddrType - DCC_ADDR_SHORT or DCC_ADDR_LONG.
|
||||||
|
* Speed - Decoder speed. 0 = Emergency stop
|
||||||
|
* 1 = Regular stop
|
||||||
|
* 2 to SpeedSteps = Speed step 1 to max.
|
||||||
|
* Dir - DCC_DIR_REV or DCC_DIR_FWD
|
||||||
|
* SpeedSteps - Highest speed, SPEED_STEP_14 = 15
|
||||||
|
* SPEED_STEP_28 = 29
|
||||||
|
* SPEED_STEP_128 = 127
|
||||||
|
*
|
||||||
|
* 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 notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak));
|
/*+
|
||||||
|
* notifyDccSpeedRaw() Callback for a multifunction decoder speed command.
|
||||||
|
* The value in Raw is the unpacked speed command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Active decoder address.
|
||||||
|
* AddrType - DCC_ADDR_SHORT or DCC_ADDR_LONG.
|
||||||
|
* Raw - Raw decoder speed command.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ((weak));
|
||||||
|
|
||||||
extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
/*+
|
||||||
|
* notifyDccFunc() Callback for a multifunction decoder function command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Active decoder address.
|
||||||
|
* AddrType - DCC_ADDR_SHORT or DCC_ADDR_LONG.
|
||||||
|
* FuncGrp - Function group. FN_0 - 14 speed step headlight function.
|
||||||
|
* Mask FN_BIT_00.
|
||||||
|
* FN_0_4 - Functions 0 to 4. Mask FN_BIT_00 - FN_BIT_04
|
||||||
|
* FN_5_8 - Functions 5 to 8. Mask FN_BIT_05 - FN_BIT_08
|
||||||
|
* FN_9_12 - Functions 9 to 12. Mask FN_BIT_09 - FN_BIT_12
|
||||||
|
* FN_13_20 - Functions 13 to 20. Mask FN_BIT_13 - FN_BIT_20
|
||||||
|
* FN_21_28 - Functions 21 to 28. Mask FN_BIT_21 - FN_BIT_28
|
||||||
|
* FuncState - Function state. Bitmask where active functions have a 1 at that bit.
|
||||||
|
* You must & FuncState with the appropriate
|
||||||
|
* FN_BIT_nn value to isolate a given bit.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
* Decoders of this type have 4 paired turnout outputs per board.
|
||||||
|
* OutputPower is 1 if the power is on, and 0 otherwise.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* BoardAddr - Per board address. Equivalent to CV 1 LSB & CV 9 MSB.
|
||||||
|
* OutputPair - Output pair number. It has a range of 0 to 3.
|
||||||
|
* Equivalent to upper 2 bits of the 3 DDD bits in the accessory packet.
|
||||||
|
* Direction - Turnout direction. It has a value of 0 or 1.
|
||||||
|
* It is equivalent to bit 0 of the 3 DDD bits in the accessory packet.
|
||||||
|
* OutputPower - Output On/Off. Equivalent to packet C bit. It has these values:
|
||||||
|
* 0 - Output pair is off.
|
||||||
|
* 1 - Output pair is on.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
* OutputPower is 1 if the power is on, and 0 otherwise.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Per output address. There will be 4 Addr addresses
|
||||||
|
* per board for a standard accessory decoder with 4 output pairs.
|
||||||
|
* Direction - Turnout direction. It has a value of 0 or 1.
|
||||||
|
* Equivalent to bit 0 of the 3 DDD bits in the accessory packet.
|
||||||
|
* OutputPower - Output On/Off. Equivalent to packet C bit. It has these values:
|
||||||
|
* 0 - Output is off.
|
||||||
|
* 1 - Output is on.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccAccBoardAddrSet() Board oriented callback for a turnout accessory decoder.
|
||||||
|
* This notification is when a new Board Address is set to the
|
||||||
|
* address of the next DCC Turnout Packet that is received
|
||||||
|
*
|
||||||
|
* This is enabled via the setAccDecDCCAddrNextReceived() method above
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* BoardAddr - Per board address. Equivalent to CV 1 LSB & CV 9 MSB.
|
||||||
|
* per board for a standard accessory decoder with 4 output pairs.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccAccOutputAddrSet() Output oriented callback for a turnout accessory decoder.
|
||||||
|
* This notification is when a new Output Address is set to the
|
||||||
|
* address of the next DCC Turnout Packet that is received
|
||||||
|
*
|
||||||
|
* This is enabled via the setAccDecDCCAddrNextReceived() method above
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Per output address. There will be 4 Addr addresses
|
||||||
|
* per board for a standard accessory decoder with 4 output pairs.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccSigOutputState() Callback for a signal aspect accessory decoder.
|
||||||
|
* Defined in S-9.2.1 as the Extended Accessory Decoder Control Packet.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Decoder address.
|
||||||
|
* State - 6 bit command equivalent to S-9.2.1 00XXXXXX.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccMsg() Raw DCC packet callback.
|
||||||
|
* Called with raw DCC packet bytes.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Msg - Pointer to DCC_MSG structure. The values are:
|
||||||
|
* Msg->Size - Number of Data bytes in the packet.
|
||||||
|
* Msg->PreambleBits - Number of preamble bits in the packet.
|
||||||
|
* Msg->Data[] - Array of data bytes in the packet.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyCVValid() Callback to determine if a given CV is valid.
|
||||||
|
* This is called when the library needs to determine
|
||||||
|
* if a CV is valid. Note: If defined, this callback
|
||||||
|
* MUST determine if a CV is valid and return the
|
||||||
|
* appropriate value. If this callback is not defined,
|
||||||
|
* the library will determine validity.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
* Writable - 1 for CV writes. 0 for CV reads.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 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.
|
||||||
|
* This is called when the library needs to read
|
||||||
|
* a CV. Note: If defined, this callback
|
||||||
|
* MUST return the value of the CV.
|
||||||
|
* If this callback is not defined,
|
||||||
|
* the library will read the CV from EEPROM.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* This is called when the library needs to write
|
||||||
|
* a CV. Note: If defined, this callback
|
||||||
|
* MUST write the Value to the CV and return the value of the CV.
|
||||||
|
* If this callback is not defined,
|
||||||
|
* the library will read the CV from EEPROM.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
* Value - Value of the CV.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* This is called when the library needs to determine
|
||||||
|
* is ready to write without blocking or failing.
|
||||||
|
* Note: If defined, this callback
|
||||||
|
* MUST determine if a CV write would block or fail
|
||||||
|
* return the appropriate value.
|
||||||
|
* If this callback is not defined,
|
||||||
|
* the library determines if a write to the EEPROM
|
||||||
|
* would block.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 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.
|
||||||
|
* This is called whenever a CV's value is changed.
|
||||||
|
* Note: It is not called if notifyCVWrite() is defined
|
||||||
|
* or if the value in the EEPROM is the same as the value
|
||||||
|
* in the write command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
* Value - Value of the CV.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
extern void notifyCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
extern void notifyCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyCVResetFactoryDefault() Called when CVs must be reset.
|
||||||
|
* This is called when CVs must be reset
|
||||||
|
* to their factory defaults. This callback
|
||||||
|
* should write the factory default value of
|
||||||
|
* relevent CVs using the setCV() method.
|
||||||
|
* setCV() must not block whens this is called.
|
||||||
|
* Test with isSetCVReady() prior to calling setCV()
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None
|
||||||
|
* *
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
|
extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyCVAck() Called when a CV write must be acknowledged.
|
||||||
|
* This callback must increase the current drawn by this
|
||||||
|
* decoder by at least 60mA for 6ms +/- 1ms.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None
|
||||||
|
* *
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
extern void notifyCVAck(void) __attribute__ ((weak));
|
extern void notifyCVAck(void) __attribute__ ((weak));
|
||||||
|
|
||||||
#if defined (__cplusplus)
|
#if defined (__cplusplus)
|
||||||
|
@@ -0,0 +1,259 @@
|
|||||||
|
// DCC Stepper Motor Controller ( A4988 ) Example for Model Railroad Turntable Control
|
||||||
|
//
|
||||||
|
// See: https://www.dccinterface.com/how-to/assemblyguide/
|
||||||
|
//
|
||||||
|
// Author: Alex Shepherd 2017-12-04
|
||||||
|
//
|
||||||
|
// This example requires two Arduino Libraries:
|
||||||
|
//
|
||||||
|
// 1) The AccelStepper library from: http://www.airspayce.com/mikem/arduino/AccelStepper/index.html
|
||||||
|
//
|
||||||
|
// 2) The NmraDcc Library from: http://mrrwa.org/download/
|
||||||
|
//
|
||||||
|
// Both libraries can be found and installed via the Arduino IDE Library Manager
|
||||||
|
//
|
||||||
|
// Also checkout the artical I wrote in this project here:
|
||||||
|
// http://mrrwa.org/2017/12/23/dcc-controlled-turntable-stepper-motor-driver/
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <AccelStepper.h>
|
||||||
|
#include <NmraDcc.h>
|
||||||
|
|
||||||
|
// The lines below define the pins used to connect to the A4988 driver module
|
||||||
|
#define A4988_STEP_PIN 4
|
||||||
|
#define A4988_DIRECTION_PIN 5
|
||||||
|
// Uncomment the next line to enable Powering-Off the Stepper when its not running to reduce heating the motor and driver
|
||||||
|
#define A4988_ENABLE_PIN 6
|
||||||
|
|
||||||
|
// The lines below define the stepping speed and acceleration, which you may need to tune for your application
|
||||||
|
#define STEPPER_MAX_SPEED 800 // Sets the maximum permitted speed
|
||||||
|
#define STEPPER_ACCELARATION 1000 // Sets the acceleration/deceleration rate
|
||||||
|
#define STEPPER_SPEED 300 // Sets the desired constant speed for use with runSpeed()
|
||||||
|
|
||||||
|
// The line below defines the number of "Full Steps" your stepper motor does for a full rotation
|
||||||
|
#define MOTOR_FULL_STEPS_PER_REVOLUTION 200
|
||||||
|
|
||||||
|
// The line below defines any reduction gearbox multiplier. No gearbox = 1
|
||||||
|
#define REDUCTION_GEARBOX_RATIO 1
|
||||||
|
|
||||||
|
#define STEPS_PER_REVOLUTION (MOTOR_FULL_STEPS_PER_REVOLUTION * REDUCTION_GEARBOX_RATIO)
|
||||||
|
|
||||||
|
// The A4988 Driver Board has 3 pins that set the Stepping Mode which are connected to 3 jumpers on the board.
|
||||||
|
// Uncomment the line below to match the Boards jumper setting MS1, MS2, MS3
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
//#define FULL_TURN_STEPS (STEPS_PER_REVOLUTION) // full steps - MS1=OFF, MS2=OFF, MS3=OFF
|
||||||
|
//#define FULL_TURN_STEPS (STEPS_PER_REVOLUTION * 2) // 1/2 steps - MS1=ON, MS2=OFF, MS3=OFF
|
||||||
|
#define FULL_TURN_STEPS (STEPS_PER_REVOLUTION * 4) // 1/4 steps - MS1=OFF, MS2=ON, MS3=OFF
|
||||||
|
//#define FULL_TURN_STEPS (STEPS_PER_REVOLUTION * 8) // 1/8 steps - MS1=ON, MS2=ON, MS3=OFF
|
||||||
|
//#define FULL_TURN_STEPS (STEPS_PER_REVOLUTION * 16) // 1/16 steps - MS1=ON, MS2=ON, MS3=ON
|
||||||
|
|
||||||
|
#ifndef FULL_TURN_STEPS
|
||||||
|
#error You need to select one of the FULL_TURN_STEPS to match the A4988 Driver Board jumper settings
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// This constant is useful to know the number of steps to rotate the turntable 180 degrees for the back entrance position
|
||||||
|
#define HALF_TURN_STEPS (FULL_TURN_STEPS / 2)
|
||||||
|
|
||||||
|
// Home Position Sensor Input
|
||||||
|
#define HOME_SENSOR_PIN 3
|
||||||
|
#define HOME_SENSOR_ACTIVE_STATE HIGH
|
||||||
|
|
||||||
|
// This structure holds the values for a turntable position wiht the DCC Address, Front Position in Steps from Home Sensor
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int dccAddress;
|
||||||
|
int positionFront;
|
||||||
|
int positionBack;
|
||||||
|
}
|
||||||
|
TurnoutPosition;
|
||||||
|
|
||||||
|
// The constant HOME_POSITION_DCC_ADDRESS is the base DCC Accessory Decoder Address for the Home Position
|
||||||
|
// with each subsequent position numbered sequentially from there
|
||||||
|
#define POSITION_01_DCC_ADDRESS 200
|
||||||
|
|
||||||
|
// I decided to divide the turntable up into 10 Positions using #defines and mathc so it all scales with changes
|
||||||
|
// to the MS1,MS2,MS3 stepping jumpers above and to make the math tidy, but you assign positions how ever you like
|
||||||
|
#define POSITION_01 (HALF_TURN_STEPS / 10)
|
||||||
|
|
||||||
|
// This array contains the Turnout Positions which can have lines added/removed to suit your turntable
|
||||||
|
TurnoutPosition turnoutPositions[] = {
|
||||||
|
{POSITION_01_DCC_ADDRESS + 0, POSITION_01 * 1, POSITION_01 * 1 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 1, POSITION_01 * 2, POSITION_01 * 2 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 2, POSITION_01 * 3, POSITION_01 * 3 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 3, POSITION_01 * 4, POSITION_01 * 4 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 4, POSITION_01 * 5, POSITION_01 * 5 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 5, POSITION_01 * 6, POSITION_01 * 6 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 6, POSITION_01 * 7, POSITION_01 * 7 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 7, POSITION_01 * 8, POSITION_01 * 8 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 8, POSITION_01 * 9, POSITION_01 * 9 + HALF_TURN_STEPS },
|
||||||
|
{POSITION_01_DCC_ADDRESS + 9, POSITION_01 *10, POSITION_01 *10 + HALF_TURN_STEPS },
|
||||||
|
};
|
||||||
|
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
// You shouldn't need to edit anything below this line unless you're needing to make big changes... ;)
|
||||||
|
// --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define MAX_TURNOUT_POSITIONS (sizeof(turnoutPositions) / sizeof(TurnoutPosition))
|
||||||
|
|
||||||
|
// Setup the AccelStepper object for the A4988 Stepper Motor Driver
|
||||||
|
AccelStepper stepper1(AccelStepper::DRIVER, A4988_STEP_PIN, A4988_DIRECTION_PIN);
|
||||||
|
|
||||||
|
// Dcc Accessory Decoder object
|
||||||
|
NmraDcc Dcc ;
|
||||||
|
|
||||||
|
// Variables to store the last DCC Turnout message Address and Direction
|
||||||
|
uint16_t lastAddr = 0xFFFF ;
|
||||||
|
uint8_t lastDirection = 0xFF;
|
||||||
|
|
||||||
|
// 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) ;
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_TURNOUT_POSITIONS ; i++)
|
||||||
|
{
|
||||||
|
if ((Addr == turnoutPositions[i].dccAddress) && ((Addr != lastAddr) || (Direction != lastDirection)) && OutputPower)
|
||||||
|
{
|
||||||
|
lastAddr = Addr ;
|
||||||
|
lastDirection = Direction ;
|
||||||
|
|
||||||
|
Serial.print(F("Moving to "));
|
||||||
|
Serial.print(Direction ? F("Front") : F("Back"));
|
||||||
|
Serial.print(F(" Position: "));
|
||||||
|
Serial.print(i, DEC);
|
||||||
|
Serial.print(F(" @ Step: "));
|
||||||
|
|
||||||
|
#ifdef A4988_ENABLE_PIN
|
||||||
|
stepper1.enableOutputs();
|
||||||
|
#endif
|
||||||
|
if (Direction)
|
||||||
|
{
|
||||||
|
Serial.println(turnoutPositions[i].positionFront, DEC);
|
||||||
|
stepper1.moveTo(turnoutPositions[i].positionFront);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.println(turnoutPositions[i].positionBack, DEC);
|
||||||
|
stepper1.moveTo(turnoutPositions[i].positionBack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef A4988_ENABLE_PIN
|
||||||
|
bool lastIsRunningState ;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void setupStepperDriver()
|
||||||
|
{
|
||||||
|
#ifdef A4988_ENABLE_PIN
|
||||||
|
stepper1.setPinsInverted(false, false, true); // Its important that these commands are in this order
|
||||||
|
stepper1.setEnablePin(A4988_ENABLE_PIN); // otherwise the Outputs are NOT enabled initially
|
||||||
|
#endif
|
||||||
|
|
||||||
|
stepper1.setMaxSpeed(STEPPER_MAX_SPEED); // Sets the maximum permitted speed
|
||||||
|
stepper1.setAcceleration(STEPPER_ACCELARATION); // Sets the acceleration/deceleration rate
|
||||||
|
stepper1.setSpeed(STEPPER_SPEED); // Sets the desired constant speed for use with runSpeed()
|
||||||
|
|
||||||
|
#ifdef A4988_ENABLE_PIN
|
||||||
|
lastIsRunningState = stepper1.isRunning();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool moveToHomePosition()
|
||||||
|
{
|
||||||
|
Serial.println(F("Finding Home Sensor...."));
|
||||||
|
|
||||||
|
pinMode(HOME_SENSOR_PIN, INPUT_PULLUP);
|
||||||
|
|
||||||
|
stepper1.move(FULL_TURN_STEPS * 2);
|
||||||
|
while(digitalRead(HOME_SENSOR_PIN) != HOME_SENSOR_ACTIVE_STATE)
|
||||||
|
stepper1.run();
|
||||||
|
|
||||||
|
if(digitalRead(HOME_SENSOR_PIN) == HOME_SENSOR_ACTIVE_STATE)
|
||||||
|
{
|
||||||
|
Serial.println(F("Found Home Position - Setting Current Position to 0"));
|
||||||
|
stepper1.setCurrentPosition(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Serial.println(F("Home Position NOT FOUND - Check Sensor Hardware"));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
while(!Serial); // Wait for the USB Device to Enumerate
|
||||||
|
|
||||||
|
Serial.println(F("\nExample Stepper Motor Driver for DCC Turntable Control"));
|
||||||
|
|
||||||
|
Serial.print(F("Full Rotation Steps: "));
|
||||||
|
Serial.println(FULL_TURN_STEPS);
|
||||||
|
|
||||||
|
for(uint8_t i = 0; i < MAX_TURNOUT_POSITIONS; i++)
|
||||||
|
{
|
||||||
|
Serial.print("DCC Addr: ");
|
||||||
|
Serial.print(turnoutPositions[i].dccAddress);
|
||||||
|
|
||||||
|
Serial.print(" Front: ");
|
||||||
|
Serial.print(turnoutPositions[i].positionFront);
|
||||||
|
|
||||||
|
Serial.print(" Back: ");
|
||||||
|
Serial.println(turnoutPositions[i].positionBack);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupStepperDriver();
|
||||||
|
|
||||||
|
if(moveToHomePosition());
|
||||||
|
{
|
||||||
|
setupDCCDecoder();
|
||||||
|
|
||||||
|
#ifdef A4988_ENABLE_PIN
|
||||||
|
stepper1.enableOutputs();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Fake a DCC Packet to cause the Turntable to move to Position 1
|
||||||
|
notifyDccAccTurnoutOutput(POSITION_01_DCC_ADDRESS, 1, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||||
|
Dcc.process();
|
||||||
|
|
||||||
|
// Process the Stepper Library
|
||||||
|
stepper1.run();
|
||||||
|
|
||||||
|
#ifdef A4988_ENABLE_PIN
|
||||||
|
if(stepper1.isRunning() != lastIsRunningState)
|
||||||
|
{
|
||||||
|
lastIsRunningState = stepper1.isRunning();
|
||||||
|
if(!lastIsRunningState)
|
||||||
|
{
|
||||||
|
stepper1.disableOutputs();
|
||||||
|
Serial.println(F("Disable Stepper Outputs"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
458
examples/NmraValidation/Accessory_Test/Accessory_Test.ino
Executable file
458
examples/NmraValidation/Accessory_Test/Accessory_Test.ino
Executable file
@@ -0,0 +1,458 @@
|
|||||||
|
/***********************************************************************************************
|
||||||
|
*
|
||||||
|
* This sketch test the NmraDcc library as an accessory decoder.
|
||||||
|
* Author: Kenneth West
|
||||||
|
* kgw4449@gmail.com
|
||||||
|
*
|
||||||
|
* This sketch has added the printf() function to the Print class.
|
||||||
|
* You can find instructions for doing this here:
|
||||||
|
* http://playground.arduino.cc/Main/Printf
|
||||||
|
*
|
||||||
|
* It is based on NmraDcc library NmraDccAccessoryDecoder_1 example.
|
||||||
|
* You can find the library here:
|
||||||
|
* https://github.com/mrrwa/NmraDcc
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* For more information refer to the file: README.md here:
|
||||||
|
* https://github.com/IowaScaledEngineering/ard-dccshield
|
||||||
|
*
|
||||||
|
* This demo assumes the following Jumper settings on the ARD-DCCSHIELD
|
||||||
|
*
|
||||||
|
* JP1 - I2C Pull-Up Resistors - Don't Care
|
||||||
|
* JP2 - (Pins 1-2) I2C /IORST JP2 - Don't-Care
|
||||||
|
* JP2 - (Pins 3-4) - DCC Signal to Arduino Pin - OFF
|
||||||
|
* JP3 - I2C /INT and /OE - Don't-Care
|
||||||
|
* JP4 - DCC Signal to Arduino Pin - D2 ON
|
||||||
|
* JP5 - Arduino Powered from DCC - User Choice
|
||||||
|
* JP6 - Boards without VIO - User Choice
|
||||||
|
* JP7 - Enable Programming ACK - 1-2 ON 3-4 ON
|
||||||
|
*
|
||||||
|
* The connections are as follows:
|
||||||
|
*
|
||||||
|
* Pin Name Mode Description
|
||||||
|
* ----------------------------------------------------------------------------------
|
||||||
|
* D2 DCC_PIN INPUT_PULLUP DCC input signal.
|
||||||
|
* A1 ACK_PIN OUTPUT CV acknowledge control.
|
||||||
|
* A0 ACC_A_PIN OUTPUT Accessory output A.
|
||||||
|
* D13 ACC_B_PIN OUTPUT Accessory output B.
|
||||||
|
* D4 SCOPE_PIN OUTPUT SCOPE trigger pin.
|
||||||
|
*
|
||||||
|
***********************************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <NmraDcc.h>
|
||||||
|
|
||||||
|
// Uncomment this line to print out minimal status information.
|
||||||
|
#define DCC_STATUS
|
||||||
|
|
||||||
|
// Uncomment this line to issue scope trigger at beginning of motor callback.
|
||||||
|
#define DO_SCOPE
|
||||||
|
|
||||||
|
// Uncomment this line to handle Basic Accessory decoders.
|
||||||
|
#define ACCESSORY_DCC
|
||||||
|
|
||||||
|
// Uncomment this line to handle Signal decoders.
|
||||||
|
#define SIGNAL_DCC
|
||||||
|
|
||||||
|
const byte VER_MAJOR = 2; // Major version in CV 7
|
||||||
|
const byte VER_MINOR = 1; // Minor version in CV 112
|
||||||
|
const byte DCC_PIN = 2; // DCC input pin.
|
||||||
|
const int ACK_PIN = A1; // CV acknowledge pin.
|
||||||
|
const byte SCOPE_PIN = 4; // Scope trigger pin.
|
||||||
|
const byte ACC_A_PIN = A0; // Motor out A pin.
|
||||||
|
const byte ACC_B_PIN = 13; // Motor out B pin.
|
||||||
|
const byte CV_VERSION = 7; // Decoder version.
|
||||||
|
const byte CV_MANUF = 8; // Manufacturer ID.
|
||||||
|
const byte CV_MANUF_01 = 112; // Manufacturer Unique 01.
|
||||||
|
const byte MANUF_ID = MAN_ID_DIY; // Manufacturer ID in CV 8.
|
||||||
|
const unsigned long DELAY_TIME = 50; // Delay time in ms.
|
||||||
|
|
||||||
|
// The following constant is the 9 bit accessory address. It is followed by the 6 LSB bits that
|
||||||
|
// go into CV1 and the 6 MBB bits that go into CV9.
|
||||||
|
// Note: Set FORCE_CVS true to force CVs to update if just the address is changed.
|
||||||
|
const bool FORCE_CVS = false; // Set true to force CV write.
|
||||||
|
const uint16_t ACC_ADDR = 1; // 11 bit address:
|
||||||
|
// 0 - broadcast, 1 to 2044 - specific.
|
||||||
|
const uint16_t BD_ADDR = ((ACC_ADDR - 1) >> 2) + 1;
|
||||||
|
// 9 bit board address:
|
||||||
|
// 0 - broadcast, 1 to 511 - specific.
|
||||||
|
const byte BD_LSB = BD_ADDR & 0x3f; // Board address LSB.
|
||||||
|
const byte BD_MSB = BD_ADDR >> 6; // Board address MSB.
|
||||||
|
|
||||||
|
// CV29_DEFAULT is the factory hardware default for CV29.
|
||||||
|
const byte CV29_DEFAULT = CV29_OUTPUT_ADDRESS_MODE |
|
||||||
|
CV29_ACCESSORY_DECODER;
|
||||||
|
|
||||||
|
enum ACC_DIR {
|
||||||
|
REV = 0, // Direction is reverse.
|
||||||
|
NORM = 1, // Direction is normal.
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ShowTypes {
|
||||||
|
S_IDLE = 0x01, // Show Idle packets.
|
||||||
|
S_RESET = 0x02, // Show Reset packets.
|
||||||
|
S_DCC = 0x04, // Show DCC information.
|
||||||
|
S_ACK = 0x08, // Basic acknowledge off.
|
||||||
|
S_ALL = 0x80, // Show raw packet data.
|
||||||
|
};
|
||||||
|
|
||||||
|
ACC_DIR AccDir = REV; // Accessory direction.
|
||||||
|
byte ShowData = 0x00; // Packet information to show.
|
||||||
|
unsigned long EndTime = 0; // End time in ms.
|
||||||
|
|
||||||
|
NmraDcc Dcc ;
|
||||||
|
|
||||||
|
struct CVPair
|
||||||
|
{
|
||||||
|
uint16_t CV;
|
||||||
|
uint8_t Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
CVPair FactoryDefaultCVs [] =
|
||||||
|
{
|
||||||
|
{CV_ACCESSORY_DECODER_ADDRESS_LSB, BD_LSB}, // Accessory address LSB.
|
||||||
|
{CV_ACCESSORY_DECODER_ADDRESS_MSB, BD_MSB}, // Accessory address MSB.
|
||||||
|
|
||||||
|
// Reload these just in case they are writeen by accident.
|
||||||
|
{CV_VERSION, VER_MAJOR}, // Decoder version.
|
||||||
|
{CV_MANUF, MANUF_ID }, // Manufacturer ID.
|
||||||
|
|
||||||
|
{CV_29_CONFIG, CV29_DEFAULT}, // Configuration CV.
|
||||||
|
{CV_MANUF_01, VER_MINOR}, // Minor decoder version.
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t FactoryDefaultCVIndex = 0;
|
||||||
|
|
||||||
|
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
|
||||||
|
Serial.println(F("notifyCVResetFactoryDefault called."));
|
||||||
|
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function is called whenever a normal DCC Turnout Packet is received
|
||||||
|
#define NOTITY_DCC_ACC_STATE
|
||||||
|
#ifdef NOTITY_DCC_ACC_STATE
|
||||||
|
void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)
|
||||||
|
{
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
Serial.print("notifyDccAccState: ") ;
|
||||||
|
Serial.print(Addr,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.print(BoardAddr,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.print(OutputAddr,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.println(State, HEX) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTITY_DCC_ACC_STATE
|
||||||
|
|
||||||
|
// This function is called whenever a normal DCC Turnout Packet is received
|
||||||
|
#define NOTITY_DCC_ACC_TURNOUT_BOARD
|
||||||
|
#ifdef NOTITY_DCC_ACC_TURNOUT_BOARD
|
||||||
|
void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower )
|
||||||
|
{
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTITY_DCC_ACC_TURNOUT_BOARD
|
||||||
|
|
||||||
|
// This function is called whenever a normal DCC Turnout Packet is received
|
||||||
|
// if NOTIFY_DCC_ACC_TURNOUT and ACCESSORY_DCC are defined.
|
||||||
|
#define NOTITY_DCC_ACC_TURNOUT_OUTPUT
|
||||||
|
#if defined(NOTITY_DCC_ACC_TURNOUT_OUTPUT) && defined(ACCESSORY_DCC)
|
||||||
|
void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower )
|
||||||
|
{
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
digitalWrite(SCOPE_PIN, HIGH);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
Serial.print("notifyDccAccTurnoutOutput: ") ;
|
||||||
|
Serial.print(Addr,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.print(Direction,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.println(OutputPower, HEX) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the output to the given direction for just the ACC_ADDR output
|
||||||
|
// since this callback is called for all 4 output addresses.
|
||||||
|
if (Addr == ACC_ADDR) {
|
||||||
|
setAcc(Direction ? REV : NORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTITY_DCC_ACC_TURNOUT_OUTPUT
|
||||||
|
|
||||||
|
// This function is called whenever a DCC Signal Aspect Packet is received
|
||||||
|
// if NOTIFY_DCC_SIG_STATE and SIGNAL_DCC are defined.
|
||||||
|
#define NOTITY_DCC_SIG_STATE
|
||||||
|
#if defined(NOTITY_DCC_SIG_STATE) && defined(SIGNAL_DCC)
|
||||||
|
void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State)
|
||||||
|
{
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
digitalWrite(SCOPE_PIN, HIGH);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
Serial.print("notifyDccSigState: ") ;
|
||||||
|
Serial.print(Addr,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.print(OutputIndex,DEC) ;
|
||||||
|
Serial.print(',');
|
||||||
|
Serial.println(State, DEC) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the output to the given direction for 1st ACC_ADDR output.
|
||||||
|
if (Addr == ACC_ADDR) {
|
||||||
|
setAcc(State == 0 ? REV : NORM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTITY_DCC_SIG_STATE
|
||||||
|
|
||||||
|
// This function is called whenever a DCC Reset packet is received.
|
||||||
|
// Uncomment to print Reset Packets
|
||||||
|
#define NOTIFY_DCC_RESET
|
||||||
|
#ifdef NOTIFY_DCC_RESET
|
||||||
|
void notifyDccReset(uint8_t hardReset )
|
||||||
|
{
|
||||||
|
if (ShowData & S_RESET) { // Show Reset packets if S_RESET is set.
|
||||||
|
Serial.printf(F("notifyDccReset: %6s.\n"), hardReset ? "HARD" : "NORMAL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_DCC_RESET
|
||||||
|
|
||||||
|
// This function is called whenever a DCC Idle packet is received.
|
||||||
|
// Uncomment to print Idle Packets
|
||||||
|
#define NOTIFY_DCC_IDLE
|
||||||
|
#ifdef NOTIFY_DCC_IDLE
|
||||||
|
void notifyDccIdle()
|
||||||
|
{
|
||||||
|
if (ShowData & S_IDLE) { // Show Idle packets if S_IDLE is set.
|
||||||
|
Serial.println("notifyDccIdle: Idle received") ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_DCC_IDLE
|
||||||
|
|
||||||
|
// Uncomment the #define below to print changed CV values.
|
||||||
|
#define NOTIFY_CV_CHANGE
|
||||||
|
#ifdef NOTIFY_CV_CHANGE
|
||||||
|
void notifyCVChange( uint16_t CV, uint8_t Value) {
|
||||||
|
Serial.printf(F("notifyCVChange: CV %4u value changed to %3u 0x%02X.\n"), CV, Value, Value);
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_CV_CHANGE
|
||||||
|
|
||||||
|
// This function is called when any DCC packet is received.
|
||||||
|
// Uncomment to print all DCC Packets
|
||||||
|
#define NOTIFY_DCC_MSG
|
||||||
|
#ifdef NOTIFY_DCC_MSG
|
||||||
|
void notifyDccMsg( DCC_MSG * Msg)
|
||||||
|
{
|
||||||
|
if (ShowData & S_ALL) { // Show all packets if S_ALL is set.
|
||||||
|
Serial.print("notifyDccMsg: ") ;
|
||||||
|
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||||
|
{
|
||||||
|
Serial.print(Msg->Data[i], HEX);
|
||||||
|
Serial.write(' ');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_DCC_MSG
|
||||||
|
|
||||||
|
// 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
|
||||||
|
void notifyCVAck(void)
|
||||||
|
{
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
digitalWrite(SCOPE_PIN, HIGH);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
if ((ShowData & S_ACK) == 0x00) { // Send CV acknowledge current pulse. [
|
||||||
|
Serial.println("notifyCVAck: Current pulse sent") ;
|
||||||
|
|
||||||
|
digitalWrite( ACK_PIN, HIGH );
|
||||||
|
delay( 6 );
|
||||||
|
digitalWrite( ACK_PIN, LOW );
|
||||||
|
}
|
||||||
|
else { // Suppress CV acknowledge current pulse.
|
||||||
|
Serial.println("notifyCVAck: Current pulse NOT sent") ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setAcc(ACC_DIR dir) {
|
||||||
|
bool on; // Output on/off.
|
||||||
|
on = dir == NORM ? true : false;
|
||||||
|
#ifdef DCC_STATUS
|
||||||
|
if (AccDir != dir) {
|
||||||
|
Serial.printf(F("Accessory changed to %4s.\n"), on ? "NORM" : "REV");
|
||||||
|
}
|
||||||
|
#endif // DCC_STATUS
|
||||||
|
AccDir = dir;
|
||||||
|
digitalWrite(ACC_A_PIN, on);
|
||||||
|
digitalWrite(ACC_B_PIN, !on);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.printf(F("Starting Accessory_Test Version %d.%d, Build date %s %s\n"),
|
||||||
|
VER_MAJOR,
|
||||||
|
VER_MINOR,
|
||||||
|
__DATE__,
|
||||||
|
__TIME__);
|
||||||
|
Serial.printf(F("Default ACC_ADDR %4u "), ACC_ADDR);
|
||||||
|
Serial.printf(F("BD_ADDR %4u 0x%04X, BD_LSB %3u 0x%02X, BD_MSB %3u 0x%02X.\n"),
|
||||||
|
BD_ADDR,
|
||||||
|
BD_ADDR,
|
||||||
|
BD_LSB,
|
||||||
|
BD_LSB,
|
||||||
|
BD_MSB,
|
||||||
|
BD_MSB);
|
||||||
|
Serial.println(F("Cmds: a - All, d - DCC, i - Idle, r - Reset, c - CV Ack off,"));
|
||||||
|
Serial.println(F(" <Other> - Everything off."));
|
||||||
|
|
||||||
|
// Set AccOn REV to force setAcc() to set the output.
|
||||||
|
AccDir = REV;
|
||||||
|
|
||||||
|
// Configure motor and fuction output pin pairs.
|
||||||
|
pinMode( ACC_A_PIN, OUTPUT);
|
||||||
|
pinMode( ACC_B_PIN, OUTPUT);
|
||||||
|
setAcc(NORM);
|
||||||
|
|
||||||
|
// Configure the DCC CV Programing ACK pin for an output
|
||||||
|
pinMode( ACK_PIN, OUTPUT );
|
||||||
|
digitalWrite( ACK_PIN, LOW);
|
||||||
|
|
||||||
|
// Configure Scope trigger output.
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
pinMode( SCOPE_PIN, OUTPUT);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||||
|
Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);
|
||||||
|
|
||||||
|
// Reset the CVs to factory default if the decode type,manuf. ID or major version
|
||||||
|
// do not match. Do this before init() since it sets these CVs.
|
||||||
|
if ( (FORCE_CVS) ||
|
||||||
|
(Dcc.getCV(CV_29_CONFIG) != CV29_DEFAULT) ||
|
||||||
|
(Dcc.getCV(CV_MANUFACTURER_ID) != MANUF_ID) ||
|
||||||
|
(Dcc.getCV(CV_VERSION_ID) != VER_MAJOR) ||
|
||||||
|
(Dcc.getCV(CV_MANUF_01) != VER_MINOR))
|
||||||
|
{
|
||||||
|
notifyCVResetFactoryDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the main DCC Init function to enable the DCC Receiver
|
||||||
|
Dcc.init( MANUF_ID, VER_MAJOR, // FLAGS_MY_ADDRESS_ONLY |
|
||||||
|
CV29_DEFAULT, 0 );
|
||||||
|
|
||||||
|
// Make sure CV_MANUF_01 CV matches VER_MINOR.
|
||||||
|
Dcc.setCV(CV_MANUF_01, VER_MINOR);
|
||||||
|
|
||||||
|
Serial.println(F("Init Done"));
|
||||||
|
|
||||||
|
// Flush serial prior to entering loop().
|
||||||
|
Serial.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||||
|
Dcc.process();
|
||||||
|
|
||||||
|
if (Serial.available()) {
|
||||||
|
// Get the new byte and process it.
|
||||||
|
switch ((char)Serial.read()) {
|
||||||
|
case 'a':
|
||||||
|
if (ShowData & S_ALL) {
|
||||||
|
ShowData &= ~S_ALL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_ALL;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_ALL ? "All ON" : "All OFF");
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
ShowData &= ~S_DCC;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_DCC;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_DCC ? "DCC ON" : "DCC OFF");
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
if (ShowData & S_IDLE) {
|
||||||
|
ShowData &= ~S_IDLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_IDLE;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_IDLE ? "Idle ON" : "Idle OFF");
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (ShowData & S_RESET) {
|
||||||
|
ShowData &= ~S_RESET;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_RESET;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_RESET ? "Reset ON" : "Reset OFF");
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
if (ShowData & S_ACK) {
|
||||||
|
ShowData &= ~S_ACK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_ACK;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_ACK ? "Ack OFF" : "Ack ON");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (ShowData != 0x00) {
|
||||||
|
EndTime = millis() + DELAY_TIME;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((EndTime != 0) && (millis() > EndTime)) {
|
||||||
|
Serial.printf(F("Clearing ShowData 0x%02X\n"), ShowData);
|
||||||
|
ShowData = 0x00;
|
||||||
|
EndTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
|
||||||
|
{
|
||||||
|
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||||
|
Serial.printf(F("CV %4u reset to factory default %3u 0x%02X.\n"),
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].CV,
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].Value,
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||||
|
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV,
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1388
examples/NmraValidation/Loco Cap 2.1/arlc_3n.log
Executable file
1388
examples/NmraValidation/Loco Cap 2.1/arlc_3n.log
Executable file
File diff suppressed because it is too large
Load Diff
1383
examples/NmraValidation/Loco Cap 2.1/arlc_3n.sum
Executable file
1383
examples/NmraValidation/Loco Cap 2.1/arlc_3n.sum
Executable file
File diff suppressed because it is too large
Load Diff
1388
examples/NmraValidation/Loco Cap 2.1/arlc_3r.log
Executable file
1388
examples/NmraValidation/Loco Cap 2.1/arlc_3r.log
Executable file
File diff suppressed because it is too large
Load Diff
1383
examples/NmraValidation/Loco Cap 2.1/arlc_3r.sum
Executable file
1383
examples/NmraValidation/Loco Cap 2.1/arlc_3r.sum
Executable file
File diff suppressed because it is too large
Load Diff
1388
examples/NmraValidation/Loco Cap 2.1/arlc_5n.log
Executable file
1388
examples/NmraValidation/Loco Cap 2.1/arlc_5n.log
Executable file
File diff suppressed because it is too large
Load Diff
1383
examples/NmraValidation/Loco Cap 2.1/arlc_5n.sum
Executable file
1383
examples/NmraValidation/Loco Cap 2.1/arlc_5n.sum
Executable file
File diff suppressed because it is too large
Load Diff
1388
examples/NmraValidation/Loco Cap 2.1/arlc_5r.log
Executable file
1388
examples/NmraValidation/Loco Cap 2.1/arlc_5r.log
Executable file
File diff suppressed because it is too large
Load Diff
1383
examples/NmraValidation/Loco Cap 2.1/arlc_5r.sum
Executable file
1383
examples/NmraValidation/Loco Cap 2.1/arlc_5r.sum
Executable file
File diff suppressed because it is too large
Load Diff
1388
examples/NmraValidation/Loco Cap 2.1/arlc_6n.log
Executable file
1388
examples/NmraValidation/Loco Cap 2.1/arlc_6n.log
Executable file
File diff suppressed because it is too large
Load Diff
1383
examples/NmraValidation/Loco Cap 2.1/arlc_6n.sum
Executable file
1383
examples/NmraValidation/Loco Cap 2.1/arlc_6n.sum
Executable file
File diff suppressed because it is too large
Load Diff
1388
examples/NmraValidation/Loco Cap 2.1/arlc_6r.log
Executable file
1388
examples/NmraValidation/Loco Cap 2.1/arlc_6r.log
Executable file
File diff suppressed because it is too large
Load Diff
1383
examples/NmraValidation/Loco Cap 2.1/arlc_6r.sum
Executable file
1383
examples/NmraValidation/Loco Cap 2.1/arlc_6r.sum
Executable file
File diff suppressed because it is too large
Load Diff
486
examples/NmraValidation/Loco_Test/Loco_Test.ino
Executable file
486
examples/NmraValidation/Loco_Test/Loco_Test.ino
Executable file
@@ -0,0 +1,486 @@
|
|||||||
|
/***********************************************************************************************
|
||||||
|
*
|
||||||
|
* This sketch tests the NmraDcc library as a multifunction decoder.
|
||||||
|
* Author: Kenneth West
|
||||||
|
* kgw4449@gmail.com
|
||||||
|
*
|
||||||
|
* This sketch has added the printf() function to the Print class.
|
||||||
|
* You can find instructions for doing this here:
|
||||||
|
* http://playground.arduino.cc/Main/Printf
|
||||||
|
*
|
||||||
|
* This sketch is based on NmraDcc library NmraDccMultiFunctionDecoder_1 example.
|
||||||
|
* You can find the library here:
|
||||||
|
* https://github.com/mrrwa/NmraDcc
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* For more information refer to the file: README.md here:
|
||||||
|
* https://github.com/IowaScaledEngineering/ard-dccshield
|
||||||
|
*
|
||||||
|
* This demo assumes the following Jumper settings on the ARD-DCCSHIELD
|
||||||
|
*
|
||||||
|
* JP1 - I2C Pull-Up Resistors - Don't Care
|
||||||
|
* JP2 - (Pins 1-2) I2C /IORST JP2 - Don't-Care
|
||||||
|
* JP2 - (Pins 3-4) - DCC Signal to Arduino Pin - OFF
|
||||||
|
* JP3 - I2C /INT and /OE - Don't-Care
|
||||||
|
* JP4 - DCC Signal to Arduino Pin - D2 ON
|
||||||
|
* JP5 - Arduino Powered from DCC - User Choice
|
||||||
|
* JP6 - Boards without VIO - User Choice
|
||||||
|
* JP7 - Enable Programming ACK - 1-2 ON 3-4 ON
|
||||||
|
*
|
||||||
|
* The connections are as follows:
|
||||||
|
*
|
||||||
|
* Pin Name Mode Description
|
||||||
|
* ----------------------------------------------------------------------------------
|
||||||
|
* D2 DCC_PIN INPUT_PULLUP DCC input signal.
|
||||||
|
* A1 ACK_PIN OUTPUT CV acknowledge control.
|
||||||
|
* A0 MOTOR_A_PIN OUTPUT Motor output A.
|
||||||
|
* D13 MOTOR_B_PIN OUTPUT Motor output B.
|
||||||
|
* D12 FUNC_A_PIN OUTPUT Function output A.
|
||||||
|
* D11 FUNC_B_PIN OUTPUT Function output B.
|
||||||
|
* D4 SCOPE_PIN OUTPUT SCOPE trigger.
|
||||||
|
*
|
||||||
|
**********************************************************************************************/
|
||||||
|
// Column locations.
|
||||||
|
//3456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456
|
||||||
|
// 1 2 3 4 5 6 7 8 9 9
|
||||||
|
//const unsigned long FAKE_LONG = 10000UL; // Fake variable for column positions.
|
||||||
|
|
||||||
|
#include <NmraDcc.h>
|
||||||
|
|
||||||
|
// Uncomment this line to print out minimal status information.
|
||||||
|
#define DCC_STATUS
|
||||||
|
|
||||||
|
// Uncomment this line to issue scope trigger at beginning of motor callback.
|
||||||
|
#define DO_SCOPE
|
||||||
|
|
||||||
|
const byte VER_MAJOR = 2; // Major version in CV 7
|
||||||
|
const byte VER_MINOR = 1; // Minor version in CV 112
|
||||||
|
const byte DCC_PIN = 2; // DCC input pin.
|
||||||
|
const int ACK_PIN = A1; // CV acknowledge pin.
|
||||||
|
const byte MOTOR_A_PIN = A0; // Motor out A pin.
|
||||||
|
const byte MOTOR_B_PIN = 13; // Motor out B pin.
|
||||||
|
const byte FUNC_A_PIN = 12; // Function A pin.
|
||||||
|
const byte FUNC_B_PIN = 11; // Function B pin.
|
||||||
|
const byte SCOPE_PIN = 4; // Scope trigger pin.
|
||||||
|
const byte CV_VERSION = 7; // Decoder version.
|
||||||
|
const byte CV_MANUF = 8; // Manufacturer ID.
|
||||||
|
const byte CV_MANUF_01 = 112; // Manufacturer Unique 01.
|
||||||
|
const byte MANUF_ID = MAN_ID_DIY; // Manufacturer ID in CV 8.
|
||||||
|
const byte DECODER_ADDR = 3; // Decoder address.
|
||||||
|
const byte SP_ESTOP = 0; // Emergency stop speed.
|
||||||
|
const byte SP_STOP = 1; // Stop speed value.
|
||||||
|
const unsigned long DELAY_TIME = 50; // Delay time in ms.
|
||||||
|
|
||||||
|
// Note: Set FORCE_CV_WRITE true to force CVs to update if just the address is changed.
|
||||||
|
const bool FORCE_CV_WRITE = false; // Set true to force CV write.
|
||||||
|
|
||||||
|
enum ShowTypes {
|
||||||
|
S_IDLE = 0x01, // Show Idle packets.
|
||||||
|
S_RESET = 0x02, // Show Reset packets.
|
||||||
|
S_DCC = 0x04, // Show DCC information.
|
||||||
|
S_ACK = 0x08, // Basic acknowledge off.
|
||||||
|
S_ALL = 0x80, // Show raw packet data.
|
||||||
|
};
|
||||||
|
|
||||||
|
bool MotorFwd = false; // Motor direction.
|
||||||
|
bool FuncOn = true; // Function on/off.
|
||||||
|
byte ShowData = 0x00; // Packet information to show.
|
||||||
|
unsigned long EndTime = 0; // End time in ms.
|
||||||
|
|
||||||
|
struct CVPair
|
||||||
|
{
|
||||||
|
uint16_t CV;
|
||||||
|
uint8_t Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
CVPair FactoryDefaultCVs [] =
|
||||||
|
{
|
||||||
|
// The CV Below defines the Short DCC Address
|
||||||
|
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, DECODER_ADDR}, // Short address.
|
||||||
|
|
||||||
|
// Reload these just in case they are writeen by accident.
|
||||||
|
{CV_VERSION, VER_MAJOR}, // Decoder version.
|
||||||
|
{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_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
|
||||||
|
{CV_MANUF_01, VER_MINOR}, // Minor decoder version.
|
||||||
|
};
|
||||||
|
|
||||||
|
NmraDcc Dcc ;
|
||||||
|
|
||||||
|
uint8_t FactoryDefaultCVIndex = 0;
|
||||||
|
|
||||||
|
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
|
||||||
|
Serial.println(F("notifyCVResetFactoryDefault called."));
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
digitalWrite(SCOPE_PIN, HIGH);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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" );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((Speed > SP_STOP) && (Dir == DCC_DIR_REV)) {
|
||||||
|
setMotor(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setMotor(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif // NOTIFY_DCC_SPEED
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
{
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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:
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
Serial.print(" FN0: ");
|
||||||
|
Serial.println((FuncState & FN_BIT_00) ? "1 " : "0 ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif // NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||||
|
|
||||||
|
case FN_0_4:
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FuncState & FN_BIT_00) {
|
||||||
|
setFunc(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setFunc(false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FN_5_8:
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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:
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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:
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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:
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
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 // NOTIFY_DCC_FUNC
|
||||||
|
|
||||||
|
// Uncomment the #define below to print all Reset Packets.
|
||||||
|
#define NOTIFY_DCC_RESET
|
||||||
|
#ifdef NOTIFY_DCC_RESET
|
||||||
|
void notifyDccReset(uint8_t hardReset) {
|
||||||
|
setMotor(true);
|
||||||
|
setFunc(false);
|
||||||
|
if (ShowData & S_RESET) {
|
||||||
|
Serial.printf(F("notifyDccReset: %6s.\n"), hardReset ? "HARD" : "NORMAL");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_DCC_RESET
|
||||||
|
|
||||||
|
// This function is called whenever a DCC Idle packet is received.
|
||||||
|
// Uncomment to print Idle Packets
|
||||||
|
#define NOTIFY_DCC_IDLE
|
||||||
|
#ifdef NOTIFY_DCC_IDLE
|
||||||
|
void notifyDccIdle()
|
||||||
|
{
|
||||||
|
if (ShowData & S_IDLE) { // Show Idle packets if S_IDLE is set.
|
||||||
|
Serial.println("notifyDccIdle: Idle received") ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_DCC_IDLE
|
||||||
|
|
||||||
|
// Uncomment the #define below to print changed CV values.
|
||||||
|
#define NOTIFY_CV_CHANGE
|
||||||
|
#ifdef NOTIFY_CV_CHANGE
|
||||||
|
void notifyCVChange( uint16_t CV, uint8_t Value) {
|
||||||
|
Serial.printf(F("notifyCVChange: CV %4u value changed to %3u 0x%02X.\n"), CV, Value, Value);
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_CV_CHANGE
|
||||||
|
|
||||||
|
// This function is called when any DCC packet is received.
|
||||||
|
// Uncomment to print all DCC Packets
|
||||||
|
#define NOTIFY_DCC_MSG
|
||||||
|
#ifdef NOTIFY_DCC_MSG
|
||||||
|
void notifyDccMsg( DCC_MSG * Msg)
|
||||||
|
{
|
||||||
|
if (ShowData & S_ALL) { // Show all packets if S_ALL is set.
|
||||||
|
Serial.print("notifyDccMsg: ") ;
|
||||||
|
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||||
|
{
|
||||||
|
Serial.print(Msg->Data[i], HEX);
|
||||||
|
Serial.write(' ');
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // NOTIFY_DCC_MSG
|
||||||
|
|
||||||
|
// 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
|
||||||
|
void notifyCVAck(void)
|
||||||
|
{
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
digitalWrite(SCOPE_PIN, HIGH);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
if ((ShowData & S_ACK) == 0x00) { // Send CV acknowledge current pulse. [
|
||||||
|
Serial.println("notifyCVAck: Current pulse sent") ;
|
||||||
|
|
||||||
|
digitalWrite( ACK_PIN, HIGH );
|
||||||
|
delay( 6 );
|
||||||
|
digitalWrite( ACK_PIN, LOW );
|
||||||
|
}
|
||||||
|
else { // Suppress CV acknowledge current pulse.
|
||||||
|
Serial.println("notifyCVAck: Current pulse NOT sent") ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMotor(bool fwd) {
|
||||||
|
#ifdef DCC_STATUS
|
||||||
|
if (MotorFwd != fwd) {
|
||||||
|
Serial.printf(F("Motor changed to %3s.\n"), fwd ? "FWD" : "REV");
|
||||||
|
}
|
||||||
|
#endif // DCC_STATUS
|
||||||
|
MotorFwd = fwd;
|
||||||
|
digitalWrite(MOTOR_A_PIN, fwd);
|
||||||
|
digitalWrite(MOTOR_B_PIN, !fwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFunc(bool on) {
|
||||||
|
#ifdef DCC_STATUS
|
||||||
|
if (FuncOn != on) {
|
||||||
|
Serial.printf(F("Function changed to %3s.\n"), on ? "ON" : "OFF");
|
||||||
|
}
|
||||||
|
#endif // DCC_STATUS
|
||||||
|
FuncOn = on;
|
||||||
|
digitalWrite(FUNC_A_PIN, !on);
|
||||||
|
digitalWrite(FUNC_B_PIN, on);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
Serial.begin(115200);
|
||||||
|
Serial.print(F("NMRA Dcc Loco_Test "));
|
||||||
|
Serial.printf(F(" Version %d.%d, Build date %s %s\n"), VER_MAJOR,
|
||||||
|
VER_MINOR,
|
||||||
|
__DATE__,
|
||||||
|
__TIME__);
|
||||||
|
Serial.println(F("Cmds: a - All, d - DCC, i - Idle, r - Reset, c - CV Ack off,"));
|
||||||
|
Serial.println(F(" <Other> - Everything off."));
|
||||||
|
|
||||||
|
// Set MotorFwd false and FuncOn true to force the set the output.
|
||||||
|
MotorFwd = false;
|
||||||
|
FuncOn = true;
|
||||||
|
|
||||||
|
// Configure motor and fuction output pin pairs.
|
||||||
|
pinMode( MOTOR_A_PIN, OUTPUT);
|
||||||
|
pinMode( MOTOR_B_PIN, OUTPUT);
|
||||||
|
setMotor(true);
|
||||||
|
pinMode( FUNC_A_PIN, OUTPUT);
|
||||||
|
pinMode( FUNC_B_PIN, OUTPUT);
|
||||||
|
setFunc(false);
|
||||||
|
|
||||||
|
// Configure Scope trigger output.
|
||||||
|
#ifdef DO_SCOPE
|
||||||
|
pinMode( SCOPE_PIN, OUTPUT);
|
||||||
|
digitalWrite(SCOPE_PIN, LOW);
|
||||||
|
#endif // DO_SCOPE
|
||||||
|
|
||||||
|
// Configure the DCC CV Programing ACK and set it LOW to keep the ACK current off.
|
||||||
|
pinMode( ACK_PIN, OUTPUT );
|
||||||
|
digitalWrite(ACK_PIN, LOW );
|
||||||
|
|
||||||
|
// Setup which External Interrupt, the Pin it's associated with that we're using
|
||||||
|
// and enable the Pull-Up.
|
||||||
|
Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, 1);
|
||||||
|
|
||||||
|
// Reset the CVs to factory default if the manuf. ID or major version do not match.
|
||||||
|
// Do this before init() since it sets these CVs.
|
||||||
|
if ( ( FORCE_CV_WRITE) ||
|
||||||
|
((Dcc.getCV(CV_29_CONFIG) & CV29_ACCESSORY_DECODER) != 0) ||
|
||||||
|
( Dcc.getCV(CV_MANUFACTURER_ID) != MANUF_ID) ||
|
||||||
|
( Dcc.getCV(CV_VERSION_ID) != VER_MAJOR) ||
|
||||||
|
( Dcc.getCV(CV_MANUF_01) != VER_MINOR))
|
||||||
|
{
|
||||||
|
notifyCVResetFactoryDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dcc.init( MANUF_ID, VER_MAJOR, FLAGS_MY_ADDRESS_ONLY, 0 );
|
||||||
|
|
||||||
|
// Make sure CV_MANUF_01 CV matches VER_MINOR.
|
||||||
|
Dcc.setCV(CV_MANUF_01, VER_MINOR);
|
||||||
|
|
||||||
|
Serial.println(F("Init Done"));
|
||||||
|
|
||||||
|
// Flush serial prior to entering loop().
|
||||||
|
Serial.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||||
|
Dcc.process();
|
||||||
|
|
||||||
|
if (Serial.available()) {
|
||||||
|
// Get the new byte and process it.
|
||||||
|
switch ((char)Serial.read()) {
|
||||||
|
case 'a':
|
||||||
|
if (ShowData & S_ALL) {
|
||||||
|
ShowData &= ~S_ALL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_ALL;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_ALL ? "All ON" : "All OFF");
|
||||||
|
break;
|
||||||
|
case 'd':
|
||||||
|
if (ShowData & S_DCC) {
|
||||||
|
ShowData &= ~S_DCC;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_DCC;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_DCC ? "DCC ON" : "DCC OFF");
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
if (ShowData & S_IDLE) {
|
||||||
|
ShowData &= ~S_IDLE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_IDLE;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_IDLE ? "Idle ON" : "Idle OFF");
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (ShowData & S_RESET) {
|
||||||
|
ShowData &= ~S_RESET;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_RESET;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_RESET ? "Reset ON" : "Reset OFF");
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
if (ShowData & S_ACK) {
|
||||||
|
ShowData &= ~S_ACK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ShowData |= S_ACK;
|
||||||
|
}
|
||||||
|
Serial.println(ShowData & S_ACK ? "Ack OFF" : "Ack ON");
|
||||||
|
break;
|
||||||
|
case '\n':
|
||||||
|
case '\r':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (ShowData != 0x00) {
|
||||||
|
EndTime = millis() + DELAY_TIME;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((EndTime != 0) && (millis() > EndTime)) {
|
||||||
|
Serial.printf(F("Clearing ShowData 0x%02x\n"), ShowData);
|
||||||
|
ShowData = 0x00;
|
||||||
|
EndTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
|
||||||
|
{
|
||||||
|
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||||
|
Serial.printf(F("CV %4u reset to factory default %3u 0x%02X.\n"),
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].CV,
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].Value,
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||||
|
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV,
|
||||||
|
FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
@@ -1,5 +1,5 @@
|
|||||||
name=NmraDcc
|
name=NmraDcc
|
||||||
version=1.4.2
|
version=1.4.4
|
||||||
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda)
|
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda)
|
||||||
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
||||||
sentence=Enables NMRA DCC Communication
|
sentence=Enables NMRA DCC Communication
|
||||||
|
Reference in New Issue
Block a user