lasselukkari / DuinoDCX

WiFi remote controller for the Behringer Ultradrive.
MIT License
91 stars 11 forks source link

ESP 32 + Wiznet W5500 #246

Open g-philipp opened 3 years ago

g-philipp commented 3 years ago

Ethernet with the Wiznet W5500 Ethernet Board

set up a static ip address and select ethernet instead of wifi in the webinterface

image image

lasselukkari commented 3 years ago

The webserver library supports any class derived from the Stream so the Wiznet W5500 should work. I personally have not ever used the ESP32 + Wiznet W5500 combination but the library works fine with other arduino boards and ethernet shields.

See the ethernet example here: https://github.com/lasselukkari/aWOT/blob/master/examples/Ethernet/Ethernet.ino

g-philipp commented 3 years ago

Is it much effort to programm the ethernet function to exist in the settings and work?

lasselukkari commented 3 years ago

Compared to the WiFi connection management with credential the Ethernet is actually simpler.

g-philipp commented 3 years ago

well ive tried a couple of things the last days and ive got it kinda working, my problems are that first it loses connection all the time: image and second, when I try to connect via html I only see the follwing: image

I of course had to modify the code and I think that the issue is cause by deleting some stuff... The code now looks like this: ``

include

include

include

include

include

include "local_config.h"

include "aWOT.h"

include "StaticFiles.h"

include "Ultradrive.h"

define VERSION "v0.0.39"

define BUILD_DATE DATE " " TIME

define DEFAULT_AUTH "Basic RENYMjQ5NjpVbHRyYWRyaXZl" // DCX2496:Ultradrive in base64

define DEFAULT_SOFT_AP_SSID "DCX2496"

define DEFAULT_SOFT_AP_PASSWORD "Ultradrive"

define DEFAULT_MDNS_NAME "ultradrive"

define DEFAULT_FLOW_CONTROL false

define DEFAULT_AUTO_DISABLE_AP false

define RTS_PIN 21

define CTS_PIN 22

define RESET_PIN 13

define WIFI_HOST_NAME "ultradrive"

define AUTH_KEY "auth"

define SOFT_AP_PASSWORD_KEY "apPassword"

define SOFT_AP_SSID_KEY "apSsid"

define MDNS_HOST_KEY "mdnsHost"

define FLOW_CONTROL_KEY "flowControl"

define AUTO_DISABLE_AP_KEY "autoDisableAP"

define AUTH_BUFFER_LENGHT 200

define MAX_DEVICES 16

define POST_PARAM_SSID_KEY "ssid"

define POST_PARAM_PASSWORD_KEY "password"

define SSID_MAX_LENGTH 33

define PASSWORD_MAX_LENGHT 65

define CONNECTION_TIMEOUT 10000

define RECONNECT_INTERVAL 20000

define BASIC_AUTH_LENGTH 200

define SOFT_AP_SSID_LENGTH 65

define SOFT_AP_PASSWORD_LENGTH 65

define MDDNS_NAME_LENGTH 65

const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message. byte packetBuffer[NTP_PACKET_SIZE]; // Buffer for both incoming and outgoing packets.

Preferences preferences; EthernetServer server(80); HardwareSerial UltradriveSerial(2); Ultradrive deviceManager(&UltradriveSerial, RTS_PIN, CTS_PIN); Application app; Router apiRouter("/api");

char basicAuth[BASIC_AUTH_LENGTH]; char softApSsid[SOFT_AP_SSID_LENGTH]; char softApPassword[SOFT_AP_PASSWORD_LENGTH]; char mdnsName[MDDNS_NAME_LENGTH]; bool flowControl; bool autoDisableAP;

char authBuffer[AUTH_BUFFER_LENGHT]; char ssidBuffer[SSID_MAX_LENGTH]; char passwordBuffer[PASSWORD_MAX_LENGHT]; unsigned long lastReconnect; bool shouldRestart = false; unsigned long requestStart;

void WizReset() { Serial.print("Resetting Wiz W5500 Ethernet Board... "); pinMode(RESET_P, OUTPUT); digitalWrite(RESET_P, HIGH); delay(250); digitalWrite(RESET_P, LOW); delay(50); digitalWrite(RESET_P, HIGH); delay(350); Serial.println("Done."); }

