diff --git a/.gdbinitlua b/.gdbinitlua index b832fab5..ef467354 100644 --- a/.gdbinitlua +++ b/.gdbinitlua @@ -3,9 +3,9 @@ set pagination off set print null-stop define prTS - set $o = &(((TString *)($arg0))->tsv) + set $o = &(((TString *)(($arg0).value))->tsv) printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked - printf "String: hash = 0x%08x, len = %u : %s\n", $o->hash, $o->len, (char *)(&$o[1]) + printf "String: hash = 0x%08x, len = %u : %s\n", $o->hash, $o->len, (char *)($o+1) end define prTnodes @@ -24,6 +24,18 @@ define prTnodes set $i = $i +1 end end + +define prTarray + set $o = (Table *)($arg0) + set $n = $o->sizearray + set $i = 0 + while $i < $n + set $nd = ($o->array) + $i + prTV $nd + set $i = $i +1 + end +end + define prTV if $arg0 set $type = ($arg0).tt @@ -78,6 +90,10 @@ define prTV end if $type == 9 # UserData + set $o = &($val->gc.u.uv) + printf "Common header: next = %p, marked = 0x%01x\n", $o->next, $o->marked + printf "UD = %p Userdata: metatable = ", ($o+1)) + print ($o)->metatable end if $type == 10 # Thread diff --git a/app/Makefile b/app/Makefile index 4fab0eed..a419c526 100644 --- a/app/Makefile +++ b/app/Makefile @@ -36,7 +36,6 @@ SUBDIRS= \ libc \ lua \ lwip \ - task \ smart \ modules \ spiffs \ @@ -64,8 +63,7 @@ COMPONENTS_eagle.app.v6 = \ user/libuser.a \ crypto/libcrypto.a \ driver/libdriver.a \ - platform/libplatform.a \ - task/libtask.a \ + platform/libplatform.a \ libc/liblibc.a \ lua/liblua.a \ lwip/liblwip.a \ diff --git a/app/driver/Makefile b/app/driver/Makefile index 5dbd6853..cee23833 100644 --- a/app/driver/Makefile +++ b/app/driver/Makefile @@ -15,7 +15,7 @@ ifndef PDIR GEN_LIBS = libdriver.a endif -STD_CFLAGS=-std=gnu11 -Wimplicit +STD_CFLAGS=-std=gnu11 -Wimplicit -Wall ############################################################# # Configuration i.e. compile options etc. diff --git a/app/driver/input.c b/app/driver/input.c new file mode 100644 index 00000000..79a522f8 --- /dev/null +++ b/app/driver/input.c @@ -0,0 +1,196 @@ +#include "platform.h" +#include "driver/uart.h" +#include "driver/input.h" +#include +#include "mem.h" + +/**DEBUG**/extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + +static void input_handler(platform_task_param_t flag, uint8 priority); + +static struct input_state { + char *data; + int line_pos; + size_t len; + const char *prompt; + uart_cb_t uart_cb; + platform_task_handle_t input_sig; + int data_len; + bool run_input; + bool uart_echo; + char last_char; + char end_char; + uint8 input_sig_flag; +} ins = {0}; + +#define NUL '\0' +#define BS '\010' +#define CR '\r' +#define LF '\n' +#define DEL 0x7f +#define BS_OVER "\010 \010" + +#define sendStr(s) uart0_sendStr(s) +#define putc(c) uart0_putc(c) + +// UartDev is defined and initialized in rom code. +extern UartDevice UartDev; + +static bool uart_getc(char *c){ + RcvMsgBuff *pRxBuff = &(UartDev.rcv_buff); + if(pRxBuff->pWritePos == pRxBuff->pReadPos){ // empty + return false; + } + // ETS_UART_INTR_DISABLE(); + ETS_INTR_LOCK(); + *c = (char)*(pRxBuff->pReadPos); + if (pRxBuff->pReadPos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) { + pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ; + } else { + pRxBuff->pReadPos++; + } + // ETS_UART_INTR_ENABLE(); + ETS_INTR_UNLOCK(); + return true; +} + +/* +** input_handler at high-priority is a system post task used to process pending Rx +** data on UART0. The flag is used as a latch to stop the interrupt handler posting +** multiple pending requests. At low priority it is used the trigger interactive +** compile. +** +** The ins.data check detects up the first task call which used to initialise +** everything. +*/ +int lua_main (void); +static bool input_readline(void); + +static void input_handler(platform_task_param_t flag, uint8 priority) { + (void) priority; + if (!ins.data) { + lua_main(); + return; + } + ins.input_sig_flag = flag & 0x1; + while (input_readline()) {} +} + +/* +** The input state (ins) is private, so input_setup() exposes the necessary +** access to public properties and is called in user_init() before the Lua +** enviroment is initialised. The second routine input_setup_receive() is +** called in lua.c after the Lua environment is available to bind the Lua +** input handler. Any UART input before this receive setup is ignored. +*/ +void input_setup(int bufsize, const char *prompt) { + // Initialise non-zero elements + ins.run_input = true; + ins.uart_echo = true; + ins.data = os_malloc(bufsize); + ins.len = bufsize; + ins.prompt = prompt; + ins.input_sig = platform_task_get_id(input_handler); + // pass the task CB parameters to the uart driver + uart_init_task(ins.input_sig, &ins.input_sig_flag); + ETS_UART_INTR_ENABLE(); +} + +void input_setup_receive(uart_cb_t uart_on_data_cb, int data_len, char end_char, bool run_input) { + ins.uart_cb = uart_on_data_cb; + ins.data_len = data_len; + ins.end_char = end_char; + ins.run_input = run_input; +} + +void input_setecho (bool flag) { + ins.uart_echo = flag; +} + +void input_setprompt (const char *prompt) { + ins.prompt = prompt; +} + +/* +** input_readline() is called from the input_handler() event routine which is +** posted by the UART Rx ISR posts. This works in one of two modes depending on +** the bool ins.run_input. +** - TRUE: it clears the UART FIFO up to EOL, doing any callback and sending +** the line to Lua. +** - FALSE: it clears the UART FIFO doing callbacks according to the data_len / +** end_char break. +*/ +void lua_input_string (const char *line, int len); + +static bool input_readline(void) { + char ch = NUL; + if (ins.run_input) { + while (uart_getc(&ch)) { + /* handle CR & LF characters and aggregate \n\r and \r\n pairs */ + if ((ch == CR && ins.last_char == LF) || + (ch == LF && ins.last_char == CR)) { + ins.last_char = NUL; + continue; + } + + /* backspace key */ + if (ch == DEL || ch == BS) { + if (ins.line_pos > 0) { + if(ins.uart_echo) sendStr(BS_OVER); + ins.line_pos--; + } + ins.data[ins.line_pos] = 0; + ins.last_char = NUL; + continue; + } + ins.last_char = ch; + + /* end of data */ + if (ch == CR || ch == LF) { + if (ins.uart_echo) putc(LF); + if (ins.uart_cb) ins.uart_cb(ins.data, ins.line_pos); + if (ins.line_pos == 0) { + /* Get a empty data, then go to get a new data */ + + sendStr(ins.prompt); + continue; + } else { + ins.data[ins.line_pos++] = LF; + lua_input_string(ins.data, ins.line_pos); + ins.line_pos = 0; + return true; + } + } + + if(ins.uart_echo) putc(ch); + + /* it's a large data, discard it */ + if ( ins.line_pos + 1 >= ins.len ){ + ins.line_pos = 0; + } + ins.data[ins.line_pos++] = ch; + } + + } else { + + if (!ins.uart_cb) { + while (uart_getc(&ch)) {} + } else if (ins.data_len == 0) { + while (uart_getc(&ch)) { + ins.uart_cb(&ch, 1); + } + } else { + while (uart_getc(&ch)) { + ins.data[ins.line_pos++] = ch; + if( ins.line_pos >= ins.len || + (ins.data_len > 0 && ins.line_pos >= ins.data_len) || + ch == ins.end_char ) { + ins.uart_cb(ins.data, ins.line_pos); + ins.line_pos = 0; + } + } + } + } + return false; +} + diff --git a/app/driver/pwm.c b/app/driver/pwm.c index 6422cc92..75b5d068 100644 --- a/app/driver/pwm.c +++ b/app/driver/pwm.c @@ -20,7 +20,7 @@ #include "driver/pwm.h" // #define PWM_DBG os_printf -#define PWM_DBG +#define PWM_DBG( ... ) // Enabling the next line will cause the interrupt handler to toggle // this output pin during processing so that the timing is obvious @@ -253,7 +253,7 @@ pwm_set_freq(uint16 freq, uint8 channel) pwm.period = PWM_1S / pwm.freq; } - +#if 0 /****************************************************************************** * FunctionName : pwm_set_freq_duty * Description : set pwm frequency and each channel's duty @@ -274,7 +274,7 @@ pwm_set_freq_duty(uint16 freq, uint16 *duty) pwm_set_duty(duty[i], pwm_out_io_num[i]); } } - +#endif /****************************************************************************** * FunctionName : pwm_get_duty * Description : get duty of each channel diff --git a/app/driver/readline.c b/app/driver/readline.c deleted file mode 100644 index 99192bbc..00000000 --- a/app/driver/readline.c +++ /dev/null @@ -1,111 +0,0 @@ -#include "ets_sys.h" -#include "os_type.h" -#include "osapi.h" -#include "driver/uart.h" -#include - -LOCAL os_timer_t readline_timer; - -// UartDev is defined and initialized in rom code. -extern UartDevice UartDev; - -#define uart_putc uart0_putc - -bool uart_getc(char *c){ - RcvMsgBuff *pRxBuff = &(UartDev.rcv_buff); - if(pRxBuff->pWritePos == pRxBuff->pReadPos){ // empty - return false; - } - // ETS_UART_INTR_DISABLE(); - ETS_INTR_LOCK(); - *c = (char)*(pRxBuff->pReadPos); - if (pRxBuff->pReadPos == (pRxBuff->pRcvMsgBuff + RX_BUFF_SIZE)) { - pRxBuff->pReadPos = pRxBuff->pRcvMsgBuff ; - } else { - pRxBuff->pReadPos++; - } - // ETS_UART_INTR_ENABLE(); - ETS_INTR_UNLOCK(); - return true; -} - -#if 0 -int readline4lua(const char *prompt, char *buffer, int length){ - char ch; - int line_position; - -start: - /* show prompt */ - uart0_sendStr(prompt); - - line_position = 0; - os_memset(buffer, 0, length); - while (1) - { - while (uart_getc(&ch)) - { - /* handle CR key */ - if (ch == '\r') - { - char next; - if (uart_getc(&next)) - ch = next; - } - /* backspace key */ - else if (ch == 0x7f || ch == 0x08) - { - if (line_position > 0) - { - uart_putc(0x08); - uart_putc(' '); - uart_putc(0x08); - line_position--; - } - buffer[line_position] = 0; - continue; - } - /* EOF(ctrl+d) */ - else if (ch == 0x04) - { - if (line_position == 0) - /* No input which makes lua interpreter close */ - return 0; - else - continue; - } - - /* end of line */ - if (ch == '\r' || ch == '\n') - { - buffer[line_position] = 0; - uart_putc('\n'); - if (line_position == 0) - { - /* Get a empty line, then go to get a new line */ - goto start; - } - else - { - return line_position; - } - } - - /* other control character or not an acsii character */ - if (ch < 0x20 || ch >= 0x80) - { - continue; - } - - /* echo */ - uart_putc(ch); - buffer[line_position] = ch; - ch = 0; - line_position++; - - /* it's a large line, discard it */ - if (line_position >= length) - line_position = 0; - } - } -} -#endif diff --git a/app/driver/rotary.c b/app/driver/rotary.c index c54bda55..5b8eb5ee 100644 --- a/app/driver/rotary.c +++ b/app/driver/rotary.c @@ -14,9 +14,9 @@ #include #include #include +#include "task/task.h" #include "driver/rotary.h" #include "user_interface.h" -#include "task/task.h" #include "ets_sys.h" // @@ -37,7 +37,7 @@ #define GET_READ_STATUS(d) (d->queue[d->read_offset & (QUEUE_SIZE - 1)]) #define ADVANCE_IF_POSSIBLE(d) if (d->read_offset < d->write_offset) { d->read_offset++; } -#define STATUS_IS_PRESSED(x) ((x & 0x80000000) != 0) +#define STATUS_IS_PRESSED(x) (((x) & 0x80000000) != 0) typedef struct { int8_t phase_a_pin; @@ -213,7 +213,6 @@ int rotary_setup(uint32_t channel, int phase_a, int phase_b, int press, task_han } data[channel] = d; - int i; d->tasknumber = tasknumber; diff --git a/app/driver/spi.c b/app/driver/spi.c index 2cd05eee..32abf114 100644 --- a/app/driver/spi.c +++ b/app/driver/spi.c @@ -15,7 +15,6 @@ static uint32_t spi_clkdiv[2]; *******************************************************************************/ void spi_lcd_mode_init(uint8 spi_no) { - uint32 regvalue; if(spi_no>1) return; //handle invalid input number //bit9 of PERIPHS_IO_MUX should be cleared when HSPI clock doesn't equal CPU clock //bit8 of PERIPHS_IO_MUX should be cleared when SPI clock doesn't equal CPU clock @@ -112,8 +111,6 @@ uint32_t spi_set_clkdiv(uint8 spi_no, uint32_t clock_div) *******************************************************************************/ void spi_master_init(uint8 spi_no, unsigned cpol, unsigned cpha, uint32_t clock_div) { - uint32 regvalue; - if(spi_no>1) return; //handle invalid input number SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_CS_SETUP|SPI_CS_HOLD|SPI_RD_BYTE_ORDER|SPI_WR_BYTE_ORDER); @@ -258,7 +255,7 @@ void spi_mast_set_mosi(uint8 spi_no, uint16 offset, uint8 bitlen, uint32 data) } shift = 64 - (offset & 0x1f) - bitlen; - spi_buf.dword &= ~((1ULL << bitlen)-1 << shift); + spi_buf.dword &= ~(((1ULL << bitlen)-1) << shift); spi_buf.dword |= (uint64)data << shift; if (wn < 15) { @@ -344,7 +341,7 @@ void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 uint16 cmd = cmd_data << (16 - cmd_bitlen); // align to MSB cmd = (cmd >> 8) | (cmd << 8); // swap byte order WRITE_PERI_REG(SPI_USER2(spi_no), - ((cmd_bitlen - 1 & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | + (((cmd_bitlen - 1) & SPI_USR_COMMAND_BITLEN) << SPI_USR_COMMAND_BITLEN_S) | (cmd & SPI_USR_COMMAND_VALUE)); SET_PERI_REG_MASK(SPI_USER(spi_no), SPI_USR_COMMAND); } @@ -387,8 +384,6 @@ void spi_mast_transaction(uint8 spi_no, uint8 cmd_bitlen, uint16 cmd_data, uint8 *******************************************************************************/ void spi_byte_write_espslave(uint8 spi_no,uint8 data) { - uint32 regvalue; - if(spi_no>1) return; //handle invalid input number while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); @@ -413,8 +408,6 @@ void spi_byte_write_espslave(uint8 spi_no,uint8 data) *******************************************************************************/ void spi_byte_read_espslave(uint8 spi_no,uint8 *data) { - uint32 regvalue; - if(spi_no>1) return; //handle invalid input number while(READ_PERI_REG(SPI_CMD(spi_no))&SPI_USR); @@ -440,7 +433,7 @@ void spi_byte_write_espslave(uint8 spi_no,uint8 data) *******************************************************************************/ void spi_slave_init(uint8 spi_no) { - uint32 regvalue; +// uint32 regvalue; if(spi_no>1) return; //handle invalid input number @@ -565,7 +558,6 @@ void hspi_master_readwrite_repeat(void) #include "mem.h" static uint8 spi_data[32] = {0}; static uint8 idx = 0; -static uint8 spi_flg = 0; #define SPI_MISO #define SPI_QUEUE_LEN 8 os_event_t * spiQueue; @@ -596,9 +588,8 @@ void ICACHE_FLASH_ATTR void spi_slave_isr_handler(void *para) { - uint32 regvalue,calvalue; - static uint8 state =0; - uint32 recv_data,send_data; + uint32 regvalue; + uint32 recv_data; if(READ_PERI_REG(0x3ff00020)&BIT4){ //following 3 lines is to clear isr signal diff --git a/app/driver/switec.c b/app/driver/switec.c index d776749a..04242b59 100644 --- a/app/driver/switec.c +++ b/app/driver/switec.c @@ -18,13 +18,13 @@ #include #include #include +#include "task/task.h" #include "driver/switec.h" #include "ets_sys.h" #include "os_type.h" #include "osapi.h" #include "hw_timer.h" #include "user_interface.h" -#include "task/task.h" #define N_STATES 6 // diff --git a/app/driver/uart.c b/app/driver/uart.c index 944e3400..5b50d194 100644 --- a/app/driver/uart.c +++ b/app/driver/uart.c @@ -12,7 +12,7 @@ #include "ets_sys.h" #include "osapi.h" #include "driver/uart.h" -#include "task/task.h" +#include "platform.h" #include "user_config.h" #include "user_interface.h" #include "osapi.h" @@ -29,15 +29,15 @@ // For event signalling -static task_handle_t sig = 0; +static platform_task_handle_t sig = 0; static uint8 *sig_flag; static uint8 isr_flag = 0; // UartDev is defined and initialized in rom code. extern UartDevice UartDev; - +#ifdef BIT_RATE_AUTOBAUD static os_timer_t autobaud_timer; - +#endif static void (*alt_uart0_tx)(char txchar); LOCAL void ICACHE_RAM_ATTR @@ -165,7 +165,7 @@ uart_tx_one_char(uint8 uart, uint8 TxChar) WRITE_PERI_REG(UART_FIFO(uart) , TxChar); return OK; } - +#if 0 /****************************************************************************** * FunctionName : uart1_write_char * Description : Internal used function @@ -189,7 +189,7 @@ uart1_write_char(char c) uart_tx_one_char(UART1, c); } } - +#endif /****************************************************************************** * FunctionName : uart0_tx_buffer * Description : use uart0 to transfer buffer @@ -300,13 +300,15 @@ uart0_rx_intr_handler(void *para) } if (got_input && sig) { + // Only post a new handler request once the handler has fired clearing the last post if (isr_flag == *sig_flag) { isr_flag ^= 0x01; - task_post_low (sig, 0x8000 | isr_flag << 14 | false); + platform_post_high(sig, isr_flag); } } } +#ifdef BIT_RATE_AUTOBAUD static void uart_autobaud_timeout(void *timer_arg) { @@ -324,7 +326,6 @@ uart_autobaud_timeout(void *timer_arg) } } #include "pm/swtimer.h" - static void uart_init_autobaud(uint32_t uart_no) { @@ -339,22 +340,17 @@ uart_stop_autobaud() { os_timer_disarm(&autobaud_timer); } - +#endif /****************************************************************************** * FunctionName : uart_init * Description : user interface for init uart * Parameters : UartBautRate uart0_br - uart0 bautrate * UartBautRate uart1_br - uart1 bautrate - * os_signal_t sig_input - signal to post - * uint8 *flag_input - flag of consumer task * Returns : NONE *******************************************************************************/ void ICACHE_FLASH_ATTR -uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input) +uart_init(UartBautRate uart0_br, UartBautRate uart1_br) { - sig = sig_input; - sig_flag = flag_input; - // rom use 74880 baut_rate, here reinitialize UartDev.baut_rate = uart0_br; uart_config(UART0); @@ -378,6 +374,19 @@ uart_setup(uint8 uart_no) ETS_UART_INTR_ENABLE(); } +/****************************************************************************** + * FunctionName : uart_init_task + * Description : user interface for init uart task callback + * Parameters : os_signal_t sig_input - signal to post + * uint8 *flag_input - flag of consumer task + * Returns : NONE +*******************************************************************************/ + +void ICACHE_FLASH_ATTR uart_init_task(os_signal_t sig_input, uint8 *flag_input) { + sig = sig_input; + sig_flag = flag_input; +} + void ICACHE_FLASH_ATTR uart_set_alt_output_uart0(void (*fn)(char)) { alt_uart0_tx = fn; } diff --git a/app/include/driver/input.h b/app/include/driver/input.h new file mode 100644 index 00000000..f18f48b6 --- /dev/null +++ b/app/include/driver/input.h @@ -0,0 +1,11 @@ +#ifndef READLINE_APP_H +#define READLINE_APP_H +typedef void (*uart_cb_t)(const char *buf, size_t len); + +extern void input_setup(int bufsize, const char *prompt); +extern void input_setup_receive(uart_cb_t uart_on_data_cb, int data_len, char end_char, bool run_input); +extern void input_setecho (bool flag); +extern void input_setprompt (const char *prompt); +extern void input_process_arm(void); + +#endif /* READLINE_APP_H */ diff --git a/app/include/driver/readline.h b/app/include/driver/readline.h deleted file mode 100644 index ae92cfd1..00000000 --- a/app/include/driver/readline.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef READLINE_APP_H -#define READLINE_APP_H - -bool uart_getc(char *c); - -#endif /* READLINE_APP_H */ diff --git a/app/include/driver/uart.h b/app/include/driver/uart.h index 310f6054..eb9c4aad 100644 --- a/app/include/driver/uart.h +++ b/app/include/driver/uart.h @@ -110,7 +110,8 @@ typedef struct { UartStopBitsNum stop_bits; } UartConfig; -void uart_init(UartBautRate uart0_br, UartBautRate uart1_br, os_signal_t sig_input, uint8 *flag_input); +void uart_init(UartBautRate uart0_br, UartBautRate uart1_br); +void uart_init_task(os_signal_t sig_input, uint8 *flag_input); UartConfig uart_get_config(uint8 uart_no); void uart0_alt(uint8 on); void uart0_sendStr(const char *str); diff --git a/app/include/task/task.h b/app/include/task/task.h index b090e54f..5292d1dc 100644 --- a/app/include/task/task.h +++ b/app/include/task/task.h @@ -1,35 +1,24 @@ #ifndef _TASK_H_ #define _TASK_H_ - -#include "ets_sys.h" -#include "osapi.h" -#include "os_type.h" -#include "user_interface.h" - -/* use LOW / MEDIUM / HIGH since it isn't clear from the docs which is higher */ - -#define TASK_PRIORITY_LOW 0 -#define TASK_PRIORITY_MEDIUM 1 -#define TASK_PRIORITY_HIGH 2 -#define TASK_PRIORITY_COUNT 3 - /* -* Signals are a 32-bit number of the form header:14; count:16, priority:2. The header -* is just a fixed fingerprint and the count is allocated serially by the task get_id() -* function. -*/ -#define task_post(priority,handle,param) system_os_post(priority, ((handle) | priority), param) -#define task_post_low(handle,param) task_post(TASK_PRIORITY_LOW, handle, param) -#define task_post_medium(handle,param) task_post(TASK_PRIORITY_MEDIUM, handle, param) -#define task_post_high(handle,param) task_post(TASK_PRIORITY_HIGH, handle, param) +** The task interface is now part of the core platform interface. +** This header is preserved for backwards compatability only. +*/ +#include "platform.h" -#define task_handle_t os_signal_t -#define task_param_t os_param_t +#define TASK_PRIORITY_LOW PLATFORM_TASK_PRIORITY_LOW +#define TASK_PRIORITY_MEDIUM PLATFORM_TASK_PRIORITY_MEDIUM +#define TASK_PRIORITY_HIGH PLATFORM_TASK_PRIORITY_HIGH -typedef void (*task_callback_t)(task_param_t param, uint8 prio); +#define task_post(priority,handle,param) platform_post(priority,handle,param) +#define task_post_low(handle,param) platform_post_low(handle,param) +#define task_post_medium(handle,param) platform_post_medium(handle,param) +#define task_post_high(handle,param) platform_post_high(handle,param) -bool task_init_handler(uint8 priority, uint8 qlen); -task_handle_t task_get_id(task_callback_t t); +#define task_handle_t platform_task_handle_t +#define task_param_t platform_task_param_t +#define task_callback_t platform_task_callback_t +#define task_get_id platform_task_get_id #endif diff --git a/app/include/user_config.h b/app/include/user_config.h index 4866e6a9..d0b923b5 100644 --- a/app/include/user_config.h +++ b/app/include/user_config.h @@ -263,14 +263,14 @@ extern void dbg_printf(const char *fmt, ...); #ifdef NODE_DEBUG #define NODE_DBG dbg_printf #else -#define NODE_DBG +#define NODE_DBG( ... ) #endif /* NODE_DEBUG */ #define NODE_ERROR #ifdef NODE_ERROR #define NODE_ERR dbg_printf #else -#define NODE_ERR +#define NODE_ERR( ... ) #endif /* NODE_ERROR */ // #define GPIO_SAFE_NO_INTR_ENABLE diff --git a/app/lua/Makefile b/app/lua/Makefile index 16d97ab0..aeb66d17 100644 --- a/app/lua/Makefile +++ b/app/lua/Makefile @@ -16,7 +16,7 @@ SUBDIRS = luac_cross GEN_LIBS = liblua.a endif -STD_CFLAGS=-std=gnu11 -Wimplicit +STD_CFLAGS=-std=gnu11 -Wimplicit -Wall ############################################################# # Configuration i.e. compile options etc. diff --git a/app/lua/lauxlib.c b/app/lua/lauxlib.c index 773d01a3..07835a19 100644 --- a/app/lua/lauxlib.c +++ b/app/lua/lauxlib.c @@ -824,10 +824,9 @@ static int errfsfile (lua_State *L, const char *what, int fnameindex) { } -LUALIB_API int luaL_loadfsfile (lua_State *L, const char *filename) { +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { LoadFSF lf; - int status, readstatus; - int c; + int status, c; int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ lf.extraline = 0; if (filename == NULL) { diff --git a/app/lua/lauxlib.h b/app/lua/lauxlib.h index 09dbd2c0..b769ad2f 100644 --- a/app/lua/lauxlib.h +++ b/app/lua/lauxlib.h @@ -79,7 +79,7 @@ LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); #ifdef LUA_CROSS_COMPILER LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); #else -LUALIB_API int (luaL_loadfsfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); #endif LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, const char *name); @@ -119,7 +119,7 @@ LUALIB_API void luaL_assertfail(const char *file, int line, const char *message) (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #else #define luaL_dofile(L, fn) \ - (luaL_loadfsfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) #endif #define luaL_dostring(L, s) \ diff --git a/app/lua/lbaselib.c b/app/lua/lbaselib.c index 02b69bc8..00e6959f 100644 --- a/app/lua/lbaselib.c +++ b/app/lua/lbaselib.c @@ -19,8 +19,6 @@ #include "lrotable.h" - - /* ** If your system does not support `stdout', you can just remove this function. ** If you need, you can define your own `print' function, following this @@ -40,20 +38,11 @@ static int luaB_print (lua_State *L) { if (s == NULL) return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("print")); -#if defined(LUA_USE_STDIO) - if (i>1) fputs("\t", c_stdout); - fputs(s, c_stdout); -#else - if (i>1) luai_writestring("\t", 1); - luai_writestring(s, strlen(s)); -#endif + if (i>1) puts("\t"); + puts(s); lua_pop(L, 1); /* pop result */ } -#if defined(LUA_USE_STDIO) - fputs("\n", c_stdout); -#else - luai_writeline(); -#endif + puts("\n"); return 0; } @@ -296,7 +285,7 @@ static int luaB_loadfile (lua_State *L) { #ifdef LUA_CROSS_COMPILER return load_aux(L, luaL_loadfile(L, fname)); #else - return load_aux(L, luaL_loadfsfile(L, fname)); + return load_aux(L, luaL_loadfile(L, fname)); #endif } @@ -343,7 +332,7 @@ static int luaB_dofile (lua_State *L) { #ifdef LUA_CROSS_COMPILER if (luaL_loadfile(L, fname) != 0) lua_error(L); #else - if (luaL_loadfsfile(L, fname) != 0) lua_error(L); + if (luaL_loadfile(L, fname) != 0) lua_error(L); #endif lua_call(L, 0, LUA_MULTRET); return lua_gettop(L) - n; diff --git a/app/lua/ldblib.c b/app/lua/ldblib.c index d3926e9d..f5193bdc 100644 --- a/app/lua/ldblib.c +++ b/app/lua/ldblib.c @@ -365,7 +365,7 @@ static int db_debug (lua_State *L) { #define LEVELS1 12 /* size of the first part of the stack */ #define LEVELS2 10 /* size of the second part of the stack */ -static int db_errorfb (lua_State *L) { +int debug_errorfb (lua_State *L) { int level; int firstpart = 1; /* still before eventual `...' */ int arg; @@ -436,7 +436,7 @@ LROT_PUBLIC_BEGIN(dblib) LROT_FUNCENTRY( setmetatable, db_setmetatable ) LROT_FUNCENTRY( setupvalue, db_setupvalue ) #endif - LROT_FUNCENTRY( traceback, db_errorfb ) + LROT_FUNCENTRY( traceback, debug_errorfb ) LROT_END(dblib, NULL, 0) LUALIB_API int luaopen_debug (lua_State *L) { diff --git a/app/lua/lflash.c b/app/lua/lflash.c index ba1c46d3..d2bf5cc2 100644 --- a/app/lua/lflash.c +++ b/app/lua/lflash.c @@ -70,7 +70,7 @@ struct OUTPUT { outBlock buffer; int ndx; uint32_t crc; - int (*fullBlkCB) (void); + void (*fullBlkCB) (void); int flashLen; int flagsLen; int flagsNdx; @@ -79,7 +79,6 @@ struct OUTPUT { } *out; #ifdef NODE_DEBUG -extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); void dumpStrt(stringtable *tb, const char *type) { int i,j; GCObject *o; @@ -173,15 +172,15 @@ LUAI_FUNC void luaN_init (lua_State *L) { } if ((fh->flash_sig & (~FLASH_SIG_ABSOLUTE)) != FLASH_SIG ) { - NODE_ERR("Flash sig not correct: %p vs %p\n", + NODE_ERR("Flash sig not correct: 0x%08x vs 0x%08x\n", fh->flash_sig & (~FLASH_SIG_ABSOLUTE), FLASH_SIG); return; } if (fh->pROhash == ALL_SET || ((fh->mainProto - cast(FlashAddr, fh)) >= fh->flash_size)) { - NODE_ERR("Flash size check failed: %p vs 0xFFFFFFFF; %p >= %p\n", - fh->mainProto - cast(FlashAddr, fh), fh->flash_size); + NODE_ERR("Flash size check failed: 0x%08x vs 0xFFFFFFFF; 0x%08x >= 0x%08x\n", + fh->pROhash, fh->mainProto - cast(FlashAddr, fh), fh->flash_size); return; } @@ -194,7 +193,7 @@ LUAI_FUNC void luaN_init (lua_State *L) { //extern void software_reset(void); static int loadLFS (lua_State *L); static int loadLFSgc (lua_State *L); -static int procFirstPass (void); +static void procFirstPass (void); /* * Library function called by node.flashreload(filename). @@ -270,7 +269,6 @@ LUALIB_API int luaN_reload_reboot (lua_State *L) { * - An array of the module names in the LFS */ LUAI_FUNC int luaN_index (lua_State *L) { - int i; int n = lua_gettop(L); /* Return nil + the LFS base address if the LFS size > 0 and it isn't loaded */ @@ -406,11 +404,10 @@ static uint8_t recall_byte (unsigned offset) { * - Once the flags array is in-buffer this is also captured. * This logic is slightly complicated by the last buffer is typically short. */ -int procFirstPass (void) { +void procFirstPass (void) { int len = (out->ndx % WRITE_BLOCKSIZE) ? out->ndx % WRITE_BLOCKSIZE : WRITE_BLOCKSIZE; if (out->ndx <= WRITE_BLOCKSIZE) { - uint32_t fl; /* Process the flash header and cache the FlashHeader fields we need */ FlashHeader *fh = cast(FlashHeader *, out->block[0]); out->flashLen = fh->flash_size; /* in bytes */ @@ -442,12 +439,10 @@ int procFirstPass (void) { memcpy(out->flags + out->flagsNdx, out->block[0]->byte + start, len - start); out->flagsNdx += (len -start) / WORDSIZE; /* flashLen and len are word aligned */ } - - return 1; } -int procSecondPass (void) { +void procSecondPass (void) { /* * The length rules are different for the second pass since this only processes * upto the flashLen and not the full image. This also works in word units. @@ -456,7 +451,7 @@ int procSecondPass (void) { int i, len = (out->ndx > out->flashLen) ? (out->flashLen % WRITE_BLOCKSIZE) / WORDSIZE : WRITE_BLOCKSIZE / WORDSIZE; - uint32_t *buf = (uint32_t *) out->buffer.byte, flags; + uint32_t *buf = (uint32_t *) out->buffer.byte, flags = 0; /* * Relocate all the addresses tagged in out->flags. This can't be done in * place because the out->blocks are still in use as dictionary content so @@ -492,7 +487,7 @@ int procSecondPass (void) { */ static int loadLFS (lua_State *L) { const char *fn = cast(const char *, lua_touserdata(L, 1)); - int i, n, res; + int i, res; uint32_t crc; /* Allocate and zero in and out structures */ @@ -541,12 +536,11 @@ static int loadLFS (lua_State *L) { flashErase(0,(out->flashLen - 1)/FLASH_PAGE_SIZE); flashSetPosition(0); - if (uzlib_inflate(get_byte, put_byte, recall_byte, - in->len, &crc, &in->inflate_state) != UZLIB_OK) - if (res < 0) { + if ((res = uzlib_inflate(get_byte, put_byte, recall_byte, + in->len, &crc, &in->inflate_state)) != UZLIB_OK) { const char *err[] = {"Data_error during decompression", "Chksum_error during decompression", - "Dictionary error during decompression" + "Dictionary error during decompression", "Memory_error during decompression"}; flash_error(err[UZLIB_DATA_ERROR - res]); } diff --git a/app/lua/lnodemcu.c b/app/lua/lnodemcu.c new file mode 100644 index 00000000..6d8cf1a2 --- /dev/null +++ b/app/lua/lnodemcu.c @@ -0,0 +1,116 @@ +/* +** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + + +#define lnodemcu_c +#define LUA_CORE + +#include "lua.h" +#include + +#include "lobject.h" +#include "lstate.h" +#include "lauxlib.h" +#include "lgc.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lrotable.h" +#include "platform.h" + +extern int debug_errorfb (lua_State *L); +#if 0 +extern int pipe_create(lua_State *L); +extern int pipe_read(lua_State *L); +extern int pipe_unread(lua_State *L); +extern int pipe_write(lua_State *L); +#endif +/* +** Error Reporting Task. We can't pass a string parameter to the error reporter +** directly through the task interface the call is wrapped in a C closure with +** the error string as an Upval and this is posted to call the Lua reporter. +*/ +static int report_traceback (lua_State *L) { +// **Temp** lua_rawgeti(L, LUA_REGISTRYINDEX, G(L)->error_reporter); + lua_getglobal(L, "print"); + lua_pushvalue(L, lua_upvalueindex(1)); + lua_call(L, 1, 0); /* Using an error handler would cause an infinite loop! */ + return 0; +} + +/* +** Catch all error handler for CB calls. This uses debug.traceback() to +** generate a full Lua traceback. +*/ +int luaN_traceback (lua_State *L) { + if (lua_isstring(L, 1)) { + lua_pushlightfunction(L, &debug_errorfb); + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback and return it as a string */ + lua_pushcclosure(L, report_traceback, 1); /* report with str as upval */ + luaN_posttask(L, LUA_TASK_HIGH); + } + return 0; +} + +/* +** Use in CBs and other C functions to call a Lua function. This includes +** an error handler which will catch any error and then post this to the +** registered reporter function as a separate follow-on task. +*/ +int luaN_call (lua_State *L, int narg, int res, int doGC) { // [-narg, +0, v] + int status; + int base = lua_gettop(L) - narg; + lua_pushcfunction(L, luaN_traceback); + lua_insert(L, base); /* put under args */ + status = lua_pcall(L, narg, (res < 0 ? LUA_MULTRET : res), base); + lua_remove(L, base); /* remove traceback function */ + /* force a complete garbage collection if requested */ + if (doGC) + lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + +static platform_task_handle_t task_handle = 0; + +/* +** Task callback handler. Uses luaN_call to do a protected call with full traceback +*/ +static void do_task (platform_task_param_t task_fn_ref, uint8_t prio) { + lua_State* L = lua_getstate(); + if (prio < 0|| prio > 2) + luaL_error(L, "invalid posk task"); + +/* Pop the CB func from the Reg */ +//dbg_printf("calling Reg[%u]\n", task_fn_ref); + lua_rawgeti(L, LUA_REGISTRYINDEX, (int) task_fn_ref); + luaL_checkanyfunction(L, -1); + luaL_unref(L, LUA_REGISTRYINDEX, (int) task_fn_ref); + lua_pushinteger(L, prio); + luaN_call (L, 1, 0, 1); +} + +/* +** Schedule a Lua function for task execution +*/ +#include "lstate.h" /*DEBUG*/ +LUA_API int luaN_posttask( lua_State* L, int prio ) { // [-1, +0, -] + if (!task_handle) + task_handle = platform_task_get_id(do_task); + + if (!lua_isanyfunction(L, -1) || prio < LUA_TASK_LOW|| prio > LUA_TASK_HIGH) + luaL_error(L, "invalid posk task"); +//void *cl = clvalue(L->top-1); + int task_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX); +//dbg_printf("posting Reg[%u]=%p\n",task_fn_ref,cl); + if(!platform_post(prio, task_handle, (platform_task_param_t)task_fn_ref)) { + luaL_unref(L, LUA_REGISTRYINDEX, task_fn_ref); + luaL_error(L, "Task queue overflow. Task not posted"); + } + return task_fn_ref; +} + diff --git a/app/lua/loadlib.c b/app/lua/loadlib.c index a4ab42c9..5005969b 100644 --- a/app/lua/loadlib.c +++ b/app/lua/loadlib.c @@ -397,7 +397,7 @@ static int loader_Lua (lua_State *L) { #ifdef LUA_CROSS_COMPILER if (luaL_loadfile(L, filename) != 0) #else - if (luaL_loadfsfile(L, filename) != 0) + if (luaL_loadfile(L, filename) != 0) #endif loaderror(L, filename); return 1; /* library loaded successfully */ diff --git a/app/lua/lobject.h b/app/lua/lobject.h index 7741ddbf..62bf224f 100644 --- a/app/lua/lobject.h +++ b/app/lua/lobject.h @@ -113,7 +113,7 @@ typedef struct lua_TValue { #define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) #define ttisrotable(o) (ttype(o) == LUA_TROTABLE) #define ttislightfunction(o) (ttype(o) == LUA_TLIGHTFUNCTION) - +#define ttisanyfunction(o) (ttisfunction(o) || ttislightfunction(o)) /* Macros to access values */ diff --git a/app/lua/lstate.c b/app/lua/lstate.c index 55e107a6..13600f83 100644 --- a/app/lua/lstate.c +++ b/app/lua/lstate.c @@ -197,11 +197,12 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { g->memlimit = 0; #endif #ifndef LUA_CROSS_COMPILER - g->ROstrt.size = 0; - g->ROstrt.nuse = 0; - g->ROstrt.hash = NULL; - g->ROpvmain = NULL; - g->LFSsize = 0; + g->ROstrt.size = 0; + g->ROstrt.nuse = 0; + g->ROstrt.hash = NULL; + g->ROpvmain = NULL; + g->LFSsize = 0; + g->error_reporter = 0; #endif for (i=0; imt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { diff --git a/app/lua/lstate.h b/app/lua/lstate.h index 88b7d57f..bd9b3660 100644 --- a/app/lua/lstate.h +++ b/app/lua/lstate.h @@ -98,6 +98,7 @@ typedef struct global_State { stringtable ROstrt; /* Flash-based hash table for RO strings */ Proto *ROpvmain; /* Flash-based Proto main */ int LFSsize; /* Size of Lua Flash Store */ + int error_reporter; /* Registry Index of error reporter task */ #endif } global_State; diff --git a/app/lua/lua.c b/app/lua/lua.c index 81b04751..80afc690 100644 --- a/app/lua/lua.c +++ b/app/lua/lua.c @@ -1,18 +1,16 @@ /* -** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $ -** Lua stand-alone interpreter +** NodeMCU Lua 5.1 main initiator and comand interpreter ** See Copyright Notice in lua.h +** +** Note this is largely a backport of some new Lua 5.3 version but +** with API changes for Lua 5.1 compatability */ - #include #include #include -#include "user_interface.h" #include "user_version.h" -#include "driver/readline.h" -#include "driver/uart.h" -#include "platform.h" +#include "driver/input.h" #define lua_c @@ -21,498 +19,283 @@ #include "lauxlib.h" #include "lualib.h" #include "legc.h" -#include "lflash.h" #include "os_type.h" +extern int debug_errorfb (lua_State *L); +extern int pipe_create(lua_State *L); +extern int pipe_read(lua_State *L); +extern int pipe_unread(lua_State *L); + +#ifndef LUA_INIT_STRING +#define LUA_INIT_STRING "@init.lua" +#endif + +static int MLref = LUA_NOREF; lua_State *globalL = NULL; -static lua_Load gLoad; -static const char *progname = LUA_PROGNAME; +static int pmain (lua_State *L); +static int dojob (lua_State *L); -static void l_message (const char *pname, const char *msg) { -#if defined(LUA_USE_STDIO) - if (pname) fprintf(c_stderr, "%s: ", pname); - fprintf(c_stderr, "%s\n", msg); - fflush(c_stderr); -#else - if (pname) luai_writestringerror("%s: ", pname); +static void l_message (const char *msg) { luai_writestringerror("%s\n", msg); -#endif } - static int report (lua_State *L, int status) { if (status && !lua_isnil(L, -1)) { const char *msg = lua_tostring(L, -1); if (msg == NULL) msg = "(error object is not a string)"; - l_message(progname, msg); + l_message(msg); lua_pop(L, 1); } return status; } +static void l_print(lua_State *L, int n) { + lua_getglobal(L, "print"); + lua_insert(L, -n-1); + if (lua_pcall(L, n, 0, 0) != 0) + l_message(lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)", + lua_tostring(L, -1))); +} static int traceback (lua_State *L) { - if (!lua_isstring(L, 1)) /* 'message' not a string? */ - return 1; /* keep it intact */ - lua_getfield(L, LUA_GLOBALSINDEX, "debug"); - if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) { - lua_pop(L, 1); - return 1; + if (lua_isstring(L, 1)) { + lua_pushlightfunction(L, &debug_errorfb); + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ } - lua_getfield(L, -1, "traceback"); - if (!lua_isfunction(L, -1) && !lua_islightfunction(L, -1)) { - lua_pop(L, 2); - return 1; - } - lua_pushvalue(L, 1); /* pass error message */ - lua_pushinteger(L, 2); /* skip this function and traceback */ - lua_call(L, 2, 1); /* call debug.traceback */ + lua_settop(L, 1); return 1; } - static int docall (lua_State *L, int narg, int clear) { int status; - int base = lua_gettop(L) - narg; /* function index */ - lua_pushcfunction(L, traceback); /* push traceback function */ - lua_insert(L, base); /* put it under chunk and args */ - // signal(SIGINT, laction); + int base = lua_gettop(L) - narg; /* function index */ + lua_pushlightfunction(L, &traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); - // signal(SIGINT, SIG_DFL); - lua_remove(L, base); /* remove traceback function */ + lua_remove(L, base); /* remove traceback function */ /* force a complete garbage collection in case of errors */ if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); return status; } - static void print_version (lua_State *L) { - lua_pushliteral (L, "\n" NODE_VERSION " build " BUILD_DATE " powered by " LUA_RELEASE " on SDK "); + lua_pushliteral (L, "\n" NODE_VERSION " build " BUILD_DATE + " powered by " LUA_RELEASE " on SDK "); lua_pushstring (L, SDK_VERSION); lua_concat (L, 2); const char *msg = lua_tostring (L, -1); - l_message (NULL, msg); + l_message (msg); lua_pop (L, 1); } - -static int getargs (lua_State *L, char **argv, int n) { - int narg; - int i; - int argc = 0; - while (argv[argc]) argc++; /* count total number of arguments */ - narg = argc - (n + 1); /* number of arguments to the script */ - luaL_checkstack(L, narg + 3, "too many arguments to script"); - for (i=n+1; i < argc; i++) - lua_pushstring(L, argv[i]); - lua_createtable(L, narg, n + 1); - for (i=0; i < argc; i++) { - lua_pushstring(L, argv[i]); - lua_rawseti(L, -2, i - n); - } - return narg; -} - -static int dofsfile (lua_State *L, const char *name) { - int status = luaL_loadfsfile(L, name) || docall(L, 0, 1); +static int dofile (lua_State *L, const char *name) { + int status = luaL_loadfile(L, name) || docall(L, 0, 1); return report(L, status); } - static int dostring (lua_State *L, const char *s, const char *name) { int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); return report(L, status); } - -static int dolibrary (lua_State *L, const char *name) { - lua_getglobal(L, "require"); - lua_pushstring(L, name); - return report(L, docall(L, 1, 1)); -} - static const char *get_prompt (lua_State *L, int firstline) { const char *p; - lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); + lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2"); p = lua_tostring(L, -1); if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); lua_pop(L, 1); /* remove global */ return p; } - +#define EOFMARK LUA_QL("") static int incomplete (lua_State *L, int status) { if (status == LUA_ERRSYNTAX) { size_t lmsg; const char *msg = lua_tolstring(L, -1, &lmsg); - const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); - if (strstr(msg, LUA_QL("")) == tp) { + if (!strcmp(msg+lmsg-sizeof(EOFMARK)+1, EOFMARK)) { lua_pop(L, 1); return 1; } } - return 0; /* else... */ -} - - -/* check that argument has no extra characters at the end */ -#define notail(x) {if ((x)[2] != '\0') return -1;} - - -static int collectargs (char **argv, int *pi, int *pv, int *pe) { - int i; - for (i = 1; argv[i] != NULL; i++) { - if (argv[i][0] != '-') /* not an option? */ - return i; - switch (argv[i][1]) { /* option */ - case '-': - notail(argv[i]); - return (argv[i+1] != NULL ? i+1 : 0); - case '\0': - return i; - case 'i': - notail(argv[i]); - *pi = 1; /* go through */ - case 'v': - notail(argv[i]); - *pv = 1; - break; - case 'e': - *pe = 1; /* go through */ - case 'm': /* go through */ - case 'l': - if (argv[i][2] == '\0') { - i++; - if (argv[i] == NULL) return -1; - } - break; - default: return -1; /* invalid option */ - } - } return 0; } - -static int runargs (lua_State *L, char **argv, int n) { - int i; - for (i = 1; i < n; i++) { - if (argv[i] == NULL) continue; - lua_assert(argv[i][0] == '-'); - switch (argv[i][1]) { /* option */ - case 'e': { - const char *chunk = argv[i] + 2; - if (*chunk == '\0') chunk = argv[++i]; - lua_assert(chunk != NULL); - if (dostring(L, chunk, "=(command line)") != 0) - return 1; - break; - } - case 'm': { - const char *limit = argv[i] + 2; - int memlimit=0; - if (*limit == '\0') limit = argv[++i]; - lua_assert(limit != NULL); - memlimit = atoi(limit); - lua_gc(L, LUA_GCSETMEMLIMIT, memlimit); - break; - } - case 'l': { - const char *filename = argv[i] + 2; - if (*filename == '\0') filename = argv[++i]; - lua_assert(filename != NULL); - if (dolibrary(L, filename)) - return 1; /* stop if file fails */ - break; - } - default: break; - } - } - return 0; -} - - -#ifndef LUA_INIT_STRING -#define LUA_INIT_STRING "@init.lua" -#endif - -static int handle_luainit (lua_State *L) { - const char *init = LUA_INIT_STRING; - if (init[0] == '@') - return dofsfile(L, init+1); - else - return dostring(L, init, LUA_INIT); -} - - -struct Smain { - int argc; - char **argv; +/* +** dojob is the CB reader for the input pipe and follows the calling convention +** for pipe reader CBs. It has one argument: the stdin pipe that it is reading. +*/ +static int dojob (lua_State *L) { + size_t l; int status; -}; + const char *prompt; +//dbg_printf("dojob entered\n"); + lua_settop(L, 1); /* pipe obj at S[1] */ + lua_pushlightfunction(L, &pipe_read); /* pobj:read at S[2] */ + lua_pushvalue(L, 1); /* dup pobj to S[3] */ + lua_pushliteral(L, "\n+"); /* S[4] = "\n+" */ + lua_call(L, 2, 1); /* S[2] = pobj:read("\n+") */ + const char* b = lua_tolstring(L, 2, &l); /* b = NULL if S[2] is nil */ + + if ((lua_isnil(L, 2) || l == 0)) { + /* If the pipe is empty then return false to suppress automatic reposting */ +//dbg_printf("stdin empty\n"); + lua_pushboolean(L, false); + return 1; /* return false */ + } + + if (b[l-1] != '\n') { +//dbg_printf("unreading part line\n"); + /* likewise if not CR terminated, then unread and ditto */ + lua_pushlightfunction(L, &pipe_unread); /* pobj:read at S[1] */ + lua_insert(L, 1); + lua_call(L, 2, 0); /* pobj:unread(line) */ + lua_pushboolean(L, false); + return 1; /* return false */ + } else { +//dbg_printf("popping CR terminated string(%d) %s", l-1, b); + } + /* + * Now we can process a proper CR terminated line + */ + lua_pushlstring(L, b, --l); /* remove end CR */ + lua_remove(L, 2); + b = lua_tostring(L, 2); + + if (MLref != LUA_NOREF) { + /* processing multiline */ + lua_rawgeti(L, LUA_REGISTRYINDEX, MLref); /* insert prev lines(s) */ + lua_pushliteral(L, "\n"); /* insert CR */ + lua_pushvalue(L, 2); /* dup new line */ + lua_concat(L, 3); /* concat all 3 */ + lua_remove(L, 2); /* and shift down to S[2] */ + } else if (b[0] == '=') { + /* If firstline and of the format = */ + lua_pushfstring(L, "return %s", b+1); + lua_remove(L, 2); + } + + /* ToS is at S[2] which contains the putative chunk to be compiled */ + + status = luaL_loadbuffer(L, lua_tostring(L, 2), lua_strlen(L, 2), "=stdin"); + + if (incomplete(L, status)) { + /* Store line back in the Reg mlref sot */ + if (MLref == LUA_NOREF) { + MLref = luaL_ref(L, LUA_REGISTRYINDEX); + } else { + lua_rawseti(L, LUA_REGISTRYINDEX, MLref); + } + } else { + /* compile finished OK or with hard error */ + lua_remove(L, 2); /* remove line because now redundant */ + if (MLref!= LUA_NOREF) { /* also remove multiline if it exists */ + luaL_unref(L, LUA_REGISTRYINDEX, MLref); + MLref= LUA_NOREF; + } + /* Execute the compiled chunk of successful */ + if (status == 0) { + status = docall(L, 0, 0); + } + /* print any returned results or error message */ + if (status && !lua_isnil(L, -1)) + l_print(L, 1); + if (status == 0 && lua_gettop(L) - 1) + l_print(L, lua_gettop(L) - 1); + + lua_settop(L, 2); + if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); + } + + prompt = get_prompt(L, MLref!= LUA_NOREF ? 0 : 1); + input_setprompt(prompt); + puts(prompt); + + lua_pushnil(L); + return 1; /* return nil will retask if pipe not empty */ +} + + +/* + * Kick off library and UART input handling before opening the module + * libraries. Note that as this is all done within a Lua task, so error + * handling is left to the Lua task traceback mechanism. + */ +extern void luaL_dbgbreak(void); static int pmain (lua_State *L) { - struct Smain *s = (struct Smain *)lua_touserdata(L, 1); - char **argv = s->argv; - int script; - int has_i = 0, has_v = 0, has_e = 0; + const char *init = LUA_INIT_STRING; globalL = L; - if (argv[0] && argv[0][0]) progname = argv[0]; - lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ - luaL_openlibs(L); /* open libraries */ - lua_gc(L, LUA_GCRESTART, 0); + + lua_gc(L, LUA_GCSTOP, 0); /* stop GC during initialization */ + luaL_openlibs(L); /* open libraries */ + lua_gc(L, LUA_GCRESTART, 0); /* restart GC and set EGC mode */ + legc_set_mode( L, EGC_ALWAYS, 4096 ); + lua_settop(L, 0); + + lua_pushliteral(L, "stdin"); + lua_pushlightfunction(L, &pipe_create); + lua_pushlightfunction(L, &dojob); + lua_pushinteger(L, LUA_TASK_LOW); + lua_call(L, 2, 1); /* ToS = pipe.create(dojob, low_priority) */ + lua_rawset(L, LUA_REGISTRYINDEX); /* and stash input pipe in Reg["stdin"] */ + + input_setup(LUA_MAXINPUT, get_prompt(L, 1)); + lua_input_string(" \n", 2); /* queue CR to issue first prompt */ print_version(L); - s->status = handle_luainit(L); - script = collectargs(argv, &has_i, &has_v, &has_e); - if (script < 0) { /* invalid args? */ - s->status = 1; - return 0; - } - s->status = runargs(L, argv, (script > 0) ? script : s->argc); - if (s->status != 0) return 0; + + /* and last of all, kick off application initialisation */ + if (init[0] == '@') + dofile(L, init+1); + else + dostring(L, init, LUA_INIT); return 0; } -static void dojob(lua_Load *load); -static bool readline(lua_Load *load); - -#ifdef LUA_RPC -int main (int argc, char **argv) { -#else -int lua_main (int argc, char **argv) { -#endif - int status; - struct Smain s; - -#if defined(NODE_DEBUG) && defined(DEVELOPMENT_USE_GDB) && \ - defined(DEVELOPMENT_BREAK_ON_STARTUP_PIN) && DEVELOPMENT_BREAK_ON_STARTUP_PIN > 0 - platform_gpio_mode( DEVELOPMENT_BREAK_ON_STARTUP_PIN, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP ); - lua_assert(platform_gpio_read(DEVELOPMENT_BREAK_ON_STARTUP_PIN)); // Break if pin pulled low -#endif - - lua_State *L = lua_open(); /* create state */ +/* +** The system initialisation CB nodemcu_init() calls lua_main() to startup +** the Lua environment by calling lua_open() which initiates the core Lua VM. +** The initialisation of the libraries, etc. is carried out by pmain in a +** separate Lua task, which also kicks off the user application through the +** LUA_INIT_STRING hook. +*/ +void lua_main (void) { + lua_State *L = lua_open(); /* create state */ if (L == NULL) { - l_message(argv[0], "cannot create state: not enough memory"); - return EXIT_FAILURE; + l_message("cannot create state: not enough memory"); + return; } - s.argc = argc; - s.argv = argv; - - status = lua_cpcall(L, &pmain, &s); - - report(L, status); - - gLoad.L = L; - gLoad.firstline = 1; - gLoad.done = 0; - gLoad.line = malloc(LUA_MAXINPUT); - gLoad.len = LUA_MAXINPUT; - gLoad.line_position = 0; - gLoad.prmt = get_prompt(L, 1); - - dojob(&gLoad); - - NODE_DBG("Heap size:%d.\n",system_get_free_heap_size()); - legc_set_mode( L, EGC_ALWAYS, 4096 ); - // legc_set_mode( L, EGC_ON_MEM_LIMIT, 4096 ); - // lua_close(L); - return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; + lua_pushlightfunction(L, &pmain); /* Call 'pmain' as a high priority task */ + luaN_posttask(L, LUA_TASK_HIGH); } -int lua_put_line(const char *s, size_t l) { - if (s == NULL || ++l > LUA_MAXINPUT || gLoad.line_position > 0) - return 0; - memcpy(gLoad.line, s, l); - gLoad.line[l] = '\0'; - gLoad.line_position = l; - gLoad.done = 1; - NODE_DBG("Get command: %s\n", gLoad.line); - return 1; -} - -void lua_handle_input (bool force) -{ - while (gLoad.L && (force || readline (&gLoad))) { - NODE_DBG("Handle Input: first=%u, pos=%u, len=%u, actual=%u, line=%s\n", gLoad.firstline, - gLoad.line_position, gLoad.len, strlen(gLoad.line), gLoad.line); - dojob (&gLoad); - force = false; - } -} - -void donejob(lua_Load *load){ - lua_close(load->L); -} - -static void dojob(lua_Load *load){ - size_t l, rs; - int status; - char *b = load->line; - lua_State *L = load->L; - - const char *oldprogname = progname; - progname = NULL; - - do{ - if(load->done == 1){ - l = strlen(b); - if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ - b[l-1] = '\0'; /* remove it */ - if (load->firstline && b[0] == '=') /* first line starts with `=' ? */ - lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ - else - lua_pushstring(L, b); - if(load->firstline != 1){ - lua_pushliteral(L, "\n"); /* add a new line... */ - lua_insert(L, -2); /* ...between the two lines */ - lua_concat(L, 3); /* join them */ - } - - status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); - if (!incomplete(L, status)) { /* cannot try to add lines? */ - lua_remove(L, 1); /* remove line */ - if (status == 0) { - status = docall(L, 0, 0); - } - report(L, status); - if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ - lua_getglobal(L, "print"); - lua_insert(L, 1); - if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) - l_message(progname, lua_pushfstring(L, - "error calling " LUA_QL("print") " (%s)", - lua_tostring(L, -1))); - } - load->firstline = 1; - load->prmt = get_prompt(L, 1); - lua_settop(L, 0); - /* force a complete garbage collection in case of errors */ - if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); - } else { - load->firstline = 0; - load->prmt = get_prompt(L, 0); - } - } - }while(0); - - progname = oldprogname; - - load->done = 0; - load->line_position = 0; - memset(load->line, 0, load->len); - puts(load->prmt); -} - -#ifndef uart_putc -#define uart_putc uart0_putc -#endif -extern bool uart_on_data_cb(const char *buf, size_t len); -extern bool uart0_echo; -extern bool run_input; -extern uint16_t need_len; -extern int16_t end_char; -static char last_nl_char = '\0'; -static bool readline(lua_Load *load){ - // NODE_DBG("readline() is called.\n"); - bool need_dojob = false; - char ch; - while (uart_getc(&ch)) - { - if(run_input) - { - char tmp_last_nl_char = last_nl_char; - // reset marker, will be finally set below when newline is processed - last_nl_char = '\0'; - - /* handle CR & LF characters - filters second char of LF&CR (\n\r) or CR&LF (\r\n) sequences */ - if ((ch == '\r' && tmp_last_nl_char == '\n') || // \n\r sequence -> skip \r - (ch == '\n' && tmp_last_nl_char == '\r')) // \r\n sequence -> skip \n - { - continue; - } - - /* backspace key */ - else if (ch == 0x7f || ch == 0x08) - { - if (load->line_position > 0) - { - if(uart0_echo) uart_putc(0x08); - if(uart0_echo) uart_putc(' '); - if(uart0_echo) uart_putc(0x08); - load->line_position--; - } - load->line[load->line_position] = 0; - continue; - } - /* EOT(ctrl+d) */ - // else if (ch == 0x04) - // { - // if (load->line_position == 0) - // // No input which makes lua interpreter close - // donejob(load); - // else - // continue; - // } - - /* end of line */ - if (ch == '\r' || ch == '\n') - { - last_nl_char = ch; - - load->line[load->line_position] = 0; - if(uart0_echo) uart_putc('\n'); - uart_on_data_cb(load->line, load->line_position); - if (load->line_position == 0) - { - /* Get a empty line, then go to get a new line */ - puts(load->prmt); - continue; - } else { - load->done = 1; - need_dojob = true; - break; - } - } - - /* other control character or not an acsii character */ - // if (ch < 0x20 || ch >= 0x80) - // { - // continue; - // } - - /* echo */ - if(uart0_echo) uart_putc(ch); - - /* it's a large line, discard it */ - if ( load->line_position + 1 >= load->len ){ - load->line_position = 0; - } - } - - load->line[load->line_position] = ch; - load->line_position++; - - if(!run_input) - { - if( ((need_len!=0) && (load->line_position >= need_len)) || \ - (load->line_position >= load->len) || \ - ((end_char>=0) && ((unsigned char)ch==(unsigned char)end_char)) ) - { - uart_on_data_cb(load->line, load->line_position); - load->line_position = 0; - } - } - - ch = 0; - } - - if( (load->line_position > 0) && (!run_input) && (need_len==0) && (end_char<0) ) - { - uart_on_data_cb(load->line, load->line_position); - load->line_position = 0; - } - - return need_dojob; +/* +** The Lua interpreter is event-driven and task-oriented in NodeMCU rather than +** based on a readline poll loop as in the standard implementation. Input lines +** can come from one of two sources: the application can "push" lines for the +** interpreter to compile and execute, or they can come from the UART. To +** minimise application blocking, the lines are queued in a pipe when received, +** with the Lua interpreter task attached to the pipe as its reader task. This +** CB processes one line of input per task execution. +** +** Even though lines can be emitted from independent sources (the UART and the +** node API), and they could in theory get interleaved, the strategy here is +** "let the programmer beware": interactive input will normally only occur in +** development and injected input occur in telnet type applications. If there +** is a need for interlocks, then the application should handle this. +*/ +//static int n = 0; +void lua_input_string (const char *line, int len) { + lua_State *L = globalL; + lua_getfield(L, LUA_REGISTRYINDEX, "stdin"); + lua_rawgeti(L, -1, 1); /* get the pipe_write from stdin[1] */ + lua_insert(L, -2); /* stick above the pipe */ + lua_pushlstring(L, line, len); + +//const char*b = lua_tostring(L, -1); +//dbg_printf("Pushing (%u): %s", len, b); + lua_call(L, 2, 0); /* stdin:write(line) */ } diff --git a/app/lua/lua.h b/app/lua/lua.h index a4b5c4e2..874f0fc4 100644 --- a/app/lua/lua.h +++ b/app/lua/lua.h @@ -273,9 +273,11 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); #define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) #define lua_islightfunction(L,n) (lua_type(L, (n)) == LUA_TLIGHTFUNCTION) +#define lua_isanyfunction(L,n) (lua_isfunction(L,n) || lua_islightfunction(L,n)) #define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) #define lua_isrotable(L,n) (lua_type(L, (n)) == LUA_TROTABLE) -#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isanytable(L,n) (lua_istable(L,n) || lua_isrotable(L,n)) +#define lua_islightuserdata(L,n) (lua_type(L, (n) == LUA_TLIGHTUSERDATA) #define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) #define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) #define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) @@ -375,20 +377,19 @@ struct lua_Debug { /* }====================================================================== */ -typedef struct __lua_load{ - lua_State *L; - int firstline; - char *line; - int line_position; - size_t len; - int done; - const char *prmt; -}lua_Load; - -int lua_main( int argc, char **argv ); #ifndef LUA_CROSS_COMPILER -void lua_handle_input (bool force); +#define LUA_QUEUE_APP 0 +#define LUA_QUEUE_UART 1 +#define LUA_TASK_LOW 0 +#define LUA_TASK_MEDIUM 1 +#define LUA_TASK_HIGH 2 + +void lua_main (void); +void lua_input_string (const char *line, int len); +int luaN_posttask (lua_State* L, int prio); +int luaN_call (lua_State *L, int narg, int res, int dogc); +/**DEBUG**/extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); #endif /****************************************************************************** diff --git a/app/modules/gpio.c b/app/modules/gpio.c index 939d3d25..626ce186 100644 --- a/app/modules/gpio.c +++ b/app/modules/gpio.c @@ -5,6 +5,7 @@ #include "lauxlib.h" #include "lmem.h" #include "platform.h" +#include "task/task.h" #include "user_interface.h" #include #include diff --git a/app/modules/gpio_pulse.c b/app/modules/gpio_pulse.c index 15909572..5032d442 100644 --- a/app/modules/gpio_pulse.c +++ b/app/modules/gpio_pulse.c @@ -45,7 +45,7 @@ typedef struct { static int active_pulser_ref; static pulse_t *active_pulser; -static task_handle_t tasknumber; +static platform_task_handle_t tasknumber; static int gpio_pulse_push_state(lua_State *L, pulse_t *pulser) { uint32_t now; @@ -321,7 +321,7 @@ static void ICACHE_RAM_ATTR gpio_pulse_timeout(os_param_t p) { active_pulser->steps++; } platform_hw_timer_close(TIMER_OWNER); - task_post_low(tasknumber, (task_param_t)0); + platform_post_low(tasknumber, 0); return; } active_pulser->steps++; @@ -341,7 +341,7 @@ static void ICACHE_RAM_ATTR gpio_pulse_timeout(os_param_t p) { int16_t stop = active_pulser->stop_pos; if (stop == -2 || stop == active_pulser->entry_pos) { platform_hw_timer_close(TIMER_OWNER); - task_post_low(tasknumber, (task_param_t)0); + platform_post_low(tasknumber, 0); return; } @@ -488,7 +488,7 @@ LROT_END( gpio_pulse, gpio_pulse, LROT_MASK_INDEX ) int gpio_pulse_init(lua_State *L) { luaL_rometatable(L, "gpio.pulse", LROT_TABLEREF(pulse)); - tasknumber = task_get_id(gpio_pulse_task); + tasknumber = platform_task_get_id(gpio_pulse_task); return 0; } diff --git a/app/modules/net.c b/app/modules/net.c index fdbd575d..51efae71 100644 --- a/app/modules/net.c +++ b/app/modules/net.c @@ -736,6 +736,14 @@ int net_getaddr( lua_State *L ) { lua_pushstring(L, addr_str); return 2; } +#if 0 +static void dbg_print_ud(const char *title, lnet_userdata *ud) { + int i; + dbg_printf("%s: Userdata %p:", title, ud); + for (i=0; i<(sizeof(*ud)/sizeof(uint32_t)); i++) + dbg_printf( " 0x%08x", ((uint32_t *)ud)[i]); + dbg_printf("\n"); +#endif // Lua: client/server/socket:close() int net_close( lua_State *L ) { @@ -764,11 +772,14 @@ int net_close( lua_State *L ) { } if (ud->type == TYPE_TCP_SERVER || (ud->pcb == NULL && ud->client.wait_dns == 0)) { - lua_gc(L, LUA_GCSTOP, 0); +// lua_gc(L, LUA_GCSTOP, 0); luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); ud->self_ref = LUA_NOREF; - lua_gc(L, LUA_GCRESTART, 0); +// lua_gc(L, LUA_GCRESTART, 0); } +#if 0 + dbg_print_ud("close exit", ud); +#endif return 0; } @@ -813,10 +824,13 @@ int net_delete( lua_State *L ) { ud->server.cb_accept_ref = LUA_NOREF; break; } - lua_gc(L, LUA_GCSTOP, 0); +// lua_gc(L, LUA_GCSTOP, 0); luaL_unref(L, LUA_REGISTRYINDEX, ud->self_ref); ud->self_ref = LUA_NOREF; - lua_gc(L, LUA_GCRESTART, 0); +// lua_gc(L, LUA_GCRESTART, 0); +#if 0 + dbg_print_ud("delete end", ud); +#endif return 0; } diff --git a/app/modules/node.c b/app/modules/node.c index 167b8b21..c4f3151d 100644 --- a/app/modules/node.c +++ b/app/modules/node.c @@ -227,69 +227,59 @@ static int node_heap( lua_State* L ) return 1; } -extern int lua_put_line(const char *s, size_t l); -extern bool user_process_input(bool force); - // Lua: input("string") static int node_input( lua_State* L ) { - size_t l = 0; - const char *s = luaL_checklstring(L, 1, &l); - if (lua_put_line(s, l)) { - NODE_DBG("Result (if any):\n"); - user_process_input(true); - } + luaL_checkstring(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, "stdin"); + lua_rawgeti(L, -1, 1); /* get the pipe_write func from stdin[1] */ + lua_insert(L, -2); /* and move above the pipe ref */ + lua_pushvalue(L, 1); + lua_call(L, 2, 0); /* stdin:write(line) */ return 0; } -static int output_redir_ref = LUA_NOREF; static int serial_debug = 1; + void output_redirect(const char *str) { lua_State *L = lua_getstate(); - // if(strlen(str)>=TX_BUFF_SIZE){ - // NODE_ERR("output too long.\n"); - // return; - // } + int n = lua_gettop(L); + lua_pushliteral(L, "stdout"); + lua_rawget(L, LUA_REGISTRYINDEX); /* fetch reg.stdout */ + if (lua_istable(L, -1)) { /* reg.stdout is pipe */ + if (serial_debug) { + uart0_sendStr(str); + } + lua_rawgeti(L, -1, 1); /* get the pipe_write func from stdout[1] */ + lua_insert(L, -2); /* and move above the pipe ref */ + lua_pushstring(L, str); + lua_call(L, 2, 0); /* Reg.stdout:write(str) */ - if (output_redir_ref == LUA_NOREF) { - uart0_sendStr(str); - return; - } - - if (serial_debug != 0) { + } else { /* reg.stdout == nil */ uart0_sendStr(str); } - - lua_rawgeti(L, LUA_REGISTRYINDEX, output_redir_ref); - lua_pushstring(L, str); - lua_call(L, 1, 0); // this call back function should never user output. + lua_settop(L, n); /* Make sure all code paths leave stack unchanged */ } +extern int pipe_create(lua_State *L); + // Lua: output(function(c), debug) static int node_output( lua_State* L ) { - // luaL_checkanyfunction(L, 1); - if (lua_type(L, 1) == LUA_TFUNCTION || lua_type(L, 1) == LUA_TLIGHTFUNCTION) { - lua_pushvalue(L, 1); // copy argument (func) to the top of stack - if (output_redir_ref != LUA_NOREF) - luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref); - output_redir_ref = luaL_ref(L, LUA_REGISTRYINDEX); - } else { // unref the key press function - if (output_redir_ref != LUA_NOREF) - luaL_unref(L, LUA_REGISTRYINDEX, output_redir_ref); - output_redir_ref = LUA_NOREF; + serial_debug = (lua_isnumber(L, 2) && lua_tointeger(L, 2) == 0) ? 0 : 1; + lua_settop(L, 1); + if (lua_isanyfunction(L, 1)) { + lua_pushlightfunction(L, &pipe_create); + lua_insert(L, 1); + lua_pushinteger(L, LUA_TASK_MEDIUM); + lua_call(L, 2, 1); /* T[1] = pipe.create(dojob, low_priority) */ + } else { // remove the stdout pipe + lua_pop(L,1); + lua_pushnil(L); /* T[1] = nil */ serial_debug = 1; - return 0; } - - if ( lua_isnumber(L, 2) ) - { - serial_debug = lua_tointeger(L, 2); - if (serial_debug != 0) - serial_debug = 1; - } else { - serial_debug = 1; // default to 1 - } - + lua_pushliteral(L, "stdout"); + lua_insert(L, 1); + lua_rawset(L, LUA_REGISTRYINDEX); /* Reg.stdout = nil or pipe */ return 0; } @@ -330,7 +320,7 @@ static int node_compile( lua_State* L ) output[strlen(output) - 1] = '\0'; NODE_DBG(output); NODE_DBG("\n"); - if (luaL_loadfsfile(L, fname) != 0) { + if (luaL_loadfile(L, fname) != 0) { luaM_free( L, output ); return luaL_error(L, lua_tostring(L, -1)); } @@ -371,39 +361,19 @@ static int node_compile( lua_State* L ) return 0; } -// Task callback handler for node.task.post() -static task_handle_t do_node_task_handle; -static void do_node_task (task_param_t task_fn_ref, uint8_t prio) -{ - lua_State* L = lua_getstate(); - lua_rawgeti(L, LUA_REGISTRYINDEX, (int)task_fn_ref); - luaL_unref(L, LUA_REGISTRYINDEX, (int)task_fn_ref); - lua_pushinteger(L, prio); - lua_call(L, 1, 0); -} - // Lua: node.task.post([priority],task_cb) -- schedule a task for execution next static int node_task_post( lua_State* L ) { - int n = 1, Ltype = lua_type(L, 1); + int n=1; unsigned priority = TASK_PRIORITY_MEDIUM; - if (Ltype == LUA_TNUMBER) { + if (lua_type(L, 1) == LUA_TNUMBER) { priority = (unsigned) luaL_checkint(L, 1); luaL_argcheck(L, priority <= TASK_PRIORITY_HIGH, 1, "invalid priority"); - Ltype = lua_type(L, ++n); - } - luaL_argcheck(L, Ltype == LUA_TFUNCTION || Ltype == LUA_TLIGHTFUNCTION, n, "invalid function"); - lua_pushvalue(L, n); - - int task_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX); - - if (!do_node_task_handle) // bind the task handle to do_node_task on 1st call - do_node_task_handle = task_get_id(do_node_task); - - if(!task_post(priority, do_node_task_handle, (task_param_t)task_fn_ref)) { - luaL_unref(L, LUA_REGISTRYINDEX, task_fn_ref); - luaL_error(L, "Task queue overflow. Task not posted"); + n++; } + luaL_checkanyfunction(L, n); + lua_settop(L, n); + (void) luaN_posttask(L, priority); return 0; } diff --git a/app/modules/pipe.c b/app/modules/pipe.c index 7e300eaf..322c3603 100644 --- a/app/modules/pipe.c +++ b/app/modules/pipe.c @@ -3,15 +3,45 @@ ** table to store the LUAL_BUFFERSIZE byte array chunks instead of the stack. ** Writing is always to the last UD in the table and overflow pushes a new UD to ** the end of the table. Reading is always from the first UD in the table and -** underrun removes the first UD to shift a new one into slot 1. +** underrun removes the first UD to shift a new one into slot 2. (Slot 1 of the +** table is reserved for the pipe reader function with 0 denoting no reader.) ** ** Reads and writes may span multiple UD buffers and if the read spans multiple UDs ** then the parts are collected as strings on the Lua stack and then concatenated -** with a `lua_concat()`. +** with a lua_concat(). ** -** Note that pipes also support the undocumented length and tostring operators -** for debugging puposes, so if p is a pipe then #p[1] gives the effective -** length of pipe slot 1 and printing p[1] gives its contents +** Note that pipe tables also support the undocumented length and tostring +** operators for debugging puposes, so if p is a pipe then #p[i] gives the +** effective length of pipe slot i and printing p[i] gives its contents. +** +** The pipe library also supports the automatic scheduling of a reader task. This +** is declared by including a Lua CB function and an optional prioirty for it to +** execute at in the pipe.create() call. The reader task may or may not empty the +** FIFO (and there is also nothing to stop the task also writing to the FIFO. The +** reader automatically reschedules itself if the pipe contains unread content. +** +** The reader tasks may be interleaved with other tasks that write to the pipe and +** others that don't. Any task writing to the pipe will also trigger the posting +** of a read task if one is not already pending. In this way at most only one +** pending reader task is pending, and this prevents overrun of the task queueing +** system. +** +** Implementation Notes: +** +** - The Pipe slot 1 is used to store the Lua CB function reference of the reader +** task. Note that is actually an auxiliary wrapper around the supplied Lua CB +** function, and this wrapper also uses upvals to store internal pipe state. +** The remaining slots are the Userdata buffer chunks. +** +** - This internal state needs to be shared with the pipe_write function, but a +** limitation of Lua 5.1 is that C functions cannot share upvals; to avoid this +** constraint, this function is also denormalised to act as the pipe_write +** function: if Arg1 is the pipe then its a pipe:write() otherwise its a +** CB wrapper. +** +** Also note that the pipe module is used by the Lua VM and therefore the create +** read, and unread methods are exposed as directly callable C functions. (Write +** is available throogh pipe[1].) ** ** Read the docs/modules/pipe.md documentation for a functional description. */ @@ -19,6 +49,8 @@ #include "module.h" #include "lauxlib.h" #include +#include "platform.h" +#include "lstate.h" #define INVALID_LEN ((unsigned)-1) @@ -31,10 +63,36 @@ typedef struct buffer { LROT_TABLE(pipe_meta) -/* Validation and utility functions */ +#define AT_TAIL 0x00 +#define AT_HEAD 0x01 +#define WRITING 0x02 -#define AT_HEAD 1 -#define AT_TAIL 0 +static buffer_t *checkPipeUD (lua_State *L, int ndx); +static buffer_t *newPipeUD(lua_State *L, int ndx, int n); +static int pipe_write_aux(lua_State *L); + +/* Validation and utility functions */ + // [-0, +0, v] +static buffer_t *checkPipeTable (lua_State *L, int tbl, int flags) { + int m = lua_gettop(L), n = lua_objlen(L, tbl); + if (lua_istable(L, tbl) && lua_getmetatable(L, tbl)) { + lua_pushrotable(L, LROT_TABLEREF(pipe_meta));/* push comparison metatable */ + if (lua_rawequal(L, -1, -2)) { /* check these match */ + buffer_t *ud; + if (n == 1) { + ud = (flags & WRITING) ? newPipeUD(L, tbl, 2) : NULL; + } else { + int i = flags & AT_HEAD ? 2 : n; /* point to head or tail of T */ + lua_rawgeti(L, tbl, i); /* and fetch UD */ + ud = checkPipeUD(L, -1); + } + lua_settop(L, m); + return ud; /* and return ptr to buffer_t rec */ + } + } + luaL_typerror(L, tbl, "pipe table"); + return NULL; /* NORETURN avoid compiler error */ +} static buffer_t *checkPipeUD (lua_State *L, int ndx) { // [-0, +0, v] buffer_t *ud = lua_touserdata(L, ndx); @@ -59,27 +117,6 @@ static buffer_t *newPipeUD(lua_State *L, int ndx, int n) { // [-0,+0,-] return ud; /* ud points to new T[#T] */ } -static buffer_t *checkPipeTable (lua_State *L, int tbl, int head) {//[-0, +0, v] - int m = lua_gettop(L), n = lua_objlen(L, tbl); - if (lua_type(L, tbl) == LUA_TTABLE && lua_getmetatable(L, tbl)) { - lua_pushrotable(L, LROT_TABLEREF(pipe_meta));/* push comparison metatable */ - if (lua_rawequal(L, -1, -2)) { /* check these match */ - buffer_t *ud; - if (n == 0) { - ud = head ? NULL : newPipeUD(L, tbl, 1); - } else { - int i = head ? 1 : n; /* point to head or tail of T */ - lua_rawgeti(L, tbl, i); /* and fetch UD */ - ud = checkPipeUD(L, -1); - } - lua_settop(L, m); - return ud; /* and return ptr to buffer_t rec */ - } - } - luaL_typerror(L, tbl, "pipe table"); - return NULL; /* NORETURN avoid compiler error */ -} - #define CHAR_DELIM -1 #define CHAR_DELIM_KEEP -2 static char getsize_delim (lua_State *L, int ndx, int *len) { // [-0, +0, v] @@ -104,22 +141,115 @@ static char getsize_delim (lua_State *L, int ndx, int *len) { // [-0, +0, v] return delim; } -/* Lua callable methods */ +/* +** Read CB Initiator AND pipe_write. If arg1 == the pipe, then this is a pipe +** write(); otherwise it is the Lua CB wapper for the task post. This botch allows +** these two functions to share Upvals within the Lua 5.1 VM: +*/ +#define UVpipe lua_upvalueindex(1) // The pipe table object +#define UVfunc lua_upvalueindex(2) // The CB's Lua function +#define UVprio lua_upvalueindex(3) // The task priority +#define UVstate lua_upvalueindex(4) // Pipe state; +#define CB_NOT_USED 0 +#define CB_ACTIVE 1 +#define CB_WRITE_UPDATED 2 +#define CB_QUIESCENT 4 +/* +** Note that nothing precludes the Lua CB function from itself writing to the +** pipe and in this case this routine will call itself recursively. +** +** The Lua CB itself takes the pipe object as a parameter and returns an optional +** boolean to force or to suppress automatic retasking if needed. If omitted, +** then the default is to repost if the pipe is not empty, otherwise the task +** chain is left to lapse. +*/ +static int pipe_write_and_read_poster (lua_State *L) { + int state = lua_tointeger(L, UVstate); + if (lua_rawequal(L, 1, UVpipe)) { + /* arg1 == the pipe, so this was invoked as a pipe_write() */ + if (pipe_write_aux(L) && state && !(state & CB_WRITE_UPDATED)) { + /* + * if this resulted in a write and not already in a CB and not already + * toggled the write update then post the task + */ + state |= CB_WRITE_UPDATED; + lua_pushinteger(L, state); + lua_replace(L, UVstate); /* Set CB state write updated flag */ + if (state == CB_QUIESCENT | CB_WRITE_UPDATED) { + lua_rawgeti(L, 1, 1); /* Get CB ref from pipe[1] */ + luaN_posttask(L, (int) lua_tointeger(L, UVprio)); /* and repost task */ + } + } -//Lua s = pipeUD:tostring() -static int pipe__tostring (lua_State *L) { - if (lua_type(L, 1) == LUA_TTABLE) { - lua_pushfstring(L, "Pipe: %p", lua_topointer(L, 1)); - } else { - buffer_t *ud = checkPipeUD(L, 1); - lua_pushlstring(L, ud->buf + ud->start, ud->end - ud->start); + } else if (state != CB_NOT_USED) { + /* invoked by the luaN_taskpost() so call the Lua CB */ + int repost; /* can take the values CB_WRITE_UPDATED or 0 */ + lua_pushinteger(L, CB_ACTIVE); /* CB state set to active only */ + lua_replace(L, UVstate); + lua_pushvalue(L, UVfunc); /* Lua CB function */ + lua_pushvalue(L, UVpipe); /* pipe table */ + lua_call(L, 1, 1); + /* + * On return from the Lua CB, the task is never reposted if the pipe is empty. + * If it is not empty then the Lua CB return status determines when reposting + * occurs: + * - true = repost + * - false = don't repost + * - nil = only repost if there has been a write update. + */ + if (lua_isboolean(L,-1)) { + repost = (lua_toboolean(L, -1) == true && + lua_objlen(L, UVpipe) > 1) ? CB_WRITE_UPDATED : 0; + } else { + repost = state & CB_WRITE_UPDATED; + } + state = CB_QUIESCENT | repost; + lua_pushinteger(L, state); /* Update the CB state */ + lua_replace(L, UVstate); + + if (repost) { + lua_rawgeti(L, UVpipe, 1); /* Get CB ref from pipe[1] */ + luaN_posttask(L, (int) lua_tointeger(L, UVprio)); /* and repost task */ + } } - return 1; + return 0; } -// len = #pipeobj[1] +/* Lua callable methods. Since the metatable is linked to both the pipe table */ +/* and the userdata entries the __len & __tostring functions must handle both */ + +// Lua: buf = pipe.create() +int pipe_create(lua_State *L) { + int prio = -1; + lua_settop(L, 2); /* fix stack sze as 2 */ + + if (!lua_isnil(L, 1)) { + luaL_checkanyfunction(L, 1); /* non-nil arg1 must be a function */ + if (lua_isnil(L, 2)) { + prio = PLATFORM_TASK_PRIORITY_MEDIUM; + } else { + prio = (int) lua_tointeger(L, 2); + luaL_argcheck(L, prio >= PLATFORM_TASK_PRIORITY_LOW && + prio <= PLATFORM_TASK_PRIORITY_HIGH, 2, "invalid priority"); + } + } + + lua_createtable (L, 1, 0); /* create pipe table */ + lua_pushrotable(L, LROT_TABLEREF(pipe_meta)); + lua_setmetatable(L, -2); /* set pipe table's metabtable to pipe_meta */ + + lua_pushvalue(L, -1); /* UV1: pipe object */ + lua_pushvalue(L, 1); /* UV2: CB function */ + lua_pushinteger(L, prio); /* UV3: task priority */ + lua_pushinteger(L, prio == -1 ? CB_NOT_USED : CB_QUIESCENT); + lua_pushcclosure(L, pipe_write_and_read_poster, 4); /* post aux func as C task */ + lua_rawseti(L, -2, 1); /* and wrtie to T[1] */ + return 1; /* return the table */ +} + +// len = #pipeobj[i] static int pipe__len (lua_State *L) { - if (lua_type(L, 1) == LUA_TTABLE) { + if (lua_type(L, 1) == LUA_TTABLE) { lua_pushinteger(L, lua_objlen(L, 1)); } else { buffer_t *ud = checkPipeUD(L, 1); @@ -128,16 +258,19 @@ static int pipe__len (lua_State *L) { return 1; } -// Lua: buf = pipe.create() -static int pipe_create(lua_State *L) { - lua_createtable (L, 1, 0); - lua_pushrotable(L, LROT_TABLEREF(pipe_meta)); - lua_setmetatable(L, 1); /* set table's metabtable to pipe_meta */ - return 1; /* return the table */ +//Lua s = pipeUD:tostring() +static int pipe__tostring (lua_State *L) { + if (lua_istable(L, 1)) { + lua_pushfstring(L, "Pipe: %p", lua_topointer(L, 1)); + } else { + buffer_t *ud = checkPipeUD(L, 1); + lua_pushlstring(L, ud->buf + ud->start, ud->end - ud->start); + } + return 1; } // Lua: rec = p:read(end_or_delim) // also [-2, +1,- ] -static int pipe_read(lua_State *L) { +int pipe_read(lua_State *L) { buffer_t *ud = checkPipeTable(L, 1, AT_HEAD); int i, k=0, n; lua_settop(L,2); @@ -158,6 +291,7 @@ static int pipe_read(lua_State *L) { want = used = i + 1 - ud->start; /* case where we've hit a delim */ if (n == CHAR_DELIM) want--; + n = 0; /* force loop exit because delim found */ } } else { want = used = (n < avail) ? n : avail; @@ -169,12 +303,12 @@ static int pipe_read(lua_State *L) { if (ud->start == ud->end) { /* shift the pipe array down overwriting T[1] */ int nUD = lua_objlen(L, 1); - for (i = 1; i < nUD; i++) { /* for i = 1, nUD-1 */ + for (i = 2; i < nUD; i++) { /* for i = 2, nUD-1 */ lua_rawgeti(L, 1, i+1); lua_rawseti(L, 1, i); /* T[i] = T[i+1] */ } lua_pushnil(L); lua_rawseti(L, 1, nUD--); /* T[n] = nil */ - if (nUD) { - lua_rawgeti(L, 1, 1); + if (nUD>1) { + lua_rawgeti(L, 1, 2); ud = checkPipeUD(L, -1); lua_pop(L, 1); } else { @@ -190,29 +324,33 @@ static int pipe_read(lua_State *L) { } // Lua: buf:unread(some_string) -static int pipe_unread(lua_State *L) { +int pipe_unread(lua_State *L) { size_t l = INVALID_LEN; const char *s = lua_tolstring(L, 2, &l); if (l==0) return 0; luaL_argcheck(L, l != INVALID_LEN, 2, "must be a string"); - buffer_t *ud = checkPipeTable(L, 1, AT_HEAD); + buffer_t *ud = checkPipeTable(L, 1, AT_HEAD | WRITING); do { - int used = ud->end - ud->start, lrem = LUAL_BUFFERSIZE-used; + int used = ud->end - ud->start; + int lrem = LUAL_BUFFERSIZE-used; if (used == LUAL_BUFFERSIZE) { + /* If the current UD is full insert a new UD at T[2] */ int i, nUD = lua_objlen(L, 1); for (i = nUD; i > 0; i--) { /* for i = nUD-1,1,-1 */ lua_rawgeti(L, 1, i); lua_rawseti(L, 1, i+1); /* T[i+1] = T[i] */ } ud = newPipeUD(L, 1, 1); used = 0; lrem = LUAL_BUFFERSIZE; - } else if (ud->end < LUAL_BUFFERSIZE) { + + } else if (ud->start < l) { + /* If the unread can't fit it before the start then shift content to end */ memmove(ud->buf + lrem, ud->buf + ud->start, used); /* must be memmove not cpy */ + ud->start = lrem; ud->end = LUAL_BUFFERSIZE; } - ud->start = lrem; ud->end = LUAL_BUFFERSIZE; if (l <= (unsigned )lrem) break; @@ -232,21 +370,24 @@ static int pipe_unread(lua_State *L) { } // Lua: buf:write(some_string) -static int pipe_write(lua_State *L) { +static int pipe_write_aux(lua_State *L) { size_t l = INVALID_LEN; const char *s = lua_tolstring(L, 2, &l); +//dbg_printf("pipe write(%u): %s", l, s); if (l==0) - return 0; + return false; luaL_argcheck(L, l != INVALID_LEN, 2, "must be a string"); - buffer_t *ud = checkPipeTable(L, 1, AT_TAIL); + buffer_t *ud = checkPipeTable(L, 1, AT_TAIL | WRITING); do { int used = ud->end - ud->start; if (used == LUAL_BUFFERSIZE) { + /* If the current UD is full insert a new UD at T[end] */ ud = newPipeUD(L, 1, lua_objlen(L, 1)+1); used = 0; - } else if (ud->start) { + } else if (LUAL_BUFFERSIZE - ud->end < l) { + /* If the write can't fit it at the end then shift content to the start */ memmove(ud->buf, ud->buf + ud->start, used); /* must be memmove not cpy */ ud->start = 0; ud->end = used; } @@ -267,7 +408,7 @@ static int pipe_write(lua_State *L) { /* Copy any residual tail to the UD buffer. Note that this is l>0 and */ memcpy(ud->buf + ud->end, s, l); ud->end += l; - return 0; + return true; } // Lua: fread = pobj:reader(1400) -- or other number @@ -289,21 +430,36 @@ static int pipe_reader(lua_State *L) { return 1; } - -LROT_BEGIN(pipe_meta) - LROT_TABENTRY( __index, pipe_meta) +LROT_BEGIN(pipe_funcs) LROT_FUNCENTRY( __len, pipe__len ) LROT_FUNCENTRY( __tostring, pipe__tostring ) LROT_FUNCENTRY( read, pipe_read ) LROT_FUNCENTRY( reader, pipe_reader ) LROT_FUNCENTRY( unread, pipe_unread ) - LROT_FUNCENTRY( write, pipe_write ) LROT_END( pipe_meta, NULL, LROT_MASK_INDEX ) +/* Using a index func is needed because the write method is at pipe[1] */ +static int pipe__index(lua_State *L) { + lua_settop(L,2); + const char *k=lua_tostring(L,2); + if(!strcmp(k,"write")){ + lua_rawgeti(L, 1, 1); + } else { + lua_pushrotable(L, LROT_TABLEREF(pipe_funcs)); + lua_replace(L, 1); + lua_rawget(L, 1); + } + return 1; +} + +LROT_BEGIN(pipe_meta) + LROT_FUNCENTRY( __index, pipe__index) + LROT_FUNCENTRY( __len, pipe__len ) + LROT_FUNCENTRY( __tostring, pipe__tostring ) +LROT_END( pipe_meta, NULL, LROT_MASK_INDEX ) LROT_BEGIN(pipe) LROT_FUNCENTRY( create, pipe_create ) LROT_END( lb, NULL, 0 ) - NODEMCU_MODULE(PIPE, "pipe", pipe, NULL); diff --git a/app/modules/somfy.c b/app/modules/somfy.c index 79abf50e..7ba0cd04 100644 --- a/app/modules/somfy.c +++ b/app/modules/somfy.c @@ -20,6 +20,7 @@ #include "lauxlib.h" #include "lmem.h" #include "platform.h" +#include "task/task.h" #include "hw_timer.h" #include "user_interface.h" diff --git a/app/modules/uart.c b/app/modules/uart.c index 5dcba1c4..a849095d 100644 --- a/app/modules/uart.c +++ b/app/modules/uart.c @@ -7,83 +7,57 @@ #include #include #include "rom.h" +#include "driver/input.h" static int uart_receive_rf = LUA_NOREF; -bool run_input = true; -bool uart_on_data_cb(const char *buf, size_t len){ - if(!buf || len==0) - return false; - if(uart_receive_rf == LUA_NOREF) - return false; + +void uart_on_data_cb(const char *buf, size_t len){ lua_State *L = lua_getstate(); - if(!L) - return false; lua_rawgeti(L, LUA_REGISTRYINDEX, uart_receive_rf); lua_pushlstring(L, buf, len); - lua_call(L, 1, 0); - return !run_input; + luaN_call(L, 1, 0, 0); } -uint16_t need_len = 0; -int16_t end_char = -1; // Lua: uart.on("method", [number/char], function, [run_input]) static int l_uart_on( lua_State* L ) { - size_t sl, el; - int32_t run = 1; - uint8_t stack = 1; - const char *method = luaL_checklstring( L, stack, &sl ); - stack++; - if (method == NULL) - return luaL_error( L, "wrong arg type" ); + size_t el; + int stack = 2, data_len = -1; + char end_char = 0; + const char *method = lua_tostring( L, 1); + bool run_input = true; + luaL_argcheck(L, method && !strcmp(method, "data"), 1, "method not supported"); - if( lua_type( L, stack ) == LUA_TNUMBER ) + if (lua_type( L, stack ) == LUA_TNUMBER) { - need_len = ( uint16_t )luaL_checkinteger( L, stack ); + data_len = luaL_checkinteger( L, stack ); + luaL_argcheck(L, data_len >= 0 && data_len <= LUA_MAXINPUT, stack, "wrong arg range"); stack++; - end_char = -1; - if( need_len > 255 ){ - need_len = 255; - return luaL_error( L, "wrong arg range" ); - } } - else if(lua_isstring(L, stack)) + else if (lua_isstring(L, stack)) { const char *end = luaL_checklstring( L, stack, &el ); + data_len = 0; + end_char = (int16_t) end[0]; stack++; - if(el!=1){ + if(el!=1) { return luaL_error( L, "wrong arg range" ); } - end_char = (int16_t)end[0]; - need_len = 0; } - // luaL_checkanyfunction(L, stack); - if (lua_type(L, stack) == LUA_TFUNCTION || lua_type(L, stack) == LUA_TLIGHTFUNCTION){ - if ( lua_isnumber(L, stack+1) ){ - run = lua_tointeger(L, stack+1); + if (lua_anyfunction(L, stack)) { + if (lua_isnumber(L, stack+1) && lua_tointeger(L, stack+1) == 0) { + run_input = false; } - lua_pushvalue(L, stack); // copy argument (func) to the top of stack + lua_pushvalue(L, stack); + luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf); + uart_receive_rf = luaL_ref(L, LUA_REGISTRYINDEX); + } else { - lua_pushnil(L); - } - if(sl == 4 && strcmp(method, "data") == 0){ - run_input = true; - if(uart_receive_rf != LUA_NOREF){ - luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf); - uart_receive_rf = LUA_NOREF; - } - if(!lua_isnil(L, -1)){ - uart_receive_rf = luaL_ref(L, LUA_REGISTRYINDEX); - if(run==0) - run_input = false; - } else { - lua_pop(L, 1); - } - }else{ - lua_pop(L, 1); - return luaL_error( L, "method not supported" ); + luaL_unref(L, LUA_REGISTRYINDEX, uart_receive_rf); + uart_receive_rf = LUA_NOREF; } + input_setup_receive(uart_on_data_cb, data_len, end_char, run_input); return 0; } @@ -91,7 +65,7 @@ bool uart0_echo = true; // Lua: actualbaud = setup( id, baud, databits, parity, stopbits, echo ) static int l_uart_setup( lua_State* L ) { - uint32_t id, databits, parity, stopbits, echo = 1; + uint32_t id, databits, parity, stopbits; uint32_t baud, res; id = luaL_checkinteger( L, 1 ); @@ -101,12 +75,8 @@ static int l_uart_setup( lua_State* L ) databits = luaL_checkinteger( L, 3 ); parity = luaL_checkinteger( L, 4 ); stopbits = luaL_checkinteger( L, 5 ); - if(lua_isnumber(L,6)){ - echo = lua_tointeger(L,6); - if(echo!=0) - uart0_echo = true; - else - uart0_echo = false; + if (lua_isnumber(L,6)) { + input_setecho(lua_tointeger(L,6) ? true : false); } res = platform_uart_setup( id, baud, databits, parity, stopbits ); diff --git a/app/platform/platform.c b/app/platform/platform.c index a343652c..ad7c2c26 100644 --- a/app/platform/platform.c +++ b/app/platform/platform.c @@ -17,7 +17,8 @@ #define INTERRUPT_TYPE_IS_LEVEL(x) ((x) >= GPIO_PIN_INTR_LOLEVEL) #ifdef GPIO_INTERRUPT_ENABLE -static task_handle_t gpio_task_handle; +static platform_task_handle_t gpio_task_handle; +static int task_init_handler(void); #ifdef GPIO_INTERRUPT_HOOK_ENABLE struct gpio_hook_entry { @@ -55,11 +56,13 @@ static const int uart_bitrates[] = { BIT_RATE_3686400 }; -int platform_init() +int platform_init () { // Setup the various forward and reverse mappings for the pins get_pin_map(); + (void) task_init_handler(); + cmn_platform_init(); // All done return PLATFORM_OK; @@ -83,7 +86,7 @@ uint8_t platform_key_led( uint8_t level){ /* * Set GPIO mode to output. Optionally in RAM helper because interrupts are dsabled */ -static void NO_INTR_CODE set_gpio_no_interrupt(uint8 pin, uint8_t push_pull) { +static void NO_INTR_CODE set_gpio_no_interrupt(uint8_t pin, uint8_t push_pull) { unsigned pnum = pin_num[pin]; ETS_GPIO_INTR_DISABLE(); #ifdef GPIO_INTERRUPT_ENABLE @@ -113,7 +116,7 @@ static void NO_INTR_CODE set_gpio_no_interrupt(uint8 pin, uint8_t push_pull) { * Set GPIO mode to interrupt. Optionally RAM helper because interrupts are dsabled */ #ifdef GPIO_INTERRUPT_ENABLE -static void NO_INTR_CODE set_gpio_interrupt(uint8 pin) { +static void NO_INTR_CODE set_gpio_interrupt(uint8_t pin) { ETS_GPIO_INTR_DISABLE(); PIN_FUNC_SELECT(pin_mux[pin], pin_func[pin]); GPIO_DIS_OUTPUT(pin_num[pin]); @@ -209,9 +212,9 @@ int platform_gpio_read( unsigned pin ) #ifdef GPIO_INTERRUPT_ENABLE static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){ - uint32 j=0; - uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); - uint32 now = system_get_time(); + uint32_t j=0; + uint32_t gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS); + uint32_t now = system_get_time(); UNUSED(dummy); #ifdef GPIO_INTERRUPT_HOOK_ENABLE @@ -244,8 +247,8 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){ GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, BIT(j)); if (diff == 0 || diff & 0x8000) { - uint32 level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j)); - if (!task_post_high (gpio_task_handle, (now << 8) + (i<<1) + level)) { + uint32_t level = 0x1 & GPIO_INPUT_GET(GPIO_ID_PIN(j)); + if (!platform_post_high (gpio_task_handle, (now << 8) + (i<<1) + level)) { // If we fail to post, then try on the next interrupt pin_counter[i].seen |= 0x8000; } @@ -260,7 +263,7 @@ static void ICACHE_RAM_ATTR platform_gpio_intr_dispatcher (void *dummy){ } } -void platform_gpio_init( task_handle_t gpio_task ) +void platform_gpio_init( platform_task_handle_t gpio_task ) { gpio_task_handle = gpio_task; @@ -871,7 +874,7 @@ uint32_t platform_s_flash_write( const void *from, uint32_t toaddr, uint32_t siz memcpy(apbuf, from, size); } system_soft_wdt_feed (); - r = flash_write(toaddr, apbuf?(uint32 *)apbuf:(uint32 *)from, size); + r = flash_write(toaddr, apbuf?(uint32_t *)apbuf:(uint32_t *)from, size); if(apbuf) free(apbuf); if(SPI_FLASH_RESULT_OK == r) @@ -899,7 +902,7 @@ uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ) if( ((uint32_t)to) & blkmask ) { uint32_t size2=size-INTERNAL_FLASH_READ_UNIT_SIZE; - uint32* to2=(uint32*)((((uint32_t)to)&(~blkmask))+INTERNAL_FLASH_READ_UNIT_SIZE); + uint32_t* to2=(uint32_t*)((((uint32_t)to)&(~blkmask))+INTERNAL_FLASH_READ_UNIT_SIZE); r = flash_read(fromaddr, to2, size2); if(SPI_FLASH_RESULT_OK == r) { @@ -910,7 +913,7 @@ uint32_t platform_s_flash_read( void *to, uint32_t fromaddr, uint32_t size ) } } else - r = flash_read(fromaddr, (uint32 *)to, size); + r = flash_read(fromaddr, (uint32_t *)to, size); if(SPI_FLASH_RESULT_OK == r) return size; @@ -1079,3 +1082,84 @@ void* platform_print_deprecation_note( const char *msg, const char *time_frame) { printf( "Warning, deprecated API! %s. It will be removed %s. See documentation for details.\n", msg, time_frame ); } + +#define TH_MONIKER 0x68680000 +#define TH_MASK 0xFFF80000 +#define TH_UNMASK (~TH_MASK) +#define TH_SHIFT 2 +#define TH_ALLOCATION_BRICK 4 // must be a power of 2 +#define TASK_DEFAULT_QUEUE_LEN 8 +#define TASK_PRIORITY_MASK 3 +#define TASK_PRIORITY_COUNT 3 + +/* + * Private struct to hold the 3 event task queues and the dispatch callbacks + */ +static struct taskQblock { + os_event_t *task_Q[TASK_PRIORITY_COUNT]; + platform_task_callback_t *task_func; + int task_count; + } TQB = {0}; + +static void platform_task_dispatch (os_event_t *e) { + platform_task_handle_t handle = e->sig; + if ( (handle & TH_MASK) == TH_MONIKER) { + uint16_t entry = (handle & TH_UNMASK) >> TH_SHIFT; + uint8_t priority = handle & TASK_PRIORITY_MASK; + if ( priority <= PLATFORM_TASK_PRIORITY_HIGH && + TQB.task_func && + entry < TQB.task_count ){ + /* call the registered task handler with the specified parameter and priority */ + TQB.task_func[entry](e->par, priority); + return; + } + } + /* Invalid signals are ignored */ + NODE_DBG ( "Invalid signal issued: %08x", handle); +} + +/* + * Initialise the task handle callback for a given priority. + */ +static int task_init_handler (void) { + int p, qlen = TASK_DEFAULT_QUEUE_LEN; + for (p = 0; p < TASK_PRIORITY_COUNT; p++){ + TQB.task_Q[p] = (os_event_t *) c_malloc( sizeof(os_event_t)*qlen ); + if (TQB.task_Q[p]) { + os_memset(TQB.task_Q[p], 0, sizeof(os_event_t)*qlen); + system_os_task(platform_task_dispatch, p, TQB.task_Q[p], TASK_DEFAULT_QUEUE_LEN); + } else { + NODE_DBG ( "Malloc failure in platform_task_init_handler" ); + return PLATFORM_ERR; + } + } +} + + +/* + * Allocate a task handle in the relevant TCB.task_Q. Note that these Qs are resized + * as needed growing in 4 unit bricks. No GC is adopted so handles are permanently + * allocated during boot life. This isn't an issue in practice as only a few handles + * are created per priority during application init and the more volitile Lua tasks + * are allocated in the Lua registery using the luaX interface which is layered on + * this mechanism. + */ +platform_task_handle_t platform_task_get_id (platform_task_callback_t t) { + if ( (TQB.task_count & (TH_ALLOCATION_BRICK - 1)) == 0 ) { + TQB.task_func = (platform_task_callback_t *) os_realloc( + TQB.task_func, + sizeof(platform_task_callback_t) * (TQB.task_count+TH_ALLOCATION_BRICK)); + if (!TQB.task_func) { + NODE_DBG ( "Malloc failure in platform_task_get_id"); + return 0; + } + os_memset (TQB.task_func+TQB.task_count, 0, + sizeof(platform_task_callback_t)*TH_ALLOCATION_BRICK); + } + TQB.task_func[TQB.task_count++] = t; + return TH_MONIKER + ((TQB.task_count-1) << TH_SHIFT); +} + +bool platform_post (uint8 prio, platform_task_handle_t handle, platform_task_param_t par) { + return system_os_post(prio, handle | prio, par); +} diff --git a/app/platform/platform.h b/app/platform/platform.h index 3a41ddfc..68c6811f 100644 --- a/app/platform/platform.h +++ b/app/platform/platform.h @@ -8,8 +8,6 @@ #include "driver/pwm.h" #include "driver/uart.h" -#include "task/task.h" - // Error / status codes enum { @@ -18,6 +16,9 @@ enum PLATFORM_UNDERFLOW = -1 }; +typedef uint32_t platform_task_handle_t; +typedef uint32_t platform_task_param_t; + // Platform initialization int platform_init(void); void platform_int_init(void); @@ -52,7 +53,7 @@ int platform_gpio_register_intr_hook(uint32_t gpio_bits, platform_hook_function #define platform_gpio_unregister_intr_hook(hook) \ platform_gpio_register_intr_hook(0, hook); void platform_gpio_intr_init( unsigned pin, GPIO_INT_TYPE type ); -void platform_gpio_init( task_handle_t gpio_task ); +void platform_gpio_init( platform_task_handle_t gpio_task ); // ***************************************************************************** // Timer subsection @@ -353,4 +354,22 @@ typedef union { uint32_t platform_rcr_read (uint8_t rec_id, void **rec); uint32_t platform_rcr_write (uint8_t rec_id, const void *rec, uint8_t size); +#define PLATFORM_TASK_PRIORITY_LOW 0 +#define PLATFORM_TASK_PRIORITY_MEDIUM 1 +#define PLATFORM_TASK_PRIORITY_HIGH 2 + +/* +* Signals are a 32-bit number of the form header:14; count:16, priority:2. The header +* is just a fixed fingerprint and the count is allocated serially by the task get_id() +* function. +*/ +#define platform_post_low(handle,param) platform_post(PLATFORM_TASK_PRIORITY_LOW, handle, param) +#define platform_post_medium(handle,param) platform_post(PLATFORM_TASK_PRIORITY_MEDIUM, handle, param) +#define platform_post_high(handle,param) platform_post(PLATFORM_TASK_PRIORITY_HIGH, handle, param) + +typedef void (*platform_task_callback_t)(platform_task_param_t param, uint8 prio); +platform_task_handle_t platform_task_get_id(platform_task_callback_t t); + +bool platform_post(uint8 prio, platform_task_handle_t h, platform_task_param_t par); + #endif diff --git a/app/platform/vfs.h b/app/platform/vfs.h index e9784e98..cf78811a 100644 --- a/app/platform/vfs.h +++ b/app/platform/vfs.h @@ -15,7 +15,7 @@ // vfs_close - close file descriptor and free memory // fd: file descriptor // Returns: VFS_RES_OK or negative value in case of error -static int32_t vfs_close( int fd ) { +static inline int32_t vfs_close( int fd ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->close( f ) : VFS_RES_ERR; } @@ -25,7 +25,7 @@ static int32_t vfs_close( int fd ) { // ptr: destination data buffer // len: requested length // Returns: Number of bytes read, or VFS_RES_ERR in case of error -static int32_t vfs_read( int fd, void *ptr, size_t len ) { +static inline int32_t vfs_read( int fd, void *ptr, size_t len ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->read( f, ptr, len ) : VFS_RES_ERR; } @@ -35,7 +35,7 @@ static int32_t vfs_read( int fd, void *ptr, size_t len ) { // ptr: source data buffer // len: requested length // Returns: Number of bytes written, or VFS_RES_ERR in case of error -static int32_t vfs_write( int fd, const void *ptr, size_t len ) { +static inline sint32_t vfs_write( int fd, const void *ptr, size_t len ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->write( f, ptr, len ) : VFS_RES_ERR; } @@ -51,7 +51,7 @@ int vfs_ungetc( int c, int fd ); // VFS_SEEK_CUR - set pointer to current position + off // VFS_SEEK_END - set pointer to end of file + off // Returns: New position, or VFS_RES_ERR in case of error -static int32_t vfs_lseek( int fd, int32_t off, int whence ) { +static inline int32_t vfs_lseek( int fd, sint32_t off, int whence ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->lseek( f, off, whence ) : VFS_RES_ERR; } @@ -59,7 +59,7 @@ static int32_t vfs_lseek( int fd, int32_t off, int whence ) { // vfs_eof - test for end-of-file // fd: file descriptor // Returns: 0 if not at end, != 0 if end of file -static int32_t vfs_eof( int fd ) { +static inline int32_t vfs_eof( int fd ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->eof( f ) : VFS_RES_ERR; } @@ -67,7 +67,7 @@ static int32_t vfs_eof( int fd ) { // vfs_tell - get read/write position // fd: file descriptor // Returns: Current position -static int32_t vfs_tell( int fd ) { +static inline int32_t vfs_tell( int fd ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->tell( f ) : VFS_RES_ERR; } @@ -75,7 +75,7 @@ static int32_t vfs_tell( int fd ) { // vfs_flush - flush write cache to file // fd: file descriptor // Returns: VFS_RES_OK, or VFS_RES_ERR in case of error -static int32_t vfs_flush( int fd ) { +static inline int32_t vfs_flush( int fd ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->flush( f ) : VFS_RES_ERR; } @@ -83,7 +83,7 @@ static int32_t vfs_flush( int fd ) { // vfs_size - get current file size // fd: file descriptor // Returns: File size -static uint32_t vfs_size( int fd ) { +static inline uint32_t vfs_size( int fd ) { vfs_file *f = (vfs_file *)fd; return f ? f->fns->size( f ) : 0; } @@ -100,13 +100,13 @@ int32_t vfs_ferrno( int fd ); // vfs_closedir - close directory descriptor and free memory // dd: dir descriptor // Returns: VFS_RES_OK, or VFS_RES_ERR in case of error -static int32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); } +static inline int32_t vfs_closedir( vfs_dir *dd ) { return dd->fns->close( dd ); } // vfs_readdir - read next directory item // dd: dir descriptor // buf: pre-allocated stat structure to be filled in // Returns: VFS_RES_OK if next item found, otherwise VFS_RES_ERR -static int32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); } +static inline int32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns->readdir( dd, buf ); } // --------------------------------------------------------------------------- // volume functions @@ -115,7 +115,7 @@ static int32_t vfs_readdir( vfs_dir *dd, struct vfs_stat *buf ) { return dd->fns // vfs_umount - unmount logical drive and free memory // vol: volume object // Returns: VFS_RES_OK, or VFS_RES_ERR in case of error -static int32_t vfs_umount( vfs_vol *vol ) { return vol->fns->umount( vol ); } +static inline int32_t vfs_umount( vfs_vol *vol ) { return vol->fns->umount( vol ); } // --------------------------------------------------------------------------- // file system functions diff --git a/app/pm/swtimer.c b/app/pm/swtimer.c index 5e0bb38b..cc0654a9 100644 --- a/app/pm/swtimer.c +++ b/app/pm/swtimer.c @@ -41,6 +41,7 @@ #include "module.h" #include "lauxlib.h" #include "platform.h" +#include "task/task.h" #include "user_interface.h" #include "user_modules.h" diff --git a/app/task/Makefile b/app/task/Makefile deleted file mode 100644 index b7db4b50..00000000 --- a/app/task/Makefile +++ /dev/null @@ -1,41 +0,0 @@ - -############################################################# -# Required variables for each makefile -# Discard this section from all parent makefiles -# Expected variables (with automatic defaults): -# CSRCS (all "C" files in the dir) -# SUBDIRS (all subdirs with a Makefile) -# GEN_LIBS - list of libs to be generated () -# GEN_IMAGES - list of images to be generated () -# COMPONENTS_xxx - a list of libs/objs in the form -# subdir/lib to be extracted and rolled up into -# a generated lib/image xxx.a () -# -ifndef PDIR -GEN_LIBS = libtask.a -endif - -############################################################# -# Configuration i.e. compile options etc. -# Target specific stuff (defines etc.) goes in here! -# Generally values applying to a tree are captured in the -# makefile at its root level - these are then overridden -# for a subtree within the makefile rooted therein -# -#DEFINES += - -############################################################# -# Recursion Magic - Don't touch this!! -# -# Each subtree potentially has an include directory -# corresponding to the common APIs applicable to modules -# rooted at that subtree. Accordingly, the INCLUDE PATH -# of a module can only contain the include directories up -# its parent path, and not its siblings -# -# Required for each makefile to inherit from the parent -# - -PDIR := ../$(PDIR) -sinclude $(PDIR)Makefile - diff --git a/app/task/task.c b/app/task/task.c deleted file mode 100644 index e9bc9ef6..00000000 --- a/app/task/task.c +++ /dev/null @@ -1,72 +0,0 @@ -/** - This file encapsulates the SDK-based task handling for the NodeMCU Lua firmware. - */ -#include "task/task.h" -#include "mem.h" -#include - -#define TASK_HANDLE_MONIKER 0x68680000 -#define TASK_HANDLE_MASK 0xFFF80000 -#define TASK_HANDLE_UNMASK (~TASK_HANDLE_MASK) -#define TASK_HANDLE_SHIFT 2 -#define TASK_HANDLE_ALLOCATION_BRICK 4 // must be a power of 2 -#define TASK_DEFAULT_QUEUE_LEN 8 -#define TASK_PRIORITY_MASK 3 - -#define CHECK(p,v,msg) if (!(p)) { NODE_DBG ( msg ); return (v); } - -/* - * Private arrays to hold the 3 event task queues and the dispatch callbacks - */ -LOCAL os_event_t *task_Q[TASK_PRIORITY_COUNT]; -LOCAL task_callback_t *task_func; -LOCAL int task_count; - -LOCAL void task_dispatch (os_event_t *e) { - task_handle_t handle = e->sig; - if ( (handle & TASK_HANDLE_MASK) == TASK_HANDLE_MONIKER) { - uint16 entry = (handle & TASK_HANDLE_UNMASK) >> TASK_HANDLE_SHIFT; - uint8 priority = handle & TASK_PRIORITY_MASK; - if ( priority <= TASK_PRIORITY_HIGH && task_func && entry < task_count ){ - /* call the registered task handler with the specified parameter and priority */ - task_func[entry](e->par, priority); - return; - } - } - /* Invalid signals are ignored */ - NODE_DBG ( "Invalid signal issued: %08x", handle); -} - -/* - * Initialise the task handle callback for a given priority. This doesn't need - * to be called explicitly as the get_id function will call this lazily. - */ -bool task_init_handler(uint8 priority, uint8 qlen) { - if (priority <= TASK_PRIORITY_HIGH && task_Q[priority] == NULL) { - task_Q[priority] = (os_event_t *) os_malloc( sizeof(os_event_t)*qlen ); - os_memset (task_Q[priority], 0, sizeof(os_event_t)*qlen); - if (task_Q[priority]) { - return system_os_task( task_dispatch, priority, task_Q[priority], qlen ); - } - } - return false; -} - -task_handle_t task_get_id(task_callback_t t) { - int p = TASK_PRIORITY_COUNT; - /* Initialise and uninitialised Qs with the default Q len */ - while(p--) if (!task_Q[p]) { - CHECK(task_init_handler( p, TASK_DEFAULT_QUEUE_LEN ), 0, "Task initialisation failed"); - } - - if ( (task_count & (TASK_HANDLE_ALLOCATION_BRICK - 1)) == 0 ) { - /* With a brick size of 4 this branch is taken at 0, 4, 8 ... and the new size is +4 */ - task_func =(task_callback_t *) os_realloc(task_func, - sizeof(task_callback_t)*(task_count+TASK_HANDLE_ALLOCATION_BRICK)); - CHECK(task_func, 0 , "Malloc failure in task_get_id"); - os_memset (task_func+task_count, 0, sizeof(task_callback_t)*TASK_HANDLE_ALLOCATION_BRICK); - } - - task_func[task_count++] = t; - return TASK_HANDLE_MONIKER + ((task_count-1) << TASK_HANDLE_SHIFT); -} diff --git a/app/user/user_main.c b/app/user/user_main.c index 2b9e97b1..25875f2b 100644 --- a/app/user/user_main.c +++ b/app/user/user_main.c @@ -20,6 +20,7 @@ #include "ets_sys.h" #include "driver/uart.h" +#include "driver/input.h" #include "task/task.h" #include "mem.h" #include "espconn.h" @@ -29,9 +30,6 @@ #include "rtc/rtctime.h" #endif -static task_handle_t input_sig; -static uint8 input_sig_flag = 0; - /* Contents of esp_init_data_default.bin */ extern const uint32_t init_data[], init_data_end[]; #define INIT_DATA_SIZE ((init_data_end - init_data)*sizeof(uint32_t)) @@ -275,40 +273,11 @@ uint32 ICACHE_RAM_ATTR user_iram_memory_is_enabled(void) { return FALSE; // NodeMCU runs like a dog if iRAM is enabled } -// +================== New task interface ==================+ -static void start_lua(task_param_t param, uint8 priority) { - char* lua_argv[] = { (char *)"lua", (char *)"-i", NULL }; - NODE_DBG("Task task_lua started.\n"); - lua_main( 2, lua_argv ); - // Only enable UART interrupts once we've successfully started up, - // otherwise the task queue might fill up with input events and prevent - // the start_lua task from being posted. - ETS_UART_INTR_ENABLE(); -} - -static void handle_input(task_param_t flag, uint8 priority) { - (void)priority; - if (flag & 0x8000) { - input_sig_flag = flag & 0x4000 ? 1 : 0; - } - lua_handle_input (flag & 0x01); -} - -bool user_process_input(bool force) { - return task_post_low(input_sig, force); -} - void nodemcu_init(void) { - NODE_ERR("\n"); - // Initialize platform first for lua modules. - if( platform_init() != PLATFORM_OK ) - { - // This should never happen - NODE_DBG("Can not init platform for modules.\n"); - return; - } - if (!task_post_low(task_get_id(start_lua),'s')) - NODE_ERR("Failed to post the start_lua task!\n"); + NODE_DBG("Task task_lua starting.\n"); + // Call the Lua bootstrap startup directly. This uses the task interface + // internally to carry out the main lua libraries initialisation. + lua_main(); } #ifdef LUA_USE_MODULES_WIFI @@ -328,18 +297,17 @@ void user_rf_pre_init(void) * Parameters : none * Returns : none *******************************************************************************/ -void user_init(void) -{ - +void user_init(void) { #ifdef LUA_USE_MODULES_RTCTIME rtctime_late_startup (); #endif - + if( platform_init() != PLATFORM_OK ) { + // This should never happen + NODE_DBG("Can not init platform for modules.\n"); + return; + } UartBautRate br = BIT_RATE_DEFAULT; - - input_sig = task_get_id(handle_input); - uart_init (br, br, input_sig, &input_sig_flag); - + uart_init (br, br); #ifndef NODE_DEBUG system_set_os_print(0); #endif diff --git a/docs/modules/node.md b/docs/modules/node.md index 033a9dc6..3c4a9833 100644 --- a/docs/modules/node.md +++ b/docs/modules/node.md @@ -341,11 +341,7 @@ print(node.info("sw_version").git_release) ## node.input() -Submits a string to the Lua interpreter. Similar to `pcall(loadstring(str))`, but without the single-line limitation. - -!!! attention - - This function only has an effect when invoked from a callback. Using it directly on the console **does not work**. +Submits a string to the Lua interpreter. Similar to `pcall(loadstring(str))`, but without the single-line limitation. Note that the Line interpreter only actions complete Lua chunks. A Lue Lua chunk must comprise one or more complete `'\n'` terminaed lines that form a complete compilation unit. #### Syntax `node.input(str)` @@ -360,56 +356,29 @@ Submits a string to the Lua interpreter. Similar to `pcall(loadstring(str))`, bu ```lua sk:on("receive", function(conn, payload) node.input(payload) end) ``` +See the `telnet/telnet.lua` in `lua_examples` for a more comprehensive example. #### See also [`node.output()`](#nodeoutput) ## node.output() -Redirects the Lua interpreter output to a callback function. Optionally also prints it to the serial console. - -!!! caution - - Do **not** attempt to `print()` or otherwise induce the Lua interpreter to produce output from within the callback function. Doing so results in infinite recursion, and leads to a watchdog-triggered restart. +Redirects the Lua interpreter to a `stdout` pipe when a CB function is specified (See `pipe` module) and resets output to normal otherwise. Optionally also prints to the serial console. #### Syntax -`node.output(function(str), serial_debug)` +`node.output(function(pipe), serial_debug)` #### Parameters - - `output_fn(str)` a function accept every output as str, and can send the output to a socket (or maybe a file). + - `output_fn(pipe)` a function accept every output as str, and can send the output to a socket (or maybe a file). Note that this function must conform to the fules for a pipe reader callback. - `serial_debug` 1 output also show in serial. 0: no serial output. #### Returns `nil` #### Example -```lua -function tonet(str) - sk:send(str) -end -node.output(tonet, 1) -- serial also get the Lua output. -``` -```lua --- a simple telnet server -s=net.createServer(net.TCP) -s:listen(2323,function(c) - con_std = c - function s_output(str) - if(con_std~=nil) - then con_std:send(str) - end - end - node.output(s_output, 0) -- re-direct output to function s_ouput. - c:on("receive",function(c,l) - node.input(l) -- works like pcall(loadstring(l)) but support multiple separate line - end) - c:on("disconnection",function(c) - con_std = nil - node.output(nil) -- un-regist the redirect output function, output goes to serial - end) -end) -``` +See the `telnet/telnet.lua` in `lua_examples` for a more comprehensive example of its use. + #### See also [`node.input()`](#nodeinput) diff --git a/docs/modules/pipe.md b/docs/modules/pipe.md index 2eb87fa4..43927600 100644 --- a/docs/modules/pipe.md +++ b/docs/modules/pipe.md @@ -11,15 +11,15 @@ task to another. Create a pipe. #### Syntax -`pobj = pipe.create()` +`pobj = pipe.create([CB_function],[task_priority])` #### Parameters -None +- `CB_function` optional reader callback which is called through the `ǹode.task.post()` when the pipe is written to. If the CB returns a boolean, then the reposting action is forced: it is reposted if true and not if false. If the return is nil or omitted then the deault is to repost if a pipe write has occured since the last call. +- `task_priority` See `ǹode.task.post()` #### Returns A pipe resource. - ## pobj:read() Read a record from a pipe object.