Skip to content

Commit dcc30bb

Browse files
committed
SoftUART fixes:
- Simplify code by using lua_L* functions and using userdata properly - Fix some edge-cases - Add more examples to documentation
1 parent 6e5edf4 commit dcc30bb

File tree

2 files changed

+104
-160
lines changed

2 files changed

+104
-160
lines changed

app/modules/softuart.c

Lines changed: 97 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ typedef struct {
3232
uint8_t armed;
3333
} softuart_t;
3434

35-
typedef struct {
36-
softuart_t *softuart;
37-
} softuart_userdata;
38-
3935
// Array of pointers to SoftUART instances
4036
softuart_t * softuart_gpio_instances[SOFTUART_GPIO_COUNT] = {NULL};
4137
// Array of callback reference to be able to find which callback is used to which rx pin
@@ -60,7 +56,7 @@ static inline uint8_t checkbit(uint8_t data, uint8_t bit)
6056
}
6157
}
6258

63-
uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
59+
static uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
6460
{
6561
// Disable all interrupts
6662
ets_intr_lock();
@@ -74,12 +70,12 @@ uint32_t ICACHE_RAM_ATTR softuart_intr_handler(uint32_t ret_gpio_status)
7470
// Load instance which has rx pin on interrupt pin attached
7571
softuart_t *s = softuart_gpio_instances[pin_num_inv[gpio_bit]];
7672
if (s == NULL) continue;
77-
if (softuart_rx_cb_ref[pin_num_inv[gpio_bit]] == LUA_NOREF) continue;
78-
if (!s->armed) continue;
79-
// There is SoftUART rx instance on that pin
8073
// Clear interrupt status on that pin
8174
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & (1 << pin_num[s->pin_rx]));
8275
ret_gpio_status &= ~(1 << pin_num[s->pin_rx]);
76+
if (softuart_rx_cb_ref[pin_num_inv[gpio_bit]] == LUA_NOREF) continue;
77+
if (!s->armed) continue;
78+
// There is an armed SoftUART rx instance on that pin
8379
// Start listening to transmission
8480
// TODO: inverted
8581
if (! (GPIO_INPUT_GET(GPIO_ID_PIN(pin_num[s->pin_rx])))) {
@@ -156,6 +152,7 @@ static void softuart_putchar(softuart_t *s, char data)
156152
// Set start bit
157153
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[s->pin_tx]), 0);
158154
for (uint32_t i = 0; i < 8; i++) {
155+
// Wait to transmit another bit
159156
while ((uint32_t)(asm_ccount() - start_time) < s->bit_time);
160157

161158
GPIO_OUTPUT_SET(GPIO_ID_PIN(pin_num[s->pin_tx]), checkbit(data, 1 << i));
@@ -172,9 +169,8 @@ static void softuart_putchar(softuart_t *s, char data)
172169
ets_intr_unlock();
173170
}
174171