void logRequestStart(Request &req, Response &res) { unsigned long now = millis(); Serial.print(now); Serial.print(": HTTP ");

switch (req.method()) { case Request::GET: { Serial.print("GET "); break; } case Request::POST: { Serial.print("POST "); break; } case Request::PUT: { Serial.print("PUT "); break; } case Request::PATCH: { Serial.print("GET "); break; } case Request::DELETE: { Serial.print("DELETE "); break; } default: {} }

Serial.print(req.path()); Serial.print(" "); requestStart = micros(); }

void logRequestEnd(Request &req, Response &res) { float delta = (micros() - requestStart) / 1000.0; Serial.print(res.bytesSent()); Serial.print(" b "); Serial.print(delta); Serial.println(" ms"); }

void auth(Request &req, Response &res) { char * authHeader = req.get("Authorization");

if (strcmp(authHeader, basicAuth) != 0) { res.set("WWW-Authenticate", "Basic realm=\"Ultradrive\""); res.sendStatus(401); res.end(); } }

void update(Request &req, Response &res) { int contentLength = req.left();

if (!Update.begin(contentLength)) { return res.sendStatus(500); }

if (Update.writeStream(req) != contentLength) { return res.sendStatus(500); }

if (!Update.end()) { return res.sendStatus(500); }

if (!Update.isFinished()) { return res.sendStatus(500); }

shouldRestart = true; res.sendStatus(204); }

void getNetworks(Request &req, Response &res) { Serial.print(""); }

void getSettings(Request &req, Response &res) { res.set("Content-Type", "application/json"); res.print("{");

res.print("\"" SOFT_AP_SSID_KEY "\":"); res.print("\""); res.print(softApSsid); res.print("\", ");

res.print("\"" SOFT_AP_PASSWORD_KEY "\":"); res.print("\""); res.print(softApPassword); res.print("\", ");

res.print("\"" AUTH_KEY "\":"); res.print("\""); res.print(basicAuth); res.print("\", ");

res.print("\"" MDNS_HOST_KEY "\":"); res.print("\""); res.print(mdnsName); res.print("\", ");

res.print("\"" FLOW_CONTROL_KEY "\":"); res.print("\""); res.print(flowControl); res.print("\", ");

res.print("\"" AUTO_DISABLE_AP_KEY "\":"); res.print("\""); res.print(autoDisableAP); res.print("\"");

res.print("}"); }

void getVersion(Request &req, Response &res) { res.set("Content-Type", "application/json"); res.print("{");

res.print("\"version\":"); res.print("\""); res.print(VERSION); res.print("\", ");

res.print("\"buildDate\":"); res.print("\""); res.print(BUILD_DATE); res.print("\"");

res.print("}"); }

void updateSettings(Request &req, Response &res) { char name[15]; char value[BASIC_AUTH_LENGTH]; preferences.begin("duinodcx", false);

while (req.left()) { req.form(name, 15, value, BASIC_AUTH_LENGTH); if (strcmp(name, AUTH_KEY) == 0) { preferences.putString(AUTH_KEY, value); } else if (strcmp(name, SOFT_AP_SSID_KEY) == 0) { preferences.putString(SOFT_AP_SSID_KEY, value); } else if (strcmp(name, SOFT_AP_PASSWORD_KEY) == 0) { preferences.putString(SOFT_AP_PASSWORD_KEY, value); } else if (strcmp(name, MDNS_HOST_KEY) == 0) { preferences.putString(MDNS_HOST_KEY, value); } else if (strcmp(name, FLOW_CONTROL_KEY) == 0) { bool isEnabled = (value[0] != '0'); preferences.putBool(FLOW_CONTROL_KEY, isEnabled); } else if (strcmp(name, AUTO_DISABLE_AP_KEY) == 0) { bool isEnabled = (value[0] != '0'); preferences.putBool(AUTO_DISABLE_AP_KEY, isEnabled); } else { preferences.end(); return res.sendStatus(400); } }

preferences.end(); res.sendStatus(204);

shouldRestart = true; }

void getConnection(Request &req, Response &res) { res.set("Content-Type", "application/json"); res.print("{");

res.print("\"current\":"); res.print("\""); res.print("Ethernet"); res.print("\",");

res.print("\"ip\":"); res.print("\""); res.print(Ethernet.localIP()); res.print("\"");

res.print("}"); }

void updateConnection(Request &req, Response &res) { char name[10]; char value[PASSWORD_MAX_LENGHT]; unsigned long timeout;

while (req.left()) { req.form(name, 10, value, PASSWORD_MAX_LENGHT); if (strcmp(name, POST_PARAM_SSID_KEY) == 0) { strcpy (ssidBuffer, value); } else if (strcmp(name, POST_PARAM_PASSWORD_KEY) == 0) { strcpy (passwordBuffer, value); } else { return res.sendStatus(400); } }

timeout = millis() + CONNECTION_TIMEOUT; }

