291 lines
8.0 KiB
C
291 lines
8.0 KiB
C
|
// 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);
|