6 Commits

Author SHA1 Message Date
Alex Shepherd
4b52381fa4 added RP2040 Pico 16 Servo project 2022-02-28 00:35:59 +13:00
Alex Shepherd
5f1e271f2c added CV Storage EEPROM delayed commit
fixed debug for RP2040
2022-02-28 00:35:14 +13:00
Alex Shepherd
6f91d4f7d4 added wait for USB Device to enumerate to some examples
added CV_MANUFACTURER_START
2022-02-27 18:21:52 +13:00
Alex Shepherd
b35c1fbbd0 add .gitignore 2021-09-12 01:23:23 +12:00
Alex Shepherd
f59048383f Reworked the handling of Accessory OPS Mode CV writing to handle the difference of 11-bit Basic Extended Commands and 11-bit Signal Commands 2021-09-12 01:22:21 +12:00
Alex Shepherd
6333abf691 made some changes to the handling of Accessory Basic and Extended OPS Mode CV Programming 2021-09-05 00:28:15 +12:00
7 changed files with 503 additions and 47 deletions

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
*.zip
*.orig
*~
*.txt

View File

@@ -48,6 +48,7 @@
#include "NmraDcc.h"
#include "EEPROM.h"
#include <elapsedMillis.h>
// Uncomment to print DEBUG messages
// #define DEBUG_PRINT
@@ -242,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, ... ) ;
@@ -322,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 ;
@@ -801,8 +812,10 @@ uint8_t readEEPROM (unsigned int CV)
void writeEEPROM (unsigned int CV, uint8_t Value)
{
EEPROM.write (CV, Value) ;
#if defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
EEPROM.commit();
DccProcState.eepromChanged = true;
DccProcState.millisSinceEEPromWrite = 0;
#endif
}
@@ -889,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) ;
@@ -1337,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
@@ -1354,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));
@@ -1379,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));
@@ -1393,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;
}
}
@@ -1406,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");
}
@@ -1419,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);
@@ -1437,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;
}
}
@@ -1485,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:
@@ -1494,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;
@@ -1719,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)
{
@@ -1748,6 +1807,8 @@ uint8_t NmraDcc::process()
execDccProcessor (&Msg);
return 1 ;
}
return 0 ;
};

View File

@@ -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
//
@@ -112,6 +116,8 @@ typedef struct
#define CV_VERSION_ID 7
#define CV_MANUFACTURER_ID 8
#define CV_29_CONFIG 29
#define CV_MANUFACTURER_START 33
#if defined(ESP32)
#include <esp_spi_flash.h>
@@ -211,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
{
@@ -234,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)
/*+

View File

@@ -95,6 +95,8 @@ void notifyDccSigOutputState( uint16_t Addr, uint8_t State)
void setup()
{
Serial.begin(115200);
elapsedMillis millisWaitedForUSB = 0;
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
// Configure the DCC CV Programing ACK pin for an output
pinMode( DccAckPin, OUTPUT );

View File

@@ -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

View File

@@ -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,6 +102,8 @@ void notifyCVChange(uint16_t CV, uint8_t Value)
void setup()
{
Serial.begin(115200);
elapsedMillis millisWaitedForUSB = 0;
while(!Serial && (millisWaitedForUSB < 3000)); // Wait up to 3 seconds for USB to Connect
Serial.println("NMRA DCC Iowa Scaled Engineering ARD-DCCSHIELD Example");

View File

@@ -1,5 +1,5 @@
name=NmraDcc
version=2.0.10
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