Lua — модуль для работы с последовательным портом


Напишем модуль 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

# include rate==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


Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Android Arduino Bluetooth CraftDuino DIY IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение