dcc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. // NodeMCU Lua port by @voborsky, @pjsg
  2. // Module for handling NMRA DCC protocol
  3. // #define NODE_DEBUG
  4. #include "module.h"
  5. #include "lauxlib.h"
  6. #include "platform.h"
  7. #include "driver/NmraDcc.h"
  8. #include "hw_timer.h"
  9. #ifdef LUA_USE_MODULES_DCC
  10. #if !defined(GPIO_INTERRUPT_ENABLE) || !defined(GPIO_INTERRUPT_HOOK_ENABLE)
  11. #error Must have GPIO_INTERRUPT and GPIO_INTERRUPT_HOOK if using DCC module
  12. #endif
  13. #endif
  14. #define TYPE "Type"
  15. #define OPERATION "Operation"
  16. #define TIMER_OWNER (('D' << 8) + 'C')
  17. static inline void register_lua_cb(lua_State* L,int* cb_ref){
  18. int ref=luaL_ref(L, LUA_REGISTRYINDEX);
  19. if( *cb_ref != LUA_NOREF){
  20. luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
  21. }
  22. *cb_ref = ref;
  23. }
  24. static inline void unregister_lua_cb(lua_State* L, int* cb_ref){
  25. if(*cb_ref != LUA_NOREF){
  26. luaL_unref(L, LUA_REGISTRYINDEX, *cb_ref);
  27. *cb_ref = LUA_NOREF;
  28. }
  29. }
  30. static int notify_cb = LUA_NOREF;
  31. static int CV_cb = LUA_NOREF;
  32. static int CV_ref = LUA_NOREF;
  33. static int8_t ackPin;
  34. static char ackInProgress;
  35. static platform_task_handle_t tasknumber;
  36. typedef struct {
  37. uint16_t cv;
  38. uint8_t value;
  39. } CVData;
  40. // DCC commands
  41. void cbInit(lua_State* L, uint16_t command) {
  42. if(notify_cb == LUA_NOREF)
  43. return;
  44. lua_rawgeti(L, LUA_REGISTRYINDEX, notify_cb);
  45. lua_pushinteger(L, command);
  46. lua_newtable(L);
  47. }
  48. void cbAddFieldInteger(lua_State* L, uint16_t Value, char *Field) {
  49. lua_pushinteger(L, Value);
  50. lua_setfield(L, -2, Field);
  51. }
  52. void notifyDccReset(uint8_t hardReset ) {
  53. lua_State* L = lua_getstate();
  54. cbInit(L, DCC_RESET);
  55. cbAddFieldInteger(L, hardReset, "hardReset");
  56. luaL_pcallx(L, 2, 0);
  57. }
  58. void notifyDccIdle(void) {
  59. lua_State* L = lua_getstate();
  60. cbInit(L, DCC_IDLE);
  61. luaL_pcallx(L, 2, 0);
  62. }
  63. void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) {
  64. lua_State* L = lua_getstate();
  65. cbInit(L, DCC_SPEED);
  66. cbAddFieldInteger(L, Addr, "Addr");
  67. cbAddFieldInteger(L, AddrType, "AddrType");
  68. cbAddFieldInteger(L, Speed, "Speed");
  69. cbAddFieldInteger(L, Dir, "Dir");
  70. cbAddFieldInteger(L, SpeedSteps, "SpeedSteps");
  71. luaL_pcallx(L, 2, 0);
  72. }
  73. void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) {
  74. lua_State* L = lua_getstate();
  75. cbInit(L, DCC_SPEED_RAW);
  76. cbAddFieldInteger(L, Addr, "Addr");
  77. cbAddFieldInteger(L, AddrType, "AddrType");
  78. cbAddFieldInteger(L, Raw, "Raw");
  79. luaL_pcallx(L, 2, 0);
  80. }
  81. void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) {
  82. lua_State* L = lua_getstate();
  83. cbInit(L, DCC_FUNC);
  84. cbAddFieldInteger(L, Addr, "Addr");
  85. cbAddFieldInteger(L, AddrType, "AddrType");
  86. cbAddFieldInteger(L, FuncGrp, "FuncGrp");
  87. cbAddFieldInteger(L, FuncState, "FuncState");
  88. luaL_pcallx(L, 2, 0);
  89. }
  90. void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) {
  91. lua_State* L = lua_getstate();
  92. cbInit(L, DCC_TURNOUT);
  93. cbAddFieldInteger(L, BoardAddr, "BoardAddr");
  94. cbAddFieldInteger(L, OutputPair, "OutputPair");
  95. cbAddFieldInteger(L, Direction, "Direction");
  96. cbAddFieldInteger(L, OutputPower, "OutputPower");
  97. luaL_pcallx(L, 2, 0);
  98. }
  99. void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) {
  100. lua_State* L = lua_getstate();
  101. cbInit(L, DCC_TURNOUT);
  102. cbAddFieldInteger(L, Addr, "Addr");
  103. cbAddFieldInteger(L, Direction, "Direction");
  104. cbAddFieldInteger(L, OutputPower, "OutputPower");
  105. luaL_pcallx(L, 2, 0);
  106. }
  107. void notifyDccAccBoardAddrSet( uint16_t BoardAddr) {
  108. lua_State* L = lua_getstate();
  109. cbInit(L, DCC_ACCESSORY);
  110. cbAddFieldInteger(L, BoardAddr, "BoardAddr");
  111. luaL_pcallx(L, 2, 0);
  112. }
  113. void notifyDccAccOutputAddrSet( uint16_t Addr) {
  114. lua_State* L = lua_getstate();
  115. cbInit(L, DCC_ACCESSORY);
  116. cbAddFieldInteger(L, Addr, "Addr");
  117. luaL_pcallx(L, 2, 0);
  118. }
  119. void notifyDccSigOutputState( uint16_t Addr, uint8_t State) {
  120. lua_State* L = lua_getstate();
  121. cbInit(L, DCC_ACCESSORY);
  122. cbAddFieldInteger(L, State, "State");
  123. luaL_pcallx(L, 2, 0);
  124. }
  125. void notifyDccMsg( DCC_MSG * Msg ) {
  126. lua_State* L = lua_getstate();
  127. cbInit(L, DCC_RAW);
  128. cbAddFieldInteger(L, Msg->Size, "Size");
  129. cbAddFieldInteger(L, Msg->PreambleBits, "PreambleBits");
  130. char field[8];
  131. for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) {
  132. ets_sprintf(field, "Data%d", i);
  133. cbAddFieldInteger(L, Msg->Data[i], field);
  134. }
  135. luaL_pcallx(L, 2, 0);
  136. }
  137. void notifyServiceMode(bool InServiceMode){
  138. lua_State* L = lua_getstate();
  139. cbInit(L, DCC_SERVICEMODE);
  140. cbAddFieldInteger(L, InServiceMode, "InServiceMode");
  141. luaL_pcallx(L, 2, 0);
  142. }
  143. // CV handling
  144. uint16_t notifyCVValid( uint16_t CV, uint8_t Writable ) {
  145. lua_State* L = lua_getstate();
  146. if (CV_ref != LUA_NOREF) {
  147. return 1;
  148. }
  149. if(notify_cb == LUA_NOREF)
  150. return 0;
  151. lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
  152. lua_pushinteger(L, CV_VALID);
  153. lua_newtable(L);
  154. cbAddFieldInteger(L, CV, "CV");
  155. cbAddFieldInteger(L, Writable, "Writable");
  156. if (luaL_pcallx(L, 2, 1) != LUA_OK)
  157. return 0;
  158. uint8 result = lua_tointeger(L, -1) || lua_toboolean(L, -1);
  159. lua_pop(L, 1);
  160. return result;
  161. }
  162. static int doDirectCVRead(lua_State *L) {
  163. CVData *data = (CVData*) lua_touserdata(L, -1);
  164. lua_rawgeti(L, LUA_REGISTRYINDEX, CV_ref);
  165. lua_pushinteger(L, data->cv);
  166. lua_gettable(L, -2);
  167. data->value = (uint8_t) luaL_checkinteger(L, -1);
  168. return 0;
  169. }
  170. static int doDirectCVWrite(lua_State *L) {
  171. CVData *data = (CVData*) lua_touserdata(L, -1);
  172. lua_rawgeti(L, LUA_REGISTRYINDEX, CV_ref);
  173. lua_pushinteger(L, data->cv);
  174. lua_pushinteger(L, data->value);
  175. lua_settable(L, -3);
  176. return 0;
  177. }
  178. uint16_t notifyCVRead( uint16_t CV) {
  179. lua_State* L = lua_getstate();
  180. if (CV_ref != LUA_NOREF) {
  181. CVData data;
  182. data.cv = CV;
  183. lua_pushcfunction(L, doDirectCVRead);
  184. lua_pushlightuserdata(L, &data);
  185. if (lua_pcall(L, 1, 0, 0)) {
  186. // An error.
  187. lua_pop(L, 1);
  188. return 256;
  189. }
  190. return data.value;
  191. }
  192. if(notify_cb == LUA_NOREF)
  193. return 0;
  194. lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
  195. lua_pushinteger(L, CV_READ);
  196. lua_newtable(L);
  197. cbAddFieldInteger(L, CV, "CV");
  198. if (luaL_pcallx(L, 2, 1) != LUA_OK)
  199. return 0;;
  200. uint8 result = lua_tointeger(L, -1);
  201. lua_pop(L, 1);
  202. return result;
  203. }
  204. uint16_t notifyCVWrite( uint16_t CV, uint8_t Value) {
  205. lua_State* L = lua_getstate();
  206. if (CV_ref != LUA_NOREF) {
  207. CVData data;
  208. data.cv = CV;
  209. data.value = Value;
  210. lua_pushcfunction(L, doDirectCVWrite);
  211. lua_pushlightuserdata(L, &data);
  212. if (lua_pcall(L, 1, 0, 0)) {
  213. // An error.
  214. lua_pop(L, 1);
  215. return 256;
  216. }
  217. return data.value;
  218. }
  219. if(notify_cb == LUA_NOREF)
  220. return 0;
  221. lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
  222. lua_pushinteger(L, CV_WRITE);
  223. lua_newtable(L);
  224. cbAddFieldInteger(L, CV, "CV");
  225. cbAddFieldInteger(L, Value, "Value");
  226. luaL_pcallx(L, 2, 1);
  227. // Return is an optional value (if integer). If nil, then it is old style
  228. if (!lua_isnil(L, -1)) {
  229. Value = lua_tointeger(L, -1);
  230. }
  231. lua_pop(L, 1);
  232. return Value;
  233. }
  234. static void notifyCVNoArgs(int callback_type) {
  235. lua_State* L = lua_getstate();
  236. if (notify_cb == LUA_NOREF) {
  237. return;
  238. }
  239. lua_rawgeti(L, LUA_REGISTRYINDEX, CV_cb);
  240. lua_pushinteger(L, callback_type);
  241. luaL_pcallx(L, 1, 0);
  242. }
  243. void notifyCVResetFactoryDefault(void) {
  244. notifyCVNoArgs(CV_RESET);
  245. }
  246. void notifyCVAck(void) {
  247. // Invoked when we should generate an ack pulse (if possible)
  248. if (ackPin >= 0 && !ackInProgress) {
  249. // Duration is 6ms +/- 1ms
  250. platform_hw_timer_arm_us(TIMER_OWNER, 6 * 1000);
  251. platform_gpio_write(ackPin, 1);
  252. ackInProgress = TRUE;
  253. }
  254. }
  255. static void ICACHE_RAM_ATTR cvAckComplete(os_param_t param) {
  256. // Invoked when we should end the ack pulse
  257. ackInProgress = FALSE;
  258. platform_gpio_write(ackPin, 0);
  259. if (CV_ref == LUA_NOREF) {
  260. platform_post_high(tasknumber, CV_ACK_COMPLETE);
  261. }
  262. }
  263. static int dcc_lua_setup(lua_State* L) {
  264. NODE_DBG("[dcc_lua_setup]\n");
  265. int narg = 1;
  266. uint8_t pin = luaL_checkinteger(L, narg);
  267. luaL_argcheck(L, platform_gpio_exists(pin) && pin>0, narg, "Invalid interrupt pin");
  268. narg++;
  269. int8_t ackpin = -1;
  270. if (lua_type(L, narg) == LUA_TNUMBER) {
  271. ackpin = luaL_checkinteger(L, narg);
  272. luaL_argcheck(L, platform_gpio_exists(ackpin), narg, "Invalid ack pin");
  273. narg++;
  274. }
  275. if (lua_isfunction(L, narg)) {
  276. lua_pushvalue(L, narg++);
  277. register_lua_cb(L, &notify_cb);
  278. } else {
  279. unregister_lua_cb(L, &notify_cb);
  280. }
  281. uint8_t ManufacturerId = luaL_checkinteger(L, narg++);
  282. uint8_t VersionId = luaL_checkinteger(L, narg++);
  283. uint8_t Flags = luaL_checkinteger(L, narg++);
  284. uint8_t OpsModeAddressBaseCV = luaL_checkinteger(L, narg++);
  285. if (lua_istable(L, narg)) {
  286. // This is the raw CV table
  287. lua_pushvalue(L, narg++);
  288. register_lua_cb(L, &CV_ref);
  289. } else {
  290. unregister_lua_cb(L, &CV_ref);
  291. }
  292. if (lua_isfunction(L, narg)) {
  293. lua_pushvalue(L, narg++);
  294. register_lua_cb(L, &CV_cb);
  295. } else {
  296. unregister_lua_cb(L, &CV_cb);
  297. }
  298. if (ackpin >= 0) {
  299. // Now start things up
  300. if (!platform_hw_timer_init(TIMER_OWNER, FRC1_SOURCE, FALSE)) {
  301. // Failed to init the timer
  302. luaL_error(L, "Unable to initialize timer");
  303. }
  304. platform_hw_timer_set_func(TIMER_OWNER, cvAckComplete, 0);
  305. platform_gpio_write(ackpin, 0);
  306. platform_gpio_mode(ackpin, PLATFORM_GPIO_OUTPUT, PLATFORM_GPIO_FLOAT);
  307. }
  308. NODE_DBG("[dcc_lua_setup] Enabling interrupt on PIN %d\n", pin);
  309. ackPin = ackpin;
  310. ackInProgress = FALSE;
  311. dcc_setup(pin, ManufacturerId, VersionId, Flags, OpsModeAddressBaseCV );
  312. return 0;
  313. }
  314. static int dcc_lua_close(lua_State* L) {
  315. dcc_close();
  316. unregister_lua_cb(L, &notify_cb);
  317. unregister_lua_cb(L, &CV_cb);
  318. unregister_lua_cb(L, &CV_ref);
  319. return 0;
  320. }
  321. static void dcc_task(os_param_t param, uint8_t prio)
  322. {
  323. (void) prio;
  324. notifyCVNoArgs(param);
  325. }
  326. int dcc_lua_init( lua_State *L ) {
  327. NODE_DBG("[dcc_lua_init]\n");
  328. dcc_init();
  329. tasknumber = platform_task_get_id(dcc_task);
  330. return 0;
  331. }
  332. // Module function map
  333. LROT_BEGIN(dcc, NULL, 0)
  334. LROT_FUNCENTRY( setup, dcc_lua_setup )
  335. LROT_FUNCENTRY( close, dcc_lua_close )
  336. LROT_NUMENTRY( DCC_RESET, DCC_RESET )
  337. LROT_NUMENTRY( DCC_IDLE, DCC_IDLE )
  338. LROT_NUMENTRY( DCC_SPEED, DCC_SPEED )
  339. LROT_NUMENTRY( DCC_SPEED_RAW, DCC_SPEED_RAW )
  340. LROT_NUMENTRY( DCC_FUNC, DCC_FUNC )
  341. LROT_NUMENTRY( DCC_TURNOUT, DCC_TURNOUT )
  342. LROT_NUMENTRY( DCC_ACCESSORY, DCC_ACCESSORY )
  343. LROT_NUMENTRY( DCC_RAW, DCC_RAW )
  344. LROT_NUMENTRY( DCC_SERVICEMODE, DCC_SERVICEMODE )
  345. LROT_NUMENTRY( CV_VALID, CV_VALID )
  346. LROT_NUMENTRY( CV_READ, CV_READ )
  347. LROT_NUMENTRY( CV_WRITE, CV_WRITE )
  348. LROT_NUMENTRY( CV_RESET, CV_RESET )
  349. LROT_NUMENTRY( CV_ACK_COMPLETE, CV_ACK_COMPLETE )
  350. LROT_NUMENTRY( MAN_ID_JMRI, MAN_ID_JMRI)
  351. LROT_NUMENTRY( MAN_ID_DIY, MAN_ID_DIY)
  352. LROT_NUMENTRY( MAN_ID_SILICON_RAILWAY, MAN_ID_SILICON_RAILWAY)
  353. LROT_NUMENTRY( FLAGS_MY_ADDRESS_ONLY, FLAGS_MY_ADDRESS_ONLY )
  354. LROT_NUMENTRY( FLAGS_AUTO_FACTORY_DEFAULT, FLAGS_AUTO_FACTORY_DEFAULT )
  355. LROT_NUMENTRY( FLAGS_OUTPUT_ADDRESS_MODE, FLAGS_OUTPUT_ADDRESS_MODE )
  356. LROT_NUMENTRY( FLAGS_DCC_ACCESSORY_DECODER, FLAGS_DCC_ACCESSORY_DECODER )
  357. LROT_END(dcc, NULL, 0)
  358. NODEMCU_MODULE(DCC, "dcc", dcc, dcc_lua_init);