Added new call-back functions: notifyDccAccTurnoutBoard and notifyDccAccTurnoutOutput

Added new example NmraDccAccessoryDecoder_Pulsed_8
changed version to 1.2.0
This commit is contained in:
Alex Shepherd
2016-03-19 23:24:55 +13:00
parent 70b44eb5d4
commit 4e4007ebf7
8 changed files with 391 additions and 40 deletions

View File

@@ -686,8 +686,17 @@ void execDccProcessor( DCC_MSG * pDccMsg )
if(pDccMsg->Data[1] & 0b10000000)
{
uint8_t direction = OutputAddress & 0x01;
uint8_t outputPower = (pDccMsg->Data[1] & 0b00001000) >> 3;
if( notifyDccAccState )
notifyDccAccState( Address, BoardAddress, OutputAddress, pDccMsg->Data[1] & 0b00001000 ) ;
notifyDccAccState( Address, BoardAddress, OutputAddress, outputPower ) ;
if( notifyDccAccTurnoutBoard )
notifyDccAccTurnoutBoard( BoardAddress, OutputIndex, direction, outputPower );
if( notifyDccAccTurnoutOutput )
notifyDccAccTurnoutOutput( Address, direction, outputPower );
}
else

View File

@@ -213,6 +213,8 @@ extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Ra
extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak));
extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak));
extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak));

View File

