Initial checkin to git and added library.properties

This commit is contained in:
Alex Shepherd
2015-05-12 18:46:14 +12:00
parent 0091ab0cd9
commit b01ca7f31e
14 changed files with 4269 additions and 0 deletions

BIN
17Ftn_Decoder_diagram.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 KiB

View File

@@ -0,0 +1,157 @@
{\rtf1\ansi\ansicpg1252\deff0\deflang1033{\fonttbl{\f0\fswiss\fprq2\fcharset0 Calibri;}}
{\*\generator Msftedit 5.41.15.1515;}\viewkind4\uc1\pard\sl276\slmult1\b\f0\fs24 Decoder Configuration Details\par
\par
\b0 The multfunction decoder examples all for 4 functions to be assigned to any of the 17 available pins: on/off control, single line blinking with variable rate, servo control with start position/stop position/transit rate CV setting and end to end control via the function (on/off), and paired line blinking with variable rate.\par
\par
When first loaded the decoder is set to short DCC address 24 (or 17 in Decoder_17LED_1Function). The decoder can be reset to the original parameters by loading CV 120 with 120 (decimal). This will reset everything including the decoder address, when the pushbutton on the Pro Mini is pushed (reset) or by powering the decoder off then on. You will know when the default CV setting are being reset as the decoder will flash Digital Pin 14 (A0) for one second.\par
The decoder address can be changed to another \ul\b short\ulnone\b0 DCC address by changing CV 1.\par
\par
\b The 7 Servo 10 LED decoder configuration\par
\par
\b0 Arduino Pro Mini Pins are set as follows: 3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19\par
\ul\b Pro Mini Pin\ulnone\b0\tab \ul\b Function\par
\ulnone\b0 3\tab\tab\tab F0 Servo\par
4\tab\tab\tab F1 Servo\par
5\tab\tab\tab F2 Servo\par
6\tab\tab\tab F3 Servo\par
7\tab\tab\tab F4 Servo\par
8\tab\tab\tab F5 Servo\par
9\tab\tab\tab F6 Servo\par
10\tab\tab\tab F7 Single LED Blink\par
11\tab\tab\tab F8 Single LED Blink\par
12\tab\tab\tab F9 Single LED On/Off\par
13\tab\tab\tab F10 Single LED On/Off\par
14\tab\tab\tab F11 Single LED Blink\par
15\tab\tab\tab F12 Single LED Blink\par
16\tab\tab\tab F13 Double LED Blink F13 and F14 LEDs (Pins 16 & 17)\par
17\tab\tab\tab F14 Single LED Blink (Ignored because of F13)\par
18\tab\tab\tab F15 Double LED Blink F15 and F16 LEDs (Pins 18 & 19)\par
19\tab\tab\tab F16 Single LED Blink (Ignored because of F15)\par
(Blink rates are set differently for demonstration purposes)\par
\par
Correspondingly by way of example, for the 7 Servo 10 LED decoder configuration, CV\rquote s are initially set to the following:\par
\par
\ul\b\{CV number, Value\} Description\par
\ulnone\b0 \{1, 24\} Decoder Initial Address\par
\{30, 2\}, //F0 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{31, 1\}, //F0 Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{32, 28\}, //F0 Start Position F0=0\par
\{33, 140\}, //F0 End Position F0=1\par
\{34, 28\}, //F0 Current Position\par
\{35, 2\}, //F1 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{36, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{37, 28\}, // Start Position Fx=0\par
\{38, 140\}, // End Position Fx=1\par
\{39, 28\}, // Current Position\par
\{40, 2\}, //F2 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{41, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{42, 28\}, // Start Position Fx=0\par
\{43, 140\}, // End Position Fx=1\par
\{44, 28\}, // Current Position\par
\{45, 2\}, //F3 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{46, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{47, 28\}, // Start Position Fx=0\par
\{48, 140\}, // End Position Fx=1\par
\{49, 28\}, // Current Position\par
\{50, 2\}, //F4 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{51, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{52, 28\}, // Start Position Fx=0\par
\{53, 140\}, // End Position Fx=1\par
\{54, 28\}, // Current Position\par
\{55, 2\}, //F5 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{56, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{57, 28\}, // Start Position Fx=0\par
\{58, 140\}, // End Position Fx=1\par
\{59, 28\}, // Current Position\par
\{60, 2\}, //F6 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{61, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{62, 28\}, // Start Position Fx=0\par
\{63, 140\}, // End Position Fx=1\par
\{64, 28\}, // Current Position\par
\{65, 1\}, //F7 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{66, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{67, 1\}, // Start Position Fx=0\par
\{68,35\}, // End Position Fx=1\par
\{69, 1\}, // Current Position\par
\{70, 1\}, //F8 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{71, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{72, 1\}, // Start Position Fx=0\par
\{73, 100\}, // End Position Fx=1\par
\{74, 1\}, // Current Position\par
\{75, 0\}, //F9 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{76, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{77, 1\}, // Start Position Fx=0\par
\{78, 10\}, // End Position Fx=1\par
\{79, 1\}, // Current Position\par
\{80, 0\}, //F10 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{81, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{82, 1\}, // Start Position Fx=0\par
\{83, 5\}, // End Position Fx=1\par
\{84, 1\}, // Current Position\par
\{85, 1\}, //F11 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{86, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{87, 1\}, // Start Position Fx=0\par
\{88, 5\}, // End Position Fx=1\par
\{89, 1\}, // Current Position\par
\{90, 1\}, //F12 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{91, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{92, 1\}, // Start Position Fx=0\par
\{93, 20\}, // End Position Fx=1\par
\{94, 1\}, // Current Position\par
\{95, 3\}, //F13 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{96, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{97, 1\}, // Start Position Fx=0\par
\{98, 35\}, // End Position Fx=1\par
\{99, 2\}, // Current Position\par
\{100, 0\}, //F14 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{101, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{102, 1\}, // Start Position Fx=0\par
\{103, 4\}, // End Position Fx=1\par
\{104, 1\}, // Current Position\par
\{105, 3\}, //F15 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{106, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{107, 1\}, // Start Position Fx=0\par
\{108, 60\}, // End Position Fx=1\par
\{109, 20\}, // Current Position\par
\{110, 0\}, //F16 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink\par
\{111, 1\}, // Rate Blink=Eate,PWM=Rate,Servo=Rate\par
\{112, 1\}, // Start Position Fx=0\par
\{113, 4\}, // End Position Fx=1\par
\{114, 1\}, // Current Position\par
\{120, 0\} Master Reset CV When set to 120 and Power cycled resets all CV\rquote s\par
\par
Each Function is controlled by a maximum of 5 CV\rquote s. \par
For example \b F0\b0 is initially set for \b servo\b0 control:\par
\{30, 2\}, // F0 Pin Function Configuration 2=Servo\par
\{31, 1\}, // F0 Rate Blink=Rate, Servo=Rate\par
\{32, 28\}, // F0 Start Position F0=0 Initially 26\par
\{33, 140\}, // F0 End Position F0=1 Initially 140\par
\{34, 28\}, // F0 Current Position or State\par
\par
\b F7\b0 is initially set for \b single LED blinking\b0 control:\par
\{65, 1\}, // F7 Pin Function Configuration 1=Blink\par
\{66, 1\}, // Rate Blink 1= Slowest\par
\{67, 1\}, // Start Count Set to 1 or 0\par
\{68,35\}, // End Count 2-255 -- 255 = Slow Blink\par
\{69, 1\}, // Current State of LED\par
\par
\b F13\b0 is initially set for \b double LED blinking control\b0 of F13 and F14 LED Pins:\par
\{95, 3\}, // F13 Pin Function Configuration 3=Double LED Blink\par
\{96, 1\}, // Rate Blink 1= Slowest\par
\{97, 1\}, // Start Count Set to 1 or 0\par
\{98, 35\}, // End Count 2-255 -- 255 = Slow Blink\par
\{99, 2\}, // Current State of LED\par
\par
\b F9\b0 is initially set for \b single LED On/Off\b0 control:\par
\{75, 0\}, // F9 Pin Function Configuration 0=On/Off\par
\{76, 1\}, // Ignored\par
\{77, 1\}, // Ignored\par
\{78, 10\}, // Ignored\par
\{79, 1\}, // Ignored\par
\par
Before changing the CV settings take a look at the initial settings and make small changes first to observe the effects. This should give modelers a starting point, and a better understanding for customizing their decoders.\par
\par
Please also note there is a new 17 LED (On/Off) decoder configuration (Decoder_17LED_4Function), which while providing the 17 LED on/off control like the very first decoder (Decoder_17LED_1Function) introduced in this project, whichh ONLY has on/off control. However, this new version can be reconfigured via CV control to perform the other functions too.\par
\pard\par
}

103
Description NmraDcc.txt Executable file
View File

@@ -0,0 +1,103 @@
------------------------------------------------------------------------
OpenDCC - NmraDcc
Copyright (c) 2008 Alex Shepherd
This source file is subject of the GNU general public license 2,
that is available at the world-wide-web at
http:www.gnu.org/licenses/gpl.txt
------------------------------------------------------------------------
file: Description NmraDcc.txt
author:
webpage:
history:
------------------------------------------------------------------------
Call the DCC pin function to define which External Interrupt and Pin to use and also to enable the Pull-Up
Dcc.pin(0, 2, 1);
Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 10, FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER, 0 )
MAN_ID_DIY = 0x0D (CV8 defined in NmraDcc.h)
FLAGS_OUTPUT_ADDRESS_MODE = 0x40 (CV29/541 bit 6 defined in NmraDcc.h)
FLAGS_DCC_ACCESSORY_DECODER = 0x80 (CV 29/541 bit 7 defined in NmraDcc.h)
------------------------------------------------------------------------
You MUST call the NmraDcc.process() method frequently from the Arduino loop()
function for correct library operation
Dcc.process();
------------------------------------------------------------------------
This function is called whenever a normal DCC Turnout Packet is received
notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)
Addr = Decoder address
BoardAddr = Address of this decoder
OutputAddr = Address of Turnout on this decoder
State =
------------------------------------------------------------------------
This function is called whenever a DCC Signal Aspect Packet is received
notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State)
Addr = Decoder address
OutputIndex = Address of Signal Aspect
State =
------------------------------------------------------------------------
notifyDccFunc( uint16_t Addr, uint8_t FuncNum, uint8_t FuncState)
Addr = Decoder address
FuncNum =
FuncState =
------------------------------------------------------------------------
Perform a decoder total reset
notifyDccReset(uint8_t hardReset )
hardReset =
------------------------------------------------------------------------
The decoder receives a Idle packet and should do nothing
notifyDccIdle(void)
------------------------------------------------------------------------
A locomotive packet is received
notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, uint8_t MaxSpeed )
Addr = Address received
Speed = Requested speed
ForwardDir = True if loco moves forward
MaxSpeed =
------------------------------------------------------------------------
Checks if a CV is present in the table and is writable
notifyCVValid( uint16_t CV, uint8_t Writable )
CV = The number of the requested CV
Writable = True is CV is writable
------------------------------------------------------------------------
Read a CV value from the decoder table
notifyCVRead( uint16_t CV)
CV = The number of the requested CV
------------------------------------------------------------------------
Write a CV with Value to the decoder table
notifyCVWrite( uint16_t CV, uint8_t Value)
CV = The number of the requested CV
Value = Value to be written into the requested CV
------------------------------------------------------------------------
Check if the CV written to the table has the correct value
notifyCVChange( uint16_t CV, uint8_t Value)
CV = The number of the requested CV
Value = Value of the changed requested CV
------------------------------------------------------------------------
notifyCVAck(void)

801
NmraDcc.cpp Executable file
View File

