diff --git a/.gitignore b/.gitignore index 6e29f8f..516c50a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,5 @@ # Development .pio -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/ipch -.vscode/settings.json \ No newline at end of file +.vscode +!.vscode\extensions.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..71eeaad --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# DMX-Interface + +Art-Net-Interface + +## Case + +[OnShape](https://cad.onshape.com/documents/7363818fd18bf0cbf094790e/w/52455282b39e47fbde5d0e53/e/9bec98aa83a813dc9a4d6ab2) + +STL's coming soon, case still being developed. + +## Wiring + +![Handwritten diagram](/assets/circuit/handwritten/circuit%20diagram.jpeg) + +## Pin usage + +| GPIO | Usage | +| ---- | -------------- | +| GND | GND to others | +| 3,5V | VIN on RS485 | +| 5V | VIN on W5500 | +| 0 | Onboard Button | +| 5 | Ext. Button | +| 7 | Ext. LED | +| 15 | Onboard LED | +| 17 | U1TXD | +| 18 | U1RXD | +| 21 | U0TXD | +| 33 | U0RXD | +| 34 | SPI CS | +| 35 | SPI MOS | +| 36 | SPI SCK | +| 37 | SPI MISO | diff --git a/assets/circuit/handwritten/circuit diagram.jpeg b/assets/circuit/handwritten/circuit diagram.jpeg new file mode 100644 index 0000000..15d287b Binary files /dev/null and b/assets/circuit/handwritten/circuit diagram.jpeg differ diff --git a/data/index.html b/data/index.html index 8011dbf..c7f9b3a 100644 --- a/data/index.html +++ b/data/index.html @@ -6,13 +6,30 @@ Konfiguration + +
-

Konfiguration

-
+
+
+ +

+
+ +
+
+ + +

Konfiguration

