diff --git a/lib/libesp32/berry_tasmota/src/embedded/leds.be b/lib/libesp32/berry_tasmota/src/embedded/leds.be index 7a1461d90..ecde9b706 100644 --- a/lib/libesp32/berry_tasmota/src/embedded/leds.be +++ b/lib/libesp32/berry_tasmota/src/embedded/leds.be @@ -321,7 +321,7 @@ class Leds : Leds_ntv def set_bytes(row, buf, offset, len) var h_bytes = self.h * self.pix_size if (len > h_bytes) len = h_bytes end - var offset_in_matrix = self.offset + row * h_bytes + var offset_in_matrix = (self.offset + row) * h_bytes self.pix_buffer.setbytes(offset_in_matrix, buf, offset, len) end diff --git a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h index c6caa2eab..d7ca9b0bd 100644 --- a/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h +++ b/lib/libesp32/berry_tasmota/src/solidify/solidified_leds.h @@ -1333,8 +1333,8 @@ be_local_closure(Leds_matrix_set_bytes, /* name */ 0x781A0000, // 0004 JMPF R6 #0006 0x5C100A00, // 0005 MOVE R4 R5 0x88180102, // 0006 GETMBR R6 R0 K2 - 0x081C0205, // 0007 MUL R7 R1 R5 - 0x00180C07, // 0008 ADD R6 R6 R7 + 0x00180C01, // 0007 ADD R6 R6 R1 + 0x08180C05, // 0008 MUL R6 R6 R5 0x881C0103, // 0009 GETMBR R7 R0 K3 0x8C1C0F04, // 000A GETMET R7 R7 K4 0x5C240C00, // 000B MOVE R9 R6 diff --git a/tasmota/berry/ArtNet.tapp b/tasmota/berry/ArtNet.tapp new file mode 100644 index 000000000..d03a2bba3 Binary files /dev/null and b/tasmota/berry/ArtNet.tapp differ diff --git a/tasmota/berry/artnet/artnet_dyn.be b/tasmota/berry/artnet/artnet_dyn.be new file mode 100644 index 000000000..bfac24463 --- /dev/null +++ b/tasmota/berry/artnet/artnet_dyn.be @@ -0,0 +1,28 @@ +################################################################################# +# dyn class +# +# Allows to use a map with members +# see https://github.com/berry-lang/berry/wiki/Chapter-8 +################################################################################# +class dyn + var _attr + def init() + self._attr = {} + end + def setmember(name, value) + self._attr[name] = value + end + def member(name) + if self._attr.contains(name) + return self._attr[name] + else + import undefined + return undefined + end + end + def tostring() + return self._attr.tostring() + end +end + +return dyn diff --git a/tasmota/berry/artnet/artnet_ui.be b/tasmota/berry/artnet/artnet_ui.be new file mode 100644 index 000000000..05869a6c4 --- /dev/null +++ b/tasmota/berry/artnet/artnet_ui.be @@ -0,0 +1,288 @@ +####################################################################### +# DMX ArtNet UI for ESP32(C3/S2/S3) +# +####################################################################### + +var artnet_ui = module('artnet_ui') + +################################################################################# +# ArtNet_UI +# +# WebUI +################################################################################# +class ArtNet_UI + + def init() + import persist + + if persist.find("artnet_autorun") == true + # autorun + end + end + + # #################################################################################################### + # Init web handlers + # #################################################################################################### + # Displays a "DMX ArtNet" button on the configuration page + def web_add_config_button() + import webserver + webserver.content_send("

") + end + + # #################################################################################################### + # Get WS2812 gpios + # + # Returns an array of valid WS2812 gpios as defined in the template, or empty array + # #################################################################################################### + def get_ws2812_gpios() + import gpio + var ret = [] + for p:0..31 + if gpio.pin_used(gpio.WS2812, p) + ret.push(p) + end + end + return ret + end + + static def read_persist() + import persist + var conf = dyn() + + conf.gpio = persist.find("artnet_gpio", 0) # gpio number from template + conf.rows = persist.find("artnet_rows", 5) # number of rows (min: 1) + conf.cols = persist.find("artnet_cols", 5) # number of columns (min: 1) + conf.offs = persist.find("artnet_offs", 0) # offset in the led strip where the matrix starts (min: 0) + conf.alt = persist.find("artnet_alt", false) # are the rows in alternate directions + + conf.univ = persist.find("artnet_univ", 0) # start universe + + # conf.addr = persist.find("artnet_addr", "uni") # listening mode, either 'uni' or 'multi' for multicast + conf.port = persist.find("artnet_port", 6454) # UDP port number + + conf.auto = persist.find("artnet_auto", true) # autorun at startup + return conf + end + + def save_persist(conf) + import persist + persist.artnet_gpio = conf.gpio + persist.artnet_rows = conf.rows + persist.artnet_cols = conf.cols + persist.artnet_offs = conf.offs + persist.artnet_alt = conf.alt + + persist.artnet_univ = conf.univ + + # persist.artnet_addr = conf.addr + persist.artnet_port = conf.port + + persist.artnet_auto = conf.auto + + persist.save() + end + + ####################################################################### + # Display the complete page on `/artnet_ui` + ####################################################################### + def page_artnet_ui() + import webserver + import string + if !webserver.check_privileged_access() return nil end + + # read configuration + var conf = self.read_persist() + + webserver.content_start("ArtNet") #- title of the web page -# + webserver.content_send_style() #- send standard Tasmota styles -# + + # webserver.content_send("

Warning: actions below can brick your device.

") + # webserver.content_send("

 (This feature requires an internet connection)

") + + webserver.content_send("
") + webserver.content_send(string.format(" ArtNet configuration")) + + webserver.content_send("

") + + # WS2812 bus configuration + webserver.content_send(string.format("

WS2812 configuration:

")) + webserver.content_send(string.format( + "" + "") + + webserver.content_send(string.format("") + webserver.content_send(string.format("") + webserver.content_send(string.format("") + + webserver.content_send(string.format("") + + webserver.content_send("") + webserver.content_send(string.format("") + # description + webserver.content_send("") + + webserver.content_send("
GPIO")) + + var ws2812_list = self.get_ws2812_gpios() + var ws2812_gpio + if size(ws2812_list) == 0 + webserver.content_send("**Not configured**") + else + webserver.content_send("") + end + webserver.content_send("
Rows")) + webserver.content_send(string.format("", conf.rows)) + webserver.content_send("
Columns")) + webserver.content_send(string.format("", conf.cols)) + webserver.content_send("
Offset")) + webserver.content_send(string.format("", conf.offs)) + webserver.content_send("
Alternate rows")) + webserver.content_send(string.format("

", conf.alt ? " checked" : "")) + webserver.content_send("
 
DMX universe")) + webserver.content_send(string.format("", conf.univ)) + webserver.content_send("
") + webserver.content_send("This this the universe number for
the first row, and gets incremented
for each row.") + webserver.content_send("

") + + # IP configuration + webserver.content_send(string.format("

IP listener:

")) + webserver.content_send("") + # "") + + webserver.content_send("") + webserver.content_send("
IP mode")) + # webserver.content_send("") + # webserver.content_send("
Port") + # webserver.content_send(string.format("
Port")) + webserver.content_send(string.format("", conf.port)) + webserver.content_send("

") + + # auto-run + webserver.content_send(string.format("

Auto-run at boot:

", conf.auto ? " checked" : "")) + + # button + webserver.content_send("") + webserver.content_send("

") + + webserver.content_send("

") + webserver.content_button(webserver.BUTTON_CONFIGURATION) + webserver.content_stop() + end + + ####################################################################### + # Web Controller, called by POST to `/artnet_ui` + ####################################################################### + def page_artnet_ctl() + import webserver + if !webserver.check_privileged_access() return nil end + + import string + import persist + import introspect + + try + if webserver.has_arg("artnetapply") + + # read argumments, sanity check and put in conf object + var conf = dyn() + + # read gpio + var ws2812_list = self.get_ws2812_gpios() + var gp_ws2812 = int(webserver.arg("ws2812")) + if ws2812_list.find(gp_ws2812) != nil + conf.gpio = gp_ws2812 + else + conf.gpio = -1 + end + + # read rows and cols + var rows = int(webserver.arg("rows")) + if rows < 1 || rows > 999 rows = 1 end + conf.rows = rows + var cols = int(webserver.arg("cols")) + if cols < 1 || cols > 999 cols = 1 end + conf.cols = cols + # alternate + conf.alt = webserver.arg("alt") == 'on' + + # offset + var offs = int(webserver.arg("offs")) + if offs < 0 || offs > 999 offs = 0 end + conf.offs = offs + + # universe + var univ = int(webserver.arg("univ")) + if univ < 0 || univ > 999 univ = 0 end + conf.univ = univ + + # universe + var port = int(webserver.arg("port")) + if port < 1 || port > 65535 port = 6454 end + conf.port = port + + # autorun + conf.auto = webserver.arg("auto") == 'on' + + self.save_persist(conf) + + artnet.stop_global() + artnet.run_from_conf() + + # tasmota.log("BRY: conf=" + str(conf), 2); + webserver.redirect("/cn?") + else + raise "value_error", "Unknown command" + end + except .. as e, m + print(string.format("BRY: Exception> '%s' - %s", e, m)) + #- display error page -# + webserver.content_start("Parameter error") #- title of the web page -# + webserver.content_send_style() #- send standard Tasmota styles -# + + webserver.content_send(string.format("

Exception:
'%s'
%s

", e, m)) + + webserver.content_button(webserver.BUTTON_CONFIGURATION) #- button back to management page -# + webserver.content_stop() #- end of web page -# + end + end + + #- ---------------------------------------------------------------------- -# + # respond to web_add_handler() event to register web listeners + #- ---------------------------------------------------------------------- -# + #- this is called at Tasmota start-up, as soon as Wifi/Eth is up and web server running -# + def web_add_handler() + import webserver + #- we need to register a closure, not just a function, that captures the current instance -# + webserver.on("/artnet_ui", / -> self.page_artnet_ui(), webserver.HTTP_GET) + webserver.on("/artnet_ui", / -> self.page_artnet_ctl(), webserver.HTTP_POST) + end +end +artnet_ui.ArtNet_UI = ArtNet_UI + + +#- create and register driver in Tasmota -# +if tasmota + var artnet_ui_instance = artnet_ui.ArtNet_UI() + tasmota.add_driver(artnet_ui_instance) + ## can be removed if put in 'autoexec.bat' + artnet_ui_instance.web_add_handler() +end + +return artnet_ui + +#- Example + +import partition + +# read +p = partition.Partition() +print(p) + +-# diff --git a/tasmota/berry/artnet/autoexec.be b/tasmota/berry/artnet/autoexec.be new file mode 100644 index 000000000..7f5e9d45c --- /dev/null +++ b/tasmota/berry/artnet/autoexec.be @@ -0,0 +1,15 @@ +# rm ArtNet.tapp; zip ArtNet.tapp -j -0 artnet/* +# +# check if `dyn` class is embedded, or load it +if !global.contains("dyn") + import artnet_dyn + global.dyn = artnet_dyn +end + +import artnet +import artnet_ui + +import persist +if persist.find("artnet_auto") + tasmota.add_rule("Wifi#Connected", def () tasmota.remove_rule("Wifi#Connected", "artnet_run") artnet.run_from_conf() end, "artnet_run") +end