@@ -0,0 +1,801 @@
//------------------------------------------------------------------------
//
// OpenDCC - NmraDcc.cpp
//
// Copyright (c) 2011 Alex Shepherd
//
// This source file is subject of the GNU general public license 2,
// that is available at the world-wide-web at
// http://www.gnu.org/licenses/gpl.txt
//
//------------------------------------------------------------------------
//
// file: NmraDcc.cpp
// author: Alex Shepherd
// webpage: http://opendcc.org/
// history: 2011-06-26 Initial Version copied in from OpenDCC
//
//------------------------------------------------------------------------
//
// purpose: Provide a simplified interface to decode NMRA DCC packets
// and build DCC Mobile and Stationary Decoders
//------------------------------------------------------------------------
#include "NmraDcc.h"
#include <avr/eeprom.h>
//------------------------------------------------------------------------
// DCC Receive Routine
//
// Howto: uses two interrupts: a rising edge in DCC polarity triggers INTx
// in INTx handler, Timer0 CompareB with a delay of 80us is started.
// On Timer0 CompareB Match the level of DCC is evaluated and
// parsed.
//
// |<-----116us----->|
//
// DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________
// ^-INTx
// |----87us--->|
// ^Timer-INT: reads zero
//
// DCC 0: _________XXXXXXXXXXXXXXXXXX__________________
// ^-INTx
// |----------->|
// ^Timer-INT: reads one
//
//------------------------------------------------------------------------
// The Timer0 prescaler is hard-coded in wiring.c
#define TIMER_PRESCALER 64
// We will use a time period of 80us and not 87us as this gives us a bit more time to do other stuff
#define DCC_BIT_SAMPLE_PERIOD (F_CPU * 70L / TIMER_PRESCALER / 1000000L)
#if (DCC_BIT_SAMPLE_PERIOD > 254)
#error DCC_BIT_SAMPLE_PERIOD too big, use either larger prescaler or slower processor
#endif
#if (DCC_BIT_SAMPLE_PERIOD < 8)
#error DCC_BIT_SAMPLE_PERIOD too small, use either smaller prescaler or faster processor
#endif
typedef enum
{
WAIT_PREAMBLE = 0,
WAIT_START_BIT,
WAIT_DATA,
WAIT_END_BIT
}
DccRxWaitState ;
struct DccRx_t
{
DccRxWaitState State ;
uint8_t DataReady ;
uint8_t BitCount ;
uint8_t TempByte ;
DCC_MSG PacketBuf;
DCC_MSG PacketCopy;
}
DccRx ;
typedef struct
{
uint8_t Flags ;
uint8_t OpsModeAddressBaseCV ;
uint8_t inServiceMode ;
long LastServiceModeMillis ;
uint8_t PageRegister ; // Used for Paged Operations in Service Mode Programming
uint8_t DuplicateCount ;
DCC_MSG LastMsg ;
uint8_t ExtIntNum;
uint8_t ExtIntPinNum;
#ifdef DCC_DEBUG
uint8_t IntCount;
uint8_t TickCount;
#endif
}
DCC_PROCESSOR_STATE ;
DCC_PROCESSOR_STATE DccProcState ;
void ExternalInterruptHandler(void)
{
OCR0B = TCNT0 + DCC_BIT_SAMPLE_PERIOD ;
#if defined(TIMSK0)
TIMSK0 |= (1<<OCIE0B); // Enable Timer0 Compare Match B Interrupt
TIFR0 |= (1<<OCF0B); // Clear Timer0 Compare Match B Flag
#elif defined(TIMSK)
TIMSK |= (1<<OCIE0B); // Enable Timer0 Compare Match B Interrupt
TIFR |= (1<<OCF0B); // Clear Timer0 Compare Match B Flag
#endif
#ifdef DCC_DEBUG
DccProcState.IntCount++;
#endif
}
ISR(TIMER0_COMPB_vect)
{
uint8_t DccBitVal ;
// Read the DCC input value, if it's low then its a 1 bit, otherwise it is a 0 bit
DccBitVal = !digitalRead(DccProcState.ExtIntPinNum) ;
// Disable Timer0 Compare Match B Interrupt
#if defined(TIMSK0)
TIMSK0 &= ~(1<<OCIE0B);
#elif defined(TIMSK)
TIMSK &= ~(1<<OCIE0B);
#endif
#ifdef DCC_DEBUG
DccProcState.TickCount++;
#endif
DccRx.BitCount++;
switch( DccRx.State )
{
case WAIT_PREAMBLE:
if( DccBitVal )
{
if( DccRx.BitCount > 10 )
DccRx.State = WAIT_START_BIT ;
}
else
DccRx.BitCount = 0 ;
break;
case WAIT_START_BIT:
if( !DccBitVal )
{
DccRx.State = WAIT_DATA ;
DccRx.PacketBuf.Size = 0;
DccRx.PacketBuf.PreambleBits = 0;
for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.Data[i] = 0;
// We now have 1 too many PreambleBits so decrement before copying
DccRx.PacketBuf.PreambleBits = DccRx.BitCount - 1 ;
DccRx.BitCount = 0 ;
DccRx.TempByte = 0 ;
}
break;
case WAIT_DATA:
DccRx.TempByte = ( DccRx.TempByte << 1 ) ;
if( DccBitVal )
DccRx.TempByte |= 1 ;
if( DccRx.BitCount == 8 )
{
if( DccRx.PacketBuf.Size == MAX_DCC_MESSAGE_LEN ) // Packet is too long - abort
{
DccRx.State = WAIT_PREAMBLE ;
DccRx.BitCount = 0 ;
}
else
{
DccRx.State = WAIT_END_BIT ;
DccRx.PacketBuf.Data[ DccRx.PacketBuf.Size++ ] = DccRx.TempByte ;
}
}
break;
case WAIT_END_BIT:
if( DccBitVal ) // End of packet?
{
DccRx.State = WAIT_PREAMBLE ;
DccRx.PacketCopy = DccRx.PacketBuf ;
DccRx.DataReady = 1 ;
}
else // Get next Byte
DccRx.State = WAIT_DATA ;
DccRx.BitCount = 0 ;
DccRx.TempByte = 0 ;
}
}
void ackCV(void)
{
if( notifyCVAck )
notifyCVAck() ;
}
uint8_t validCV( uint16_t CV, uint8_t Writable )
{
if( notifyCVResetFactoryDefault && (CV == CV_MANUFACTURER_ID ) && Writable )
notifyCVResetFactoryDefault();
if( notifyCVValid )
return notifyCVValid( CV, Writable ) ;
uint8_t Valid = 1 ;
if( CV > E2END )
Valid = 0 ;
if( Writable && ( ( CV ==CV_VERSION_ID ) || (CV == CV_MANUFACTURER_ID ) ) )
Valid = 0 ;
return Valid ;
}
uint8_t readCV( uint16_t CV )
{
uint8_t Value ;
if( notifyCVRead )
return notifyCVRead( CV ) ;
Value = eeprom_read_byte( (uint8_t*) CV ) ;
return Value ;
}
uint8_t writeCV( uint16_t CV, uint8_t Value)
{
if( notifyCVWrite )
return notifyCVWrite( CV, Value ) ;
if( eeprom_read_byte( (uint8_t*) CV ) != Value )
{
eeprom_write_byte( (uint8_t*) CV, Value ) ;
if( notifyCVChange )
notifyCVChange( CV, Value) ;
}
return eeprom_read_byte( (uint8_t*) CV ) ;
}
uint16_t getMyAddr(void)
{
uint16_t Addr ;
uint8_t CV29Value ;
CV29Value = readCV( CV_29_CONFIG ) ;
if( CV29Value & 0b10000000 ) // Accessory Decoder?
Addr = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 6 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) ;
else // Multi-Function Decoder?
{
if( CV29Value & 0b00100000 ) // Two Byte Address?
Addr = ( readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB ) << 8 ) | readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB ) ;
else
Addr = readCV( 1 ) ;
}
return Addr ;
}
void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
{
// is it a Byte Operation
if( Cmd & 0x04 )
{
// Perform the Write Operation
if( Cmd & 0x08 )
{
if( validCV( CVAddr, 1 ) )
{
if( writeCV( CVAddr, Value ) == Value )
ackCV();
}
}
else // Perform the Verify Operation
{
if( validCV( CVAddr, 0 ) )
{
if( readCV( CVAddr ) == Value )
ackCV();
}
}
}
// Perform the Bit-Wise Operation
else
{
uint8_t BitMask = (1 << (Value & 0x07) ) ;
uint8_t BitValue = Value & 0x08 ;
uint8_t BitWrite = Value & 0x10 ;
uint8_t tempValue = readCV( CVAddr ) ; // Read the Current CV Value
// Perform the Bit Write Operation
if( BitWrite )
{
if( validCV( CVAddr, 1 ) )
{
if( BitValue )
tempValue |= BitMask ; // Turn the Bit On
else
tempValue &= ~BitMask ; // Turn the Bit Off
if( writeCV( CVAddr, tempValue ) == tempValue )
ackCV() ;
}
}
// Perform the Bit Verify Operation
else
{
if( validCV( CVAddr, 0 ) )
{
if( BitValue )
{
if( tempValue & BitMask )
ackCV() ;
}
else
{
if( !( tempValue & BitMask) )
ackCV() ;
}
}
}
}
}
#ifdef NMRA_DCC_PROCESS_MULTIFUNCTION
void processMultiFunctionMessage( uint16_t Addr, uint8_t Cmd, uint8_t Data1, uint8_t Data2 )
{
uint8_t speed ;
uint16_t CVAddr ;
uint8_t CmdMasked = Cmd & 0b11100000 ;
// If we are an Accessory Decoder
if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER )
{
// and this isn't an Ops Mode Write or we are NOT faking the Multifunction Ops mode address in CV 33+34 or
// it's not our fake address, then return
if( ( CmdMasked != 0b11100000 ) || ( DccProcState.OpsModeAddressBaseCV == 0 ) )
return ;
uint16_t FakeOpsAddr = readCV( DccProcState.OpsModeAddressBaseCV ) | ( readCV( DccProcState.OpsModeAddressBaseCV + 1 ) << 8 ) ;
uint16_t OpsAddr = Addr & 0x3FFF ;
if( OpsAddr != FakeOpsAddr )
return ;
}
// We are looking for FLAGS_MY_ADDRESS_ONLY but it does not match and it is not a Broadcast Address then return
else if( ( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) && ( Addr != getMyAddr() ) && ( Addr != 0 ) )
return ;
switch( CmdMasked )
{
case 0b00000000: // Decoder Control
switch( Cmd & 0b00001110 )
{
case 0b00000000:
if( notifyDccReset && ( Cmd & 0b00000001 ) ) // Hard Reset
if( notifyDccReset)
notifyDccReset( 1 ) ;
break ;
case 0b00000010: // Factory Test
break ;
case 0b00000110: // Set Decoder Flasg
break ;
case 0b00001010: // Set Advanced Addressing
break ;
case 0b00001110: // Decoder Acknowledgment
break ;
default: // Reserved
;
}
break ;
case 0b00100000: // Advanced Operations
switch( Cmd & 0b00011111 )
{
case 0b00011111:
if( notifyDccSpeed )
{
switch( Data1 & 0b01111111 )
{
case 0b00000000:
speed = 1 ;
break ;
case 0b00000001:
speed = 0 ;
break ;
default:
speed = (Data1 & 0b01111111) - 1 ;
}
notifyDccSpeed( Addr, speed, Data1 & 0b10000000, 127 ) ;
}
}
break;
case 0b01000000:
case 0b01100000:
if( notifyDccSpeed )
{
switch( Cmd & 0b00011111 )
{
case 0b00000000:
case 0b00010000:
speed = 1 ;
break ;
case 0b00000001:
case 0b00010001:
speed = 0 ;
break ;
default:
// This speed is not quite right as 14 bit mode can happen and we should check CV29 but...
speed = (((Cmd & 0b00001111) << 1 ) | ((Cmd & 0b00010000) >> 4)) - 2 ;
}
notifyDccSpeed( Addr, speed, Cmd & 0b00100000, 28 ) ;
}
break;
case 0b10000000: // Function Group 0..4
if( notifyDccFunc )
notifyDccFunc( Addr, FN_0_4, Cmd & 0b00011111 ) ;
break;
case 0b10100000: // Function Group 5..8
if( notifyDccFunc)
{
if (Cmd & 0b00010000 )
notifyDccFunc( Addr, FN_5_8, Cmd & 0b00001111 ) ;
else
notifyDccFunc( Addr, FN_9_12, Cmd & 0b00001111 ) ;
}
break;
case 0b11000000: // Feature Expansion Instruction
switch(Cmd & 0b00011111)
{
case 0B00011110:
if( notifyDccFunc )
notifyDccFunc( Addr, FN_13_20, Data1 ) ;
break;
case 0B00011111:
if( notifyDccFunc )
notifyDccFunc( Addr, FN_21_28, Data1 ) ;
break;
}
break;
case 0b11100000: // CV Access
CVAddr = ( ( ( Cmd & 0x03 ) << 8 ) | Data1 ) + 1 ;
processDirectOpsOperation( Cmd, CVAddr, Data2 ) ;
break;
}
}
#endif
#ifdef NMRA_DCC_PROCESS_SERVICEMODE
void processServiceModeOperation( DCC_MSG * pDccMsg )
{
uint16_t CVAddr ;
uint8_t Value ;
if( pDccMsg->Size == 3) // 3 Byte Packets are for Address Only, Register and Paged Mode
{
uint8_t RegisterAddr ;
RegisterAddr = pDccMsg->Data[0] & 0x07 ;
Value = pDccMsg->Data[1] ;
if( RegisterAddr == 5 )
{
DccProcState.PageRegister = Value ;
ackCV();
}
else
{
if( RegisterAddr == 4 )
CVAddr = CV_29_CONFIG ;
else if( ( RegisterAddr <= 3 ) && ( DccProcState.PageRegister > 0 ) )
CVAddr = ( ( DccProcState.PageRegister - 1 ) * 4 ) + RegisterAddr + 1 ;
else
CVAddr = RegisterAddr + 1 ;
if( pDccMsg->Data[0] & 0x08 ) // Perform the Write Operation
{
if( validCV( CVAddr, 1 ) )
{
if( writeCV( CVAddr, Value ) == Value )
ackCV();
}
}
else // Perform the Verify Operation
{
if( validCV( CVAddr, 0 ) )
{
if( readCV( CVAddr ) == Value )
ackCV();
}
}
}
}
else if( pDccMsg->Size == 4) // 4 Byte Packets are for Direct Byte & Bit Mode
{
CVAddr = ( ( ( pDccMsg->Data[0] & 0x03 ) << 8 ) | pDccMsg->Data[1] ) + 1 ;
Value = pDccMsg->Data[2] ;
processDirectOpsOperation( pDccMsg->Data[0] & 0b00001100, CVAddr, Value ) ;
}
}
#endif
void resetServiceModeTimer(uint8_t inServiceMode)
{
// Set the Service Mode
DccProcState.inServiceMode = inServiceMode ;
DccProcState.LastServiceModeMillis = inServiceMode ? millis() : 0 ;
}
void clearDccProcState(uint8_t inServiceMode)
{
resetServiceModeTimer( inServiceMode ) ;
// Set the Page Register to it's default of 1 only on the first Reset
DccProcState.PageRegister = 1 ;
// Clear the LastMsg buffer and DuplicateCount in preparation for possible CV programming
DccProcState.DuplicateCount = 0 ;
memset( &DccProcState.LastMsg, 0, sizeof( DCC_MSG ) ) ;
}
void execDccProcessor( DCC_MSG * pDccMsg )
{
if( ( pDccMsg->Data[0] == 0 ) && ( pDccMsg->Data[1] == 0 ) )
{
if( notifyDccReset )
notifyDccReset( 0 ) ;
#ifdef NMRA_DCC_PROCESS_SERVICEMODE
// If this is the first Reset then perform some one-shot actions as we maybe about to enter service mode
if( DccProcState.inServiceMode )
resetServiceModeTimer( 1 ) ;
else
clearDccProcState( 1 );
#endif
}
else
{
#ifdef NMRA_DCC_PROCESS_SERVICEMODE
if( DccProcState.inServiceMode && ( pDccMsg->Data[0] >= 112 ) && ( pDccMsg->Data[0] < 128 ) )
{
resetServiceModeTimer( 1 ) ;
if( memcmp( pDccMsg, &DccProcState.LastMsg, sizeof( DCC_MSG ) ) )
{
DccProcState.DuplicateCount = 0 ;
memcpy( &DccProcState.LastMsg, pDccMsg, sizeof( DCC_MSG ) ) ;
}
// Wait until you see 2 identicle packets before acting on a Service Mode Packet
else
{
DccProcState.DuplicateCount++ ;
processServiceModeOperation( pDccMsg ) ;
}
}
else
{
if( DccProcState.inServiceMode )
clearDccProcState( 0 );
#endif
// Idle Packet
if( ( pDccMsg->Data[0] == 0b11111111 ) && ( pDccMsg->Data[1] == 0 ) )
{
if( notifyDccIdle )
notifyDccIdle() ;
}
#ifdef NMRA_DCC_PROCESS_MULTIFUNCTION
// Multi Function Decoders (7-bit address)
else if( pDccMsg->Data[0] < 128 )
processMultiFunctionMessage( pDccMsg->Data[0], pDccMsg->Data[1], pDccMsg->Data[2], pDccMsg->Data[3] ) ;
// Basic Accessory Decoders (9-bit) & Extended Accessory Decoders (11-bit)
else if( pDccMsg->Data[0] < 192 )
#else
else if( ( pDccMsg->Data[0] >= 128 ) && ( pDccMsg->Data[0] < 192 ) )
#endif
{
if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER )
{
uint16_t BoardAddress ;
uint8_t OutputAddress ;
uint8_t OutputIndex ;
uint16_t Address ;
BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ;
// If we're filtering was it my board address Our or a broadcast address
if( ( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) && ( BoardAddress != getMyAddr() ) && ( BoardAddress != 511 ) )
return;
OutputAddress = pDccMsg->Data[1] & 0b00000111 ;
OutputIndex = OutputAddress >> 1;
Address = ( ( ( BoardAddress - 1 ) << 2 ) | OutputIndex ) + 1 ;
if(pDccMsg->Data[1] & 0b10000000)
{
if( notifyDccAccState )
notifyDccAccState( Address, BoardAddress, OutputAddress, pDccMsg->Data[1] & 0b00001000 ) ;
}
else
{
if( notifyDccSigState )
notifyDccSigState( Address, OutputIndex, pDccMsg->Data[2] ) ;
}
}
}
#ifdef NMRA_DCC_PROCESS_MULTIFUNCTION
// Multi Function Decoders (14-bit address)
else if( pDccMsg->Data[0] < 232 )
{
uint16_t Address ;
Address = ( pDccMsg->Data[0] << 8 ) | pDccMsg->Data[1];
processMultiFunctionMessage( Address, pDccMsg->Data[2], pDccMsg->Data[3], pDccMsg->Data[4] ) ;
}
#endif
#ifdef NMRA_DCC_PROCESS_SERVICEMODE
}
#endif
}
}
void initDccProcessor( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV )
{
}
NmraDcc::NmraDcc()
{
}
void NmraDcc::pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup)
{
DccProcState.ExtIntNum = ExtIntNum;
DccProcState.ExtIntPinNum = ExtIntPinNum;
pinMode( ExtIntPinNum, INPUT );
if( EnablePullup )
digitalWrite(ExtIntPinNum, HIGH);
}
void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV )
{
// Clear all the static member variables
memset( &DccRx, 0, sizeof( DccRx) );
#ifdef TCCR0A
// Change Timer0 Waveform Generation Mode from Fast PWM back to Normal Mode
TCCR0A &= ~((1<<WGM01)|(1<<WGM00));
#else
#error NmraDcc Library requires a processor with Timer0 Output Compare B feature
#endif
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, RISING);
DccProcState.Flags = Flags ;
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
uint8_t cv29Mask = Flags & 0b11000000 ; // peal off the top two bits
writeCV( 7, VersionId ) ;
writeCV( 8, ManufacturerId ) ;
// Set the Bits that control Multifunction or Accessory behaviour
// and if the Accessory decoder optionally handles Output Addressing
writeCV( CV_29_CONFIG, ( readCV( CV_29_CONFIG ) & ~cv29Mask ) | cv29Mask ) ;
clearDccProcState( 0 );
}
uint8_t NmraDcc::getCV( uint16_t CV )
{
return readCV(CV);
}
uint8_t NmraDcc::setCV( uint16_t CV, uint8_t Value)
{
return writeCV(CV,Value);
}
uint8_t NmraDcc::isSetCVReady(void)
{
if(notifyIsSetCVReady)
return notifyIsSetCVReady();
return eeprom_is_ready();
}
#ifdef DCC_DEBUG
uint8_t NmraDcc::getIntCount(void)
{
return DccProcState.IntCount;
}
uint8_t NmraDcc::getTickCount(void)
{
return DccProcState.TickCount;
}
uint8_t NmraDcc::getState(void)
{
return DccRx.State;
}
uint8_t NmraDcc::getBitCount(void)
{
return DccRx.BitCount;
}
#endif
uint8_t NmraDcc::process()
{
if( DccProcState.inServiceMode )
{
if( (millis() - DccProcState.LastServiceModeMillis ) > 20L )
{
clearDccProcState( 0 ) ;
}
}
if( DccRx.DataReady )
{
// We need to do this check with interrupts disabled
cli();
Msg = DccRx.PacketCopy ;
DccRx.DataReady = 0 ;
sei();
uint8_t xorValue = 0 ;
for(uint8_t i = 0; i < DccRx.PacketCopy.Size; i++)
xorValue ^= DccRx.PacketCopy.Data[i];
if(xorValue)
return 0 ;
else
{
if( notifyDccMsg )
notifyDccMsg( &Msg );
execDccProcessor( &Msg );
}
return 1 ;
}
return 0 ;
};