void removeConnection(Request &req, Response &res) { Serial.print(""); }

void getDevice(Request &req, Response &res) { res.set("Content-Type", "application/binary"); deviceManager.writeDevice(&res); }

void getStatus(Request &req, Response &res) { res.set("Content-Type", "application/binary"); deviceManager.writeDeviceStatus(&res); }

void selectDevice(Request &req, Response &res) { byte buffer[100];

if (!req.readBytes(buffer, 100)) { return res.sendStatus(400); }

int id = atoi((const char *)buffer); deviceManager.setSelected(id); }

void getState(Request &req, Response &res) { res.set("Content-Type", "application/binary"); res.write(deviceManager.getSelected()); deviceManager.writeDevice(&res); deviceManager.writeDevices(&res); }

void createDirectCommand(Request &req, Response &res) { while (req.left()) { deviceManager.processOutgoing(&req); }

res.sendStatus(204); }

void processWebServer() { EthernetClient client = server.available(); app.process(&client); }

void restartIfNeeded() { if (shouldRestart) { delay(5000); ESP.restart(); } }

void loadPreferences() { pinMode(RESET_PIN, INPUT_PULLUP);

preferences.begin("duinodcx", false);

unsigned long now = millis(); while (!digitalRead(RESET_PIN)) { if (millis() - now > 1000) { preferences.clear(); break; } }

if (!preferences.getString(AUTH_KEY, basicAuth, BASIC_AUTH_LENGTH)) { strcpy(basicAuth, DEFAULT_AUTH); }

if (!preferences.getString(SOFT_AP_SSID_KEY, softApSsid, SOFT_AP_SSID_LENGTH)) { strcpy(softApSsid, DEFAULT_SOFT_AP_SSID); }

if (!preferences.getString(SOFT_AP_PASSWORD_KEY, softApPassword, SOFT_AP_PASSWORD_LENGTH)) { strcpy(softApPassword, DEFAULT_SOFT_AP_PASSWORD); }

if (!preferences.getString(MDNS_HOST_KEY, mdnsName, MDDNS_NAME_LENGTH)) { strcpy(mdnsName, DEFAULT_MDNS_NAME); }

flowControl = preferences.getBool(FLOW_CONTROL_KEY, DEFAULT_FLOW_CONTROL);

autoDisableAP = preferences.getBool(AUTO_DISABLE_AP_KEY, DEFAULT_AUTO_DISABLE_AP);

preferences.end(); }

void setupHttpServer() { app.header("Authorization", authBuffer, AUTH_BUFFER_LENGHT);

apiRouter.get("/state", &getState); apiRouter.get("/status", &getStatus); apiRouter.put("/selected", &selectDevice); apiRouter.post("/commands", &createDirectCommand); apiRouter.get("/connection", &getConnection); apiRouter.patch("/connection", &updateConnection); apiRouter.del("/connection", &removeConnection); apiRouter.get("/settings", &getSettings); apiRouter.patch("/settings", &updateSettings); apiRouter.get("/networks", &getNetworks); apiRouter.post("/update", &update); apiRouter.get("/version", &getVersion);

app.use(&logRequestStart); app.use(&auth); app.route(&apiRouter); app.route(staticFiles()); app.use(&logRequestEnd); }

void setup() { Ethernet.init(5); // GPIO5 on the ESP32. WizReset(); Serial.begin(38400); UltradriveSerial.begin(38400);

loadPreferences();

if (flowControl) { deviceManager.enableFlowControl(true); pinMode(RTS_PIN, OUTPUT); pinMode(CTS_PIN, INPUT_PULLUP); }

Ethernet.begin(eth_MAC, eth_IP, eth_DNS, eth_GW, eth_MASK); delay(200); Ethernet.begin(eth_MAC, eth_IP); server.begin();

setupHttpServer();

MDNS.begin(mdnsName); MDNS.addService("http", "tcp", 80); }

void loop() { unsigned long now = millis(); deviceManager.processIncoming(now); processWebServer(); restartIfNeeded(); } ``

The new file "local_config.h" looks like the following:

