/*
TODO: check for maximum when changing CVs 31 ... 34
TODO: implement change of direction --> forward / backward
*/
/* **********************************************************************************
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************** */
#include <Arduino.h> // Needed for C++ conversion of INOfile.
#include <avr/wdt.h> // Needed for automatic reset functions.
#include <NmraDcc.h> // Needed for computing the DCC signals.
NmraDcc Dcc ;
DCC_MSG Packet ;
/* ******************************************************************************* */
// The next line is there to prevent large in-rush currents during PWM cycly.
#define SOFTPWM_OUTPUT_DELAY
// #include <SoftPWM.h>
/* ================================================================================================================
The following part is the included SoftPWM.h file - DO NOT CHANGE ANYTHING TILL THE END OF THIS INCLUSION
*/
/*
Arduino-SoftPWM: a software PWM library for Arduino
Copyright 2016, Victor Tseng <[email protected]>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _SOFTPWM_H_
#define _SOFTPWM_H_
#include <Arduino.h>
// helper macros
#define SOFTPWM_DEFINE_PINMODE(CHANNEL, PMODE, PORT, BIT) \
namespace Palatis { \
template <> \
inline void pinModeStatic<CHANNEL>(uint8_t const mode) { \
if (mode == INPUT) { \
bitClear(PMODE, BIT); bitClear(PORT, BIT); \
} \
else if (mode == INPUT_PULLUP) { \
bitClear(PMODE, BIT); bitSet(PORT, BIT); \
} \
else { \
bitSet(PMODE, BIT); \
} \
} \
}
#define SOFTPWM_DEFINE_CHANNEL(CHANNEL, PMODE, PORT, BIT) \
namespace Palatis { \
template <> \
inline void bitWriteStatic<CHANNEL>(bool const value) { \
bitWrite( PORT, BIT, value ); \
} \
} \
SOFTPWM_DEFINE_PINMODE(CHANNEL, PMODE, PORT, BIT)
#define SOFTPWM_DEFINE_CHANNEL_INVERT(CHANNEL, PMODE, PORT, BIT) \
namespace Palatis { \
template <> \
inline void bitWriteStatic<CHANNEL>(bool const value) { \
bitWrite(PORT, BIT, !value); \
} \
} \
SOFTPWM_DEFINE_PINMODE(CHANNEL, PMODE, PORT, BIT)
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
#define SOFTPWM_DEFINE_OBJECT_WITH_PWM_LEVELS(CHANNEL_CNT, PWM_LEVELS) \
namespace Palatis { \
CSoftPWM<CHANNEL_CNT, PWM_LEVELS> SoftPWM; \
} \
ISR(TIM1_COMPA_vect) { \
interrupts(); \
Palatis::SoftPWM.update(); \
}
#else
#if defined (__AVR_ATmega8__) || defined (__AVR_ATmega8A__)
#define TIMSK1 TIMSK
#endif
#define SOFTPWM_DEFINE_OBJECT_WITH_PWM_LEVELS(CHANNEL_CNT, PWM_LEVELS) \
namespace Palatis { \
CSoftPWM<CHANNEL_CNT, PWM_LEVELS> SoftPWM; \
} \
ISR(TIMER1_COMPA_vect) { \
interrupts(); \
Palatis::SoftPWM.update(); \
}
#endif
#define SOFTPWM_DEFINE_OBJECT(CHANNEL_CNT) \
SOFTPWM_DEFINE_OBJECT_WITH_PWM_LEVELS(CHANNEL_CNT, 0)
#define SOFTPWM_DEFINE_EXTERN_OBJECT_WITH_PWM_LEVELS(CHANNEL_CNT, PWM_LEVELS) \
namespace Palatis { \
extern CSoftPWM<CHANNEL_CNT, PWM_LEVELS> SoftPWM; \
}
#define SOFTPWM_DEFINE_EXTERN_OBJECT(CHANNEL_CNT) \
SOFTPWM_DEFINE_EXTERN_OBJECT_WITH_PWM_LEVELS(CHANNEL_CNT, 0)
// here comes the magic @o@
namespace Palatis {
template <int channel> inline void bitWriteStatic(bool value) {}
template <int channel> inline void pinModeStatic(uint8_t mode) {}
template <int channel>
struct bitWriteStaticExpander {
void operator() (bool value) const {
bitWriteStatic<channel>(value);
bitWriteStaticExpander < channel - 1 > ()(value);
}
void operator() (uint8_t const &count, uint8_t const * const &channels) const {
#ifdef SOFTPWM_OUTPUT_DELAY
bitWriteStatic<channel>((count + channel) < channels[channel]);
#else
bitWriteStatic<channel>(count < channels[channel]);
#endif
bitWriteStaticExpander < channel - 1 > ()(count, channels);
}
};
template <>
struct bitWriteStaticExpander < -1 > {
void operator() (bool) const {}
void operator() (uint8_t const &, uint8_t const * const &) const {}
};
template <int channel>
struct pinModeStaticExpander {
void operator() (uint8_t const mode) const
{
pinModeStatic<channel>(mode);
pinModeStaticExpander < channel - 1 > ()(mode);
}
};
template <>
struct pinModeStaticExpander < -1 > {
void operator() (uint8_t const mode) const {}
};
template <unsigned int num_channels, unsigned int num_PWM_levels>
class CSoftPWM {
public:
void begin(const unsigned long hertz) {
allOff(); //this prevents inverted channels from momentarily going LOW
asm volatile ("/************ pinModeStaticExpander begin ************/");
const uint8_t oldSREG = SREG;
noInterrupts();
pinModeStaticExpander < num_channels - 1 > ()( OUTPUT );
SREG = oldSREG;
asm volatile ("/************ pinModeStaticExpander end ************/");
/* the setup of timer1 is stolen from ShiftPWM :-P
http://www.elcojacobs.com/shiftpwm/ */
asm volatile ("/************ timer setup begin ************/");
TCCR1A = 0b00000000;
TCCR1B = 0b00001001;
OCR1A = (F_CPU - hertz * PWMlevels() / 2) / (hertz * PWMlevels());
bitSet(TIMSK1, OCIE1A);
asm volatile ("/************ timer setup end ************/");
_count = 0;
}
void set(const int channel_idx, const uint8_t value) {
_channels[channel_idx] = value;
}
size_t size() const {
return num_channels;
}
unsigned int PWMlevels() const {
return num_PWM_levels ? num_PWM_levels : 256;
}
void allOff() {
asm volatile ("/********** CSoftPWM::allOff() begin **********/");
const uint8_t oldSREG = SREG;
noInterrupts();
for (int i = 0; i < num_channels; ++i)
_channels[i] = 0;
bitWriteStaticExpander < num_channels - 1 > ()(false);
SREG = oldSREG;
asm volatile ("/********** CSoftPWM::allOff() end **********/");
}
/* This function cannot be private because the ISR uses it, and I have
no idea about how to make friends with ISR. :-( */
void update() __attribute__((always_inline)) {
asm volatile ("/********** CSoftPWM::update() begin **********/");
const uint8_t count = _count;
bitWriteStaticExpander < num_channels - 1 > ()(count, _channels);
++_count;
if (_count == PWMlevels())
_count = 0;
asm volatile ("/********** CSoftPWM::update() end **********/");
}
#if defined(HAVE_HWSERIAL0)
/* this function is stolen from ShiftPWM :-P
http://www.elcojacobs.com/shiftpwm/ */
void printInterruptLoad() {
unsigned long time1, time2;
bitSet(TIMSK1, OCIE1A); // enable interrupt
time1 = micros();
delayMicroseconds(5000);
time1 = micros() - time1;
bitClear(TIMSK1, OCIE1A); // disable interrupt
time2 = micros();
delayMicroseconds(5000);
time2 = micros() - time2;
const double load = static_cast<double>(time1 - time2) / time1;
const double interrupt_frequency = static_cast<double>(F_CPU) / (OCR1A + 1);
const double cycles_per_interrupt = load * F_CPU / interrupt_frequency;
Serial.println(F("SoftPWM::printInterruptLoad():"));
Serial.print(F(" Load of interrupt: "));
Serial.println(load, 10);
Serial.print(F(" Clock cycles per interrupt: "));
Serial.println( cycles_per_interrupt );
Serial.print(F(" Interrupt frequency: "));
Serial.print(interrupt_frequency);
Serial.println(F(" Hz"));
Serial.print(F(" PWM frequency: "));
Serial.print(interrupt_frequency / PWMlevels());
Serial.println(F(" Hz"));
Serial.print(F(" PWM levels: "));
Serial.println(PWMlevels());
bitSet(TIMSK1, OCIE1A); // enable interrupt again
}
#endif
private:
uint8_t _channels[num_channels];
uint8_t _count;
};
} // namespace Palatis
#endif // #ifndef _SOFTPWM_H_
/*
End of the SoftPWM.h included file - you can apply changes below this line again
================================================================================================================ */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ***** Uncomment to use the PinChangeInterrupts ico External Interrupts *****
// #define PIN_CHANGE_INT
//
// ******** UNLESS YOU WANT ALL CV'S RESET UPON EVERY POWER UP REMOVE THE "//" IN THE FOLLOWING LINE!!
#define _DECODER_LOADED_
//
// ******** REMOVE THE "//" IN THE NEXT LINE IF YOU WANT TO RUN A TEST DURING SETUP SEQUENCES
#define _TESTRUN_
//
// ******** REMOVE THE "//" IN THE FOLLOWING LINE TO SEND DEBUGGING INFO TO THE SERIAL OUTPUT
// #define _DEBUG36_
//
// ******** REMOVE THE "//" IN THE NEXT LINE IF YOU WANT TO USE YOUR SERIAL PORT FOR COMMANDS
#define _SHOW_IT_
//
// ******** REMOVE THE "//" IN THE NEXT LINE IF YOU WANT TO SHOW ALL THE MODIFICATIONS
// #define _MONITOR_
//
// ******** REMOVE THE "//" IN THE FOLLOWING LINE TO USE AS A '6LAMP PCB WITH AUX PORTS' ELSE IT'S A '3LAMP W/O AUX PORTS'
// #define _LAMPS36_
//
#if defined(_DEBUG36_) || defined(_MONITOR_)
#define _PP( a ) Serial.print( a );
#define _PL( a ) Serial.println( a );
#define _2P(a,b) Serial.print( a, b );
#define _2L(a,b) Serial.println(a, b);
#else
#define _PP( a )
#define _PL( a )
#define _2P(a,b)
#define _2L(a,b)
#endif
#if defined(_MONITOR_) || defined(_SHOW_IT_)
#define _MP( a ) Serial.print( a );
#define _ML( a ) Serial.println( a );
#define _3P(a,b) Serial.print( a, b);
#define _3L(a,b) Serial.println(a, b);
#else
#define _MP( a )
#define _ML( a )
#define _3P(a,b)
#define _3L(a,b)
#endif
char bomMarker = '<'; // Begin of message marker.
char eomMarker = '>'; // End of message marker.
char commandString[ 32] ; // Max length for a buffer.
char sprintfBuffer[ 32] ; // Max length for a buffer.
boolean foundBom = false; // Found begin of messages.
boolean foundEom = false; // Founf end of messages.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ***** small macro used to create all the timed events *****
#define runEvery( n ) for ( static unsigned long lasttime; millis() - lasttime > ( unsigned long )( n ); lasttime = millis() )
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SOFTPWM_DEFINE_CHANNEL( 1, DDRC, PORTC, PORTC0); //Arduino pin A0 --> LB1
SOFTPWM_DEFINE_CHANNEL( 2, DDRC, PORTC, PORTC1); //Arduino pin A1 --> LB2
SOFTPWM_DEFINE_CHANNEL( 3, DDRC, PORTC, PORTC2); //Arduino pin A2 --> LB3
SOFTPWM_DEFINE_CHANNEL( 4, DDRD, PORTD, PORTD3); //Arduino pin D3 --> LS4
SOFTPWM_DEFINE_CHANNEL( 5, DDRD, PORTD, PORTD4); //Arduino pin D4 --> LS3
SOFTPWM_DEFINE_CHANNEL( 6, DDRD, PORTD, PORTD6); //Arduino pin D6 --> LS2
SOFTPWM_DEFINE_CHANNEL( 7, DDRD, PORTD, PORTD7); //Arduino pin D7 --> LS1
SOFTPWM_DEFINE_CHANNEL( 8, DDRB, PORTB, PORTB0); //Arduino pin D8 --> LB4
SOFTPWM_DEFINE_CHANNEL( 9, DDRB, PORTB, PORTB1); //Arduino pin D9 --> LB5
SOFTPWM_DEFINE_CHANNEL(10, DDRB, PORTB, PORTB2); //Arduino pin 10 --> LB6
SOFTPWM_DEFINE_CHANNEL(11, DDRD, PORTD, PORTD5); //Arduino pin D5 --> AX1
SOFTPWM_DEFINE_CHANNEL(12, DDRC, PORTC, PORTC3); //Arduino pin A3 --> AX2
SOFTPWM_DEFINE_CHANNEL(13, DDRC, PORTC, PORTC4); //Arduino pin A4 --> AX3
SOFTPWM_DEFINE_CHANNEL(14, DDRC, PORTC, PORTC5); //Arduino pin A5 --> AX4
SOFTPWM_DEFINE_CHANNEL(15, DDRB, PORTB, PORTB5); //Arduino pin 13 --> LED
SOFTPWM_DEFINE_OBJECT_WITH_PWM_LEVELS( 15, 100); // Set 15 pulsed outputs
int tim_delay = 500;
const int numINpins = 1; // Number of INput pins to initialize.
byte inputpins[ ] = { 2 }; // These are all the used Input Pins.
const int numfpins = 17; // Number of Output pins to initialize.
byte fpins [ ] = { 11, 14, 15, 16, 3, 4, 6, 7, 8, 9, 10, 5, 17, 18, 19, 13, 12};
// pinnames: > DNU, PC0, PC1, PC2, PD3, PD4, PD6, PD7, PB0, PB1, PB2, PD5, PC3, PC4, PC5, PB5, DNU <
const int FunctionPinRx = 0; // PD0 Tx0
const int FunctionPinTx = 1; // PD1 Rx1
const int FunctionPinDcc = 2; // PD2 DCC
const int FunctionPin0 = 3; // PD3 LS4
const int FunctionPin1 = 4; // PD4 LS3
const int FunctionPin2 = 5; // PD5 AX1
const int FunctionPin3 = 6; // PD6 LS2
const int FunctionPin4 = 7; // PD7 LS1
const int FunctionPin5 = 8; // PB0 LB4
const int FunctionPin6 = 9; // PB1 LB5
const int FunctionPin7 = 10; // PB2 LB6
const int FunctionPin8 = 11; // PB3 DNU
const int FunctionPin9 = 12; // PB4 DNU
const int FunctionPinLed = 13; // PB5 LED
const int FunctionPin10 = 14; // PC0 LB1
const int FunctionPin11 = 15; // PC1 LB2
const int FunctionPin12 = 16; // PC2 LB3
const int FunctionPin13 = 17; // PC3 AX2
const int FunctionPin14 = 18; // PC4 AX3
const int FunctionPin15 = 19; // PC5 AX4
// AD0 = LB1 PD0 = Rx1 PB0 = LB4
// AD1 = LB2 PD1 = Tx0 PB1 = LB5
// AD2 = LB3 PD2 = DCC PB2 = LB6
// AD3 = AX2 PD3 = LS4 PB3 = ICSP
// AD4 = AX3 PD4 = LS3 PB4 = ICSP
// AD5 = AX4 PD5 = AX1 PB5 = ILED
// AD6 = DNU PD6 = LS2 PB6 = Xtal
// AD7 = DNU PD7 = LS1 PB7 = Xtal
/* ******************************************************************************* */
#define SET_CV_Address 24 // THIS ADDRESS IS FOR SETTING CV'S (LIKE A LOCO)
#define Accessory_Address 140 // THIS ADDRESS IS THE ADDRESS OF THIS DCC DECODER
uint8_t CV_DECODER_JUST_A_RESET = 251;
uint8_t CV_DECODER_MASTER_RESET = 252; // THIS IS THE CV ADDRESS > FACTORY DEFAULT
#define CV_To_Store_SET_CV_Address 121
#define CV_Accessory_Address CV_ACCESSORY_DECODER_ADDRESS_LSB
struct QUEUE
{
int inUse; // output in use or not
unsigned long previousMicros; // last time for fading
unsigned long waitMicros; // interval in micros()
int fadeCounter; // count of fadinglevel
int maxLevel; // maximum output level
bool upDown; // travel direction led
unsigned long previousMillis; // last time we blinked
unsigned long blinkInterval; // interval in millis()
int ledState; // state of this output
int previousState; // previous outputstate
};
QUEUE volatile *ftn_queue = new QUEUE[ 17 ];
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
CVPair FactoryDefaultCVs [ ] =
{
// These two CVs define the Long Accessory Address
{CV_ACCESSORY_DECODER_ADDRESS_LSB, Accessory_Address & 0xFF},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, (Accessory_Address >> 8) & 0x07},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, 0},
// Speed Steps don't matter for this decoder, only for loc decoders
// ONLY uncomment 1 CV_29_CONFIG line below as approprate DEFAULT IS SHORT ADDRESS
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
{CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
// {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long Address 28/128 Speed Steps
// {CV_29_CONFIG, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE | CV29_F0_LOCATION}, // Accesory Decoder Short Address
// {CV_29_CONFIG, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE | CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Accesory Decoder Long Address
{CV_DECODER_MASTER_RESET, 0},
{CV_To_Store_SET_CV_Address + 0, SET_CV_Address & 0xFF }, // LSB Set CV Address
{CV_To_Store_SET_CV_Address + 1, (SET_CV_Address >> 8) & 0x3F }, // MSB Set CV Address
{ 30, 0}, // GEN 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 31, 50}, // Maximum level outputs (100 max)
{ 32, 100}, // Waitmicros (250 * 100,000 max)
{ 33, 5}, // Waitmicros divider (standard 5)
{ 34, 100}, // Standard blink interval (* 10)
{ 35, 0}, // LB1 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 36, 50}, // Maximum level this output
{ 37, 100}, // Waitmicros output highend
{ 38, 5}, // Waitmicros output divider
{ 39, 100}, // Blinkinterval this output
{ 40, 0}, // LB2 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 41, 50}, // Maximum level this output
{ 42, 100}, // Waitmicros output highend
{ 43, 5}, // Waitmicros output divider
{ 44, 100}, // Blinkinterval this output
{ 45, 0}, // LB3 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 46, 50}, // Maximum level this output
{ 47, 100}, // Waitmicros output highend
{ 48, 5}, // Waitmicros output divider
{ 49, 100}, // Blinkinterval this output
{ 50, 0}, // LS4 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 51, 50}, // Maximum level this output
{ 52, 100}, // Waitmicros output highend
{ 53, 5}, // Waitmicros output divider
{ 54, 100}, // Blinkinterval this output
{ 55, 0}, // LS3 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 56, 50}, // Maximum level this output
{ 57, 100}, // Waitmicros output highend
{ 58, 5}, // Waitmicros output divider
{ 59, 100}, // Blinkinterval this output
{ 60, 0}, // LS2 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 61, 50}, // Maximum level this output
{ 62, 100}, // Waitmicros output highend
{ 63, 5}, // Waitmicros output divider
{ 64, 100}, // Blinkinterval this output
{ 65, 0}, // LS1 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 66, 50}, // Maximum level this output
{ 67, 100}, // Waitmicros output highend
{ 68, 5}, // Waitmicros output divider
{ 69, 100}, // Blinkinterval this output
{ 70, 0}, // LB4 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 71, 50}, // Maximum level this output
{ 72, 100}, // Waitmicros output highend
{ 73, 5}, // Waitmicros output divider
{ 74, 100}, // Blinkinterval this output
{ 75, 0}, // LB5 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 76, 50}, // Maximum level this output
{ 77, 100}, // Waitmicros output highend
{ 78, 5}, // Waitmicros output divider
{ 79, 100}, // Blinkinterval this output
{ 80, 0}, // LB6 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 81, 50}, // Maximum level this output
{ 82, 100}, // Waitmicros output highend
{ 83, 5}, // Waitmicros output divider
{ 84, 100}, // Blinkinterval this output
{ 85, 0}, // AX1 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 86, 50}, // Maximum level this output
{ 87, 100}, // Waitmicros output highend
{ 88, 5}, // Waitmicros output divider
{ 89, 100}, // Blinkinterval this output
{ 90, 0}, // AX2 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 91, 50}, // Maximum level this output
{ 92, 100}, // Waitmicros output highend
{ 93, 5}, // Waitmicros output divider
{ 94, 100}, // Blinkinterval this output
{ 95, 0}, // AX3 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{ 96, 50}, // Maximum level this output
{ 97, 100}, // Waitmicros output highend
{ 98, 5}, // Waitmicros output divider
{ 99, 100}, // Blinkinterval this output
{100, 0}, // AX4 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{101, 50}, // Maximum level this output
{102, 100}, // Waitmicros output highend
{103, 5}, // Waitmicros output divider
{104, 100}, // Blinkinterval this output
{105, 1}, // LED 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{106, 50}, // Maximum level this output
{107, 100}, // Waitmicros output highend
{108, 5}, // Waitmicros output divider
{109, 100}, // Blinkinterval this output
{110, 0}, // DNU 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{111, 0}, // Maximum level this output
{112, 0}, // Waitmicros output highend
{113, 0}, // Waitmicros output divider
{114, 0}, // Blinkinterval this output
{251, 0}, // CV_DECODER_JUST_A_RESET */
/* {252, 0}, // CV_DECODER_MASTER_RESET */
{253, 0}, // Extra input - not used yet
};
/* **********************************************************************************
* notifyCVResetFactoryDefault() Called when CVs must be reset.
* This is called when CVs must be reset
* to their factory defaults. This callback
* should write the factory default value of
* relevent CVs using the setCV() method.
* setCV() must not block whens this is called.
* Test with isSetCVReady() prior to calling setCV()
*
* Inputs:
* None
*
* Returns:
* None
*/
uint8_t FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset - sizeof()
// To flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
};
// These are the absolute maximums allowed !!!
volatile uint8_t M_maxLevel = 100; // Dcc.getCV( 31 );
volatile uint8_t M_highend = 250; // Dcc.getCV( 32 );
volatile uint8_t M_divider = 5; // Dcc.getCV( 33 );
volatile uint8_t M_interval = 100; // Dcc.getCV( 34 );
// This is the switch between F0 or F1 - F14
volatile uint8_t C_between = Dcc.getCV(111 );
/* ******************************************************************************* */
void setup()
{
noInterrupts();
// initialize the digital pins as outputs
for (int i = 1; i < numfpins - 1; ++i)
{
digitalWrite( fpins[ i ], 0 ); // Switch the pin off first.
pinMode( fpins[ i ], OUTPUT ); // Then set it as an output.
}
// initialize the digital pins as inputs
for ( int i = 0; i < numINpins; ++i )
{
digitalWrite( inputpins[ i ], 0 ); // Switch the pin off first.
pinMode( inputpins[ i ], INPUT_PULLUP ); // Then set as input_pullup.
}
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
Serial.begin(115200);
while (!Serial)
{
; // wait for Serial port to connect. Needed for native USB port.
}
Serial.flush(); // Wait for all the rubbish to finish displaying.
while (Serial.available() > 0)
{
Serial.read(); // Clear the input buffer to get 'real' inputdata.
}
Serial.println( "-------------------------------------" );
#endif
// Call the DCC 'pin' and 'init' functions to enable the DCC Receivermode
Dcc.pin( digitalPinToInterrupt( FunctionPinDcc ), FunctionPinDcc, false );
Dcc.init( MAN_ID_DIY, 201, FLAGS_MY_ADDRESS_ONLY, 0 ); delay( 1000 );
#if defined(_DECODER_LOADED_)
if ( Dcc.getCV( CV_DECODER_MASTER_RESET ) == CV_DECODER_MASTER_RESET )
{
#endif
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
Serial.println( "wait for the copy process to finish.." );
Serial.println( "-------------------------------------" );
#endif
FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
for ( int i = 0; i < FactoryDefaultCVIndex; ++i )
{
Dcc.setCV( FactoryDefaultCVs[ i ].CV, FactoryDefaultCVs[ i ].Value );
}
#if defined(_DECODER_LOADED_)
}
#endif
Dcc.setCV( CV_DECODER_JUST_A_RESET, 0 ); // Reset the just_a_reset CV
Palatis::SoftPWM.begin( 50); // Start SoftPWM with 50hz pwm frequency
#if defined(_TESTRUN_)
for (unsigned int i = 0; i < Palatis::SoftPWM.size(); ++i)
{
Palatis::SoftPWM.set(i, M_maxLevel);
}
Palatis::SoftPWM.update(); // Switch all outputs ON
delay( tim_delay );
Palatis::SoftPWM.allOff(); // Set the PWM value of all channels to 0.
#endif
// These are the absolute maximums set for this system in the CVs
M_maxLevel = Dcc.getCV( 31 ); // Maximum level outputs (100 max)
M_highend = Dcc.getCV( 32 ); // Waitmicros (250 * 100,000 max)
M_divider = Dcc.getCV( 33 ); // Waitmicros divider (standard 5)
M_interval = Dcc.getCV( 34 ); // Standard blink interval (* 10)
// This is the switch between F0 or F1 - F14
C_between = Dcc.getCV( 111 );
// Loop through all the settings for checking and correcting
for (int i = 1; i < numfpins - 1; i++)
{
calculateFtnQueue( i );
}
#if defined(_SHOW_IT_)
#if defined( HAVE_HWSERIAL0 )
// SoftPWM print interrupt load for diagnostic purposes
Palatis::SoftPWM.printInterruptLoad();
Serial.println( "-------------------------------------" );
#endif
displayText(); // Shows the standard text.
#endif
// Switch on the LED to signal we're ready.
digitalWrite(FunctionPinLed, 0);
delay (tim_delay);
digitalWrite(FunctionPinLed, 1);
delay (tim_delay);
digitalWrite(FunctionPinLed, 0);
delay (tim_delay);
digitalWrite(FunctionPinLed, 1);
interrupts(); // Ready to rumble....
}
/* ******************************************************************************* */
void loop()
{
unsigned long currentMillis = millis();
unsigned long currentMicros = micros();
Dcc.process(); // Loop through the DCC process.
// Check the number of outputs, not the number of numfpins!!!
for (unsigned int i = 1; i < Palatis::SoftPWM.size(); ++i)
{
switch ( ftn_queue[ i ].inUse )
{
case 1: // 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{
Palatis::SoftPWM.set( i, ftn_queue[ i ].maxLevel );
break;
}
case 3: // 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{
if (currentMillis - ftn_queue[ i ].previousMillis >= ftn_queue[ i ].blinkInterval)
{
// Save the last time you blinked the LED
ftn_queue[ i ].previousMillis = currentMillis;
// If the LED is off turn it on and vice-versa
if (ftn_queue[ i ].ledState == LOW)
{
Palatis::SoftPWM.set( i, ftn_queue[ i ].maxLevel);
ftn_queue[ i ].ledState = HIGH;
}
else
{
ftn_queue[ i ].ledState = LOW;
Palatis::SoftPWM.set( i, 0);
}
}
break;
}
case 5: // 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{
if (currentMicros - ftn_queue[ i ].previousMicros > ftn_queue[ i ].waitMicros)
{
ftn_queue[ i ].previousMicros = currentMicros;
if ((ftn_queue[ i ].fadeCounter > ftn_queue[ i ].maxLevel - 1) || (ftn_queue[ i ].fadeCounter < 1))
{
ftn_queue[ i ].upDown = !ftn_queue[ i ].upDown;
}
if (ftn_queue[ i ].upDown == true) { ftn_queue[ i ].fadeCounter++; } else { ftn_queue[ i ].fadeCounter--; }
Palatis::SoftPWM.set( i, ftn_queue[ i ].fadeCounter);
}
break;
}
default: // 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On
{
Palatis::SoftPWM.set( i, 0);
break;
}
}
}
#if defined(_SHOW_IT_)
if (foundEom)
{
foundBom = false;
foundEom = false;
parseCom( commandString );
}
#endif
}
/* ******************************************************************************* */
void softwareReset( uint8_t preScaler )
{
wdt_enable( preScaler );
while( 1 ) {} // Wait for the prescaler time to expire and do an auto-reset
}
/* ******************************************************************************* */
void setAllOutputCVs( uint8_t cvvalue )
{
noInterrupts(); // Disable interrupts.
for (int i = 1; i < numfpins - 2; i++)
{
Dcc.setCV( 30 + ( i * 5 ), cvvalue );
ftn_queue[ i ].inUse = cvvalue;
}
interrupts(); // Enable the interrupts.
#if defined(_DEBUG36_) || defined(_MONITOR_)
_MP( "\t" "<" );
_3P( cvvalue, DEC );
_ML( ">" );
#endif
}
/* ******************************************************************************* */
void setFtnQueue( uint16_t cvNumber, uint8_t cvValue )
{
#if defined(_DEBUG36_) || defined(_MONITOR_)
_PP( "setFTNQueue: ");
_2P( cvNumber, DEC);
_PP( " Value: " );
_2L( cvValue, DEC);
#endif
switch ( cvNumber )
{
case 30:
{
char recData[ 4 ] = "<©>";
recData[ 1 ] = 48 + cvValue; // Make it an ASCII code
parseCom( recData ); // Recursive action on CV value
break;
}
case 31 ... 34:
{
// These are the absolute maximums allowed
M_maxLevel = Dcc.getCV( 31 ); // Maximum level outputs (100 max)
M_highend = Dcc.getCV( 32 ); // Waitmicros (250 * 100,000 max)
M_divider = Dcc.getCV( 33 ); // Waitmicros divider (standard 5)
M_interval = Dcc.getCV( 34 ); // Standard blink interval (* 10)
// Loop through all the settings for checking, correcting the values
for (int i = 1; i < numfpins - 1; ++i)
{
calculateFtnQueue( i );
}
break;
}
case 35 ... 104:
{
calculateFtnQueue( ( cvNumber - 30 ) / 5 ); // Group of 5 values
break;
}
case 105 ... 109:
{
break;
}
case 110 ... 114:
{
// This is the switch between F0 or F1 - F14
C_between = Dcc.getCV( 111 );
break;
}
default:
{
break;
}
}
}
/* ******************************************************************************* */
void calculateFtnQueue( int number )
{
// Now we're going to do some calculations
ftn_queue[ number ].inUse = Dcc.getCV( 30 + ( number * 5 ) );
#if defined(_DEBUG36_) || defined(_MONITOR_)
_PP( "\t" "calculateFtnQueue cv: ");
_2P( 30 + ( number * 5 ), DEC);
_PP( "\t" " value: ");
_2L( ftn_queue[ number ].inUse, DEC);
#endif
// Default settings for counters and states
ftn_queue[ number ].fadeCounter = 0;
ftn_queue[ number ].ledState = LOW;
ftn_queue[ number ].previousMicros = 0;
ftn_queue[ number ].previousMillis = 0;
ftn_queue[ number ].upDown = false;
ftn_queue[ number ].waitMicros = 0;
// These values stored in the Configuration Variables
uint8_t S_maxLevel = Dcc.getCV( 31 + ( number * 5 ));
uint8_t S_highend = Dcc.getCV( 32 + ( number * 5 ));
uint8_t S_divider = Dcc.getCV( 33 + ( number * 5 ));
uint8_t S_interval = Dcc.getCV( 34 + ( number * 5 ));
if (M_maxLevel > S_maxLevel)
{
ftn_queue[ number ].maxLevel = S_maxLevel;
}
else
{
ftn_queue[ number ].maxLevel = M_maxLevel;
}
if (M_interval > S_interval)
{
ftn_queue[ number ].blinkInterval = S_interval * 10;
}
else
{
ftn_queue[ number ].blinkInterval = M_interval * 10;
}
if (M_highend > S_highend)
{
ftn_queue[ number ].waitMicros = ( (S_highend * 100000) / Palatis::SoftPWM.PWMlevels() );
}
else
{
ftn_queue[ number ].waitMicros = ( (M_highend * 100000) / Palatis::SoftPWM.PWMlevels() );
}
if (M_divider > S_divider)
{
ftn_queue[ number ].waitMicros = ( ftn_queue[ number ].waitMicros / S_divider );
}
else
{
ftn_queue[ number ].waitMicros = ( ftn_queue[ number ].waitMicros / M_divider );
}
}
/* ******************************************************************************* */
// The next part will / can only be used for an mpu with enough memory and SerialPort
#if defined(_MONITOR_) || defined(_SHOW_IT_)
/* **********************************************************************************
// 0 = Off, 1 = On, 2 = Blink Off, 3 = Blink On, 4 = Fade Off, 5 = Fade On ***
/* **********************************************************************************
<0> all outputs: Off
<1> all outputs: On
<2> all outputs: Blink Off
<3> all outputs: Blink On
<4> all outputs: Fade Off
<5> all outputs: Fade On
<C> clear everything: Factory Default
<D> dumps everything: to Serial.Print
<f> controls mobile engine decoder functions F0-F12: <f x y>
<F> lists all funtions and settings for all outputs: <F>
<R> reads a configuration variable: <R x>
<W> write a configuration variable: <W x y>
*/
void displayText()
{
Serial.println(""); // An extra empty line for better understanding
Serial.println(F("Put in one of the following commands: " ));
Serial.println(F("------------------------------------------------"));
Serial.println(F("<0> all outputs: Power Off" ));
Serial.println(F("<1> all outputs: Power On" ));
Serial.println(F("<2> all outputs: Blink Off" ));
Serial.println(F("<3> all outputs: Blink On" ));
Serial.println(F("<4> all outputs: Fader Off" ));
Serial.println(F("<5> all outputs: Fader On" ));
Serial.println("");
Serial.println(F("<C> clear everything: Factory Default" ));
Serial.println(F("<D> prints CV values: to your monitor" ));
Serial.println("");
Serial.println(F("<f> control decoder functions F0-F14: <f x y> "));
Serial.println(F("<F> dump the functionqueue and settings: <F> "));
Serial.println("");
Serial.println(F("<M> list the available SRAM on the chip" ));
Serial.println("");
Serial.println(F("<R> reads a configuration variable: <R x>" ));
Serial.println(F("<W> write a configuration variable: <W x y>" ));
Serial.println(F("----------------------------------------------- "));
Serial.println(F("* include '<', '>' and spaces in your command * "));
Serial.println(""); // An extra empty line for better understanding
}
/* ******************************************************************************* */
/*
.* SerialEvent occurs whenever a new data comes in the hardware serial RX. This
.* routine is run between each time loop() runs, so using delay inside loop can
.* delay response. Multiple bytes may be available and be put in commandString
.*/
void serialEvent() {
while (Serial.available())
{
char inChar = (char)Serial.read(); // get the new byte
if (inChar == bomMarker) // start of new command
{
sprintf(commandString, " ");
foundBom = true;
}
else if (inChar == eomMarker) // end of new command
{
// sprintf(commandString, " ");
foundEom = true;
// parse(commandString) in the loop
}
else if (strlen(commandString) < 16) // put it all in
{
sprintf(commandString, "%s%c", commandString, inChar);
// if comandString still has space, append character just read from serial line
// otherwise, character is ignored, (but we'll continue to look for '<' or '>')
}
}
}
#endif // MONITOR
/* ******************************************************************************* */
void parseCom( char *com )
{
switch ( com[1] ) // com[0] = '<' or ' '
{
/***** SWITCH ALL THE OUTPUTS AT ONCE ****/
case '0': // <0> all outputs: Off
/*
* returns: <0>
*/
{
setAllOutputCVs( 0 );
break;
}
case '1': // <1> all outputs: On
/*
* returns: <1>
*/
{
setAllOutputCVs( 1 );
break;
}
case '2': // <2> all outputs: Blink Off
/*
* returns: <2>
*/
{
setAllOutputCVs( 2 );
break;
}
case '3': // <3> all outputs: Blink On
/*
* returns: <3>
*/
{
setAllOutputCVs( 3 );
break;
}
case '4': // <4> all outputs: Fade Off
/*
* returns: <4>
*/
{
setAllOutputCVs( 4 );
break;
}
case '5': // <5> all outputs: Fade On
/*
* returns: <5>
*/
{
setAllOutputCVs( 5 );
break;
}
/**** CLEAR SETTINGS TO FACTORY DEFAULTS ****/
case 'C': // <C>
/*
* clears settings to Factory Defaults
*
* returns: <FD done check>
*/
{
uint8_t cvCheck = Dcc.setCV( CV_DECODER_MASTER_RESET, CV_DECODER_MASTER_RESET );
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
_MP( "\t" "<FD done>" "\t" );
_3L( cvCheck, DEC );
#endif
softwareReset( WDTO_15MS );
break;
}
/***** DUMPS CONFIGURATION VARIABLES FROM DECODER ****/
case 'D': // <D>
/*
* dumps all Configuration Variables from the decoder
*
* returns a list of: <CV VALUE)
* where VALUE is a number from 0-255 as read from the CV, or -1 if read could not be verified
*/
{
FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
for (int i = 0; i < FactoryDefaultCVIndex; i++)
{
uint8_t cvValue = Dcc.getCV( FactoryDefaultCVs[ i ].CV );
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
_MP( " cv: " );
_3P( FactoryDefaultCVs[i].CV, DEC );
_MP( "\t" " value: " );
_3L( cvValue, DEC );
#endif
}
break;
}
/***** DUMPS ALL FTNQUEUE SETTINGS FOR ALL OUTPUTS ****/
case 'F': // <F>
/*
* dumps all FTNqueue settings from memory
*
* returns a list of: <settings>
*/
{
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
_ML("\n\npin iU prevMic waitMic fC mL uD prevMil blnkInt lS pS \n");
for (int i = 1; i < numfpins - 2; i++)
{
// sprintf( sprintfBuffer, " %2i %3i %11lu" , i, ftn_queue[ i ].inUse, ftn_queue[ i ].previousMicros );
_MP( sprintfBuffer );
// sprintf( sprintfBuffer, " %11lu %3i %3i" , ftn_queue[ i ].waitMicros, ftn_queue[ i ].fadeCounter, ftn_queue[ i ].maxLevel );
_MP( sprintfBuffer );
// sprintf( sprintfBuffer, " %3i %11lu %11lu", ftn_queue[ i ].upDown, ftn_queue[ i ].previousMillis, ftn_queue[ i ].blinkInterval );
_MP( sprintfBuffer );
// sprintf( sprintfBuffer, " %3i %3i " , ftn_queue[ i ].ledState, ftn_queue[ i ].previousState );
_ML( sprintfBuffer );
}
_ML( "-------------------------------------" );
#endif
break;
}
/***** DUMPS CONFIGURATION VARIABLES FROM DECODER ****/
case 'f': // <f>
/*
* dumps all Configuration Variables from the decoder
*
* returns a list of: <CV VALUE)
* where VALUE is a number from 0-255 as read from the CV, or -1 if read could not be verified
*/
{
FactoryDefaultCVIndex = sizeof( FactoryDefaultCVs ) / sizeof( CVPair );
for (int i = 0; i < FactoryDefaultCVIndex; i++)
{
uint8_t cvValue = Dcc.getCV( FactoryDefaultCVs[ i ].CV );
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
_MP( " cv: " );
_3P( FactoryDefaultCVs[i].CV, DEC );
_MP( "\t" " value: " );
_3L( cvValue, DEC );
#endif
}
break;
}
/***** READ CONFIGURATION VARIABLE BYTE FROM DECODER ****/
case 'R': // <R CV>
/*
* reads a Configuration Variable from the decoder
*
* CV: the number of the Configuration Variable memory location in the decoder to read from (1-1024)
*
* returns: <r CV VALUE>
* where VALUE is a number from 0-255 as read from the requested CV, or -1 if read could not be verified
*/
{
int cv;
if( sscanf( com + 2, "%d", &cv ) != 1 ) { return; }
uint8_t cvCheck = Dcc.getCV( cv );
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
_MP( "<r " );
_3P( cv + 0 , DEC );
_MP( " " );
_3P( cvCheck, DEC );
_ML( ">" );
#endif
break;
}
/***** WRITE CONFIGURATION VARIABLE BYTE TO DECODER ****/
case 'W': // <W CV VALUE>
/*
* writes, and then verifies, a Configuration Variable to the decoder
*
* CV: the number of the Configuration Variable memory location in the decoder to write to (1-1024)
* VALUE: the value to be written to the Configuration Variable memory location (0-255)
*
* returns: <w CV Value)
* where VALUE is a number from 0-255 as read from the requested CV, or -1 if verificaiton read fails
*/
{
int bValue, cv;
if( sscanf( com + 2, "%d %d", &cv, &bValue ) != 2 ) { return; }
uint8_t cvCheck = Dcc.setCV( cv, bValue );
#if defined(_DEBUG36_) || defined(_MONITOR_) || defined(_SHOW_IT_)
_MP( "<w " );
_3P( cv + 0 , DEC );
_MP( " " );
_3P( cvCheck, DEC );
_ML( ">" );
#endif
break;
}
/**** PRINT CARRIAGE RETURN IN SERIAL_SHOW_IT_WINDOW ****/
case ' ': // < >
/*
* simply prints a carriage return - useful when interacting with Ardiuno through serial monitor window
*
* returns: a carriage return and the menu text
*/
{
_ML("");
#if defined(_MONITOR_) || defined(_SHOW_IT_)
displayText(); // Shows the standard explanation text
#endif
break;
}
/**** DEFAULT FOR THE SWITCH FUNCTION = NO ACTION ****/
default:
break;
}
}
/************************************************************************************
Call-back functions from DCC
************************************************************************************/
/* **********************************************************************************
* notifyDccFunc() Callback for a multifunction decoder function command.
*
* Inputs:
* Addr - Active decoder address.
* AddrType - DCC_ADDR_SHORT or DCC_ADDR_LONG.
* FuncGrp - Function group. FN_0 - 14 speed headlight Mask FN_BIT_00
*
* FN_0_4 - Functions 0 to 4. Mask FN_BIT_00 - FN_BIT_04
* FN_5_8 - Functions 5 to 8. Mask FN_BIT_05 - FN_BIT_08
* FN_9_12 - Functions 9 to 12. Mask FN_BIT_09 - FN_BIT_12
* FN_13_20 - Functions 13 to 20. Mask FN_BIT_13 - FN_BIT_20
* FN_21_28 - Functions 21 to 28. Mask FN_BIT_21 - FN_BIT_28
* FuncState - Function state. Bitmask where active functions have a 1 at that bit.
* You must & FuncState with the appropriate
* FN_BIT_nn value to isolate a given bit.
*
* Returns:
* None
*/
void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState )
{
static bool B_between = ( FuncState & FN_BIT_00 ? true : false ); // Keeps track of F0 function.
if ( C_between == 0 ) // Use the F0 function to switch lights.
{
if ( B_between != ( FuncState & FN_BIT_00 ? true : false ) && ( FuncGrp == FN_0_4 ) )
{
B_between = ( FuncState & FN_BIT_00 ? true : false );
Dcc.setCV( 30, FuncState & FN_BIT_00 ? 1 : 0 );
}
}
else
{
switch( FuncGrp )
{
case FN_0_4: //Function Group 1 F0 F4 F3 F2 F1
{
Dcc.setCV( 35, FuncState & FN_BIT_01 ? 1 : 0 );
Dcc.setCV( 40, FuncState & FN_BIT_02 ? 1 : 0 );
Dcc.setCV( 45, FuncState & FN_BIT_03 ? 1 : 0 );
Dcc.setCV( 50, FuncState & FN_BIT_04 ? 1 : 0 );
break;
}
case FN_5_8: //Function Group 1 F8 F7 F6 F5
{
Dcc.setCV( 55, FuncState & FN_BIT_05 ? 1 : 0 );
Dcc.setCV( 60, FuncState & FN_BIT_06 ? 1 : 0 );
Dcc.setCV( 65, FuncState & FN_BIT_07 ? 1 : 0 );
Dcc.setCV( 70, FuncState & FN_BIT_08 ? 1 : 0 );
break;
}
case FN_9_12: //Function Group 1 F12 F11 F10 F9
{
Dcc.setCV( 75, FuncState & FN_BIT_09 ? 1 : 0 );
Dcc.setCV( 80, FuncState & FN_BIT_10 ? 1 : 0 );
Dcc.setCV( 85, FuncState & FN_BIT_11 ? 1 : 0 );
Dcc.setCV( 90, FuncState & FN_BIT_12 ? 1 : 0 );
break;
}
case FN_13_20: //Function Group 2 == F20 - F13
{
Dcc.setCV( 95, FuncState & FN_BIT_13 ? 1 : 0 );
Dcc.setCV(100, FuncState & FN_BIT_14 ? 1 : 0 );
break;
}
case FN_21_28:
default:
{
break;
}
}
}
}
/* **********************************************************************************
* notifyCVChange() Called when a CV value is changed.
* This is called whenever a CV's value is changed.
* notifyDccCVChange() Called only when a CV value is changed by a Dcc packet or a lib function.
* it is NOT called if the CV is changed by means of the setCV() method.
* Note: It is not called if notifyCVWrite() is defined
* or if the value in the EEPROM is the same as the value
* in the write command.
*
* Inputs:
* CV - CV number.
* Value - Value of the CV.
*
* Returns:
* None
*/
void notifyCVChange( uint16_t CV, uint8_t Value )
{
#if defined(_DEBUG36_) || defined(_MONITOR_)
_PP( " notifyCVChange: CV: ");
_2P( CV, DEC );
_PP( " Value: " );
_2L( Value, DEC );
#endif
setFtnQueue( CV, Value );
}
//