178
NmraDcc.h Executable file
View File

@@ -0,0 +1,178 @@
//------------------------------------------------------------------------
//
// OpenDCC - NmraDcc.cpp
//
// Copyright (c) 2008 Alex Shepherd
//
// This source file is subject of the GNU general public license 2,
// that is available at the world-wide-web at
// http://www.gnu.org/licenses/gpl.txt
//
//------------------------------------------------------------------------
//
// file: NmraDccTest.cpp
// author: Alex Shepherd
// webpage: http://opendcc.org/
// history: 2008-03-20 Initial Version
//
//------------------------------------------------------------------------
//
// purpose: Provide a simplified interface to decode NMRA DCC packets
// and build DCC MutliFunction and Stationary Decoders
//
//------------------------------------------------------------------------
// Uncomment the following Line to Enable Service Mode CV Programming
#define NMRA_DCC_PROCESS_SERVICEMODE
// Uncomment the following line to Enable MutliFunction Decoder Operations
#define NMRA_DCC_PROCESS_MULTIFUNCTION
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#ifndef NMRADCC_IS_IN
#define NMRADCC_IS_IN
#define MAX_DCC_MESSAGE_LEN 6 // including XOR-Byte
typedef struct
{
uint8_t Size ;
uint8_t PreambleBits ;
uint8_t Data[MAX_DCC_MESSAGE_LEN] ;
} DCC_MSG ;
//--------------------------------------------------------------------------
// This section contains the NMRA Assigned DCC Manufacturer Id Codes that
// are used in projects
//
// This value is to be used for CV8
//--------------------------------------------------------------------------
#define MAN_ID_JMRI 0x12
#define MAN_ID_DIY 0x0D
#define MAN_ID_SILICON_RAILWAY 0x21
//--------------------------------------------------------------------------
// This section contains the Product/Version Id Codes for projects
//
// This value is to be used for CV7
//
// NOTE: Each Product/Version Id Code needs to be UNIQUE for that particular
// the DCC Manufacturer Id Code
//--------------------------------------------------------------------------
// Product/Version Id Codes allocated under: MAN_ID_JMRI
// Product/Version Id Codes allocated under: MAN_ID_DIY
// Standard CV Addresses
#define CV_ACCESSORY_DECODER_ADDRESS_LSB 1
#define CV_ACCESSORY_DECODER_ADDRESS_MSB 9
#define CV_MULTIFUNCTION_PRIMARY_ADDRESS 1
#define CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB 17
#define CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB 18
#define CV_VERSION_ID 7
#define CV_MANUFACTURER_ID 8
#define CV_29_CONFIG 29
#define MAXCV E2END // the upper limit of the CV value currently defined to max memory.
typedef enum
{
FN_0_4 = 1,
FN_5_8,
FN_9_12,
FN_13_20,
FN_21_28
} FN_GROUP;
#define FN_BIT_00 0x10
#define FN_BIT_01 0x01
#define FN_BIT_02 0x02
#define FN_BIT_03 0x04
#define FN_BIT_04 0x08
#define FN_BIT_05 0x01
#define FN_BIT_06 0x02
#define FN_BIT_07 0x04
#define FN_BIT_08 0x08
#define FN_BIT_09 0x01
#define FN_BIT_10 0x02
#define FN_BIT_11 0x04
#define FN_BIT_12 0x08
#define FN_BIT_13 0x01
#define FN_BIT_14 0x02
#define FN_BIT_15 0x04
#define FN_BIT_16 0x08
#define FN_BIT_17 0x10
#define FN_BIT_18 0x20
#define FN_BIT_19 0x40
#define FN_BIT_20 0x80
#define FN_BIT_21 0x01
#define FN_BIT_22 0x02
#define FN_BIT_23 0x04
#define FN_BIT_24 0x08
#define FN_BIT_25 0x10
#define FN_BIT_26 0x20
#define FN_BIT_27 0x40
#define FN_BIT_28 0x80
class NmraDcc
{
private:
DCC_MSG Msg ;
public:
NmraDcc();
// Flag values to be logically ORed together and passed into the init() method
#define FLAGS_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup);
void init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
uint8_t process();
uint8_t getCV( uint16_t CV );
uint8_t setCV( uint16_t CV, uint8_t Value);
uint8_t isSetCVReady( void );
// #define DCC_DEBUG
#ifdef DCC_DEBUG
uint8_t getIntCount(void);
uint8_t getTickCount(void);
uint8_t getBitCount(void);
uint8_t getState(void);
#endif
};
extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak));
extern void notifyDccIdle(void) __attribute__ ((weak));
extern void notifyDccSpeed( uint16_t Addr, uint8_t Speed, uint8_t ForwardDir, uint8_t MaxSpeed ) __attribute__ ((weak));
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak));
extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak));
extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak));
extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
extern uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((weak));
extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak));
extern uint8_t notifyIsSetCVReady(void) __attribute__ ((weak));
extern void notifyCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
extern void notifyCVAck(void) __attribute__ ((weak));
#endif

View File

