Напишем модуль Lua для работы с последовательным портом:
Возьмём за основу старый исходник для проброса последовательного порта в сокет — suart. Почитаем про магию метатаблиц Lua и получим простой тестовый модуль.
myserial.c
// // serial library for use in Lua // // compile: // $(CC) -shared -fPIC -I./lua-5.2.1/src -o myserial.so myserial.c // // mips-openwrt-linux-gcc myserial.c -shared -fpic -I./lua-5.2.1/src -o myserial.so // #include <stdio.h> #include <stdlib.h> #include <unistd.h> // UNIX standard function definitions #include <fcntl.h> // File control definitions #include <errno.h> // Error number definitions #include <termios.h> // POSIX terminal control definitions # includerate==9600) { s->baudrate=B9600; } else if(s->rate==19200) { s->baudrate=B19200; } else if(s->rate==38400) { s->baudrate=B38400; } else if(s->rate==57600) { s->baudrate=B57600; } else if(s->rate==115200) { s->baudrate=B115200; } else { printf("[!] Error: bad baudrate: %d!\n", s->rate); return -1; } printf("[i] open: %s %d!\n", s->port_name, s->rate); s->fd = open(s->port_name, O_RDWR | O_NOCTTY | O_NONBLOCK); if (s->fd == -1) { printf("[!] Error: Unable to open serial port: %s\n", s->port_name); return -1; } else { fcntl(s->fd, F_SETFL, 0); tcgetattr(s->fd, &(s->options)); memset(&(s->options), 0, sizeof(s->options)); //cfmakeraw(&options); //---------------------------------------------------------------------------------------------------------------- // c_cflag s->options.c_cflag |= CS8; s->options.c_cflag &= ~PARENB; s->options.c_cflag &= ~CSTOPB; s->options.c_cflag |= CREAD; // Enable Receiver s->options.c_cflag |= CLOCAL; // Ignore modem control lines. //cfsetispeed(&options, baudrate); //cfsetospeed(&options, baudrate); s->options.c_cflag&=(~CBAUD); s->options.c_cflag |= s->baudrate; //options.c_ispeed = baudrate; //options.c_ospeed = baudrate; tcsetattr(s->fd, TCSAFLUSH, &(s->options)); //tcflush(tty_fd, TCIOFLUSH); } return 0; } // create serial object by open port static int ser_open(lua_State *L) { printf("[i] ser_open()\n"); const char *filename = luaL_checkstring(L, 1); // first param - dev name int rate = (int)luaL_checknumber(L, 2); // second param - baudrate LSerial *s = (LSerial *)lua_newuserdata(L, sizeof(LSerial)); luaL_getmetatable(L, LUA_MYSERIAL); lua_setmetatable(L, -2); if(s) { s->fd = -1; s->rate = rate; } if(filename) { strncpy(s->port_name, filename, sizeof(s->port_name)); myserial_open(s); } return 1; // new userdatum is already on the stack } // open method for myserial object static int serial_open(lua_State *L) { printf("[i] serial_open()\n"); LSerial *s = tomyserial(L); // object const char *filename = luaL_checkstring(L, 2); // first param - dev name int rate = (int)luaL_checknumber(L, 3); // second param - baudrate int res = -1; if( filename && s) { s->rate = rate; strncpy(s->port_name, filename, sizeof(s->port_name)); res = myserial_open(s); } lua_pushinteger(L, res); return 1; } // close method for myserial object static int serial_close(lua_State *L) { printf("[i] serial_close()\n"); LSerial *s = tomyserial(L); // object if(s) { if(s->fd>0) { close(s->fd); s->fd = -1; } } return 0; } static int serial_write(lua_State *L) { printf("[i] serial_write()\n"); LSerial *s = tomyserial(L); // object int res = 0; // number of bytes sent if(s && s->fd>0) { if(lua_isstring(L, 2)) { size_t dlen; char *data = (char *)lua_tolstring(L, 2, &dlen); if (dlen > 0) { res = write(s->fd, data, dlen); } } else if(lua_isnumber(L, 2)) { char data = (char)luaL_checknumber(L, 2); res = write(s->fd, &data, sizeof(data)); } } lua_pushinteger(L, res); return 1; } static int serial_read(lua_State *L) { printf("[i] serial_read()\n"); LSerial *s = tomyserial(L); // object int size = (int)luaL_checknumber(L, 2); int res = 0; char *data = NULL; if(s && s->fd>0 && size>0) { data = malloc(size); if(data) { res = read(s->fd, data, size); } else { printf("[!] Error: cant allocate memory!\n"); } } lua_pushlstring(L, data, res); // free allocated buffer if (data) free(data); return 1; } static int serial_wait(lua_State *L) { LSerial *s = tomyserial(L); // object int msec = (int)luaL_checknumber(L, 2); int res = 0; if(s && s->fd>0) { fd_set rfds; struct timeval tv; tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; if( tv.tv_usec > 1000000 ){ tv.tv_sec++; tv.tv_usec -= 1000000; } FD_ZERO(&rfds); FD_SET(s->fd, &rfds); res = select(s->fd+1, &rfds, NULL, NULL, &tv); } lua_pushinteger(L, res); return 1; } static int serial_available(lua_State *L) { printf("[i] serial_available()\n"); LSerial *s = tomyserial(L); // object int res = 0; if(s && s->fd>0) { ioctl(s->fd, FIONREAD, &res); } lua_pushinteger(L, res); return 1; } // special methods for metatable static int ser_gc (lua_State *L) { printf("[i] serial GC!\n"); return serial_close(L); } static int ser_tostring (lua_State *L) { LSerial *s = tomyserial(L); // object if(s) { if (s->fd>0) lua_pushfstring(L, "serial (%s %d)", s->port_name, s->rate); else lua_pushliteral(L, "serial (closed)"); } return 1; } // functions for 'myserial' library static const struct luaL_Reg myseriallib_m[] = { {"open", serial_open}, {"close", serial_close}, {"write", serial_write}, {"read", serial_read}, {"wait", serial_wait}, {"available", serial_available}, {"__gc", ser_gc}, {"__tostring", ser_tostring}, {NULL, NULL} }; // methods static const struct luaL_Reg myseriallib_f[] = { {"open", ser_open}, {NULL, NULL} }; LUAMOD_API int luaopen_myserial (lua_State *L) { //luaL_openlib(L, "myserial", myseriallib, 0); #if 0 luaL_newlib(L, myseriallib_m); // new module // createmeta luaL_newmetatable(L, LUA_MYSERIAL); // create metatable for file handles lua_pushvalue(L, -1); // push metatable lua_setfield(L, -2, "__index"); // metatable.__index = metatable luaL_setfuncs(L, myseriallib_f, 0); // add file methods to new metatable lua_pop(L, 1); // pop new metatable #else luaL_newmetatable(L, LUA_MYSERIAL); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); /* pushes the metatable */ lua_settable(L, -3); /* metatable.__index = metatable */ luaL_openlib(L, NULL, myseriallib_m, 0); luaL_openlib(L, LUA_MYSERIAL, myseriallib_f, 0); #endif return 1; }
собираем
mips-openwrt-linux-gcc myserial.c -shared -fpic -I./lua-5.2.1/src -o myserial.so
заливаем
scp myserial.so [email protected]:/root/
пишем тестовый скрипт
callmyserial.lua
-- test myserial print("start lua..."); require('myserial'); serial = myserial.open("/dev/ttyATH0", 9600); print(serial); res = serial:write('A'); print(res); serial:close(); print(serial); print(serial.open); res = serial:open("/dev/ttyUSB0", 9600); print(res); res = serial:write('A'); print(res); while 1 do if serial:wait(100000) > 0 then res = serial:available(); print(res); data = serial:read(res); print(data); end end print("end lua...");
scp callmyserial.lua [email protected]:/root/
тестируем, нажимая в терминале клавишу q
# ./lua ./callmyserial.lua start lua... [i] ser_open() [i] open: /dev/ttyATH0 9600! serial (/dev/ttyATH0 9600) [i] serial_write() 1 [i] serial_close() serial (closed) function: 0x76f42230 [i] serial_open() [i] open: /dev/ttyUSB0 9600! 0 [i] serial_write() 1 [i] serial_available() 1 [i] serial_read() q ^C./lua: ./callmyserial.lua:24: interrupted! stack traceback: [C]: in function 'wait' ./callmyserial.lua:24: in main chunk [C]: in ? [i] serial GC! [i] serial_close()
Супер! Теперь этот модуль можно взять за основу для Lua-изации своих роботов и прочих автоматов 🙂
Ссылки
http://www.lua.org
По теме
Использование Lua в робототехнике
Исследование Wi-Fi-роутера TP-LINK TL-MR3020
Кросс-компиляция Lua для TP-LINK TL-MR3020
Превращаем ADSL-модем в Ethernet-шилд для Arduino/CraftDuino