175-
static void softuart_init(softuart_t *s)
172+
static int softuart_init(softuart_t *s)
176173
{
177-
NODE_DBG("SoftUART initialize gpio\n");
178174

179175
// Init tx pin
180176
if (s->pin_tx != 0xFF){
@@ -185,12 +181,17 @@ static void softuart_init(softuart_t *s)
185181
// Init rx pin
186182
if (s->pin_rx != 0xFF){
187183
platform_gpio_mode(s->pin_rx, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
188-
uint32_t mask = 1 << pin_num[s->pin_rx];
189-
platform_gpio_register_intr_hook(mask, softuart_intr_handler);
190-
191-
softuart_gpio_instances[s->pin_rx] = s;
192184
// Enable interrupt for pin on falling edge
193185
platform_gpio_intr_init(s->pin_rx, GPIO_PIN_INTR_NEGEDGE);
186+
softuart_gpio_instances[s->pin_rx] = s;
187+
// Preserve other rx gpio pins
188+
uint32_t mask = 0;
189+
for (uint8_t i = 0; i < SOFTUART_GPIO_COUNT; i++) {
190+
if (softuart_gpio_instances[i] != NULL) {
191+
mask = mask | (1 << pin_num[softuart_gpio_instances[i]->pin_rx]);
192+
}
193+
}
194+
return platform_gpio_register_intr_hook(mask, softuart_intr_handler);
194195
}
195196
}
196197

@@ -199,70 +200,59 @@ static int softuart_setup(lua_State *L)
199200
{
200201
uint32_t baudrate;
201202
uint8_t tx_gpio_id, rx_gpio_id;
202-
uint8_t stack = 1;
203-
softuart_userdata *suart = NULL;
204-
205-
NODE_DBG("SoftUART setup called\n");
206-
207-
if(lua_isnumber(L, stack)) {
208-
baudrate = (uint32_t)luaL_checkinteger( L, stack);
209-
//230400 Is the max baudrate the author of Arduino-Esp8266-Software-UART tested
210-
if (baudrate <= 0 || baudrate > 230400) {
211-
return luaL_error(L, "Invalid baud rate" );
212-
}
213-
stack++;
214-
} else {
215-
return luaL_error(L, "Invalid argument type");
216-
}
203+
softuart_t *softuart = NULL;
204+
205+
NODE_DBG("[SoftUART]: setup called\n");
206+
baudrate = (uint32_t)luaL_checkinteger(L, 1); // Get Baudrate from
207+
luaL_argcheck(L, (baudrate > 0 && baudrate < 230400), 1, "Invalid baud rate");
208+
lua_remove(L, 1); // Remove baudrate argument from stack
209+
if (lua_gettop(L) == 2) { // 2 arguments: 1st can be nil
210+
if (lua_isnil(L, 1)) {
211+
tx_gpio_id = 0xFF;
212+
} else {
213+
tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1);
214+
luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
215+
, 2, "Invalid SoftUART tx GPIO");
216+
}
217+
rx_gpio_id = (uint8_t)luaL_checkinteger(L, 2);
218+
luaL_argcheck(L, (platform_gpio_exists(rx_gpio_id) && rx_gpio_id != 0)
219+
, 3, "Invalid SoftUART rx GPIO");
220+
luaL_argcheck(L, softuart_gpio_instances[rx_gpio_id] == NULL
221+
, 3, "SoftUART rx already configured on the pin");
217222

218-
if(lua_isnumber(L, stack)) {
219-
tx_gpio_id = (uint8_t)luaL_checkinteger( L, stack);
220-
if (!platform_gpio_exists(tx_gpio_id) || tx_gpio_id == 0) {
221-
return luaL_error(L, "SoftUART tx GPIO not valid");
222-
}
223-
stack++;
224-
} else {
225-
tx_gpio_id = 0xFF;
226-
stack++;
227-
}
228-
if (lua_isnumber(L, stack)) {
229-
rx_gpio_id = (uint8_t)luaL_checkinteger( L, stack);
230-
if (!platform_gpio_exists(rx_gpio_id) || rx_gpio_id == 0) {
231-
return luaL_error(L, "SoftUART rx GPIO not valid");
232-
}
233-
if (softuart_gpio_instances[rx_gpio_id] != NULL) {
234-
return luaL_error( L, "SoftUART rx already configured on the pin.");
235-
}
236-
} else {
223+
} else if (lua_gettop(L) == 1) { // 1 argument: transmit part only
237224
rx_gpio_id = 0xFF;
225+
tx_gpio_id = (uint8_t)luaL_checkinteger(L, 1);
226+
luaL_argcheck(L, (platform_gpio_exists(tx_gpio_id) && tx_gpio_id != 0)
227+
, 2, "Invalid SoftUART tx GPIO");
228+
} else {
229+
// SoftUART object without receive and transmit part would be useless
230+
return luaL_error(L, "Not enough arguments");
238231
}
239232

240-
suart = (softuart_userdata*)lua_newuserdata(L, sizeof(softuart_userdata));
241-
suart->softuart = malloc(sizeof(softuart_t));
242-
if (!suart->softuart) {
243-
free(suart->softuart);
244-
suart->softuart = NULL;
245-
return luaL_error(L, "Not enough memory");
246-
}
247-
suart->softuart->pin_rx = rx_gpio_id;
248-
suart->softuart->pin_tx = tx_gpio_id;
249-
suart->softuart->need_len = RX_BUFF_SIZE;
250-
suart->softuart->armed = 0;
233+
softuart = (softuart_t*)lua_newuserdata(L, sizeof(softuart_t));
234+
softuart->pin_rx = rx_gpio_id;
235+
softuart->pin_tx = tx_gpio_id;
236+
softuart->need_len = RX_BUFF_SIZE;
237+
softuart->armed = 0;
251238

252239
// Set buffer
253-
suart->softuart->buffer.buffer_first = 0;
254-
suart->softuart->buffer.buffer_last = 0;
255-
suart->softuart->buffer.bytes_count = 0;
256-
suart->softuart->buffer.buffer_overflow = 0;
240+
softuart->buffer.buffer_first = 0;
241+
softuart->buffer.buffer_last = 0;
242+
softuart->buffer.bytes_count = 0;
243+
softuart->buffer.buffer_overflow = 0;
257244

258245
// Set bit time
259-
suart->softuart->bit_time = system_get_cpu_freq() * 1000000 / baudrate;
246+
softuart->bit_time = system_get_cpu_freq() * 1000000 / baudrate;
260247

261248
// Set metatable
262249
luaL_getmetatable(L, "softuart.port");
263250
lua_setmetatable(L, -2);
264251
// Init SoftUART
265-
softuart_init(suart->softuart);
252+
int result = softuart_init(softuart);
253+
if (result == 0) {
254+
luaL_error(L, "Couldn't register interrupt");
255+
}
266256
return 1;
267257
}
268258

@@ -293,103 +283,59 @@ static void softuart_rx_callback(task_param_t arg)
293283
// Arguments: event name, minimum buffer filled to run callback, callback function
294284
static int softuart_on(lua_State *L)
295285
{
296-
NODE_DBG("SoftUART on called\n");
297-
softuart_userdata *suart = NULL;
286+
NODE_DBG("[SoftUART] on: called\n");
298287
size_t name_len, arg_len;
299-
uint8_t stack = 1;
300288

301-
suart = (softuart_userdata *)luaL_checkudata(L, 1, "softuart.port");
302-
luaL_argcheck(L, suart, stack, "softuart.port expected");
303-
if (suart == NULL) {
304-
NODE_DBG("Userdata is nil\n");
305-
return 0;
306-
}
307-
stack++;
308-
309-
const char *method = luaL_checklstring(L, stack, &name_len);
310-
if (method == NULL)
311-
return luaL_error(L, "Wrong argument type");
312-
stack++;
313-
314-
if (lua_type(L, stack) == LUA_TNUMBER) {
315-
suart->softuart->need_len = (uint16_t)luaL_checkinteger( L, stack );
316-
stack++;
317-
suart->softuart->end_char = 0;
318-
if (suart->softuart->need_len > SOFTUART_MAX_RX_BUFF) {
319-
suart->softuart->need_len = 0;
320-
return luaL_error(L, "Argument bigger than SoftUART buffer");
321-
}
322-
suart->softuart->armed = 1;
323-
} else if (lua_isstring(L, stack)) {
324-
const char *end = luaL_checklstring(L , stack, &arg_len);
325-
stack++;
326-
if ( arg_len != 1) {
327-
return luaL_error(L, "Wrong end char length");
328-
}
329-
suart->softuart->end_char = end[0];
330-
suart->softuart->need_len = 0;
331-
suart->softuart->armed = 1;
289+
softuart_t *softuart = (softuart_t*)luaL_checkudata(L, 1, "softuart.port");
290+
const char *method = luaL_checklstring(L, 2, &name_len);
291+
292+
if (lua_isnumber(L, 3)) {
293+
luaL_argcheck(L, luaL_checkinteger(L, 3) < SOFTUART_MAX_RX_BUFF,
294+
2, "Argument bigger than SoftUART buffer");
295+
softuart->end_char = 0;
296+
softuart->need_len = (uint16_t) luaL_checkinteger(L, 3);
297+
softuart->armed = 1;
298+
} else if (lua_isstring(L, 3)) {
299+
const char *end = luaL_checklstring(L , 3, &arg_len);
300+
luaL_argcheck(L, arg_len == 1, 3, "Wrong end char length");
301+
softuart->end_char = end[0];
302+
softuart->need_len = 0;
303+
softuart->armed = 1;
332304
} else {
333305
return luaL_error(L, "Wrong argument type");
334306
}
335307

336-
337-
if (lua_type(L, stack) == LUA_TFUNCTION) {
338-
lua_pushvalue(L, stack); // Copy to top of the stack
339-
} else {
340-
lua_pushnil(L);
341-
}
342-
343-
if (name_len == 4 && strcmp(method, "data") == 0) {
344-
345-
if(suart->softuart->pin_rx == 0xFF) {
346-
return luaL_error(L, "Rx pin was not declared");
347-
}
348-
349-
if (softuart_rx_cb_ref[suart->softuart->pin_rx] != LUA_NOREF) {
350-
luaL_unref(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[suart->softuart->pin_rx]);
351-
softuart_rx_cb_ref[suart->softuart->pin_rx] = LUA_NOREF;
352-
}
353-
if (! lua_isnil(L, -1)) {
354-
softuart_rx_cb_ref[suart->softuart->pin_rx] = luaL_ref(L, LUA_REGISTRYINDEX);
355-
} else {
356-
lua_pop(L, 1);
357-
}
358-
} else {
359-
lua_pop(L, 1);
360-
return luaL_error(L, "Method not supported");
308+
luaL_argcheck(L, lua_isfunction(L, 4), -1, "No callback function specified");
309+
luaL_argcheck(L, (name_len == 4 && strcmp(method, "data") == 0), 2, "Method not supported");
310+
luaL_argcheck(L, softuart->pin_rx != 0xFF, 1, "Rx pin was not declared");
311+
lua_settop(L, 4); // Move to the top of the stack
312+
if (softuart_rx_cb_ref[softuart->pin_rx] != LUA_NOREF) {
313+
// Remove old callback and add new one
314+
luaL_unref(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[softuart->pin_rx]);
361315
}
316+
// Register callback
317+
softuart_rx_cb_ref[softuart->pin_rx] = luaL_ref(L, LUA_REGISTRYINDEX);
318+
362319
return 0;
363320
}
364321

365322
static int softuart_write(lua_State *L)
366323
{
367-
NODE_DBG("SoftUART write called\n");
368-
softuart_userdata *suart = NULL;
369-
uint8_t stack = 1;
324+
softuart_t *softuart = NULL;
370325
size_t str_len;
371-
suart = (softuart_userdata *)luaL_checkudata(L, 1, "softuart.port");
372-
luaL_argcheck(L, suart, stack, "softuart.port expected");
373-
if (suart == NULL) {
374-
NODE_DBG("Userdata is nil\n");
375-
return 0;
376-
}
377-
stack++;
378-
if(suart->softuart->pin_tx == 0xFF) {
379-
return luaL_error(L, "Tx pin was not declared");
380-
}
381-
if (lua_type(L, stack) == LUA_TNUMBER) {
326+
softuart = (softuart_t*) luaL_checkudata(L, 1, "softuart.port");
327+
luaL_argcheck(L, softuart->pin_tx != 0xFF, 1, "Tx pin was not declared");
328+
329+
if (lua_isnumber(L, 2)) {
382330
// Send byte
383-
uint32_t byte = (uint32_t)luaL_checkinteger( L, stack );
384-
if (byte > 255) {
385-
return luaL_error(L, "Integer too large for a byte");
386-
}
387-
softuart_putchar(suart->softuart, (char)byte);
388-
} else if (lua_isstring(L, stack)) {
331+
uint32_t byte = (uint32_t)luaL_checkinteger(L, 2);
332+
luaL_argcheck(L, byte < 256, 2, "Integer too large for a byte");
333+
softuart_putchar(softuart, (char)byte);
334+
} else if (lua_isstring(L, 2)) {
389335
// Send string
390-
const char *string = luaL_checklstring(L , stack, &str_len);
336+
const char *string = luaL_checklstring(L, 2, &str_len);
391337
for (size_t i = 0; i < str_len; i++) {
392-
softuart_putchar(suart->softuart, string[i]);
338+
softuart_putchar(softuart, string[i]);
393339
}
394340
} else {
395341
return luaL_error(L, "Wrong argument type");
@@ -400,17 +346,11 @@ static int softuart_write(lua_State *L)
400346
static int softuart_gcdelete(lua_State *L)
401347
{
402348
NODE_DBG("SoftUART GC called\n");
403-
softuart_userdata *suart = NULL;
404-
suart = (softuart_userdata *)luaL_checkudata(L, 1, "softuart.port");
405-
luaL_argcheck(L, suart, 1, "softuart.port expected");
406-
if (suart == NULL) {
407-
NODE_DBG("Userdata is nil\n");
408-
return 0;
409-
}
410-
softuart_gpio_instances[suart->softuart->pin_rx] = NULL;
411-
luaL_unref(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[suart->softuart->pin_rx]);
412-
softuart_rx_cb_ref[suart->softuart->pin_rx] = LUA_NOREF;
413-
free(suart->softuart);
349+
softuart_t *softuart = NULL;
350+
softuart = (softuart_t*) luaL_checkudata(L, 1, "softuart.port");
351+
softuart_gpio_instances[softuart->pin_rx] = NULL;
352+
luaL_unref(L, LUA_REGISTRYINDEX, softuart_rx_cb_ref[softuart->pin_rx]);
353+
softuart_rx_cb_ref[softuart->pin_rx] = LUA_NOREF;
414354
return 0;
415355
}
416356

docs/modules/softuart.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@
55

66
The SoftUART module provides access to multiple software-based UART ports.
77

8-
ESP8266 has only 1 full hardware UART port that is used to program the chip and communicate with NodeMCU firmware. The second port is transmit-only. More information can be found in [uart module documentation](uart/). This module provides access to more UART ports and can be used to communicate with devices like GSM or GPS modules. The code is based on [esp8266-software-uart](https://github.com/plieningerweb/esp8266-software-uart) and [Arduino-esp8266-Software-UART](https://github.com/juancgalvez/Arduino-esp8266-Software-UART) projects. Currently doesn't support inverted serial data logic or modes other than 8N1. It's important to notice that this is a software implementation of the serial protocol. There could be some interrupts that make the transmition or reception fail due to invalid timing.
8+
ESP8266 has only 1 full hardware UART port that is used to program the chip and communicate with NodeMCU firmware. The second port is transmit-only. More information can be found in [uart module documentation](uart/). This module provides access to more UART ports and can be used to communicate with devices like GSM or GPS modules. The code is based on [esp8266-software-uart](https://github.com/plieningerweb/esp8266-software-uart) and [Arduino-esp8266-Software-UART](https://github.com/juancgalvez/Arduino-esp8266-Software-UART) projects. Currently doesn't support inverted serial data logic or modes other than 8N1. It's important to notice that this is a software implementation of the serial protocol. There could be some interrupts that make the transmission or reception fail due to invalid timing.
99

1010
!!! note
11-
SoftUART cannot be used on D0 pin.
11+
12+
SoftUART cannot be used on D0 pin.
1213

1314
## softuart.setup()
1415

15-
Creates new SoftUART instance. Note that rx pin cannot be shared between instances but tx pin can.
16+
Creates new SoftUART instance. Note that rx pin cannot be shared between instances but tx pin can.
1617

1718
#### Syntax
1819
`softuart.setup(baudrate, txPin, rxPin)`
@@ -52,6 +53,7 @@ Sets up the callback function to receive data.
5253
```lua
5354
-- Create new software UART with baudrate of 9600, D2 as Tx pin and D3 as Rx pin
5455
s = softuart.setup(9600, 2, 3)
56+
-- Set callback to run when 10 characters show up in the buffer
5557
s:on("data", 10, function(data)
5658
print("Lua handler called!")
5759
print(data)
@@ -75,4 +77,6 @@ Transmits a byte or sequence of them.
7577
-- Create new software UART with baudrate of 9600, D2 as Tx pin and D3 as Rx pin
7678
s = softuart.setup(9600, 2, 3)
7779
s:write("Hello!")
80+
-- Send character 'a'
81+
s:write(97)
7882
```

0 commit comments

Comments
 (0)