@@ -0,0 +1,544 @@
// Production 17 Function DCC Decoder
// Version 3.0 Geoff Bunza 2014
// Uses modified software servo Lib
//
// ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP
// ******** AFTER THE INITIAL DECODER LOAD REMOVE THE "//" IN THE FOOLOWING LINE!!
//#define DECODER_LOADED
#include <NmraDcc.h>
#include <SoftwareServo.h>
SoftwareServo servo0;
SoftwareServo servo1;
SoftwareServo servo2;
SoftwareServo servo3;
SoftwareServo servo4;
SoftwareServo servo5;
SoftwareServo servo6;
SoftwareServo servo7;
SoftwareServo servo8;
SoftwareServo servo9;
SoftwareServo servo10;
SoftwareServo servo11;
SoftwareServo servo12;
SoftwareServo servo13;
SoftwareServo servo14;
SoftwareServo servo15;
SoftwareServo servo16;
#define servo_start_delay 50
#define servo_init_delay 7
int tim_delay = 500;
int numfpins = 17;
byte fpins [] = {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
const int FunctionPin0 = 3;
const int FunctionPin1 = 4;
const int FunctionPin2 = 5;
const int FunctionPin3 = 6;
const int FunctionPin4 = 7;
const int FunctionPin5 = 8;
const int FunctionPin6 = 9;
const int FunctionPin7 = 10;
const int FunctionPin8 = 11;
const int FunctionPin9 = 12;
const int FunctionPin10 = 13;
const int FunctionPin11 = 14; //A0
const int FunctionPin12 = 15; //A1
const int FunctionPin13 = 16; //A2
const int FunctionPin14 = 17; //A3 & LOAD ACK
const int FunctionPin15 = 18; //A4
const int FunctionPin16 = 19; //A5
NmraDcc Dcc ;
DCC_MSG Packet ;
uint8_t CV_DECODER_MASTER_RESET = 120;
int t; // temp
#define This_Decoder_Address 24
struct QUEUE
{
int inuse;
int current_position;
int increment;
int stop_value;
int start_value;
};
QUEUE *ftn_queue = new QUEUE[16];
extern uint8_t Decoder_Address = This_Decoder_Address;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
{CV_DECODER_MASTER_RESET, 0},
{30, 2}, //F0 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{31, 1}, //F0 Rate Blink=Eate,PWM=Rate,Servo=Rate
{32, 28}, //F0 Start Position F0=0
{33, 140}, //F0 End Position F0=1
{34, 28}, //F0 Current Position
{35, 2}, //F1 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{36, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{37, 28}, // Start Position Fx=0
{38, 140}, // End Position Fx=1
{39, 28}, // Current Position
{40, 2}, //F2 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{41, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{42, 28}, // Start Position Fx=0
{43, 140}, // End Position Fx=1
{44, 28}, // Current Position
{45, 2}, //F3 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{46, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{47, 28}, // Start Position Fx=0
{48, 140}, // End Position Fx=1
{49, 28}, // Current Position
{50, 2}, //F4 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{51, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{52, 28}, // Start Position Fx=0
{53, 140}, // End Position Fx=1
{54, 28}, // Current Position
{55, 2}, //F5 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{56, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{57, 28}, // Start Position Fx=0
{58, 140}, // End Position Fx=1
{59, 28}, // Current Position
{60, 2}, //F6 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{61, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{62, 28}, // Start Position Fx=0
{63, 140}, // End Position Fx=1
{64, 28}, // Current Position
{65, 2}, //F7 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{66, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{67, 28}, // Start Position Fx=0
{68,140}, // End Position Fx=1
{69, 28}, // Current Position
{70, 2}, //F8 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{71, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{72, 28}, // Start Position Fx=0
{73, 140}, // End Position Fx=1
{74, 28}, // Current Position
{75, 2}, //F9 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{76, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{77, 28}, // Start Position Fx=0
{78, 140}, // End Position Fx=1
{79, 28}, // Current Position
{80, 0}, //F10 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{81, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{82, 1}, // Start Position Fx=0
{83, 5}, // End Position Fx=1
{84, 1}, // Current Position
{85, 1}, //F11 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{86, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{87, 1}, // Start Position Fx=0
{88, 5}, // End Position Fx=1
{89, 1}, // Current Position
{90, 1}, //F12 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{91, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{92, 1}, // Start Position Fx=0
{93, 20}, // End Position Fx=1
{94, 1}, // Current Position
{95, 3}, //F13 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{96, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{97, 1}, // Start Position Fx=0
{98, 35}, // End Position Fx=1
{99, 2}, // Current Position
{100, 0}, //F14 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{101, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{102, 1}, // Start Position Fx=0
{103, 4}, // End Position Fx=1
{104, 1}, // Current Position
{105, 3}, //F15 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{106, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{107, 1}, // Start Position Fx=0
{108, 60}, // End Position Fx=1
{109, 1}, // Current Position
{110, 0}, //F16 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{111, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{112, 1}, // Start Position Fx=0
{113, 4}, // End Position Fx=1
{114, 1}, // Current Position
//FUTURE USE
{115, 0}, //F17 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{116, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{117, 28}, // Start Position Fx=0
{118, 50}, // End Position Fx=1
{119, 28}, // Current Position
};
uint8_t FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
};
void setup() //******************************************************
{
int i;
uint8_t cv_value;
Serial.begin(115200);
// initialize the digital pins as outputs
for (int i=0; i < numfpins; i++) {
pinMode(fpins[i], OUTPUT);
digitalWrite(fpins[i], 0);
}
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 1);
delay (tim_delay/10);
}
delay( tim_delay);
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 0);
delay (tim_delay/10);
}
delay( tim_delay);
// Setup which External Interrupt, the Pin it's associated with that we're using
Dcc.pin(0, 2, 0);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 100, FLAGS_MY_ADDRESS_ONLY, 0 );
delay(800);
#if defined(DECODER_LOADED)
if ( Dcc.getCV(CV_DECODER_MASTER_RESET)== CV_DECODER_MASTER_RESET )
#endif
{
for (int j=0; j < FactoryDefaultCVIndex; j++ )
Dcc.setCV( FactoryDefaultCVs[j].CV, FactoryDefaultCVs[j].Value);
digitalWrite(fpins[14], 1);
delay (1000);
digitalWrite(fpins[14], 0);
}
for ( i=0; i < numfpins; i++) {
cv_value = Dcc.getCV( 30+(i*5)) ;
//Serial.print(" cv_value: ");
//Serial.println(cv_value, DEC) ;
switch ( cv_value ) {
case 0: // LED on/off
ftn_queue[i].inuse = 0;
break;
case 1: // LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2: //servo
{ ftn_queue[i].current_position =int (Dcc.getCV( 34+(i*5)));
ftn_queue[i].stop_value = int (Dcc.getCV( 33+(i*5)));
ftn_queue[i].start_value = int (Dcc.getCV( 32+(i*5)));
ftn_queue[i].increment = -int (char (Dcc.getCV( 31+(i*5))));
switch ( i ) {
case 0: servo0.attach(FunctionPin0); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo0.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 1: servo1.attach(FunctionPin1); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo1.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 2: servo2.attach(FunctionPin2); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo2.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 3: servo3.attach(FunctionPin3); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo3.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 4: servo4.attach(FunctionPin4); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo4.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 5: servo5.attach(FunctionPin5); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo5.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 6: servo6.attach(FunctionPin6); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo6.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 7: servo7.attach(FunctionPin7); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo7.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 8: servo8.attach(FunctionPin8); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo8.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 9: servo9.attach(FunctionPin9); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo9.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 10: servo10.attach(FunctionPin10); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo10.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 11: servo11.attach(FunctionPin11); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo11.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 12: servo12.attach(FunctionPin12); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo12.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 13: servo13.attach(FunctionPin13); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo13.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 14: servo14.attach(FunctionPin14); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo14.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 15: servo15.attach(FunctionPin15); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo15.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 16: servo16.attach(FunctionPin16); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo16.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
default:
break;
}
}
break;
case 3: // DOUBLE ALTERNATING LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
digitalWrite(fpins[i+1], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 4: // NEXT FEATURE to pin
break;
default:
break;
}
}
}
void loop() //**********************************************************************
{
//MUST call the NmraDcc.process() method frequently
// from the Arduino loop() function for correct library operation
Dcc.process();
SoftwareServo::refresh();
delay(8);
for (int i=0; i < numfpins; i++) {
if (ftn_queue[i].inuse==1) {
ftn_queue[i].current_position = ftn_queue[i].current_position + ftn_queue[i].increment;
switch (Dcc.getCV( 30+(i*5))) {
case 0:
break;
case 1:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2:
{
if (ftn_queue[i].increment > 0) {
if (ftn_queue[i].current_position > ftn_queue[i].stop_value)
ftn_queue[i].current_position = ftn_queue[i].stop_value;
}
if (ftn_queue[i].increment < 0) {
if (ftn_queue[i].current_position < ftn_queue[i].start_value)
ftn_queue[i].current_position = ftn_queue[i].start_value;
}
set_servo(i, ftn_queue[i].current_position);
}
break;
case 3:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
digitalWrite(fpins[i]+1, ~ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
i++;
break;
case 4: //FUTURE FUNCTION
break;
default:
break;
}
}
}
}
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) {
switch(FuncGrp)
{
case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1
exec_function( 0, FunctionPin0, (FuncState & FN_BIT_00)>>4 );
exec_function( 1, FunctionPin1, (FuncState & FN_BIT_01));
exec_function( 2, FunctionPin2, (FuncState & FN_BIT_02)>>1);
exec_function( 3, FunctionPin3, (FuncState & FN_BIT_03)>>2 );
exec_function( 4, FunctionPin4, (FuncState & FN_BIT_04)>>3 );
break;
case FN_5_8: //Function Group 1 S FFFF == 1 F8 F7 F6 F5 & == 0 F12 F11 F10 F9 F8
exec_function( 5, FunctionPin5, (FuncState & FN_BIT_05));
exec_function( 6, FunctionPin6, (FuncState & FN_BIT_06)>>1 );
exec_function( 7, FunctionPin7, (FuncState & FN_BIT_07)>>2 );
exec_function( 8, FunctionPin8, (FuncState & FN_BIT_08)>>3 );
break;
case FN_9_12:
exec_function( 9, FunctionPin9, (FuncState & FN_BIT_09));
exec_function( 10, FunctionPin10, (FuncState & FN_BIT_10)>>1 );
exec_function( 11, FunctionPin11, (FuncState & FN_BIT_11)>>2 );
exec_function( 12, FunctionPin12, (FuncState & FN_BIT_12)>>3 );
break;
case FN_13_20: //Function Group 2 FuncState == F20-F13 Function Control
exec_function( 13, FunctionPin13, (FuncState & FN_BIT_13));
exec_function( 14, FunctionPin14, (FuncState & FN_BIT_14)>>1 );
exec_function( 15, FunctionPin15, (FuncState & FN_BIT_15)>>2 );
exec_function( 16, FunctionPin16, (FuncState & FN_BIT_16)>>3 );
break;
case FN_21_28:
break;
}
}
void exec_function (int function, int pin, int FuncState) {
switch ( Dcc.getCV( 30+(function*5)) ) { // Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
case 0: // On - Off LED
digitalWrite (pin, FuncState);
ftn_queue[function].inuse = 0;
break;
case 1: // Blinking LED
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if ((ftn_queue[function].inuse==1) && (FuncState==0)) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
}
}
break;
case 2: // Servo
ftn_queue[function].inuse = 1;
if (FuncState==1) ftn_queue[function].increment = char ( Dcc.getCV( 31+(function*5)));
else ftn_queue[function].increment = - char(Dcc.getCV( 31+(function*5)));
if (FuncState==1) ftn_queue[function].stop_value = Dcc.getCV( 33+(function*5));
else ftn_queue[function].stop_value = Dcc.getCV( 32+(function*5));
/*
Serial.print("servo inc: ") ;
Serial.print(ftn_queue[function].increment,DEC) ;
Serial.print("servo inuse: ") ;
Serial.print(ftn_queue[function].inuse,DEC) ;
Serial.print("servo num: ") ;
Serial.print(function,DEC) ;
Serial.print(" stop: ");
Serial.println(ftn_queue[function].stop_value,DEC) ;
*/
break;
case 3: // Blinking LED PAIR
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 1);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if (FuncState==0) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 0);
}
}
break;
case 4: // Future Function
ftn_queue[function].inuse = 0;
break;
default:
ftn_queue[function].inuse = 0;
break;
}
}
void set_servo (int servo_num, int servo_pos) {
switch (servo_num) {
case 0: servo0.write(servo_pos);
break;
case 1: servo1.write(servo_pos);
break;
case 2: servo2.write(servo_pos);
break;
case 3: servo3.write(servo_pos);
break;
case 4: servo4.write(servo_pos);
break;
case 5: servo5.write(servo_pos);
break;
case 6: servo6.write(servo_pos);
break;
case 7: servo7.write(servo_pos);
break;
case 8: servo8.write(servo_pos);
break;
case 9: servo9.write(servo_pos);
break;
case 10: servo10.write(servo_pos);
break;
case 11: servo11.write(servo_pos);
break;
case 12: servo12.write(servo_pos);
break;
case 13: servo13.write(servo_pos);
break;
case 14: servo14.write(servo_pos);
break;
case 15: servo15.write(servo_pos);
break;
case 16: servo16.write(servo_pos);
break;
default:
break;
}
}

View File

