NmraDcc port to NodeMCU Lua
Merging @pjsg's ack functionalities and other fixes
This commit is contained in:
parent
e2c7ee411f
commit
f017338216
|
@ -45,8 +45,59 @@
|
|||
//
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// NodeMCU Lua port by @voborsky
|
||||
|
||||
// #define NODE_DEBUG
|
||||
#define NODEMCUDCC
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "platform.h"
|
||||
#include "user_interface.h"
|
||||
#include "task/task.h"
|
||||
#include "driver/NmraDcc.h"
|
||||
|
||||
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define BYTE_TO_BINARY(byte) \
|
||||
(byte & 0x80 ? '1' : '0'), \
|
||||
(byte & 0x40 ? '1' : '0'), \
|
||||
(byte & 0x20 ? '1' : '0'), \
|
||||
(byte & 0x10 ? '1' : '0'), \
|
||||
(byte & 0x08 ? '1' : '0'), \
|
||||
(byte & 0x04 ? '1' : '0'), \
|
||||
(byte & 0x02 ? '1' : '0'), \
|
||||
(byte & 0x01 ? '1' : '0')
|
||||
|
||||
#define byte uint8_t
|
||||
#define word int16_t
|
||||
|
||||
#define abs(a) ((a) > 0 ? (a) : (0-a))
|
||||
|
||||
#define RISING GPIO_PIN_INTR_POSEDGE
|
||||
#define FALLING GPIO_PIN_INTR_NEGEDGE
|
||||
#define CHANGE GPIO_PIN_INTR_ANYEDGE
|
||||
|
||||
static uint32_t last_time_overflow_millis;
|
||||
static uint32_t last_system_time;
|
||||
|
||||
uint32_t millis() {
|
||||
uint32_t now = system_get_time();
|
||||
|
||||
if (now < last_system_time) {
|
||||
// we have an overflow situation
|
||||
// assume only one overflow
|
||||
last_time_overflow_millis += (1 << 29) / 125; // (1 << 32) / 1000
|
||||
}
|
||||
|
||||
last_system_time = now;
|
||||
return last_time_overflow_millis + now / 1000;
|
||||
}
|
||||
#else
|
||||
#include "NmraDcc.h"
|
||||
#include "EEPROM.h"
|
||||
#endif
|
||||
|
||||
// Uncomment to print DEBUG messages
|
||||
// #define DEBUG_PRINT
|
||||
|
@ -113,6 +164,9 @@
|
|||
|
||||
// Debug-Ports
|
||||
//#define debug // Testpulse for logic analyser
|
||||
#ifdef NODE_DEBUG
|
||||
#define debug
|
||||
#endif
|
||||
#ifdef debug
|
||||
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
|
||||
#define MODE_TP1 DDRF |= (1<<2) //pinA2
|
||||
|
@ -210,6 +264,24 @@
|
|||
|
||||
|
||||
//#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
|
||||
#elif defined(NODE_DEBUG)
|
||||
#define PULLUP PLATFORM_GPIO_PULLUP
|
||||
#define OUTPUT PLATFORM_GPIO_OUTPUT
|
||||
#define HIGH PLATFORM_GPIO_HIGH
|
||||
#define LOW PLATFORM_GPIO_LOW
|
||||
|
||||
#define MODE_TP1 platform_gpio_mode( 5, OUTPUT, PULLUP ); // GPIO 14
|
||||
#define SET_TP1 platform_gpio_write(5, HIGH);
|
||||
#define CLR_TP1 platform_gpio_write(5, LOW);
|
||||
#define MODE_TP2 platform_gpio_mode( 6, OUTPUT, PULLUP ); // GPIO 12
|
||||
#define SET_TP2 platform_gpio_write(6, HIGH);
|
||||
#define CLR_TP2 platform_gpio_write(6, LOW);
|
||||
#define MODE_TP3 platform_gpio_mode( 7, OUTPUT, PULLUP ); // GPIO 13
|
||||
#define SET_TP3 platform_gpio_write(7, HIGH);
|
||||
#define CLR_TP3 platform_gpio_write(7, LOW);
|
||||
#define MODE_TP4 platform_gpio_mode( 8, OUTPUT, PULLUP ); // GPIO 15
|
||||
#define SET_TP4 platform_gpio_write(8, HIGH);
|
||||
#define CLR_TP4 platform_gpio_write(8, LOW);
|
||||
#else
|
||||
#define MODE_TP1
|
||||
#define SET_TP1
|
||||
|
@ -241,8 +313,12 @@
|
|||
|
||||
#endif
|
||||
#ifdef DEBUG_PRINT
|
||||
#ifdef NODEMCUDCC
|
||||
#define DB_PRINT NODE_DBG
|
||||
#else
|
||||
#define DB_PRINT( x, ... ) { char dbgbuf[80]; sprintf_P( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.println( dbgbuf ); }
|
||||
#define DB_PRINT_( x, ... ) { char dbgbuf[80]; sprintf_P( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.print( dbgbuf ); }
|
||||
#endif
|
||||
#else
|
||||
#define DB_PRINT( x, ... ) ;
|
||||
#define DB_PRINT_( x, ... ) ;
|
||||
|
@ -257,6 +333,10 @@ static ExtIntTriggerMode ISREdge;
|
|||
#elif defined ( ESP32 )
|
||||
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static byte ISRWatch; // Interrupt Handler Edge Filter
|
||||
#elif defined ( NODEMCUDCC )
|
||||
static uint8_t ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static int16_t bitMax, bitMin;
|
||||
DCC_MSG Msg ;
|
||||
#else
|
||||
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
|
||||
static byte ISRWatch; // Interrupt Handler Edge Filter
|
||||
|
@ -307,10 +387,15 @@ typedef struct
|
|||
uint8_t PageRegister ; // Used for Paged Operations in Service Mode Programming
|
||||
uint8_t DuplicateCount ;
|
||||
DCC_MSG LastMsg ;
|
||||
#ifdef NODEMCUDCC
|
||||
uint8_t IntPin;
|
||||
uint8_t IntBitmask;
|
||||
#else
|
||||
uint8_t ExtIntNum;
|
||||
uint8_t ExtIntPinNum;
|
||||
volatile uint8_t *ExtIntPort; // use port and bitmask to read input at AVR in ISR
|
||||
uint8_t ExtIntMask; // digitalRead is too slow on AVR
|
||||
#endif
|
||||
int16_t myDccAddress; // Cached value of DCC Address from CVs
|
||||
uint8_t inAccDecDCCAddrNextReceivedMode;
|
||||
uint8_t cv29Value;
|
||||
|
@ -330,12 +415,27 @@ portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
|||
void IRAM_ATTR ExternalInterruptHandler(void)
|
||||
#elif defined(ESP8266)
|
||||
void ICACHE_RAM_ATTR ExternalInterruptHandler(void)
|
||||
#elif defined(NODEMCUDCC)
|
||||
task_handle_t DataReady_taskid;
|
||||
static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status)
|
||||
#else
|
||||
void ExternalInterruptHandler(void)
|
||||
#endif
|
||||
{
|
||||
SET_TP3;
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
// This function really is running at interrupt level with everything
|
||||
// else masked off. It should take as little time as necessary.
|
||||
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
|
||||
if ((gpio_status & DccProcState.IntBitmask) == 0) {
|
||||
return ret_gpio_status;
|
||||
}
|
||||
|
||||
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & DccProcState.IntBitmask);
|
||||
ret_gpio_status &= ~(DccProcState.IntBitmask);
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
// switch (ISRWatch)
|
||||
// {
|
||||
|
@ -363,7 +463,11 @@ void ExternalInterruptHandler(void)
|
|||
uint8_t DccBitVal;
|
||||
static int8_t bit1, bit2 ;
|
||||
static unsigned int lastMicros = 0;
|
||||
#ifdef NODEMCUDCC
|
||||
static byte halfBit, preambleBitCount;
|
||||
#else
|
||||
static byte halfBit, DCC_IrqRunning, preambleBitCount;
|
||||
#endif
|
||||
unsigned int actMicros, bitMicros;
|
||||
#ifdef ALLOW_NESTED_IRQ
|
||||
if ( DCC_IrqRunning ) {
|
||||
|
@ -377,19 +481,29 @@ void ExternalInterruptHandler(void)
|
|||
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
|
||||
}
|
||||
#endif
|
||||
#ifdef NODEMCUDCC
|
||||
actMicros = system_get_time();
|
||||
#else
|
||||
actMicros = micros();
|
||||
#endif
|
||||
bitMicros = actMicros-lastMicros;
|
||||
|
||||
CLR_TP3; SET_TP3;
|
||||
#ifdef __AVR_MEGA__
|
||||
if ( bitMicros < bitMin || ( DccRx.State != WAIT_START_BIT && (*DccProcState.ExtIntPort & DccProcState.ExtIntMask) != (ISRLevel) ) ) {
|
||||
#elif defined(NODEMCUDCC)
|
||||
if ( bitMicros < bitMin ) {
|
||||
#else
|
||||
if ( bitMicros < bitMin || ( DccRx.State != WAIT_START_BIT && digitalRead( DccProcState.ExtIntPinNum ) != (ISRLevel) ) ) {
|
||||
#endif
|
||||
// too short - my be false interrupt due to glitch or false protocol or level does not match RISING / FALLING edge -> ignore this IRQ
|
||||
CLR_TP3;
|
||||
SET_TP4; /*delayMicroseconds(1); */ CLR_TP4;
|
||||
#ifdef NODEMCUDCC
|
||||
return ret_gpio_status; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
|
||||
#else
|
||||
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
|
||||
#endif
|
||||
}
|
||||
CLR_TP3; SET_TP3;
|
||||
|
||||
|
@ -409,6 +523,8 @@ void ExternalInterruptHandler(void)
|
|||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#elif defined(NODEMCUDCC)
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge );
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
#endif
|
||||
|
@ -492,6 +608,8 @@ void ExternalInterruptHandler(void)
|
|||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#elif defined(NODEMCUDCC)
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge);
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
// enable level checking ( with direct port reading @ AVR )
|
||||
|
@ -542,13 +660,17 @@ void ExternalInterruptHandler(void)
|
|||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#elif defined(NODEMCUDCC)
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge);
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
#endif
|
||||
#ifndef NODEMCUDCC
|
||||
// enable level-checking
|
||||
ISRChkMask = DccProcState.ExtIntMask;
|
||||
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ;
|
||||
//CLR_TP4;
|
||||
#endif
|
||||
break;
|
||||
case 4: // previous (first) halfbit was 0
|
||||
// if this halfbit is 0 too, we got the startbit
|
||||
|
@ -585,14 +707,17 @@ void ExternalInterruptHandler(void)
|
|||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#elif defined(NODEMCUDCC)
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge);
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
#endif
|
||||
#ifndef NODEMCUDCC
|
||||
// enable level-checking
|
||||
ISRChkMask = DccProcState.ExtIntMask;
|
||||
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ;
|
||||
|
||||
//CLR_TP4;
|
||||
#endif
|
||||
break;
|
||||
|
||||
}
|
||||
|
@ -642,6 +767,8 @@ void ExternalInterruptHandler(void)
|
|||
DccRx.DataReady = 1 ;
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
#elif defined(NODEMCUDCC)
|
||||
task_post_high(DataReady_taskid, (os_param_t) 0);
|
||||
#endif
|
||||
// SET_TP2; CLR_TP2;
|
||||
preambleBitCount = 0 ;
|
||||
|
@ -701,6 +828,8 @@ void ExternalInterruptHandler(void)
|
|||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = CHANGE;
|
||||
#elif defined(NODEMCUDCC)
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), CHANGE);
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
|
||||
#endif
|
||||
|
@ -726,6 +855,9 @@ void ExternalInterruptHandler(void)
|
|||
#endif
|
||||
//CLR_TP1;
|
||||
CLR_TP3;
|
||||
#ifdef NODEMCUDCC
|
||||
return ret_gpio_status;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ackCV(void)
|
||||
|
@ -747,6 +879,7 @@ void ackAdvancedCV(void)
|
|||
}
|
||||
|
||||
|
||||
#ifndef NODEMCUDCC
|
||||
uint8_t readEEPROM( unsigned int CV )
|
||||
{
|
||||
return EEPROM.read(CV) ;
|
||||
|
@ -773,6 +906,7 @@ bool readyEEPROM()
|
|||
return true;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t validCV( uint16_t CV, uint8_t Writable )
|
||||
{
|
||||
|
@ -782,6 +916,9 @@ uint8_t validCV( uint16_t CV, uint8_t Writable )
|
|||
if( notifyCVValid )
|
||||
return notifyCVValid( CV, Writable ) ;
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
return 0;
|
||||
#else
|
||||
uint8_t Valid = 1 ;
|
||||
|
||||
if( CV > MAXCV )
|
||||
|
@ -791,17 +928,28 @@ uint8_t validCV( uint16_t CV, uint8_t Writable )
|
|||
Valid = 0 ;
|
||||
|
||||
return Valid ;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
uint16_t readCV( unsigned int CV )
|
||||
#else
|
||||
uint8_t readCV( unsigned int CV )
|
||||
#endif
|
||||
{
|
||||
#ifndef NODEMCUDCC
|
||||
uint8_t Value ;
|
||||
#endif
|
||||
|
||||
if( notifyCVRead )
|
||||
return notifyCVRead( CV ) ;
|
||||
|
||||
#ifndef NODEMCUDCC
|
||||
Value = readEEPROM(CV);
|
||||
return Value ;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t writeCV( unsigned int CV, uint8_t Value)
|
||||
|
@ -825,6 +973,9 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
|||
if( notifyCVWrite )
|
||||
return notifyCVWrite( CV, Value ) ;
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
return 0;
|
||||
#else
|
||||
if( readEEPROM( CV ) != Value )
|
||||
{
|
||||
writeEEPROM( CV, Value ) ;
|
||||
|
@ -837,6 +988,7 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
|||
}
|
||||
|
||||
return readEEPROM( CV ) ;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint16_t getMyAddr(void)
|
||||
|
@ -896,9 +1048,18 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
|
|||
uint8_t BitValue = Value & 0x08 ;
|
||||
uint8_t BitWrite = Value & 0x10 ;
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
uint16_t tempValue = readCV( CVAddr ) ; // Read the Current CV Value
|
||||
#else
|
||||
uint8_t tempValue = readCV( CVAddr ) ; // Read the Current CV Value
|
||||
#endif
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
if (tempValue <= 255) {
|
||||
DB_PRINT("CV: %d Current Value: %02X Bit-Wise Mode: %s Mask: %02X Value: %02X", CVAddr, tempValue, BitWrite ? "Write":"Read", BitMask, BitValue);
|
||||
#else
|
||||
DB_PRINT("CV: %d Current Value: %02X Bit-Wise Mode: %s Mask: %02X Value: %02X", CVAddr, tempValue, BitWrite ? "Write":"Read", BitMask, BitValue);
|
||||
#endif
|
||||
|
||||
// Perform the Bit Write Operation
|
||||
if( BitWrite )
|
||||
|
@ -915,7 +1076,6 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
|
|||
ackFunction() ;
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the Bit Verify Operation
|
||||
else
|
||||
{
|
||||
|
@ -933,6 +1093,9 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
|
|||
}
|
||||
}
|
||||
}
|
||||
#ifdef NODEMCUDCC
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -947,9 +1110,13 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
|
|||
|
||||
uint8_t CmdMasked = Cmd & 0b11100000 ;
|
||||
|
||||
// NODE_DBG("[dcc_processMultiFunctionMessage] Addr: %d, Type: %d, Cmd: %d ("BYTE_TO_BINARY_PATTERN"), Data: %d, %d, CmdMasked="BYTE_TO_BINARY_PATTERN"\n", Addr, AddrType, Cmd, BYTE_TO_BINARY(Cmd), Data1, Data2, BYTE_TO_BINARY(CmdMasked));
|
||||
|
||||
// If we are an Accessory Decoder
|
||||
if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER )
|
||||
{
|
||||
// NODE_DBG("[dcc_processMultiFunctionMessage] DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER\n");
|
||||
|
||||
// and this isn't an Ops Mode Write or we are NOT faking the Multifunction Ops mode address in CV 33+34 or
|
||||
// it's not our fake address, then return
|
||||
if( ( CmdMasked != 0b11100000 ) || ( DccProcState.OpsModeAddressBaseCV == 0 ) )
|
||||
|
@ -966,6 +1133,7 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
|
|||
else if( ( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) && ( Addr != getMyAddr() ) && ( Addr != 0 ) )
|
||||
return ;
|
||||
|
||||
NODE_DBG("[dcc_processMultiFunctionMessage] CmdMasked: %x\n", CmdMasked);
|
||||
switch( CmdMasked )
|
||||
{
|
||||
case 0b00000000: // Decoder Control
|
||||
|
@ -1224,6 +1392,7 @@ void SerialPrintPacketHex(const __FlashStringHelper *strLabel, DCC_MSG * pDccMsg
|
|||
///////////////////////////////////////////////////////////////////////////////
|
||||
void execDccProcessor( DCC_MSG * pDccMsg )
|
||||
{
|
||||
NODE_DBG("[dcc_execDccProcessor]\n");
|
||||
if( ( pDccMsg->Data[0] == 0 ) && ( pDccMsg->Data[1] == 0 ) )
|
||||
{
|
||||
if( notifyDccReset )
|
||||
|
@ -1295,7 +1464,7 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||
|
||||
BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ;
|
||||
TurnoutPairIndex = (pDccMsg->Data[1] & 0b00000110) >> 1;
|
||||
DB_PRINT("eDP: BAddr:%d, Index:%d", BoardAddress, TurnoutPairIndex);
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: BAddr:%d, Index:%d", BoardAddress, TurnoutPairIndex);
|
||||
|
||||
// First check for Legacy Accessory Decoder Configuration Variable Access Instruction
|
||||
// as it's got a different format to the others
|
||||
|
@ -1305,13 +1474,13 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||
// Check if this command is for our address or the broadcast address
|
||||
if((BoardAddress != getMyAddr()) && ( BoardAddress < 511 ))
|
||||
{
|
||||
DB_PRINT("eDP: Board Address Not Matched");
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: Board Address Not Matched");
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t cvAddress = ((pDccMsg->Data[1] & 0b00000011) << 8) + pDccMsg->Data[2] + 1;
|
||||
uint8_t cvValue = pDccMsg->Data[3];
|
||||
DB_PRINT("eDP: CV:%d Value:%d", cvAddress, cvValue );
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: CV:%d Value:%d", cvAddress, cvValue );
|
||||
if(validCV( cvAddress, 1 ))
|
||||
writeCV(cvAddress, cvValue);
|
||||
return;
|
||||
|
@ -1320,7 +1489,7 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||
|
||||
OutputAddress = (((BoardAddress - 1) << 2 ) | TurnoutPairIndex) + 1 ; //decoder output addresses start with 1, packet address range starts with 0
|
||||
// ( according to NMRA 9.2.2 )
|
||||
DB_PRINT("eDP: OAddr:%d", OutputAddress);
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: OAddr:%d", OutputAddress);
|
||||
|
||||
if( DccProcState.inAccDecDCCAddrNextReceivedMode)
|
||||
{
|
||||
|
@ -1351,18 +1520,18 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||
if( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY )
|
||||
{
|
||||
if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) {
|
||||
DB_PRINT(" AddrChk: OAddr:%d, BAddr:%d, myAddr:%d Chk=%d", OutputAddress, BoardAddress, getMyAddr(), OutputAddress != getMyAddr() );
|
||||
DB_PRINT("[dcc_execDccProcessor] AddrChk: OAddr:%d, BAddr:%d, myAddr:%d Chk=%d", OutputAddress, BoardAddress, getMyAddr(), OutputAddress != getMyAddr() );
|
||||
if ( OutputAddress != getMyAddr() && OutputAddress < 2045 ) {
|
||||
DB_PRINT(" eDP: OAddr:%d, myAddr:%d - no match", OutputAddress, getMyAddr() );
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: OAddr:%d, myAddr:%d - no match", OutputAddress, getMyAddr() );
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if( ( BoardAddress != getMyAddr() ) && ( BoardAddress < 511 ) ) {
|
||||
DB_PRINT(" eDP: BAddr:%d, myAddr:%d - no match", BoardAddress, getMyAddr() );
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: BAddr:%d, myAddr:%d - no match", BoardAddress, getMyAddr() );
|
||||
return;
|
||||
}
|
||||
}
|
||||
DB_PRINT("eDP: Address Matched");
|
||||
DB_PRINT("[dcc_execDccProcessor] eDP: Address Matched");
|
||||
}
|
||||
|
||||
|
||||
|
@ -1491,6 +1660,7 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
#ifndef NODEMCUDCC
|
||||
NmraDcc::NmraDcc()
|
||||
{
|
||||
}
|
||||
|
@ -1538,9 +1708,14 @@ void NmraDcc::initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, u
|
|||
{
|
||||
init(ManufacturerId, VersionId, Flags | FLAGS_DCC_ACCESSORY_DECODER, OpsModeAddressBaseCV);
|
||||
}
|
||||
#endif //#ifndef NODEMCUDCC
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
#ifdef NODEMCUDCC
|
||||
void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV)
|
||||
#else
|
||||
void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV )
|
||||
#endif
|
||||
{
|
||||
#if defined(ESP8266)
|
||||
EEPROM.begin(MAXCV);
|
||||
|
@ -1564,13 +1739,24 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
|||
DccProcState.inAccDecDCCAddrNextReceivedMode = 0;
|
||||
|
||||
ISREdge = RISING;
|
||||
#ifdef NODEMCUDCC
|
||||
DccProcState.IntPin = pin;
|
||||
DccProcState.IntBitmask = 1 << pin_num[pin];
|
||||
#else
|
||||
// level checking to detect false IRQ's fired by glitches
|
||||
ISRLevel = DccProcState.ExtIntMask;
|
||||
ISRChkMask = DccProcState.ExtIntMask;
|
||||
#endif
|
||||
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
|
||||
#elif defined(NODEMCUDCC)
|
||||
platform_gpio_mode(pin, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
|
||||
NODE_DBG("[dcc_setup] platform_gpio_register_intr_hook - pin: %d, mask: %d\n", DccProcState.IntPin, DccProcState.IntBitmask);
|
||||
platform_gpio_register_intr_hook(DccProcState.IntBitmask, InterruptHandler);
|
||||
|
||||
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), RISING);
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, RISING);
|
||||
#endif
|
||||
|
@ -1593,6 +1779,7 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
|||
notifyCVResetFactoryDefault();
|
||||
}
|
||||
|
||||
#ifndef NODEMCUDCC
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
uint8_t NmraDcc::getCV( uint16_t CV )
|
||||
{
|
||||
|
@ -1662,9 +1849,14 @@ void NmraDcc::setAccDecDCCAddrNextReceived(uint8_t enable)
|
|||
{
|
||||
DccProcState.inAccDecDCCAddrNextReceivedMode = enable;
|
||||
}
|
||||
#endif //#ifndef NODEMCUDCC
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
#ifdef NODEMCUDCC
|
||||
static uint8_t process (os_param_t param, uint8_t prio)
|
||||
#else
|
||||
uint8_t NmraDcc::process()
|
||||
#endif
|
||||
{
|
||||
if( DccProcState.inServiceMode )
|
||||
{
|
||||
|
@ -1679,6 +1871,8 @@ uint8_t NmraDcc::process()
|
|||
// We need to do this check with interrupts disabled
|
||||
#ifdef ESP32
|
||||
portENTER_CRITICAL(&mux);
|
||||
#elif defined(NODEMCUDCC)
|
||||
ETS_GPIO_INTR_DISABLE();
|
||||
#else
|
||||
noInterrupts();
|
||||
#endif
|
||||
|
@ -1687,6 +1881,8 @@ uint8_t NmraDcc::process()
|
|||
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL(&mux);
|
||||
#elif defined(NODEMCUDCC)
|
||||
ETS_GPIO_INTR_ENABLE();
|
||||
#else
|
||||
interrupts();
|
||||
#endif
|
||||
|
@ -1699,9 +1895,26 @@ uint8_t NmraDcc::process()
|
|||
|
||||
if( notifyDccMsg ) notifyDccMsg( &Msg );
|
||||
|
||||
NODE_DBG("[dcc_process] Size: %d\tPreambleBits: %d\t%d, %d, %d, %d, %d, %d\n",
|
||||
Msg.Size, Msg.PreambleBits, Msg.Data[0], Msg.Data[1], Msg.Data[2], Msg.Data[3], Msg.Data[4], Msg.Data[5]);
|
||||
execDccProcessor( &Msg );
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
return 0 ;
|
||||
};
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
void dcc_close()
|
||||
{
|
||||
NODE_DBG("[dcc_close]\n");
|
||||
platform_gpio_mode(DccProcState.IntPin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
|
||||
}
|
||||
|
||||
void dcc_init()
|
||||
{
|
||||
NODE_DBG("[dcc_init]\n");
|
||||
DataReady_taskid = task_get_id((task_callback_t) process);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -39,6 +39,13 @@
|
|||
//
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
// NodeMCU Lua port by @voborsky
|
||||
|
||||
#define NODEMCUDCC
|
||||
// #define NODE_DEBUG
|
||||
// #define DCC_DEBUG
|
||||
// #define DCC_DBGVAR
|
||||
|
||||
// Uncomment the following Line to Enable Service Mode CV Programming
|
||||
#define NMRA_DCC_PROCESS_SERVICEMODE
|
||||
|
||||
|
@ -50,6 +57,7 @@
|
|||
|
||||
#if defined(ARDUINO) && ARDUINO >= 100
|
||||
#include "Arduino.h"
|
||||
#elif defined(NODEMCUDCC)
|
||||
#else
|
||||
#include "WProgram.h"
|
||||
#endif
|
||||
|
@ -204,9 +212,14 @@ typedef struct countOf_t {
|
|||
unsigned long Err;
|
||||
}countOf_t ;
|
||||
|
||||
#ifdef NODEMCUDCC
|
||||
countOf_t countOf;
|
||||
#else
|
||||
extern struct countOf_t countOf;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef NODEMCUDCC
|
||||
class NmraDcc
|
||||
{
|
||||
private:
|
||||
|
@ -214,6 +227,7 @@ class NmraDcc
|
|||
|
||||
public:
|
||||
NmraDcc();
|
||||
#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
|
||||
|
@ -225,7 +239,7 @@ class NmraDcc
|
|||
// Flag Bits that are cloned from CV29 relating the DCC Accessory Decoder
|
||||
#define FLAGS_CV29_BITS (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
|
||||
|
||||
|
||||
#ifndef NODEMCUDCC
|
||||
/*+
|
||||
* pin() is called from setup() and sets up the pin used to receive DCC packets.
|
||||
*
|
||||
|
@ -403,6 +417,32 @@ void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
|||
|
||||
};
|
||||
|
||||
#else
|
||||
#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
|
||||
#define CV_ACK_COMPLETE 14
|
||||
|
||||
|
||||
void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV);
|
||||
|
||||
|
||||
void dcc_close();
|
||||
|
||||
void dcc_init();
|
||||
#endif //#ifndef NODEMCUDCC
|
||||
|
||||
/************************************************************************************
|
||||
Call-back functions
|
||||
************************************************************************************/
|
||||
|
@ -608,7 +648,11 @@ extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
|||
* 1 - CV is valid.
|
||||
* 0 - CV is not valid.
|
||||
*/
|
||||
#ifdef NODEMCUDCC
|
||||
extern uint16_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((weak));
|
||||
#else
|
||||
extern uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((weak));
|
||||
#endif
|
||||
|
||||
/*+
|
||||
* notifyCVRead() Callback to read a CV.
|
||||
|
@ -622,10 +666,13 @@ extern uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) __attribute__ ((we
|
|||
* CV - CV number.
|
||||
*
|
||||
* Returns:
|
||||
* Value - Value of the CV.
|
||||
* Value - Value of the CV. Or a value > 255 to indicate error.
|
||||
*/
|
||||
#ifdef NODEMCUDCC
|
||||
extern uint16_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
|
||||
#else
|
||||
extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
|
||||
|
||||
#endif
|
||||
/*+
|
||||
* notifyCVWrite() Callback to write a value to a CV.
|
||||
* This is called when the library needs to write
|
||||
|
@ -639,10 +686,15 @@ extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
|
|||
* Value - Value of the CV.
|
||||
*
|
||||
* Returns:
|
||||
* Value - Value of the CV.
|
||||
* Value - Value of the CV. Or a value > 255 to signal error.
|
||||
*/
|
||||
#ifdef NODEMCUDCC
|
||||
extern uint16_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
#else
|
||||
extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
#endif
|
||||
|
||||
#ifndef NODEMCUDCC
|
||||
/*+
|
||||
* notifyIsSetCVReady() Callback to to determine if CVs can be written.
|
||||
* This is called when the library needs to determine
|
||||
|
@ -681,6 +733,7 @@ extern uint8_t notifyIsSetCVReady(void) __attribute__ ((weak));
|
|||
*/
|
||||
extern void notifyCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
extern void notifyDccCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
|
||||
#endif
|
||||
|
||||
/*+
|
||||
* notifyCVResetFactoryDefault() Called when CVs must be reset.
|
||||
|
@ -743,3 +796,4 @@ extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State
|
|||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// NodeMCU Lua port by @voborsky
|
||||
// NodeMCU Lua port by @voborsky, @pjsg
|
||||
// Module for handling NMRA DCC protocol
|
||||
// #define NODE_DEBUG
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
|||
#include "lauxlib.h"
|
||||
#include "platform.h"
|
||||
#include "driver/NmraDcc.h"
|
||||
#include "hw_timer.h"
|
||||
|
||||
#ifdef LUA_USE_MODULES_DCC
|
||||
#if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE)
|
||||
|
@ -16,6 +17,8 @@
|
|||
#define TYPE "Type"
|
||||
#define OPERATION "Operation"
|
||||
|
||||
#define TIMER_OWNER (('D' << 8) + 'C')
|
||||
|
||||
static inline void register_lua_cb(lua_State* L,int* cb_ref){
|
||||
int ref=luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
if( *cb_ref != LUA_NOREF){
|
||||
|
@ -33,6 +36,15 @@ static inline void unregister_lua_cb(lua_State* L, int* cb_ref){
|
|||
|
||||
static int notify_cb = LUA_NOREF;
|
||||
static int CV_cb = LUA_NOREF;
|
||||
static int CV_ref = LUA_NOREF;
|
||||
static int8_t ackPin;
|
||||
static char ackInProgress;
|
||||
static platform_task_handle_t tasknumber;
|
||||
|
||||
typedef struct {
|
||||
uint16_t cv;
|
||||
uint8_t value;
|
||||
} CVData;
|
||||
|
||||
// DCC commands
|
||||
|
||||
|
@ -154,8 +166,11 @@ void notifyServiceMode(bool InServiceMode){
|
|||
|
||||
// CV handling
|
||||
|
||||
uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) {
|
||||
uint16_t notifyCVValid( uint16_t CV, uint8_t Writable ) {
|
||||
lua_State* L = lua_getstate();
|
||||
if (CV_ref != LUA_NOREF) {
|
||||
return 1;
|
||||
}
|
||||
if(notify_cb == LUA_NOREF)
|
||||
return 0;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||
|
@ -165,13 +180,45 @@ uint8_t notifyCVValid( uint16_t CV, uint8_t Writable ) {
|
|||
cbAddFieldInteger(L, Writable, "Writable");
|
||||
if (luaL_pcallx(L, 2, 1) != LUA_OK)
|
||||
return 0;
|
||||
uint8 result = lua_tointeger(L, -1);
|
||||
uint8 result = lua_tointeger(L, -1) || lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t notifyCVRead( uint16_t CV) {
|
||||
static int doDirectCVRead(lua_State *L) {
|
||||
CVData *data = (CVData*) lua_touserdata(L, -1);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_ref);
|
||||
lua_pushinteger(L, data->cv);
|
||||
lua_gettable(L, -2);
|
||||
data->value = (uint8_t) luaL_checkinteger(L, -1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doDirectCVWrite(lua_State *L) {
|
||||
CVData *data = (CVData*) lua_touserdata(L, -1);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_ref);
|
||||
lua_pushinteger(L, data->cv);
|
||||
lua_pushinteger(L, data->value);
|
||||
lua_settable(L, -3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t notifyCVRead( uint16_t CV) {
|
||||
lua_State* L = lua_getstate();
|
||||
if (CV_ref != LUA_NOREF) {
|
||||
CVData data;
|
||||
data.cv = CV;
|
||||
|
||||
lua_pushcfunction(L, doDirectCVRead);
|
||||
lua_pushlightuserdata(L, &data);
|
||||
if (lua_pcall(L, 1, 0, 0)) {
|
||||
// An error.
|
||||
lua_pop(L, 1);
|
||||
return 256;
|
||||
}
|
||||
|
||||
return data.value;
|
||||
}
|
||||
if(notify_cb == LUA_NOREF)
|
||||
return 0;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||
|
@ -185,8 +232,23 @@ uint8_t notifyCVRead( uint16_t CV) {
|
|||
return result;
|
||||
}
|
||||
|
||||
uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) {
|
||||
uint16_t notifyCVWrite( uint16_t CV, uint8_t Value) {
|
||||
lua_State* L = lua_getstate();
|
||||
if (CV_ref != LUA_NOREF) {
|
||||
CVData data;
|
||||
data.cv = CV;
|
||||
data.value = Value;
|
||||
|
||||
lua_pushcfunction(L, doDirectCVWrite);
|
||||
lua_pushlightuserdata(L, &data);
|
||||
if (lua_pcall(L, 1, 0, 0)) {
|
||||
// An error.
|
||||
lua_pop(L, 1);
|
||||
return 256;
|
||||
}
|
||||
|
||||
return data.value;
|
||||
}
|
||||
if(notify_cb == LUA_NOREF)
|
||||
return 0;
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||
|
@ -194,53 +256,106 @@ uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) {
|
|||
lua_newtable(L);
|
||||
cbAddFieldInteger(L, CV, "CV");
|
||||
cbAddFieldInteger(L, Value, "Value");
|
||||
luaL_pcallx(L, 2, 0);
|
||||
luaL_pcallx(L, 2, 1);
|
||||
// Return is an optional value (if integer). If nil, then it is old style
|
||||
if (!lua_isnil(L, -1)) {
|
||||
Value = lua_tointeger(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
return Value;
|
||||
}
|
||||
|
||||
void notifyCVResetFactoryDefault(void) {
|
||||
static void notifyCVNoArgs(int callback_type) {
|
||||
lua_State* L = lua_getstate();
|
||||
if(notify_cb == LUA_NOREF)
|
||||
if (notify_cb == LUA_NOREF) {
|
||||
return;
|
||||
}
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
|
||||
lua_pushinteger(L, CV_RESET);
|
||||
lua_pushinteger(L, callback_type);
|
||||
luaL_pcallx(L, 1, 0);
|
||||
}
|
||||
|
||||
void notifyCVResetFactoryDefault(void) {
|
||||
notifyCVNoArgs(CV_RESET);
|
||||
}
|
||||
|
||||
void notifyCVAck(void) {
|
||||
// Invoked when we should generate an ack pulse (if possible)
|
||||
if (ackPin >= 0 && !ackInProgress) {
|
||||
// Duration is 6ms +/- 1ms
|
||||
platform_hw_timer_arm_us(TIMER_OWNER, 6 * 1000);
|
||||
platform_gpio_write(ackPin, 1);
|
||||
ackInProgress = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void ICACHE_RAM_ATTR cvAckComplete(os_param_t param) {
|
||||
// Invoked when we should end the ack pulse
|
||||
ackInProgress = FALSE;
|
||||
platform_gpio_write(ackPin, 0);
|
||||
if (CV_ref == LUA_NOREF) {
|
||||
platform_post_high(tasknumber, CV_ACK_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
int narg = 1;
|
||||
uint8_t pin = luaL_checkinteger(L, narg);
|
||||
luaL_argcheck(L, platform_gpio_exists(pin) && pin>0, narg, "Invalid interrupt pin");
|
||||
narg++;
|
||||
|
||||
if (lua_type(L, 2) == LUA_TFUNCTION)
|
||||
{
|
||||
lua_pushvalue(L, 2);
|
||||
register_lua_cb(L, ¬ify_cb);
|
||||
int8_t ackpin = -1;
|
||||
|
||||
if (lua_type(L, narg) == LUA_TNUMBER) {
|
||||
ackpin = luaL_checkinteger(L, narg);
|
||||
luaL_argcheck(L, platform_gpio_exists(ackpin), narg, "Invalid ack pin");
|
||||
narg++;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (lua_isfunction(L, narg)) {
|
||||
lua_pushvalue(L, narg++);
|
||||
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);
|
||||
uint8_t ManufacturerId = luaL_checkinteger(L, narg++);
|
||||
uint8_t VersionId = luaL_checkinteger(L, narg++);
|
||||
uint8_t Flags = luaL_checkinteger(L, narg++);
|
||||
uint8_t OpsModeAddressBaseCV = luaL_checkinteger(L, narg++);
|
||||
|
||||
if (lua_type(L, 7) == LUA_TFUNCTION)
|
||||
{
|
||||
lua_pushvalue(L, 7);
|
||||
register_lua_cb(L, &CV_cb);
|
||||
if (lua_istable(L, narg)) {
|
||||
// This is the raw CV table
|
||||
lua_pushvalue(L, narg++);
|
||||
register_lua_cb(L, &CV_ref);
|
||||
} else {
|
||||
unregister_lua_cb(L, &CV_ref);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (lua_isfunction(L, narg)) {
|
||||
lua_pushvalue(L, narg++);
|
||||
register_lua_cb(L, &CV_cb);
|
||||
} else {
|
||||
unregister_lua_cb(L, &CV_cb);
|
||||
}
|
||||
|
||||
if (ackpin >= 0) {
|
||||
// Now start things up
|
||||
if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, FALSE)) {
|
||||
// Failed to init the timer
|
||||
luaL_error(L, "Unable to initialize timer");
|
||||
}
|
||||
|
||||
platform_hw_timer_set_func(TIMER_OWNER, cvAckComplete, 0);
|
||||
|
||||
platform_gpio_write(ackpin, 0);
|
||||
platform_gpio_mode(ackpin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
|
||||
}
|
||||
|
||||
NODE_DBG("[dcc_lua_setup] Enabling interrupt on PIN %d\n", pin);
|
||||
ackPin = ackpin;
|
||||
ackInProgress = FALSE;
|
||||
dcc_setup(pin, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV );
|
||||
|
||||
return 0;
|
||||
|
@ -249,13 +364,22 @@ static int dcc_lua_setup(lua_State* L) {
|
|||
static int dcc_lua_close(lua_State* L) {
|
||||
dcc_close();
|
||||
unregister_lua_cb(L, ¬ify_cb);
|
||||
unregister_lua_cb(L, &CV_cb);
|
||||
unregister_lua_cb(L, &CV_ref);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int luaopen_dcc( lua_State *L ) {
|
||||
NODE_DBG("[dcc_luaopen]\n");
|
||||
static void dcc_task(os_param_t param, uint8_t prio)
|
||||
{
|
||||
(void) prio;
|
||||
|
||||
notifyCVNoArgs(param);
|
||||
}
|
||||
|
||||
int dcc_lua_init( lua_State *L ) {
|
||||
NODE_DBG("[dcc_lua_init]\n");
|
||||
dcc_init();
|
||||
//DccRx.lua_cb_ref = LUA_NOREF;
|
||||
tasknumber = platform_task_get_id(dcc_task);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -278,6 +402,7 @@ LROT_BEGIN(dcc, NULL, 0)
|
|||
LROT_NUMENTRY( CV_READ, CV_READ )
|
||||
LROT_NUMENTRY( CV_WRITE, CV_WRITE )
|
||||
LROT_NUMENTRY( CV_RESET, CV_RESET )
|
||||
LROT_NUMENTRY( CV_ACK_COMPLETE, CV_ACK_COMPLETE )
|
||||
|
||||
LROT_NUMENTRY( MAN_ID_JMRI, MAN_ID_JMRI)
|
||||
LROT_NUMENTRY( MAN_ID_DIY, MAN_ID_DIY)
|
||||
|
@ -289,4 +414,4 @@ LROT_BEGIN(dcc, NULL, 0)
|
|||
LROT_NUMENTRY( FLAGS_DCC_ACCESSORY_DECODER, FLAGS_DCC_ACCESSORY_DECODER )
|
||||
LROT_END(dcc, NULL, 0)
|
||||
|
||||
NODEMCU_MODULE(DCC, "dcc", dcc, luaopen_dcc);
|
||||
NODEMCU_MODULE(DCC, "dcc", dcc, dcc_lua_init);
|
||||
|
|
|
@ -7,23 +7,25 @@ The dcc module implements decoder of the [National Model Railroad Association](h
|
|||
|
||||
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).
|
||||
The module is based on the project NmraDcc [https://github.com/mrrwa/NmraDcc](https://github.com/mrrwa/NmraDcc) by Alex Shepherd (@kiwi64ajs). The module is based on latest commit from Oct 2020, commit [7e3b3e346991d74926e6c4f6fb46e27156b08578](https://github.com/mrrwa/NmraDcc/tree/7e3b3e346991d74926e6c4f6fb46e27156b08578).
|
||||
|
||||
## dcc.setup()
|
||||
|
||||
Initializes the dcc module and links callback functions.
|
||||
|
||||
#### Syntax
|
||||
`dcc.setup(DCC_command, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV, CV_callback)`
|
||||
`dcc.setup(Pin, [AckPin, ] DCC_command, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV [, CV_table] [, CV_callback])`
|
||||
|
||||
#### Parameters
|
||||
- `Pin` the GPIO pin number connected to the DCC detector (must be interrupt capable pin).
|
||||
- `AckPin` the (optional) GPIO pin number connected to the ACK mechanism. Will be set HIGH to signal an ACK.
|
||||
- `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` 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_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`.
|
||||
|
@ -35,11 +37,13 @@ Initializes the dcc module and links callback functions.
|
|||
- `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_table` The CV values will be directly accessed from this table. metamethods will be invoked if needed. Any errors thrown will cause the CV to be considered invalid. Using this option will prevent `CV_VALID`, `CV_READ`, `CV_WRITE` and `CV_ACK_COMPLETE` from happening.
|
||||
- `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_VALID` to determine if a given CV is valid and (possibly) writable. This callback must determine if a CV is readable or writable and return the appropriate value(0/1/true/false). The `param` collection has members `CV` and `Writable`.
|
||||
- `dcc.CV_READ` to read a CV. This callback must return the value of the CV. The `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. The `param` collection has members `CV` and `Value`. Ideally, the final value should be returned -- this may differ from the requested value.
|
||||
- `dcc.CV_RESET` Called when CVs must be reset to their factory defaults.
|
||||
- `dcc.CV_ACK_COMPLETE` Called when an ACK pulse has finished being sent. Only invoked if `AckPin` is specified.
|
||||
|
||||
#### Returns
|
||||
`nil`
|
||||
|
|
Loading…
Reference in New Issue