Add DCC decoder module (#2905)
This commit is contained in:
parent
db1e4454de
commit
36df8d009f
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,479 @@
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// Model Railroading with Arduino - NmraDcc.h
|
||||||
|
//
|
||||||
|
// Copyright (c) 2008 - 2018 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.h
|
||||||
|
// author: Alex Shepherd
|
||||||
|
// webpage: http://mrrwa.org/
|
||||||
|
// history: 2008-03-20 Initial Version
|
||||||
|
// 2011-06-26 Migrated into Arduino library from OpenDCC codebase
|
||||||
|
// 2014 Added getAddr to NmraDcc Geoff Bunza
|
||||||
|
// 2015-11-06 Martin Pischky (martin@pischky.de):
|
||||||
|
// Experimental Version to support 14 speed steps
|
||||||
|
// and new signature of notifyDccSpeed and notifyDccFunc
|
||||||
|
// 2017-11-29 Ken West (kgw4449@gmail.com):
|
||||||
|
// Added method and callback headers.
|
||||||
|
//
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// purpose: Provide a simplified interface to decode NMRA DCC packets
|
||||||
|
// and build DCC MutliFunction and Stationary Decoders
|
||||||
|
//
|
||||||
|
//------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// NodeMCU Lua port by @voborsky
|
||||||
|
|
||||||
|
// #define NODE_DEBUG
|
||||||
|
// #define DCC_DEBUG
|
||||||
|
// #define DCC_DBGVAR
|
||||||
|
|
||||||
|
// Uncomment the following Line to Enable Service Mode CV Programming
|
||||||
|
#define NMRA_DCC_PROCESS_SERVICEMODE
|
||||||
|
|
||||||
|
// Uncomment the following line to Enable MultiFunction Decoder Operations
|
||||||
|
#define NMRA_DCC_PROCESS_MULTIFUNCTION
|
||||||
|
|
||||||
|
// #ifndef NMRADCC_IS_IN
|
||||||
|
// #define NMRADCC_IS_IN
|
||||||
|
|
||||||
|
#define NMRADCC_VERSION 201 // Version 2.0.1
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CV29_LOCO_DIR = 0b00000001, /** bit 0: Locomotive Direction: "0" = normal, "1" = reversed */
|
||||||
|
CV29_F0_LOCATION = 0b00000010, /** bit 1: F0 location: "0" = bit 4 in Speed and Direction instructions, "1" = bit 4 in function group one instruction */
|
||||||
|
CV29_APS = 0b00000100, /** bit 2: Alternate Power Source (APS) "0" = NMRA Digital only, "1" = Alternate power source set by CV12 */
|
||||||
|
CV29_ADV_ACK = 0b00001000, /** bit 3: ACK, Advanced Acknowledge mode enabled if 1, disabled if 0 */
|
||||||
|
CV29_SPEED_TABLE_ENABLE = 0b00010000, /** bit 4: STE, Speed Table Enable, "0" = values in CVs 2, 4 and 6, "1" = Custom table selected by CV 25 */
|
||||||
|
CV29_EXT_ADDRESSING = 0b00100000, /** bit 5: "0" = one byte addressing, "1" = two byte addressing */
|
||||||
|
CV29_OUTPUT_ADDRESS_MODE = 0b01000000, /** bit 6: "0" = Decoder Address Mode "1" = Output Address Mode */
|
||||||
|
CV29_ACCESSORY_DECODER = 0b10000000, /** bit 7: "0" = Multi-Function Decoder Mode "1" = Accessory Decoder Mode */
|
||||||
|
} CV_29_BITS;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||||
|
SPEED_STEP_14 = 15, /**< ESTOP=0, 1 to 15 */
|
||||||
|
#endif
|
||||||
|
SPEED_STEP_28 = 29, /**< ESTOP=0, 1 to 29 */
|
||||||
|
SPEED_STEP_128 = 127 /**< ESTOP=0, 1 to 127 */
|
||||||
|
} DCC_SPEED_STEPS;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DCC_DIR_REV = 0, /** The locomotive to go in the reverse direction */
|
||||||
|
DCC_DIR_FWD = 1, /** The locomotive should move in the forward direction */
|
||||||
|
} DCC_DIRECTION;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DCC_ADDR_SHORT, /** Short address is used. The range is 0 to 127. */
|
||||||
|
DCC_ADDR_LONG, /** Long Address is used. The range is 1 to 10239 */
|
||||||
|
} DCC_ADDR_TYPE;
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
FN_0_4 = 1,
|
||||||
|
FN_5_8,
|
||||||
|
FN_9_12,
|
||||||
|
FN_13_20,
|
||||||
|
FN_21_28,
|
||||||
|
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
|
||||||
|
FN_0 /** function light is controlled by base line package (14 speed steps) */
|
||||||
|
#endif
|
||||||
|
} 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
|
||||||
|
|
||||||
|
#ifdef DCC_DBGVAR
|
||||||
|
typedef struct countOf_t {
|
||||||
|
unsigned long Tel;
|
||||||
|
unsigned long Err;
|
||||||
|
}countOf_t ;
|
||||||
|
|
||||||
|
countOf_t countOf;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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_AUTO_FACTORY_DEFAULT 0x02 // Call notifyCVResetFactoryDefault() if CV 7 & 8 == 255
|
||||||
|
#define FLAGS_SETCV_CALLED 0x10 // only used internally !!
|
||||||
|
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
||||||
|
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
||||||
|
|
||||||
|
// Flag Bits that are cloned from CV29 relating the DCC Accessory Decoder
|
||||||
|
#define FLAGS_CV29_BITS (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
|
||||||
|
|
||||||
|
#define DCC_RESET 1
|
||||||
|
#define DCC_IDLE 2
|
||||||
|
#define DCC_SPEED 3
|
||||||
|
#define DCC_SPEED_RAW 4
|
||||||
|
#define DCC_FUNC 5
|
||||||
|
#define DCC_TURNOUT 6
|
||||||
|
#define DCC_ACCESSORY 7
|
||||||
|
#define DCC_RAW 8
|
||||||
|
#define DCC_SERVICEMODE 9
|
||||||
|
|
||||||
|
#define CV_VALID 10
|
||||||
|
#define CV_READ 11
|
||||||
|
#define CV_WRITE 12
|
||||||
|
#define CV_RESET 13
|
||||||
|
|
||||||
|
|
||||||
|
void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
|
||||||
|
|
||||||
|
|
||||||
|
void dcc_close();
|
||||||
|
|
||||||
|
void dcc_init();
|
||||||
|
|
||||||
|
|
||||||
|
/************************************************************************************
|
||||||
|
Call-back functions
|
||||||
|
************************************************************************************/
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccReset(uint8_t hardReset) Callback for a DCC reset command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* hardReset - 0 normal reset command.
|
||||||
|
* 1 hard reset command.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccIdle() Callback for a DCC idle command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* None
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccIdle(void) __attribute__ ((weak));
|
||||||
|
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccSpeed() Callback for a multifunction decoder speed command.
|
||||||
|
* The received speed and direction are unpacked to separate values.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Active decoder address.
|
||||||
|
* AddrType - DCC_ADDR_SHORT or DCC_ADDR_LONG.
|
||||||
|
* Speed - Decoder speed. 0 = Emergency stop
|
||||||
|
* 1 = Regular stop
|
||||||
|
* 2 to SpeedSteps = Speed step 1 to max.
|
||||||
|
* Dir - DCC_DIR_REV or DCC_DIR_FWD
|
||||||
|
* SpeedSteps - Highest speed, SPEED_STEP_14 = 15
|
||||||
|
* SPEED_STEP_28 = 29
|
||||||
|
* SPEED_STEP_128 = 127
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccSpeedRaw() Callback for a multifunction decoder speed command.
|
||||||
|
* The value in Raw is the unpacked speed command.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Active decoder address.
|
||||||
|
* AddrType - DCC_ADDR_SHORT or DCC_ADDR_LONG.
|
||||||
|
* Raw - Raw decoder speed command.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* 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 step headlight function.
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccAccTurnoutBoard() Board oriented callback for a turnout accessory decoder.
|
||||||
|
* Most useful when CV29_OUTPUT_ADDRESS_MODE is not set.
|
||||||
|
* Decoders of this type have 4 paired turnout outputs per board.
|
||||||
|
* OutputPower is 1 if the power is on, and 0 otherwise.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* BoardAddr - Per board address. Equivalent to CV 1 LSB & CV 9 MSB.
|
||||||
|
* OutputPair - Output pair number. It has a range of 0 to 3.
|
||||||
|
* Equivalent to upper 2 bits of the 3 DDD bits in the accessory packet.
|
||||||
|
* Direction - Turnout direction. It has a value of 0 or 1.
|
||||||
|
* It is equivalent to bit 0 of the 3 DDD bits in the accessory packet.
|
||||||
|
* OutputPower - Output On/Off. Equivalent to packet C bit. It has these values:
|
||||||
|
* 0 - Output pair is off.
|
||||||
|
* 1 - Output pair is on.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
|
||||||
|
/*+
|
||||||
|
* notifyDccAccTurnoutOutput() Output oriented callback for a turnout accessory decoder.
|
||||||
|
* Most useful when CV29_OUTPUT_ADDRESS_MODE is not set.
|
||||||
|
* Decoders of this type have 4 paired turnout outputs per board.
|
||||||
|
* OutputPower is 1 if the power is on, and 0 otherwise.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Per output address. There will be 4 Addr addresses
|
||||||
|
* per board for a standard accessory decoder with 4 output pairs.
|
||||||
|
* Direction - Turnout direction. It has a value of 0 or 1.
|
||||||
|
* Equivalent to bit 0 of the 3 DDD bits in the accessory packet.
|
||||||
|
* OutputPower - Output On/Off. Equivalent to packet C bit. It has these values:
|
||||||
|
* 0 - Output is off.
|
||||||
|
* 1 - Output is on.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccAccBoardAddrSet() Board oriented callback for a turnout accessory decoder.
|
||||||
|
* This notification is when a new Board Address is set to the
|
||||||
|
* address of the next DCC Turnout Packet that is received
|
||||||
|
*
|
||||||
|
* This is enabled via the setAccDecDCCAddrNextReceived() method above
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* BoardAddr - Per board address. Equivalent to CV 1 LSB & CV 9 MSB.
|
||||||
|
* per board for a standard accessory decoder with 4 output pairs.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccAccOutputAddrSet() Output oriented callback for a turnout accessory decoder.
|
||||||
|
* This notification is when a new Output Address is set to the
|
||||||
|
* address of the next DCC Turnout Packet that is received
|
||||||
|
*
|
||||||
|
* This is enabled via the setAccDecDCCAddrNextReceived() method above
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Per output address. There will be 4 Addr addresses
|
||||||
|
* per board for a standard accessory decoder with 4 output pairs.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccSigOutputState() Callback for a signal aspect accessory decoder.
|
||||||
|
* Defined in S-9.2.1 as the Extended Accessory Decoder Control Packet.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Addr - Decoder address.
|
||||||
|
* State - 6 bit command equivalent to S-9.2.1 00XXXXXX.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyDccMsg() Raw DCC packet callback.
|
||||||
|
* Called with raw DCC packet bytes.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* Msg - Pointer to DCC_MSG structure. The values are:
|
||||||
|
* Msg->Size - Number of Data bytes in the packet.
|
||||||
|
* Msg->PreambleBits - Number of preamble bits in the packet.
|
||||||
|
* Msg->Data[] - Array of data bytes in the packet.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyCVValid() Callback to determine if a given CV is valid.
|
||||||
|
* This is called when the library needs to determine
|
||||||
|
* if a CV is valid. Note: If defined, this callback
|
||||||
|
* MUST determine if a CV is valid and return the
|
||||||
|
* appropriate value. If this callback is not defined,
|
||||||
|
* the library will determine validity.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
* Writable - 1 for CV writes. 0 for CV reads.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* 1 - CV is valid.
|
||||||
|
* 0 - CV is not valid.
|
||||||
|
*/
|
||||||
|
extern uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyCVRead() Callback to read a CV.
|
||||||
|
* This is called when the library needs to read
|
||||||
|
* a CV. Note: If defined, this callback
|
||||||
|
* MUST return the value of the CV.
|
||||||
|
* If this callback is not defined,
|
||||||
|
* the library will read the CV from EEPROM.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Value - Value of the CV.
|
||||||
|
*/
|
||||||
|
extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyCVWrite() Callback to write a value to a CV.
|
||||||
|
* This is called when the library needs to write
|
||||||
|
* a CV. Note: If defined, this callback
|
||||||
|
* MUST write the Value to the CV and return the value of the CV.
|
||||||
|
* If this callback is not defined,
|
||||||
|
* the library will read the CV from EEPROM.
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* CV - CV number.
|
||||||
|
* Value - Value of the CV.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* Value - Value of the CV.
|
||||||
|
*/
|
||||||
|
extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
|
||||||
|
|
||||||
|
/*+
|
||||||
|
* notifyServiceMode(bool) Called when state of 'inServiceMode' changes
|
||||||
|
*
|
||||||
|
* Inputs:
|
||||||
|
* bool state of inServiceMode
|
||||||
|
* *
|
||||||
|
* Returns:
|
||||||
|
* None
|
||||||
|
*/
|
||||||
|
extern void notifyServiceMode(bool) __attribute__ ((weak));
|
||||||
|
|
||||||
|
// Deprecated, only for backward compatibility with version 1.4.2.
|
||||||
|
// Don't use in new designs. These functions may be dropped in future versions
|
||||||
|
// 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));
|
|
@ -21,6 +21,7 @@
|
||||||
//#define LUA_USE_MODULES_COLOR_UTILS
|
//#define LUA_USE_MODULES_COLOR_UTILS
|
||||||
//#define LUA_USE_MODULES_CRON
|
//#define LUA_USE_MODULES_CRON
|
||||||
//#define LUA_USE_MODULES_CRYPTO
|
//#define LUA_USE_MODULES_CRYPTO
|
||||||
|
//#define LUA_USE_MODULES_DCC
|
||||||
#define LUA_USE_MODULES_DHT
|
#define LUA_USE_MODULES_DHT
|
||||||
//#define LUA_USE_MODULES_ENCODER
|
//#define LUA_USE_MODULES_ENCODER
|
||||||
//#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
|
//#define LUA_USE_MODULES_ENDUSER_SETUP // USE_DNS in dhcpserver.h needs to be enabled for this module to work.
|
||||||
|
|
|
@ -0,0 +1,290 @@
|
||||||
|
// NodeMCU Lua port by @voborsky
|
||||||
|
// Module for handling NMRA DCC protocol
|
||||||
|
// #define NODE_DEBUG
|
||||||
|
|
||||||
|
#include "module.h"
|
||||||
|
#include "lauxlib.h"
|
||||||
|
#include "platform.h"
|
||||||
|
#include "driver/NmraDcc.h"
|
||||||
|
|
||||||
|
#ifdef LUA_USE_MODULES_DCC
|
||||||
|
#if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE)
|
||||||
|
#error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using DCC module
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define TYPE "Type"
|
||||||
|
#define OPERATION "Operation"
|
||||||
|
|
||||||
|
static inline void register_lua_cb(lua_State* L,int* cb_ref){
|
||||||
|
int ref=luaL_ref(L, LUA_REGISTRYINDEX);
|
||||||
|
if( *cb_ref != LUA_NOREF){
|
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
|
||||||
|
}
|
||||||
|
*cb_ref = ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void unregister_lua_cb(lua_State* L, int* cb_ref){
|
||||||
|
if(*cb_ref != LUA_NOREF){
|
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
|
||||||
|
*cb_ref = LUA_NOREF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int notify_cb = LUA_NOREF;
|
||||||
|
static int CV_cb = LUA_NOREF;
|
||||||
|
|
||||||
|
// DCC commands
|
||||||
|
|
||||||
|
void cbInit(lua_State* L, uint16_t command) {
|
||||||
|
if(notify_cb == LUA_NOREF)
|
||||||
|
return;
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, notify_cb);
|
||||||
|
lua_pushinteger(L, command);
|
||||||
|
lua_newtable(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cbAddFieldInteger(lua_State* L, uint16_t Value, char *Field) {
|
||||||
|
lua_pushinteger(L, Value);
|
||||||
|
lua_setfield(L, -2, Field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccReset(uint8_t hardReset ) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_RESET);
|
||||||
|
cbAddFieldInteger(L, hardReset, "hardReset");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccIdle(void) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_IDLE);
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_SPEED);
|
||||||
|
cbAddFieldInteger(L, Addr, "Addr");
|
||||||
|
cbAddFieldInteger(L, AddrType, "AddrType");
|
||||||
|
cbAddFieldInteger(L, Speed, "Speed");
|
||||||
|
cbAddFieldInteger(L, Dir, "Dir");
|
||||||
|
cbAddFieldInteger(L, SpeedSteps, "SpeedSteps");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_SPEED_RAW);
|
||||||
|
cbAddFieldInteger(L, Addr, "Addr");
|
||||||
|
cbAddFieldInteger(L, AddrType, "AddrType");
|
||||||
|
cbAddFieldInteger(L, Raw, "Raw");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_FUNC);
|
||||||
|
cbAddFieldInteger(L, Addr, "Addr");
|
||||||
|
cbAddFieldInteger(L, AddrType, "AddrType");
|
||||||
|
cbAddFieldInteger(L, FuncGrp, "FuncGrp");
|
||||||
|
cbAddFieldInteger(L, FuncState, "FuncState");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_TURNOUT);
|
||||||
|
cbAddFieldInteger(L, BoardAddr, "BoardAddr");
|
||||||
|
cbAddFieldInteger(L, OutputPair, "OutputPair");
|
||||||
|
cbAddFieldInteger(L, Direction, "Direction");
|
||||||
|
cbAddFieldInteger(L, OutputPower, "OutputPower");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_TURNOUT);
|
||||||
|
cbAddFieldInteger(L, Addr, "Addr");
|
||||||
|
cbAddFieldInteger(L, Direction, "Direction");
|
||||||
|
cbAddFieldInteger(L, OutputPower, "OutputPower");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccAccBoardAddrSet( uint16_t BoardAddr) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_ACCESSORY);
|
||||||
|
cbAddFieldInteger(L, BoardAddr, "BoardAddr");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccAccOutputAddrSet( uint16_t Addr) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_ACCESSORY);
|
||||||
|
cbAddFieldInteger(L, Addr, "Addr");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccSigOutputState( uint16_t Addr, uint8_t State) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_ACCESSORY);
|
||||||
|
cbAddFieldInteger(L, State, "State");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyDccMsg( DCC_MSG * Msg ) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_RAW);
|
||||||
|
cbAddFieldInteger(L, Msg->Size, "Size");
|
||||||
|
cbAddFieldInteger(L, Msg->PreambleBits, "PreambleBits");
|
||||||
|
char field[8];
|
||||||
|
for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) {
|
||||||
|
ets_sprintf(field, "Data%d", i);
|
||||||
|
cbAddFieldInteger(L, Msg->Data[i], field);
|
||||||
|
}
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyServiceMode(bool InServiceMode){
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
cbInit(L, DCC_SERVICEMODE);
|
||||||
|
cbAddFieldInteger(L, InServiceMode, "InServiceMode");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CV handling
|
||||||
|
|
||||||
|
uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
if(notify_cb == LUA_NOREF)
|
||||||
|
return 0;
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||||
|
lua_pushinteger(L, CV_VALID);
|
||||||
|
lua_newtable(L);
|
||||||
|
cbAddFieldInteger(L, CV, "CV");
|
||||||
|
cbAddFieldInteger(L, Writable, "Writable");
|
||||||
|
lua_call(L, 2, 1);
|
||||||
|
uint8 result = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t notifyCVRead( uint16_t CV) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
if(notify_cb == LUA_NOREF)
|
||||||
|
return 0;
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||||
|
lua_pushinteger(L, CV_READ);
|
||||||
|
lua_newtable(L);
|
||||||
|
cbAddFieldInteger(L, CV, "CV");
|
||||||
|
lua_call(L, 2, 1);
|
||||||
|
uint8 result = lua_tointeger(L, -1);
|
||||||
|
lua_pop(L, 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
if(notify_cb == LUA_NOREF)
|
||||||
|
return 0;
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||||
|
lua_pushinteger(L, CV_WRITE);
|
||||||
|
lua_newtable(L);
|
||||||
|
cbAddFieldInteger(L, CV, "CV");
|
||||||
|
cbAddFieldInteger(L, Value, "Value");
|
||||||
|
lua_call(L, 2, 0);
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void notifyCVResetFactoryDefault(void) {
|
||||||
|
lua_State* L = lua_getstate();
|
||||||
|
if(notify_cb == LUA_NOREF)
|
||||||
|
return;
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||||
|
lua_pushinteger(L, CV_RESET);
|
||||||
|
lua_call(L, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcc_lua_setup(lua_State* L) {
|
||||||
|
NODE_DBG("[dcc_lua_setup]\n");
|
||||||
|
if (!lua_isnumber(L, 6) && !lua_isnumber(L, 7)) {
|
||||||
|
return luaL_error(L, "wrong arg range");
|
||||||
|
}
|
||||||
|
uint8_t pin = luaL_checkinteger(L, 1);
|
||||||
|
luaL_argcheck(L, platform_gpio_exists(pin) && pin>0, 1, "Invalid interrupt pin");
|
||||||
|
|
||||||
|
if (lua_type(L, 2) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION)
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, 2);
|
||||||
|
register_lua_cb(L, ¬ify_cb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unregister_lua_cb(L, ¬ify_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t ManufacturerId = luaL_checkinteger(L, 3);
|
||||||
|
uint8_t VersionId = luaL_checkinteger(L, 4);
|
||||||
|
uint8_t Flags = luaL_checkinteger(L, 5);
|
||||||
|
uint8_t OpsModeAddressBaseCV = luaL_checkinteger(L, 6);
|
||||||
|
|
||||||
|
if (lua_type(L, 7) == LUA_TFUNCTION || lua_type(L, 3) == LUA_TLIGHTFUNCTION)
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, 7);
|
||||||
|
register_lua_cb(L, &CV_cb);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unregister_lua_cb(L, &CV_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DBG("[dcc_lua_setup] Enabling interrupt on PIN %d\n", pin);
|
||||||
|
dcc_setup(pin, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV );
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dcc_lua_close(lua_State* L) {
|
||||||
|
dcc_close();
|
||||||
|
unregister_lua_cb(L, ¬ify_cb);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int luaopen_dcc( lua_State *L ) {
|
||||||
|
NODE_DBG("[dcc_luaopen]\n");
|
||||||
|
dcc_init();
|
||||||
|
//DccRx.lua_cb_ref = LUA_NOREF;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Module function map
|
||||||
|
LROT_BEGIN( dcc )
|
||||||
|
LROT_FUNCENTRY( setup, dcc_lua_setup )
|
||||||
|
LROT_FUNCENTRY( close, dcc_lua_close )
|
||||||
|
|
||||||
|
LROT_NUMENTRY( DCC_RESET, DCC_RESET )
|
||||||
|
LROT_NUMENTRY( DCC_IDLE, DCC_IDLE )
|
||||||
|
LROT_NUMENTRY( DCC_SPEED, DCC_SPEED )
|
||||||
|
LROT_NUMENTRY( DCC_SPEED_RAW, DCC_SPEED_RAW )
|
||||||
|
LROT_NUMENTRY( DCC_FUNC, DCC_FUNC )
|
||||||
|
LROT_NUMENTRY( DCC_TURNOUT, DCC_TURNOUT )
|
||||||
|
LROT_NUMENTRY( DCC_ACCESSORY, DCC_ACCESSORY )
|
||||||
|
LROT_NUMENTRY( DCC_RAW, DCC_RAW )
|
||||||
|
LROT_NUMENTRY( DCC_SERVICEMODE, DCC_SERVICEMODE )
|
||||||
|
|
||||||
|
LROT_NUMENTRY( CV_VALID, CV_VALID )
|
||||||
|
LROT_NUMENTRY( CV_READ, CV_READ )
|
||||||
|
LROT_NUMENTRY( CV_WRITE, CV_WRITE )
|
||||||
|
LROT_NUMENTRY( CV_RESET, CV_RESET )
|
||||||
|
|
||||||
|
LROT_NUMENTRY( MAN_ID_JMRI, MAN_ID_JMRI)
|
||||||
|
LROT_NUMENTRY( MAN_ID_DIY, MAN_ID_DIY)
|
||||||
|
LROT_NUMENTRY( MAN_ID_SILICON_RAILWAY, MAN_ID_SILICON_RAILWAY)
|
||||||
|
|
||||||
|
LROT_NUMENTRY( FLAGS_MY_ADDRESS_ONLY, FLAGS_MY_ADDRESS_ONLY )
|
||||||
|
LROT_NUMENTRY( FLAGS_AUTO_FACTORY_DEFAULT, FLAGS_AUTO_FACTORY_DEFAULT )
|
||||||
|
LROT_NUMENTRY( FLAGS_OUTPUT_ADDRESS_MODE, FLAGS_OUTPUT_ADDRESS_MODE )
|
||||||
|
LROT_NUMENTRY( FLAGS_DCC_ACCESSORY_DECODER, FLAGS_DCC_ACCESSORY_DECODER )
|
||||||
|
LROT_END( dcc, NULL, 0 )
|
||||||
|
|
||||||
|
NODEMCU_MODULE(DCC, "dcc", dcc, luaopen_dcc);
|
|
@ -0,0 +1,118 @@
|
||||||
|
# DCC module
|
||||||
|
| Since | Origin / Contributor | Maintainer | Source |
|
||||||
|
| :----- | :-------------------- | :---------- | :------ |
|
||||||
|
| 2019-12-28 | [vsky279](https://github.com/vsky279) | [vsky279](https://github.com/vsky279) | [dcc.c](../../app/modules/dcc.c)|
|
||||||
|
|
||||||
|
The dcc module implements decoder of the [National Model Railroad Association](https://www.nmra.org/) (NMRA) Digital Command Control (DCC) decoder - see [DCC wiki](https://dccwiki.com/Introduction_to_DCC) for details.
|
||||||
|
|
||||||
|
The hardware needed to decode the DCC signal can be built based on different DCC decoders implementation for Arduino, for inspiration see [https://mrrwa.org/dcc-decoder-interface/](https://mrrwa.org/dcc-decoder-interface/). Basically the signal from the DCC bus is connected via an optocoupler to any GPIO pin. The DCC bus can be also used to power the ESP.
|
||||||
|
|
||||||
|
The module is based on the project NmraDcc [https://github.com/mrrwa/NmraDcc](https://github.com/mrrwa/NmraDcc) by Alex Shepherd. The module is based on the version from May 2005, commit [6d12e6cd3f5f520020d49946652a94c1e3473f6b](https://github.com/mrrwa/NmraDcc/tree/6d12e6cd3f5f520020d49946652a94c1e3473f6b).
|
||||||
|
|
||||||
|
## dcc.setup()
|
||||||
|
|
||||||
|
Initializes the dcc module and links callback functions.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`dcc.setup(DCC_command, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV, CV_callback)`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
- `DCC_command(cmd, params)` calllback function that is called when a DCC command is decoded. `cmd` parameters is one of the following values. `params` contains a collection of parameters specific to given command.
|
||||||
|
- `dcc.DCC_RESET` no additional parameters, `params` is `nil`.
|
||||||
|
- `dcc.DCC_IDLE` no additional parameters, `params` is `nil`.
|
||||||
|
- `dcc.DCC_SPEED` parameters collection members are `Addr`, `AddrType`, `Speed`,`Dir`, `SpeedSteps`.
|
||||||
|
- `dcc.DCC_SPEED_RAW` parameters collection members are `Addr`, `AddrType`, `Raw`.
|
||||||
|
- `dcc.DCC_FUNC` parameters collection members are `Addr`, `AddrType`, `FuncGrp`,`FuncState`.
|
||||||
|
- `dcc.DCC_TURNOUT` parameters collection members are `BoardAddr`, `OutputPair`, `Direction`,`OutputPower` or `Addr`, `Direction`,`OutputPower`.
|
||||||
|
- `dcc.DCC_ACCESSORY` parameters collection has one member `BoardAddr` or `Addr` or `State`.
|
||||||
|
- `dcc.DCC_RAW` parameters collection member are `Size`, `PreambleBits`, `Data1` to `Data6`.
|
||||||
|
- `dcc.DCC_SERVICEMODE` parameters collection has one member `InServiceMode`.
|
||||||
|
- `ManufacturerId` Manufacturer ID returned in CV 8. Commonly `dcc.MAN_ID_DIY`.
|
||||||
|
- `VersionId` Version ID returned in CV 7.
|
||||||
|
- `Flags` one of or combination (OR operator) of
|
||||||
|
- `dcc.FLAGS_MY_ADDRESS_ONLY`Only process packets with My Address.
|
||||||
|
- `dcc.FLAGS_DCC_ACCESSORY_DECODER` Decoder is an accessory decode.
|
||||||
|
- `dcc.FLAGS_OUTPUT_ADDRESS_MODE` This flag applies to accessory decoders only. Accessory decoders normally have 4 paired outputs and a single address refers to all 4 outputs. Setting this flag causes each address to refer to a single output.
|
||||||
|
- `dcc.FLAGS_AUTO_FACTORY_DEFAULT` Call DCC command callback with `dcc.CV_RESET` command if CV 7 & 8 == 255.
|
||||||
|
- `OpsModeAddressBaseCV` Ops Mode base address. Set it to 0?
|
||||||
|
- `CV_callback(operation, param)` callback function that is called when any manipulation with CV ([Configuarion Variable](https://dccwiki.com/Configuration_Variable)) is requested.
|
||||||
|
- `dcc.CV_VALID`to determine if a given CV is valid. This callback must determine if a CV is valid and return the appropriate value. `param` collection has members `CV` and `Value`.
|
||||||
|
- `dcc.CV_READ` to read a CV. This callback must return the value of the CV. `param` collection has one member `CV` determing the CV number to be read.
|
||||||
|
- `dcc.CV_WRITE` to write a value to a CV. This callback must write the Value to the CV and return the value of the CV. `param` collection has members `CV` and `Value`.
|
||||||
|
- `dcc.CV_RESET` Called when CVs must be reset to their factory defaults.
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
`bit` module is used in the example though it is not needed for the dcc module functionality.
|
||||||
|
```lua
|
||||||
|
local PIN = 2 -- GPIO4
|
||||||
|
|
||||||
|
local addr = 0x12a
|
||||||
|
|
||||||
|
CV = {[29]=0,
|
||||||
|
[1]=bit.band(addr, 0x3f), --CV_ACCESSORY_DECODER_ADDRESS_LSB (6 bits)
|
||||||
|
[9]=bit.band(bit.rshift(addr,6), 0x7) --CV_ACCESSORY_DECODER_ADDRESS_MSB (3 bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
local function DCC_command(cmd, params)
|
||||||
|
if cmd == dcc.DCC_IDLE then
|
||||||
|
return
|
||||||
|
elseif cmd == dcc.DCC_TURNOUT then
|
||||||
|
print("Turnout command")
|
||||||
|
elseif cmd == dcc.DCC_SPEED then
|
||||||
|
print("Speed command")
|
||||||
|
elseif cmd == dcc.DCC_FUNC then
|
||||||
|
print("Function command")
|
||||||
|
else
|
||||||
|
print("Other command", cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,j in pairs(params) do
|
||||||
|
print(i, j)
|
||||||
|
end
|
||||||
|
print(("="):rep(80))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CV_callback(operation, param)
|
||||||
|
local oper = ""
|
||||||
|
local result
|
||||||
|
if operation == dcc.CV_WRITE then
|
||||||
|
oper = "Write"
|
||||||
|
CV[param.CV]=param.Value
|
||||||
|
elseif operation == dcc.CV_READ then
|
||||||
|
oper = "Read"
|
||||||
|
result = CV[param.CV]
|
||||||
|
elseif operation == dcc.CV_VALID then
|
||||||
|
oper = "Valid"
|
||||||
|
result = 1
|
||||||
|
elseif operation == CV_RESET then
|
||||||
|
oper = "Reset"
|
||||||
|
CV = {}
|
||||||
|
end
|
||||||
|
print(("[CV_callback] %s CV %d%s"):format(oper, param.CV or `nil`, param.Value and "\tValue: "..param.Value or "\tValue: nil"))
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
dcc.setup(PIN,
|
||||||
|
DCC_command,
|
||||||
|
dcc.MAN_ID_DIY, 1,
|
||||||
|
--bit.bor(dcc.FLAGS_AUTO_FACTORY_DEFAULT, dcc.FLAGS_DCC_ACCESSORY_DECODER, dcc.FLAGS_MY_ADDRESS_ONLY),
|
||||||
|
bit.bor(dcc.FLAGS_AUTO_FACTORY_DEFAULT),
|
||||||
|
0, -- ???
|
||||||
|
CV_callback)
|
||||||
|
```
|
||||||
|
|
||||||
|
## dcc.close()
|
||||||
|
|
||||||
|
Stops the dcc module.
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
`dcc.close()`
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
`nil`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
`nil`
|
|
@ -0,0 +1,86 @@
|
||||||
|
-- Simple example for responding to NMRA DCC commands
|
||||||
|
-- author @voborsky
|
||||||
|
local PIN = 2 -- GPIO4
|
||||||
|
|
||||||
|
local addr = 0x12a
|
||||||
|
|
||||||
|
CV = {[29]=0,
|
||||||
|
[1]=bit.band(addr, 0x3f), --CV_ACCESSORY_DECODER_ADDRESS_LSB (6 bits)
|
||||||
|
[9]=bit.band(bit.rshift(addr,6), 0x7) --CV_ACCESSORY_DECODER_ADDRESS_MSB (3 bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
local function deepcopy(orig)
|
||||||
|
local orig_type = type(orig)
|
||||||
|
local copy
|
||||||
|
if orig_type == 'table' then
|
||||||
|
copy = {}
|
||||||
|
for orig_key, orig_value in next, orig, nil do
|
||||||
|
copy[deepcopy(orig_key)] = deepcopy(orig_value)
|
||||||
|
end
|
||||||
|
setmetatable(copy, deepcopy(getmetatable(orig)))
|
||||||
|
else -- number, string, boolean, etc
|
||||||
|
copy = orig
|
||||||
|
end
|
||||||
|
return copy
|
||||||
|
end
|
||||||
|
|
||||||
|
local cmd_last
|
||||||
|
local params_last
|
||||||
|
|
||||||
|
local function is_new(cmd, params)
|
||||||
|
if cmd ~= cmd_last then return true end
|
||||||
|
for i,j in pairs(params) do
|
||||||
|
if params_last[i] ~= j then return true end
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function DCC_command(cmd, params)
|
||||||
|
if not is_new(cmd, params) then return end
|
||||||
|
if cmd == dcc.DCC_IDLE then
|
||||||
|
return
|
||||||
|
elseif cmd == dcc.DCC_TURNOUT then
|
||||||
|
print("Turnout command")
|
||||||
|
elseif cmd == dcc.DCC_SPEED then
|
||||||
|
print("Speed command")
|
||||||
|
elseif cmd == dcc.DCC_FUNC then
|
||||||
|
print("Function command")
|
||||||
|
else
|
||||||
|
print("Other command", cmd)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i,j in pairs(params) do
|
||||||
|
print(i, j)
|
||||||
|
end
|
||||||
|
print(("="):rep(80))
|
||||||
|
cmd_last = cmd
|
||||||
|
params_last = deepcopy(params)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function CV_callback(operation, param)
|
||||||
|
local oper = ""
|
||||||
|
local result
|
||||||
|
if operation == dcc.CV_WRITE then
|
||||||
|
oper = "Write"
|
||||||
|
CV[param.CV]=param.Value
|
||||||
|
elseif operation == dcc.CV_READ then
|
||||||
|
oper = "Read"
|
||||||
|
result = CV[param.CV]
|
||||||
|
elseif operation == dcc.CV_VALID then
|
||||||
|
oper = "Valid"
|
||||||
|
result = 1
|
||||||
|
elseif operation == CV_RESET then
|
||||||
|
oper = "Reset"
|
||||||
|
CV = {}
|
||||||
|
end
|
||||||
|
print(("[CV_callback] %s CV %d%s"):format(oper, param.CV, param.Value and "\tValue: "..param.Value or "\tValue: nil"))
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
dcc.setup(PIN,
|
||||||
|
DCC_command,
|
||||||
|
dcc.MAN_ID_DIY, 1,
|
||||||
|
--bit.bor(dcc.FLAGS_AUTO_FACTORY_DEFAULT, dcc.FLAGS_DCC_ACCESSORY_DECODER, dcc.FLAGS_MY_ADDRESS_ONLY), -- Accessories (turnouts) decoder
|
||||||
|
bit.bor(dcc.FLAGS_AUTO_FACTORY_DEFAULT), -- Cab (train) decoder
|
||||||
|
0, -- ???
|
||||||
|
CV_callback)
|
|
@ -67,6 +67,7 @@ pages:
|
||||||
- 'color-utils': 'modules/color-utils.md'
|
- 'color-utils': 'modules/color-utils.md'
|
||||||
- 'cron': 'modules/cron.md'
|
- 'cron': 'modules/cron.md'
|
||||||
- 'crypto': 'modules/crypto.md'
|
- 'crypto': 'modules/crypto.md'
|
||||||
|
- 'dcc': 'modules/dcc.md'
|
||||||
- 'dht': 'modules/dht.md'
|
- 'dht': 'modules/dht.md'
|
||||||
- 'encoder': 'modules/encoder.md'
|
- 'encoder': 'modules/encoder.md'
|
||||||
- 'enduser setup / captive portal / WiFi manager': 'modules/enduser-setup.md'
|
- 'enduser setup / captive portal / WiFi manager': 'modules/enduser-setup.md'
|
||||||
|
|
Loading…
Reference in New Issue