@@ -69,6 +69,30 @@ void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, u
Serial.println(State, HEX) ;
}
// This function is called whenever a normal DCC Turnout Packet is received
void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower )
{
Serial.print("notifyDccAccTurnoutBoard: ") ;
Serial.print(BoardAddr,DEC) ;
Serial.print(',');
Serial.print(OutputPair,DEC) ;
Serial.print(',');
Serial.print(Direction,DEC) ;
Serial.print(',');
Serial.println(OutputPower, HEX) ;
}
// This function is called whenever a normal DCC Turnout Packet is received
void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower )
{
Serial.print("notifyDccAccTurnoutOutput: ") ;
Serial.print(Addr,DEC) ;
Serial.print(',');
Serial.print(Direction,DEC) ;
Serial.print(',');
Serial.println(OutputPower, HEX) ;
}
// This function is called whenever a DCC Signal Aspect Packet is received
void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State)
{

View File

@@ -0,0 +1,194 @@
#include <NmraDcc.h>
#include "PinPulser.h"
// This Example shows how to use the library as a DCC Accessory Decoder to drive 8 Pulsed Turnouts
// You can print every DCC packet by un-commenting the line below
//#define NOTIFY_DCC_MSG
// You can print every notifyDccAccTurnoutOutput call-back by un-commenting the line below
#define NOTIFY_TURNOUT_MSG
// You can also print other Debug Messages uncommenting the line below
#define DEBUG_MSG
NmraDcc Dcc ;
DCC_MSG Packet ;
struct CVPair
{
uint16_t CV;
uint8_t Value;
};
#define CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME 2 // CV for the Output Pulse ON ms
#define CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME 3 // CV for the delay in ms to allow a CDU to recharge
#define CV_ACCESSORY_DECODER_ACTIVE_STATE 4 // CV to define the ON Output State
#define NUM_TURNOUTS 8 // Number of Turnouts
CVPair FactoryDefaultCVs [] =
{
{CV_ACCESSORY_DECODER_ADDRESS_LSB, 1},
{CV_ACCESSORY_DECODER_ADDRESS_MSB, 0},
{CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME, 50}, // x 10mS for the output pulse duration
{CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME, 30}, // x 10mS for the CDU recharge delay time
{CV_ACCESSORY_DECODER_ACTIVE_STATE, HIGH},
};
uint8_t FactoryDefaultCVIndex = 0;
// Un-Comment the line below to force CVs to be written to the Factory Default values defined above
//FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs);
// This is the Arduino Pin Mapping to Turnout Addresses with 2 pins per turnout
// base address 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
byte outputs[] = { 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
// pins D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 A0 A1 A2 A4 A5
PinPulser pinPulser;
// To enable DCC CV Read capability with a DCC Service Mode CV Programmer un-comment the line below
//#define ENABLE_DCC_ACK
#ifdef ENABLE_DCC_ACK
const int DccAckPin = 3 ;
#endif
uint16_t BaseTurnoutAddress; //
// This function is called whenever a normal DCC Turnout Packet is received
void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower )
{
#ifdef NOTIFY_TURNOUT_MSG
Serial.print("notifyDccAccTurnoutOutput: Turnout: ") ;
Serial.print(Addr,DEC) ;
Serial.print(" Direction: ");
Serial.print(Direction ? "Closed" : "Thrown") ;
Serial.print(" Output: ");
Serial.print(OutputPower ? "On" : "Off") ;
#endif
if(( Addr >= BaseTurnoutAddress ) && ( Addr < (BaseTurnoutAddress + NUM_TURNOUTS )) && OutputPower )
{
uint16_t pinIndex = ( (Addr - BaseTurnoutAddress) << 1 ) + Direction ;
pinPulser.addPin(outputs[pinIndex]);
#ifdef NOTIFY_TURNOUT_MSG
Serial.print(" Pin Index: ");
Serial.print(pinIndex,DEC);
Serial.print(" Pin: ");
Serial.print(outputs[pinIndex],DEC);
#endif
}
#ifdef NOTIFY_TURNOUT_MSG
Serial.println();
#endif
}
void setup()
{
Serial.begin(115200);
// Configure the DCC CV Programing ACK pin for an output
#ifdef ENABLE_DCC_ACK
pinMode( DccAckPin, OUTPUT );
#endif
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
Dcc.pin(0, 2, 1);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
BaseTurnoutAddress = (Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_MSB) * 4) + Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_LSB) ;
#ifdef DEBUG_MSG
Serial.println("NMRA DCC 8-Turnout Accessory Decoder");
Serial.print("DCC Turnout Base Address: ");
Serial.println(BaseTurnoutAddress, DEC);
#endif
for(uint8_t i = 0; i < (NUM_TURNOUTS * 2); i++)
pinMode( outputs[i], OUTPUT );
uint16_t onMs = Dcc.getCV(CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME) * 10;
uint16_t cduRechargeMs = Dcc.getCV(CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME) * 10;
uint8_t activeOutputState = Dcc.getCV(CV_ACCESSORY_DECODER_ACTIVE_STATE);
pinPulser.init(onMs, cduRechargeMs, activeOutputState);
#ifdef DEBUG_MSG
Serial.println("Init Done");
#endif
}
void loop()
{
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
Dcc.process();
pinPulser.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);
}
}
void notifyCVChange(uint16_t CV, uint8_t Value)
{
#ifdef DEBUG_MSG
Serial.print("notifyCVChange: CV: ") ;
Serial.print(CV,DEC) ;
Serial.print(" Value: ") ;
Serial.println(Value, DEC) ;
#endif
Value = Value; // Silence Compiler Warnings...
if((CV == CV_ACCESSORY_DECODER_ADDRESS_MSB) || (CV == CV_ACCESSORY_DECODER_ADDRESS_LSB))
{
BaseTurnoutAddress = (Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_MSB) * 4) + Dcc.getCV(CV_ACCESSORY_DECODER_ADDRESS_LSB) ;
return;
}
if((CV == CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME) || (CV == CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME) || (CV == CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME))
{
uint16_t onMs = Dcc.getCV(CV_ACCESSORY_DECODER_OUTPUT_PULSE_TIME) * 10;
uint16_t cduRechargeMs = Dcc.getCV(CV_ACCESSORY_DECODER_CDU_RECHARGE_TIME) * 10;
uint8_t activeOutputState = Dcc.getCV(CV_ACCESSORY_DECODER_ACTIVE_STATE);
pinPulser.init(onMs, cduRechargeMs, activeOutputState);
}
}
void notifyCVResetFactoryDefault()
{
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
// to flag to the loop() function that a reset to Factory Defaults needs to be done
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
};
// This function is called by the NmraDcc library when a DCC ACK needs to be sent
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
#ifdef ENABLE_DCC_ACK
void notifyCVAck(void)
{
#ifdef DEBUG_MSG
Serial.println("notifyCVAck") ;
#endif
digitalWrite( DccAckPin, HIGH );
delay( 6 );
digitalWrite( DccAckPin, LOW );
}
#endif
#ifdef NOTIFY_DCC_MSG
void notifyDccMsg( DCC_MSG * Msg)
{
Serial.print("notifyDccMsg: ") ;
for(uint8_t i = 0; i < Msg->Size; i++)
{
Serial.print(Msg->Data[i], HEX);
Serial.write(' ');
}
Serial.println();
}
#endif

View File

