earlephilhower / arduino-pico

Raspberry Pi Pico Arduino core, for all RP2040 and RP2350 boards
GNU Lesser General Public License v2.1
2.03k stars 423 forks source link

W6100 implementation based on W5500 driver #2346

Closed veecle-stefan closed 2 months ago

veecle-stefan commented 2 months ago

This is a first WIP for supporting the Wiznet W6100 Ethernet chip in the lwIP_Ethernet.

Preliminary Testing & Known Bugs

Known Bugs

earlephilhower commented 2 months ago

...As of now, both (lwIP_5500 and this lwIP_W6100) cannot run the libraries/WebServer/examples/AdvancedWebServer/AdvancedWebServer.ino due to default stack limits (crashes after few requests because the stack grows too deep...

This is kind of news to me because the default test I run is the AdvancedWebServer (but I normally just run that graph on F5-reload on a few PCs in the house for an hour or so, but if there's some other page/CGI being used to cause the crash it might have been missed). I have a nice W5100-EVB supplied by WizNet, but I do have some AX no-name PCBs that I wire up for the 5500 and ENC chips. Will give it a go. Maybe something got broken recently (or I never tested some special situation)...

I do know MDNS is kind of a bad boy because it does mallocs from inside an IRQ so maybe that's where things bomb? Do you have a stack trace?

veecle-stefan commented 2 months ago

I just checked the pure AdvancedWebServer example again, and you're right: It doesn't crash. But I found the culprit: In my example, I added #include <FreeRTOS.h> as well. Nothing else - I don't even use any FreeRTOS functions.

This is the stack trace (after only 3 HTTP requests it crahes)

#3  0x100075c4 in xQueueTakeMutexRecursive (xMutex=0x20013618, xTicksToWait=4294967295) at /Users/stefan/.platformio/packages/framework-arduinopico/libraries/FreeRTOS/src/../lib/FreeRTOS-Kernel/queue.c:837
#4  0x1000671e in __freertos_recursive_mutex_take (mtx=<optimized out>) at /Users/stefan/.platformio/packages/framework-arduinopico/libraries/FreeRTOS/src/variantHooks.cpp:80
#5  0x100119f6 in __retarget_lock_acquire_recursive (lock=<optimized out>) at /Users/stefan/.platformio/packages/framework-arduinopico/cores/rp2040/lock.cpp:150
#6  0x100217c8 in __malloc_lock (ptr=ptr@entry=0x20013c8c) at /workdir/repo/newlib/newlib/libc/stdlib/mlock.c:44
#7  0x10021400 in _malloc_r (reent_ptr=0x20013c8c, bytes=bytes@entry=52) at /workdir/repo/newlib/newlib/libc/stdlib/mallocr.c:2362
#8  0x1002137e in malloc (nbytes=52) at /workdir/repo/newlib/newlib/libc/stdlib/malloc.c:164
#9  0x100126d2 in __wrap_malloc (size=52) at /Users/stefan/.platformio/packages/framework-arduinopico/cores/rp2040/malloc-lock.cpp:30
#10 0x10014666 in operator new (n=n@entry=52) at /home/earle/Arduino/hardware/pico/rp2040/pico-sdk/src/rp2_common/pico_cxx_options/new_delete.cpp:15
#11 0x1000519c in WiFiServer::_accept (this=0x200053dc <server+204>, apcb=0x2000eecc <memp_memory_TCP_PCB_base+656>, err=<optimized out>) at /Users/stefan/.platformio/packages/framework-arduinopico/libraries/WiFi/src/WiFiServer.cpp:197
#12 0x100051c2 in WiFiServer::_s_accept (arg=<optimized out>, newpcb=<optimized out>, err=<optimized out>) at /Users/stefan/.platformio/packages/framework-arduinopico/libraries/WiFi/src/WiFiServer.cpp:218
#13 0x1001be44 in tcp_process (pcb=0x2000eecc <memp_memory_TCP_PCB_base+656>) at /home/earle/Arduino/hardware/pico/rp2040/pico-sdk/lib/lwip/src/core/tcp_in.c:956
#14 tcp_input (p=p@entry=0x2000f2f4 <ram_heap+9>, inp=inp@entry=0x20004c08 <eth+28>) at /home/earle/Arduino/hardware/pico/rp2040/pico-sdk/lib/lwip/src/core/tcp_in.c:438
#15 0x1002023e in ip4_input (p=p@entry=0x2000f2f4 <ram_heap+9>, inp=inp@entry=0x20004c08 <eth+28>) at /home/earle/Arduino/hardware/pico/rp2040/pico-sdk/lib/lwip/src/core/ipv4/ip4.c:743
#16 0x1002064c in ethernet_input (p=0x2000f2f4 <ram_heap+9>, netif=0x20004c08 <eth+28>) at /home/earle/Arduino/hardware/pico/rp2040/pico-sdk/lib/lwip/src/netif/ethernet.c:186
#17 0x10003c26 in LwipIntfDev<Wiznet5500>::handlePackets (this=this@entry=0x20004bec <eth>) at /Users/stefan/.platformio/packages/framework-arduinopico/libraries/lwIP_Ethernet/src/LwipIntfDev.h:586
#18 0x10003c72 in LwipIntfDev<Wiznet5500>::_irq (param=0x20004bec <eth>) at /Users/stefan/.platformio/packages/framework-arduinopico/libraries/lwIP_Ethernet/src/LwipIntfDev.h:450
#19 0x10012902 in CBInfo::callback (this=this@entry=0x20041f54) at /Users/stefan/.platformio/packages/framework-arduinopico/cores/rp2040/wiring_private.cpp:60
#20 0x100129f2 in _gpioInterruptDispatcher (gpio=<optimized out>, events=<optimized out>) at /Users/stefan/.platformio/packages/framework-arduinopico/cores/rp2040/wiring_private.cpp:85
#21 0x1001331c in gpio_default_irq_handler () at /home/earle/Arduino/hardware/pico/rp2040/pico-sdk/src/rp2_common/hardware_gpio/gpio.c:165
#22 0x20000f70 in irq_handler_chain_slots ()
Backtrace stopped: previous frame identical to this frame (corrupt stack?)

The slightly modified example (to make it work with W5500):

#include <Arduino.h>
#include <W5500lwIP.h>
#include <EthernetCompat.h>
#include <SPI.h>
#include <WebServer.h>
#include <LEAmDNS.h>
#include <StreamString.h>
#include <FreeRTOS.h>

//
ArduinoEthernet<Wiznet5500> eth(17, SPI, 21);

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01};
IPAddress ip(10, 42, 42, 6);

WebServer server(80);

const int led = LED_BUILTIN;

void handleRoot()
{
  static int cnt = 0;
  digitalWrite(led, 1);
  int sec = millis() / 1000;
  int hr = sec / 3600;
  int min = (sec / 60) % 60;
  sec = sec % 60;

  StreamString temp;
  temp.reserve(500); // Preallocate a large chunk to avoid memory fragmentation
  temp.printf("<html>\
  <head>\
    <meta http-equiv='refresh' content='5'/>\
    <title>" BOARD_NAME " Demo</title>\
    <style>\
      body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
    </style>\
  </head>\
  <body>\
    <h1>Hello from the " BOARD_NAME "!</h1>\
    <p>Uptime: %02d:%02d:%02d</p>\
    <p>Free Memory: %d</p>\
    <p>Page Count: %d</p>\
    <img src=\"/test.svg\" />\
  </body>\
</html>",
              hr, min, sec, rp2040.getFreeHeap(), ++cnt);
  server.send(200, "text/html", temp);
  digitalWrite(led, 0);
}

void handleNotFound()
{
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";

  for (uint8_t i = 0; i < server.args(); i++)
  {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }

  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

void drawGraph()
{
  String out;
  out.reserve(2600);
  char temp[70];
  out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
  out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
  out += "<g stroke=\"black\">\n";
  int y = rand() % 130;
  for (int x = 10; x < 390; x += 10)
  {
    int y2 = rand() % 130;
    sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
    out += temp;
    y = y2;
  }
  out += "</g>\n</svg>\n";

  server.send(200, "image/svg+xml", out);
}

void setup(void)
{
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  Serial.begin(115200);

  // Set up SPI pinout to match your HW
  SPI.setRX(16);
  SPI.setCS(17);
  SPI.setSCK(18);
  SPI.setTX(19);

  // Start the Ethernet port
  if (!eth.begin(mac, ip))
  {
    Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
    while (1)
    {
      delay(1000);
    }
  }

  // Wait for connection
  while (eth.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.print("IP address: ");
  Serial.println(eth.localIP());

  if (MDNS.begin("picow"))
  {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);
  server.on("/test.svg", drawGraph);
  server.on("/inline", []()
            { server.send(200, "text/plain", "this works as well"); });
  server.onNotFound(handleNotFound);
  server.begin();
  Serial.println("HTTP server started");
}

void loop(void)
{
  server.handleClient();
  MDNS.update();

}

In my using PIO with platformio.ini like so:

[env]
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
platform_packages = framework-arduinopico@https://github.com/earlephilhower/arduino-pico.git
board = pico
board_build.core = earlephilhower
board_build.filesystem_size = 0.5m
framework = arduino
earlephilhower commented 2 months ago

Yeah, that's calling malloc from within an IRQ context. Not stack related, just FreeRTOS complaining that a mutex is being grabbed from an IRQ-level. So known restriction and not related to your PR.