Просмотр исходного кода

Lua 5.1 to 5.3 realignement phase 1

Terry Ellison 4 лет назад
Родитель
Сommit
e7c29fe38e

+ 18 - 2
.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

+ 1 - 3
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 				\

+ 1 - 1
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.

+ 196 - 0
app/driver/input.c

@@ -0,0 +1,196 @@
+#include "platform.h"
+#include "driver/uart.h"
+#include "driver/input.h"
+#include <stdint.h>
+#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;
+}
+

+ 3 - 3
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

+ 0 - 111
app/driver/readline.c

@@ -1,111 +0,0 @@
-#include "ets_sys.h"
-#include "os_type.h"
-#include "osapi.h"
-#include "driver/uart.h"
-#include <stdint.h>
-
-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

+ 2 - 3
app/driver/rotary.c

@@ -14,9 +14,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
+#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;
 

+ 5 - 14
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

+ 1 - 1
app/driver/switec.c

@@ -18,13 +18,13 @@
 #include <stddef.h>
 #include <stdlib.h>
 #include <stdio.h>
+#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
 //

+ 24 - 15
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;
 }

+ 11 - 0
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 */

+ 0 - 6
app/include/driver/readline.h

@@ -1,6 +0,0 @@
-#ifndef READLINE_APP_H
-#define READLINE_APP_H
-
-bool uart_getc(char *c);
-
-#endif /* READLINE_APP_H */

+ 2 - 1
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);

+ 18 - 29
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)
-
-#define task_handle_t os_signal_t
-#define task_param_t os_param_t
-
-typedef void (*task_callback_t)(task_param_t param, uint8 prio);
-
-bool task_init_handler(uint8 priority, uint8 qlen);
-task_handle_t task_get_id(task_callback_t t);
+** The task interface is now part of the core platform interface.  
+** This header is preserved for backwards compatability only.
+*/  
+#include "platform.h"
+
+#define TASK_PRIORITY_LOW    PLATFORM_TASK_PRIORITY_LOW     
+#define TASK_PRIORITY_MEDIUM PLATFORM_TASK_PRIORITY_MEDIUM  
+#define TASK_PRIORITY_HIGH   PLATFORM_TASK_PRIORITY_HIGH  
+
+#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)
+
+#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
 

+ 2 - 2
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

+ 1 - 1
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.

+ 2 - 3
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) {

+ 2 - 2
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) \

+ 5 - 16
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;

+ 2 - 2
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) {

+ 12 - 18
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]);
   }

+ 116 - 0
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 <string.h>
+
+#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;
+}
+

+ 1 - 1
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 */

+ 1 - 1
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 */
 

+ 6 - 5
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; i<NUM_TAGS; i++) g->mt[i] = NULL;
   if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {

+ 1 - 0
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;
 

+ 197 - 414
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 <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#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;
-  }
-  lua_getfield(L, -1, "traceback");
-  if (!lua_isfunction(L, -1) && !lua_islightfunction(L, -1)) {
-    lua_pop(L, 2);
-    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_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("<eof>")
 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("<eof>")) - 1);
-    if (strstr(msg, LUA_QL("<eof>")) == tp) {
+    if (!strcmp(msg+lmsg-sizeof(EOFMARK)+1, EOFMARK)) {
       lua_pop(L, 1);
       return 1;
     }
   }
-  return 0;  /* else... */
+  return 0;
 }
 
+/*
+** 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 */
+  }
 
-/* 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 */
-    }
+  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);
   }
-  return 0;
-}
+ /*
+  * 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 =<expression> */
+    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");
 
-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;
+  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);
   }
-  return 0;
-}
-
 
-#ifndef LUA_INIT_STRING
-#define LUA_INIT_STRING "@init.lua"
-#endif
+  prompt = get_prompt(L, MLref!= LUA_NOREF ? 0 : 1);
+  input_setprompt(prompt);
+  puts(prompt);
 
-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);
+  lua_pushnil(L);
+  return 1;                       /* return nil will retask if pipe not empty */
 }
 
 
-struct Smain {
-  int argc;
-  char **argv;
-  int status;
-};
-
+/*
+ * 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);
-  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;
-  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 */
-  if (L == NULL) {
-    l_message(argv[0], "cannot create state: not enough memory");
-    return EXIT_FAILURE;
-  }
-  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());
+  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 );
-  // legc_set_mode( L, EGC_ON_MEM_LIMIT, 4096 );
-  // lua_close(L);
-  return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;
-}
-
-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);
-}
+  lua_settop(L, 0);
 
-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);
+  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"] */
 
-  progname = oldprogname;
+  input_setup(LUA_MAXINPUT, get_prompt(L, 1));
+  lua_input_string(" \n", 2);               /* queue CR to issue first prompt */
+  print_version(L);
 
-  load->done = 0;
-  load->line_position = 0;
-  memset(load->line, 0, load->len);
-  puts(load->prmt);
+  /* and last of all, kick off application initialisation */
+  if (init[0] == '@')
+    dofile(L, init+1);
+  else
+    dostring(L, init, LUA_INIT);
+  return 0;
 }
 
-#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;
+/*
+** 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("cannot create state: not enough memory");
+    return;
   }
+  lua_pushlightfunction(L, &pmain);   /* Call 'pmain' as a high priority task */
+  luaN_posttask(L, LUA_TASK_HIGH);
+}
 
-  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) */
 }

+ 14 - 13
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
 
 /******************************************************************************

+ 1 - 0
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 <stdint.h>
 #include <string.h>

+ 4 - 4
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;
 }
 

+ 18 - 4
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;
 }
 

+ 42 - 72
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;
-  // }
-
-  if (output_redir_ref == LUA_NOREF) {
-    uart0_sendStr(str);
-    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 (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;
 }
 

+ 221 - 65
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 <string.h>
+#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
+
+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);
 
-#define AT_HEAD 1
-#define AT_TAIL 0
+/* 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;
+}
+
+/* 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[1]
+// 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);

+ 1 - 0
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"
 

+ 31 - 61
app/modules/uart.c

@@ -7,83 +7,57 @@
 #include <stdint.h>
 #include <string.h>
 #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" );
-
-  if( lua_type( L, stack ) == LUA_TNUMBER )
+  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)
   {
-    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 );

+ 97 - 13
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);
+}

+ 22 - 3
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

+ 11 - 11
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

+ 1 - 0
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"

+ 0 - 41
app/task/Makefile

@@ -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
-

+ 0 - 72
app/task/task.c

@@ -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 <stdio.h>
-
-#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);
-}

+ 12 - 44
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

+ 7 - 38
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)
 

+ 3 - 3
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.