`` /*

/*

define RESET_P 26 // Tie the Wiz820io/W5500 reset pin to ESP32 GPIO26 pin.

const uint16_t localPort = 55432; // Local port for UDP packets.

/*

const uint8_t SLEEP_SECS = 15; // Number of seconds to sleep between queries to the time // server. Please don't set this any lower than 10 unless // timeServer[] is a local NTP server on -your- network. ``

Its my first time using an ethernet board and i dont fully understand your code so forgive me...

Hope you can help me

~Philipp

lasselukkari commented 3 years ago

Hi. I have never tested ESP 32 + Wiznet W5500 combination myself. With the ESP32 WiFi the WifiClient destructor stops the client automatically when it goes away from the scope. The EthernetClient in Ethernet2 library does not define a destructor.

So you will need to at least change the

void processWebServer() {
  EthernetClient client = server.available();
  app.process(&client);
}

to

void processWebServer() {
  EthernetClient client = server.available();
  if (client.connected()) {
    app.process(&client);
    client.stop();  // add the client stop here
  }
}
lasselukkari commented 3 years ago

I have the same ethernet module somewhere. I could try it out tomorrow if I find it.

g-philipp commented 3 years ago

with the change in "processWebServer()" its working! But it would be awesome if you could check the code, you may implement this into your code? Merry Christmas!

Edit: Ive got the problem that sometimes it kicks me out of the settings and is searching again for some time until it picks back up, do you know something about this? Could it be a problem with my cable? its just a normal rs232 to rs232 cable but i switched and soldered two pins and made it male2male instead of fm2m but the shielding is connected to both jacks....

lasselukkari commented 3 years ago

I'm glad you got the Ethernet connection working! It sounds like your RS232 connection is not reliable for some reason. This is not normal and I have not seen the connection to behave like that.

I would try another set of cabling first. If that does not work I would try another MAX3232 module. I have read from old forum discussions that the chinese fake copies can be unreliable especially at higher baud rates. You can also make sure it's not the changes that you have made by switching back to the wifi code.

Feel free to create a pull request. I can add the support for the Ethernet to codebase but I need to find my Ethernet module first. I ordered a new one but it will take a couple of weeks to arrive.

lasselukkari commented 3 years ago

I got a new ethernet module today. Let's see how this goes.

lasselukkari commented 3 years ago

Is there a reason you are using the Ethernet2 library? The standard one seems to work as well.

g-philipp commented 3 years ago

The UDP example worked for me but the webserver didnt, also with many other libs i had the same problem, but with the Ethernet2 (there are different Eth2 libs, only one worked for me) ist worked as it should

lasselukkari commented 3 years ago

And did you get the RS232 working reliably? I bought a module and board that does not expose any extra pins. If you got the rs232 connection working I would not have to test the changes together with the rs232 module. I can hook up the module to another board but it would be more convenient if I do not have to.

g-philipp commented 3 years ago

No sadly i didnt get it working reliably but I found out, that sometime (only sometimes) when the music stops, it "catches itself" and I can use the webinterface like normal, but then if the music starts, it is endless looping with loading.

It is very strange :(

lasselukkari commented 3 years ago

OK. I will let you know if I get it working properly.

The ESP32 core has a non standard definition for the server that is not compatible with the rest of the Arduino ecosystem. If you want to use the normal Ethernet library you have to do a simple modification to the ESP32 core files.

You need to change this: https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/Server.h#L28

from

virtual void begin(int port) =0;

to

virtual void begin() =0;

Unfortunately you can not compile the WiFiServer lib anymore with this change until you change it back. The result is that the WiFiServer and EthernetServer can not be used simultaneously. Until the esp32 core gets their shit together I will add this functionality to be configured with a feature flag and there will not be a user interface for setting the static ip.

lasselukkari commented 3 years ago

BTW have you tried or considered the lan8710/lan8720 for the Ethernet. It seems to be supported out of the box with the Arduino framework. I'm new to Ethernet options with ESP32 and I'm trying to figure out the pros and cons between these two solutions. The Wiznet chips uses SPI and this may be at least in theory blocking the other IO pins under heavy use. This could explain the problems you are seeing with RS232 port stability. I ordered this board for evaluation https://www.olimex.com/Products/IoT/ESP32/ESP32-GATEWAY/. Not just because of this project but rather to verify the compatibility with the web server library I have been developing.

g-philipp commented 3 years ago

Hey, its been a long time since my last post. In the meantime i used the ethernet board and i think the only problem is still that the serial converter sometimes looses connection. But i will not change it. Have you tried some testing with your ethernet board?