mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-15 14:56:30 +00:00
Berry ArtNet implementation
This commit is contained in:
parent
e00529c1b6
commit
d40c24d6d3
@ -321,7 +321,7 @@ class Leds : Leds_ntv
|
|||||||
def set_bytes(row, buf, offset, len)
|
def set_bytes(row, buf, offset, len)
|
||||||
var h_bytes = self.h * self.pix_size
|
var h_bytes = self.h * self.pix_size
|
||||||
if (len > h_bytes) len = h_bytes end
|
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)
|
self.pix_buffer.setbytes(offset_in_matrix, buf, offset, len)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1333,8 +1333,8 @@ be_local_closure(Leds_matrix_set_bytes, /* name */
|
|||||||
0x781A0000, // 0004 JMPF R6 #0006
|
0x781A0000, // 0004 JMPF R6 #0006
|
||||||
0x5C100A00, // 0005 MOVE R4 R5
|
0x5C100A00, // 0005 MOVE R4 R5
|
||||||
0x88180102, // 0006 GETMBR R6 R0 K2
|
0x88180102, // 0006 GETMBR R6 R0 K2
|
||||||
0x081C0205, // 0007 MUL R7 R1 R5
|
0x00180C01, // 0007 ADD R6 R6 R1
|
||||||
0x00180C07, // 0008 ADD R6 R6 R7
|
0x08180C05, // 0008 MUL R6 R6 R5
|
||||||
0x881C0103, // 0009 GETMBR R7 R0 K3
|
0x881C0103, // 0009 GETMBR R7 R0 K3
|
||||||
0x8C1C0F04, // 000A GETMET R7 R7 K4
|
0x8C1C0F04, // 000A GETMET R7 R7 K4
|
||||||
0x5C240C00, // 000B MOVE R9 R6
|
0x5C240C00, // 000B MOVE R9 R6
|
||||||
|
BIN
tasmota/berry/ArtNet.tapp
Normal file
BIN
tasmota/berry/ArtNet.tapp
Normal file
Binary file not shown.
28
tasmota/berry/artnet/artnet_dyn.be
Normal file
28
tasmota/berry/artnet/artnet_dyn.be
Normal file
@ -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
|
288
tasmota/berry/artnet/artnet_ui.be
Normal file
288
tasmota/berry/artnet/artnet_ui.be
Normal file
@ -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("<p><form id=artnet_ui action='artnet_ui' style='display: block;' method='get'><button>Configure ArtNet animations</button></form></p>")
|
||||||
|
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("<p style='width:320px;'><b style='color:#f56'>Warning:</b> actions below can brick your device.</p>")
|
||||||
|
# webserver.content_send("<p><small> (This feature requires an internet connection)</small></p>")
|
||||||
|
|
||||||
|
webserver.content_send("<fieldset><style>.bdis{background:#888;}.bdis:hover{background:#888;}</style>")
|
||||||
|
webserver.content_send(string.format("<legend><b title='ArtNet'> ArtNet configuration</b></legend>"))
|
||||||
|
|
||||||
|
webserver.content_send("<p><form id=artnet_ui style='display: block;' action='/artnet_ui' method='post'>")
|
||||||
|
|
||||||
|
# WS2812 bus configuration
|
||||||
|
webserver.content_send(string.format("<p>WS2812 configuration: </p>"))
|
||||||
|
webserver.content_send(string.format(
|
||||||
|
"<table style='width:100%%'>"
|
||||||
|
"<tr><td style='width:150px'><b>GPIO</b></td><td style='width:150px'>"))
|
||||||
|
|
||||||
|
var ws2812_list = self.get_ws2812_gpios()
|
||||||
|
var ws2812_gpio
|
||||||
|
if size(ws2812_list) == 0
|
||||||
|
webserver.content_send("<b>**Not configured**</b>")
|
||||||
|
else
|
||||||
|
webserver.content_send("<select id='ws2812'>")
|
||||||
|
for gp:ws2812_list
|
||||||
|
webserver.content_send(string.format("<option value='%i'>%s</option>", gp, "WS2812 - " + str(gp+1)))
|
||||||
|
end
|
||||||
|
webserver.content_send("</select>")
|
||||||
|
end
|
||||||
|
webserver.content_send("</td><td></td></tr>")
|
||||||
|
|
||||||
|
webserver.content_send(string.format("<tr><td><b>Rows</b></td><td>"))
|
||||||
|
webserver.content_send(string.format("<input type='number' min='1' name='rows' value='%i'>", conf.rows))
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
webserver.content_send(string.format("<tr><td><b>Columns</b></td><td>"))
|
||||||
|
webserver.content_send(string.format("<input type='number' min='1' name='cols' value='%i'>", conf.cols))
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
webserver.content_send(string.format("<tr><td><b>Offset</b></td><td>"))
|
||||||
|
webserver.content_send(string.format("<input type='number' min='0' name='offs' value='%i'>", conf.offs))
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
|
||||||
|
webserver.content_send(string.format("<tr><td><b>Alternate rows</b></td><td>"))
|
||||||
|
webserver.content_send(string.format("<input type='checkbox' name='alt'%s></p>", conf.alt ? " checked" : ""))
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
|
||||||
|
webserver.content_send("<tr><td> </td></tr>")
|
||||||
|
webserver.content_send(string.format("<tr><td><b>DMX universe</b></td><td>"))
|
||||||
|
webserver.content_send(string.format("<input type='number' min='0' name='univ' value='%i'>", conf.univ))
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
# description
|
||||||
|
webserver.content_send("<tr><td colspan='3' style='word-wrap: break-word;'>")
|
||||||
|
webserver.content_send("This this the universe number for<br>the first row, and gets incremented<br>for each row.")
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
|
||||||
|
webserver.content_send("</table><hr>")
|
||||||
|
|
||||||
|
# IP configuration
|
||||||
|
webserver.content_send(string.format("<p>IP listener: </p>"))
|
||||||
|
webserver.content_send("<table style='width:100%%'>")
|
||||||
|
# "<tr><td style='width:120px'><b>IP mode</b></td><td style='width:180px'>"))
|
||||||
|
# webserver.content_send("<select id='ip'>")
|
||||||
|
# webserver.content_send(string.format("<option value='uni'%s>unicast</option>", conf.addr == 'uni' ? " selected" : ""))
|
||||||
|
# webserver.content_send(string.format("<option value='multi'%s>multicast</option>", conf.addr == 'multi' ? " selected" : ""))
|
||||||
|
# webserver.content_send("</select>")
|
||||||
|
# webserver.content_send("</td><td></td></tr>")
|
||||||
|
|
||||||
|
webserver.content_send("<tr><td style='width:120px'><b>Port</b></td><td style='width:180px'>")
|
||||||
|
# webserver.content_send(string.format("<tr><td><b>Port</b></td><td>"))
|
||||||
|
webserver.content_send(string.format("<input type='number' min='1' name='port' value='%i'>", conf.port))
|
||||||
|
webserver.content_send("</td></tr>")
|
||||||
|
webserver.content_send("</table><hr>")
|
||||||
|
|
||||||
|
# auto-run
|
||||||
|
webserver.content_send(string.format("<p><b>Auto-run at boot:</b> <input type='checkbox' name='auto'%s></p>", conf.auto ? " checked" : ""))
|
||||||
|
|
||||||
|
# button
|
||||||
|
webserver.content_send("<button name='artnetapply' class='button bgrn'>Apply and Run</button>")
|
||||||
|
webserver.content_send("</form></p>")
|
||||||
|
|
||||||
|
webserver.content_send("<p></p></fieldset><p></p>")
|
||||||
|
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("<p style='width:340px;'><b>Exception:</b><br>'%s'<br>%s</p>", 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)
|
||||||
|
|
||||||
|
-#
|
15
tasmota/berry/artnet/autoexec.be
Normal file
15
tasmota/berry/artnet/autoexec.be
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user