@@ -0,0 +1,545 @@
// Production 17 Function DCC Decoder
// Version 3.0 Geoff Bunza 2014
// Uses modified software servo Lib
//
// ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP
// ******** AFTER THE INITIAL DECODER LOAD REMOVE THE "//" IN THE FOOLOWING LINE!!
//#define DECODER_LOADED
#include <NmraDcc.h>
#include <SoftwareServo.h>
SoftwareServo servo0;
SoftwareServo servo1;
SoftwareServo servo2;
SoftwareServo servo3;
SoftwareServo servo4;
SoftwareServo servo5;
SoftwareServo servo6;
SoftwareServo servo7;
SoftwareServo servo8;
SoftwareServo servo9;
SoftwareServo servo10;
SoftwareServo servo11;
SoftwareServo servo12;
SoftwareServo servo13;
SoftwareServo servo14;
SoftwareServo servo15;
SoftwareServo servo16;
#define servo_start_delay 50
#define servo_init_delay 7
int tim_delay = 500;
int numfpins = 17;
byte fpins [] = {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
const int FunctionPin0 = 3;
const int FunctionPin1 = 4;
const int FunctionPin2 = 5;
const int FunctionPin3 = 6;
const int FunctionPin4 = 7;
const int FunctionPin5 = 8;
const int FunctionPin6 = 9;
const int FunctionPin7 = 10;
const int FunctionPin8 = 11;
const int FunctionPin9 = 12;
const int FunctionPin10 = 13;
const int FunctionPin11 = 14; //A0
const int FunctionPin12 = 15; //A1
const int FunctionPin13 = 16; //A2
const int FunctionPin14 = 17; //A3 & LOAD ACK
const int FunctionPin15 = 18; //A4
const int FunctionPin16 = 19; //A5
NmraDcc Dcc ;
DCC_MSG Packet ;
uint8_t CV_DECODER_MASTER_RESET = 120;
int t; // temp
#define This_Decoder_Address 24
struct QUEUE
{
int inuse;
int current_position;
int increment;
int stop_value;
int start_value;
};
QUEUE *ftn_queue = new QUEUE[16];
extern uint8_t Decoder_Address = This_Decoder_Address;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
{CV_DECODER_MASTER_RESET, 0},
{30, 2}, //F0 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{31, 1}, //F0 Rate Blink=Eate,PWM=Rate,Servo=Rate
{32, 28}, //F0 Start Position F0=0
{33, 140}, //F0 End Position F0=1
{34, 28}, //F0 Current Position
{35, 2}, //F1 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{36, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{37, 28}, // Start Position Fx=0
{38, 140}, // End Position Fx=1
{39, 28}, // Current Position
{40, 2}, //F2 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{41, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{42, 28}, // Start Position Fx=0
{43, 140}, // End Position Fx=1
{44, 28}, // Current Position
{45, 2}, //F3 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{46, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{47, 28}, // Start Position Fx=0
{48, 140}, // End Position Fx=1
{49, 28}, // Current Position
{50, 2}, //F4 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{51, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{52, 28}, // Start Position Fx=0
{53, 140}, // End Position Fx=1
{54, 28}, // Current Position
{55, 2}, //F5 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{56, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{57, 28}, // Start Position Fx=0
{58, 140}, // End Position Fx=1
{59, 28}, // Current Position
{60, 2}, //F6 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{61, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{62, 28}, // Start Position Fx=0
{63, 140}, // End Position Fx=1
{64, 28}, // Current Position
{65, 2}, //F7 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{66, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{67, 28}, // Start Position Fx=0
{68, 140}, // End Position Fx=1
{69, 28}, // Current Position
{70, 2}, //F8 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{71, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{72, 28}, // Start Position Fx=0
{73, 140}, // End Position Fx=1
{74, 28}, // Current Position
{75, 2}, //F9 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{76, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{77, 28}, // Start Position Fx=0
{78, 140}, // End Position Fx=1
{79, 28}, // Current Position
{80, 2}, //F10 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{81, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{82, 28}, // Start Position Fx=0
{83, 140}, // End Position Fx=1
{84, 28}, // Current Position
{85, 2}, //F11 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{86, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{87, 28}, // Start Position Fx=0
{88, 140}, // End Position Fx=1
{89, 28}, // Current Position
{90, 2}, //F12 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{91, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{92, 28}, // Start Position Fx=0
{93, 140}, // End Position Fx=1
{94, 28}, // Current Position
{95, 1}, //F13 Config 0=On/Off,1=Blink,2=Servo,3=PWM
{96, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{97, 1}, // Start Position Fx=0
{98, 20}, // End Position Fx=1
{99, 1}, // Current Position
{100, 0}, //F14 Config 0=On/Off,1=Blink,2=Servo,3=PWM
{101, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{102, 1}, // Start Position Fx=0
{103, 4}, // End Position Fx=1
{104, 1}, // Current Position
{105, 3}, //F15 Config 0=On/Off,1=Blink,2=Servo,3=PWM
{106, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{107, 1}, // Start Position Fx=0
{108, 60}, // End Position Fx=1
{109, 20}, // Current Position
{110, 0}, //F16 Config 0=On/Off,1=Blink,2=Servo,3=PWM
{111, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{112, 1}, // Start Position Fx=0
{113, 4}, // End Position Fx=1
{114, 1}, // Current Position
//FUTURE USE
{115, 0}, //F17 Config 0=On/Off,1=Blink,2=Servo,3=PWM
{116, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{117, 28}, // Start Position Fx=0
{118, 50}, // End Position Fx=1
{119, 28}, // Current Position
};
uint8_t FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
};
void setup() //******************************************************
{
int i;
uint8_t cv_value;
Serial.begin(115200);
// initialize the digital pins as outputs
for (int i=0; i < numfpins; i++) {
pinMode(fpins[i], OUTPUT);
digitalWrite(fpins[i], 0);
}
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 1);
delay (tim_delay/10);
}
delay( tim_delay);
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 0);
delay (tim_delay/10);
}
delay( tim_delay);
// Setup which External Interrupt, the Pin it's associated with that we're using
Dcc.pin(0, 2, 0);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 100, FLAGS_MY_ADDRESS_ONLY, 0 );
delay(800);
#if defined(DECODER_LOADED)
if ( Dcc.getCV(CV_DECODER_MASTER_RESET)== CV_DECODER_MASTER_RESET )
#endif
{
for (int j=0; j < FactoryDefaultCVIndex; j++ )
Dcc.setCV( FactoryDefaultCVs[j].CV, FactoryDefaultCVs[j].Value);
digitalWrite(fpins[14], 1);
delay (1000);
digitalWrite(fpins[14], 0);
}
for ( i=0; i < numfpins; i++) {
cv_value = Dcc.getCV( 30+(i*5)) ;
//Serial.print(" cv_value: ");
//Serial.println(cv_value, DEC) ;
switch ( cv_value ) {
case 0: // LED on/off
ftn_queue[i].inuse = 0;
break;
case 1: // LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2: //servo
{ ftn_queue[i].current_position =int (Dcc.getCV( 34+(i*5)));
ftn_queue[i].stop_value = int (Dcc.getCV( 33+(i*5)));
ftn_queue[i].start_value = int (Dcc.getCV( 32+(i*5)));
ftn_queue[i].increment = -int (char (Dcc.getCV( 31+(i*5))));
switch ( i ) {
case 0: servo0.attach(FunctionPin0); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo0.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 1: servo1.attach(FunctionPin1); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo1.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 2: servo2.attach(FunctionPin2); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo2.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 3: servo3.attach(FunctionPin3); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo3.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 4: servo4.attach(FunctionPin4); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo4.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 5: servo5.attach(FunctionPin5); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo5.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 6: servo6.attach(FunctionPin6); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo6.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 7: servo7.attach(FunctionPin7); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo7.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 8: servo8.attach(FunctionPin8); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo8.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 9: servo9.attach(FunctionPin9); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo9.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 10: servo10.attach(FunctionPin10); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo10.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 11: servo11.attach(FunctionPin11); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo11.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 12: servo12.attach(FunctionPin12); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo12.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 13: servo13.attach(FunctionPin13); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo13.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 14: servo14.attach(FunctionPin14); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo14.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 15: servo15.attach(FunctionPin15); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo15.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 16: servo16.attach(FunctionPin16); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo16.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
default:
break;
}
}
break;
case 3: // DOUBLE ALTERNATING LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
digitalWrite(fpins[i+1], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 4: // NEXT FEATURE to pin
break;
default:
break;
}
}
}
void loop() //**********************************************************************
{
//MUST call the NmraDcc.process() method frequently
// from the Arduino loop() function for correct library operation
Dcc.process();
SoftwareServo::refresh();
delay(8);
for (int i=0; i < numfpins; i++) {
if (ftn_queue[i].inuse==1) {
ftn_queue[i].current_position = ftn_queue[i].current_position + ftn_queue[i].increment;
switch (Dcc.getCV( 30+(i*5))) {
case 0:
break;
case 1:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2:
{
if (ftn_queue[i].increment > 0) {
if (ftn_queue[i].current_position > ftn_queue[i].stop_value)
ftn_queue[i].current_position = ftn_queue[i].stop_value;
}
if (ftn_queue[i].increment < 0) {
if (ftn_queue[i].current_position < ftn_queue[i].start_value)
ftn_queue[i].current_position = ftn_queue[i].start_value;
}
set_servo(i, ftn_queue[i].current_position);
}
break;
case 3:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
digitalWrite(fpins[i]+1, ~ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
i++;
break;
case 4: //FUTURE FUNCTION
break;
default:
break;
}
}
}
}
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) {
switch(FuncGrp)
{
case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1
exec_function( 0, FunctionPin0, (FuncState & FN_BIT_00)>>4 );
exec_function( 1, FunctionPin1, (FuncState & FN_BIT_01));
exec_function( 2, FunctionPin2, (FuncState & FN_BIT_02)>>1);
exec_function( 3, FunctionPin3, (FuncState & FN_BIT_03)>>2 );
exec_function( 4, FunctionPin4, (FuncState & FN_BIT_04)>>3 );
break;
case FN_5_8: //Function Group 1 S FFFF == 1 F8 F7 F6 F5 & == 0 F12 F11 F10 F9 F8
exec_function( 5, FunctionPin5, (FuncState & FN_BIT_05));
exec_function( 6, FunctionPin6, (FuncState & FN_BIT_06)>>1 );
exec_function( 7, FunctionPin7, (FuncState & FN_BIT_07)>>2 );
exec_function( 8, FunctionPin8, (FuncState & FN_BIT_08)>>3 );
break;
case FN_9_12:
exec_function( 9, FunctionPin9, (FuncState & FN_BIT_09));
exec_function( 10, FunctionPin10, (FuncState & FN_BIT_10)>>1 );
exec_function( 11, FunctionPin11, (FuncState & FN_BIT_11)>>2 );
exec_function( 12, FunctionPin12, (FuncState & FN_BIT_12)>>3 );
break;
case FN_13_20: //Function Group 2 FuncState == F20-F13 Function Control
exec_function( 13, FunctionPin13, (FuncState & FN_BIT_13));
exec_function( 14, FunctionPin14, (FuncState & FN_BIT_14)>>1 );
exec_function( 15, FunctionPin15, (FuncState & FN_BIT_15)>>2 );
exec_function( 16, FunctionPin16, (FuncState & FN_BIT_16)>>3 );
break;
case FN_21_28:
break;
}
}
void exec_function (int function, int pin, int FuncState) {
switch ( Dcc.getCV( 30+(function*5)) ) { // Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
case 0: // On - Off LED
//Serial.println("****************cv:0 ") ;
digitalWrite (pin, FuncState);
ftn_queue[function].inuse = 0;
break;
case 1: // Blinking LED
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if ((ftn_queue[function].inuse==1) && (FuncState==0)) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
}
}
break;
case 2: // Servo
ftn_queue[function].inuse = 1;
if (FuncState==1) ftn_queue[function].increment = char ( Dcc.getCV( 31+(function*5)));
else ftn_queue[function].increment = - char(Dcc.getCV( 31+(function*5)));
if (FuncState==1) ftn_queue[function].stop_value = Dcc.getCV( 33+(function*5));
else ftn_queue[function].stop_value = Dcc.getCV( 32+(function*5));
/*
Serial.print("servo inc: ") ;
Serial.print(ftn_queue[function].increment,DEC) ;
Serial.print("servo inuse: ") ;
Serial.print(ftn_queue[function].inuse,DEC) ;
Serial.print("servo num: ") ;
Serial.print(function,DEC) ;
Serial.print(" stop: ");
Serial.println(ftn_queue[function].stop_value,DEC) ;
*/
break;
case 3: // Blinking LED PAIR
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 1);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if (FuncState==0) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 0);
}
}
break;
case 4: // Future Function
ftn_queue[function].inuse = 0;
break;
default:
ftn_queue[function].inuse = 0;
break;
}
}
void set_servo (int servo_num, int servo_pos) {
switch (servo_num) {
case 0: servo0.write(servo_pos);
break;
case 1: servo1.write(servo_pos);
break;
case 2: servo2.write(servo_pos);
break;
case 3: servo3.write(servo_pos);
break;
case 4: servo4.write(servo_pos);
break;
case 5: servo5.write(servo_pos);
break;
case 6: servo6.write(servo_pos);
break;
case 7: servo7.write(servo_pos);
break;
case 8: servo8.write(servo_pos);
break;
case 9: servo9.write(servo_pos);
break;
case 10: servo10.write(servo_pos);
break;
case 11: servo11.write(servo_pos);
break;
case 12: servo12.write(servo_pos);
break;
case 13: servo13.write(servo_pos);
break;
case 14: servo14.write(servo_pos);
break;
case 15: servo15.write(servo_pos);
break;
case 16: servo16.write(servo_pos);
break;
default:
break;
}
}

View File

@@ -0,0 +1,544 @@
// Production 17 Function DCC Decoder
// Version 3.0 Geoff Bunza 2014
// Uses modified software servo Lib
//
// ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP
// ******** AFTER THE INITIAL DECODER LOAD REMOVE THE "//" IN THE FOOLOWING LINE!!
//#define DECODER_LOADED
#include <NmraDcc.h>
#include <SoftwareServo.h>
SoftwareServo servo0;
SoftwareServo servo1;
SoftwareServo servo2;
SoftwareServo servo3;
SoftwareServo servo4;
SoftwareServo servo5;
SoftwareServo servo6;
SoftwareServo servo7;
SoftwareServo servo8;
SoftwareServo servo9;
SoftwareServo servo10;
SoftwareServo servo11;
SoftwareServo servo12;
SoftwareServo servo13;
SoftwareServo servo14;
SoftwareServo servo15;
SoftwareServo servo16;
#define servo_start_delay 50
#define servo_init_delay 7
int tim_delay = 500;
int numfpins = 17;
byte fpins [] = {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
const int FunctionPin0 = 3;
const int FunctionPin1 = 4;
const int FunctionPin2 = 5;
const int FunctionPin3 = 6;
const int FunctionPin4 = 7;
const int FunctionPin5 = 8;
const int FunctionPin6 = 9;
const int FunctionPin7 = 10;
const int FunctionPin8 = 11;
const int FunctionPin9 = 12;
const int FunctionPin10 = 13;
const int FunctionPin11 = 14; //A0
const int FunctionPin12 = 15; //A1
const int FunctionPin13 = 16; //A2
const int FunctionPin14 = 17; //A3 & LOAD ACK
const int FunctionPin15 = 18; //A4
const int FunctionPin16 = 19; //A5
NmraDcc Dcc ;
DCC_MSG Packet ;
uint8_t CV_DECODER_MASTER_RESET = 120;
int t; // temp
#define This_Decoder_Address 24
struct QUEUE
{
int inuse;
int current_position;
int increment;
int stop_value;
int start_value;
};
QUEUE *ftn_queue = new QUEUE[16];
extern uint8_t Decoder_Address = This_Decoder_Address;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
{CV_DECODER_MASTER_RESET, 0},
{30, 2}, //F0 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{31, 1}, //F0 Rate Blink=Eate,PWM=Rate,Servo=Rate
{32, 28}, //F0 Start Position F0=0
{33, 140}, //F0 End Position F0=1
{34, 28}, //F0 Current Position
{35, 2}, //F1 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{36, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{37, 28}, // Start Position Fx=0
{38, 140}, // End Position Fx=1
{39, 28}, // Current Position
{40, 2}, //F2 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{41, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{42, 28}, // Start Position Fx=0
{43, 140}, // End Position Fx=1
{44, 28}, // Current Position
{45, 2}, //F3 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{46, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{47, 28}, // Start Position Fx=0
{48, 140}, // End Position Fx=1
{49, 28}, // Current Position
{50, 2}, //F4 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{51, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{52, 28}, // Start Position Fx=0
{53, 140}, // End Position Fx=1
{54, 28}, // Current Position
{55, 2}, //F5 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{56, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{57, 28}, // Start Position Fx=0
{58, 140}, // End Position Fx=1
{59, 28}, // Current Position
{60, 2}, //F6 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{61, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{62, 28}, // Start Position Fx=0
{63, 140}, // End Position Fx=1
{64, 28}, // Current Position
{65, 2}, //F7 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{66, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{67, 28}, // Start Position Fx=0
{68, 140}, // End Position Fx=1
{69, 28}, // Current Position
{70, 2}, //F8 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{71, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{72, 28}, // Start Position Fx=0
{73, 140}, // End Position Fx=1
{74, 28}, // Current Position
{75, 2}, //F9 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{76, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{77, 28}, // Start Position Fx=0
{78, 140}, // End Position Fx=1
{79, 28}, // Current Position
{80, 2}, //F10 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{81, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{82, 28}, // Start Position Fx=0
{83, 140}, // End Position Fx=1
{84, 28}, // Current Position
{85, 2}, //F11 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{86, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{87, 28}, // Start Position Fx=0
{88, 140}, // End Position Fx=1
{89, 28}, // Current Position
{90, 2}, //F12 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{91, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{92, 28}, // Start Position Fx=0
{93, 140}, // End Position Fx=1
{94, 28}, // Current Position
{95, 2}, //F13 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{96, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{97, 28}, // Start Position Fx=0
{98, 140}, // End Position Fx=1
{99, 28}, // Current Position
{100, 2}, //F14 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{101, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{102, 28}, // Start Position Fx=0
{103, 140}, // End Position Fx=1
{104, 28}, // Current Position
{105, 1}, //F15 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{106, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{107, 1}, // Start Position Fx=0
{108, 10}, // End Position Fx=1
{109, 1}, // Current Position
{110, 0}, //F16 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{111, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{112, 1}, // Start Position Fx=0
{113, 10}, // End Position Fx=1
{114, 1}, // Current Position
//FUTURE USE
{115, 0}, //F17 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{116, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{117, 28}, // Start Position Fx=0
{118, 50}, // End Position Fx=1
{119, 28}, // Current Position
};
uint8_t FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
};
void setup() //******************************************************
{
int i;
uint8_t cv_value;
Serial.begin(115200);
// initialize the digital pins as outputs
for (int i=0; i < numfpins; i++) {
pinMode(fpins[i], OUTPUT);
digitalWrite(fpins[i], 0);
}
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 1);
delay (tim_delay/10);
}
delay( tim_delay);
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 0);
delay (tim_delay/10);
}
delay( tim_delay);
// Setup which External Interrupt, the Pin it's associated with that we're using
Dcc.pin(0, 2, 0);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 100, FLAGS_MY_ADDRESS_ONLY, 0 );
delay(800);
#if defined(DECODER_LOADED)
if ( Dcc.getCV(CV_DECODER_MASTER_RESET)== CV_DECODER_MASTER_RESET )
#endif
{
for (int j=0; j < FactoryDefaultCVIndex; j++ )
Dcc.setCV( FactoryDefaultCVs[j].CV, FactoryDefaultCVs[j].Value);
digitalWrite(fpins[14], 1);
delay (1000);
digitalWrite(fpins[14], 0);
}
for ( i=0; i < numfpins; i++) {
cv_value = Dcc.getCV( 30+(i*5)) ;
//Serial.print(" cv_value: ");
//Serial.println(cv_value, DEC) ;
switch ( cv_value ) {
case 0: // LED on/off
ftn_queue[i].inuse = 0;
break;
case 1: // LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2: //servo
{ ftn_queue[i].current_position =int (Dcc.getCV( 34+(i*5)));
ftn_queue[i].stop_value = int (Dcc.getCV( 33+(i*5)));
ftn_queue[i].start_value = int (Dcc.getCV( 32+(i*5)));
ftn_queue[i].increment = -int (char (Dcc.getCV( 31+(i*5))));
switch ( i ) {
case 0: servo0.attach(FunctionPin0); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo0.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 1: servo1.attach(FunctionPin1); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo1.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 2: servo2.attach(FunctionPin2); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo2.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 3: servo3.attach(FunctionPin3); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo3.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 4: servo4.attach(FunctionPin4); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo4.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 5: servo5.attach(FunctionPin5); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo5.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 6: servo6.attach(FunctionPin6); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo6.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 7: servo7.attach(FunctionPin7); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo7.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 8: servo8.attach(FunctionPin8); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo8.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 9: servo9.attach(FunctionPin9); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo9.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 10: servo10.attach(FunctionPin10); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo10.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 11: servo11.attach(FunctionPin11); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo11.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 12: servo12.attach(FunctionPin12); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo12.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 13: servo13.attach(FunctionPin13); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo13.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 14: servo14.attach(FunctionPin14); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo14.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 15: servo15.attach(FunctionPin15); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo15.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
case 16: servo16.attach(FunctionPin16); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo16.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(10);}
break;
default:
break;
}
}
break;
case 3: // DOUBLE ALTERNATING LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = Dcc.getCV( 31+(i*5));
digitalWrite(fpins[i], 0);
digitalWrite(fpins[i+1], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 4: // NEXT FEATURE to pin
break;
default:
break;
}
}
}
void loop() //**********************************************************************
{
//MUST call the NmraDcc.process() method frequently
// from the Arduino loop() function for correct library operation
Dcc.process();
SoftwareServo::refresh();
delay(8);
for (int i=0; i < numfpins; i++) {
if (ftn_queue[i].inuse==1) {
ftn_queue[i].current_position = ftn_queue[i].current_position + ftn_queue[i].increment;
switch (Dcc.getCV( 30+(i*5))) {
case 0:
break;
case 1:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2:
{
if (ftn_queue[i].increment > 0) {
if (ftn_queue[i].current_position > ftn_queue[i].stop_value)
ftn_queue[i].current_position = ftn_queue[i].stop_value;
}
if (ftn_queue[i].increment < 0) {
if (ftn_queue[i].current_position < ftn_queue[i].start_value)
ftn_queue[i].current_position = ftn_queue[i].start_value;
}
set_servo(i, ftn_queue[i].current_position);
}
break;
case 3:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
digitalWrite(fpins[i]+1, ~ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
i++;
break;
case 4: //FUTURE FUNCTION
break;
default:
break;
}
}
}
}
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) {
switch(FuncGrp)
{
case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1
exec_function( 0, FunctionPin0, (FuncState & FN_BIT_00)>>4 );
exec_function( 1, FunctionPin1, (FuncState & FN_BIT_01));
exec_function( 2, FunctionPin2, (FuncState & FN_BIT_02)>>1);
exec_function( 3, FunctionPin3, (FuncState & FN_BIT_03)>>2 );
exec_function( 4, FunctionPin4, (FuncState & FN_BIT_04)>>3 );
break;
case FN_5_8: //Function Group 1 S FFFF == 1 F8 F7 F6 F5 & == 0 F12 F11 F10 F9 F8
exec_function( 5, FunctionPin5, (FuncState & FN_BIT_05));
exec_function( 6, FunctionPin6, (FuncState & FN_BIT_06)>>1 );
exec_function( 7, FunctionPin7, (FuncState & FN_BIT_07)>>2 );
exec_function( 8, FunctionPin8, (FuncState & FN_BIT_08)>>3 );
break;
case FN_9_12:
exec_function( 9, FunctionPin9, (FuncState & FN_BIT_09));
exec_function( 10, FunctionPin10, (FuncState & FN_BIT_10)>>1 );
exec_function( 11, FunctionPin11, (FuncState & FN_BIT_11)>>2 );
exec_function( 12, FunctionPin12, (FuncState & FN_BIT_12)>>3 );
break;
case FN_13_20: //Function Group 2 FuncState == F20-F13 Function Control
exec_function( 13, FunctionPin13, (FuncState & FN_BIT_13));
exec_function( 14, FunctionPin14, (FuncState & FN_BIT_14)>>1 );
exec_function( 15, FunctionPin15, (FuncState & FN_BIT_15)>>2 );
exec_function( 16, FunctionPin16, (FuncState & FN_BIT_16)>>3 );
break;
case FN_21_28:
break;
}
}
void exec_function (int function, int pin, int FuncState) {
switch ( Dcc.getCV( 30+(function*5)) ) { // Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
case 0: // On - Off LED
digitalWrite (pin, FuncState);
ftn_queue[function].inuse = 0;
break;
case 1: // Blinking LED
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if ((ftn_queue[function].inuse==1) && (FuncState==0)) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
}
}
break;
case 2: // Servo
ftn_queue[function].inuse = 1;
if (FuncState==1) ftn_queue[function].increment = char ( Dcc.getCV( 31+(function*5)));
else ftn_queue[function].increment = - char(Dcc.getCV( 31+(function*5)));
if (FuncState==1) ftn_queue[function].stop_value = Dcc.getCV( 33+(function*5));
else ftn_queue[function].stop_value = Dcc.getCV( 32+(function*5));
/*
Serial.print("servo inc: ") ;
Serial.print(ftn_queue[function].increment,DEC) ;
Serial.print("servo inuse: ") ;
Serial.print(ftn_queue[function].inuse,DEC) ;
Serial.print("servo num: ") ;
Serial.print(function,DEC) ;
Serial.print(" stop: ");
Serial.println(ftn_queue[function].stop_value,DEC) ;
*/
break;
case 3: // Blinking LED PAIR
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 1);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if (FuncState==0) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 0);
}
}
break;
case 4: // Future Function
ftn_queue[function].inuse = 0;
break;
default:
ftn_queue[function].inuse = 0;
break;
}
}
void set_servo (int servo_num, int servo_pos) {
switch (servo_num) {
case 0: servo0.write(servo_pos);
break;
case 1: servo1.write(servo_pos);
break;
case 2: servo2.write(servo_pos);
break;
case 3: servo3.write(servo_pos);
break;
case 4: servo4.write(servo_pos);
break;
case 5: servo5.write(servo_pos);
break;
case 6: servo6.write(servo_pos);
break;
case 7: servo7.write(servo_pos);
break;
case 8: servo8.write(servo_pos);
break;
case 9: servo9.write(servo_pos);
break;
case 10: servo10.write(servo_pos);
break;
case 11: servo11.write(servo_pos);
break;
case 12: servo12.write(servo_pos);
break;
case 13: servo13.write(servo_pos);
break;
case 14: servo14.write(servo_pos);
break;
case 15: servo15.write(servo_pos);
break;
case 16: servo16.write(servo_pos);
break;
default:
break;
}
}

