From dad6d9f9978181a213b7eeb7af43b0cd32f96b92 Mon Sep 17 00:00:00 2001 From: gemu2015 Date: Sat, 15 Jun 2019 07:02:34 +0200 Subject: [PATCH] update scripter bug fixes and enhancements --- scripter.md | 50 +++- sonoff/xdrv_10_scripter.ino | 491 ++++++++++++++++++++++++++++++------ 2 files changed, 452 insertions(+), 89 deletions(-) diff --git a/scripter.md b/scripter.md index 40ad3451e..43c966b6b 100644 --- a/scripter.md +++ b/scripter.md @@ -167,7 +167,7 @@ the last closing bracket must be on a single line the condition may not be enclosed in brackets >**break** exits a section or terminates a for next loop -**dprecx** sets decimal precision to x (0-9) +**dpx** sets decimal precision to x (0-9) **svars** save permanent vars **delay(x)** pauses x milliseconds (should be as short as possible) **spin(x m)** set gpio pin x (0-16) to value m (0,1) only the last bit is used, so even values set the pin to zero and uneven values set the pin to 1 @@ -189,22 +189,38 @@ specifies a for next loop, (loop count must not be less then 1) specifies a switch case selector **sd card support** -enable by CARD_CS = gpio pin of card chip select +enable by CARD_CS = gpio pin of card chip select (+ 10k flash) \#define USE_SCRIPT_FATFS CARD_CS sd card uses standard hardware spi gpios: mosi,miso,sclk max 4 files open at a time allows for e.g. logging sensors to a tab delimited file and then download (see example below) +the download of files may be executed in a kind of "multitasking" when bit 7 of loglvl is set (128+loglevel) +without multitasking 150kb/s (all processes are stopped during download), with multitasking 50kb/s (other tasmota processes are running) script itself is also stored on sdcard with a default size of 4096 chars -requires additional 10k flash + + +enable sd card directory support (+ 1,2k flash) +\#define SDCARD_DIR +shows a web sdcard directory (submeu of scripter) where you can +upload and download files to/from sd card + >**fr=fo("fname" m)** open file fname, mode 0=read, 1=write (returns file reference (0-3) or -1 for error) **res=fw("text" fr)** writes text to (the end of) file fr, returns number of bytes written **res=fr(svar fr)** reads a string into svar, returns bytes read (string is read until delimiter \t \n \r or eof) **fc(fr)** close file +**ff(fr)** flush file, writes cached data and updates directory **fd("fname")** delete file fname **flx(fname)** create download link for file (x=1 or 2) fname = file name of file to download **fsm** return 1 if filesystem is mounted, (valid sd card found) +extended commands (+0,9k flash) +\#define USE_SCRIPT_FATFS_EXT +>**fmd("fname")** make directory fname +>**frd("fname")** remove directory fname +>**fx("fname")** check if file fname exists +>**fe("fname")** execute script fname (max 2048 bytes, script file must start with '>' char on the 1. line) + **konsole script cmds** >**script 1 or 0** switch script on or off @@ -506,10 +522,25 @@ M:mtemp=0 60 str="" **\>B** -; open file for write -fr=fo("slog.txt" 1) ; set sensor file download link fl1("slog.txt") +; delete file in case we want to start fresh +;fd("slog.txt") + + +; list all files in root directory +fr=fo("/" 0) +for cnt 1 20 1 +res=fr(str fr) +if res>0 +then +=>print %cnt% : %str% +else +break +endif +next +fc(fr) + **\>T** ; get sensor values @@ -524,15 +555,18 @@ mtemp=temp ; write average to sensor log every minute if upsecs%60==0 then -; compose string for tab delimited file entry +; open file for write +fr=fo("slog.txt" 1) +; compose string for tab delimited file entry str=s(upsecs)+"\t"+s(mhum)+"\t"+s(mtemp)+"\n" ; write string to log file res=fw(str fr) +; close file +fc(fr) endif **\>R** -; close file -fc(fr) + **a real example** diff --git a/sonoff/xdrv_10_scripter.ino b/sonoff/xdrv_10_scripter.ino index d4734ba68..f9ae2762c 100644 --- a/sonoff/xdrv_10_scripter.ino +++ b/sonoff/xdrv_10_scripter.ino @@ -36,12 +36,9 @@ no math hierarchy (costs ram and execution time, better group with brackets, an keywords if then else endif, or, and are better readable for beginners (others may use {}) changelog after merging to Tasmota -1 show remaining chars in webui, -2 now can expand script space to 2048 chars by setting MAX_RULE_SETS to 4 -3 at24256 eeprom support #ifdef defaults to 4095 bytes script size (reduces heap by this amount) -4 some housekeeping -5 sd card support #ifdef allows eg for sensor logging -6 download link for sdcard files +1. draw color picture from sd card +2. upload files to sc card + \*********************************************************************************************/ @@ -96,6 +93,21 @@ struct M_FILT { float rbuff[1]; }; + +typedef union { + uint8_t data; + struct { + uint8_t nutu8 : 1; + uint8_t nutu7 : 1; + uint8_t nutu6 : 1; + uint8_t nutu5 : 1; + uint8_t nutu4 : 1; + uint8_t nutu3 : 1; + uint8_t is_dir : 1; + uint8_t is_open : 1; + }; +} FILE_FLAGS; + #define SFS_MAX 4 // global memory struct SCRIPT_MEM { @@ -122,7 +134,7 @@ struct SCRIPT_MEM { uint8_t flags; #ifdef USE_SCRIPT_FATFS File files[SFS_MAX]; - uint8_t file_flags[SFS_MAX]; + FILE_FLAGS file_flags[SFS_MAX]; uint8_t script_sd_found; char flink[2][14]; #endif @@ -139,6 +151,7 @@ char *GetNumericResult(char *lp,uint8_t lastop,float *fp,JsonObject *jo); char *GetStringResult(char *lp,uint8_t lastop,char *cp,JsonObject *jo); char *ForceStringVar(char *lp,char *dstr); void send_download(void); +uint8_t reject(char *name); void ScriptEverySecond(void) { @@ -496,7 +509,7 @@ char *script; } } for (uint8_t cnt=0;cnt=SFS_MAX) ind=SFS_MAX-1; glob_script_mem.files[ind].close(); - glob_script_mem.file_flags[ind]=0; + glob_script_mem.file_flags[ind].is_open=0; + fvar=0; + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"ff(",3)) { + lp+=3; + lp=GetNumericResult(lp,OPER_EQU,&fvar,0); + uint8_t ind=fvar; + if (ind>=SFS_MAX) ind=SFS_MAX-1; + glob_script_mem.files[ind].flush(); fvar=0; lp++; len=0; @@ -923,7 +955,7 @@ chknext: lp=GetNumericResult(lp,OPER_EQU,&fvar,0); uint8_t ind=fvar; if (ind>=SFS_MAX) ind=SFS_MAX-1; - if (glob_script_mem.file_flags[ind]&1) { + if (glob_script_mem.file_flags[ind].is_open) { fvar=glob_script_mem.files[ind].print(str); } else { fvar=0; @@ -960,19 +992,37 @@ chknext: uint8_t index=0; char str[glob_script_mem.max_ssize+1]; char *cp=str; - if (glob_script_mem.file_flags[find]&1) { - while (glob_script_mem.files[find].available()) { - uint8_t buf[1]; - glob_script_mem.files[find].read(buf,1); - if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { - break; - } else { - *cp++=buf[0]; - index++; - if (index>=glob_script_mem.max_ssize-1) break; + if (glob_script_mem.file_flags[find].is_open) { + if (glob_script_mem.file_flags[find].is_dir) { + while (true) { + File entry=glob_script_mem.files[find].openNextFile(); + if (entry) { + if (!reject((char*)entry.name())) { + strcpy(str,entry.name()); + entry.close(); + break; + } + } else { + *cp=0; + break; + } + entry.close(); } + index=strlen(str); + } else { + while (glob_script_mem.files[find].available()) { + uint8_t buf[1]; + glob_script_mem.files[find].read(buf,1); + if (buf[0]=='\t' || buf[0]==',' || buf[0]=='\n' || buf[0]=='\r') { + break; + } else { + *cp++=buf[0]; + index++; + if (index>=glob_script_mem.max_ssize-1) break; + } + } + *cp=0; } - *cp=0; } else { strcpy(str,"file error"); } @@ -991,6 +1041,59 @@ chknext: len=0; goto exit; } +#ifdef USE_SCRIPT_FATFS_EXT + if (!strncmp(vname,"fe(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + // execute script + File ef=SD.open(str); + if (ef) { + uint16_t fsiz=ef.size(); + if (fsiz<2048) { + char *script=(char*)calloc(fsiz+16,1); + if (script) { + ef.read((uint8_t*)script,fsiz); + execute_script(script); + free(script); + fvar=1; + } + } + ef.close(); + } + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fmd(",4)) { + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SD.mkdir(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"frd(",4)) { + lp+=4; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + fvar=SD.rmdir(str); + lp++; + len=0; + goto exit; + } + if (!strncmp(vname,"fx(",3)) { + lp+=3; + char str[glob_script_mem.max_ssize+1]; + lp=GetStringResult(lp,OPER_EQU,str,0); + if (SD.exists(str)) fvar=1; + else fvar=0; + lp++; + len=0; + goto exit; + } +#endif if (!strncmp(vname,"fl1(",4) || !strncmp(vname,"fl2(",4) ) { uint8_t lknum=*(lp+2)&3; lp+=4; @@ -1746,6 +1849,7 @@ void toSLog(const char *str) { +//#define IFTHEN_DEBUG #define IF_NEST 8 // execute section of scripter @@ -1800,7 +1904,8 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { } glob_script_mem.var_not_found=0; -#if SCRIPT_DEBUG>0 +//#if SCRIPT_DEBUG>0 +#ifdef IFTHEN_DEBUG char tbuff[128]; sprintf(tbuff,"stack=%d,state=%d,cmpres=%d line: ",ifstck,if_state[ifstck],if_result[ifstck]); toLogEOL(tbuff,lp); @@ -1825,6 +1930,8 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (ifstck>0) { ifstck--; } + if (if_state[ifstck]==3 && if_result[ifstck]) goto next_line; + if (if_state[ifstck]==2 && !if_result[ifstck]) goto next_line; s_ifstck=ifstck; // >>>>> goto next_line; } else if (!strncmp(lp,"or",2) && if_state[ifstck]==1) { @@ -1866,6 +1973,8 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (ifstck>0) { ifstck--; } + if (if_state[ifstck]==3 && if_result[ifstck]) goto next_line; + if (if_state[ifstck]==2 && !if_result[ifstck]) goto next_line; s_ifstck=ifstck; // >>>>> } } @@ -1926,16 +2035,20 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { if (swflg==2) goto next_line; - SCRIPT_SKIP_SPACES //SCRIPT_SKIP_EOL if (*lp==SCRIPT_EOL) { goto next_line; } + //toLogN(lp,16); if (if_state[s_ifstck]==3 && if_result[s_ifstck]) goto next_line; if (if_state[s_ifstck]==2 && !if_result[s_ifstck]) goto next_line; +#ifdef IFTHEN_DEBUG + sprintf(tbuff,"stack=%d,state=%d,cmpres=%d execute line: ",ifstck,if_state[ifstck],if_result[ifstck]); + toLogEOL(tbuff,lp); +#endif s_ifstck=ifstck; if (!strncmp(lp,"break",5)) { @@ -1946,8 +2059,8 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { section=0; } break; - } else if (!strncmp(lp,"dprec",5)) { - lp+=5; + } else if (!strncmp(lp,"dp",2) && isdigit(*(lp+2))) { + lp+=2; // number precision glob_script_mem.script_dprec=atoi(lp); goto next_line; @@ -1987,6 +2100,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { else if (!strncmp(lp,"=>",2)) { // execute cmd lp+=2; + char *slp=lp; SCRIPT_SKIP_SPACES #define SCRIPT_CMDMEM 512 char *cmdmem=(char*)malloc(SCRIPT_CMDMEM); @@ -2019,6 +2133,7 @@ int16_t Run_Scripter(const char *type, uint8_t tlen, char *js) { } if (cmdmem) free(cmdmem); } + lp=slp; goto next_line; } else if (!strncmp(lp,"=#",2)) { // subroutine @@ -2374,6 +2489,9 @@ void Scripter_save_pvars(void) { #define WEB_HANDLE_SCRIPT "s10" #define D_CONFIGURE_SCRIPT "Edit script" #define D_SCRIPT "edit script" +#define D_SDCARD_UPLOAD "file upload" +#define D_SDCARD_DIR "sd card directory" +#define D_UPL_DONE "Done" const char S_CONFIGURE_SCRIPT[] PROGMEM = D_CONFIGURE_SCRIPT; @@ -2387,8 +2505,8 @@ const char HTTP_FORM_SCRIPT[] PROGMEM = const char HTTP_FORM_SCRIPT1[] PROGMEM = "
" - "script enable
" - "