Compare commits
6 Commits
master
...
Accessory-
Author | SHA1 | Date | |
---|---|---|---|
|
4b52381fa4 | ||
|
5f1e271f2c | ||
|
6f91d4f7d4 | ||
|
b35c1fbbd0 | ||
|
f59048383f | ||
|
6333abf691 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
||||
*.zip
|
||||
*.orig
|
||||
*~
|
||||
*.txt
|
||||
|
183
NmraDcc.cpp
183
NmraDcc.cpp
@@ -47,11 +47,8 @@
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
#include "NmraDcc.h"
|
||||
#ifdef ARDUINO_SAMD_ZERO
|
||||
#include <FlashStorage_SAMD.h>
|
||||
#else
|
||||
#include "EEPROM.h"
|
||||
#endif
|
||||
#include <elapsedMillis.h>
|
||||
|
||||
// Uncomment to print DEBUG messages
|
||||
// #define DEBUG_PRINT
|
||||
@@ -246,8 +243,13 @@
|
||||
|
||||
#endif
|
||||
#ifdef DEBUG_PRINT
|
||||
#ifdef ARDUINO_ARCH_RP2040
|
||||
#define DB_PRINT( x, ... ) { char dbgbuf[80]; sprintf( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.println( dbgbuf ); }
|
||||
#define DB_PRINT_( x, ... ) { char dbgbuf[80]; sprintf( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.print( dbgbuf ); }
|
||||
#else
|
||||
#define DB_PRINT( x, ... ) { char dbgbuf[80]; sprintf_P( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.println( dbgbuf ); }
|
||||
#define DB_PRINT_( x, ... ) { char dbgbuf[80]; sprintf_P( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.print( dbgbuf ); }
|
||||
#endif
|
||||
#else
|
||||
#define DB_PRINT( x, ... ) ;
|
||||
#define DB_PRINT_( x, ... ) ;
|
||||
@@ -262,14 +264,8 @@
|
||||
#elif defined ( ESP32 )
|
||||
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static byte ISRWatch; // Interrupt Handler Edge Filter
|
||||
#elif defined ( ARDUINO_AVR_NANO_EVERY )
|
||||
static PinStatus ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
#elif defined ( ARDUINO_ARCH_RP2040)
|
||||
static PinStatus ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static byte ISRWatch; // Interrupt Handler Edge Filter
|
||||
#elif defined ( ARDUINO_ARCH_RENESAS_UNO)
|
||||
static PinStatus ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static byte ISRWatch; // Interrupt Handler Edge Filter
|
||||
#elif defined ( ARDUINO_AVR_NANO_EVERY ) || defined(ARDUINO_ARCH_RP2040)
|
||||
static PinStatus ISREdge;
|
||||
#else
|
||||
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static byte ISRWatch; // Interrupt Handler Edge Filter
|
||||
@@ -332,6 +328,11 @@ typedef struct
|
||||
uint8_t TickCount;
|
||||
uint8_t NestedIrqCount;
|
||||
#endif
|
||||
#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
elapsedMillis millisSinceEEPromWrite = 0;
|
||||
bool eepromChanged = false;
|
||||
#endif
|
||||
|
||||
}
|
||||
DCC_PROCESSOR_STATE ;
|
||||
|
||||
@@ -349,7 +350,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
{
|
||||
SET_TP3;
|
||||
|
||||
#if defined(ESP32) || defined ( ARDUINO_ARCH_RP2040)
|
||||
#ifdef ESP32
|
||||
// switch (ISRWatch)
|
||||
// {
|
||||
// case RISING: if (digitalRead(DccProcState.ExtIntPinNum)) break;
|
||||
@@ -426,7 +427,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt (DccProcState.ExtIntNum);
|
||||
#endif
|
||||
#if defined(ESP32) || defined ( ARDUINO_ARCH_RP2040)
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt (DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge);
|
||||
@@ -517,7 +518,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt (DccProcState.ExtIntNum);
|
||||
#endif
|
||||
#if defined(ESP32) || defined ( ARDUINO_ARCH_RP2040)
|
||||
#if defined(ESP32)
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt (DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge);
|
||||
@@ -574,7 +575,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt (DccProcState.ExtIntNum);
|
||||
#endif
|
||||
#if defined(ESP32) || defined ( ARDUINO_ARCH_RP2040)
|
||||
#if defined(ESP32)
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt (DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge);
|
||||
@@ -623,7 +624,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
detachInterrupt (DccProcState.ExtIntNum);
|
||||
#endif
|
||||
|
||||
#if defined(ESP32) || defined ( ARDUINO_ARCH_RP2040)
|
||||
#if defined(ESP32)
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt (DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge);
|
||||
@@ -683,7 +684,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
portENTER_CRITICAL_ISR (&mux);
|
||||
#endif
|
||||
DccRx.PacketCopy = DccRx.PacketBuf ;
|
||||
DccRx.DataReady += 1 ;
|
||||
DccRx.DataReady = 1 ;
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL_ISR (&mux);
|
||||
#endif
|
||||
@@ -753,7 +754,7 @@ DCC_PROCESSOR_STATE DccProcState ;
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt (DccProcState.ExtIntNum);
|
||||
#endif
|
||||
#if defined(ESP32) || defined ( ARDUINO_ARCH_RP2040)
|
||||
#ifdef ESP32
|
||||
ISRWatch = CHANGE;
|
||||
#else
|
||||
attachInterrupt (DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
|
||||
@@ -812,17 +813,10 @@ void writeEEPROM (unsigned int CV, uint8_t Value)
|
||||
{
|
||||
EEPROM.write (CV, Value) ;
|
||||
|
||||
#if defined(ESP8266)
|
||||
noInterrupts();
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
EEPROM.commit();
|
||||
DccProcState.eepromChanged = true;
|
||||
DccProcState.millisSinceEEPromWrite = 0;
|
||||
#endif
|
||||
|
||||
#if defined(ESP8266)
|
||||
interrupts();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool readyEEPROM()
|
||||
@@ -908,7 +902,7 @@ uint16_t getMyAddr (void)
|
||||
|
||||
if (DccProcState.cv29Value & CV29_ACCESSORY_DECODER) // Accessory Decoder?
|
||||
{
|
||||
if (DccProcState.cv29Value & CV29_OUTPUT_ADDRESS_MODE)
|
||||
if (DccProcState.cv29Value & (CV29_OUTPUT_ADDRESS_MODE | CV29_EXT_ADDRESSING))
|
||||
DccProcState.myDccAddress = (readCV (CV_ACCESSORY_DECODER_ADDRESS_MSB) << 8) | readCV (CV_ACCESSORY_DECODER_ADDRESS_LSB);
|
||||
else
|
||||
DccProcState.myDccAddress = ( (readCV (CV_ACCESSORY_DECODER_ADDRESS_MSB) & 0b00000111) << 6) | (readCV (CV_ACCESSORY_DECODER_ADDRESS_LSB) & 0b00111111) ;
|
||||
@@ -1356,8 +1350,8 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
#endif
|
||||
|
||||
BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000) << 2) | (pDccMsg->Data[0] & 0b00111111) ;
|
||||
TurnoutPairIndex = (pDccMsg->Data[1] & 0b00000110) >> 1;
|
||||
DB_PRINT ("eDP: BAddr:%d, Index:%d", BoardAddress, TurnoutPairIndex);
|
||||
TurnoutPairIndex = ( pDccMsg->Data[1] & 0b00000110) >> 1;
|
||||
DB_PRINT ("eDP: Board Address: %d, Index: %d", BoardAddress, TurnoutPairIndex);
|
||||
|
||||
// First check for Legacy Accessory Decoder Configuration Variable Access Instruction
|
||||
// as it's got a different format to the others
|
||||
@@ -1373,22 +1367,21 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
|
||||
uint16_t cvAddress = ( (pDccMsg->Data[1] & 0b00000011) << 8) + pDccMsg->Data[2] + 1;
|
||||
uint8_t cvValue = pDccMsg->Data[3];
|
||||
DB_PRINT ("eDP: CV:%d Value:%d", cvAddress, cvValue);
|
||||
DB_PRINT ("eDP: CV: %d Value: %d", cvAddress, cvValue);
|
||||
if (validCV (cvAddress, 1))
|
||||
writeCV (cvAddress, cvValue);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
OutputAddress = ( ( (BoardAddress - 1) << 2) | TurnoutPairIndex) + 1 ; //decoder output addresses start with 1, packet address range starts with 0
|
||||
// ( according to NMRA 9.2.2 )
|
||||
DB_PRINT ("eDP: OAddr:%d", OutputAddress);
|
||||
DB_PRINT ("eDP: Output Address: %d", OutputAddress);
|
||||
|
||||
if (DccProcState.inAccDecDCCAddrNextReceivedMode)
|
||||
{
|
||||
if (DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE)
|
||||
if (DccProcState.Flags & (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_EXTENDED_ADDRESS_MODE))
|
||||
{
|
||||
DB_PRINT ("eDP: Set OAddr:%d", OutputAddress);
|
||||
DB_PRINT ("eDP: 11-bit Accessory Address: %d", OutputAddress);
|
||||
//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) (OutputAddress % 256));
|
||||
writeCV (CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t) (OutputAddress / 256));
|
||||
@@ -1398,7 +1391,7 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
}
|
||||
else
|
||||
{
|
||||
DB_PRINT ("eDP: Set BAddr:%d", BoardAddress);
|
||||
DB_PRINT ("eDP: Set 9-bit Board Address: %d", BoardAddress);
|
||||
writeCV (CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t) (BoardAddress % 64));
|
||||
writeCV (CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t) (BoardAddress / 64));
|
||||
|
||||
@@ -1412,12 +1405,12 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
// 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)
|
||||
DB_PRINT ("eDP: Check if the Address matches");
|
||||
if (DccProcState.Flags & (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_EXTENDED_ADDRESS_MODE))
|
||||
{
|
||||
DB_PRINT (" AddrChk: OAddr:%d, BAddr:%d, myAddr:%d Chk=%d", OutputAddress, BoardAddress, getMyAddr(), OutputAddress != getMyAddr());
|
||||
if (OutputAddress != getMyAddr() && OutputAddress < 2045)
|
||||
{
|
||||
DB_PRINT (" eDP: OAddr:%d, myAddr:%d - no match", OutputAddress, getMyAddr());
|
||||
DB_PRINT ("eDP: OAddr: %d, myAddr: %d - no match", OutputAddress, getMyAddr());
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1425,10 +1418,11 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
{
|
||||
if ( (BoardAddress != getMyAddr()) && (BoardAddress < 511))
|
||||
{
|
||||
DB_PRINT (" eDP: BAddr:%d, myAddr:%d - no match", BoardAddress, getMyAddr());
|
||||
DB_PRINT ("eDP: BAddr: %d, myAddr: %d - no match", BoardAddress, getMyAddr());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DB_PRINT ("eDP: Address Matched");
|
||||
}
|
||||
|
||||
@@ -1438,7 +1432,7 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
// According to the NMRA Dcc Spec the Signal State should only use the lower 5 Bits,
|
||||
// however some manufacturers seem to allow/use all 8 bits, so we'll relax that constraint for now
|
||||
uint8_t state = pDccMsg->Data[2] ;
|
||||
DB_PRINT ("eDP: OAddr:%d Extended State:%0X", OutputAddress, state);
|
||||
DB_PRINT ("eDP: Output Address: %d Extended State: %0X", OutputAddress, state);
|
||||
if (notifyDccSigOutputState)
|
||||
notifyDccSigOutputState (OutputAddress, state);
|
||||
|
||||
@@ -1456,45 +1450,82 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
if (notifyDccAccState)
|
||||
notifyDccAccState (OutputAddress, BoardAddress, pDccMsg->Data[1] & 0b00000111, outputPower);
|
||||
|
||||
if (DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE)
|
||||
if (DccProcState.Flags & FLAGS_EXTENDED_ADDRESS_MODE)
|
||||
{
|
||||
DB_PRINT ("eDP: OAddr:%d Turnout Dir:%d Output Power:%d", OutputAddress, direction, outputPower);
|
||||
DB_PRINT ("eDP: Output Address: %d Turnout Dir: %d Output Power: %d", OutputAddress, direction, outputPower);
|
||||
if (notifyDccAccTurnoutOutput)
|
||||
notifyDccAccTurnoutOutput (OutputAddress, direction, outputPower);
|
||||
}
|
||||
else
|
||||
{
|
||||
DB_PRINT ("eDP: Turnout Pair Index:%d Dir:%d Output Power: ", TurnoutPairIndex, direction, outputPower);
|
||||
DB_PRINT ("eDP: Turnout Pair Index: %d Dir: %d Output Power: ", TurnoutPairIndex, direction, outputPower);
|
||||
if (notifyDccAccTurnoutBoard)
|
||||
notifyDccAccTurnoutBoard (BoardAddress, TurnoutPairIndex, direction, outputPower);
|
||||
}
|
||||
}
|
||||
else if (pDccMsg->Size == 6) // Accessory Decoder OPS Mode Programming
|
||||
{
|
||||
DB_PRINT ("eDP: OPS Mode CV Programming Command");
|
||||
// Check for unsupported OPS Mode Addressing mode
|
||||
if ( ( (pDccMsg->Data[1] & 0b10001001) != 1) && ( (pDccMsg->Data[1] & 0b10001111) != 0x80))
|
||||
{
|
||||
DB_PRINT ("eDP: Unsupported OPS Mode CV Addressing Mode");
|
||||
return;
|
||||
DB_PRINT ("eDP: Accessory OPS Mode CV Programming Command");
|
||||
// If its a "Basic Accessory Decoder Packet" OPS Command
|
||||
if (pDccMsg->Data[1] & 0x80)
|
||||
{
|
||||
DB_PRINT ("eDP: Handle Basic OPS Command");
|
||||
|
||||
if(DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE)
|
||||
{
|
||||
DB_PRINT ("eDP: Ignore Basic OPS Command in Signal Address Mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if(pDccMsg->Data[1] & 0b00001111)
|
||||
{
|
||||
DB_PRINT ("eDP: Handle 11-bit Basic OPS Command");
|
||||
if((DccProcState.Flags & FLAGS_EXTENDED_ADDRESS_MODE) == 0)
|
||||
{
|
||||
DB_PRINT ("eDP: Ignore 11-bit Basic OPS Command in Board Address Mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if((OutputAddress != getMyAddr()) && (OutputAddress < 2045))
|
||||
{
|
||||
DB_PRINT ("eDP: Extended Address Not Matched");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DB_PRINT ("eDP: Handle 9-bit Basic OPS Command");
|
||||
if(DccProcState.Flags & FLAGS_EXTENDED_ADDRESS_MODE)
|
||||
{
|
||||
DB_PRINT ("eDP: Ignore 9-bit Basic OPS Command in Extended Address Mode");
|
||||
return;
|
||||
}
|
||||
|
||||
if ( (BoardAddress != getMyAddr()) && (BoardAddress < 511))
|
||||
{
|
||||
DB_PRINT ("eDP: Board Address Not Matched");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this command is for our address or the broadcast address
|
||||
if (DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE)
|
||||
{
|
||||
DB_PRINT ("eDP: Check Output Address:%d", OutputAddress);
|
||||
// If its a "Extended (Signal) Decoder OPS Command
|
||||
else if ((pDccMsg->Data[1] & 0x81) == 0x01)
|
||||
{
|
||||
DB_PRINT ("eDP: Handle Signal Decoder OPS Command");
|
||||
|
||||
// If we're not in FLAGS_OUTPUT_ADDRESS_MODE then return as this isn't for us
|
||||
if ((DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE) == 0)
|
||||
{
|
||||
DB_PRINT ("eDP: Ignore Signal Decoder OPS Command in Board or Extended Address Mode");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this command is for our Output Address or the broadcast address
|
||||
DB_PRINT ("eDP: Compare Signal Address: %d to MyAddr: %d", OutputAddress, getMyAddr());
|
||||
if ( (OutputAddress != getMyAddr()) && (OutputAddress < 2045))
|
||||
{
|
||||
DB_PRINT ("eDP: Output Address Not Matched");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DB_PRINT ("eDP: Check Board Address:%d", BoardAddress);
|
||||
if ( (BoardAddress != getMyAddr()) && (BoardAddress < 511))
|
||||
{
|
||||
DB_PRINT ("eDP: Board Address Not Matched");
|
||||
DB_PRINT ("eDP: Signal Address Not Matched");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -1504,7 +1535,7 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
|
||||
OpsInstructionType insType = (OpsInstructionType) ( (pDccMsg->Data[2] & 0b00001100) >> 2) ;
|
||||
|
||||
DB_PRINT ("eDP: OPS Mode Instruction:%d", insType);
|
||||
DB_PRINT ("eDP: OPS Mode Instruction: %d", insType);
|
||||
switch (insType)
|
||||
{
|
||||
case OPS_INS_RESERVED:
|
||||
@@ -1513,7 +1544,7 @@ void execDccProcessor (DCC_MSG * pDccMsg)
|
||||
break; // We only support Write Byte or Bit Manipulation
|
||||
|
||||
case OPS_INS_WRITE_BYTE:
|
||||
DB_PRINT ("eDP: CV:%d Value:%d", cvAddress, cvValue);
|
||||
DB_PRINT ("eDP: CV:%d Value: %d", cvAddress, cvValue);
|
||||
if (validCV (cvAddress, 1))
|
||||
writeCV (cvAddress, cvValue);
|
||||
break;
|
||||
@@ -1633,7 +1664,7 @@ void NmraDcc::init (uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
||||
ISRLevel = DccProcState.ExtIntMask;
|
||||
ISRChkMask = DccProcState.ExtIntMask;
|
||||
|
||||
#if defined(ESP32)|| defined ( ARDUINO_ARCH_RP2040)
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
attachInterrupt (DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
|
||||
#else
|
||||
@@ -1731,8 +1762,6 @@ void NmraDcc::setAccDecDCCAddrNextReceived (uint8_t enable)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
uint8_t NmraDcc::process()
|
||||
{
|
||||
uint8_t copyDataReady ;
|
||||
|
||||
if (DccProcState.inServiceMode)
|
||||
{
|
||||
if ( (millis() - DccProcState.LastServiceModeMillis) > 20L)
|
||||
@@ -1740,6 +1769,15 @@ uint8_t NmraDcc::process()
|
||||
clearDccProcState (0) ;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
if(DccProcState.eepromChanged && (DccProcState.millisSinceEEPromWrite >= EEPROM_COMMIT_DELAY_MS))
|
||||
{
|
||||
EEPROM.commit();
|
||||
DccProcState.eepromChanged = false;
|
||||
DB_PRINT ("process: EEPROM Commit");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (DccRx.DataReady)
|
||||
{
|
||||
@@ -1750,7 +1788,6 @@ uint8_t NmraDcc::process()
|
||||
noInterrupts();
|
||||
#endif
|
||||
Msg = DccRx.PacketCopy ;
|
||||
copyDataReady = DccRx.DataReady;
|
||||
DccRx.DataReady = 0 ;
|
||||
|
||||
#ifdef ESP32
|
||||
@@ -1768,8 +1805,10 @@ uint8_t NmraDcc::process()
|
||||
if (notifyDccMsg) notifyDccMsg (&Msg);
|
||||
|
||||
execDccProcessor (&Msg);
|
||||
return copyDataReady ;
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return 0 ;
|
||||
};
|
||||
|
29
NmraDcc.h
29
NmraDcc.h
@@ -81,6 +81,10 @@ typedef struct
|
||||
#define MAN_ID_DIY 0x0D
|
||||
#define MAN_ID_SILICON_RAILWAY 0x21
|
||||
|
||||
#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
|
||||
#define EEPROM_COMMIT_DELAY_MS 3000
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// This section contains the Product/Version Id Codes for projects
|
||||
//
|
||||
@@ -114,6 +118,7 @@ typedef struct
|
||||
#define CV_29_CONFIG 29
|
||||
#define CV_MANUFACTURER_START 33
|
||||
|
||||
|
||||
#if defined(ESP32)
|
||||
#include <esp_spi_flash.h>
|
||||
#define MAXCV SPI_FLASH_SEC_SIZE
|
||||
@@ -127,11 +132,8 @@ typedef struct
|
||||
#define PRIO_DCC_IRQ 9
|
||||
#define PRIO_SYSTIC 8 // MUST be higher priority than DCC Irq
|
||||
#elif defined(ARDUINO_ARCH_RP2040)
|
||||
#define MAXCV 256 // todo: maybe somebody knows a good define for it
|
||||
#elif defined ( ARDUINO_ARCH_RENESAS_UNO)
|
||||
#define MAXCV EEPROM.length()
|
||||
#elif defined(ARDUINO_SAMD_ZERO)
|
||||
#define MAXCV EEPROM_EMULATION_SIZE
|
||||
#define MAXCV 256 // todo: maybe somebody knows a good define for it
|
||||
|
||||
#else
|
||||
#define MAXCV E2END // the upper limit of the CV value currently defined to max memory.
|
||||
#endif
|
||||
@@ -177,9 +179,8 @@ typedef enum
|
||||
FN_13_20,
|
||||
FN_21_28,
|
||||
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
FN_0, /** function light is controlled by base line package (14 speed steps) */
|
||||
FN_0 /** function light is controlled by base line package (14 speed steps) */
|
||||
#endif
|
||||
FN_LAST
|
||||
} FN_GROUP;
|
||||
|
||||
#define FN_BIT_00 0x10
|
||||
@@ -216,7 +217,7 @@ typedef enum
|
||||
#define FN_BIT_27 0x40
|
||||
#define FN_BIT_28 0x80
|
||||
|
||||
//#define DCC_DBGVAR
|
||||
// #define DCC_DBGVAR
|
||||
#ifdef DCC_DBGVAR
|
||||
typedef struct countOf_t
|
||||
{
|
||||
@@ -239,11 +240,17 @@ public:
|
||||
#define FLAGS_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address
|
||||
#define FLAGS_AUTO_FACTORY_DEFAULT 0x02 // Call notifyCVResetFactoryDefault() if CV 7 & 8 == 255
|
||||
#define FLAGS_SETCV_CALLED 0x10 // only used internally !!
|
||||
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
||||
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
||||
#define FLAGS_EXTENDED_ADDRESS_MODE 0x20 // CV 29/541 bit 5 0 = 9-bit Board or Decoder Addressing mode, 1 = 11-bit Extended Address using middle 2 DD of the CDDD bits for the extra 2 bits
|
||||
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6 0 = Board or Extended Addressing Mode, 1 = Signal Decoder Output Addressing Mode
|
||||
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7 0 = Multifunction Decoder, 1 = Accessory Decoder
|
||||
|
||||
#define FLAGS_MULTIFUNCTION_DECODER ()
|
||||
#define FLAGS_BASIC_ACCESSORY_DECODER (FLAGS_DCC_ACCESSORY_DECODER)
|
||||
#define FLAGS_EXTENDED_ACCESSORY_DECODER (FLAGS_DCC_ACCESSORY_DECODER | FLAGS_EXTENDED_ADDRESS_MODE)
|
||||
#define FLAGS_SIGNAL_ACCESSORY_DECODER (FLAGS_DCC_ACCESSORY_DECODER | FLAGS_OUTPUT_ADDRESS_MODE)
|
||||
|
||||
// Flag Bits that are cloned from CV29 relating the DCC Accessory Decoder
|
||||
#define FLAGS_CV29_BITS (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
|
||||
#define FLAGS_CV29_BITS (FLAGS_EXTENDED_ACCESSORY_DECODER | FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
|
||||
|
||||
|
||||
/*+
|
||||
|
@@ -32,9 +32,6 @@
|
||||
#define DISABLE_OUTPUTS_IDLE
|
||||
#endif
|
||||
|
||||
// Uncomment the following line to enable Debug Print of DCC Messages
|
||||
//#define NOTIFY_DCC_MSG
|
||||
|
||||
// By default the stepper motor will move the shortest distance to the desired position.
|
||||
// If you need the turntable to only move in the Positive/Increasing or Negative/Decreasing step numbers to better handle backlash in the mechanism
|
||||
// Then uncomment the appropriate line below
|
||||
@@ -125,11 +122,16 @@ uint16_t lastAddr = 0xFFFF ;
|
||||
uint8_t lastDirection = 0xFF;
|
||||
int lastStep = 0;
|
||||
|
||||
|
||||
void processTurnoutCommand(uint16_t Addr, uint8_t Direction, uint8_t OutputPower)
|
||||
// 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(F("processTurnoutCommand: "));
|
||||
|
||||
Serial.print(F("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)
|
||||
@@ -187,22 +189,6 @@ void processTurnoutCommand(uint16_t Addr, uint8_t Direction, uint8_t OutputPower
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This function is called from the Library whenever a normal DCC Turnout Packet is received
|
||||
void notifyDccAccTurnoutBoard (uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower)
|
||||
{
|
||||
uint16_t Addr = ((BoardAddr - 1) * 4) + OutputPair + 1;
|
||||
|
||||
Serial.print(F("notifyDccAccTurnoutBoard: "));
|
||||
Serial.print(Addr,DEC) ;
|
||||
Serial.print(',');
|
||||
Serial.print(Direction,DEC) ;
|
||||
Serial.print(',');
|
||||
Serial.println(OutputPower, HEX) ;
|
||||
|
||||
processTurnoutCommand(Addr, Direction, OutputPower);
|
||||
};
|
||||
|
||||
#ifdef DISABLE_OUTPUTS_IDLE
|
||||
@@ -270,16 +256,14 @@ void setupDCCDecoder()
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER, 0 );
|
||||
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--) // Wait for the USB Device to Enumerate
|
||||
delay(20);
|
||||
|
||||
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: "));
|
||||
@@ -312,7 +296,7 @@ void setup()
|
||||
setupDCCDecoder();
|
||||
|
||||
// Fake a DCC Packet to cause the Turntable to move to Position 1
|
||||
processTurnoutCommand(POSITION_01_DCC_ADDRESS, 1, 1);
|
||||
notifyDccAccTurnoutOutput(POSITION_01_DCC_ADDRESS, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,16 +320,3 @@ void loop()
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NOTIFY_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg: ") ;
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
@@ -1,487 +0,0 @@
|
||||
#include <AccelStepper.h> // Requires AccelStepper Library - http://www.airspayce.com/mikem/arduino/AccelStepper/
|
||||
#include <EncButton2.h> // Requires EncButton library - https://github.com/GyverLibs/EncButton
|
||||
#include <elapsedMillis.h> // Requires elapsedMillis library - https://github.com/pfeerick/elapsedMillis
|
||||
|
||||
#define OPTIMIZE_I2C 1
|
||||
|
||||
#include <Wire.h>
|
||||
#include <SSD1306Ascii.h>
|
||||
#include <SSD1306AsciiWire.h>
|
||||
#include <EEPROM.h>
|
||||
#include <NmraDcc.h>
|
||||
|
||||
// You can print every DCC packet by un-commenting the line below
|
||||
//#define NOTIFY_DCC_MSG
|
||||
|
||||
// Define the Arduino Pin to connect to the DCC input signal
|
||||
#define DCC_PIN 2
|
||||
|
||||
// Define the DCC Turnout Address to select the first level = 1
|
||||
#define DCC_ACCESSORY_DECODER_BASE_ADDRESS 500
|
||||
|
||||
// Define the manimus numbr of Levels
|
||||
#define NUM_LIFT_LEVELS 8
|
||||
|
||||
#define PROGRAM_NAME "Fahrstuhl"
|
||||
#define PROGRAM_VERSION "1.1"
|
||||
|
||||
// Locate the Persistant State storage EEPROM space well above the DCC Accessory Decoder CV Storage
|
||||
#define EEPROM_BASE_ADDR 100
|
||||
#define EEPROM_VALID_DATA_SIGNATURE 0xA5A5
|
||||
// Uncomment the line below to force the EEPROM values to be reset to defaults
|
||||
//#define EEPROM_FORCE_RELOAD_DEFAULT_VALUES
|
||||
|
||||
#define BUTTON_LONG_PRESS_DELAY 2000
|
||||
|
||||
// Uncomment ONE of the next 2 lines to enable AJS or UWE Board Settings
|
||||
// #define AJS_BOARD_SETTINGS
|
||||
#define UWE_BOARD_SETTINGS
|
||||
|
||||
#if defined(AJS_BOARD_SETTINGS) // Setting for AJS Dev System
|
||||
|
||||
// Uncomment the next line to reverse the direction of the stepper movement
|
||||
#define REVERSE_STEPPER_DIRECTION
|
||||
|
||||
#define HOME_SENSOR_PIN 10
|
||||
|
||||
#define STEPPER_PULSE_PIN 11
|
||||
#define STEPPER_ENABLE_PIN 12
|
||||
#define STEPPER_DIR_PIN 13
|
||||
|
||||
#define STEPPER_MAX_SPEED 2100
|
||||
#define STEPPER_NORMAL_ACCELERATION 5000
|
||||
#define STEPPER_MAX_POSITION 300000U // Maximum Steps to allow the stepper to drive Up Saftey mechanism
|
||||
|
||||
#define BUTTON_MANUAL A3
|
||||
#define BUTTON_DOWN A2
|
||||
#define BUTTON_UP A1
|
||||
#define BUTTON_STOP_HOME A0
|
||||
|
||||
long defaultPositions[NUM_LIFT_LEVELS] = {1000, 4000, 7000, 10000, 13000, 16000, 19000, 22000}; // Default positions
|
||||
|
||||
#define STEPPER_INC_SPEED (STEPPER_MAX_SPEED / 10)
|
||||
|
||||
#define OLED_DISPLAY_I2C_ADDRESS 0x3C
|
||||
|
||||
#elif defined (UWE_BOARD_SETTINGS) // Setting for Uwe's Fahrstuhl System
|
||||
|
||||
// Uncomment the next line to reverse the direction of the stepper movement
|
||||
//#define REVERSE_STEPPER_DIRECTION
|
||||
|
||||
#define HOME_SENSOR_PIN 7
|
||||
|
||||
#define STEPPER_PULSE_PIN 4
|
||||
#define STEPPER_ENABLE_PIN 5
|
||||
#define STEPPER_DIR_PIN 6
|
||||
|
||||
#define STEPPER_MAX_SPEED 2100
|
||||
#define STEPPER_NORMAL_ACCELERATION 5000
|
||||
#define STEPPER_MAX_POSITION 1970000U // Maximum Steps to allow the stepper to drive Up Saftey mechanism
|
||||
|
||||
#define BUTTON_MANUAL 8
|
||||
#define BUTTON_DOWN 9
|
||||
#define BUTTON_UP 10
|
||||
#define BUTTON_STOP_HOME 11
|
||||
|
||||
long defaultPositions[NUM_LIFT_LEVELS] = {0, 161064, 32500, 483284, 645326, 808041, 1967457, 1130774}; // Default positions
|
||||
|
||||
#define STEPPER_INC_SPEED (STEPPER_MAX_SPEED / 2)
|
||||
|
||||
#define OLED_DISPLAY_I2C_ADDRESS 0x3C
|
||||
|
||||
#else
|
||||
#error No Board Settings Defined
|
||||
#endif
|
||||
|
||||
SSD1306AsciiWire oled;
|
||||
|
||||
#define STEPPER_MAN_SPEED_CHANGE_MILLIS 5
|
||||
#define STEPPER_EMERGENCY_STOP_ACCELERATION 100000
|
||||
#define LIFT_LEVEL_NOT_SET -1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t numLiftLevels;
|
||||
uint8_t lastLiftLevel;
|
||||
long lastStepperPosition;
|
||||
long levelPositions[NUM_LIFT_LEVELS];
|
||||
uint16_t objectSignature;
|
||||
} PERSISTENT_VALUES;
|
||||
|
||||
PERSISTENT_VALUES persistentValues;
|
||||
|
||||
// Define a stepper and the pins it will use
|
||||
AccelStepper stepper(AccelStepper::DRIVER, STEPPER_PULSE_PIN, STEPPER_DIR_PIN, -1, -1, false);
|
||||
|
||||
EncButton2<EB_BTN> homeSensor(INPUT_PULLUP, HOME_SENSOR_PIN);
|
||||
|
||||
EncButton2<EB_BTN> btnManual(INPUT, BUTTON_MANUAL);
|
||||
EncButton2<EB_BTN> btnDown(INPUT, BUTTON_DOWN);
|
||||
EncButton2<EB_BTN> btnUp(INPUT, BUTTON_UP);
|
||||
EncButton2<EB_BTN> btnStopHome(INPUT, BUTTON_STOP_HOME);
|
||||
|
||||
// NMRA DCC Accessory Decoder object
|
||||
NmraDcc Dcc;
|
||||
|
||||
void displayLevel(int newLevel)
|
||||
{
|
||||
oled.setCursor(0,0);
|
||||
oled.set2X();
|
||||
oled.print("Level: ");
|
||||
oled.print(newLevel);
|
||||
oled.clearToEOL();
|
||||
}
|
||||
|
||||
void displayMessage(const char* Msg)
|
||||
{
|
||||
oled.setCursor(0,4);
|
||||
oled.set2X();
|
||||
oled.print(Msg); oled.clearToEOL();
|
||||
}
|
||||
|
||||
void displayMessageNumber(const char* Msg, int Number)
|
||||
{
|
||||
oled.setCursor(0,4);
|
||||
oled.set2X();
|
||||
oled.print(Msg);
|
||||
oled.print(Number);
|
||||
oled.clearToEOL();
|
||||
}
|
||||
|
||||
void displayPosition(long newPosition)
|
||||
{
|
||||
oled.setCursor(0,7);
|
||||
oled.set1X();
|
||||
oled.print("Pos: ");
|
||||
oled.print(newPosition);
|
||||
oled.clearToEOL();
|
||||
}
|
||||
|
||||
void initPersistentValues()
|
||||
{
|
||||
EEPROM.get(EEPROM_BASE_ADDR, persistentValues);
|
||||
|
||||
#ifdef EEPROM_FORCE_RELOAD_DEFAULT_VALUES
|
||||
persistentValues.objectSignature = 0;
|
||||
#endif
|
||||
|
||||
if(persistentValues.objectSignature != EEPROM_VALID_DATA_SIGNATURE)
|
||||
{
|
||||
Serial.println("initPersistentValues: set detault values");
|
||||
|
||||
persistentValues.numLiftLevels = NUM_LIFT_LEVELS;
|
||||
persistentValues.lastLiftLevel = 0;
|
||||
persistentValues.lastStepperPosition = 0;
|
||||
persistentValues.objectSignature = EEPROM_VALID_DATA_SIGNATURE;
|
||||
for(uint8_t i = 0; i < NUM_LIFT_LEVELS; i++)
|
||||
persistentValues.levelPositions[i] = defaultPositions[i];
|
||||
|
||||
EEPROM.put(EEPROM_BASE_ADDR, persistentValues);
|
||||
}
|
||||
else
|
||||
Serial.println("initPersistentValues: restored values from EEPROM");
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
Serial.println(); Serial.print(PROGRAM_NAME); Serial.print(" Version: "); Serial.println(PROGRAM_VERSION);
|
||||
|
||||
initPersistentValues();
|
||||
|
||||
Wire.begin();
|
||||
Wire.setClock(400000L);
|
||||
|
||||
oled.setFont(cp437font8x8);
|
||||
|
||||
oled.begin(&Adafruit128x64, OLED_DISPLAY_I2C_ADDRESS);
|
||||
oled.clear();
|
||||
oled.println(PROGRAM_NAME);
|
||||
oled.println();
|
||||
oled.print("Ver: "); oled.println(PROGRAM_VERSION);
|
||||
oled.println();
|
||||
oled.print("Max Levels: "); oled.println(NUM_LIFT_LEVELS);
|
||||
oled.println();
|
||||
oled.print("Used Levels: "); oled.println(persistentValues.numLiftLevels);
|
||||
delay(2000);
|
||||
oled.clear();
|
||||
displayLevel(persistentValues.lastLiftLevel + 1);
|
||||
displayPosition(persistentValues.lastStepperPosition);
|
||||
|
||||
stepper.setCurrentPosition(persistentValues.lastStepperPosition);
|
||||
|
||||
stepper.setEnablePin(STEPPER_ENABLE_PIN);
|
||||
#ifdef REVERSE_STEPPER_DIRECTION
|
||||
stepper.setPinsInverted(true, false, true);
|
||||
#else
|
||||
stepper.setPinsInverted(false, false, true);
|
||||
#endif
|
||||
|
||||
stepper.setMaxSpeed(STEPPER_MAX_SPEED);
|
||||
|
||||
btnStopHome.setHoldTimeout(BUTTON_LONG_PRESS_DELAY);
|
||||
btnManual.setHoldTimeout(BUTTON_LONG_PRESS_DELAY);
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(DCC_PIN, 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 stepperMoveTo(long newPosition)
|
||||
{
|
||||
stepper.enableOutputs();
|
||||
stepper.setAcceleration(STEPPER_NORMAL_ACCELERATION);
|
||||
stepper.moveTo(newPosition);
|
||||
}
|
||||
|
||||
void stepperMove(long newRelPosition)
|
||||
{
|
||||
stepper.enableOutputs();
|
||||
stepper.setAcceleration(STEPPER_NORMAL_ACCELERATION);
|
||||
stepper.move(newRelPosition);
|
||||
}
|
||||
void stopStepper(void)
|
||||
{
|
||||
stepper.setAcceleration(STEPPER_EMERGENCY_STOP_ACCELERATION);
|
||||
stepper.move(0);
|
||||
stepper.stop();
|
||||
while(stepper.run());
|
||||
stepper.disableOutputs();
|
||||
}
|
||||
|
||||
int lastSpeed = 0;
|
||||
int newSpeed = 0;
|
||||
bool wasRunning = false;
|
||||
bool configMode = false;
|
||||
bool homing = false;
|
||||
elapsedMillis lastSpeedChange = 0;
|
||||
|
||||
|
||||
// This function is called whenever a normal DCC Turnout Packet is received
|
||||
// The DCC Turnout Address is checked to see if it is within the range used to Select Elevator levels and starts a Move if a new level is selected
|
||||
void notifyDccAccTurnoutOutput(uint16_t receivedAddress, uint8_t direction, uint8_t outputPower)
|
||||
{
|
||||
if((receivedAddress >= DCC_ACCESSORY_DECODER_BASE_ADDRESS) && (receivedAddress < (DCC_ACCESSORY_DECODER_BASE_ADDRESS + (NUM_LIFT_LEVELS/2))))
|
||||
{
|
||||
uint8_t newLevel = (receivedAddress - DCC_ACCESSORY_DECODER_BASE_ADDRESS) * 2 + direction;
|
||||
if(persistentValues.lastLiftLevel != newLevel)
|
||||
{
|
||||
persistentValues.lastLiftLevel = newLevel;
|
||||
|
||||
long newPos = persistentValues.levelPositions[persistentValues.lastLiftLevel];
|
||||
stepperMoveTo(newPos);
|
||||
|
||||
Serial.print("notifyDccAccTurnoutOutput: Move to Level: "); Serial.print(persistentValues.lastLiftLevel); Serial.print(" Pos: "); Serial.println(newPos);
|
||||
|
||||
displayMessageNumber("Mv To: ", persistentValues.lastLiftLevel + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
Dcc.process();
|
||||
|
||||
//First check the Home Sensor and stop the motor if going in the down direction
|
||||
homeSensor.tick();
|
||||
if(homeSensor.state())
|
||||
{
|
||||
if((configMode || homing) && stepper.isRunning() && (lastSpeed <= 0))
|
||||
{
|
||||
stopStepper();
|
||||
|
||||
Serial.print("Home Sensor Hit - LastSpeed: ");
|
||||
Serial.print(lastSpeed);
|
||||
Serial.print(" Last Position: ");
|
||||
Serial.println(stepper.currentPosition());
|
||||
|
||||
newSpeed = 0;
|
||||
lastSpeed = newSpeed;
|
||||
|
||||
persistentValues.lastLiftLevel = 0;
|
||||
persistentValues.lastStepperPosition = 0;
|
||||
stepper.setCurrentPosition(persistentValues.lastStepperPosition);
|
||||
|
||||
EEPROM.put(EEPROM_BASE_ADDR, persistentValues);
|
||||
|
||||
if(homing)
|
||||
{
|
||||
long newPos = persistentValues.levelPositions[persistentValues.lastLiftLevel];
|
||||
stepperMoveTo(newPos);
|
||||
Serial.print("Home Sensor Hit: Move To: "); Serial.print(persistentValues.lastLiftLevel); Serial.print(" Pos: "); Serial.println(newPos);
|
||||
homing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we haven't gone beyond the end point of the traverser.
|
||||
if(stepper.currentPosition() >= STEPPER_MAX_POSITION)
|
||||
{
|
||||
if(configMode && stepper.isRunning() && (lastSpeed >= 0))
|
||||
{
|
||||
stopStepper();
|
||||
|
||||
Serial.print("Maximum Position Hit - LastSpeed: ");
|
||||
Serial.print(lastSpeed);
|
||||
Serial.print(" Last Position: ");
|
||||
Serial.println(stepper.currentPosition());
|
||||
|
||||
newSpeed = 0;
|
||||
lastSpeed = newSpeed;
|
||||
|
||||
displayMessage("At Max");
|
||||
}
|
||||
}
|
||||
|
||||
btnStopHome.tick();
|
||||
if(btnStopHome.press())
|
||||
{
|
||||
Serial.print("StopHome Click - Current Pos: "); Serial.println(stepper.currentPosition());
|
||||
displayMessage("Stop");
|
||||
if(stepper.isRunning())
|
||||
{
|
||||
newSpeed = 0;
|
||||
stopStepper();
|
||||
}
|
||||
}
|
||||
|
||||
if(btnStopHome.held())
|
||||
{
|
||||
Serial.println("StopHome Held: Moving to Home Position");
|
||||
displayMessage("Homing");
|
||||
homing = true;
|
||||
newSpeed = -STEPPER_MAX_SPEED;
|
||||
}
|
||||
|
||||
btnManual.tick();
|
||||
if(btnManual.press())
|
||||
{
|
||||
Serial.print("Manual Press - Current Pos: "); Serial.println(stepper.currentPosition());
|
||||
if(configMode)
|
||||
{
|
||||
configMode = false;
|
||||
Serial.println("Home Click - Exit Manual Mode");
|
||||
}
|
||||
}
|
||||
|
||||
if(btnManual.held())
|
||||
{
|
||||
Serial.print("Manual Held - Enter Manual Mode Pos: "); Serial.println(stepper.currentPosition());
|
||||
configMode = true;
|
||||
}
|
||||
|
||||
btnDown.tick();
|
||||
if(configMode)
|
||||
{
|
||||
if((btnDown.press() || btnDown.step()) && (stepper.currentPosition() < STEPPER_MAX_POSITION) && (lastSpeed <= (STEPPER_MAX_SPEED - STEPPER_INC_SPEED)))
|
||||
{
|
||||
newSpeed = lastSpeed + STEPPER_INC_SPEED;
|
||||
lastSpeedChange = STEPPER_MAN_SPEED_CHANGE_MILLIS;
|
||||
Serial.print("Down Press - Current Pos: "); Serial.print(stepper.currentPosition()); Serial.print(" New Speed: "); Serial.println(newSpeed);
|
||||
|
||||
displayMessage("Down");
|
||||
}
|
||||
}
|
||||
|
||||
else if((btnDown.press() || btnDown.step()) && persistentValues.lastLiftLevel > 0)
|
||||
{
|
||||
Serial.print("Down Press - Current Level: "); Serial.print(persistentValues.lastLiftLevel);
|
||||
persistentValues.lastLiftLevel--;
|
||||
long newPos = persistentValues.levelPositions[persistentValues.lastLiftLevel];
|
||||
stepperMoveTo(newPos);
|
||||
Serial.print(" Move To: "); Serial.print(persistentValues.lastLiftLevel); Serial.print(" Pos: "); Serial.println(newPos);
|
||||
|
||||
displayMessageNumber("Dn To: ", persistentValues.lastLiftLevel + 1);
|
||||
}
|
||||
|
||||
btnUp.tick();
|
||||
if(configMode)
|
||||
{
|
||||
if((btnUp.press() || btnDown.step()) && (homeSensor.state() == 0) && (lastSpeed >= -(STEPPER_MAX_SPEED - STEPPER_INC_SPEED)))
|
||||
{
|
||||
newSpeed = lastSpeed - STEPPER_INC_SPEED;
|
||||
lastSpeedChange = STEPPER_MAN_SPEED_CHANGE_MILLIS;
|
||||
Serial.print("Up Press - Current Pos: "); Serial.print(stepper.currentPosition()); Serial.print(" New Speed: "); Serial.println(newSpeed);
|
||||
|
||||
displayMessage("Up");
|
||||
}
|
||||
}
|
||||
|
||||
else if((btnUp.press() || btnDown.step()) && (persistentValues.lastLiftLevel < (persistentValues.numLiftLevels - 1)))
|
||||
{
|
||||
Serial.print("Up Press - Current Level: "); Serial.print(persistentValues.lastLiftLevel);
|
||||
persistentValues.lastLiftLevel++;
|
||||
long newPos = persistentValues.levelPositions[persistentValues.lastLiftLevel];
|
||||
stepperMoveTo(newPos);
|
||||
Serial.print(" Move To: "); Serial.print(persistentValues.lastLiftLevel); Serial.print(" Pos: "); Serial.println(newPos);
|
||||
|
||||
displayMessageNumber("Up To: ", persistentValues.lastLiftLevel + 1);
|
||||
}
|
||||
|
||||
|
||||
if(lastSpeed != newSpeed)
|
||||
{
|
||||
// Serial.print("Speed Change: Last: "); Serial.print(lastSpeed); Serial.print(" New: "); Serial.print(newSpeed);
|
||||
// Serial.print(" - Current Pos: "); Serial.print(stepper.currentPosition());
|
||||
|
||||
if( newSpeed == 0)
|
||||
{
|
||||
lastSpeed = newSpeed;
|
||||
stopStepper();
|
||||
Serial.print("Speed Change: Stopped Last: "); Serial.print(lastSpeed); Serial.print(" New: "); Serial.println(newSpeed);
|
||||
}
|
||||
|
||||
else if(lastSpeedChange >= STEPPER_MAN_SPEED_CHANGE_MILLIS)
|
||||
{
|
||||
lastSpeedChange = 0;
|
||||
|
||||
if(newSpeed > lastSpeed)
|
||||
lastSpeed++;
|
||||
else
|
||||
lastSpeed--;
|
||||
|
||||
stepper.setSpeed(lastSpeed);
|
||||
stepper.enableOutputs();
|
||||
|
||||
// Serial.print(" Set New Speed: "); Serial.println(newSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
if(lastSpeed)
|
||||
stepper.runSpeed();
|
||||
|
||||
else
|
||||
stepper.run();
|
||||
|
||||
if(!stepper.isRunning() && wasRunning)
|
||||
{
|
||||
Serial.println("Disable Outputs");
|
||||
stepper.disableOutputs();
|
||||
|
||||
displayLevel(persistentValues.lastLiftLevel + 1);
|
||||
displayMessage("");
|
||||
persistentValues.lastStepperPosition = stepper.currentPosition();
|
||||
displayPosition(persistentValues.lastStepperPosition);
|
||||
EEPROM.put(EEPROM_BASE_ADDR, persistentValues);
|
||||
}
|
||||
wasRunning = stepper.isRunning();
|
||||
}
|
||||
|
||||
#ifdef NOTIFY_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg: ") ;
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
@@ -95,10 +95,9 @@ void notifyDccSigOutputState( uint16_t Addr, uint8_t State)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
elapsedMillis millisWaitedForUSB = 0;
|
||||
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
|
||||
|
||||
// Configure the DCC CV Programing ACK pin for an output
|
||||
pinMode( DccAckPin, OUTPUT );
|
||||
|
||||
|
@@ -9,7 +9,7 @@
|
||||
#define NOTIFY_TURNOUT_MSG
|
||||
|
||||
// You can also print other Debug Messages uncommenting the line below
|
||||
//#define DEBUG_MSG
|
||||
#define DEBUG_MSG
|
||||
|
||||
// Un-Comment the line below to force CVs to be written to the Factory Default values
|
||||
// defined in the FactoryDefaultCVs below on Start-Up
|
||||
@@ -120,10 +120,7 @@ void initPinPulser(void)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
@@ -134,7 +131,7 @@ void setup()
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, DCC_DECODER_VERSION_NUM, FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER, 0 );
|
||||
Dcc.init( MAN_ID_DIY, DCC_DECODER_VERSION_NUM, CV29_ACCESSORY_DECODER, 0 );
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("\nNMRA DCC 8-Turnout Accessory Decoder. Ver: "); Serial.println(DCC_DECODER_VERSION_NUM,DEC);
|
||||
|
@@ -0,0 +1,377 @@
|
||||
// This Example shows how to use the library as a DCC Accessory Decoder to drive 16 Servos to control Turnouts
|
||||
#include <NmraDcc.h>
|
||||
#include <Servo.h>
|
||||
#include <SlowMotionServo.h>
|
||||
#include <elapsedMillis.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
// You can print every DCC packet by un-commenting the line below
|
||||
//#define NOTIFY_DCC_MSG
|
||||
|
||||
// You can print every notifyDccAccTurnoutOutput call-back by un-commenting the line below
|
||||
#define NOTIFY_TURNOUT_MSG
|
||||
|
||||
// You can also print other Debug Messages uncommenting the line below
|
||||
#define DEBUG_MSG
|
||||
|
||||
// Un-Comment the line below to force CVs to be written to the Factory Default values
|
||||
// defined in the FactoryDefaultCVs below on Start-Up
|
||||
//#define FORCE_RESET_FACTORY_DEFAULT_CV
|
||||
|
||||
// Un-Comment the line below to Enable DCC ACK for Service Mode Programming Read CV Capablilty
|
||||
#define ENABLE_DCC_ACK 27 // This is A1 on the Iowa Scaled Engineering ARD-DCCSHIELD DCC Shield
|
||||
|
||||
// Define the Arduino input Pin number for the DCC Signal
|
||||
#define DCC_PIN 26
|
||||
|
||||
#define NUM_SERVOS 16 // Set Number of Servos
|
||||
|
||||
#define DCC_DECODER_VERSION_NUM 12 // Set the Decoder Version - Used by JMRI to Identify the decoder
|
||||
|
||||
#define EEPROM_COMMIT_DELAY_MS 3000
|
||||
|
||||
struct CVPair
|
||||
{
|
||||
uint16_t CV;
|
||||
uint8_t Value;
|
||||
};
|
||||
|
||||
#define CV_VALUE_SERVO_DETACH_MILLIS 0 // CV Default Value for the delay in ms x 10ms to Detach the servo signal to stop chattering
|
||||
#define CV_ADDRESS_SERVO_DETACH_MILLIS CV_MANUFACTURER_START
|
||||
|
||||
#define CV_VALUE_SERVO_MOVE_SPEED 40 // CV Default Value for the Serov movement speed x 10
|
||||
#define CV_ADDRESS_SERVO_MOVE_SPEED (CV_MANUFACTURER_START + 1)
|
||||
|
||||
#define CV_ADDRESS_SERVO_INDIVIDUAL_VALUES (CV_MANUFACTURER_START + 2)
|
||||
|
||||
#define CV_ADDRESS_LAST_POS_LSB (CV_MANUFACTURER_START + (NUM_SERVOS * 2 ) + 3)
|
||||
#define CV_ADDRESS_LAST_POS_MSB (CV_MANUFACTURER_START + (NUM_SERVOS * 2 ) + 4)
|
||||
|
||||
#define CV_VALUE_SERVO_MIN_POS 40 // CV Default Value for the Minimum Servo Position as a % of Full Scale
|
||||
#define CV_ADDRESS_SERVO_MIN_POS(x) (CV_ADDRESS_SERVO_INDIVIDUAL_VALUES + (2 * x) + 1)
|
||||
|
||||
#define CV_VALUE_SERVO_MAX_POS 60 // CV Default Value for the Maximum Servo Position as a % of Full Scale
|
||||
#define CV_ADDRESS_SERVO_MAX_POS(x) (CV_ADDRESS_SERVO_INDIVIDUAL_VALUES + (2 * x) + 2)
|
||||
|
||||
// To set the Turnout Addresses for this board you need to change the CV values for CV1 (CV_ACCESSORY_DECODER_ADDRESS_LSB) and
|
||||
// CV9 (CV_ACCESSORY_DECODER_ADDRESS_MSB) in the FactoryDefaultCVs structure below. The Turnout Addresses are defined as:
|
||||
// Base Turnout Address is: ((((CV9 * 64) + CV1) - 1) * 4) + 1
|
||||
// With NUM_TURNOUTS 8 (above) a CV1 = 1 and CV9 = 0, the Turnout Addresses will be 1..8, for CV1 = 2 the Turnout Address is 5..12
|
||||
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_LSB, DEFAULT_ACCESSORY_DECODER_ADDRESS & 0xFF},
|
||||
{CV_ACCESSORY_DECODER_ADDRESS_MSB, DEFAULT_ACCESSORY_DECODER_ADDRESS >> 8},
|
||||
{CV_ADDRESS_SERVO_DETACH_MILLIS, CV_VALUE_SERVO_DETACH_MILLIS}, // 0 = don't Detach else the Detach will occur after the CV Value x 10ms
|
||||
{CV_ADDRESS_SERVO_MOVE_SPEED, CV_VALUE_SERVO_MOVE_SPEED}, // 0 = don't Detach else the Detach will occur after the CV Value x 10ms
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(0), CV_VALUE_SERVO_MIN_POS}, // Servo 0
|
||||
{CV_ADDRESS_SERVO_MAX_POS(0), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(1), CV_VALUE_SERVO_MIN_POS}, // Servo 1
|
||||
{CV_ADDRESS_SERVO_MAX_POS(1), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(2), CV_VALUE_SERVO_MIN_POS}, // Servo 2
|
||||
{CV_ADDRESS_SERVO_MAX_POS(2), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(3), CV_VALUE_SERVO_MIN_POS}, // Servo 3
|
||||
{CV_ADDRESS_SERVO_MAX_POS(3), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(4), CV_VALUE_SERVO_MIN_POS}, // Servo 4
|
||||
{CV_ADDRESS_SERVO_MAX_POS(4), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(5), CV_VALUE_SERVO_MIN_POS}, // Servo 5
|
||||
{CV_ADDRESS_SERVO_MAX_POS(5), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(6), CV_VALUE_SERVO_MIN_POS}, // Servo 6
|
||||
{CV_ADDRESS_SERVO_MAX_POS(6), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(7), CV_VALUE_SERVO_MIN_POS}, // Servo 7
|
||||
{CV_ADDRESS_SERVO_MAX_POS(7), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(8), CV_VALUE_SERVO_MIN_POS}, // Servo 8
|
||||
{CV_ADDRESS_SERVO_MAX_POS(8), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(9), CV_VALUE_SERVO_MIN_POS}, // Servo 9
|
||||
{CV_ADDRESS_SERVO_MAX_POS(9), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(10), CV_VALUE_SERVO_MIN_POS}, // Servo 10
|
||||
{CV_ADDRESS_SERVO_MAX_POS(10), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(11), CV_VALUE_SERVO_MIN_POS}, // Servo 11
|
||||
{CV_ADDRESS_SERVO_MAX_POS(11), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(12), CV_VALUE_SERVO_MIN_POS}, // Servo 12
|
||||
{CV_ADDRESS_SERVO_MAX_POS(12), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(13), CV_VALUE_SERVO_MIN_POS}, // Servo 13
|
||||
{CV_ADDRESS_SERVO_MAX_POS(13), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(14), CV_VALUE_SERVO_MIN_POS}, // Servo 14
|
||||
{CV_ADDRESS_SERVO_MAX_POS(14), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_SERVO_MIN_POS(15), CV_VALUE_SERVO_MIN_POS}, // Servo 15
|
||||
{CV_ADDRESS_SERVO_MAX_POS(15), CV_VALUE_SERVO_MAX_POS},
|
||||
|
||||
{CV_ADDRESS_LAST_POS_LSB, 0},
|
||||
{CV_ADDRESS_LAST_POS_MSB, 0},
|
||||
};
|
||||
|
||||
uint8_t FactoryDefaultCVIndex = 0;
|
||||
|
||||
NmraDcc Dcc ;
|
||||
DCC_MSG Packet ;
|
||||
uint16_t BaseTurnoutAddress;
|
||||
|
||||
uint16_t lastPositionBits;
|
||||
|
||||
SMSSmooth Servos[NUM_SERVOS];
|
||||
|
||||
// This function is called whenever a normal DCC Turnout Packet is received
|
||||
void notifyDccAccTurnoutOutput (uint16_t OutputAddress, uint8_t Direction, uint8_t OutputPower)
|
||||
{
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print("notifyDccAccTurnoutOutput: Output Addr: ") ;
|
||||
Serial.print(OutputAddress,DEC) ;
|
||||
Serial.print(" Direction: ");
|
||||
Serial.print(Direction ? "Closed" : "Thrown") ;
|
||||
Serial.print(" Output: ");
|
||||
Serial.print(OutputPower ? "On" : "Off") ;
|
||||
#endif
|
||||
if(( OutputAddress >= BaseTurnoutAddress ) && ( OutputAddress < (BaseTurnoutAddress + NUM_SERVOS )) && OutputPower ) // Only Drive the Servo on Activation
|
||||
{
|
||||
uint16_t servoIndex = OutputAddress - BaseTurnoutAddress;
|
||||
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print(" Servo Num: ");
|
||||
Serial.println(servoIndex,DEC);
|
||||
#endif
|
||||
|
||||
setServoPos(servoIndex, Direction != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void notifyDccAccTurnoutBoard (uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower)
|
||||
{
|
||||
uint16_t Addr = ((BoardAddr - 1) * 4) + OutputPair + 1;
|
||||
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print("notifyDccAccTurnoutBoard: Board Addr: ") ;
|
||||
Serial.print(BoardAddr,DEC) ;
|
||||
Serial.print(" Output Pair: ");
|
||||
Serial.print(OutputPair,DEC) ;
|
||||
Serial.print(" Turnout Addr: ");
|
||||
Serial.print(Addr,DEC) ;
|
||||
Serial.print(" Base Turnout Addr: ");
|
||||
Serial.print(BaseTurnoutAddress,DEC) ;
|
||||
Serial.print(" Direction: ");
|
||||
Serial.print(Direction ? "Closed" : "Thrown") ;
|
||||
Serial.print(" Output: ");
|
||||
Serial.print(OutputPower ? "On" : "Off") ;
|
||||
|
||||
#endif
|
||||
|
||||
if(( Addr >= BaseTurnoutAddress ) && ( Addr < (BaseTurnoutAddress + NUM_SERVOS )) && OutputPower ) // Only Drive the Servo on Activation
|
||||
{
|
||||
uint16_t servoIndex = Addr - BaseTurnoutAddress;
|
||||
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.print(" Servo Num: ");
|
||||
Serial.println(servoIndex,DEC);
|
||||
#endif
|
||||
|
||||
setServoPos(servoIndex, Direction != 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef NOTIFY_TURNOUT_MSG
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setServoPos(uint8_t index, bool position)
|
||||
{
|
||||
if(index < NUM_SERVOS)
|
||||
{
|
||||
Servos[index].goTo( position ? 1.0 : 0.0);
|
||||
|
||||
setLastTurnoutPosition(index, position);
|
||||
}
|
||||
}
|
||||
|
||||
bool getLastTurnoutPosition(uint8_t index)
|
||||
{
|
||||
if(index < NUM_SERVOS)
|
||||
{
|
||||
uint16_t bitMask = 1 << index;
|
||||
return lastPositionBits & bitMask ? true : false;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void setLastTurnoutPosition(uint8_t index, bool position)
|
||||
{
|
||||
if(index < NUM_SERVOS)
|
||||
{
|
||||
uint16_t bitMask = 1 << index;
|
||||
|
||||
if(position)
|
||||
lastPositionBits |= bitMask;
|
||||
else
|
||||
lastPositionBits &= ~bitMask;
|
||||
|
||||
Dcc.setCV(CV_ADDRESS_LAST_POS_LSB, lastPositionBits & 0x00ff);
|
||||
Dcc.setCV(CV_ADDRESS_LAST_POS_MSB, (lastPositionBits >> 8) & 0x00ff);
|
||||
}
|
||||
}
|
||||
|
||||
void initServos()
|
||||
{
|
||||
BaseTurnoutAddress = (((Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_MSB) * 64) + Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_LSB) - 1) * 4) + 1 ;
|
||||
|
||||
uint16_t servoDetachMillis = Dcc.getCV(CV_ADDRESS_SERVO_DETACH_MILLIS) * 10;
|
||||
SlowMotionServo::setDelayUntilStop(servoDetachMillis); // This is a static class member so only needs to be set once and is used globally
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("initServos: DCC Turnout Base Address: "); Serial.print(BaseTurnoutAddress, DEC);
|
||||
Serial.print(" Detach Servo Signal MilliSeconds: "); Serial.print(servoDetachMillis);
|
||||
#endif
|
||||
|
||||
float servoMoveSpeed = Dcc.getCV(CV_ADDRESS_SERVO_MOVE_SPEED) / 10.0;
|
||||
lastPositionBits = Dcc.getCV(CV_ADDRESS_LAST_POS_LSB) | (Dcc.getCV(CV_ADDRESS_LAST_POS_MSB) << 8);
|
||||
|
||||
for(uint8_t i = 0; i < NUM_SERVOS; i++)
|
||||
{
|
||||
Servos[i].setPin(i);
|
||||
Servos[i].setupMin( map(Dcc.getCV(CV_ADDRESS_SERVO_MIN_POS(i)), 0, 100, 544, 2400));
|
||||
Servos[i].setupMax( map(Dcc.getCV(CV_ADDRESS_SERVO_MAX_POS(i)), 0, 100, 544, 2400));
|
||||
Servos[i].setSpeed(servoMoveSpeed);
|
||||
Servos[i].setDetach(servoDetachMillis != 0);
|
||||
Servos[i].setInitialPosition(getLastTurnoutPosition(i) ? 1.0 : 0.0);
|
||||
}
|
||||
|
||||
SlowMotionServo::update();
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.println("\ninitServos: Done");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
elapsedMillis millisWaitedForUSB = 0;
|
||||
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
|
||||
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("\nNMRA DCC 16-Servo Accessory Decoder. Ver: "); Serial.println(DCC_DECODER_VERSION_NUM,DEC);
|
||||
#endif
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 1);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
// Call the main DCC Init function to enable the DCC Receiver
|
||||
Dcc.init( MAN_ID_DIY, DCC_DECODER_VERSION_NUM, FLAGS_DCC_ACCESSORY_DECODER|FLAGS_EXTENDED_ADDRESS_MODE, 0);
|
||||
|
||||
#ifdef FORCE_RESET_FACTORY_DEFAULT_CV
|
||||
Serial.println("Resetting CVs to Factory Defaults");
|
||||
notifyCVResetFactoryDefault();
|
||||
#endif
|
||||
|
||||
if( FactoryDefaultCVIndex == 0) // Not forcing a reset CV Reset to Factory Defaults so initPinPulser
|
||||
initServos();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||
Dcc.process();
|
||||
|
||||
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
|
||||
{
|
||||
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||
uint16_t cv = FactoryDefaultCVs[FactoryDefaultCVIndex].CV;
|
||||
uint8_t val = FactoryDefaultCVs[FactoryDefaultCVIndex].Value;
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("loop: Write Default CV: "); Serial.print(cv,DEC); Serial.print(" Value: "); Serial.println(val,DEC);
|
||||
#endif
|
||||
Dcc.setCV( cv, val );
|
||||
|
||||
if( FactoryDefaultCVIndex == 0) // Is this the last Default CV to set? if so re-initPinPulser
|
||||
initServos();
|
||||
}
|
||||
|
||||
SlowMotionServo::update();
|
||||
}
|
||||
|
||||
void notifyCVChange(uint16_t CV, uint8_t Value)
|
||||
{
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.print("notifyCVChange: CV: ") ;
|
||||
Serial.print(CV,DEC) ;
|
||||
Serial.print(" Value: ") ;
|
||||
Serial.println(Value, DEC) ;
|
||||
#endif
|
||||
|
||||
Value = Value; // Silence Compiler Warnings...
|
||||
|
||||
if((CV == CV_ACCESSORY_DECODER_ADDRESS_MSB) || (CV == CV_ACCESSORY_DECODER_ADDRESS_LSB) ||
|
||||
((CV >= CV_ADDRESS_SERVO_DETACH_MILLIS) && (CV < CV_ADDRESS_LAST_POS_LSB)) && (FactoryDefaultCVIndex == 0))
|
||||
initServos(); // Some CV we care about changed so re-init the PinPulser with the new CV settings
|
||||
}
|
||||
|
||||
void notifyCVResetFactoryDefault()
|
||||
{
|
||||
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
|
||||
// to flag to the loop() function that a reset to Factory Defaults needs to be done
|
||||
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
|
||||
};
|
||||
|
||||
// This function is called by the NmraDcc library when a DCC ACK needs to be sent
|
||||
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
|
||||
#ifdef ENABLE_DCC_ACK
|
||||
void notifyCVAck(void)
|
||||
{
|
||||
#ifdef DEBUG_MSG
|
||||
Serial.println("notifyCVAck") ;
|
||||
#endif
|
||||
// Configure the DCC CV Programing ACK pin for an output
|
||||
pinMode( ENABLE_DCC_ACK, OUTPUT );
|
||||
|
||||
// Generate the DCC ACK 60mA pulse
|
||||
digitalWrite( ENABLE_DCC_ACK, HIGH );
|
||||
delay( 10 ); // The DCC Spec says 6ms but 10 makes sure... ;)
|
||||
digitalWrite( ENABLE_DCC_ACK, LOW );
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef NOTIFY_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg: ") ;
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
@@ -164,10 +164,6 @@ void notifyCVAck(void)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
Serial.println("NMRA Dcc Multifunction Decoder Demo 1");
|
||||
|
||||
// Configure the DCC CV Programing ACK pin for an output
|
||||
@@ -203,3 +199,4 @@ void loop()
|
||||
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,311 +0,0 @@
|
||||
// NMRA Dcc Multifunction Motor Decoder Demo using the Seeed XIAO Expansion board
|
||||
// See: https://wiki.seeedstudio.com/Seeeduino-XIAO-Expansion-Board/
|
||||
//
|
||||
// Author: Alex Shepherd 2023-02-15
|
||||
//
|
||||
// This example requires these Arduino Libraries:
|
||||
//
|
||||
// 1) The NmraDcc Library from: http://mrrwa.org/download/
|
||||
//
|
||||
// These libraries can be found and installed via the Arduino IDE Library Manager
|
||||
//
|
||||
// This simple demo displays the Multifunction Decoder actions on the builtin OLED Display
|
||||
//
|
||||
|
||||
#include <NmraDcc.h>
|
||||
|
||||
#include <U8x8lib.h>
|
||||
#include <Wire.h>
|
||||
|
||||
// Uncomment any of the lines below to enable debug messages for different parts of the code
|
||||
#define DEBUG_FUNCTIONS
|
||||
#define DEBUG_SPEED
|
||||
//#define DEBUG_DCC_MSG
|
||||
|
||||
#if defined(DEBUG_FUNCTIONS) or defined(DEBUG_SPEED) or defined(DEBUG_PWM) or defined(DEBUG_DCC_MSG)
|
||||
#define DEBUG_PRINT
|
||||
#endif
|
||||
|
||||
// This is the default DCC Address
|
||||
#define DEFAULT_DECODER_ADDRESS 3
|
||||
|
||||
#ifndef ARDUINO_SEEED_XIAO_M0
|
||||
#error "Unsupported CPU, you need to add another configuration section for your CPU"
|
||||
#endif
|
||||
|
||||
// I used a IoTT DCC Interface connected to Grove Analog Input which has A0 or 0 Pin
|
||||
#define DCC_PIN 0
|
||||
|
||||
uint8_t newDirection = 0;
|
||||
uint8_t lastDirection = 0;
|
||||
|
||||
uint8_t newSpeed = 0;
|
||||
uint8_t lastSpeed = 0;
|
||||
uint8_t numSpeedSteps = SPEED_STEP_128;
|
||||
|
||||
uint8_t lastFuncStateList[FN_LAST+1];
|
||||
|
||||
// Structure for CV Values Table
|
||||
struct CVPair
|
||||
{
|
||||
uint16_t CV;
|
||||
uint8_t Value;
|
||||
};
|
||||
|
||||
// Default CV Values Table
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
// The CV Below defines the Short DCC Address
|
||||
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, DEFAULT_DECODER_ADDRESS},
|
||||
|
||||
// These two CVs define the Long DCC Address
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_MSB(DEFAULT_DECODER_ADDRESS)},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, CALC_MULTIFUNCTION_EXTENDED_ADDRESS_LSB(DEFAULT_DECODER_ADDRESS)},
|
||||
|
||||
// ONLY uncomment 1 CV_29_CONFIG line below as approprate
|
||||
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
|
||||
{CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
|
||||
// {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long Address 28/128 Speed Steps
|
||||
};
|
||||
|
||||
NmraDcc Dcc ;
|
||||
|
||||
U8X8_SSD1306_128X64_NONAME_HW_I2C u8x8(PIN_WIRE_SCL, PIN_WIRE_SDA, U8X8_PIN_NONE); // OLEDs without Reset of the Display
|
||||
|
||||
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
|
||||
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
|
||||
};
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Speed packet for our address
|
||||
void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps )
|
||||
{
|
||||
#ifdef DEBUG_SPEED
|
||||
Serial.print("notifyDccSpeed: Addr: ");
|
||||
Serial.print(Addr,DEC);
|
||||
Serial.print( (AddrType == DCC_ADDR_SHORT) ? "-S" : "-L" );
|
||||
Serial.print(" Speed: ");
|
||||
Serial.print(Speed,DEC);
|
||||
Serial.print(" Steps: ");
|
||||
Serial.print(SpeedSteps,DEC);
|
||||
Serial.print(" Dir: ");
|
||||
Serial.println( (Dir == DCC_DIR_FWD) ? "Forward" : "Reverse" );
|
||||
#endif
|
||||
|
||||
newDirection = Dir;
|
||||
newSpeed = Speed;
|
||||
numSpeedSteps = SpeedSteps;
|
||||
};
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Function packet for our address
|
||||
void notifyDccFunc(uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState)
|
||||
{
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("notifyDccFunc: Addr: ");
|
||||
Serial.print(Addr,DEC);
|
||||
Serial.print( (AddrType == DCC_ADDR_SHORT) ? 'S' : 'L' );
|
||||
Serial.print(" Function Group: ");
|
||||
Serial.print(FuncGrp,DEC);
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
if(lastFuncStateList[FuncGrp] != FuncState)
|
||||
{
|
||||
lastFuncStateList[FuncGrp] = FuncState;
|
||||
switch(FuncGrp)
|
||||
{
|
||||
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||
case FN_0:
|
||||
Serial.print(" FN0: ");
|
||||
Serial.println((FuncState & FN_BIT_00) ? "1 " : "0 ");
|
||||
|
||||
u8x8.setCursor(0, 2);
|
||||
u8x8.print("FN0 : ");
|
||||
u8x8.println((FuncState & FN_BIT_00) ? "1" : "0");
|
||||
break;
|
||||
#endif
|
||||
|
||||
case FN_0_4:
|
||||
u8x8.setCursor(0, 2);
|
||||
u8x8.print("FN0 : ");
|
||||
|
||||
if(Dcc.getCV(CV_29_CONFIG) & CV29_F0_LOCATION) // Only process Function 0 in this packet if we're not in Speed Step 14 Mode
|
||||
{
|
||||
Serial.print(" FN 0: ");
|
||||
Serial.print((FuncState & FN_BIT_00) ? "1 ": "0 ");
|
||||
|
||||
u8x8.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 ");
|
||||
|
||||
u8x8.print((FuncState & FN_BIT_01) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_02) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_03) ? "1": "0");
|
||||
u8x8.println((FuncState & FN_BIT_04) ? "1": "0");
|
||||
break;
|
||||
|
||||
case FN_5_8:
|
||||
Serial.print(" FN 5-8: ");
|
||||
Serial.print((FuncState & FN_BIT_05) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_06) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_07) ? "1 ": "0 ");
|
||||
Serial.println((FuncState & FN_BIT_08) ? "1 ": "0 ");
|
||||
|
||||
u8x8.setCursor(0, 3);
|
||||
u8x8.print("FN5 : ");
|
||||
u8x8.print((FuncState & FN_BIT_05) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_06) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_07) ? "1": "0");
|
||||
u8x8.println((FuncState & FN_BIT_08) ? "1": "0");
|
||||
break;
|
||||
|
||||
case FN_9_12:
|
||||
Serial.print(" FN 9-12: ");
|
||||
Serial.print((FuncState & FN_BIT_09) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_10) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_11) ? "1 ": "0 ");
|
||||
Serial.println((FuncState & FN_BIT_12) ? "1 ": "0 ");
|
||||
|
||||
u8x8.setCursor(0, 4);
|
||||
u8x8.print("FN9 : ");
|
||||
u8x8.print((FuncState & FN_BIT_09) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_10) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_11) ? "1": "0");
|
||||
u8x8.println((FuncState & FN_BIT_12) ? "1": "0");
|
||||
break;
|
||||
|
||||
case FN_13_20:
|
||||
Serial.print(" FN 13-20: ");
|
||||
Serial.print((FuncState & FN_BIT_13) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_14) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_15) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_16) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_17) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_18) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_19) ? "1 ": "0 ");
|
||||
Serial.println((FuncState & FN_BIT_20) ? "1 ": "0 ");
|
||||
|
||||
u8x8.setCursor(0, 5);
|
||||
u8x8.print("FN13: ");
|
||||
u8x8.print((FuncState & FN_BIT_13) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_14) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_15) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_16) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_17) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_18) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_19) ? "1": "0");
|
||||
u8x8.println((FuncState & FN_BIT_20) ? "1": "0");
|
||||
break;
|
||||
|
||||
case FN_21_28:
|
||||
Serial.print(" FN 21-28: ");
|
||||
Serial.print((FuncState & FN_BIT_21) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_22) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_23) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_24) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_25) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_26) ? "1 ": "0 ");
|
||||
Serial.print((FuncState & FN_BIT_27) ? "1 ": "0 ");
|
||||
Serial.println((FuncState & FN_BIT_28) ? "1 ": "0 ");
|
||||
|
||||
u8x8.setCursor(0, 6);
|
||||
u8x8.print("FN21: ");
|
||||
u8x8.print((FuncState & FN_BIT_21) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_22) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_23) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_24) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_25) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_26) ? "1": "0");
|
||||
u8x8.print((FuncState & FN_BIT_27) ? "1": "0");
|
||||
u8x8.println((FuncState & FN_BIT_28) ? "1": "0");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Packet
|
||||
#ifdef DEBUG_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg: ") ;
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
void setup()
|
||||
{
|
||||
#ifdef DEBUG_PRINT
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
Serial.println("NMRA Dcc Multifunction Motor Decoder Demo");
|
||||
#endif
|
||||
|
||||
u8x8.begin();
|
||||
u8x8.setFlipMode(1);
|
||||
u8x8.setFont(u8x8_font_chroma48medium8_r);
|
||||
u8x8.setCursor(0, 0);
|
||||
u8x8.println("NMRA DCC");
|
||||
u8x8.println("MultiFunction");
|
||||
u8x8.println("Decoder Demo");
|
||||
delay(2000);
|
||||
u8x8.clearDisplay();
|
||||
u8x8.setCursor(0, 0);
|
||||
u8x8.println("Speed:");
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
||||
Dcc.init( MAN_ID_DIY, 10, FLAGS_MY_ADDRESS_ONLY | FLAGS_AUTO_FACTORY_DEFAULT, 0 );
|
||||
|
||||
// Uncomment to force CV Reset to Factory Defaults
|
||||
// notifyCVResetFactoryDefault();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||
Dcc.process();
|
||||
|
||||
// Handle Speed changes
|
||||
if((lastSpeed != newSpeed) || (lastDirection != newDirection))
|
||||
{
|
||||
lastSpeed = newSpeed;
|
||||
lastDirection = newDirection;
|
||||
|
||||
u8x8.setCursor(0, 0);
|
||||
u8x8.print("Speed: ");
|
||||
u8x8.print(newSpeed);
|
||||
u8x8.print(":");
|
||||
u8x8.println( newDirection ? "Fwd" : "Rev");
|
||||
}
|
||||
|
||||
// Handle resetting CVs back to Factory Defaults
|
||||
if( FactoryDefaultCVIndex )
|
||||
{
|
||||
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||
}
|
||||
}
|
@@ -207,10 +207,6 @@ void setup()
|
||||
{
|
||||
#ifdef DEBUG_PRINT
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
|
||||
Serial.println("NMRA Dcc Multifunction Motor Decoder Demo");
|
||||
#endif
|
||||
|
||||
|
@@ -1,5 +1,3 @@
|
||||
#include <NmraDcc.h>
|
||||
|
||||
// This Example shows how to use the library with the Iowa Scaled Engineering ARD-DCCSHIELD
|
||||
// You can find out more about this DCC Interface here: http://www.iascaled.com/store/ARD-DCCSHIELD
|
||||
//
|
||||
@@ -16,6 +14,9 @@
|
||||
// JP6 - Boards without VIO - User Choice
|
||||
// JP7 - Enable Programming ACK - 1-2 ON 3-4 ON
|
||||
//
|
||||
#include <NmraDcc.h>
|
||||
#include <elapsedMillis.h>
|
||||
|
||||
// It is a very basic DCC Accessory Decoder that does nothing except allow CV Read/Write and
|
||||
// you can also print every DCC packet by uncommenting the "#define NOTIFY_DCC_MSG" line below
|
||||
#define NOTIFY_DCC_MSG
|
||||
@@ -101,9 +102,8 @@ void notifyCVChange(uint16_t CV, uint8_t Value)
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
uint8_t maxWaitLoops = 255;
|
||||
while(!Serial && maxWaitLoops--)
|
||||
delay(20);
|
||||
elapsedMillis millisWaitedForUSB = 0;
|
||||
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
|
||||
|
||||
Serial.println("NMRA DCC Iowa Scaled Engineering ARD-DCCSHIELD Example");
|
||||
|
||||
@@ -111,7 +111,7 @@ void setup()
|
||||
// Many Arduino Cores now support the digitalPinToInterrupt() function that makes it easier to figure out the
|
||||
// Interrupt Number for the Arduino Pin number, which reduces confusion.
|
||||
#ifdef digitalPinToInterrupt
|
||||
Dcc.pin(DCC_PIN, 1);
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
#else
|
||||
Dcc.pin(0, DCC_PIN, 1);
|
||||
#endif
|
||||
|
@@ -1,5 +1,5 @@
|
||||
name=NmraDcc
|
||||
version=2.0.17
|
||||
version=2.0.11
|
||||
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner, bugfixes by Jueff
|
||||
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
||||
sentence=Enables NMRA DCC Communication
|
||||
|
Reference in New Issue
Block a user