diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..48580f7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "lib/ArtNet"] + path = lib/ArtNet + url = https://github.com/psxde/ArtNet.git +[submodule "lib/AsyncWebServer_ESP32_W5500"] + path = lib/AsyncWebServer_ESP32_W5500 + url = https://github.com/psxde/AsyncWebServer_ESP32_W5500.git diff --git a/lib/ArtNet b/lib/ArtNet new file mode 160000 index 0000000..5d9c42b --- /dev/null +++ b/lib/ArtNet @@ -0,0 +1 @@ +Subproject commit 5d9c42b531404ccfbcb14106d6312b03a166868a diff --git a/lib/AsyncWebServer_ESP32_W5500 b/lib/AsyncWebServer_ESP32_W5500 new file mode 160000 index 0000000..38de6ac --- /dev/null +++ b/lib/AsyncWebServer_ESP32_W5500 @@ -0,0 +1 @@ +Subproject commit 38de6ac248c7f270ca3b77ba38512ba39919aed8 diff --git a/platformio.ini b/platformio.ini index a372945..7caf3e0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,6 +13,4 @@ platform = espressif32 board = lolin_s2_mini framework = arduino lib_deps = - hideakitai/ArtNet @ ^0.8.0 bblanchon/ArduinoJson @ ^7.2.0 - me-no-dev/ESP Async WebServer diff --git a/src/ESPDMX.cpp b/src/ESPDMX.cpp index 5408807..d2e8985 100644 --- a/src/ESPDMX.cpp +++ b/src/ESPDMX.cpp @@ -7,11 +7,16 @@ // uint8_t dmxDataStores[MAX_IDS][DMXCHANNELS + 1]; // Set up the DMX-Protocol -void DMXESPSerial::init(int pinSend = 19, int pinRecv = -1) +void DMXESPSerial::init(int pinSend = DMX_DEFAULT_TX, int pinRecv = DMX_DEFAULT_RX, HardwareSerial& port = DMX_DEFAULT_PORT) { sendPin = pinSend; recvPin = pinRecv; - SERIALPORT.begin(DMXSPEED, DMXFORMAT, recvPin, sendPin); + _Serial = &port; + _Serial->begin(DMXSPEED, DMXFORMAT, recvPin, sendPin); + Serial.print("Init DMX with pins (TX/RX): "); + Serial.print(sendPin); + Serial.print("/"); + Serial.println(recvPin); pinMode(sendPin, OUTPUT); dmxStarted = true; } @@ -28,6 +33,14 @@ uint8_t DMXESPSerial::read(int channel) channel = DMXCHANNELS; return (dmxDataStore[channel]); } +uint8_t* DMXESPSerial::readAll() +{ + if (dmxStarted == false) + init(); + + return (&dmxDataStore[1]); +} + // Function to send DMX data void DMXESPSerial::write(int channel, uint8_t value) @@ -40,17 +53,13 @@ void DMXESPSerial::write(int channel, uint8_t value) channel = 1; if (channel > DMXCHANNELS) channel = DMXCHANNELS; - if (value < 0) - value = 0; - if (value > 255) - value = 255; dmxDataStore[channel] = value; } void DMXESPSerial::end() { - SERIALPORT.end(); + _Serial->end(); } // Function to update the DMX bus @@ -58,17 +67,17 @@ void DMXESPSerial::update() { // Send break digitalWrite(sendPin, HIGH); - SERIALPORT.begin(BREAKSPEED, BREAKFORMAT, recvPin, sendPin); - SERIALPORT.write(0); - SERIALPORT.flush(); + _Serial->begin(BREAKSPEED, BREAKFORMAT, recvPin, sendPin); + _Serial->write(0); + _Serial->flush(); delay(1); - SERIALPORT.end(); + _Serial->end(); // send data - SERIALPORT.begin(DMXSPEED, DMXFORMAT, recvPin, sendPin); + _Serial->begin(DMXSPEED, DMXFORMAT, recvPin, sendPin); digitalWrite(sendPin, LOW); - SERIALPORT.write(dmxDataStore, DMXCHANNELS); - SERIALPORT.flush(); + _Serial->write(dmxDataStore, DMXCHANNELS); + _Serial->flush(); delay(1); - SERIALPORT.end(); + _Serial->end(); } \ No newline at end of file diff --git a/src/ESPDMX.h b/src/ESPDMX.h index 04123ae..4c8e2a2 100644 --- a/src/ESPDMX.h +++ b/src/ESPDMX.h @@ -1,4 +1,5 @@ #include +#include #ifndef ESPDMX_h #define ESPDMX_h @@ -7,7 +8,9 @@ #define DMXFORMAT SERIAL_8N2 #define BREAKSPEED 83333 #define BREAKFORMAT SERIAL_8N1 -#define SERIALPORT Serial0 +#define DMX_DEFAULT_PORT Serial0 +#define DMX_DEFAULT_TX 21 +#define DMX_DEFAULT_RX 33 #define DMXCHANNELS 512 class DMXESPSerial @@ -15,14 +18,20 @@ class DMXESPSerial public: int sendPin; int recvPin; + HardwareSerial *port; bool dmxStarted; uint8_t dmxDataStore[DMXCHANNELS + 1]; - void init(int pinSend, int pinRecv); - uint8_t read(int Channel); + void init(int pinSend, int pinRecv, HardwareSerial& port); + uint8_t read(int channel); + uint8_t* readAll(); void write(int channel, uint8_t value); void update(); void end(); + +private: + // Member variables + HardwareSerial* _Serial; }; #endif diff --git a/src/main.cpp b/src/main.cpp index b81c6ab..931b0c0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,43 +1,139 @@ +#ifdef ESP32 +#include +#include +#elif defined(ESP8266) +#include +#include +#endif + +#include +//#include "w5500/esp32_w5500.h" +//#include + #include -// #include +#include + #include "ESPDMX.h" -#include +#include "SPI.h" + #include #include "routes/config.h" DMXESPSerial dmx1; DMXESPSerial dmx2; +// Button +#define PIN_LED 7 +#define PIN_BUTTON 5 + +uint8_t brightness_led = 20; +bool status_led; + +hw_timer_t * timer = NULL; //H/W timer defining (Pointer to the Structure) +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; +void IRAM_ATTR onTimer() { //Defining Inerrupt function with IRAM_ATTR for faster access + portENTER_CRITICAL_ISR(&timerMux); + status_led = !status_led; + if (!status_led) { + analogWrite(PIN_LED, brightness_led); + } else { + analogWrite(PIN_LED, 0); + } + portEXIT_CRITICAL_ISR(&timerMux); +} + +// Ethernet stuff +#define ETH_SCK 36 +#define ETH_SS 34 +#define ETH_MOSI 35 +#define ETH_MISO 37 +#define ETH_INT 38 +#define ETH_SPI_HOST SPI2_HOST +#define ETH_SPI_CLOCK_MHZ 25 +byte mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; + AsyncWebServer server(80); ArtnetWiFi artnet; -DMXESPSerial dmx; -const uint16_t size = 512; -uint8_t data[size]; +String broadcastIp; +uint8_t universe1; +uint8_t universe2; +Direction direction1; +Direction direction2; +//const uint16_t size = 512; +//uint8_t data[DMXCHANNELS]; + +void ledBlink(int ms) { + if(timer == NULL) { + timer = timerBegin(0, 80, true); // timer 0, prescalar: 80, UP counting + timerAttachInterrupt(timer, &onTimer, true); // Attach interrupt + } + if(ms == 0) { + timerAlarmDisable(timer); + analogWrite(PIN_LED, 0); + } else if(ms == 1) { + timerAlarmDisable(timer); + analogWrite(PIN_LED, brightness_led); + } else { + ms = ms*1000; + timerAlarmWrite(timer, ms, true); // Match value= 1000000 for 1 sec. delay. + timerAlarmEnable(timer); // Enable Timer with interrupt (Alarm Enable) + } +} void setup() { + // Get ETH mac + esp_read_mac(mac, ESP_MAC_ETH); + + // LED + analogWrite(PIN_LED, brightness_led); + delay(5000); + ledBlink(500); + + // Serial console Serial.begin(9600); + Serial.print("Start DMX-Interface"); + delay(1000); + Serial.println("..."); config.begin("dmx", true); - uint8_t universe1 = config.getUChar("universe-1", 1); - uint8_t universe2 = config.getUChar("universe-2", 1); + universe1 = config.getUChar("universe-1", 1); + universe2 = config.getUChar("universe-2", 1); - Direction direction1 = static_cast(config.getUInt("direction-1", 0)); - Direction direction2 = static_cast(config.getUInt("direction-2", 1)); + direction1 = static_cast(config.getUInt("direction-1", 0)); + direction2 = static_cast(config.getUInt("direction-2", 1)); + + Serial.print("Port A: Universe "); + Serial.print(universe1); + Serial.print(" "); + Serial.println((direction1==Input)?"DMX -> Art-Net":"Art-Net -> DMX"); + + Serial.print("Port B: Universe "); + Serial.print(universe2); + Serial.print(" "); + Serial.println((direction2==Input)?"DMX -> Art-Net":"Art-Net -> DMX"); Connection connection = static_cast(config.getUInt("connection", WiFiAP)); IpMethod ipMethod = static_cast(config.getUInt("ip-method"), Static); - String ssid = config.getString("ssid", "artnet"); - String pwd = config.getString("password", "mbgmbgmbg"); - IPAddress defaultIp(192, 168, 1, 201); + WiFi.macAddress(mac); + char hostname[30]; + snprintf(hostname, sizeof(hostname), "ChaosDMX-%02X%02X", mac[4], mac[5]); + Serial.print("Hostname: "); + Serial.println(hostname); + + String ssid = config.getString("ssid", hostname); + String pwd = config.getString("pwd", "mbgmbgmbg"); + + // 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, 255, 255, 0); + IPAddress defaultSubnet(255, 0, 0, 0); IPAddress subnet = config.getUInt("subnet", defaultSubnet); - IPAddress defaultGateway(192, 168, 1, 1); + IPAddress defaultGateway(2, 0, 0, 1); IPAddress gateway = config.getUInt("gateway", defaultGateway); config.end(); @@ -45,10 +141,20 @@ void setup() // wait for serial monitor delay(5000); + // Button + pinMode(PIN_BUTTON,INPUT_PULLUP); + if(digitalRead(PIN_BUTTON) == LOW){ + ledBlink(100); + delay(2000); + Serial.println("Start AP-Mode"); + connection = WiFiAP; + } + switch (connection) { case WiFiSta: - Serial.println("Initialize as WiFi-Station"); + Serial.println("Initialize as WiFi Station"); + WiFi.setHostname(hostname); WiFi.begin(ssid, pwd); if (ipMethod == Static) { @@ -59,44 +165,103 @@ void setup() Serial.print("."); delay(500); } + broadcastIp = String(WiFi.broadcastIP().toString().c_str()); Serial.println(""); Serial.print("WiFi connected, IP = "); Serial.println(WiFi.localIP()); + Serial.print("MAC address: "); + Serial.println(WiFi.macAddress()); break; - 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: { + Serial.println("Initialize as ETH"); + ESP32_W5500_onEvent(); - case Ethernet: - Serial.print("Not yet implemented"); + 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); + } + broadcastIp = ETH.broadcastIP().toString(); + + 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; } - - delay(500); + default: + Serial.println("Initialize as WiFi AccessPoint"); + WiFi.softAPsetHostname(hostname); + WiFi.softAP(ssid, pwd); + // AP always with DHCP + //WiFi.softAPConfig(ip, gateway, subnet); + broadcastIp = WiFi.softAPBroadcastIP().toString(); + Serial.print("WiFi AP enabled, IP = "); + Serial.println(WiFi.softAPIP()); + Serial.print("MAC address: "); + Serial.println(WiFi.softAPmacAddress()); + break; + } + + // Initialize DMX ports + Serial.println("Initialize DMX..."); + dmx1.init(21, 33, Serial0); + dmx2.init(17, 18, Serial1); // Initialize Art-Net + Serial.println("Initialize Art-Net..."); artnet.begin(); - // Initialize DMX ports - dmx1.init(19, -1); - // if Artnet packet comes to this universe, this function is called - artnet.subscribeArtDmxUniverse(universe1, [&](const uint8_t *data, uint16_t size, const ArtDmxMetadata &metadata, const ArtNetRemoteInfo &remote) - { - for (size_t i = 0; i < size; ++i) - { - dmx1.write((i + 1), data[i]); - } + if (direction1 == Output) { + artnet.subscribeArtDmxUniverse(universe1, [&](const uint8_t *data, uint16_t size, const ArtDmxMetadata &metadata, const ArtNetRemoteInfo &remote) + { + for (size_t i = 0; i < size; ++i) + { + dmx1.write((i + 1), data[i]); + } + dmx1.update(); + }); + } - dmx1.update(); }); + if (direction2 == Output) { + artnet.subscribeArtDmxUniverse(universe2, [&](const uint8_t *data, uint16_t size, const ArtDmxMetadata &metadata, const ArtNetRemoteInfo &remote) + { + for (size_t i = 0; i < size; ++i) + { + dmx2.write((i + 1), data[i]); + } + + dmx2.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) {}); + //artnet.subscribeArtDmx([&](const uint8_t *data, uint16_t size, const ArtDmxMetadata &metadata, const ArtNetRemoteInfo &remote) {}); if (!SPIFFS.begin(true)) { @@ -129,9 +294,23 @@ void setup() delay(1000); server.begin(); Serial.println("Server started!"); + + ledBlink(1); } void loop() { - artnet.parse(); // check if artnet packet has come and execute callback + // check if artnet packet has come and execute callback + artnet.parse(); + + // Receive Callback/INT currently not implemented + /*if (direction1 == Input) { + artnet.setArtDmxData(dmx1.readAll(), DMXCHANNELS); + artnet.streamArtDmxTo(broadcastIp, universe1); + } + + if (direction2 == Input) { + artnet.setArtDmxData(dmx2.readAll(), DMXCHANNELS); + artnet.streamArtDmxTo(broadcastIp, universe2); + }*/ } diff --git a/src/routes/config.h b/src/routes/config.h index cc325ee..2a839b8 100644 --- a/src/routes/config.h +++ b/src/routes/config.h @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include