123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417 |
- // NodeMCU Lua port by @voborsky, @pjsg
- // Module for handling NMRA DCC protocol
- // #define NODE_DEBUG
- #include "module.h"
- #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)
- #error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using DCC module
- #endif
- #endif
- #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){
- 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;
- 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
- 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");
- luaL_pcallx(L, 2, 0);
- }
- void notifyDccIdle(void) {
- lua_State* L = lua_getstate();
- cbInit(L, DCC_IDLE);
- luaL_pcallx(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");
- luaL_pcallx(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");
- luaL_pcallx(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");
- luaL_pcallx(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");
- luaL_pcallx(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");
- luaL_pcallx(L, 2, 0);
- }
- void notifyDccAccBoardAddrSet( uint16_t BoardAddr) {
- lua_State* L = lua_getstate();
- cbInit(L, DCC_ACCESSORY);
- cbAddFieldInteger(L, BoardAddr, "BoardAddr");
- luaL_pcallx(L, 2, 0);
- }
- void notifyDccAccOutputAddrSet( uint16_t Addr) {
- lua_State* L = lua_getstate();
- cbInit(L, DCC_ACCESSORY);
- cbAddFieldInteger(L, Addr, "Addr");
- luaL_pcallx(L, 2, 0);
- }
- void notifyDccSigOutputState( uint16_t Addr, uint8_t State) {
- lua_State* L = lua_getstate();
- cbInit(L, DCC_ACCESSORY);
- cbAddFieldInteger(L, State, "State");
- luaL_pcallx(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);
- }
- luaL_pcallx(L, 2, 0);
- }
- void notifyServiceMode(bool InServiceMode){
- lua_State* L = lua_getstate();
- cbInit(L, DCC_SERVICEMODE);
- cbAddFieldInteger(L, InServiceMode, "InServiceMode");
- luaL_pcallx(L, 2, 0);
- }
- // CV handling
- 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);
- lua_pushinteger(L, CV_VALID);
- lua_newtable(L);
- cbAddFieldInteger(L, CV, "CV");
- cbAddFieldInteger(L, Writable, "Writable");
- if (luaL_pcallx(L, 2, 1) != LUA_OK)
- return 0;
- uint8 result = lua_tointeger(L, -1) || lua_toboolean(L, -1);
- lua_pop(L, 1);
- return result;
- }
- 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);
- lua_pushinteger(L, CV_READ);
- lua_newtable(L);
- cbAddFieldInteger(L, CV, "CV");
- if (luaL_pcallx(L, 2, 1) != LUA_OK)
- return 0;;
- uint8 result = lua_tointeger(L, -1);
- lua_pop(L, 1);
- return result;
- }
- 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);
- lua_pushinteger(L, CV_WRITE);
- lua_newtable(L);
- cbAddFieldInteger(L, CV, "CV");
- cbAddFieldInteger(L, Value, "Value");
- 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;
- }
- static void notifyCVNoArgs(int callback_type) {
- lua_State* L = lua_getstate();
- if (notify_cb == LUA_NOREF) {
- return;
- }
- lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
- 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");
- int narg = 1;
- uint8_t pin = luaL_checkinteger(L, narg);
- luaL_argcheck(L, platform_gpio_exists(pin) && pin>0, narg, "Invalid interrupt pin");
- narg++;
- 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++;
- }
-
- 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, narg++);
- uint8_t VersionId = luaL_checkinteger(L, narg++);
- uint8_t Flags = luaL_checkinteger(L, narg++);
- uint8_t OpsModeAddressBaseCV = luaL_checkinteger(L, narg++);
- 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);
- }
- 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;
- }
- 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;
- }
- 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();
- tasknumber = platform_task_get_id(dcc_task);
- return 0;
- }
- // Module function map
- LROT_BEGIN(dcc, NULL, 0)
- 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( CV_ACK_COMPLETE, CV_ACK_COMPLETE )
- 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, dcc_lua_init);
|