diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b4a11279..a7a0a77fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. - Berry language improved Tasmota integration - Filesystem commands ``Ufs``, ``UfsType``, ``UfsSize``, ``UfsFree``, ``UfsDelete``, ``UfsRename`` and ``UfsRun`` - Basic support for filesystem ``autoexec.bat`` +- Berry add file system support ### Changed - IRremoteESP8266 library from v2.7.14 to v2.7.15 diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp b/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp index fd9ac48d6..003a1042a 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp +++ b/lib/libesp32/Berry-0.1.10/src/port/be_port.cpp @@ -8,10 +8,14 @@ #include "berry.h" #include "be_mem.h" #include "be_sys.h" -#include +// #include #include #include +// Local pointer for file managment +#include +extern FS *dfsp; + /* this file contains configuration for the file system. */ /* standard input and output */ @@ -47,9 +51,36 @@ extern "C" { } } +// We need to create a local buffer, since we might mess up mqtt_data +#ifndef BERRY_LOGSZ +#define BERRY_LOGSZ 128 +#endif +static char log_berry_buffer[BERRY_LOGSZ] = { 0, }; +extern void berry_log(const char * berry_buf); + BERRY_API void be_writebuffer(const char *buffer, size_t length) { - Serial.write(buffer, length); + if (buffer == nullptr || length == 0) { return; } + uint32_t idx = 0; + while (idx < length) { + int32_t cr_pos = -1; + // find next occurence of '\n' + for (uint32_t i = idx; i < length; i++) { + if (buffer[i] == '\n') { + cr_pos = i; + break; + } + } + uint32_t chars_to_append = (cr_pos >= 0) ? cr_pos - idx : length - idx; // note cr_pos < length + snprintf(log_berry_buffer, sizeof(log_berry_buffer), "%s%.*s", log_berry_buffer, chars_to_append, buffer); // append at most `length` chars + if (cr_pos >= 0) { + // flush + berry_log(log_berry_buffer); + log_berry_buffer[0] = 0; // clear string + } + idx += chars_to_append + 1; // skip '\n' + } + // Serial.write(buffer, length); // be_fwrite(stdout, buffer, length); } @@ -63,54 +94,116 @@ BERRY_API char* be_readstring(char *buffer, size_t size) void* be_fopen(const char *filename, const char *modes) { + if (dfsp != nullptr && filename != nullptr && modes != nullptr) { + // Serial.printf("be_fopen filename=%s, modes=%s\n", filename, modes); + File f = dfsp->open(filename, modes); // returns an object, not a pointer + if (f) { + File * f_ptr = new File(f); // copy to dynamic object + *f_ptr = f; // TODO is this necessary? + return f_ptr; + } + } return nullptr; // return fopen(filename, modes); } int be_fclose(void *hfile) { - return 0; + // Serial.printf("be_fclose\n"); + if (dfsp != nullptr && hfile != nullptr) { + File * f_ptr = (File*) hfile; + f_ptr->close(); + delete f_ptr; + return 0; + } + return -1; // return fclose(hfile); } size_t be_fwrite(void *hfile, const void *buffer, size_t length) { + // Serial.printf("be_fwrite %d\n", length); + if (dfsp != nullptr && hfile != nullptr && buffer != nullptr) { + File * f_ptr = (File*) hfile; + return f_ptr->write((const uint8_t*) buffer, length); + } return 0; // return fwrite(buffer, 1, length, hfile); } size_t be_fread(void *hfile, void *buffer, size_t length) { + // Serial.printf("be_fread %d\n", length); + if (dfsp != nullptr && hfile != nullptr && buffer != nullptr) { + File * f_ptr = (File*) hfile; + int32_t ret = f_ptr->read((uint8_t*) buffer, length); + if (ret >= 0) { + // Serial.printf("be_fread ret = %d\n", ret); + return ret; + } + } return 0; // return fread(buffer, 1, length, hfile); } char* be_fgets(void *hfile, void *buffer, int size) { + // Serial.printf("be_fgets %d\n", size); + uint8_t * buf = (uint8_t*) buffer; + if (dfsp != nullptr && hfile != nullptr && buffer != nullptr && size > 0) { + File * f_ptr = (File*) hfile; + int ret = f_ptr->readBytesUntil('\n', buf, size - 1); + if (ret >= 0) { + buf[ret] = 0; // add string terminator + return (char*) buffer; + } + } return nullptr; // return fgets(buffer, size, hfile); } int be_fseek(void *hfile, long offset) { - return 0; + // Serial.printf("be_fseek %d\n", offset); + if (dfsp != nullptr && hfile != nullptr) { + File * f_ptr = (File*) hfile; + if (f_ptr->seek(offset)) { + return 0; // success + } + } + return -1; // return fseek(hfile, offset, SEEK_SET); } long int be_ftell(void *hfile) { + // Serial.printf("be_ftell\n"); + if (dfsp != nullptr && hfile != nullptr) { + File * f_ptr = (File*) hfile; + return f_ptr->position(); + } return 0; // return ftell(hfile); } long int be_fflush(void *hfile) { + // Serial.printf("be_fflush\n"); + if (dfsp != nullptr && hfile != nullptr) { + File * f_ptr = (File*) hfile; + f_ptr->flush(); + } return 0; // return fflush(hfile); } size_t be_fsize(void *hfile) { + // Serial.printf("be_fsize\n"); + if (dfsp != nullptr && hfile != nullptr) { + File * f_ptr = (File*) hfile; + return f_ptr->size(); + } // long int size, offset = be_ftell(hfile); // fseek(hfile, 0L, SEEK_END); // size = ftell(hfile); @@ -121,233 +214,233 @@ size_t be_fsize(void *hfile) -// #if BE_USE_FILE_SYSTEM -// #if defined(USE_FATFS) /* FatFs */ +#if BE_USE_FILE_SYSTEM +#if defined(USE_FATFS) /* FatFs */ -// int be_isdir(const char *path) -// { -// FILINFO fno; -// FRESULT fr = f_stat(path, &fno); -// return fr == FR_OK && fno.fattrib & AM_DIR; -// } +int be_isdir(const char *path) +{ + FILINFO fno; + FRESULT fr = f_stat(path, &fno); + return fr == FR_OK && fno.fattrib & AM_DIR; +} -// int be_isfile(const char *path) -// { -// FILINFO fno; -// FRESULT fr = f_stat(path, &fno); -// return fr == FR_OK && !(fno.fattrib & AM_DIR); -// } +int be_isfile(const char *path) +{ + FILINFO fno; + FRESULT fr = f_stat(path, &fno); + return fr == FR_OK && !(fno.fattrib & AM_DIR); +} -// int be_isexist(const char *path) -// { -// FILINFO fno; -// return f_stat(path, &fno) == FR_OK; -// } +int be_isexist(const char *path) +{ + FILINFO fno; + return f_stat(path, &fno) == FR_OK; +} -// char* be_getcwd(char *buf, size_t size) -// { -// FRESULT fr = f_getcwd(buf, (UINT)size); -// return fr == FR_OK ? buf : NULL; -// } +char* be_getcwd(char *buf, size_t size) +{ + FRESULT fr = f_getcwd(buf, (UINT)size); + return fr == FR_OK ? buf : NULL; +} -// int be_chdir(const char *path) -// { -// return f_chdir(path); -// } +int be_chdir(const char *path) +{ + return f_chdir(path); +} -// int be_mkdir(const char *path) -// { -// return f_mkdir(path); -// } +int be_mkdir(const char *path) +{ + return f_mkdir(path); +} -// int be_unlink(const char *filename) -// { -// return f_unlink(filename); -// } +int be_unlink(const char *filename) +{ + return f_unlink(filename); +} -// int be_dirfirst(bdirinfo *info, const char *path) -// { -// info->dir = be_os_malloc(sizeof(DIR)); -// info->file = be_os_malloc(sizeof(FILINFO)); -// if (info->dir && info->file) { -// FRESULT fr = f_opendir(info->dir, path); -// return fr == FR_OK ? be_dirnext(info) : 1; -// } -// be_os_free(info->dir); -// be_os_free(info->file); -// info->dir = NULL; -// info->file = NULL; -// return 1; -// } +int be_dirfirst(bdirinfo *info, const char *path) +{ + info->dir = be_os_malloc(sizeof(DIR)); + info->file = be_os_malloc(sizeof(FILINFO)); + if (info->dir && info->file) { + FRESULT fr = f_opendir(info->dir, path); + return fr == FR_OK ? be_dirnext(info) : 1; + } + be_os_free(info->dir); + be_os_free(info->file); + info->dir = NULL; + info->file = NULL; + return 1; +} -// int be_dirnext(bdirinfo *info) -// { -// FRESULT fr = f_readdir(info->dir, info->file); -// info->name = ((FILINFO *)info->file)->fname; -// return fr != FR_OK || *info->name == '\0'; -// } +int be_dirnext(bdirinfo *info) +{ + FRESULT fr = f_readdir(info->dir, info->file); + info->name = ((FILINFO *)info->file)->fname; + return fr != FR_OK || *info->name == '\0'; +} -// int be_dirclose(bdirinfo *info) -// { -// if (info->dir) { -// int res = f_closedir(info->dir) != FR_OK; -// be_os_free(info->dir); -// be_os_free(info->file); -// return res; -// } -// return 1; -// } +int be_dirclose(bdirinfo *info) +{ + if (info->dir) { + int res = f_closedir(info->dir) != FR_OK; + be_os_free(info->dir); + be_os_free(info->file); + return res; + } + return 1; +} -// #elif defined(_MSC_VER) /* MSVC*/ +#elif defined(_MSC_VER) /* MSVC*/ -// #include -// #include -// #include +#include +#include +#include -// int be_isdir(const char *path) -// { -// DWORD type = GetFileAttributes(path); -// return type != INVALID_FILE_ATTRIBUTES -// && (type & FILE_ATTRIBUTE_DIRECTORY) != 0; -// } +int be_isdir(const char *path) +{ + DWORD type = GetFileAttributes(path); + return type != INVALID_FILE_ATTRIBUTES + && (type & FILE_ATTRIBUTE_DIRECTORY) != 0; +} -// int be_isfile(const char *path) -// { -// DWORD type = GetFileAttributes(path); -// return type != INVALID_FILE_ATTRIBUTES -// && (type & FILE_ATTRIBUTE_DIRECTORY) == 0; -// } +int be_isfile(const char *path) +{ + DWORD type = GetFileAttributes(path); + return type != INVALID_FILE_ATTRIBUTES + && (type & FILE_ATTRIBUTE_DIRECTORY) == 0; +} -// int be_isexist(const char *path) -// { -// return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES; -// } +int be_isexist(const char *path) +{ + return GetFileAttributes(path) != INVALID_FILE_ATTRIBUTES; +} -// char* be_getcwd(char *buf, size_t size) -// { -// return _getcwd(buf, (int)size); -// } +char* be_getcwd(char *buf, size_t size) +{ + return _getcwd(buf, (int)size); +} -// int be_chdir(const char *path) -// { -// return _chdir(path); -// } +int be_chdir(const char *path) +{ + return _chdir(path); +} -// int be_mkdir(const char *path) -// { -// return _mkdir(path); -// } +int be_mkdir(const char *path) +{ + return _mkdir(path); +} -// int be_unlink(const char *filename) -// { -// return remove(filename); -// } +int be_unlink(const char *filename) +{ + return remove(filename); +} -// int be_dirfirst(bdirinfo *info, const char *path) -// { -// char *buf = be_os_malloc(strlen(path) + 3); -// info->file = be_os_malloc(sizeof(struct _finddata_t)); -// info->dir = NULL; -// if (buf && info->file) { -// struct _finddata_t *cfile = info->file; -// strcat(strcpy(buf, path), "/*"); -// info->dir = (void *)_findfirst(buf, cfile); -// info->name = cfile->name; -// be_os_free(buf); -// return (intptr_t)info->dir == -1; -// } -// be_os_free(buf); -// return 1; -// } +int be_dirfirst(bdirinfo *info, const char *path) +{ + char *buf = be_os_malloc(strlen(path) + 3); + info->file = be_os_malloc(sizeof(struct _finddata_t)); + info->dir = NULL; + if (buf && info->file) { + struct _finddata_t *cfile = info->file; + strcat(strcpy(buf, path), "/*"); + info->dir = (void *)_findfirst(buf, cfile); + info->name = cfile->name; + be_os_free(buf); + return (intptr_t)info->dir == -1; + } + be_os_free(buf); + return 1; +} -// int be_dirnext(bdirinfo *info) -// { -// struct _finddata_t *cfile = info->file; -// int res = _findnext((intptr_t)info->dir, cfile) != 0; -// info->name = cfile->name; -// return res; -// } +int be_dirnext(bdirinfo *info) +{ + struct _finddata_t *cfile = info->file; + int res = _findnext((intptr_t)info->dir, cfile) != 0; + info->name = cfile->name; + return res; +} -// int be_dirclose(bdirinfo *info) -// { -// be_os_free(info->file); -// return _findclose((intptr_t)info->dir) != 0; -// } +int be_dirclose(bdirinfo *info) +{ + be_os_free(info->file); + return _findclose((intptr_t)info->dir) != 0; +} -// #else /* must be POSIX */ +#else /* must be POSIX */ -// #include -// #include -// #include +#include +#include +#include -// int be_isdir(const char *path) -// { -// struct stat path_stat; -// int res = stat(path, &path_stat); -// return res == 0 && S_ISDIR(path_stat.st_mode); -// } +int be_isdir(const char *path) +{ + struct stat path_stat; + int res = stat(path, &path_stat); + return res == 0 && S_ISDIR(path_stat.st_mode); +} -// int be_isfile(const char *path) -// { -// struct stat path_stat; -// int res = stat(path, &path_stat); -// return res == 0 && !S_ISDIR(path_stat.st_mode); -// } +int be_isfile(const char *path) +{ + struct stat path_stat; + int res = stat(path, &path_stat); + return res == 0 && !S_ISDIR(path_stat.st_mode); +} -// int be_isexist(const char *path) -// { -// struct stat path_stat; -// return stat(path, &path_stat) == 0; -// } +int be_isexist(const char *path) +{ + struct stat path_stat; + return stat(path, &path_stat) == 0; +} -// char* be_getcwd(char *buf, size_t size) -// { -// return getcwd(buf, size); -// } +char* be_getcwd(char *buf, size_t size) +{ + return getcwd(buf, size); +} -// int be_chdir(const char *path) -// { -// return chdir(path); -// } +int be_chdir(const char *path) +{ + return chdir(path); +} -// int be_mkdir(const char *path) -// { -// #ifdef _WIN32 -// return mkdir(path); -// #else -// return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); -// #endif -// } +int be_mkdir(const char *path) +{ +#ifdef _WIN32 + return mkdir(path); +#else + return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#endif +} -// int be_unlink(const char *filename) -// { -// return remove(filename); -// } +int be_unlink(const char *filename) +{ + return remove(filename); +} -// int be_dirfirst(bdirinfo *info, const char *path) -// { -// info->dir = opendir(path); -// if (info->dir) { -// return be_dirnext(info); -// } -// return 1; -// } +int be_dirfirst(bdirinfo *info, const char *path) +{ + info->dir = opendir(path); + if (info->dir) { + return be_dirnext(info); + } + return 1; +} -// int be_dirnext(bdirinfo *info) -// { -// struct dirent *file; -// info->file = file = readdir(info->dir); -// if (file) { -// info->name = file->d_name; -// return 0; -// } -// return 1; -// } +int be_dirnext(bdirinfo *info) +{ + struct dirent *file; + info->file = file = readdir(info->dir); + if (file) { + info->name = file->d_name; + return 0; + } + return 1; +} -// int be_dirclose(bdirinfo *info) -// { -// return closedir(info->dir) != 0; -// } +int be_dirclose(bdirinfo *info) +{ + return closedir(info->dir) != 0; +} -// #endif /* POSIX */ -// #endif /* BE_USE_OS_MODULE || BE_USE_FILE_SYSTEM */ +#endif /* POSIX */ +#endif /* BE_USE_OS_MODULE || BE_USE_FILE_SYSTEM */ diff --git a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h index 5228f567f..820207003 100644 --- a/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h +++ b/lib/libesp32/Berry-0.1.10/src/port/berry_conf.h @@ -38,7 +38,7 @@ extern "C" { * type when the value is 2. * Default: 2 */ -#define BE_INTGER_TYPE 1 +#define BE_INTGER_TYPE 1 // use long int = uint32_t /* Macro: BE_USE_SINGLE_FLOAT * Select floating point precision. @@ -47,7 +47,7 @@ extern "C" { * numbers. * Default: 0 **/ -#define BE_USE_SINGLE_FLOAT 1 +#define BE_USE_SINGLE_FLOAT 1 // use `float` not `double` /* Macro: BE_USE_PRECOMPILED_OBJECT * Use precompiled objects to avoid creating these objects at @@ -118,14 +118,14 @@ extern "C" { * otherwise disable the feature. * Default: 1 **/ -#define BE_USE_BYTECODE_SAVER 0 +#define BE_USE_BYTECODE_SAVER 1 /* Macro: BE_USE_BYTECODE_LOADER * Enable load bytecode from file when BE_USE_BYTECODE_LOADER is not 0, * otherwise disable the feature. * Default: 1 **/ -#define BE_USE_BYTECODE_LOADER 0 +#define BE_USE_BYTECODE_LOADER 1 /* Macro: BE_USE_SHARED_LIB * Enable shared library when BE_USE_SHARED_LIB is not 0, @@ -160,7 +160,7 @@ extern "C" { #define BE_USE_TIME_MODULE 0 #define BE_USE_OS_MODULE 0 #define BE_USE_SYS_MODULE 0 -#define BE_USE_DEBUG_MODULE 0 +#define BE_USE_DEBUG_MODULE 1 #define BE_USE_GC_MODULE 1 /* Macro: BE_EXPLICIT_XXX diff --git a/tasmota/berry/tasmota.be b/tasmota/berry/tasmota.be index c5629a5e6..bff83c022 100644 --- a/tasmota/berry/tasmota.be +++ b/tasmota/berry/tasmota.be @@ -1,5 +1,9 @@ import json import string tasmota = module("tasmota") +def log(m) print(m) end + +####### + def charsinstring(s,c) for i:0..size(s)-1 for j:0..size(c)-1 @@ -32,17 +36,17 @@ tasmota._operators="=<>!|" # split the item when there is an operator, returns a list of (left,op,right) # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"] tasmota.find_op = def (item) - pos = charsinstring(item, tasmota._operators) + var pos = charsinstring(item, tasmota._operators) if pos>=0 - op_split = string.split(item,pos) + var op_split = string.split(item,pos) #print(op_split) - op_left = op_split[0] - op_rest = op_split[1] + var op_left = op_split[0] + var op_rest = op_split[1] # iterate through operators for op:tasmota._op if string.find(op_rest,op[0]) == 0 - op_func = op[1] - op_right = string.split(op_rest,size(op[0]))[1] + var op_func = op[1] + var op_right = string.split(op_rest,size(op[0]))[1] return [op_left,op_func,op_right] end end @@ -52,7 +56,7 @@ end def findkeyi(m,keyi) - keyu=string.toupper(keyi) + var keyu = string.toupper(keyi) if classof(m) == map for k:m.keys() if string.toupper(k)==keyu || keyi=='?' @@ -64,9 +68,9 @@ end tasmota.try_rule = def (ev, rule, f) - rl_list = tasmota.find_op(rule) - e=ev - rl=string.split(rl_list[0],'#') + var rl_list = tasmota.find_op(rule) + var e=ev + var rl=string.split(rl_list[0],'#') for it:rl found=findkeyi(e,it) if found == nil @@ -89,11 +93,11 @@ tasmota_rules={} tasmota.rule = def(pat,f) tasmota_rules[pat] = f end tasmota.exec_rules = def (ev_json) - ev = json.load(ev_json) + var ev = json.load(ev_json) + var ret = false if ev == nil - log("ERROR, bad json: "+ev_json, 3) + log('BRY: ERROR, bad json: '+ev_json, 3) end - ret = false for r:tasmota_rules.keys() ret = tasmota.try_rule(ev,r,tasmota_rules[r]) || ret end diff --git a/tasmota/xdrv_52_berry.ino b/tasmota/xdrv_52_berry.ino index a37fd78ad..b8f10eb25 100644 --- a/tasmota/xdrv_52_berry.ino +++ b/tasmota/xdrv_52_berry.ino @@ -186,6 +186,11 @@ extern "C" { } } +// called as a replacement to Berry `print()` +void berry_log(const char * berry_buf); +void berry_log(const char * berry_buf) { + AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf); +} /*********************************************************************************************\ * Handlers for Berry calls and async @@ -302,7 +307,7 @@ const char berry_prog[] PROGMEM = // find a key in map, case insensitive, return actual key or nil if not found "def findkeyi(m,keyi) " - "keyu=string.toupper(keyi) " + "var keyu = string.toupper(keyi) " "if classof(m) == map " "for k:m.keys() " "if string.toupper(k)==keyu || keyi=='?' " @@ -341,17 +346,17 @@ const char berry_prog[] PROGMEM = // # split the item when there is an operator, returns a list of (left,op,right) // # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"] "tasmota.find_op = def (item) " - "pos = charsinstring(item, tasmota._operators) " + "var pos = charsinstring(item, tasmota._operators) " "if pos>=0 " - "op_split = string.split(item,pos) " + "var op_split = string.split(item,pos) " // #print(op_split) - "op_left = op_split[0] " - "op_rest = op_split[1] " + "var op_left = op_split[0] " + "var op_rest = op_split[1] " // # iterate through operators "for op:tasmota._op " "if string.find(op_rest,op[0]) == 0 " - "op_func = op[1] " - "op_right = string.split(op_rest,size(op[0]))[1] " + "var op_func = op[1] " + "var op_right = string.split(op_rest,size(op[0]))[1] " "return [op_left,op_func,op_right] " "end " "end " @@ -362,9 +367,9 @@ const char berry_prog[] PROGMEM = // Rules trigger if match. return true if match, false if not // Note: condition is not yet managed "tasmota.try_rule = def (ev, rule, f) " - "rl_list = tasmota.find_op(rule) " - "e=ev " - "rl=string.split(rl_list[0],'#') " + "var rl_list = tasmota.find_op(rule) " + "var e=ev " + "var rl=string.split(rl_list[0],'#') " "for it:rl " "found=findkeyi(e,it) " "if found == nil " @@ -386,8 +391,8 @@ const char berry_prog[] PROGMEM = // Run rules, i.e. check each individual rule // Returns true if at least one rule matched, false if none "tasmota.exec_rules = def (ev_json) " - "ev = json.load(ev_json) " - "ret = false " + "var ev = json.load(ev_json) " + "var ret = false " "if ev == nil " "log('BRY: ERROR, bad json: '+ev_json, 3) " "end " @@ -404,7 +409,7 @@ const char berry_prog[] PROGMEM = "tasmota.timer = def (delay,f) tasmota_timers.push([tasmota.millis(delay),f]) end " "def _run_deferred() " - "i=0 " + "var i=0 " "while i