diff --git a/lib/lib_i2c/MPU6886/src/MPU6886.cpp b/lib/lib_i2c/MPU6886/src/MPU6886.cpp
index 9ae45461d..8774d09f7 100755
--- a/lib/lib_i2c/MPU6886/src/MPU6886.cpp
+++ b/lib/lib_i2c/MPU6886/src/MPU6886.cpp
@@ -4,24 +4,24 @@
void MPU6886::I2C_Read_NBytes(uint8_t driver_Addr, uint8_t start_Addr, uint8_t number_Bytes, uint8_t *read_Buffer){
- myWire.beginTransmission(driver_Addr);
- myWire.write(start_Addr);
- myWire.endTransmission(false);
+ myWire->beginTransmission(driver_Addr);
+ myWire->write(start_Addr);
+ myWire->endTransmission(false);
uint8_t i = 0;
- myWire.requestFrom(driver_Addr,number_Bytes);
+ myWire->requestFrom(driver_Addr,number_Bytes);
//! Put read results in the Rx buffer
- while (myWire.available()) {
- read_Buffer[i++] = myWire.read();
+ while (myWire->available()) {
+ read_Buffer[i++] = myWire->read();
}
}
void MPU6886::I2C_Write_NBytes(uint8_t driver_Addr, uint8_t start_Addr, uint8_t number_Bytes, uint8_t *write_Buffer){
- myWire.beginTransmission(driver_Addr);
- myWire.write(start_Addr);
- myWire.write(*write_Buffer);
- myWire.endTransmission();
+ myWire->beginTransmission(driver_Addr);
+ myWire->write(start_Addr);
+ myWire->write(*write_Buffer);
+ myWire->endTransmission();
}
diff --git a/lib/lib_i2c/MPU6886/src/MPU6886.h b/lib/lib_i2c/MPU6886/src/MPU6886.h
index 025f71587..b4a5541e0 100755
--- a/lib/lib_i2c/MPU6886/src/MPU6886.h
+++ b/lib/lib_i2c/MPU6886/src/MPU6886.h
@@ -71,9 +71,9 @@ class MPU6886 {
public:
MPU6886(void) {};
#ifdef ESP32
- void setBus(uint32_t _bus) { myWire = _bus ? Wire1 : Wire; };
+ void setBus(uint32_t _bus) { myWire = _bus ? &Wire1 : &Wire; };
#else
- void setBus(uint32_t _bus) { myWire = Wire; };
+ void setBus(uint32_t _bus) { myWire = &Wire; };
#endif
int Init(void);
void getAccelAdc(int16_t* ax, int16_t* ay, int16_t* az);
@@ -93,7 +93,7 @@ class MPU6886 {
// void getAhrsData(float *pitch,float *roll,float *yaw);
public:
- TwoWire & myWire = Wire; // default to Wire (bus 0)
+ TwoWire * myWire = &Wire; // default to Wire (bus 0)
float aRes, gRes;
private:
diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c b/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c
index 444133852..020766460 100644
--- a/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c
+++ b/lib/libesp32/Berry-0.1.10/src/port/be_energylib.c
@@ -19,7 +19,7 @@ be_define_native_module(energy, NULL);
#else
/* @const_object_info_begin
module tasmota (scope: global, depend: 1) {
- getfreeheap, func(l_getFreeHeap)
+ get_free_heap, func(l_getFreeHeap)
}
@const_object_info_end */
#include "../generate/be_fixed_tasmota.h"
diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c
index 17b09a219..713effefd 100644
--- a/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c
+++ b/lib/libesp32/Berry-0.1.10/src/port/be_tasmotalib.c
@@ -27,6 +27,8 @@ extern int l_getpower(bvm *vm);
extern int l_setlight(bvm *vm);
extern int l_setpower(bvm *vm);
+extern int l_i2cenabled(bvm *vm);
+
// #if !BE_USE_PRECOMPILED_OBJECT
#if 1 // TODO we will do pre-compiled later
// Class definition
@@ -39,27 +41,30 @@ void be_load_tasmota_ntvlib(bvm *vm)
{ "_rules", NULL },
{ "_timers", NULL },
{ "_cmd", NULL },
- { "getfreeheap", l_getFreeHeap },
+ { "_drivers", NULL },
+ { "get_free_heap", l_getFreeHeap },
{ "publish", l_publish },
{ "cmd", l_cmd },
- { "getoption", l_getoption },
+ { "get_option", l_getoption },
{ "millis", l_millis },
- { "timereached", l_timereached },
+ { "time_reached", l_timereached },
{ "yield", l_yield },
{ "delay", l_delay },
- { "scaleuint", l_scaleuint },
+ { "scale_uint", l_scaleuint },
- { "respcmnd", l_respCmnd },
- { "respcmndstr", l_respCmndStr },
- { "respcmnd_done", l_respCmndDone },
- { "respcmnd_error", l_respCmndError },
- { "respcmnd_failed", l_respCmndFailed },
+ { "resp_cmnd", l_respCmnd },
+ { "resp_cmnd_str", l_respCmndStr },
+ { "resp_cmnd_done", l_respCmndDone },
+ { "resp_cmnd_error", l_respCmndError },
+ { "resp_cmnd_failed", l_respCmndFailed },
{ "resolvecmnd", l_resolveCmnd },
- { "getlight", l_getlight },
- { "getpower", l_getpower },
- { "setlight", l_setlight },
- { "setpower", l_setpower },
+ { "get_light", l_getlight },
+ { "get_power", l_getpower },
+ { "set_light", l_setlight },
+ { "set_power", l_setpower },
+
+ { "i2c_enabled", l_i2cenabled },
{ NULL, NULL }
};
@@ -69,7 +74,7 @@ void be_load_tasmota_ntvlib(bvm *vm)
#else
/* @const_object_info_begin
module tasmota (scope: global, depend: 1) {
- getfreeheap, func(l_getFreeHeap)
+ get_free_heap, func(l_getFreeHeap)
}
@const_object_info_end */
#include "../generate/be_fixed_tasmota.h"
diff --git a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c
index 07581b2a4..b6a81c469 100644
--- a/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c
+++ b/lib/libesp32/Berry-0.1.10/src/port/be_wirelib.c
@@ -20,6 +20,9 @@ extern int b_wire_scan(bvm *vm);
extern int b_wire_validwrite(bvm *vm);
extern int b_wire_validread(bvm *vm);
+extern int b_wire_readbytes(bvm *vm);
+extern int b_wire_writebytes(bvm *vm);
+extern int b_wire_detect(bvm *vm);
// #if !BE_USE_PRECOMPILED_OBJECT
#if 1 // TODO we will do pre-compiled later
@@ -28,15 +31,18 @@ void be_load_wirelib(bvm *vm)
static const bnfuncinfo members[] = {
{ "_bus", NULL }, // bus number
{ "init", b_wire_init },
- { "_begintransmission", b_wire_begintransmission },
- { "_endtransmission", b_wire_endtransmission },
- { "_requestfrom", b_wire_requestfrom },
+ { "_begin_transmission", b_wire_begintransmission },
+ { "_end_transmission", b_wire_endtransmission },
+ { "_request_from", b_wire_requestfrom },
{ "_available", b_wire_available },
{ "_write", b_wire_write },
{ "_read", b_wire_read },
{ "scan", b_wire_scan },
{ "write", b_wire_validwrite },
{ "read", b_wire_validread },
+ { "read_bytes", b_wire_validread },
+ { "write_bytes", b_wire_validread },
+ { "detect", b_wire_detect },
{ NULL, NULL }
};
@@ -45,7 +51,7 @@ void be_load_wirelib(bvm *vm)
#else
/* @const_object_info_begin
module tasmota (scope: global, depend: 1) {
- getfreeheap, func(l_getFreeHeap)
+ get_free_heap, func(l_getFreeHeap)
}
@const_object_info_end */
#include "../generate/be_fixed_tasmota.h"
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 e3720b4d1..57fbe6208 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
@@ -162,11 +162,21 @@
* are not required.
* The default is to use the functions in the standard library.
**/
+#ifdef USE_BERRY_PSRAM
+ extern void *special_malloc(uint32_t size);
+ extern void *special_realloc(void *ptr, size_t size);
+ #define BE_EXPLICIT_MALLOC special_malloc
+ #define BE_EXPLICIT_REALLOC special_realloc
+#else
+ #define BE_EXPLICIT_MALLOC malloc
+ #define BE_EXPLICIT_REALLOC realloc
+#endif // USE_BERRY_PSRAM
+
#define BE_EXPLICIT_ABORT abort
#define BE_EXPLICIT_EXIT exit
-#define BE_EXPLICIT_MALLOC malloc
+// #define BE_EXPLICIT_MALLOC malloc
#define BE_EXPLICIT_FREE free
-#define BE_EXPLICIT_REALLOC realloc
+// #define BE_EXPLICIT_REALLOC realloc
/* Macro: be_assert
* Berry debug assertion. Only enabled when BE_DEBUG is active.
diff --git a/tasmota/berry/denky.be b/tasmota/berry/denky.be
index bcec51407..416e26078 100644
--- a/tasmota/berry/denky.be
+++ b/tasmota/berry/denky.be
@@ -10,11 +10,11 @@ runcolor = nil
def runcolor()
var pwr = energy.read().find('activepower',0)
print(pwr)
- var red = tasmota.scaleuint(int(pwr), 0, 2500, 0, 255)
+ var red = tasmota.scale_uint(int(pwr), 0, 2500, 0, 255)
var green = 255 - red
var channels = [red, green, 0]
- tasmota.setlight({"channels":channels, "bri":64, "power":true})
- tasmota.settimer(2000, runcolor)
+ tasmota.set_light({"channels":channels, "bri":64, "power":true})
+ tasmota.set_timer(2000, runcolor)
end
#- run animation -#
diff --git a/tasmota/berry/tasmota.be b/tasmota/berry/tasmota.be
deleted file mode 100644
index 213eb0be0..000000000
--- a/tasmota/berry/tasmota.be
+++ /dev/null
@@ -1,211 +0,0 @@
-import json import string
-tasmota = module("tasmota")
-def log(m) print(m) end
-def save() end
-
-#######
-import string
-import json
-import gc
-import tasmota
-#// import alias
-import tasmota as t
-
-def charsinstring(s,c)
- for i:0..size(s)-1
- for j:0..size(c)-1
- if s[i] == c[j] return i end
- end
- end
- return -1
-end
-
-###
-class Tasmota
- var _op, _operators, _rules
- def init()
- self._operators = "=<>!|"
- self._op = [
- ['==', /s1,s2-> str(s1) == str(s2)],
- ['!==',/s1,s2-> str(s1) != str(s2)],
- ['=', /f1,f2-> real(f1) == real(f2)],
- ['!=', /f1,f2-> real(f1) != real(f2)],
- ['>=', /f1,f2-> real(f1) >= real(f2)],
- ['<=', /f1,f2-> real(f1) <= real(f2)],
- ['>', /f1,f2-> real(f1) > real(f2)],
- ['<', /f1,f2-> real(f1) < real(f2)],
- ]
- self._rules = {}
- end
-end
-###
-
-tasmota._eqstr=/s1,s2-> str(s1) == str(s2)
-tasmota._neqstr=/s1,s2-> str(s1) != str(s2)
-tasmota._eq=/f1,f2-> real(f1) == real(f2)
-tasmota._neq=/f1,f2-> real(f1) != real(f2)
-tasmota._gt=/f1,f2-> real(f1) > real(f2)
-tasmota._lt=/f1,f2-> real(f1) < real(f2)
-tasmota._ge=/f1,f2-> real(f1) >= real(f2)
-tasmota._le=/f1,f2-> real(f1) <= real(f2)
-tasmota._op=[
- ['==',tasmota._eqstr],
- ['!==',tasmota._neqstr],
- ['=',tasmota._eq],
- ['!=',tasmota._neq],
- ['>=',tasmota._ge],
- ['<=',tasmota._le],
- ['>',tasmota._gt],
- ['<',tasmota._lt],
-]
-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)
- var pos = charsinstring(item, tasmota._operators)
- if pos>=0
- var op_split = string.split(item,pos)
- #print(op_split)
- 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
- 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
- end
- return [item, nil, nil]
-end
-
-
-def findkeyi(m,keyi)
- var keyu = string.toupper(keyi)
- if classof(m) == map
- for k:m.keys()
- if string.toupper(k)==keyu || keyi=='?'
- return k
- end
- end
- end
-end
-
-
-tasmota.try_rule = def (ev, rule, f)
- 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
- return false
- end
- e=e[found]
- end
- # check if condition is true
- if rl_list[1]
- # did we find a function
- if !rl_list[1](e,rl_list[2])
- # condition is not met
- return false
- end
- end
- f(e,ev)
- return true
-end
-tasmota_rules={}
-tasmota.rule = def(pat,f) tasmota_rules[pat] = f end
-
-tasmota.exec_rules = def (ev_json)
- var ev = json.load(ev_json)
- var ret = false
- if ev == nil
- log('BRY: ERROR, bad json: '+ev_json, 3)
- end
- for r:tasmota_rules.keys()
- ret = tasmota.try_rule(ev,r,tasmota_rules[r]) || ret
- end
- return ret
-end
-
-tasmota.delay = def(ms)
- tend = tasmota.millis(ms)
- while !tasmota.timereached(tend)
- tasmota.yield()
- end
-end
-
-def load(f)
- try
- if f[0] != '/' f = '/' + f end
- compile(f,'file')()
- except .. as e
- log(string.format("BRY: could not load file '%s' - %s",f,e))
- end
-end
-
-#- Test
-#################################################################
-
-def log(m) print(m) end
-def my_rule(e,ev) log("e1="+str(e)+" e2="+str(ev)) end
-
-tasmota.rule("ZBRECEIVED#?#LINKQUALITY", my_rule)
-tasmota.rule("ZBRECEIVED#0x1234", my_rule)
-
-tasmota.rule("ZBRECEIVED#?#LINKQUALITY<10", my_rule)
-
-tasmota.rule("Dimmer>50", my_rule)
-tasmota.rule("Dimmer=01", my_rule)
-
-
-tasmota.rule("Color==022600", my_rule)
-
-tasmota.exec_rules('{"Color":"022600"}')
-
-tasmota.exec_rules('{"ZbReceived":{"0x1234":{"Device":"0x1234","LinkQuality":50}}}')
-
-tasmota.exec_rules('{"Dimmer":10}')
-
-
-
-# tasmota.rule("DIMMER", my_rule)
-# tasmota.rule("DIMMER#DATA#DATA", my_rule)
-# tasmota.exec_rules('{"Dimmer":{"Data":50}}')
-
-
--#
-
-#-
-tasmota.find_op("aaa")
-tasmota.find_op("aaa>50")
--#
-
-#-
-# Example of backlog equivalent
-
-def backlog(cmd_list)
- delay_backlog = tasmota.getoption(34) # in milliseconds
- delay = 0
- for cmd:cmd_list
- tasmota.timer(delay, /-> tasmota.cmd(cmd))
- delay = delay + delay_backlog
- end
-end
-
-
-br def backlog(cmd_list) delay_backlog = tasmota.getoption(34) delay = 0 for cmd:cmd_list tasmota.timer(delay, /-> tasmota.cmd(cmd)) delay = delay + delay_backlog end end
-
-br backlog( [ "Power 0", "Status 4", "Power 1" ] )
-
--#
-
-#-
-
-tasmota.delay = def(ms) tend = tasmota.millis(ms) log(str(tasmota.millis())) while !tasmota.timereached(tend) end log(str(tasmota.millis())) end
-tasmota.delay = def(ms) a=0 tend = tasmota.millis(ms) log(str(tasmota.millis())) while !tasmota.timereached(tend) a=a+1 end log(str(tasmota.millis())) log(str(a)) end
-
--#
\ No newline at end of file
diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h
index 51c3999ac..f27f920d5 100644
--- a/tasmota/my_user_config.h
+++ b/tasmota/my_user_config.h
@@ -463,6 +463,11 @@
// #define SUPPORT_MQTT_EVENT // Support trigger event with MQTT subscriptions (+3k5 code)
+// -- Berry Scripting Language - ESP32 only ----------------------------
+// #define USE_BERRY // Enable Berry scripting language
+ #define USE_BERRY_PSRAM // Allocate Berry memory in PSRAM if PSRAM is connected - this might be slightly slower but leaves main memory intact
+
+
// -- Optional modules ----------------------------
#define ROTARY_V1 // Add support for Rotary Encoder as used in MI Desk Lamp (+0k8 code)
#define ROTARY_MAX_STEPS 10 // Rotary step boundary
diff --git a/tasmota/support_esp.ino b/tasmota/support_esp.ino
index 1f929f400..65dd3871c 100644
--- a/tasmota/support_esp.ino
+++ b/tasmota/support_esp.ino
@@ -74,6 +74,10 @@ void *special_malloc(uint32_t size) {
return malloc(size);
}
+void *special_realloc(void *ptr, size_t size) {
+ return realloc(ptr, size);
+}
+
#endif
/*********************************************************************************************\
@@ -461,6 +465,13 @@ void *special_malloc(uint32_t size) {
return malloc(size);
}
}
+void *special_realloc(void *ptr, size_t size) {
+ if (psramFound()) {
+ return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
+ } else {
+ return realloc(ptr, size);
+ }
+}
#endif // ESP32
diff --git a/tasmota/xdrv_52_0_berry_struct.ino b/tasmota/xdrv_52_0_berry_struct.ino
new file mode 100644
index 000000000..22f062ba5
--- /dev/null
+++ b/tasmota/xdrv_52_0_berry_struct.ino
@@ -0,0 +1,62 @@
+/*
+ xdrv_52_0_berry_struct.ino - Berry scripting language, native fucnctions
+
+ Copyright (C) 2021 Stephan Hadinger, Berry language by Guan Wenliang https://github.com/Skiars/berry
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+
+#ifdef USE_BERRY
+
+#include
+
+typedef LList_elt log_elt; // store the string after the header to avoid double allocation if we had used char*
+
+class BerryLog {
+public:
+ // typedef LList_elt log_elt; // store the string after the header to avoid double allocation if we had used char*
+ inline static size_t size(size_t chars) { return sizeof(log_elt) + chars; }
+ inline bool isEmpty(void) const { return log.isEmpty(); }
+ log_elt * addString(const char * s, const char * prefix = nullptr, const char * suffix = nullptr) {
+ if (suffix == nullptr) { suffix = ""; }
+ if (prefix == nullptr) { prefix = ""; }
+ if (s == nullptr) { s = ""; }
+ size_t s_len = strlen_P(s) + strlen_P(prefix) + strlen_P(suffix);
+ if (0 == s_len) { return nullptr; } // do nothing
+ log_elt * elt = (log_elt*) ::operator new(sizeof(log_elt) + s_len + 1); // use low-level new to specify the bytes size
+ snprintf_P((char*) &elt->val(), s_len+1, PSTR("%s%s%s"), prefix, s, suffix);
+ log.addToLast(elt);
+ return elt;
+ }
+ void reset(void) {
+ log.reset();
+ }
+ LList log;
+};
+
+class BerrySupport {
+public:
+ bvm *vm = nullptr; // berry vm
+ bool rules_busy = false; // are we already processing rules, avoid infinite loop
+ bool autoexec_done = false; // do we still need to load 'autoexec.be'
+ bool repl_active = false; // is REPL running (activates log recording)
+ // output log is stored as a LinkedList of buffers
+ // and active only when a REPL command is running
+ BerryLog log;
+};
+BerrySupport berry;
+
+
+#endif // USE_BERRY
diff --git a/tasmota/xdrv_52_3_berry_tasmota.ino b/tasmota/xdrv_52_3_berry_tasmota.ino
index f39f3cc1c..bd9e1a8cc 100644
--- a/tasmota/xdrv_52_3_berry_tasmota.ino
+++ b/tasmota/xdrv_52_3_berry_tasmota.ino
@@ -23,6 +23,8 @@
#include
#include
+const uint32_t BERRY_MAX_LOGS = 16; // max number of print output recorded when outside of REPL, used to avoid infinite grow of logs
+
/*********************************************************************************************\
* Native functions mapped to Berry functions
*
@@ -30,18 +32,18 @@
*
* import tasmota
*
- * tasmota.getfreeheap() -> int
+ * tasmota.get_free_heap() -> int
* tasmota.publish(topic:string, payload:string[, retain:bool]) -> nil
* tasmota.cmd(command:string) -> string
- * tasmota.getoption(index:int) -> int
+ * tasmota.get_option(index:int) -> int
* tasmota.millis([delay:int]) -> int
- * tasmota.timereached(timer:int) -> bool
+ * tasmota.time_reached(timer:int) -> bool
* tasmota.yield() -> nil
*
- * tasmota.getlight([index:int = 0]) -> map
- * tasmota.getpower([index:int = 0]) -> bool
- * tasmota.setpower(idx:int, power:bool) -> bool or nil
- * tasmota.setlight(idx:int, values:map) -> map
+ * tasmota.get_light([index:int = 0]) -> map
+ * tasmota.get_power([index:int = 0]) -> bool
+ * tasmota.set_power(idx:int, power:bool) -> bool or nil
+ * tasmota.set_light(idx:int, values:map) -> map
*
\*********************************************************************************************/
extern "C" {
@@ -97,7 +99,7 @@ extern "C" {
be_raise(vm, kTypeError, nullptr);
}
- // Berry: tasmota.getoption(index:int) -> int
+ // Berry: tasmota.get_option(index:int) -> int
//
int32_t l_getoption(struct bvm *vm);
int32_t l_getoption(struct bvm *vm) {
@@ -110,7 +112,7 @@ extern "C" {
be_raise(vm, kTypeError, nullptr);
}
- // Berry: tasmota.timereached(timer:int) -> bool
+ // Berry: tasmota.time_reached(timer:int) -> bool
//
int32_t l_timereached(struct bvm *vm);
int32_t l_timereached(struct bvm *vm) {
@@ -145,7 +147,7 @@ extern "C" {
be_return_nil(vm);
}
- // Berry: tasmota.scaleuint(int * 5) -> int
+ // Berry: tasmota.scale_uint(int * 5) -> int
//
int32_t l_scaleuint(struct bvm *vm);
int32_t l_scaleuint(struct bvm *vm) {
@@ -470,6 +472,24 @@ extern "C" {
be_raise(vm, kTypeError, nullptr);
}
+#ifdef USE_I2C
+ // I2C specific
+ // Berry: `i2c_enabled(index:int) -> bool` is I2C device enabled
+ int32_t l_i2cenabled(struct bvm *vm);
+ int32_t l_i2cenabled(struct bvm *vm) {
+ int32_t top = be_top(vm); // Get the number of arguments
+ if (top == 2 && be_isint(vm, 2)) {
+ int32_t index = be_toint(vm, 2);
+ bool enabled = I2cEnabled(index);
+ be_pushbool(vm, enabled);
+ be_return(vm); // Return
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
+#else // USE_I2C
+ int32_t l_i2cenabled(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
+#endif // USE_I2C
+
}
/*********************************************************************************************\
@@ -522,6 +542,13 @@ extern "C" {
// called as a replacement to Berry `print()`
void berry_log(const char * berry_buf);
void berry_log(const char * berry_buf) {
+ if (berry.repl_active) {
+ if (berry.log.log.length() >= BERRY_MAX_LOGS) {
+ berry.log.log.remove(berry.log.log.head());
+ }
+ }
+ // AddLog(LOG_LEVEL_INFO, PSTR("[Add to log] %s"), berry_buf);
+ berry.log.addString(berry_buf, nullptr, "\n");
AddLog(LOG_LEVEL_INFO, PSTR("%s"), berry_buf);
}
diff --git a/tasmota/xdrv_52_3_berry_wire.ino b/tasmota/xdrv_52_3_berry_wire.ino
index 5959b98b9..70e457ca9 100644
--- a/tasmota/xdrv_52_3_berry_wire.ino
+++ b/tasmota/xdrv_52_3_berry_wire.ino
@@ -48,7 +48,7 @@ int32_t getBus(bvm *vm) {
*
* import wire
*
- * wire.getfreeheap() -> int
+ * wire.get_free_heap() -> int
*
\*********************************************************************************************/
extern "C" {
@@ -135,6 +135,8 @@ extern "C" {
int32_t b_wire_write(struct bvm *vm);
int32_t b_wire_write(struct bvm *vm) {
int32_t top = be_top(vm); // Get the number of arguments
+ const void * buf;
+ size_t len;
TwoWire & myWire = getWire(vm);
if (top == 2 && (be_isint(vm, 2) || be_isstring(vm, 2))) {
if (be_isint(vm, 2)) {
@@ -143,6 +145,8 @@ extern "C" {
} else if (be_isstring(vm, 2)) {
const char * s = be_tostring(vm, 1);
myWire.write(s);
+ } else if ((buf = be_tobytes(vm, 2, &len)) != nullptr) {
+ myWire.write((uint8_t*) buf, len);
} else {
be_return_nil(vm);
}
@@ -211,7 +215,7 @@ extern "C" {
uint8_t addr = be_toint(vm, 2);
uint8_t reg = be_toint(vm, 3);
uint8_t size = be_toint(vm, 4);
- bool ok = I2cValidRead(addr, reg, size); // TODO
+ bool ok = I2cValidRead(addr, reg, size, bus); // TODO
if (ok) {
be_pushint(vm, i2c_buffer);
} else {
@@ -221,6 +225,62 @@ extern "C" {
}
be_raise(vm, kTypeError, nullptr);
}
+
+ // Berry: `read_bytes(address:int, reg:int, size:int) -> bytes() or nil`
+ int32_t b_wire_readbytes(struct bvm *vm);
+ int32_t b_wire_readbytes(struct bvm *vm) {
+ // int32_t top = be_top(vm); // Get the number of arguments
+ // int32_t bus = getBus(vm);
+ // if (top == 4 && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4)) {
+ // uint8_t addr = be_toint(vm, 2);
+ // uint8_t reg = be_toint(vm, 3);
+ // uint8_t size = be_toint(vm, 4);
+ // bool ok = I2cValidRead(addr, reg, size, bus); // TODO
+ // if (ok) {
+ // be_pushint(vm, i2c_buffer);
+ // } else {
+ // be_pushnil(vm);
+ // }
+ // be_return(vm); // Return
+ // }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ // Berry: `write_bytes(address:int, reg:int, val:bytes()) -> bool or nil`
+ int32_t b_wire_writebytes(struct bvm *vm);
+ int32_t b_wire_writebytes(struct bvm *vm) {
+ // int32_t top = be_top(vm); // Get the number of arguments
+ // int32_t bus = getBus(vm);
+ // if (top == 4 && be_isint(vm, 2) && be_isint(vm, 3) && be_isint(vm, 4)) {
+ // uint8_t addr = be_toint(vm, 2);
+ // uint8_t reg = be_toint(vm, 3);
+ // uint8_t size = be_toint(vm, 4);
+ // bool ok = I2cValidRead(addr, reg, size, bus); // TODO
+ // if (ok) {
+ // be_pushint(vm, i2c_buffer);
+ // } else {
+ // be_pushnil(vm);
+ // }
+ // be_return(vm); // Return
+ // }
+ be_raise(vm, kTypeError, nullptr);
+ }
+
+ // Berry: `find(address:int) -> bool` true if device responds
+ int32_t b_wire_detect(struct bvm *vm);
+ int32_t b_wire_detect(struct bvm *vm) {
+ int32_t top = be_top(vm); // Get the number of arguments
+ TwoWire & myWire = getWire(vm);
+ if (top == 2 && be_isint(vm, 2)) {
+ uint8_t addr = be_toint(vm, 2);
+ // check the presence of the device
+ myWire.beginTransmission((uint8_t)addr);
+ bool found = (0 == myWire.endTransmission());
+ be_pushbool(vm, found);
+ be_return(vm); // Return
+ }
+ be_raise(vm, kTypeError, nullptr);
+ }
#else // USE_I2C
//
int32_t b_wire_i2cmissing(struct bvm *vm);
@@ -239,6 +299,9 @@ extern "C" {
int32_t b_wire_scan(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
int32_t b_wire_validwrite(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
int32_t b_wire_validread(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
+ int32_t b_wire_readbytes(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
+ int32_t b_wire_writebytes(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
+ int32_t b_wire_detect(struct bvm *vm) __attribute__ ((weak, alias ("b_wire_i2cmissing")));
#endif // USE_I2C
}
diff --git a/tasmota/xdrv_52_7_berry_embedded.ino b/tasmota/xdrv_52_7_berry_embedded.ino
index 83a3895e7..65dfb616f 100644
--- a/tasmota/xdrv_52_7_berry_embedded.ino
+++ b/tasmota/xdrv_52_7_berry_embedded.ino
@@ -55,14 +55,11 @@ const char berry_prog[] =
"/f1,f2-> real(f1) < real(f2),"
"] "
"self._operators = \"=<>!|\" "
- "self._rules = {} "
- "self._timers = [] "
- "self._cmd = {} "
"end "
- // add `charsinstring(s:string,c:string) -> int``
+ // add `chars_in_string(s:string,c:string) -> int``
// looks for any char in c, and return the position of the first chat
// or -1 if not found
- "def charsinstring(s,c) "
+ "def chars_in_string(s,c) "
"for i:0..size(s)-1 "
"for j:0..size(c)-1 "
"if s[i] == c[j] return i end "
@@ -72,7 +69,7 @@ const char berry_prog[] =
"end "
// find a key in map, case insensitive, return actual key or nil if not found
- "def findkeyi(m,keyi) "
+ "def find_key_i(m,keyi) "
"import string "
"var keyu = string.toupper(keyi) "
"if classof(m) == map "
@@ -84,14 +81,11 @@ const char berry_prog[] =
"end "
"end "
- // Rules
- "def addrule(pat,f) self._rules[pat] = f end "
-
// # split the item when there is an operator, returns a list of (left,op,right)
// # ex: "Dimmer>50" -> ["Dimmer",tasmota_gt,"50"]
"def find_op(item) "
"import string "
- "var pos = self.charsinstring(item, self._operators) "
+ "var pos = self.chars_in_string(item, self._operators) "
"if pos>=0 "
"var op_split = string.split(item,pos) "
// #print(op_split)
@@ -109,6 +103,14 @@ const char berry_prog[] =
"end "
"return [item, nil, nil] "
"end "
+
+ // Rules
+ "def add_rule(pat,f) "
+ "if !self._rules "
+ "self._rules={} "
+ "end "
+ "self._rules[pat] = f "
+ "end "
// Rules trigger if match. return true if match, false if not
"def try_rule(ev, rule, f) "
@@ -117,7 +119,7 @@ const char berry_prog[] =
"var e=ev "
"var rl=string.split(rl_list[0],'#') "
"for it:rl "
- "found=self.findkeyi(e,it) "
+ "found=self.find_key_i(e,it) "
"if found == nil "
"return false "
"end "
@@ -138,55 +140,69 @@ const char berry_prog[] =
// Run rules, i.e. check each individual rule
// Returns true if at least one rule matched, false if none
"def exec_rules(ev_json) "
- "import json "
- "var ev = json.load(ev_json) "
- "var ret = false "
- "if ev == nil "
- "print('BRY: ERROR, bad json: '+ev_json, 3) "
- "else "
- "for r: self._rules.keys() "
- "ret = self.try_rule(ev,r,self._rules[r]) || ret "
+ "if self._rules "
+ "import json "
+ "var ev = json.load(ev_json) "
+ "var ret = false "
+ "if ev == nil "
+ "print('BRY: ERROR, bad json: '+ev_json, 3) "
+ "else "
+ "for r: self._rules.keys() "
+ "ret = self.try_rule(ev,r,self._rules[r]) || ret "
+ "end "
"end "
+ "return ret "
"end "
- "return ret "
+ "return false "
"end "
- "def settimer(delay,f) self._timers.push([self.millis(delay),f]) end "
+ "def set_timer(delay,f) "
+ "if !self._timers self._timers=[] end "
+ "self._timers.push([self.millis(delay),f]) "
+ "end "
+ // run every 50ms tick
"def run_deferred() "
- "var i=0 "
- "while i
+#define BERRY_CONSOLE_CMD_DELIMITER "\x01"
+
const char kBrCommands[] PROGMEM = D_PRFX_BR "|" // prefix
D_CMND_BR_RUN "|" D_CMND_BR_RESET
;
@@ -32,14 +34,6 @@ void (* const BerryCommand[])(void) PROGMEM = {
CmndBrRun, CmndBrReset,
};
-class BerrySupport {
-public:
- bvm *vm = nullptr; // berry vm
- bool rules_busy = false; // are we already processing rules, avoid infinite loop
- bool autoexec_done = false; // do we still need to load 'autoexec.be'
-};
-BerrySupport berry;
-
//
// Sanity Check for be_top()
//
@@ -64,79 +58,13 @@ bool callBerryRule(void) {
berry.rules_busy = true;
char * json_event = TasmotaGlobal.mqtt_data;
bool serviced = false;
-
- checkBeTop();
- be_getglobal(berry.vm, "_exec_rules");
- if (!be_isnil(berry.vm, -1)) {
-
- // {
- // String event_saved = TasmotaGlobal.mqtt_data;
- // // json_event = {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}}
- // // json_event = {"System":{"Boot":1}}
- // // json_event = {"SerialReceived":"on"} - invalid but will be expanded to {"SerialReceived":{"Data":"on"}}
- // char *p = strchr(json_event, ':');
- // if ((p != NULL) && !(strchr(++p, ':'))) { // Find second colon
- // event_saved.replace(F(":"), F(":{\"Data\":"));
- // event_saved += F("}");
- // // event_saved = {"SerialReceived":{"Data":"on"}}
- // }
- // be_pushstring(berry.vm, event_saved.c_str());
- // }
- be_pushstring(berry.vm, TasmotaGlobal.mqtt_data);
- int ret = be_pcall(berry.vm, 1);
- serviced = be_tobool(berry.vm, 1);
- AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_BERRY "Event (%s) serviced=%d"), TasmotaGlobal.mqtt_data, serviced);
- be_pop(berry.vm, 2); // remove function object
- } else {
- be_pop(berry.vm, 1); // remove nil object
- }
- checkBeTop();
+ serviced = callBerryEventDispatcher(PSTR("exec_rules"), nullptr, 0, TasmotaGlobal.mqtt_data);
berry.rules_busy = false;
-
- return serviced; // TODO event not handled
-}
-
-bool callBerryCommand(void) {
- bool serviced = false;
-
- checkBeTop();
- be_getglobal(berry.vm, "_exec_cmd");
- if (!be_isnil(berry.vm, -1)) {
- be_pushstring(berry.vm, XdrvMailbox.topic);
- be_pushint(berry.vm, XdrvMailbox.index);
- be_pushstring(berry.vm, XdrvMailbox.data);
- int ret = be_pcall(berry.vm, 3);
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: top=%d", be_top(berry.vm));
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(1)=%s", be_typename(berry.vm, 1));
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(2)=%s", be_typename(berry.vm, 2));
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(3)=%s", be_typename(berry.vm, 3));
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(4)=%s", be_typename(berry.vm, 4));
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: type(5)=%s", be_typename(berry.vm, 5));
- serviced = be_tobool(berry.vm, 1); // return value is in slot 1
- // AddLog(LOG_LEVEL_INFO, "callBerryCommand: serviced=%d", serviced);
- be_pop(berry.vm, 4); // remove function object
- } else {
- be_pop(berry.vm, 1); // remove nil object
- }
- checkBeTop();
-
return serviced; // TODO event not handled
}
size_t callBerryGC(void) {
- size_t ram_used = 0;
- checkBeTop();
- be_getglobal(berry.vm, "_gc");
- if (!be_isnil(berry.vm, -1)) {
- int ret = be_pcall(berry.vm, 0);
- ram_used = be_toint(berry.vm, 1);
- be_pop(berry.vm, 1); // remove function object
- } else {
- be_pop(berry.vm, 1); // remove nil object
- }
- checkBeTop();
-
- return ram_used;
+ return callBerryEventDispatcher(PSTR("gc"), nullptr, 0, nullptr);
}
// void callBerryMqttData(void) {
@@ -161,16 +89,79 @@ size_t callBerryGC(void) {
// checkBeTop();
// }
-// call a function (if exists) of type void -> void
-void callBerryFunctionVoid(const char * fname) {
- if (nullptr == berry.vm) { return; }
- checkBeTop();
- be_getglobal(berry.vm, fname);
+/*
+// Call a method of a global object, with n args
+// Before: stack must containt n args
+// After: stack contains return value or nil if something wrong (args removes)
+// returns true is successful, false if object or method not found
+bool callMethodObjectWithArgs(const char * objname, const char * method, size_t argc) {
+ if (nullptr == berry.vm) { return false; }
+ int32_t top = be_top(berry.vm);
+ // stacks contains n x arg
+ be_getglobal(berry.vm, objname);
+ // stacks contains n x arg + object
if (!be_isnil(berry.vm, -1)) {
- be_pcall(berry.vm, 0);
+ be_getmethod(berry.vm, -1, method);
+ // stacks contains n x arg + object + method
+ if (!be_isnil(berry.vm, -1)) {
+ // reshuffle the entire stack since we want: method + object + n x arg
+ be_pushvalue(berry.vm, -1); // add instance as first arg
+ // stacks contains n x arg + object + method + method
+ be_pushvalue(berry.vm, -3); // add instance as first arg
+ // stacks contains n x arg + object + method + method + object
+ // now move args 2 slots up to make room for method and object
+ for (uint32_t i = 1; i <= argc; i++) {
+ be_moveto(berry.vm, -4 - i, -2 - i);
+ }
+ // stacks contains free + free + n x arg + method + object
+ be_moveto(berry.vm, -2, -4 - argc);
+ be_moveto(berry.vm, -1, -3 - argc);
+ // stacks contains method + object + n x arg + method + object
+ be_pop(berry.vm, 2);
+ // stacks contains method + object + n x arg
+ be_pcall(berry.vm, argc + 1);
+ // stacks contains return_val + object + n x arg
+ be_pop(berry.vm, argc + 1);
+ // stacks contains return_val
+ return true;
+ }
+ be_pop(berry.vm, 1); // remove method
+ // stacks contains n x arg + object
}
- be_pop(berry.vm, 1); // remove function or nil object
+ // stacks contains n x arg + object
+ be_pop(berry.vm, argc + 1); // clear stack
+ be_pushnil(berry.vm); // put nil object
+ return false;
+}
+*/
+
+
+// call the event dispatcher from Tasmota object
+int32_t callBerryEventDispatcher(const char *type, const char *cmd, int32_t idx, const char *payload) {
+ int32_t ret = 0;
+
+ if (nullptr == berry.vm) { return ret; }
checkBeTop();
+ be_getglobal(berry.vm, PSTR("tasmota"));
+ if (!be_isnil(berry.vm, -1)) {
+ be_getmethod(berry.vm, -1, PSTR("event"));
+ if (!be_isnil(berry.vm, -1)) {
+ be_pushvalue(berry.vm, -2); // add instance as first arg
+ be_pushstring(berry.vm, type != nullptr ? type : "");
+ be_pushstring(berry.vm, cmd != nullptr ? cmd : "");
+ be_pushint(berry.vm, idx);
+ be_pushstring(berry.vm, payload != nullptr ? payload : "{}"); // empty json
+ be_pcall(berry.vm, 5); // 5 arguments
+ be_pop(berry.vm, 5);
+ if (be_isint(berry.vm, -1)) {
+ ret = be_toint(berry.vm, -1);
+ }
+ }
+ be_pop(berry.vm, 1); // remove method
+ }
+ be_pop(berry.vm, 1); // remove instance object
+ checkBeTop();
+ return ret;
}
/*********************************************************************************************\
@@ -349,6 +340,313 @@ void CmndBrReset(void) {
BrReset();
}
+/*********************************************************************************************\
+ * Berry console
+\*********************************************************************************************/
+#ifdef USE_WEBSERVER
+
+void BrREPLRun(char * cmd) {
+ if (berry.vm == nullptr) { return; }
+
+ size_t cmd_len = strlen(cmd);
+ size_t cmd2_len = cmd_len + 12;
+ char * cmd2 = (char*) malloc(cmd2_len);
+ do {
+ int32_t ret_code;
+
+ snprintf_P(cmd2, cmd2_len, PSTR("return (%s)"), cmd);
+ ret_code = be_loadbuffer(berry.vm, PSTR("input"), cmd2, strlen(cmd2));
+ // AddLog(LOG_LEVEL_INFO, PSTR(">>>> be_loadbuffer cmd2 '%s', ret=%i"), cmd2, ret_code);
+ if (be_getexcept(berry.vm, ret_code) == BE_SYNTAX_ERROR) {
+ be_pop(berry.vm, 2); // remove exception values
+ // if fails, try the direct command
+ ret_code = be_loadbuffer(berry.vm, PSTR("input"), cmd, cmd_len);
+ // AddLog(LOG_LEVEL_INFO, PSTR(">>>> be_loadbuffer cmd1 '%s', ret=%i"), cmd, ret_code);
+ }
+ if (0 == ret_code) { // code is ready to run
+ ret_code = be_pcall(berry.vm, 0); // execute code
+ // AddLog(LOG_LEVEL_INFO, PSTR(">>>> be_pcall ret=%i"), ret_code);
+ if (0 == ret_code) {
+ if (!be_isnil(berry.vm, 1)) {
+ const char * ret_val = be_tostring(berry.vm, 1);
+ berry.log.addString(ret_val, nullptr, "\n");
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> %s"), ret_val);
+ }
+ be_pop(berry.vm, 1);
+ }
+ }
+ if (BE_EXCEPTION == ret_code) {
+ be_dumpstack(berry.vm);
+ char exception_s[120];
+ ext_snprintf_P(exception_s, sizeof(exception_s), PSTR("%s: %s"), be_tostring(berry.vm, -2), be_tostring(berry.vm, -1));
+ berry.log.addString(exception_s, nullptr, "\n");
+ // AddLog_P(LOG_LEVEL_INFO, PSTR(">>> %s"), exception_s);
+ be_pop(berry.vm, 2);
+ }
+ } while(0);
+
+ if (cmd2 != nullptr) {
+ free(cmd2);
+ cmd2 = nullptr;
+ }
+ checkBeTop();
+}
+
+const char HTTP_SCRIPT_BERRY_CONSOLE[] PROGMEM =
+ "var sn=0,id=0,ft,ltm=%d;" // Scroll position, Get most of weblog initially
+ // Console command history
+ "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown
+
+ "function l(p){" // Console log and command service
+ "var c,cc,o='';"
+ "clearTimeout(lt);"
+ "clearTimeout(ft);"
+ "t=eb('t1');"
+ "if(p==1){"
+ "c=eb('c1');" // Console command id
+ "cc=c.value.trim();"
+ "if(cc){"
+ "o='&c1='+encodeURIComponent(cc);"
+ "hc.length>19&&hc.pop();"
+ "hc.unshift(cc);"
+ "cn=0;"
+ "}"
+ "c.value='';"
+ "t.scrollTop=99999;"
+ "sn=t.scrollTop;"
+ "}"
+ "if(t.scrollTop>=sn){" // User scrolled back so no updates
+ "if(x!=null){x.abort();}" // Abort if no response within 2 seconds (happens on restart 1)
+ "x=new XMLHttpRequest();"
+ "x.onreadystatechange=function(){"
+ "if(x.readyState==4&&x.status==200){"
+ "var d,t1;"
+ "d=x.responseText.split(/" BERRY_CONSOLE_CMD_DELIMITER "/);" // Field separator
+ "var d1=d.shift();"
+ "if(d1){"
+ "t1=document.createElement('div');"
+ "t1.classList.add('br1');"
+ "t1.innerText=d1;"
+ "t.appendChild(t1);"
+ "}"
+ "d1=d.shift();"
+ "if(d1){"
+ "t1=document.createElement('div');"
+ "t1.classList.add('br2');"
+ "t1.innerText=d1;"
+ "t.appendChild(t1);"
+ "}"
+ "t.scrollTop=99999;"
+ "sn=t.scrollTop;"
+ "clearTimeout(ft);"
+ "lt=setTimeout(l,ltm);" // webrefresh timer....
+ "}"
+ "};"
+ "x.open('GET','bs?c2='+id+o,true);" // Related to Webserver->hasArg("c2") and WebGetArg("c2", stmp, sizeof(stmp))
+ "x.send();"
+ "ft=setTimeout(l,20000);" // fail timeout, triggered 20s after asking for XHR
+ "}else{"
+ "lt=setTimeout(l,ltm);" // webrefresh timer....
+ "}"
+ "c1.focus();"
+ "return false;"
+ "}"
+ "wl(l);" // Load initial console text
+; // Add console command key eventlistener after name has been synced with id (= wl(jd))
+
+const char HTTP_SCRIPT_BERRY_CONSOLE2[] PROGMEM =
+ // // Console command history
+ // "var hc=[],cn=0;" // hc = History commands, cn = Number of history being shown
+ "var pc=0;" // pc = previous char
+ "function h(){"
+// "if(!(navigator.maxTouchPoints||'ontouchstart'in document.documentElement)){eb('c1').autocomplete='off';}" // No touch so stop browser autocomplete
+ "eb('c1').addEventListener('keydown',function(e){"
+ "var b=eb('c1'),c=e.keyCode;" // c1 = Console command id
+ "if((38==c||40==c)&&0==this.selectionStart&&0==this.selectionEnd){"
+ "b.autocomplete='off';"
+ "e.preventDefault();"
+ "38==c?(++cn>hc.length&&(cn=hc.length),b.value=hc[cn-1]||''):" // ArrowUp
+ "40==c?(0>--cn&&(cn=0),b.value=hc[cn-1]||''):" // ArrowDown
+ "0;"
+ "this.selectionStart=this.selectionEnd=0;"
+ "}" // ArrowUp or ArrowDown must be a keyboard so stop browser autocomplete
+ "if(c==13&&pc==13){"
+ "e.preventDefault();" // prevent 'enter' from being inserted
+ "l(1);"
+ "}"
+ "if(c==9){"
+ "e.preventDefault();"
+ "var start=this.selectionStart;"
+ "var end=this.selectionEnd;"
+ // set textarea value to: text before caret + tab + text after caret
+ "this.value=this.value.substring(0, start)+\" \"+this.value.substring(end);"
+ // put caret at right position again
+ "this.selectionStart=this.selectionEnd=start + 1;"
+ "}"
+ "pc=c;" // record previous key
+ // "13==c&&(hc.length>19&&hc.pop(),hc.unshift(b.value),cn=0)" // Enter, 19 = Max number -1 of commands in history
+ "});"
+ "}"
+ "wl(h);"; // Add console command key eventlistener after name has been synced with id (= wl(jd))
+
+const char HTTP_BERRY_STYLE_CMND[] PROGMEM =
+ ""
+ ;
+
+const char HTTP_BERRY_FORM_CMND[] PROGMEM =
+ "
"
+ ""
+ "
Welcome to the Berry Scripting console. "
+ "Check the
documentation."
+ "
"
+ "
"
+ // ""
+ // "
"
+ "";
+
+const char HTTP_BTN_BERRY_CONSOLE[] PROGMEM =
+ "
";
+
+
+void HandleBerryConsoleRefresh(void)
+{
+ String svalue = Webserver->arg(F("c1"));
+
+ svalue.trim();
+ if (svalue.length()) {
+ berry.log.reset(); // clear all previous logs
+ berry.repl_active = true; // start recording
+ AddLog_P(LOG_LEVEL_INFO, PSTR("BRY: received command %s"), svalue.c_str());
+ berry.log.addString(svalue.c_str(), nullptr, BERRY_CONSOLE_CMD_DELIMITER);
+
+ // Call berry
+ BrREPLRun((char*)svalue.c_str());
+ berry.repl_active = false; // don't record further
+ }
+
+ WSContentBegin(200, CT_PLAIN);
+
+ if (!berry.log.isEmpty()) {
+
+ WSContentFlush();
+
+ for (auto & l: berry.log.log) {
+ _WSContentSend((char*) l);
+ }
+
+ berry.log.reset();
+ }
+ WSContentEnd();
+}
+
+void HandleBerryConsole(void)
+{
+ if (!HttpCheckPriviledgedAccess()) { return; }
+ // int i=16;
+ // // AddLog(LOG_LEVEL_INFO, PSTR("Size = %d %d"), sizeof(LList_elt), sizeof(LList_elt)+12);
+ // LList_elt * elt = (LList_elt*) ::operator new(sizeof(LList_elt) + 12);
+
+ if (Webserver->hasArg(F("c2"))) { // Console refresh requested
+ HandleBerryConsoleRefresh();
+ return;
+ }
+
+ AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_HTTP "Berry " D_CONSOLE));
+
+ WSContentStart_P(PSTR("Berry " D_CONSOLE));
+ WSContentSend_P(HTTP_SCRIPT_BERRY_CONSOLE, Settings.web_refresh);
+ WSContentSend_P(HTTP_SCRIPT_BERRY_CONSOLE2);
+ WSContentSendStyle();
+ WSContentFlush();
+ _WSContentSend(HTTP_BERRY_STYLE_CMND);
+ _WSContentSend(HTTP_BERRY_FORM_CMND);
+ WSContentSpaceButton(BUTTON_MAIN);
+ WSContentStop();
+}
+
+// void HandleBerryConsoleRefresh(void)
+// {
+// String svalue = Webserver->arg(F("c1"));
+// if (svalue.length() && (svalue.length() < MQTT_MAX_PACKET_SIZE)) {
+// // TODO run command and store result
+// // AddLog_P(LOG_LEVEL_INFO, PSTR(D_LOG_COMMAND "%s"), svalue.c_str());
+// // ExecuteWebCommand((char*)svalue.c_str(), SRC_WEBCONSOLE);
+// }
+
+// char stmp[8];
+// WebGetArg(PSTR("c2"), stmp, sizeof(stmp));
+// uint32_t index = 0; // Initial start, dump all
+// if (strlen(stmp)) { index = atoi(stmp); }
+
+// WSContentBegin(200, CT_PLAIN);
+// WSContentSend_P(PSTR("%d}1%d}1"), TasmotaGlobal.log_buffer_pointer, Web.reset_web_log_flag);
+// if (!Web.reset_web_log_flag) {
+// index = 0;
+// Web.reset_web_log_flag = true;
+// }
+// bool cflg = (index);
+// char* line;
+// size_t len;
+// while (GetLog(Settings.weblog_level, &index, &line, &len)) {
+// if (len > sizeof(TasmotaGlobal.mqtt_data) -2) { len = sizeof(TasmotaGlobal.mqtt_data); }
+// char stemp[len +1];
+// strlcpy(stemp, line, len);
+// WSContentSend_P(PSTR("%s%s"), (cflg) ? PSTR("\n") : "", stemp);
+// cflg = true;
+// }
+// WSContentSend_P(PSTR("}1"));
+// WSContentEnd();
+// }
+#endif // USE_WEBSERVER
+
/*********************************************************************************************\
* Interface
\*********************************************************************************************/
@@ -363,51 +661,63 @@ bool Xdrv52(uint8_t function)
break;
case FUNC_LOOP:
if (!berry.autoexec_done) {
- BrAutoexec();
+ BrAutoexec(); // run autoexec.be at first tick, so we know all modules are initialized
berry.autoexec_done = true;
}
break;
+
+ // Berry wide commands and events
+ case FUNC_RULES_PROCESS:
+ result = callBerryRule();
+ break;
+ case FUNC_MQTT_DATA:
+ result = callBerryEventDispatcher(PSTR("mqtt_data"), XdrvMailbox.topic, 0, XdrvMailbox.data);
+ break;
case FUNC_EVERY_50_MSECOND:
- callBerryFunctionVoid(PSTR("_run_deferred"));
- break;
- case FUNC_EVERY_100_MSECOND:
- callBerryFunctionVoid(PSTR("every_100ms"));
- break;
- case FUNC_EVERY_SECOND:
- callBerryFunctionVoid(PSTR("every_second"));
+ callBerryEventDispatcher(PSTR("every_50ms"), nullptr, 0, nullptr);
break;
case FUNC_COMMAND:
result = DecodeCommand(kBrCommands, BerryCommand);
if (!result) {
- result = callBerryCommand();
+ result = callBerryEventDispatcher(PSTR("cmd"), XdrvMailbox.topic, XdrvMailbox.index, XdrvMailbox.data);
}
break;
+
+ // Module specific events
+ case FUNC_EVERY_100_MSECOND:
+ callBerryEventDispatcher(PSTR("every_100ms"), nullptr, 0, nullptr);
+ break;
+ case FUNC_EVERY_SECOND:
+ callBerryEventDispatcher(PSTR("every_second"), nullptr, 0, nullptr);
+ break;
// case FUNC_SET_POWER:
// break;
- case FUNC_RULES_PROCESS:
- result = callBerryRule();
- break;
#ifdef USE_WEBSERVER
case FUNC_WEB_ADD_BUTTON:
+ WSContentSend_P(HTTP_BTN_BERRY_CONSOLE);
+ callBerryEventDispatcher(PSTR("web_add_button"), nullptr, 0, nullptr);
break;
case FUNC_WEB_ADD_MAIN_BUTTON:
-
+ callBerryEventDispatcher(PSTR("web_add_main_button"), nullptr, 0, nullptr);
break;
case FUNC_WEB_ADD_HANDLER:
+ callBerryEventDispatcher(PSTR("web_add_handler"), nullptr, 0, nullptr);
+ WebServer_on(PSTR("/bs"), HandleBerryConsole);
break;
#endif // USE_WEBSERVER
case FUNC_SAVE_BEFORE_RESTART:
- break;
- case FUNC_MQTT_DATA:
- // callBerryMqttData();
+ callBerryEventDispatcher(PSTR("save_before_restart"), nullptr, 0, nullptr);
break;
case FUNC_WEB_SENSOR:
+ callBerryEventDispatcher(PSTR("web_sensor"), nullptr, 0, nullptr);
break;
case FUNC_JSON_APPEND:
+ callBerryEventDispatcher(PSTR("json_aooend"), nullptr, 0, nullptr);
break;
case FUNC_BUTTON_PRESSED:
+ callBerryEventDispatcher(PSTR("button_pressed"), nullptr, 0, nullptr);
break;