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

@@ -22,8 +22,11 @@ isSetCVReady KEYWORD2
notifyDccReset KEYWORD2
notifyDccIdle KEYWORD2
notifyDccSpeed KEYWORD2
notifyDccSpeedRaw
notifyDccFunc KEYWORD2
notifyDccAccState KEYWORD2
notifyDccAccTurnoutBoard
notifyDccAccTurnoutOutput
notifyDccSigState KEYWORD2
notifyDccMsg KEYWORD2
notifyCVValid KEYWORD2

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