Verbindung -
+
+ +
@@ -44,12 +79,12 @@ id="input-connection" title="Verbindung" > - - - + + + -
+
-
+
-
+
- +
+ + +
diff --git a/data/load-data.js b/data/load-data.js index 2ae6f1e..1ec35a7 100644 --- a/data/load-data.js +++ b/data/load-data.js @@ -1,35 +1,47 @@ +import { + showLoadingScreen, + showError, + hideLoadingScreen, +} from "./loading-screen.js"; + const form = document.querySelector("form"); -async function loadData() { - try { - const req = await fetch("/config", { - method: "GET", - }); - if (!req.ok) { - throw new Error(`Response status: ${req.status}`); - } - - const json = await req.json(); - console.log(json); - return json; - } catch (error) { - console.log(error); - return null; +export async function loadData(timeout = null) { + const req = await fetch("/config", { + method: "GET", + signal: timeout !== null ? AbortSignal.timeout(timeout) : undefined, + }); + if (!req.ok) { + throw new Error(`Response status: ${req.status}`); } + + const json = await req.json(); + console.log(json); + return json; } -function writeDataToInput(data) { - console.log("write data", typeof data); +export function writeDataToInput(data) { + console.log("write data"); for (const [key, value] of Object.entries(data)) { const element = document.querySelector(`[name=${key}]`); - console.log(element); - element.value = value; + console.log(key, element); + + if (element.type === "checkbox") { + element.checked = value; + } else { + element.value = value; + } } // send "change" event form.dispatchEvent(new Event("change", { bubbles: true })); } -const data = await loadData(); -if (data !== null) { +showLoadingScreen("Konfiguration wird geladen..."); +try { + const data = await loadData(); + hideLoadingScreen(); writeDataToInput(data); +} catch (error) { + console.log(error.message); + showError("Die Konfiguration konnte nicht geladen werden."); } diff --git a/data/loading-screen.js b/data/loading-screen.js new file mode 100644 index 0000000..4b04769 --- /dev/null +++ b/data/loading-screen.js @@ -0,0 +1,40 @@ +const form = document.querySelector("form"); +const loadingScreen = document.querySelector(".loading-screen"); +const loadingMsg = loadingScreen.querySelector("h2"); +const spinner = loadingScreen.querySelector(".spinner"); +const reloadBtn = loadingScreen.querySelector(".reload"); + +export function showLoadingScreen(msg) { + hide(form, reloadBtn); + show(loadingScreen, spinner); + loadingMsg.classList.remove("error"); + loadingMsg.textContent = msg; +} + +export function showError(msg) { + showLoadingScreen(msg); + loadingMsg.innerHTML += + "
Stelle sicher, dass du mit dem DMX-Interface verbunden bist und die IP-Adresse stimmt."; + show(reloadBtn); + hide(spinner); + loadingMsg.classList.add("error"); +} + +export function hideLoadingScreen() { + hide(loadingScreen, reloadBtn); + show(form); + loadingMsg.classList.remove("error"); + loadingMsg.textContent = ""; +} + +function show(...elements) { + for (const element of elements) { + element.classList.remove("hidden"); + } +} + +function hide(...elements) { + for (const element of elements) { + element.classList.add("hidden"); + } +} diff --git a/data/reset.js b/data/reset.js new file mode 100644 index 0000000..c34c41f --- /dev/null +++ b/data/reset.js @@ -0,0 +1,16 @@ +import { updateConfig } from "/submit.js"; + +const form = document.querySelector("form"); + +form.addEventListener("reset", async (event) => { + event.preventDefault(); + + const ok = confirm( + "Sicher, dass du alle Einstellungen zurücksetzen möchtest?" + ); + if (ok) { + updateConfig({ + method: "DELETE", + }); + } +}); diff --git a/data/style.css b/data/style.css index b0676df..7cce882 100644 --- a/data/style.css +++ b/data/style.css @@ -1,6 +1,8 @@ :root { --color-primary: #087e8b; --color-on-primary: white; + --color-background: #222; + --color-danger: #fa2b58; } body { @@ -13,7 +15,7 @@ body { } main { - background-color: #222; + background-color: var(--color-background); max-width: 700px; padding: 8px max(5%, 8px); margin: 0 auto; @@ -44,7 +46,7 @@ label { input, select { width: clamp(200px, 100%, 400px); - background-color: #222; + background-color: var(--color-background); color: white; border: 1px solid white; border-radius: 8px; @@ -59,14 +61,16 @@ select:focus { } button { - display: block; border: none; inset: none; border-radius: 8px; background-color: var(--color-primary); color: var(--color-on-primary); padding: 8px 16px; - margin: 0 auto; +} + +button[type="reset"] { + background-color: var(--color-danger); } :is(div:has(:is(input, select)), input, select, label) @@ -75,7 +79,7 @@ button { } .hidden { - display: none; + display: none !important; } label.switch { @@ -126,3 +130,68 @@ label.switch input:checked + .slider::before { left: 100%; translate: -100% -50%; } + +.buttons { + display: flex; + flex-direction: row; + justify-content: center; + gap: 8px; +} + +.loading-screen { + display: grid; + justify-content: center; +} + +h2.error { + color: var(--color-danger); +} + +button.reload { + display: block; + margin: 0 auto; +} + +.spinner-container { + width: min(max-content, 100%); +} + +.spinner { + position: relative; + margin: 10px auto; + background: conic-gradient(transparent 150deg, var(--color-primary)); + --outer-diameter: 50px; + width: var(--outer-diameter); + height: var(--outer-diameter); + border-radius: 50%; + + animation-name: spin; + animation-duration: 1s; + animation-iteration-count: infinite; + animation-timing-function: linear; +} + +.spinner::after { + position: absolute; + content: ""; + display: block; + --spinner-border: 5px; + top: var(--spinner-border); + left: var(--spinner-border); + + --inner-diameter: calc(var(--outer-diameter) - 2 * var(--spinner-border)); + width: var(--inner-diameter); + height: var(--inner-diameter); + + background-color: var(--color-background); + border-radius: 50%; +} + +@keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} diff --git a/data/submit.js b/data/submit.js index 6a24318..0fdbda8 100644 --- a/data/submit.js +++ b/data/submit.js @@ -1,3 +1,10 @@ +import { loadData, writeDataToInput } from "./load-data.js"; +import { + showLoadingScreen, + hideLoadingScreen, + showError, +} from "./loading-screen.js"; + const form = document.querySelector("form"); function parseValue(input) { @@ -13,7 +20,7 @@ function parseValue(input) { if (input.type === "number") { const number = Number(input.value); - return isNaN(number) ? null : number; + return Number.isNaN(number) ? null : number; } return input.value; @@ -31,25 +38,35 @@ form.addEventListener("submit", (event) => { }, {}); console.log(data); - putData(data); + updateConfig({ + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); }); -async function putData(data) { +export async function updateConfig(fetchOptions) { + showLoadingScreen("Konfiguration anwenden und ESP neustarten..."); + try { - const res = await fetch("/config", { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data), - }); + const res = await fetch("/config", fetchOptions); if (!res.ok) { throw new Error(`Response status: ${res.status}`); } - const json = await res.json(); - console.log(json); + // wait for the esp to restart + const delay = new Promise((resolve) => + setTimeout(() => resolve(), 500) + ); + await delay; + + const data = await loadData(30 * 1000); + writeDataToInput(data); + hideLoadingScreen(); } catch (error) { console.error(error.message); + showError(error.message); } } diff --git a/src/ESPDMX.cpp b/src/ESPDMX.cpp index 7dc0cd1..5408807 100644 --- a/src/ESPDMX.cpp +++ b/src/ESPDMX.cpp @@ -1,94 +1,71 @@ -// - - - - - -// ESPDMX - A Arduino library for sending and receiving DMX using the builtin serial hardware port. -// ESPDMX.cpp: Library implementation file -// -// Copyright (C) 2015 Rick -// This work is licensed under a GNU style license. -// -// Last change: Marcel Seerig -// -// Documentation and samples are available at https://github.com/Rickgg/ESP-Dmx -// - - - - - - /* ----- LIBRARIES ----- */ #include #include "ESPDMX.h" -#define DMXSPEED 250000 -#define DMXFORMAT SERIAL_8N2 -#define BREAKSPEED 83333 -#define BREAKFORMAT SERIAL_8N1 -#define SERIALPORT Serial0 -#define DMXCHANNELS 512 - -bool dmxStarted = false; -int sendPin = 33; -int receivePin = -1; - // DMX value array and size. Entry 0 will hold startbyte, so we need 512+1 elements -uint8_t dmxDataStore[DMXCHANNELS + 1] = {}; +// std::vector dmxDataStores(MAX_IDS); +// uint8_t dmxDataStores[MAX_IDS][DMXCHANNELS + 1]; // Set up the DMX-Protocol -void DMXESPSerial::init() +void DMXESPSerial::init(int pinSend = 19, int pinRecv = -1) { - SERIALPORT.begin(DMXSPEED, DMXFORMAT, receivePin, sendPin); + sendPin = pinSend; + recvPin = pinRecv; + SERIALPORT.begin(DMXSPEED, DMXFORMAT, recvPin, sendPin); pinMode(sendPin, OUTPUT); dmxStarted = true; } // Function to read DMX data -uint8_t DMXESPSerial::read(int Channel) +uint8_t DMXESPSerial::read(int channel) { if (dmxStarted == false) init(); - if (Channel < 1) - Channel = 1; - if (Channel > DMXCHANNELS) - Channel = DMXCHANNELS; - return (dmxDataStore[Channel]); + if (channel < 1) + channel = 1; + if (channel > DMXCHANNELS) + channel = DMXCHANNELS; + return (dmxDataStore[channel]); } // Function to send DMX data -void DMXESPSerial::write(int Channel, uint8_t value) +void DMXESPSerial::write(int channel, uint8_t value) { + if (dmxStarted == false) init(); - if (Channel < 1) - Channel = 1; - if (Channel > DMXCHANNELS) - Channel = DMXCHANNELS; + if (channel < 1) + channel = 1; + if (channel > DMXCHANNELS) + channel = DMXCHANNELS; if (value < 0) value = 0; if (value > 255) value = 255; - dmxDataStore[Channel] = value; + dmxDataStore[channel] = value; } void DMXESPSerial::end() { SERIALPORT.end(); - dmxStarted = false; } // Function to update the DMX bus void DMXESPSerial::update() { - if (dmxStarted == false) - init(); - // Send break digitalWrite(sendPin, HIGH); - SERIALPORT.begin(BREAKSPEED, BREAKFORMAT, receivePin, sendPin); + SERIALPORT.begin(BREAKSPEED, BREAKFORMAT, recvPin, sendPin); SERIALPORT.write(0); SERIALPORT.flush(); delay(1); SERIALPORT.end(); // send data - SERIALPORT.begin(DMXSPEED, DMXFORMAT, receivePin, sendPin); + SERIALPORT.begin(DMXSPEED, DMXFORMAT, recvPin, sendPin); digitalWrite(sendPin, LOW); SERIALPORT.write(dmxDataStore, DMXCHANNELS); SERIALPORT.flush(); diff --git a/src/ESPDMX.h b/src/ESPDMX.h index 8cdc522..04123ae 100644 --- a/src/ESPDMX.h +++ b/src/ESPDMX.h @@ -1,29 +1,28 @@ -// - - - - - -// ESPDMX - A Arduino library for sending and receiving DMX using the builtin serial hardware port. -// ESPDMX.cpp: Library implementation file -// -// Copyright (C) 2015 Rick -// This work is licensed under a GNU style license. -// -// Last change: Marcel Seerig -// -// Documentation and samples are available at https://github.com/Rickgg/ESP-Dmx -// - - - - - - #include - #ifndef ESPDMX_h #define ESPDMX_h -// ---- Methods ---- +#define DMXSPEED 250000 +#define DMXFORMAT SERIAL_8N2 +#define BREAKSPEED 83333 +#define BREAKFORMAT SERIAL_8N1 +#define SERIALPORT Serial0 +#define DMXCHANNELS 512 -class DMXESPSerial { - public: - void init(); - uint8_t read(int Channel); - void write(int channel, uint8_t value);void update(); - void end(); +class DMXESPSerial +{ +public: + int sendPin; + int recvPin; + bool dmxStarted; + uint8_t dmxDataStore[DMXCHANNELS + 1]; + + void init(int pinSend, int pinRecv); + uint8_t read(int Channel); + void write(int channel, uint8_t value); + void update(); + void end(); }; #endif diff --git a/src/main.cpp b/src/main.cpp index 8c783e8..60b3af9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,17 +10,17 @@ //#include "w5500/esp32_w5500.h" //#include - #include #include + #include "ESPDMX.h" #include "SPI.h" #include -#include +#include "routes/config.h" -Preferences config; -DMXESPSerial dmx; +DMXESPSerial dmx1; +DMXESPSerial dmx2; // Button #define PIN_LED 7 @@ -96,9 +96,16 @@ void setup() delay(1000); Serial.println("..."); - config.begin("dmx", false); + config.begin("dmx", true); - uint8_t universe = config.getUChar("universe", 1); + uint8_t universe1 = config.getUChar("universe-1", 1); + uint8_t universe2 = config.getUChar("universe-2", 1); + + Direction direction1 = static_cast(config.getUInt("direction-1", 0)); + Direction direction2 = static_cast(config.getUInt("direction-2", 1)); + + Connection connection = static_cast(config.getUInt("connection", WiFiAP)); + IpMethod ipMethod = static_cast(config.getUInt("ip-method"), Static); WiFi.macAddress(mac); char hostname[30]; @@ -111,84 +118,113 @@ void setup() IPAddress defaultIp(2, mac[3], mac[4], mac[5]); 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 cidr = config.getUChar("cidr", 24); + config.end(); - // TODO: \/ Herleiten \/ @psxde - const IPAddress gateway(2, 0, 0, 1); - const IPAddress subnet(255, 0, 0, 0); + // wait for serial monitor + delay(5000); - // TODO: Initialize Interface connection type - to be changed to Raffaels(TM) enum - ethType = TYP_ETH; - - - // Button - pinMode(PIN_BUTTON,INPUT_PULLUP); - if(digitalRead(PIN_BUTTON) == LOW){ - ledBlink(100); - delay(2000); - Serial.println("Start AP-Mode"); - ethType = TYP_AP; - } - - switch (ethType) + switch (connection) { - case TYP_STA: - Serial.println("Initialize as WiFi-STA"); + case WiFiSta: + Serial.println("Initialize as WiFi-Station"); WiFi.begin(ssid, pwd); - WiFi.setHostname(hostname); - WiFi.config(ip, gateway, subnet); - while (WiFi.status() != WL_CONNECTED) { + if (ipMethod == Static) + { + WiFi.config(ip, gateway, subnet); + } + while (WiFi.status() != WL_CONNECTED) + { Serial.print("."); delay(500); } + Serial.println(""); Serial.print("WiFi connected, IP = "); Serial.println(WiFi.localIP()); - Serial.print("MAC address: "); - Serial.println(WiFi.macAddress()); break; - case TYP_ETH:{ - Serial.println("Initialize as ETH"); - ESP32_W5500_onEvent(); - - - if (ETH.begin( ETH_MISO, ETH_MOSI, ETH_SCK, ETH_SS, ETH_INT, ETH_SPI_CLOCK_MHZ, ETH_SPI_HOST, mac )) { // Dynamic IP setup - }else{ - Serial.println("Failed to configure Ethernet"); - } - ETH.setHostname(hostname); - - //ESP32_W5500_waitForConnect(); - uint8_t timeout = 5; // in s - Serial.print("Wait for connect"); - while (!ESP32_W5500_eth_connected && timeout > 0) { - delay(1000); - timeout--; - Serial.print("."); - } - Serial.println(); - if (ESP32_W5500_eth_connected) { - Serial.println("DHCP OK!"); - } else { - Serial.println("Set static IP"); - ETH.config(ip, gateway, subnet); - } - - - Serial.print("Local IP : "); - Serial.println(ETH.localIP()); - Serial.print("Subnet Mask : "); - Serial.println(ETH.subnetMask()); - Serial.print("Gateway IP : "); - Serial.println(ETH.gatewayIP()); - Serial.print("DNS Server : "); - Serial.println(ETH.dnsIP()); - Serial.print("MAC address : "); - Serial.println(ETH.macAddress()); - - Serial.println("Ethernet Successfully Initialized"); + case WiFiAP: + Serial.println("Initialize as WiFi-Access-Point"); + WiFi.softAP(ssid, pwd); + WiFi.softAPConfig(ip, gateway, subnet); + Serial.print("WiFi AP enabled, IP = "); + Serial.println(WiFi.softAPIP()); break; + + case Ethernet: + // TODO: Initialize Interface connection type - to be changed to Raffaels(TM) enum + ethType = TYP_ETH; + + // Button + pinMode(PIN_BUTTON,INPUT_PULLUP); + if(digitalRead(PIN_BUTTON) == LOW){ + ledBlink(100); + delay(2000); + Serial.println("Start AP-Mode"); + ethType = TYP_AP; + } + + switch (ethType) + { + case TYP_STA: + Serial.println("Initialize as WiFi-STA"); + WiFi.begin(ssid, pwd); + WiFi.setHostname(hostname); + WiFi.config(ip, gateway, subnet); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(500); + } + Serial.print("WiFi connected, IP = "); + Serial.println(WiFi.localIP()); + Serial.print("MAC address: "); + Serial.println(WiFi.macAddress()); + break; + case TYP_ETH:{ + Serial.println("Initialize as ETH"); + ESP32_W5500_onEvent(); + + + if (ETH.begin( ETH_MISO, ETH_MOSI, ETH_SCK, ETH_SS, ETH_INT, ETH_SPI_CLOCK_MHZ, ETH_SPI_HOST, mac )) { // Dynamic IP setup + }else{ + Serial.println("Failed to configure Ethernet"); + } + ETH.setHostname(hostname); + + //ESP32_W5500_waitForConnect(); + uint8_t timeout = 5; // in s + Serial.print("Wait for connect"); + while (!ESP32_W5500_eth_connected && timeout > 0) { + delay(1000); + timeout--; + Serial.print("."); + } + Serial.println(); + if (ESP32_W5500_eth_connected) { + Serial.println("DHCP OK!"); + } else { + Serial.println("Set static IP"); + ETH.config(ip, gateway, subnet); + } + + + Serial.print("Local IP : "); + Serial.println(ETH.localIP()); + Serial.print("Subnet Mask : "); + Serial.println(ETH.subnetMask()); + Serial.print("Gateway IP : "); + Serial.println(ETH.gatewayIP()); + Serial.print("DNS Server : "); + Serial.println(ETH.dnsIP()); + Serial.print("MAC address : "); + Serial.println(ETH.macAddress()); + + Serial.println("Ethernet Successfully Initialized"); + break; } default: Serial.println("Initialize as WiFi-AP"); @@ -202,12 +238,11 @@ void setup() break; } - delay(500); + } - // Initialize DMX ports Serial.println("Initialize DMX..."); - dmx.init(); + dmx1.init(19, -1); // Initialize Art-Net Serial.println("Initialize Art-Net..."); @@ -218,12 +253,13 @@ void setup() { for (size_t i = 0; i < size; ++i) { - dmx.write((i + 1), data[i]); + dmx1.write((i + 1), data[i]); } - dmx.update(); + dmx1.update(); }); + // if Artnet packet comes, this function is called to every universe artnet.subscribeArtDmx([&](const uint8_t *data, uint16_t size, const ArtDmxMetadata &metadata, const ArtNetRemoteInfo &remote) {}); @@ -235,28 +271,24 @@ void setup() server.serveStatic("/", SPIFFS, "/").setDefaultFile("index.html"); - server.on("/config", HTTP_GET, [&, defaultIp, ssid, pwd, universe](AsyncWebServerRequest *request) + server.on("/config", HTTP_GET, [](AsyncWebServerRequest *request) + { onGetConfig(request); }); + + server.on("/config", HTTP_DELETE, [](AsyncWebServerRequest *request) { - JsonDocument doc; + config.begin("dmx", false); + config.clear(); + config.end(); + // respond with default config + onGetConfig(request); - doc["ssid"] = ssid; - doc["pwd"] = pwd; - doc["ip"] = defaultIp; - doc["universe"] = universe; - - String jsonString; - serializeJson(doc, jsonString); - - request->send(200, "application/json", jsonString); }); + ESP.restart(); }); server.onRequestBody([](AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { if (request->url() == "/config" && request->method() == HTTP_PUT) { - Serial.printf("[REQUEST]\t%s\r\n", (const char *)data); - - StaticJsonDocument<256> doc; - deserializeJson(doc, data); - request->send(200); + onPutConfig(request, data, len, index, total); + ESP.restart(); } }); delay(1000); diff --git a/src/routes/config.cpp b/src/routes/config.cpp new file mode 100644 index 0000000..fe68a5a --- /dev/null +++ b/src/routes/config.cpp @@ -0,0 +1,163 @@ +#include "config.h" +#include +#include + +Preferences config; + +#pragma region Utility + +uint32_t parseIp(String str) +{ + const int size = 4; + + String ipStrings[size]; + uint8_t ipIndex = 0; + + for (int i = 0; i < str.length(); i++) + { + if (str[i] == '.') + { + ipIndex++; + continue; + } + ipStrings[ipIndex] += str[i]; + } + + String ip = ""; + for (int i = 0; i < size; i++) + { + String paddedString = ipStrings[i]; + while (paddedString.length() < 3) + { + paddedString = "0" + paddedString; + } + ip.concat(paddedString); + } + + Serial.println("ip string: " + ip); + return atoi(ip.c_str()); +} + +IpMethod parseIpMethod(uint8_t ipMethod) +{ + if (ipMethod > 0 || ipMethod < IP_METHOD_SIZE) + { + return static_cast(ipMethod); + } + + throw ::std::invalid_argument("Invalid IP method value" + ipMethod); +} + +Connection parseConnection(uint8_t connection) +{ + if (connection > 0 || connection < CONNECTION_SIZE) + { + return static_cast(connection); + } + + throw ::std::invalid_argument("Invalid connection value: " + connection); +} + +Direction parseDirection(uint8_t direction) +{ + if (direction > 0 || direction < DIRECTION_SIZE) + { + return static_cast(direction); + } + + throw ::std::invalid_argument("Invalid direction value: " + direction); +} + +#pragma endregion + +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); + + 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["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); + + config.end(); + + String jsonString; + serializeJson(doc, jsonString); + + request->send(200, "application/json", jsonString); +} + +void onPutConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) +{ + Serial.printf("[REQUEST]\t%s\r\n", (const char *)data); + + JsonDocument doc; + deserializeJson(doc, data); + + try + { + config.begin("dmx", false); + + IpMethod ipMethod = parseIpMethod(doc["ip-method"].as()); + config.putUInt("ip-method", ipMethod); + + if (ipMethod == Static) + { + IPAddress ipAddress; + ipAddress.fromString(doc["ip"].as()); + config.putUInt("ip", ipAddress); + + IPAddress subnet; + subnet.fromString(doc["subnet"].as()); + config.putUInt("subnet", subnet); + + IPAddress gateway; + gateway.fromString(doc["gateway"].as()); + config.putUInt("gateway", gateway); + } + + Connection connection = parseConnection(doc["connection"].as()); + config.putUInt("connection", connection); + + if (connection == WiFiSta || connection == WiFiAP) + { + config.putString("ssid", doc["ssid"].as()); + config.putString("password", doc["password"].as()); + } + + Direction direction1 = parseDirection(doc["direction-1"].as()); + config.putUInt("direction-1", direction1); + + Direction direction2 = parseDirection(doc["direction-2"].as()); + config.putUInt("direction-2", direction2); + + config.putUInt("universe-1", doc["universe-1"]); + config.putUInt("universe-2", doc["universe-2"]); + + config.end(); + + request->send(200); + } + catch (::std::invalid_argument &e) + { + config.end(); + request->send(400, "text/plain", e.what()); + } +} diff --git a/src/routes/config.h b/src/routes/config.h new file mode 100644 index 0000000..cc325ee --- /dev/null +++ b/src/routes/config.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +// #ifndef CONFIG_h +// #define CONFIG_h + +extern Preferences config; + +enum IpMethod +{ + Static, + DHCP +}; +const uint8_t IP_METHOD_SIZE = 2; + +enum Connection +{ + WiFiSta, + WiFiAP, + Ethernet +}; +const uint8_t CONNECTION_SIZE = 3; + +enum Direction +{ + Output, + Input +}; +const uint8_t DIRECTION_SIZE = 2; + +void onGetConfig(AsyncWebServerRequest *request); + +void onPutConfig(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total); + +// #endif \ No newline at end of file