View File

@@ -0,0 +1,129 @@
// Working 14 Function DCC Decoder DccAckPin not needed
// Version 3.0 Geoff Bunza 2014
// Uses modified software servo Lib
//
#include <NmraDcc.h>
int tim_delay = 500;
#define numleds 17
byte ledpins [] = {0,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
const int FunctionPin0 = 3;
const int FunctionPin1 = 4;
const int FunctionPin2 = 5;
const int FunctionPin3 = 6;
const int FunctionPin4 = 7;
const int FunctionPin5 = 8;
const int FunctionPin6 = 9;
const int FunctionPin7 = 10;
const int FunctionPin8 = 11;
const int FunctionPin9 = 12;
const int FunctionPin10 = 13;
const int FunctionPin11 = 14; //A0
const int FunctionPin12 = 15; //A1
const int FunctionPin13 = 16; //A2
const int FunctionPin14 = 17; //A3
const int FunctionPin15 = 18; //A4
const int FunctionPin16 = 19; //A5
NmraDcc Dcc ;
DCC_MSG Packet ;
#define This_Decoder_Address 17
extern uint8_t Decoder_Address = This_Decoder_Address;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
};
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);
};
void setup()
{
// initialize the digital pins as an outputs
for (int i=1; i<= numleds; i++) {
pinMode(ledpins[i], OUTPUT);
digitalWrite(ledpins[i], LOW);
}
for (int i=1; i<= numleds; i++) {
digitalWrite(ledpins[i], HIGH);
delay (tim_delay/10);
}
delay( tim_delay);
for (int i=1; i<= numleds; i++) {
digitalWrite(ledpins[i], LOW);
delay (tim_delay/10);
}
delay( tim_delay);
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
Dcc.pin(0, 2, 0);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 100, FLAGS_MY_ADDRESS_ONLY, 0 );
}
void loop()
{
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
Dcc.process();
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
{
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
}
}
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState)
{
switch(FuncGrp)
{
case FN_0_4:
digitalWrite( FunctionPin0, (FuncState & FN_BIT_00) );
digitalWrite( FunctionPin1, (FuncState & FN_BIT_01) );
digitalWrite( FunctionPin2, (FuncState & FN_BIT_02) );
digitalWrite( FunctionPin3, (FuncState & FN_BIT_03) );
digitalWrite( FunctionPin4, (FuncState & FN_BIT_04) );
break;
case FN_5_8:
digitalWrite( FunctionPin5, (FuncState & FN_BIT_05) );
digitalWrite( FunctionPin6, (FuncState & FN_BIT_06) );
digitalWrite( FunctionPin7, (FuncState & FN_BIT_07) );
digitalWrite( FunctionPin8, (FuncState & FN_BIT_08) );
break;
case FN_9_12:
digitalWrite( FunctionPin9, (FuncState & FN_BIT_09) );
digitalWrite( FunctionPin10, (FuncState & FN_BIT_10) );
digitalWrite( FunctionPin11, (FuncState & FN_BIT_11) );
digitalWrite( FunctionPin12, (FuncState & FN_BIT_12) );
break;
case FN_13_20:
digitalWrite( FunctionPin13, (FuncState & FN_BIT_13) );
digitalWrite( FunctionPin14, (FuncState & FN_BIT_14) );
digitalWrite( FunctionPin15, (FuncState & FN_BIT_15) );
digitalWrite( FunctionPin16, (FuncState & FN_BIT_16) );
break;
case FN_21_28:
break;
}
}

View File

