forked from biomood/LuaSerial
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathserial.c
More file actions
245 lines (205 loc) · 5.88 KB
/
serial.c
File metadata and controls
245 lines (205 loc) · 5.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
/*
* POSIX Serial Port C Library with Lua Bindings
* See luaserialterm.lua for an example of how to use
*
* Uses posix library so it should work with Linux/UNIX
* to communicate with devices that via a serial port
*
* HISTORY
*
* Forked from code found at:
* https://github.com/biomood/LuaSerial
*
* That was derived from code available at:
* https://github.com/rtacconi/arduino-serial-posix
*
* Which in turn was derived from code now found at:
* https://github.com/todbot/arduino-serial
*
* My additions and corrections were derived from (among other places):
* A very good description of all options:
* https://blog.mbedded.ninja
* /programming/operating-systems/linux/linux-serial-ports-using-c-cpp/
*
* Also the whole thing explained clearly:
* https://support.dce.felk.cvut.cz/pos/cv5/doc/serial.html#config
*
* Compiled on Debian 12 for Lua5.1 using:
* gcc -I /usr/include/lua5.1/ -fPIC -shared \
* -l:liblua5.1.so.0 serial.c -o serial.so
*
* There are some Lua 5.1 specific calls (int, register, etc) that need
* to be adressed to build for later Lua versions that no longer have
* the calls used in this code.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int iopen(lua_State *L);
int iread_no_bytes(lua_State *L);
int iread_until(lua_State *L);
int iwrite(lua_State *L);
int iclose(lua_State *L);
int isleep(lua_State *L);
int iusleep(lua_State *L);
int luaopen_serial(lua_State *L);
// set up the libray methods
const struct luaL_Reg serial [] = {
{"open", iopen},
{"write", iwrite},
{"readbytes", iread_no_bytes},
{"close", iclose},
{"sleep", isleep},
{"usleep", iusleep},
{NULL, NULL}
};
speed_t get_baud_bitmask (int baud) {
switch (baud){
// matching to baud defines found in
// <bits/termios.h> and <bits/termios-baud.h>
case 300: return B300;
case 1200: return B1200;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 921600: return B921600;
// baud rate not found so map to hangup
default: return B0;
break;
}
}
int iopen(lua_State *L) {
const char *serialport = luaL_checkstring(L, 1);
int baud = luaL_checkint(L, 2);
// attempt to open the serial port
int fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd==-1) {
lua_pushnumber(L, fd);
lua_pushstring(L, strerror(errno));
return 2;
}
else
// called due to open() O_NDELAY
fcntl(fd, F_SETFL, 0);
// get terminal paramaters
struct termios toptions;
if (tcgetattr(fd, &toptions) < 0) {
lua_pushnumber(L, fd);
lua_pushstring(L, strerror(errno));
return 2;
}
// set input/output speed
// or use cfsetispeed() and cfsetospeed()
cfsetspeed(&toptions, get_baud_bitmask(baud));
// disable software flow control
toptions.c_iflag &= ~(IXON | IXOFF | IXANY);
// Disable any special handling of received bytes
toptions.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL);
// Prevent special interpretation of output bytes (e.g. newline chars)
toptions.c_oflag &= ~OPOST;
// Prevent conversion of newline to carriage return/line feed
toptions.c_oflag &= ~ONLCR;
toptions.c_cflag &= ~CSIZE; // Clear all the size bits, then
toptions.c_cflag |= CS8; // 8 bits
toptions.c_cflag &= ~PARENB; // Clear parity bit, disabling parity
toptions.c_cflag &= ~CSTOPB; // Clear stop field, only one stop bit used
toptions.c_cflag &= ~CRTSCTS; // Disable RTS/CTS hardware flow control
toptions.c_cflag |= CREAD | CLOCAL; // Turn on READ & ignore ctrl lines
// Disable: canonical, echo, erasure, n/l echo also INTR, QUIT and SUSP
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL | ISIG);
// VMIN = 0, VTIME = 0 : read() grabs what is available and returns
// VMIN > 0, VTIME = 0 : read() waits for VMIN bytes before returning
// VMIN = 0, VTIME > 0 : read() waits for VTIME then returns
// VMIN > 0, VTIME > 0 : read() for VMIN bytes or there is a VTIME gap
// in time between chars recieved
toptions.c_cc[VMIN] = 0;
toptions.c_cc[VTIME] = 10;
// set terminal paramaters
if (tcsetattr(fd, TCSANOW, &toptions) < 0) {
// on failure
lua_pushnumber(L, fd);
lua_pushstring(L, strerror(errno));
return 2;
}
// on success
lua_pushnumber(L, fd);
return 1;
}
int iwrite(lua_State *L) {
int fd = luaL_checkint(L, 1);
const char *value = luaL_checkstring(L, 2);
int count = strlen(value);
int r = write(fd, value, count);
if (r!=count) {
lua_pushnumber(L, r);
lua_pushstring(L, strerror(errno));
return 2;
}
lua_pushnumber(L, r);
return 1;
}
int iread_no_bytes(lua_State *L) {
int fd = luaL_checkint(L, 1);
int count = luaL_checkint(L, 2);
char* buf = (char*)calloc(count+1, sizeof(char));
int r = read(fd, buf, count);
if (r < 0) {
lua_pushnumber(L, r);
lua_pushstring(L, strerror(errno));
return 2;
}
// return number of bytes read and data
lua_pushnumber(L, r);
lua_pushstring(L, buf);
free(buf);
return 2;
}
int iclose(lua_State *L) {
int fd = luaL_checkint(L, 1);
int r = close(fd);
if (r<0) {
lua_pushnumber(L, r);
lua_pushstring(L, strerror(errno));
return 2;
}
lua_pushnumber(L, r);
return 1;
}
int isleep(lua_State *L) {
int seconds = luaL_checkint(L, 1);
int r = sleep(seconds);
if (r>0) {
lua_pushnumber(L, r);
lua_pushstring(L, "awoke early");
return 2;
}
lua_pushnumber(L, r);
return 1;
}
int iusleep(lua_State *L) {
int useconds = luaL_checkint(L, 1);
int r = usleep(useconds);
if (r<0) {
lua_pushnumber(L, r);
lua_pushstring(L, strerror(errno));
return 2;
}
lua_pushnumber(L, r);
return 1;
}
int luaopen_serial(lua_State *L) {
luaL_register(L, "serial", serial);
return 1;
}