mirror of
https://github.com/HendrikRauh/dmx-interface.git
synced 2025-05-19 10:32:56 +00:00
Merge pull request #47 from HendrikRauh/DMX-3-configuration-WebUI
Dmx 3 configuration web UI
This commit is contained in:
commit
4a14d86e15
11 changed files with 184 additions and 83 deletions
|
@ -11,6 +11,7 @@
|
|||
<script type="module" src="/networks.js" defer></script>
|
||||
<script type="module" src="/submit.js" defer></script>
|
||||
<script type="module" src="/reset.js" defer></script>
|
||||
<script type="module" src="/range-input.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
|
@ -39,6 +40,7 @@
|
|||
name="ip-method"
|
||||
id="input-ip-method"
|
||||
title="IP-"
|
||||
required
|
||||
>
|
||||
<option value="0">Statisch</option>
|
||||
<option value="1">DHCP</option>
|
||||
|
@ -52,6 +54,7 @@
|
|||
name="ip"
|
||||
id="input-ip"
|
||||
placeholder="IP-Adresse"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
|
@ -61,6 +64,7 @@
|
|||
name="subnet"
|
||||
id="input-subnet"
|
||||
placeholder="Subnetzmaske"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
|
@ -70,6 +74,7 @@
|
|||
name="gateway"
|
||||
id="input-gateway"
|
||||
placeholder="Gateway"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -79,6 +84,7 @@
|
|||
name="connection"
|
||||
id="input-connection"
|
||||
title="Verbindung"
|
||||
required
|
||||
>
|
||||
<option value="0">WiFi-Station</option>
|
||||
<option value="1">WiFi-AccessPoint</option>
|
||||
|
@ -93,6 +99,7 @@
|
|||
name="ssid"
|
||||
id="input-ssid"
|
||||
placeholder="SSID"
|
||||
required
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
|
@ -103,6 +110,7 @@
|
|||
name="ssid"
|
||||
id="select-network"
|
||||
title="Netzwerk"
|
||||
required
|
||||
></select>
|
||||
<button
|
||||
type="button"
|
||||
|
@ -133,8 +141,8 @@
|
|||
type="checkbox"
|
||||
name="direction-1"
|
||||
id="input-direction-1"
|
||||
data-value-not-checked="output"
|
||||
data-value-checked="input"
|
||||
data-value-not-checked="0"
|
||||
data-value-checked="1"
|
||||
/>
|
||||
<span class="slider"></span>
|
||||
<span>Input</span>
|
||||
|
@ -159,8 +167,8 @@
|
|||
type="checkbox"
|
||||
name="direction-2"
|
||||
id="input-direction-2"
|
||||
data-value-not-checked="output"
|
||||
data-value-checked="input"
|
||||
data-value-not-checked="0"
|
||||
data-value-checked="1"
|
||||
/>
|
||||
<span class="slider"></span>
|
||||
<span>Input</span>
|
||||
|
@ -177,6 +185,23 @@
|
|||
/>
|
||||
</label>
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<legend>Sonstiges</legend>
|
||||
<label>
|
||||
LED-Helligkeit
|
||||
<div>
|
||||
<input
|
||||
type="range"
|
||||
name="led-brightness"
|
||||
id="led-brightness"
|
||||
min="0"
|
||||
max="255"
|
||||
class="range"
|
||||
/>
|
||||
<span class="range-value"></span>
|
||||
</div>
|
||||
</label>
|
||||
</fieldset>
|
||||
<div class="buttons">
|
||||
<button type="reset">Zurücksetzen</button>
|
||||
<button type="submit">Speichern</button>
|
||||
|
|
|
@ -6,6 +6,8 @@ import {
|
|||
|
||||
const form = document.querySelector("form");
|
||||
|
||||
export let data = {};
|
||||
|
||||
export async function loadData(timeout = null) {
|
||||
const req = await fetch("/config", {
|
||||
method: "GET",
|
||||
|
@ -38,7 +40,7 @@ export function writeDataToInput(data) {
|
|||
|
||||
showLoadingScreen("Konfiguration wird geladen...");
|
||||
try {
|
||||
const data = await loadData();
|
||||
data = await loadData();
|
||||
hideLoadingScreen();
|
||||
writeDataToInput(data);
|
||||
} catch (error) {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { data } from "./load-data.js";
|
||||
|
||||
const networkDropdown = document.querySelector("#select-network");
|
||||
const refreshButton = document.querySelector("#refresh-networks");
|
||||
const refreshIcon = refreshButton.querySelector("img");
|
||||
|
@ -5,11 +7,24 @@ const refreshIcon = refreshButton.querySelector("img");
|
|||
let isLoading = false;
|
||||
|
||||
refreshButton.addEventListener("click", async () => {
|
||||
// check if interface is connected via WiFi
|
||||
if (data.connection == 0 || data.connection == 1) {
|
||||
alert(
|
||||
"Beim WLAN-Scan wird die Verbindung hardwarebedingt kurzzeitig" +
|
||||
"unterbrochen.\n" +
|
||||
"Möglicherweise muss das Interface neu verbunden werden."
|
||||
);
|
||||
}
|
||||
updateNetworks();
|
||||
});
|
||||
|
||||
// check if connected via WiFi-Station
|
||||
if (data.connection === 0) {
|
||||
// show currently connected wifi
|
||||
insertNetworks([data.ssid]);
|
||||
}
|
||||
|
||||
function insertNetworks(networks) {
|
||||
networks.unshift(""); // add empty option
|
||||
networkDropdown.textContent = ""; // clear dropdown
|
||||
|
||||
for (const ssid of networks) {
|
||||
|
@ -53,8 +68,6 @@ async function loadNetworks() {
|
|||
async function updateNetworks() {
|
||||
const networks = await loadNetworks();
|
||||
if (networks) {
|
||||
insertNetworks(networks);
|
||||
insertNetworks(["", ...networks], true);
|
||||
}
|
||||
}
|
||||
|
||||
updateNetworks();
|
||||
|
|
14
data/range-input.js
Normal file
14
data/range-input.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
document.querySelector("form").addEventListener("input", (event) => {
|
||||
if (event.target.classList.contains("range")) {
|
||||
updateValue(event.target);
|
||||
}
|
||||
});
|
||||
|
||||
function updateValue(slider) {
|
||||
const percentage = Math.round((slider.value / slider.max) * 100);
|
||||
slider.nextElementSibling.innerText = `${percentage}%`;
|
||||
}
|
||||
|
||||
document.querySelectorAll("input[type='range'].range").forEach((element) => {
|
||||
updateValue(element);
|
||||
});
|
|
@ -3,7 +3,7 @@
|
|||
--color-on-primary: white;
|
||||
--color-background: #222;
|
||||
--color-danger: #fa2b58;
|
||||
--icon-button-size: 2.5rem;
|
||||
--appended-item-size: 2.5rem;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -50,8 +50,13 @@ label span {
|
|||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
select,
|
||||
label div {
|
||||
width: clamp(200px, 100%, 400px);
|
||||
}
|
||||
|
||||
input,
|
||||
select {
|
||||
background-color: var(--color-background);
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
|
@ -66,8 +71,13 @@ select:focus {
|
|||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
select:has(+ .icon-button) {
|
||||
width: calc(clamp(200px, 100%, 400px) - var(--icon-button-size) - 8px);
|
||||
select:has(+ .icon-button),
|
||||
label div input[type="range"] {
|
||||
width: calc(clamp(200px, 100%, 400px) - var(--appended-item-size) - 8px);
|
||||
}
|
||||
|
||||
input[type="range"] {
|
||||
accent-color: var(--color-primary);
|
||||
}
|
||||
|
||||
button {
|
||||
|
@ -213,8 +223,8 @@ button.reload {
|
|||
.icon-button {
|
||||
padding: 8px;
|
||||
font-size: 0;
|
||||
width: var(--icon-button-size);
|
||||
height: var(--icon-button-size);
|
||||
width: var(--appended-item-size);
|
||||
height: var(--appended-item-size);
|
||||
}
|
||||
|
||||
.spinning {
|
||||
|
@ -223,3 +233,16 @@ button.reload {
|
|||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
div:has(.range-value) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.range-value {
|
||||
width: var(--appended-item-size);
|
||||
height: var(--appended-item-size);
|
||||
text-align: center;
|
||||
line-height: var(--appended-item-size);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ function parseValue(input) {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (input.type === "number") {
|
||||
if (input.type === "number" || input.type === "range") {
|
||||
const number = Number(input.value);
|
||||
return Number.isNaN(number) ? null : number;
|
||||
}
|
||||
|
|
34
src/main.cpp
34
src/main.cpp
|
@ -16,6 +16,7 @@
|
|||
#include "ESPDMX.h"
|
||||
#include <LittleFS.h>
|
||||
#include "routes/config.h"
|
||||
#include "routes/networks.h"
|
||||
|
||||
DMXESPSerial dmx1;
|
||||
DMXESPSerial dmx2;
|
||||
|
@ -94,6 +95,9 @@ void setup()
|
|||
esp_read_mac(mac, ESP_MAC_ETH);
|
||||
|
||||
// LED
|
||||
config.begin("dmx", true);
|
||||
brightness_led = config.getUInt("led-brightness", DEFAULT_LED_BRIGHTNESS);
|
||||
config.end();
|
||||
analogWrite(PIN_LED, brightness_led);
|
||||
|
||||
// Button
|
||||
|
@ -124,11 +128,11 @@ void setup()
|
|||
|
||||
config.begin("dmx", true);
|
||||
|
||||
universe1 = config.getUInt("universe-1", 1);
|
||||
universe2 = config.getUInt("universe-2", 2);
|
||||
universe1 = config.getUInt("universe-1", DEFAULT_UNIVERSE1);
|
||||
universe2 = config.getUInt("universe-2", DEFAULT_UNIVERSE2);
|
||||
|
||||
direction1 = static_cast<Direction>(config.getUInt("direction-1", 0));
|
||||
direction2 = static_cast<Direction>(config.getUInt("direction-2", 1));
|
||||
direction1 = static_cast<Direction>(config.getUInt("direction-1", DEFAULT_DIRECTION1));
|
||||
direction2 = static_cast<Direction>(config.getUInt("direction-2", DEFAULT_DIRECTION2));
|
||||
|
||||
Serial.print("Port A: Universe ");
|
||||
Serial.print(universe1);
|
||||
|
@ -140,25 +144,22 @@ void setup()
|
|||
Serial.print(" ");
|
||||
Serial.println((direction2 == Input) ? "DMX -> Art-Net" : "Art-Net -> DMX");
|
||||
|
||||
Connection connection = static_cast<Connection>(config.getUInt("connection", WiFiAP));
|
||||
IpMethod ipMethod = static_cast<IpMethod>(config.getUInt("ip-method"), DHCP);
|
||||
Connection connection = static_cast<Connection>(config.getUInt("connection", DEFAULT_CONNECTION));
|
||||
IpMethod ipMethod = static_cast<IpMethod>(config.getUInt("ip-method"), DEFAULT_IP_METHOD);
|
||||
|
||||
WiFi.macAddress(mac);
|
||||
char hostname[30];
|
||||
snprintf(hostname, sizeof(hostname), "ChaosDMX-%02X%02X", mac[4], mac[5]);
|
||||
DEFAULT_SSID = hostname;
|
||||
Serial.print("Hostname: ");
|
||||
Serial.println(hostname);
|
||||
|
||||
String ssid = config.getString("ssid", hostname);
|
||||
String pwd = config.getString("password", "mbgmbgmbg");
|
||||
String ssid = config.getString("ssid", DEFAULT_SSID);
|
||||
String pwd = config.getString("password", DEFAULT_PASSWORD);
|
||||
|
||||
// Default IP as defined in standard https://art-net.org.uk/downloads/art-net.pdf, page 13
|
||||
IPAddress defaultIp(2, mac[3], mac[4], mac[5]);
|
||||
IPAddress ip = config.getUInt("ip", defaultIp);
|
||||
IPAddress defaultSubnet(255, 0, 0, 0);
|
||||
IPAddress subnet = config.getUInt("subnet", defaultSubnet);
|
||||
IPAddress defaultGateway(2, 0, 0, 1);
|
||||
IPAddress gateway = config.getUInt("gateway", defaultGateway);
|
||||
IPAddress ip = config.getUInt("ip", DEFAULT_IP);
|
||||
IPAddress subnet = config.getUInt("subnet", DEFAULT_SUBNET);
|
||||
IPAddress gateway = config.getUInt("gateway", NULL);
|
||||
|
||||
config.end();
|
||||
|
||||
|
@ -320,6 +321,9 @@ void setup()
|
|||
server.begin();
|
||||
Serial.println("Server started!");
|
||||
|
||||
// scan networks and cache them
|
||||
WiFi.scanNetworks(true);
|
||||
|
||||
ledBlink(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "WiFi.h"
|
||||
|
||||
Preferences config;
|
||||
String DEFAULT_SSID = "";
|
||||
|
||||
#pragma region Utility
|
||||
|
||||
|
@ -75,27 +76,23 @@ void onGetConfig(AsyncWebServerRequest *request)
|
|||
{
|
||||
config.begin("dmx", true);
|
||||
|
||||
IPAddress defaultIp(192, 168, 1, 201);
|
||||
IPAddress ip = config.getUInt("ip", defaultIp);
|
||||
|
||||
IPAddress defaultSubnet(255, 255, 255, 0);
|
||||
IPAddress subnet = config.getUInt("subnet", defaultSubnet);
|
||||
|
||||
IPAddress defaultGateway(192, 168, 1, 1);
|
||||
IPAddress gateway = config.getUInt("gateway", defaultGateway);
|
||||
IPAddress ip = config.getUInt("ip", DEFAULT_IP);
|
||||
IPAddress subnet = config.getUInt("subnet", DEFAULT_SUBNET);
|
||||
IPAddress gateway = config.getUInt("gateway", NULL);
|
||||
|
||||
JsonDocument doc;
|
||||
doc["connection"] = config.getUInt("connection", WiFiSta);
|
||||
doc["ssid"] = config.getString("ssid", "artnet");
|
||||
doc["password"] = config.getString("password", "mbgmbgmbg");
|
||||
doc["ip-method"] = config.getUInt("ip-method"), Static;
|
||||
doc["connection"] = config.getUInt("connection", DEFAULT_CONNECTION);
|
||||
doc["ssid"] = config.getString("ssid", DEFAULT_SSID);
|
||||
doc["password"] = config.getString("password", DEFAULT_PASSWORD);
|
||||
doc["ip-method"] = config.getUInt("ip-method", DEFAULT_IP_METHOD);
|
||||
doc["ip"] = ip.toString();
|
||||
doc["subnet"] = subnet.toString();
|
||||
doc["gateway"] = gateway.toString();
|
||||
doc["universe-1"] = config.getUInt("universe-1", 1);
|
||||
doc["direction-1"] = config.getUInt("direction-1", Output);
|
||||
doc["universe-2"] = config.getUInt("universe-2", 1);
|
||||
doc["direction-2"] = config.getUInt("direction-2", Input);
|
||||
doc["gateway"] = gateway != NULL ? gateway.toString() : "";
|
||||
doc["universe-1"] = config.getUInt("universe-1", DEFAULT_UNIVERSE1);
|
||||
doc["direction-1"] = config.getUInt("direction-1", DEFAULT_DIRECTION1);
|
||||
doc["universe-2"] = config.getUInt("universe-2", DEFAULT_UNIVERSE2);
|
||||
doc["direction-2"] = config.getUInt("direction-2", DEFAULT_DIRECTION2);
|
||||
doc["led-brightness"] = config.getUInt("led-brightness", DEFAULT_LED_BRIGHTNESS);
|
||||
|
||||
config.end();
|
||||
|
||||
|
@ -152,6 +149,8 @@ void onPutConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size
|
|||
config.putUInt("universe-1", doc["universe-1"]);
|
||||
config.putUInt("universe-2", doc["universe-2"]);
|
||||
|
||||
config.putUInt("led-brightness", doc["led-brightness"]);
|
||||
|
||||
config.end();
|
||||
|
||||
request->send(200);
|
||||
|
@ -162,31 +161,3 @@ void onPutConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size
|
|||
request->send(400, "text/plain", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void onGetNetworks(AsyncWebServerRequest *request)
|
||||
{
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
int numberOfNetworks = WiFi.scanComplete();
|
||||
if (numberOfNetworks == WIFI_SCAN_FAILED)
|
||||
{
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
else if (numberOfNetworks)
|
||||
{
|
||||
for (int i = 0; i < numberOfNetworks; ++i)
|
||||
{
|
||||
array.add(WiFi.SSID(i));
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
if (WiFi.scanComplete() == WIFI_SCAN_FAILED)
|
||||
{
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
}
|
||||
|
||||
String jsonString;
|
||||
serializeJson(doc, jsonString);
|
||||
request->send(200, "application/json", jsonString);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <AsyncWebServer_ESP32_W5500.h>
|
||||
#include <ESPDMX.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
// #ifndef CONFIG_h
|
||||
// #define CONFIG_h
|
||||
#ifndef CONFIG_h
|
||||
#define CONFIG_h
|
||||
|
||||
extern Preferences config;
|
||||
|
||||
|
@ -31,10 +29,23 @@ enum Direction
|
|||
};
|
||||
const uint8_t DIRECTION_SIZE = 2;
|
||||
|
||||
const Connection DEFAULT_CONNECTION = WiFiAP;
|
||||
const IpMethod DEFAULT_IP_METHOD = DHCP;
|
||||
extern String DEFAULT_SSID; // initialized in setup because it depends on the mac address
|
||||
const String DEFAULT_PASSWORD = "mbgmbgmbg";
|
||||
const IPAddress DEFAULT_IP(192, 168, 4, 1);
|
||||
const IPAddress DEFAULT_SUBNET(255, 255, 255, 0);
|
||||
const IPAddress DEFAULT_GATEWAY(2, 0, 0, 1);
|
||||
|
||||
const Direction DEFAULT_DIRECTION1 = Output;
|
||||
const Direction DEFAULT_DIRECTION2 = Input;
|
||||
const uint8_t DEFAULT_UNIVERSE1 = 1;
|
||||
const uint8_t DEFAULT_UNIVERSE2 = 2;
|
||||
|
||||
const uint8_t DEFAULT_LED_BRIGHTNESS = 25;
|
||||
|
||||
void onGetConfig(AsyncWebServerRequest *request);
|
||||
|
||||
void onPutConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total);
|
||||
|
||||
void onGetNetworks(AsyncWebServerRequest *request);
|
||||
|
||||
// #endif
|
||||
#endif
|
29
src/routes/networks.cpp
Normal file
29
src/routes/networks.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#include "networks.h";
|
||||
|
||||
void onGetNetworks(AsyncWebServerRequest *request)
|
||||
{
|
||||
JsonDocument doc;
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
int numberOfNetworks = WiFi.scanComplete();
|
||||
if (numberOfNetworks == WIFI_SCAN_FAILED)
|
||||
{
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
else if (numberOfNetworks)
|
||||
{
|
||||
for (int i = 0; i < numberOfNetworks; ++i)
|
||||
{
|
||||
array.add(WiFi.SSID(i));
|
||||
}
|
||||
WiFi.scanDelete();
|
||||
if (WiFi.scanComplete() == WIFI_SCAN_FAILED)
|
||||
{
|
||||
WiFi.scanNetworks(true);
|
||||
}
|
||||
}
|
||||
|
||||
String jsonString;
|
||||
serializeJson(doc, jsonString);
|
||||
request->send(200, "application/json", jsonString);
|
||||
}
|
9
src/routes/networks.h
Normal file
9
src/routes/networks.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#include <AsyncWebServer_ESP32_W5500.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#ifndef NETWORKS_H
|
||||
#define NETWORKS_H
|
||||
|
||||
void onGetNetworks(AsyncWebServerRequest *request);
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue