mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-15 06:46:33 +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)
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
|
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