@@ -0,0 +1,545 @@
// Production 17 Function DCC Decoder
// Version 3.0 Geoff Bunza 2014
// Uses modified software servo Lib
//
// ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP
// ******** AFTER THE INITIAL DECODER LOAD REMOVE THE "//" IN THE FOOLOWING LINE!!
//#define DECODER_LOADED
#include <NmraDcc.h>
#include <SoftwareServo.h>
SoftwareServo servo0;
SoftwareServo servo1;
SoftwareServo servo2;
SoftwareServo servo3;
SoftwareServo servo4;
SoftwareServo servo5;
SoftwareServo servo6;
SoftwareServo servo7;
SoftwareServo servo8;
SoftwareServo servo9;
SoftwareServo servo10;
SoftwareServo servo11;
SoftwareServo servo12;
SoftwareServo servo13;
SoftwareServo servo14;
SoftwareServo servo15;
SoftwareServo servo16;
#define servo_start_delay 50
#define servo_init_delay 7
int tim_delay = 500;
int numfpins = 17;
byte fpins [] = {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
const int FunctionPin0 = 3;
const int FunctionPin1 = 4;
const int FunctionPin2 = 5;
const int FunctionPin3 = 6;
const int FunctionPin4 = 7;
const int FunctionPin5 = 8;
const int FunctionPin6 = 9;
const int FunctionPin7 = 10;
const int FunctionPin8 = 11;
const int FunctionPin9 = 12;
const int FunctionPin10 = 13;
const int FunctionPin11 = 14; //A0
const int FunctionPin12 = 15; //A1
const int FunctionPin13 = 16; //A2
const int FunctionPin14 = 17; //A3 & LOAD ACK
const int FunctionPin15 = 18; //A4
const int FunctionPin16 = 19; //A5
NmraDcc Dcc ;
DCC_MSG Packet ;
uint8_t CV_DECODER_MASTER_RESET = 120;
int t; // temp
#define This_Decoder_Address 24
struct QUEUE
{
int inuse;
int current_position;
int increment;
int stop_value;
int start_value;
};
QUEUE *ftn_queue = new QUEUE[16];
extern uint8_t Decoder_Address = This_Decoder_Address;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
{CV_DECODER_MASTER_RESET, 0},
{30, 0}, //F0 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{31, 1}, //F0 Rate Blink=Eate,PWM=Rate,Servo=Rate
{32, 28}, //F0 Start Position F0=0
{33, 140}, //F0 End Position F0=1
{34, 28}, //F0 Current Position
{35, 0}, //F1 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{36, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{37, 28}, // Start Position Fx=0
{38, 140}, // End Position Fx=1
{39, 28}, // Current Position
{40, 0}, //F2 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{41, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{42, 28}, // Start Position Fx=0
{43, 140}, // End Position Fx=1
{44, 28}, // Current Position
{45, 0}, //F3 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{46, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{47, 28}, // Start Position Fx=0
{48, 140}, // End Position Fx=1
{49, 28}, // Current Position
{50, 0}, //F4 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{51, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{52, 28}, // Start Position Fx=0
{53, 140}, // End Position Fx=1
{54, 28}, // Current Position
{55, 0}, //F5 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{56, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{57, 28}, // Start Position Fx=0
{58, 140}, // End Position Fx=1
{59, 28}, // Current Position
{60, 0}, //F6 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{61, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{62, 28}, // Start Position Fx=0
{63, 140}, // End Position Fx=1
{64, 28}, // Current Position
{65, 0}, //F7 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{66, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{67, 1}, // Start Position Fx=0
{68,35}, // End Position Fx=1
{69, 1}, // Current Position
{70, 0}, //F8 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{71, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{72, 1}, // Start Position Fx=0
{73, 100}, // End Position Fx=1
{74, 1}, // Current Position
{75, 0}, //F9 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{76, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{77, 1}, // Start Position Fx=0
{78, 10}, // End Position Fx=1
{79, 1}, // Current Position
{80, 0}, //F10 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{81, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{82, 1}, // Start Position Fx=0
{83, 5}, // End Position Fx=1
{84, 1}, // Current Position
{85, 0}, //F11 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{86, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{87, 1}, // Start Position Fx=0
{88, 5}, // End Position Fx=1
{89, 1}, // Current Position
{90, 0}, //F12 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{91, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{92, 1}, // Start Position Fx=0
{93, 20}, // End Position Fx=1
{94, 1}, // Current Position
{95, 0}, //F13 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{96, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{97, 1}, // Start Position Fx=0
{98, 35}, // End Position Fx=1
{99, 2}, // Current Position
{100, 0}, //F14 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{101, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{102, 1}, // Start Position Fx=0
{103, 4}, // End Position Fx=1
{104, 1}, // Current Position
{105, 0}, //F15 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{106, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{107, 1}, // Start Position Fx=0
{108, 60}, // End Position Fx=1
{109, 20}, // Current Position
{110, 0}, //F16 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{111, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{112, 1}, // Start Position Fx=0
{113, 4}, // End Position Fx=1
{114, 1}, // Current Position
//FUTURE USE
{115, 0}, //F17 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{116, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{117, 28}, // Start Position Fx=0
{118, 50}, // End Position Fx=1
{119, 28}, // Current Position
};
uint8_t FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
};
void setup() //******************************************************
{
int i;
uint8_t cv_value;
//Serial.begin(115200);
// initialize the digital pins as outputs
for (int i=0; i < numfpins; i++) {
pinMode(fpins[i], OUTPUT);
digitalWrite(fpins[i], 0);
}
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 1);
delay (tim_delay/10);
}
delay( tim_delay);
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 0);
delay (tim_delay/10);
}
delay( tim_delay);
// Setup which External Interrupt, the Pin it's associated with that we're using
Dcc.pin(0, 2, 0);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 100, FLAGS_MY_ADDRESS_ONLY, 0 );
delay(800);
#if defined(DECODER_LOADED)
if ( Dcc.getCV(CV_DECODER_MASTER_RESET)== CV_DECODER_MASTER_RESET )
#endif
{
for (int j=0; j < FactoryDefaultCVIndex; j++ )
Dcc.setCV( FactoryDefaultCVs[j].CV, FactoryDefaultCVs[j].Value);
digitalWrite(fpins[14], 1);
delay (1000);
digitalWrite(fpins[14], 0);
}
for ( i=0; i < numfpins; i++) {
cv_value = Dcc.getCV( 30+(i*5)) ;
//Serial.print(" cv_value: ");
//Serial.println(cv_value, DEC) ;
switch ( cv_value ) {
case 0: // LED on/off
ftn_queue[i].inuse = 0;
break;
case 1: // LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5))) ;
}
break;
case 2: //servo
{ ftn_queue[i].current_position =int (Dcc.getCV( 34+(i*5)));
ftn_queue[i].stop_value = int (Dcc.getCV( 33+(i*5)));
ftn_queue[i].start_value = int (Dcc.getCV( 32+(i*5)));
ftn_queue[i].increment = -int (char (Dcc.getCV( 31+(i*5))));
switch ( i ) {
case 0: servo0.attach(FunctionPin0); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo0.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 1: servo1.attach(FunctionPin1); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo1.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 2: servo2.attach(FunctionPin2); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo2.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 3: servo3.attach(FunctionPin3); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo3.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 4: servo4.attach(FunctionPin4); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo4.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 5: servo5.attach(FunctionPin5); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo5.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 6: servo6.attach(FunctionPin6); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo6.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 7: servo7.attach(FunctionPin7); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo7.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 8: servo8.attach(FunctionPin8); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo8.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 9: servo9.attach(FunctionPin9); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo9.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 10: servo10.attach(FunctionPin10); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo10.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 11: servo11.attach(FunctionPin11); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo11.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 12: servo12.attach(FunctionPin12); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo12.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 13: servo13.attach(FunctionPin13); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo13.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 14: servo14.attach(FunctionPin14); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo14.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 15: servo15.attach(FunctionPin15); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo15.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 16: servo16.attach(FunctionPin16); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo16.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
default:
break;
}
}
break;
case 3: // DOUBLE ALTERNATING LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = Dcc.getCV( 31+(i*5));
digitalWrite(fpins[i], 0);
digitalWrite(fpins[i+1], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 4: // NEXT FEATURE to pin
break;
default:
break;
}
}
}
void loop() //**********************************************************************
{
//MUST call the NmraDcc.process() method frequently
// from the Arduino loop() function for correct library operation
Dcc.process();
SoftwareServo::refresh();
delay(8);
for (int i=0; i < numfpins; i++) {
if (ftn_queue[i].inuse==1) {
ftn_queue[i].current_position = ftn_queue[i].current_position + ftn_queue[i].increment;
switch (Dcc.getCV( 30+(i*5))) {
case 0:
break;
case 1:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2:
{
if (ftn_queue[i].increment > 0) {
if (ftn_queue[i].current_position > ftn_queue[i].stop_value)
ftn_queue[i].current_position = ftn_queue[i].stop_value;
}
if (ftn_queue[i].increment < 0) {
if (ftn_queue[i].current_position < ftn_queue[i].start_value)
ftn_queue[i].current_position = ftn_queue[i].start_value;
}
set_servo(i, ftn_queue[i].current_position);
}
break;
case 3:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
digitalWrite(fpins[i]+1, ~ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
i++;
break;
case 4: //FUTURE FUNCTION
break;
default:
break;
}
}
}
}
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) {
switch(FuncGrp)
{
case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1
exec_function( 0, FunctionPin0, (FuncState & FN_BIT_00)>>4 );
exec_function( 1, FunctionPin1, (FuncState & FN_BIT_01));
exec_function( 2, FunctionPin2, (FuncState & FN_BIT_02)>>1);
exec_function( 3, FunctionPin3, (FuncState & FN_BIT_03)>>2 );
exec_function( 4, FunctionPin4, (FuncState & FN_BIT_04)>>3 );
break;
case FN_5_8: //Function Group 1 S FFFF == 1 F8 F7 F6 F5 & == 0 F12 F11 F10 F9 F8
exec_function( 5, FunctionPin5, (FuncState & FN_BIT_05));
exec_function( 6, FunctionPin6, (FuncState & FN_BIT_06)>>1 );
exec_function( 7, FunctionPin7, (FuncState & FN_BIT_07)>>2 );
exec_function( 8, FunctionPin8, (FuncState & FN_BIT_08)>>3 );
break;
case FN_9_12:
exec_function( 9, FunctionPin9, (FuncState & FN_BIT_09));
exec_function( 10, FunctionPin10, (FuncState & FN_BIT_10)>>1 );
exec_function( 11, FunctionPin11, (FuncState & FN_BIT_11)>>2 );
exec_function( 12, FunctionPin12, (FuncState & FN_BIT_12)>>3 );
break;
case FN_13_20: //Function Group 2 FuncState == F20-F13 Function Control
exec_function( 13, FunctionPin13, (FuncState & FN_BIT_13));
exec_function( 14, FunctionPin14, (FuncState & FN_BIT_14)>>1 );
exec_function( 15, FunctionPin15, (FuncState & FN_BIT_15)>>2 );
exec_function( 16, FunctionPin16, (FuncState & FN_BIT_16)>>3 );
break;
case FN_21_28:
break;
}
}
void exec_function (int function, int pin, int FuncState) {
switch ( Dcc.getCV( 30+(function*5)) ) { // Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
case 0: // On - Off LED
//Serial.println("****************cv:0 ") ;
digitalWrite (pin, FuncState);
ftn_queue[function].inuse = 0;
break;
case 1: // Blinking LED
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(pin, 0);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if ((ftn_queue[function].inuse==1) && (FuncState==0)) {
ftn_queue[function].inuse = 0;
digitalWrite(pin, 0);
}
}
break;
case 2: // Servo
ftn_queue[function].inuse = 1;
if (FuncState==1) ftn_queue[function].increment = char ( Dcc.getCV( 31+(function*5)));
else ftn_queue[function].increment = - char(Dcc.getCV( 31+(function*5)));
if (FuncState==1) ftn_queue[function].stop_value = Dcc.getCV( 33+(function*5));
else ftn_queue[function].stop_value = Dcc.getCV( 32+(function*5));
/*
Serial.print("servo inc: ") ;
Serial.print(ftn_queue[function].increment,DEC) ;
Serial.print("servo inuse: ") ;
Serial.print(ftn_queue[function].inuse,DEC) ;
Serial.print("servo num: ") ;
Serial.print(function,DEC) ;
Serial.print(" stop: ");
Serial.println(ftn_queue[function].stop_value,DEC) ;
*/
break;
case 3: // Blinking LED PAIR
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 1);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if (FuncState==0) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 0);
}
}
break;
case 4: // Future Function
ftn_queue[function].inuse = 0;
break;
default:
ftn_queue[function].inuse = 0;
break;
}
}
void set_servo (int servo_num, int servo_pos) {
switch (servo_num) {
case 0: servo0.write(servo_pos);
break;
case 1: servo1.write(servo_pos);
break;
case 2: servo2.write(servo_pos);
break;
case 3: servo3.write(servo_pos);
break;
case 4: servo4.write(servo_pos);
break;
case 5: servo5.write(servo_pos);
break;
case 6: servo6.write(servo_pos);
break;
case 7: servo7.write(servo_pos);
break;
case 8: servo8.write(servo_pos);
break;
case 9: servo9.write(servo_pos);
break;
case 10: servo10.write(servo_pos);
break;
case 11: servo11.write(servo_pos);
break;
case 12: servo12.write(servo_pos);
break;
case 13: servo13.write(servo_pos);
break;
case 14: servo14.write(servo_pos);
break;
case 15: servo15.write(servo_pos);
break;
case 16: servo16.write(servo_pos);
break;
default:
break;
}
}

View File

