DMX Support for WLED (#704)
* initial dmx setup * adds support for multiple fixtures, addr gaps, start addresses and all that good DMX stuff * removes init function. do not need. * adds some comments, removes others. words. * added menu entry and dummy HTML * added server request handler * cloned options page UI for DMX * only add code when DMX is enabled * added infobutton to HTML * DMX settings form * procedurally generated HTML form. OBACHT: Values still not coming from the EEPROM. * upped eeprom version to 15 * changed index for set to 255 to 6 because web interface wants it that way * gets values for XML from actual settings * changes the default values for dmx to blanks * reads and writes DMX settings from EEPROM (2550 - 2569) * fixes addressing bug in DMX EEPROM read * saves settings from WebUI to memory * disables DMX by default * changed a comment in the ENABLE_DMX line * makes the display of the DMX entry in settings dependant on WLED_DMX_ENABLE * adds the server listener for the DMX map * fixes a bug when selecting 255 for a channel at the dmx settings page * now actually reads the DMX settings back to the HTML UI. * cleans up a little * adds a warning message to the HTML UI when setting up defunct DMX settings * changed DMX EEPROM addressing to close a gap * basic DMX map * fixes a few styling flaws and bugs in the DMX map * changes config variables to uint16_t Co-authored-by: Aircoookie <cschwinne@gmail.com>
This commit is contained in:
parent
513bc2c0ab
commit
8be72f6f23
@ -15,6 +15,36 @@ const char PAGE_msg[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<style>.bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}</style></head>
|
||||
<body><h2>%MSG%</body></html>)=====";
|
||||
|
||||
//DMX channel map
|
||||
const char PAGE_dmxmap[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta content='width=device-width' name='viewport'>
|
||||
<title>DMX Map</title>
|
||||
<script>function B(){window.history.back()};function RS(){window.location = "/settings";}function RP(){top.location.href="/";}function FM() {%DMXVARS%
|
||||
var dmxlabels = ["SET 0","RED","GREEN","BLUE","WHITE","SHUTTER","SET 255", "DISABLED"];
|
||||
var dmxchans = [];
|
||||
for (i=0;i<512;i++) {
|
||||
dmxchans.push(7); // set all to DISABLED
|
||||
}
|
||||
for (i=0;i<LC;i++) {
|
||||
FS = CS + (CG * i);
|
||||
for (j=0;j<CN;j++) {
|
||||
DA=FS+j;
|
||||
dmxchans[DA-1] = CH[j];
|
||||
}
|
||||
}
|
||||
DMXMap = "";
|
||||
for (i=0;i<512;i++) {
|
||||
isstart = "";
|
||||
if ((i+1) % 10 == 0) {
|
||||
isstart="S"
|
||||
}
|
||||
DMXMap += "<div class=\"anytype " + isstart + " type" + dmxchans[i] + "\">" + String(i+1) + "<br />" + dmxlabels[dmxchans[i]] + "</div>";
|
||||
}
|
||||
document.getElementById("map").innerHTML = DMXMap;
|
||||
}</script>
|
||||
<style>.anytype{border: 1px solid white; margin: 1px; float: left; width: 100px; height: 100px;}.S { margin: 0px; border: 2px solid white;} .type7{color: #888; border: 1px dotted grey;}.type6{color: #FFF;}.type4{color: #FFF; font-weight: bold; }.type3{color: #00F; font-weight: bold; }.type2{color: #0F0; font-weight: bold; }.type1{color: #F00; font-weight: bold; } .bt{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}</style></head>
|
||||
<body onload="FM();"><div id="map">...</div></body></html>)=====";
|
||||
|
||||
|
||||
//firmware update page
|
||||
const char PAGE_update[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
|
@ -1,23 +1,23 @@
|
||||
/*
|
||||
* Settings html
|
||||
*/
|
||||
Settings html
|
||||
*/
|
||||
|
||||
//common CSS of settings pages
|
||||
const char PAGE_settingsCss[] PROGMEM = R"=====(<style>body{font-family:Verdana,sans-serif;text-align:center;background:#222;color:#fff;line-height:200%%;margin:0}hr{border-color:#666}button{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.3ch solid #333;display:inline-block;font-size:20px;margin:8px;margin-top:12px}.helpB{text-align:left;position:absolute;width:60px}input{background:#333;color:#fff;font-family:Verdana,sans-serif;border:.5ch solid #333}input[type=number]{width:4em}select{background:#333;color:#fff;font-family:Verdana,sans-serif;border:0.5ch solid #333}td{padding:2px;}</style>)=====";
|
||||
|
||||
|
||||
//settings menu
|
||||
const char PAGE_settings[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><title>WLED Settings</title><style>body{text-align:center;background:#222;height:100%;margin:0}html{--h:11.55vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:.3ch solid #333;display:inline-block;font-size:8vmin;height:var(--h);width:95%;margin-top:2.4vh}</style>
|
||||
<html><head><title>WLED Settings</title><style>body{text-align:center;background:#222;height:100%%;margin:0}html{--h:11.55vh}button{background:#333;color:#fff;font-family:Verdana,Helvetica,sans-serif;border:.3ch solid #333;display:inline-block;font-size:8vmin;height:var(--h);width:95%%;margin-top:2.4vh}</style>
|
||||
<script>function BB(){if(window.frameElement){document.getElementById("b").style.display="none";document.documentElement.style.setProperty("--h","13.86vh")}};</script></head>
|
||||
<body onload=BB()>
|
||||
<form action=/><button type=submit id=b>Back</button></form>
|
||||
<form action=/settings/wifi><button type=submit>WiFi Setup</button></form>
|
||||
<form action=/settings/leds><button type=submit>LED Preferences</button></form>
|
||||
<form action=/settings/ui><button type=submit>User Interface</button></form>
|
||||
<form action=/settings/ui><button type=submit>User Interface</button></form>%DMXMENU%
|
||||
<form action=/settings/sync><button type=submit>Sync Interfaces</button></form>
|
||||
<form action=/settings/time><button type=submit>Time & Macros</button></form>
|
||||
<form action=/settings/sec><button type=submit>Security & Updates</button></form>
|
||||
|
||||
</body></html>)=====";
|
||||
|
||||
|
||||
@ -172,6 +172,64 @@ Skip first LED: <input type=checkbox name=SL><hr>
|
||||
</form></body></html>)=====";
|
||||
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
//DMX Output settings
|
||||
const char PAGE_settings_dmx[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>DMX Settings</title><script>
|
||||
function GCH(num) {
|
||||
d=document;
|
||||
|
||||
d.getElementById('dmxchannels').innerHTML += "";
|
||||
for (i=0;i<num;i++) {
|
||||
d.getElementById('dmxchannels').innerHTML += "<span id=CH" + (i+1) + "s >Channel " + (i+1) + ": <select name=CH" + (i+1) + " id=\"CH" + (i+1) + "\"><option value=0>Set to 0</option><option value=1>Red</option><option value=2>Green</option><option value=3>Blue</option><option value=4>White</option><option value=5>Shutter (Brightness)</option><option value=6>Set to 255</option></select></span><br />\n";
|
||||
}
|
||||
}
|
||||
function mMap(){
|
||||
d=document;
|
||||
numCh=document.Sf.CN.value;
|
||||
numGap=document.Sf.CG.value;
|
||||
if (parseInt(numCh)>parseInt(numGap)) {
|
||||
d.getElementById("gapwarning").style.display="block";
|
||||
} else {
|
||||
d.getElementById("gapwarning").style.display="none";
|
||||
}
|
||||
for (i=0;i<15;i++) {
|
||||
if (i>=numCh) {
|
||||
d.getElementById("CH"+(i+1) + "s").style.opacity = "0.5";
|
||||
d.getElementById("CH"+(i+1)).disabled = true;
|
||||
|
||||
} else {
|
||||
d.getElementById("CH"+(i+1) + "s").style.opacity = "1";
|
||||
d.getElementById("CH"+(i+1)).disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
function S(){GCH(15);GetV();mMap();}function H(){window.open("https://github.com/Aircoookie/WLED/wiki/DMX");}function B(){window.history.back();}function GetV(){var d=document;
|
||||
%CSS%%SCSS%</head>
|
||||
<body onload="S()">
|
||||
<form id="form_s" name="Sf" method="post">
|
||||
<div class="helpB"><button type="button" onclick="H()">?</button></div>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save</button><hr>
|
||||
<h2>Imma firin ma lazer (if it has DMX support)</h2><!-- TODO: Change to something less-meme-related //-->
|
||||
|
||||
<i>Number of fixtures is taken from LED config page</i><br>
|
||||
|
||||
channels per fixture (15 max): <input type="number" min="1" max="15" name="CN" maxlength="2" onchange="mMap();"><br />
|
||||
start channel: <input type="number" min="1" max="512" name="CS" maxlength="2"><br />
|
||||
spacing between start channels: <input type="number" min="1" max="512" name="CG" maxlength="2" onchange="mMap();"> [ <a href="javascript:alert('if set to 10, first fixture will start at 10,\nsecond will start at 20 etc.\nRegardless of the channel count.\nMakes memorizing channel numbers easier.');">info</a> ]<br>
|
||||
<div id="gapwarning" style="color: orange; display: none;">WARNING: Channel gap is lower than channels per fixture.<br />This will cause overlap.</div>
|
||||
<button type="button" onclick="location.href='/dmxmap';">DMX Map</button>
|
||||
<h3>channel functions</h3>
|
||||
<div id="dmxchannels"></div>
|
||||
<hr><button type="button" onclick="B()">Back</button><button type="submit">Save</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>)=====";
|
||||
|
||||
#else
|
||||
const char PAGE_settings_dmx[] PROGMEM = R"=====()=====";
|
||||
#endif
|
||||
|
||||
//User Interface settings
|
||||
const char PAGE_settings_ui[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>UI Settings</title><script>
|
||||
@ -190,6 +248,7 @@ Sync button toggles both send and receive: <input type="checkbox" name="ST"><br>
|
||||
</html>)=====";
|
||||
|
||||
|
||||
|
||||
//sync settings
|
||||
const char PAGE_settings_sync[] PROGMEM = R"=====(<!DOCTYPE html>
|
||||
<html><head><meta name="viewport" content="width=500"><meta charset="utf-8"><title>Sync Settings</title>
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Main sketch, global variable declarations
|
||||
*/
|
||||
Main sketch, global variable declarations
|
||||
*/
|
||||
/*
|
||||
* @title WLED project sketch
|
||||
* @version 0.9.1
|
||||
@ -24,6 +24,7 @@
|
||||
//#define WLED_DISABLE_INFRARED //there is no pin left for this on ESP8266-01, saves 25kb (!)
|
||||
#define WLED_ENABLE_MQTT //saves 12kb
|
||||
#define WLED_ENABLE_ADALIGHT //saves 500b only
|
||||
//#define WLED_ENABLE_DMX //uses 3.5kb
|
||||
|
||||
#define WLED_DISABLE_FILESYSTEM //SPIFFS is not used by any WLED feature yet
|
||||
//#define WLED_ENABLE_FS_SERVING //Enable sending html file from SPIFFS before serving progmem version
|
||||
@ -34,6 +35,10 @@
|
||||
|
||||
//library inclusions
|
||||
#include <Arduino.h>
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
#include <ESPDMX.h>
|
||||
DMXESPSerial dmx;
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
@ -42,11 +47,11 @@
|
||||
#include <user_interface.h>
|
||||
}
|
||||
#else
|
||||
#include <WiFi.h>
|
||||
#include "esp_wifi.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include "SPIFFS.h"
|
||||
#include <WiFi.h>
|
||||
#include "esp_wifi.h"
|
||||
#include <ESPmDNS.h>
|
||||
#include <AsyncTCP.h>
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
|
||||
#include <ESPAsyncWebServer.h>
|
||||
@ -54,20 +59,20 @@
|
||||
#include <WiFiUdp.h>
|
||||
#include <DNSServer.h>
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
#include <ArduinoOTA.h>
|
||||
#include <ArduinoOTA.h>
|
||||
#endif
|
||||
#include <SPIFFSEditor.h>
|
||||
#include "src/dependencies/time/TimeLib.h"
|
||||
#include "src/dependencies/timezone/Timezone.h"
|
||||
#ifndef WLED_DISABLE_ALEXA
|
||||
#define ESPALEXA_ASYNC
|
||||
#define ESPALEXA_NO_SUBPAGE
|
||||
#define ESPALEXA_MAXDEVICES 1
|
||||
//#define ESPALEXA_DEBUG
|
||||
#include "src/dependencies/espalexa/Espalexa.h"
|
||||
#define ESPALEXA_ASYNC
|
||||
#define ESPALEXA_NO_SUBPAGE
|
||||
#define ESPALEXA_MAXDEVICES 1
|
||||
//#define ESPALEXA_DEBUG
|
||||
#include "src/dependencies/espalexa/Espalexa.h"
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_BLYNK
|
||||
#include "src/dependencies/blynk/BlynkSimpleEsp.h"
|
||||
#include "src/dependencies/blynk/BlynkSimpleEsp.h"
|
||||
#endif
|
||||
#include "src/dependencies/e131/ESPAsyncE131.h"
|
||||
#include "src/dependencies/async-mqtt-client/AsyncMqttClient.h"
|
||||
@ -82,16 +87,16 @@
|
||||
|
||||
|
||||
#if IR_PIN < 0
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#define WLED_DISABLE_INFRARED
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#define WLED_DISABLE_INFRARED
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRrecv.h>
|
||||
#include <IRutils.h>
|
||||
#endif
|
||||
#ifndef WLED_DISABLE_INFRARED
|
||||
#include <IRremoteESP8266.h>
|
||||
#include <IRrecv.h>
|
||||
#include <IRutils.h>
|
||||
#endif
|
||||
|
||||
// remove flicker because PWM signal of RGB channels can become out of phase
|
||||
#if defined(WLED_USE_ANALOG_LEDS) && defined(ESP8266)
|
||||
@ -107,7 +112,6 @@
|
||||
|
||||
//version code in format yymmddb (b = daily build)
|
||||
#define VERSION 2002192
|
||||
|
||||
char versionString[] = "0.9.1";
|
||||
|
||||
|
||||
@ -145,8 +149,8 @@ bool useRGBW = false; //SK6812 strips can contain an ext
|
||||
bool turnOnAtBoot = true; //turn on LEDs at power-up
|
||||
byte bootPreset = 0; //save preset to load after power-up
|
||||
|
||||
byte col[]{255, 160, 0, 0}; //default RGB(W) color
|
||||
byte colSec[]{0, 0, 0, 0}; //default RGB(W) secondary color
|
||||
byte col[] {255, 160, 0, 0}; //default RGB(W) color
|
||||
byte colSec[] {0, 0, 0, 0}; //default RGB(W) secondary color
|
||||
byte briS = 128; //default brightness
|
||||
|
||||
byte nightlightTargetBri = 0; //brightness after nightlight is over
|
||||
@ -214,7 +218,7 @@ bool huePollingEnabled = false; //poll hue bridge for light state
|
||||
uint16_t huePollIntervalMs = 2500; //low values (< 1sec) may cause lag but offer quicker response
|
||||
char hueApiKey[47] = "api"; //key token will be obtained from bridge
|
||||
byte huePollLightId = 1; //ID of hue lamp to sync to. Find the ID in the hue app ("about" section)
|
||||
IPAddress hueIP = (0,0,0,0); //IP address of the bridge
|
||||
IPAddress hueIP = (0, 0, 0, 0); //IP address of the bridge
|
||||
bool hueApplyOnOff = true;
|
||||
bool hueApplyBri = true;
|
||||
bool hueApplyColor = true;
|
||||
@ -227,7 +231,7 @@ byte currentTimezone = 0; //Timezone ID. Refer to timezones
|
||||
int utcOffsetSecs = 0; //Seconds to offset from UTC before timzone calculation
|
||||
|
||||
byte overlayDefault = 0; //0: no overlay 1: analog clock 2: single-digit clocl 3: cronixie
|
||||
byte overlayMin = 0, overlayMax = ledCount-1; //boundaries of overlay mode
|
||||
byte overlayMin = 0, overlayMax = ledCount - 1; //boundaries of overlay mode
|
||||
|
||||
byte analogClock12pixel = 0; //The pixel in your strip where "midnight" would be
|
||||
bool analogClockSecondsTrail = false; //Display seconds as trail of LEDs instead of a single pixel
|
||||
@ -257,6 +261,14 @@ bool aOtaEnabled = true; //ArduinoOTA allows easy updates d
|
||||
|
||||
uint16_t userVar0 = 0, userVar1 = 0;
|
||||
|
||||
//dmx CONFIG
|
||||
uint16_t DMXChannels = 7; // number of channels per fixture
|
||||
uint16_t DMXFixtureMap[15] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
// assigns the different channels to different functions. See wled21_dmx.ino for more information.
|
||||
uint16_t DMXGap = 10; // gap between the fixtures. makes addressing easier because you don't have to memorize odd numbers when climbing up onto a rig.
|
||||
uint16_t DMXStart = 10; // start address of the first fixture
|
||||
|
||||
|
||||
|
||||
//internal global variable declarations
|
||||
//wifi
|
||||
@ -267,12 +279,12 @@ bool interfacesInited = false;
|
||||
bool wasConnected = false;
|
||||
|
||||
//color
|
||||
byte colOld[]{0, 0, 0, 0}; //color before transition
|
||||
byte colT[]{0, 0, 0, 0}; //current color
|
||||
byte colIT[]{0, 0, 0, 0}; //color that was last sent to LEDs
|
||||
byte colSecT[]{0, 0, 0, 0};
|
||||
byte colSecOld[]{0, 0, 0, 0};
|
||||
byte colSecIT[]{0, 0, 0, 0};
|
||||
byte colOld[] {0, 0, 0, 0}; //color before transition
|
||||
byte colT[] {0, 0, 0, 0}; //current color
|
||||
byte colIT[] {0, 0, 0, 0}; //color that was last sent to LEDs
|
||||
byte colSecT[] {0, 0, 0, 0};
|
||||
byte colSecOld[] {0, 0, 0, 0};
|
||||
byte colSecIT[] {0, 0, 0, 0};
|
||||
|
||||
byte lastRandomIndex = 0; //used to save last random color so the new one is not the same
|
||||
|
||||
@ -291,7 +303,7 @@ uint32_t nightlightDelayMs = 10;
|
||||
uint8_t nightlightDelayMinsDefault = nightlightDelayMins;
|
||||
unsigned long nightlightStartTime;
|
||||
byte briNlT = 0; //current nightlight brightness
|
||||
byte colNlT[]{0, 0, 0, 0}; //current nightlight color
|
||||
byte colNlT[] {0, 0, 0, 0}; //current nightlight color
|
||||
|
||||
//brightness
|
||||
unsigned long lastOnTime = 0;
|
||||
@ -331,9 +343,9 @@ bool showWelcomePage = false;
|
||||
//hue
|
||||
char hueError[25] = "Inactive";
|
||||
//uint16_t hueFailCount = 0;
|
||||
float hueXLast=0, hueYLast=0;
|
||||
uint16_t hueHueLast=0, hueCtLast=0;
|
||||
byte hueSatLast=0, hueBriLast=0;
|
||||
float hueXLast = 0, hueYLast = 0;
|
||||
uint16_t hueHueLast = 0, hueCtLast = 0;
|
||||
byte hueSatLast = 0, hueBriLast = 0;
|
||||
unsigned long hueLastRequestSent = 0;
|
||||
bool hueAuthRequired = false;
|
||||
bool hueReceived = false;
|
||||
@ -346,7 +358,7 @@ unsigned long overlayRefreshMs = 200;
|
||||
unsigned long overlayRefreshedTime;
|
||||
|
||||
//cronixie
|
||||
byte dP[]{0,0,0,0,0,0};
|
||||
byte dP[] {0, 0, 0, 0, 0, 0};
|
||||
bool cronixieInit = false;
|
||||
|
||||
//countdown
|
||||
@ -355,10 +367,10 @@ bool countdownOverTriggered = true;
|
||||
|
||||
//timer
|
||||
byte lastTimerMinute = 0;
|
||||
byte timerHours[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerMinutes[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerMacro[] = {0,0,0,0,0,0,0,0};
|
||||
byte timerWeekday[] = {255,255,255,255,255,255,255,255}; //weekdays to activate on
|
||||
byte timerHours[] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
byte timerMinutes[] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
byte timerMacro[] = {0, 0, 0, 0, 0, 0, 0, 0};
|
||||
byte timerWeekday[] = {255, 255, 255, 255, 255, 255, 255, 255}; //weekdays to activate on
|
||||
//bit pattern of arr elem: 0b11111111: sun,sat,fri,thu,wed,tue,mon,validity
|
||||
|
||||
//blynk
|
||||
@ -436,8 +448,8 @@ AsyncClient* hueClient = NULL;
|
||||
AsyncMqttClient* mqtt = NULL;
|
||||
|
||||
//function prototypes
|
||||
void colorFromUint32(uint32_t,bool=false);
|
||||
void serveMessage(AsyncWebServerRequest*,uint16_t,String,String,byte);
|
||||
void colorFromUint32(uint32_t, bool = false);
|
||||
void serveMessage(AsyncWebServerRequest*, uint16_t, String, String, byte);
|
||||
void handleE131Packet(e131_packet_t*, IPAddress);
|
||||
void arlsLock(uint32_t,byte);
|
||||
void handleOverlayDraw();
|
||||
@ -458,34 +470,36 @@ WS2812FX strip = WS2812FX();
|
||||
|
||||
//debug macros
|
||||
#ifdef WLED_DEBUG
|
||||
#define DEBUG_PRINT(x) Serial.print (x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println (x)
|
||||
#define DEBUG_PRINTF(x) Serial.printf (x)
|
||||
unsigned long debugTime = 0;
|
||||
int lastWifiState = 3;
|
||||
unsigned long wifiStateChangedTime = 0;
|
||||
int loops = 0;
|
||||
#define DEBUG_PRINT(x) Serial.print (x)
|
||||
#define DEBUG_PRINTLN(x) Serial.println (x)
|
||||
#define DEBUG_PRINTF(x) Serial.printf (x)
|
||||
unsigned long debugTime = 0;
|
||||
int lastWifiState = 3;
|
||||
unsigned long wifiStateChangedTime = 0;
|
||||
int loops = 0;
|
||||
#else
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x)
|
||||
#define DEBUG_PRINT(x)
|
||||
#define DEBUG_PRINTLN(x)
|
||||
#define DEBUG_PRINTF(x)
|
||||
#endif
|
||||
|
||||
//filesystem
|
||||
#ifndef WLED_DISABLE_FILESYSTEM
|
||||
#include <FS.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include "SPIFFSEditor.h"
|
||||
#include <FS.h>
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "SPIFFS.h"
|
||||
#endif
|
||||
#include "SPIFFSEditor.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//turns all LEDs off and restarts ESP
|
||||
void reset()
|
||||
{
|
||||
briT = 0;
|
||||
long dly = millis();
|
||||
while(millis() - dly < 250)
|
||||
while (millis() - dly < 250)
|
||||
{
|
||||
yield(); //enough time to send response to client
|
||||
}
|
||||
@ -510,13 +524,15 @@ bool oappend(const char* txt)
|
||||
bool oappendi(int i)
|
||||
{
|
||||
char s[11];
|
||||
sprintf(s,"%ld", i);
|
||||
sprintf(s, "%ld", i);
|
||||
return oappend(s);
|
||||
}
|
||||
|
||||
|
||||
//boot starts here
|
||||
void setup() {
|
||||
|
||||
|
||||
wledInit();
|
||||
}
|
||||
|
||||
@ -528,6 +544,7 @@ void loop() {
|
||||
handleSerial();
|
||||
handleNotifications();
|
||||
handleTransitions();
|
||||
handleDMX();
|
||||
userLoop();
|
||||
|
||||
yield();
|
||||
@ -547,9 +564,9 @@ void loop() {
|
||||
if (!realtimeMode) //block stuff if WARLS/Adalight is enabled
|
||||
{
|
||||
if (apActive) dnsServer.processNextRequest();
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
#ifndef WLED_DISABLE_OTA
|
||||
if (WLED_CONNECTED && aOtaEnabled) ArduinoOTA.handle();
|
||||
#endif
|
||||
#endif
|
||||
handleNightlight();
|
||||
yield();
|
||||
|
||||
@ -566,26 +583,26 @@ void loop() {
|
||||
if (millis() - lastMqttReconnectAttempt > 30000) initMqtt();
|
||||
|
||||
//DEBUG serial logging
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - debugTime > 9999)
|
||||
{
|
||||
DEBUG_PRINTLN("---DEBUG INFO---");
|
||||
DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis());
|
||||
DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now());
|
||||
DEBUG_PRINT("Free heap: "); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status());
|
||||
if (WiFi.status() != lastWifiState)
|
||||
{
|
||||
wifiStateChangedTime = millis();
|
||||
}
|
||||
lastWifiState = WiFi.status();
|
||||
DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime);
|
||||
DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime);
|
||||
DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP());
|
||||
DEBUG_PRINT("Loops/sec: "); DEBUG_PRINTLN(loops/10);
|
||||
loops = 0;
|
||||
debugTime = millis();
|
||||
}
|
||||
loops++;
|
||||
#endif
|
||||
#ifdef WLED_DEBUG
|
||||
if (millis() - debugTime > 9999)
|
||||
{
|
||||
DEBUG_PRINTLN("---DEBUG INFO---");
|
||||
DEBUG_PRINT("Runtime: "); DEBUG_PRINTLN(millis());
|
||||
DEBUG_PRINT("Unix time: "); DEBUG_PRINTLN(now());
|
||||
DEBUG_PRINT("Free heap: "); DEBUG_PRINTLN(ESP.getFreeHeap());
|
||||
DEBUG_PRINT("Wifi state: "); DEBUG_PRINTLN(WiFi.status());
|
||||
if (WiFi.status() != lastWifiState)
|
||||
{
|
||||
wifiStateChangedTime = millis();
|
||||
}
|
||||
lastWifiState = WiFi.status();
|
||||
DEBUG_PRINT("State time: "); DEBUG_PRINTLN(wifiStateChangedTime);
|
||||
DEBUG_PRINT("NTP last sync: "); DEBUG_PRINTLN(ntpLastSyncTime);
|
||||
DEBUG_PRINT("Client IP: "); DEBUG_PRINTLN(WiFi.localIP());
|
||||
DEBUG_PRINT("Loops/sec: "); DEBUG_PRINTLN(loops / 10);
|
||||
loops = 0;
|
||||
debugTime = millis();
|
||||
}
|
||||
loops++;
|
||||
#endif
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
#define EEPSIZE 2560 //Maximum is 4096
|
||||
|
||||
//eeprom Version code, enables default settings instead of 0 init on update
|
||||
#define EEPVER 16
|
||||
#define EEPVER 17
|
||||
//0 -> old version, default
|
||||
//1 -> 0.4p 1711272 and up
|
||||
//2 -> 0.4p 1711302 and up
|
||||
@ -24,6 +24,7 @@
|
||||
//14-> 0.9.0-b1
|
||||
//15-> 0.9.0-b3
|
||||
//16-> 0.9.1
|
||||
//17-> 0.9.1-dmx
|
||||
|
||||
void commit()
|
||||
{
|
||||
@ -63,7 +64,6 @@ void readStringFromEEPROM(uint16_t pos, char* str, uint16_t len)
|
||||
str[len] = 0; //make sure every string is properly terminated. str must be at least len +1 big.
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Write configuration to flash
|
||||
*/
|
||||
@ -257,6 +257,17 @@ void saveSettingsToEEPROM()
|
||||
EEPROM.write(2522, mqttPort & 0xFF);
|
||||
EEPROM.write(2523, (mqttPort >> 8) & 0xFF);
|
||||
|
||||
// DMX (2530 - 2549)
|
||||
EEPROM.write(2530, DMXChannels);
|
||||
EEPROM.write(2531, DMXGap & 0xFF);
|
||||
EEPROM.write(2532, (DMXGap >> 8) & 0xFF);
|
||||
EEPROM.write(2533, DMXStart & 0xFF);
|
||||
EEPROM.write(2534, (DMXStart >> 8) & 0xFF);
|
||||
|
||||
for (int i=0;i<15;i++) {
|
||||
EEPROM.write(2535+i, DMXFixtureMap[i]);
|
||||
} // last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye.
|
||||
|
||||
commit();
|
||||
}
|
||||
|
||||
@ -525,6 +536,17 @@ void loadSettingsFromEEPROM(bool first)
|
||||
readStringFromEEPROM(2220, blynkApiKey, 35);
|
||||
if (strlen(blynkApiKey) < 25) blynkApiKey[0] = 0;
|
||||
|
||||
|
||||
// DMX (2530 - 2549)2535
|
||||
DMXChannels = EEPROM.read(2530);
|
||||
DMXGap = EEPROM.read(2531) + ((EEPROM.read(2532) << 8) & 0xFF00);
|
||||
DMXStart = EEPROM.read(2533) + ((EEPROM.read(2534) << 8) & 0xFF00);
|
||||
|
||||
for (int i=0;i<15;i++) {
|
||||
DMXFixtureMap[i] = EEPROM.read(2535+i);
|
||||
} //last used: 2549. maybe leave a few bytes for future expansion and go on with 2600 kthxbye.
|
||||
|
||||
|
||||
//user MOD memory
|
||||
//2944 - 3071 reserved
|
||||
|
||||
|
@ -162,7 +162,7 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
obuf = dest;
|
||||
olen = 0;
|
||||
|
||||
if (subPage <1 || subPage >6) return;
|
||||
if (subPage <1 || subPage >7) return;
|
||||
|
||||
if (subPage == 1) {
|
||||
sappends('s',"CS",clientSSID);
|
||||
@ -391,5 +391,30 @@ void getSettingsJS(byte subPage, char* dest)
|
||||
oappendi(VERSION);
|
||||
oappend(") OK\";");
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
|
||||
if (subPage == 7)
|
||||
{
|
||||
sappend('v',"CN",DMXChannels);
|
||||
sappend('v',"CG",DMXGap);
|
||||
sappend('v',"CS",DMXStart);
|
||||
|
||||
sappend('i',"CH1",DMXFixtureMap[0]);
|
||||
sappend('i',"CH2",DMXFixtureMap[1]);
|
||||
sappend('i',"CH3",DMXFixtureMap[2]);
|
||||
sappend('i',"CH4",DMXFixtureMap[3]);
|
||||
sappend('i',"CH5",DMXFixtureMap[4]);
|
||||
sappend('i',"CH6",DMXFixtureMap[5]);
|
||||
sappend('i',"CH7",DMXFixtureMap[6]);
|
||||
sappend('i',"CH8",DMXFixtureMap[7]);
|
||||
sappend('i',"CH9",DMXFixtureMap[8]);
|
||||
sappend('i',"CH10",DMXFixtureMap[9]);
|
||||
sappend('i',"CH11",DMXFixtureMap[10]);
|
||||
sappend('i',"CH12",DMXFixtureMap[11]);
|
||||
sappend('i',"CH13",DMXFixtureMap[12]);
|
||||
sappend('i',"CH14",DMXFixtureMap[13]);
|
||||
sappend('i',"CH15",DMXFixtureMap[14]);
|
||||
}
|
||||
#endif
|
||||
oappend("}</script>");
|
||||
}
|
||||
|
@ -27,8 +27,9 @@ bool isAsterisksOnly(const char* str, byte maxLen)
|
||||
//called upon POST settings form submit
|
||||
void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
{
|
||||
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec
|
||||
if (subPage <1 || subPage >6) return;
|
||||
|
||||
//0: menu 1: wifi 2: leds 3: ui 4: sync 5: time 6: sec 7: DMX
|
||||
if (subPage <1 || subPage >7) return;
|
||||
|
||||
//WIFI SETTINGS
|
||||
if (subPage == 1)
|
||||
@ -293,6 +294,29 @@ void handleSettingsSet(AsyncWebServerRequest *request, byte subPage)
|
||||
aOtaEnabled = request->hasArg("AO");
|
||||
}
|
||||
}
|
||||
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
|
||||
if (subPage == 7)
|
||||
{
|
||||
int t = request->arg("CN").toInt();
|
||||
if (t>0 && t<16) {
|
||||
DMXChannels = t;
|
||||
}
|
||||
t = request->arg("CS").toInt();
|
||||
if (t>0 && t<513) {
|
||||
DMXStart = t;
|
||||
}
|
||||
t = request->arg("CG").toInt();
|
||||
if (t>0 && t<513) {
|
||||
DMXGap = t;
|
||||
}
|
||||
for (int i=0; i<15; i++) {
|
||||
String argname = "CH" + String((i+1));
|
||||
t = request->arg(argname).toInt();
|
||||
DMXFixtureMap[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
if (subPage != 6 || !doReboot) saveSettingsToEEPROM(); //do not save if factory reset
|
||||
if (subPage == 2) {
|
||||
strip.init(useRGBW,ledCount,skipFirstLed);
|
||||
|
@ -83,7 +83,9 @@ void wledInit()
|
||||
if (strlen(cmDNS) > 0) ArduinoOTA.setHostname(cmDNS);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
dmx.init(512); // initialize with bus length
|
||||
#endif
|
||||
//HTTP server page init
|
||||
initServer();
|
||||
}
|
||||
|
@ -82,6 +82,11 @@ void initServer()
|
||||
serveMessage(request, 200,"UI settings saved.","Redirecting...",1);
|
||||
});
|
||||
|
||||
server.on("/settings/dmx", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
handleSettingsSet(request, 7);
|
||||
serveMessage(request, 200,"UI settings saved.","Redirecting...",1);
|
||||
});
|
||||
|
||||
server.on("/settings/sync", HTTP_POST, [](AsyncWebServerRequest *request){
|
||||
handleSettingsSet(request, 4);
|
||||
serveMessage(request, 200,"Sync settings saved.","Redirecting...",1);
|
||||
@ -201,6 +206,16 @@ void initServer()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
request->send_P(200, "text/html", PAGE_dmxmap , dmxProcessor);
|
||||
});
|
||||
#else
|
||||
server.on("/dmxmap", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
serveMessage(request, 501, "Not implemented", "DMX support is not enabled in this build.", 254);
|
||||
});
|
||||
#endif
|
||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
||||
if (captivePortal(request)) return;
|
||||
serveIndexOrWelcome(request);
|
||||
@ -301,10 +316,38 @@ String settingsProcessor(const String& var)
|
||||
getSettingsJS(optionType, buf);
|
||||
return String(buf);
|
||||
}
|
||||
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
|
||||
if (var == "DMXMENU") {
|
||||
return String("<form action=/settings/dmx><button type=submit>DMX Output</button></form>");
|
||||
}
|
||||
|
||||
#endif
|
||||
if (var == "SCSS") return String(FPSTR(PAGE_settingsCss));
|
||||
return String();
|
||||
}
|
||||
|
||||
String dmxProcessor(const String& var)
|
||||
{
|
||||
String mapJS;
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
if (var == "DMXVARS") {
|
||||
mapJS += "\nCN=" + String(DMXChannels) + ";\n";
|
||||
mapJS += "CS=" + String(DMXStart) + ";\n";
|
||||
mapJS += "CG=" + String(DMXGap) + ";\n";
|
||||
mapJS += "LC=" + String(ledCount) + ";\n";
|
||||
mapJS += "var CH=[";
|
||||
for (int i=0;i<15;i++) {
|
||||
mapJS += String(DMXFixtureMap[i]) + ",";
|
||||
}
|
||||
mapJS += "0];";
|
||||
}
|
||||
#endif
|
||||
|
||||
return mapJS;
|
||||
}
|
||||
|
||||
|
||||
void serveSettings(AsyncWebServerRequest* request)
|
||||
{
|
||||
@ -318,6 +361,9 @@ void serveSettings(AsyncWebServerRequest* request)
|
||||
else if (url.indexOf("sync") > 0) subPage = 4;
|
||||
else if (url.indexOf("time") > 0) subPage = 5;
|
||||
else if (url.indexOf("sec") > 0) subPage = 6;
|
||||
#ifdef WLED_ENABLE_DMX // include only if DMX is enabled
|
||||
else if (url.indexOf("dmx") > 0) subPage = 7;
|
||||
#endif
|
||||
} else subPage = 255; //welcome page
|
||||
|
||||
if (subPage == 1 && wifiLock && otaLock)
|
||||
@ -339,7 +385,8 @@ void serveSettings(AsyncWebServerRequest* request)
|
||||
case 4: request->send_P(200, "text/html", PAGE_settings_sync, settingsProcessor); break;
|
||||
case 5: request->send_P(200, "text/html", PAGE_settings_time, settingsProcessor); break;
|
||||
case 6: request->send_P(200, "text/html", PAGE_settings_sec , settingsProcessor); break;
|
||||
case 7: request->send_P(200, "text/html", PAGE_settings_dmx , settingsProcessor); break;
|
||||
case 255: request->send_P(200, "text/html", PAGE_welcome); break;
|
||||
default: request->send_P(200, "text/html", PAGE_settings);
|
||||
default: request->send_P(200, "text/html", PAGE_settings , settingsProcessor);
|
||||
}
|
||||
}
|
||||
|
55
wled00/wled21_dmx.ino
Normal file
55
wled00/wled21_dmx.ino
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Support for DMX via MAX485.
|
||||
* Needs the espdmx library. You might have to change the output pin within the library. Sketchy, i know.
|
||||
* https://github.com/Rickgg/ESP-Dmx
|
||||
*/
|
||||
#ifdef WLED_ENABLE_DMX
|
||||
|
||||
void handleDMX() {
|
||||
// TODO: calculate brightness manually if no shutter channel is set
|
||||
|
||||
uint8_t brightness = strip.getBrightness();
|
||||
|
||||
for (int i = 0; i < ledCount; i++) { // uses the amount of LEDs as fixture count
|
||||
|
||||
uint32_t in = strip.getPixelColor(i); // time to get the colors for the individual fixtures as suggested by AirCookie at issue #462
|
||||
byte w = in >> 24 & 0xFF;
|
||||
byte r = in >> 16 & 0xFF;
|
||||
byte g = in >> 8 & 0xFF;
|
||||
byte b = in & 0xFF;
|
||||
|
||||
int DMXFixtureStart = DMXStart + (DMXGap * i);
|
||||
for (int j = 0; j < DMXChannels; j++) {
|
||||
int DMXAddr = DMXFixtureStart + j;
|
||||
switch (DMXFixtureMap[j]) {
|
||||
case 0: // Set this channel to 0. Good way to tell strobe- and fade-functions to fuck right off.
|
||||
dmx.write(DMXAddr, 0);
|
||||
break;
|
||||
case 1: // Red
|
||||
dmx.write(DMXAddr, r);
|
||||
break;
|
||||
case 2: // Green
|
||||
dmx.write(DMXAddr, g);
|
||||
break;
|
||||
case 3: // Blue
|
||||
dmx.write(DMXAddr, b);
|
||||
break;
|
||||
case 4: // White
|
||||
dmx.write(DMXAddr, w);
|
||||
break;
|
||||
case 5: // Shutter channel. Controls the brightness.
|
||||
dmx.write(DMXAddr, brightness);
|
||||
break;
|
||||
case 6:// Sets this channel to 255. Like 0, but more wholesome.
|
||||
dmx.write(DMXAddr, 255);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dmx.update(); // update the DMX bus
|
||||
}
|
||||
|
||||
#else
|
||||
void handleDMX() {}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user