@@ -0,0 +1,92 @@
#include "PinPulser.h"
#define PIN_PULSER_SLOT_EMPTY 255
void PinPulser::init(uint16_t onMs, uint16_t cduRechargeMs, uint8_t activeOutputState)
{
this->onMs = onMs;
this->cduRechargeMs = cduRechargeMs;
this->activeOutputState = activeOutputState;
state = PP_IDLE;
targetMs = 0;
memset(pinQueue, PIN_PULSER_SLOT_EMPTY, PIN_PULSER_MAX_PINS + 1);
}
uint8_t PinPulser::addPin(uint8_t Pin)
{
// Serial.print(" PinPulser::addPin: "); Serial.print(Pin,DEC);
for(uint8_t i = 0; i < PIN_PULSER_MAX_PINS; i++)
{
if(pinQueue[i] == Pin)
{
// Serial.print(" Already in Index: "); Serial.println(i,DEC);
return i;
}
else if(pinQueue[i] == PIN_PULSER_SLOT_EMPTY)
{
// Serial.print(" pinQueue Index: "); Serial.println(i,DEC);
pinQueue[i] = Pin;
process();
return i;
}
}
// Serial.println();
return PIN_PULSER_SLOT_EMPTY;
}
PP_State PinPulser::process(void)
{
unsigned long now;
switch(state)
{
case PP_IDLE:
if(pinQueue[0] != PIN_PULSER_SLOT_EMPTY)
{
// Serial.print(" PinPulser::process: PP_IDLE: Pin: "); Serial.println(pinQueue[0],DEC);
digitalWrite(pinQueue[0], activeOutputState);
targetMs = millis() + onMs;
state = PP_OUTPUT_ON_DELAY;
}
break;
case PP_OUTPUT_ON_DELAY:
now = millis();
if(now >= targetMs)
{
// Serial.print(" PinPulser::process: PP_OUTPUT_ON_DELAY: Done Deactivate Pin: "); Serial.println(pinQueue[0],DEC);
digitalWrite(pinQueue[0], !activeOutputState);
targetMs = now + cduRechargeMs;
memmove(pinQueue, pinQueue + 1, PIN_PULSER_MAX_PINS);
state = PP_CDU_RECHARGE_DELAY;
}
break;
case PP_CDU_RECHARGE_DELAY:
now = millis();
if(now >= targetMs)
{
if(pinQueue[0] != PIN_PULSER_SLOT_EMPTY)
{
// Serial.print(" PinPulser::process: PIN_PULSER_SLOT_EMPTY: Done Deactivate Pin: "); Serial.println(pinQueue[0],DEC);
digitalWrite(pinQueue[0], activeOutputState);
targetMs = now + onMs;
state = PP_OUTPUT_ON_DELAY;
}
else
{
// Serial.println(" PinPulser::process: PP_CDU_RECHARGE_DELAY - Now PP_IDLE");
state = PP_IDLE;
}
}
break;
}
return state;
}

View File

@@ -0,0 +1,27 @@
#include <Arduino.h>
#define PIN_PULSER_MAX_PINS 16
enum PP_State
{
PP_IDLE = 0,
PP_OUTPUT_ON_DELAY,
PP_CDU_RECHARGE_DELAY,
};
class PinPulser
{
private:
uint16_t onMs;
uint16_t cduRechargeMs;
PP_State state = PP_IDLE;
unsigned long targetMs = 0;
uint8_t activeOutputState = HIGH;
uint8_t pinQueue[PIN_PULSER_MAX_PINS + 1];
public:
void init(uint16_t onMs, uint16_t cduRechargeMs, uint8_t activeOutputState);
uint8_t addPin(uint8_t pin);
PP_State process(void);
};

View File

@@ -6,54 +6,57 @@
# Datatypes (KEYWORD1)
#######################################
DCC_MSG KEYWORD1
NmraDcc 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
NmraDcc KEYWORD2
pin KEYWORD2
init KEYWORD2
process KEYWORD2
getCV KEYWORD2
setCV KEYWORD2
isSetCVReady KEYWORD2
notifyDccReset KEYWORD2
notifyDccIdle KEYWORD2
notifyDccSpeed KEYWORD2
notifyDccSpeedRaw
notifyDccFunc KEYWORD2
notifyDccAccState KEYWORD2
notifyDccAccTurnoutBoard
notifyDccAccTurnoutOutput
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
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_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_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
CV_VERSION_ID LITERAL1
CV_MANUFACTURER_ID LITERAL1
CV_29_CONFIG LITERAL1
CV_OPS_MODE_ADDRESS_LSB LITERAL1
#######################################

View File

@@ -1,5 +1,5 @@
name=NmraDcc
version=1.1.0
version=1.2.0
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
sentence=Enables NMRA DCC Communication