@@ -0,0 +1,545 @@
// Production 17 Function DCC Decoder
// Version 3.0 Geoff Bunza 2014
// Uses modified software servo Lib
//
// ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP
// ******** AFTER THE INITIAL DECODER LOAD REMOVE THE "//" IN THE FOOLOWING LINE!!
//#define DECODER_LOADED
#include <NmraDcc.h>
#include <SoftwareServo.h>
SoftwareServo servo0;
SoftwareServo servo1;
SoftwareServo servo2;
SoftwareServo servo3;
SoftwareServo servo4;
SoftwareServo servo5;
SoftwareServo servo6;
SoftwareServo servo7;
SoftwareServo servo8;
SoftwareServo servo9;
SoftwareServo servo10;
SoftwareServo servo11;
SoftwareServo servo12;
SoftwareServo servo13;
SoftwareServo servo14;
SoftwareServo servo15;
SoftwareServo servo16;
#define servo_start_delay 50
#define servo_init_delay 7
int tim_delay = 500;
int numfpins = 17;
byte fpins [] = {3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19};
const int FunctionPin0 = 3;
const int FunctionPin1 = 4;
const int FunctionPin2 = 5;
const int FunctionPin3 = 6;
const int FunctionPin4 = 7;
const int FunctionPin5 = 8;
const int FunctionPin6 = 9;
const int FunctionPin7 = 10;
const int FunctionPin8 = 11;
const int FunctionPin9 = 12;
const int FunctionPin10 = 13;
const int FunctionPin11 = 14; //A0
const int FunctionPin12 = 15; //A1
const int FunctionPin13 = 16; //A2
const int FunctionPin14 = 17; //A3 & LOAD ACK
const int FunctionPin15 = 18; //A4
const int FunctionPin16 = 19; //A5
NmraDcc Dcc ;
DCC_MSG Packet ;
uint8_t CV_DECODER_MASTER_RESET = 120;
int t; // temp
#define This_Decoder_Address 24
struct QUEUE
{
int inuse;
int current_position;
int increment;
int stop_value;
int start_value;
};
QUEUE *ftn_queue = new QUEUE[16];
extern uint8_t Decoder_Address = This_Decoder_Address;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, This_Decoder_Address},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
{CV_DECODER_MASTER_RESET, 0},
{30, 2}, //F0 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{31, 1}, //F0 Rate Blink=Eate,PWM=Rate,Servo=Rate
{32, 28}, //F0 Start Position F0=0
{33, 140}, //F0 End Position F0=1
{34, 28}, //F0 Current Position
{35, 2}, //F1 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{36, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{37, 28}, // Start Position Fx=0
{38, 140}, // End Position Fx=1
{39, 28}, // Current Position
{40, 2}, //F2 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{41, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{42, 28}, // Start Position Fx=0
{43, 140}, // End Position Fx=1
{44, 28}, // Current Position
{45, 2}, //F3 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{46, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{47, 28}, // Start Position Fx=0
{48, 140}, // End Position Fx=1
{49, 28}, // Current Position
{50, 2}, //F4 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{51, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{52, 28}, // Start Position Fx=0
{53, 140}, // End Position Fx=1
{54, 28}, // Current Position
{55, 2}, //F5 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{56, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{57, 28}, // Start Position Fx=0
{58, 140}, // End Position Fx=1
{59, 28}, // Current Position
{60, 2}, //F6 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{61, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{62, 28}, // Start Position Fx=0
{63, 140}, // End Position Fx=1
{64, 28}, // Current Position
{65, 1}, //F7 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{66, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{67, 1}, // Start Position Fx=0
{68,35}, // End Position Fx=1
{69, 1}, // Current Position
{70, 1}, //F8 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{71, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{72, 1}, // Start Position Fx=0
{73, 100}, // End Position Fx=1
{74, 1}, // Current Position
{75, 0}, //F9 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{76, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{77, 1}, // Start Position Fx=0
{78, 10}, // End Position Fx=1
{79, 1}, // Current Position
{80, 0}, //F10 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{81, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{82, 1}, // Start Position Fx=0
{83, 5}, // End Position Fx=1
{84, 1}, // Current Position
{85, 1}, //F11 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{86, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{87, 1}, // Start Position Fx=0
{88, 5}, // End Position Fx=1
{89, 1}, // Current Position
{90, 1}, //F12 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{91, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{92, 1}, // Start Position Fx=0
{93, 20}, // End Position Fx=1
{94, 1}, // Current Position
{95, 3}, //F13 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{96, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{97, 1}, // Start Position Fx=0
{98, 35}, // End Position Fx=1
{99, 2}, // Current Position
{100, 0}, //F14 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{101, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{102, 1}, // Start Position Fx=0
{103, 4}, // End Position Fx=1
{104, 1}, // Current Position
{105, 3}, //F15 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{106, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{107, 1}, // Start Position Fx=0
{108, 60}, // End Position Fx=1
{109, 20}, // Current Position
{110, 0}, //F16 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{111, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{112, 1}, // Start Position Fx=0
{113, 4}, // End Position Fx=1
{114, 1}, // Current Position
//FUTURE USE
{115, 0}, //F17 Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
{116, 1}, // Rate Blink=Eate,PWM=Rate,Servo=Rate
{117, 28}, // Start Position Fx=0
{118, 50}, // End Position Fx=1
{119, 28}, // Current Position
};
uint8_t FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
};
void setup() //******************************************************
{
int i;
uint8_t cv_value;
//Serial.begin(115200);
// initialize the digital pins as outputs
for (int i=0; i < numfpins; i++) {
pinMode(fpins[i], OUTPUT);
digitalWrite(fpins[i], 0);
}
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 1);
delay (tim_delay/10);
}
delay( tim_delay);
for (int i=0; i < numfpins; i++) {
digitalWrite(fpins[i], 0);
delay (tim_delay/10);
}
delay( tim_delay);
// Setup which External Interrupt, the Pin it's associated with that we're using
Dcc.pin(0, 2, 0);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 100, FLAGS_MY_ADDRESS_ONLY, 0 );
delay(800);
#if defined(DECODER_LOADED)
if ( Dcc.getCV(CV_DECODER_MASTER_RESET)== CV_DECODER_MASTER_RESET )
#endif
{
for (int j=0; j < FactoryDefaultCVIndex; j++ )
Dcc.setCV( FactoryDefaultCVs[j].CV, FactoryDefaultCVs[j].Value);
digitalWrite(fpins[14], 1);
delay (1000);
digitalWrite(fpins[14], 0);
}
for ( i=0; i < numfpins; i++) {
cv_value = Dcc.getCV( 30+(i*5)) ;
//Serial.print(" cv_value: ");
//Serial.println(cv_value, DEC) ;
switch ( cv_value ) {
case 0: // LED on/off
ftn_queue[i].inuse = 0;
break;
case 1: // LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = int (char (Dcc.getCV( 31+(i*5))));
digitalWrite(fpins[i], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5))) ;
}
break;
case 2: //servo
{ ftn_queue[i].current_position =int (Dcc.getCV( 34+(i*5)));
ftn_queue[i].stop_value = int (Dcc.getCV( 33+(i*5)));
ftn_queue[i].start_value = int (Dcc.getCV( 32+(i*5)));
ftn_queue[i].increment = -int (char (Dcc.getCV( 31+(i*5))));
switch ( i ) {
case 0: servo0.attach(FunctionPin0); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo0.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 1: servo1.attach(FunctionPin1); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo1.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 2: servo2.attach(FunctionPin2); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo2.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 3: servo3.attach(FunctionPin3); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo3.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 4: servo4.attach(FunctionPin4); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo4.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 5: servo5.attach(FunctionPin5); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo5.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 6: servo6.attach(FunctionPin6); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo6.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 7: servo7.attach(FunctionPin7); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo7.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 8: servo8.attach(FunctionPin8); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo8.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 9: servo9.attach(FunctionPin9); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo9.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 10: servo10.attach(FunctionPin10); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo10.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 11: servo11.attach(FunctionPin11); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo11.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 12: servo12.attach(FunctionPin12); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo12.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 13: servo13.attach(FunctionPin13); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo13.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 14: servo14.attach(FunctionPin14); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo14.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 15: servo15.attach(FunctionPin15); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo15.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
case 16: servo16.attach(FunctionPin16); // attaches servo on pin to the servo object
ftn_queue[i].inuse = 1;
servo16.write(ftn_queue[i].start_value);
for (t=0; t<servo_start_delay; t++) {SoftwareServo::refresh();delay(servo_init_delay);}
break;
default:
break;
}
}
break;
case 3: // DOUBLE ALTERNATING LED Blink
{
ftn_queue[i].inuse = 0;
ftn_queue[i].current_position = 0;
ftn_queue[i].start_value = 0;
ftn_queue[i].increment = Dcc.getCV( 31+(i*5));
digitalWrite(fpins[i], 0);
digitalWrite(fpins[i+1], 0);
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 4: // NEXT FEATURE to pin
break;
default:
break;
}
}
}
void loop() //**********************************************************************
{
//MUST call the NmraDcc.process() method frequently
// from the Arduino loop() function for correct library operation
Dcc.process();
SoftwareServo::refresh();
delay(8);
for (int i=0; i < numfpins; i++) {
if (ftn_queue[i].inuse==1) {
ftn_queue[i].current_position = ftn_queue[i].current_position + ftn_queue[i].increment;
switch (Dcc.getCV( 30+(i*5))) {
case 0:
break;
case 1:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
break;
case 2:
{
if (ftn_queue[i].increment > 0) {
if (ftn_queue[i].current_position > ftn_queue[i].stop_value)
ftn_queue[i].current_position = ftn_queue[i].stop_value;
}
if (ftn_queue[i].increment < 0) {
if (ftn_queue[i].current_position < ftn_queue[i].start_value)
ftn_queue[i].current_position = ftn_queue[i].start_value;
}
set_servo(i, ftn_queue[i].current_position);
}
break;
case 3:
if (ftn_queue[i].current_position > ftn_queue[i].stop_value) {
ftn_queue[i].start_value = ~ftn_queue[i].start_value;
digitalWrite(fpins[i], ftn_queue[i].start_value);
digitalWrite(fpins[i]+1, ~ftn_queue[i].start_value);
ftn_queue[i].current_position = 0;
ftn_queue[i].stop_value = int(Dcc.getCV( 33+(i*5)));
}
i++;
break;
case 4: //FUTURE FUNCTION
break;
default:
break;
}
}
}
}
extern void notifyDccFunc( uint16_t Addr, FN_GROUP FuncGrp, uint8_t FuncState) {
switch(FuncGrp)
{
case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1
exec_function( 0, FunctionPin0, (FuncState & FN_BIT_00)>>4 );
exec_function( 1, FunctionPin1, (FuncState & FN_BIT_01));
exec_function( 2, FunctionPin2, (FuncState & FN_BIT_02)>>1);
exec_function( 3, FunctionPin3, (FuncState & FN_BIT_03)>>2 );
exec_function( 4, FunctionPin4, (FuncState & FN_BIT_04)>>3 );
break;
case FN_5_8: //Function Group 1 S FFFF == 1 F8 F7 F6 F5 & == 0 F12 F11 F10 F9 F8
exec_function( 5, FunctionPin5, (FuncState & FN_BIT_05));
exec_function( 6, FunctionPin6, (FuncState & FN_BIT_06)>>1 );
exec_function( 7, FunctionPin7, (FuncState & FN_BIT_07)>>2 );
exec_function( 8, FunctionPin8, (FuncState & FN_BIT_08)>>3 );
break;
case FN_9_12:
exec_function( 9, FunctionPin9, (FuncState & FN_BIT_09));
exec_function( 10, FunctionPin10, (FuncState & FN_BIT_10)>>1 );
exec_function( 11, FunctionPin11, (FuncState & FN_BIT_11)>>2 );
exec_function( 12, FunctionPin12, (FuncState & FN_BIT_12)>>3 );
break;
case FN_13_20: //Function Group 2 FuncState == F20-F13 Function Control
exec_function( 13, FunctionPin13, (FuncState & FN_BIT_13));
exec_function( 14, FunctionPin14, (FuncState & FN_BIT_14)>>1 );
exec_function( 15, FunctionPin15, (FuncState & FN_BIT_15)>>2 );
exec_function( 16, FunctionPin16, (FuncState & FN_BIT_16)>>3 );
break;
case FN_21_28:
break;
}
}
void exec_function (int function, int pin, int FuncState) {
switch ( Dcc.getCV( 30+(function*5)) ) { // Config 0=On/Off,1=Blink,2=Servo,3=Double LED Blink
case 0: // On - Off LED
//Serial.println("****************cv:0 ") ;
digitalWrite (pin, FuncState);
ftn_queue[function].inuse = 0;
break;
case 1: // Blinking LED
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(pin, 0);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if ((ftn_queue[function].inuse==1) && (FuncState==0)) {
ftn_queue[function].inuse = 0;
digitalWrite(pin, 0);
}
}
break;
case 2: // Servo
ftn_queue[function].inuse = 1;
if (FuncState==1) ftn_queue[function].increment = char ( Dcc.getCV( 31+(function*5)));
else ftn_queue[function].increment = - char(Dcc.getCV( 31+(function*5)));
if (FuncState==1) ftn_queue[function].stop_value = Dcc.getCV( 33+(function*5));
else ftn_queue[function].stop_value = Dcc.getCV( 32+(function*5));
/*
Serial.print("servo inc: ") ;
Serial.print(ftn_queue[function].increment,DEC) ;
Serial.print("servo inuse: ") ;
Serial.print(ftn_queue[function].inuse,DEC) ;
Serial.print("servo num: ") ;
Serial.print(function,DEC) ;
Serial.print(" stop: ");
Serial.println(ftn_queue[function].stop_value,DEC) ;
*/
break;
case 3: // Blinking LED PAIR
if ((ftn_queue[function].inuse==0) && (FuncState==1)) {
ftn_queue[function].inuse = 1;
ftn_queue[function].start_value = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 1);
ftn_queue[function].stop_value = int(Dcc.getCV( 33+(function*5)));
} else {
if (FuncState==0) {
ftn_queue[function].inuse = 0;
digitalWrite(fpins[function], 0);
digitalWrite(fpins[function+1], 0);
}
}
break;
case 4: // Future Function
ftn_queue[function].inuse = 0;
break;
default:
ftn_queue[function].inuse = 0;
break;
}
}
void set_servo (int servo_num, int servo_pos) {
switch (servo_num) {
case 0: servo0.write(servo_pos);
break;
case 1: servo1.write(servo_pos);
break;
case 2: servo2.write(servo_pos);
break;
case 3: servo3.write(servo_pos);
break;
case 4: servo4.write(servo_pos);
break;
case 5: servo5.write(servo_pos);
break;
case 6: servo6.write(servo_pos);
break;
case 7: servo7.write(servo_pos);
break;
case 8: servo8.write(servo_pos);
break;
case 9: servo9.write(servo_pos);
break;
case 10: servo10.write(servo_pos);
break;
case 11: servo11.write(servo_pos);
break;
case 12: servo12.write(servo_pos);
break;
case 13: servo13.write(servo_pos);
break;
case 14: servo14.write(servo_pos);
break;
case 15: servo15.write(servo_pos);
break;
case 16: servo16.write(servo_pos);
break;
default:
break;
}
}

View File

@@ -0,0 +1,110 @@
#include <NmraDcc.h>
// This Example shows how to use the library as a DCC Accessory Decoder or a DCC Signalling Decoder
// It responds to both the normal DCC Turnout Control packets and the newer DCC Signal Aspect packets
// You can also print every DCC packet by uncommenting the "#define NOTIFY_DCC_MSG" line below
NmraDcc Dcc ;
DCC_MSG Packet ;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [] =
{
{CV_ACCESSORY_DECODER_ADDRESS_LSB, 1},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
};
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);
};
const int DccAckPin = 3 ;
// This function is called by the NmraDcc library when a DCC ACK needs to be sent
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
void notifyCVAck(void)
{
Serial.println("notifyCVAck") ;
digitalWrite( DccAckPin, HIGH );
delay( 6 );
digitalWrite( DccAckPin, LOW );
}
// Uncomment to print all DCC Packets
//#define NOTIFY_DCC_MSG
#ifdef NOTIFY_DCC_MSG
void notifyDccMsg( DCC_MSG * Msg)
{
Serial.print("notifyDccMsg: ") ;
for(uint8_t i = 0; i < Msg->Size; i++)
{
Serial.print(Msg->Data[i], HEX);
Serial.write(' ');
}
Serial.println();
}
#endif
// This function is called whenever a normal DCC Turnout Packet is received
void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State)
{
Serial.print("notifyDccAccState: ") ;
Serial.print(Addr,DEC) ;
Serial.print(',');
Serial.print(BoardAddr,DEC) ;
Serial.print(',');
Serial.print(OutputAddr,DEC) ;
Serial.print(',');
Serial.println(State, HEX) ;
}
// This function is called whenever a DCC Signal Aspect Packet is received
void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State)
{
Serial.print("notifyDccSigState: ") ;
Serial.print(Addr,DEC) ;
Serial.print(',');
Serial.print(OutputIndex,DEC) ;
Serial.print(',');
Serial.println(State, HEX) ;
}
void setup()
{
Serial.begin(115200);
// Configure the DCC CV Programing ACK pin for an output
pinMode( DccAckPin, OUTPUT );
Serial.println("NMRA DCC Example 1");
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
Dcc.pin(0, 2, 1);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 10, FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER, 0 );
Serial.println("Init Done");
}
void loop()
{
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
Dcc.process();
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
{
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
}
}

59
keywords.txt Executable file
View File

@@ -0,0 +1,59 @@
#######################################
# Syntax Coloring Map Servo
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
DCC_MSG KEYWORD1
NmraDcc KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
NmraDcc KEYWORD2
pin KEYWORD2
init KEYWORD2
process KEYWORD2
getCV KEYWORD2
setCV KEYWORD2
isSetCVReady KEYWORD2
notifyDccReset KEYWORD2
notifyDccIdle KEYWORD2
notifyDccSpeed KEYWORD2
notifyDccFunc KEYWORD2
notifyDccAccState KEYWORD2
notifyDccSigState KEYWORD2
notifyDccMsg KEYWORD2
notifyCVValid KEYWORD2
notifyCVRead KEYWORD2
notifyCVWrite KEYWORD2
notifyIsSetCVReady KEYWORD2
notifyCVChange KEYWORD2
notifyCVAck KEYWORD2
notifyCVResetFactoryDefault KEYWORD2
#######################################
# Constants (LITERAL1)
MAN_ID_JMRI LITERAL1
MAN_ID_DIY LITERAL1
MAN_ID_SILICON_RAILWAY LITERAL1
FLAGS_MY_ADDRESS_ONLY LITERAL1
FLAGS_OUTPUT_ADDRESS_MODE LITERAL1
FLAGS_DCC_ACCESSORY_DECODER LITERAL1
CV_ACCESSORY_DECODER_ADDRESS_LSB LITERAL1
CV_ACCESSORY_DECODER_ADDRESS_MSB LITERAL1
CV_MULTIFUNCTION_PRIMARY_ADDRESS LITERAL1
CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB LITERAL1
CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB LITERAL1
CV_VERSION_ID LITERAL1
CV_MANUFACTURER_ID LITERAL1
CV_29_CONFIG LITERAL1
CV_OPS_MODE_ADDRESS_LSB LITERAL1
#######################################

9
library.properties Normal file
View File

@@ -0,0 +1,9 @@
name=NmraDcc
version=1.0.0
author=Alex Shepherd, Wolfgang Kuffer
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
sentence=Enables NMRA DCC Communication
paragraph=This library allows you to interface to a NMRA DCC track signal and receive DCC commands. The library currently supports the AVR ATTiny84/85 & ATMega88/168/328/32u4 using the INT0/1 Hardware Interrupt and Timer0 Compare Match B
category=Communication
url=http://mrrwa.org/dcc-decoder-interface/
architectures=avr