EthernetWebServer for STM32 boards using built-in LAN8742A, LAN8720 or shields

EthernetWebServer_STM32 library

How To Install Using Arduino Library Manager

This library currently supports

  1. STM32 boards with built-in Ethernet such as :
  1. STM32 boards (with 64+K Flash) running EMC28J60 shields

List of supported STM32 Boards
This library currently supports

  1. STM32 boards with built-in Ethernet such as :
  • Nucleo-144 (F429ZI, F767ZI)
  • Discovery (STM32F746G-DISCOVERY)
  • All STM32 Boards with Built-in Ethernet
  1. STM32 boards (with 64+K Flash) running EMC28J60 shields
  • Nucleo-144
  • Nucleo-64
  • Discovery
  • STM32MP1
  • Generic STM32F1 (with 64+K Flash): C8 and up
  • Generic STM32F4
  • STM32L0
  • LoRa boards
  • 3-D printer boards
  • Generic Flight Controllers
  • Midatronics boards

and these boards are not supported:

  • Nucleo-32 (small Flash/memory)
  • Eval (no Serial, just need to redefine in sketch, library and UIPEthernet)
  • Generic STM32F0 (small Flash/memory)
  • Generic STM32F1 (with <64K Flash): C6
  • Generic STM32F3 : no HardwareSPI.h
  • Electronics Speed Controllers (small Flash/memory)

This is simple yet complete WebServer library for STM32 boards running built-in Ethernet (Nucleo-144, Discovery) or EMC28J60 Ethernet shields. The functions are similar and compatible to ESP8266/ESP32 WebServer libraries to make life much easier to port sketches from ESP8266/ESP32.

The library supports

  1. HTTP Server and Client
  2. HTTP GET and POST requests, provides argument parsing, handles one client at a time.

Library is based on and modified from :Ivan Grokhotkov's ESP8266WebServer

The EthernetWebServer class found in EthernetWebServer.h header, is a simple web server that knows how to handle HTTP requests such as GET and POST and can only support one simultaneous client.

Sample Code

/*
 * Currently support 
 * 1) STM32 boards with built-in Ethernet (to use USE_BUILTIN_ETHERNET = true) such as :
 *    - Nucleo-144 (F429ZI, F767ZI)
 *    - Discovery (STM32F746G-DISCOVERY)
 * 2) STM32 boards (with 64+K Flash) running EMC28J60 shields (to use USE_BUILTIN_ETHERNET = false)
 * 
 */

#define USE_BUILTIN_ETHERNET   false    //true

#include <EthernetWebServer_STM32.h>

// Enter a MAC address and IP address for your controller below.

byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};

// Select the IP address according to your local network
IPAddress ip(192, 168, 2, 200);

EthernetWebServer server(80);

const int led = 13;

const String postForms = 
"<html>\
<head>\
<title>EthernetWebServer POST handling</title>\
<style>\
body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
</style>\
</head>\
<body>\
<h1>POST plain text to /postplain/</h1>
\
<form method=\"post\" enctype=\"text/plain\" action=\"/postplain/\">\
<input type=\"text\" name=\'{\"hello\": \"world\", \"trash\": \"\' value=\'\"}\'>
\
<input type=\"submit\" value=\"Submit\">\
</form>\
<h1>POST form data to /postform/</h1>
\
<form method=\"post\" enctype=\"application/x-www-form-urlencoded\" action=\"/postform/\">\
<input type=\"text\" name=\"hello\" value=\"world\">
\
<input type=\"submit\" value=\"Submit\">\
</form>\
</body>\
</html>";

void handleRoot() 
{
  digitalWrite(led, 1);
  server.send(200, "text/html", postForms);
  digitalWrite(led, 0);
}

void handlePlain() 
{
  if (server.method() != HTTP_POST) 
  {
    digitalWrite(led, 1);
    server.send(405, "text/plain", "Method Not Allowed");
    digitalWrite(led, 0);
  } else 
  {
    digitalWrite(led, 1);
    server.send(200, "text/plain", "POST body was:\n" + server.arg("plain"));
    digitalWrite(led, 0);
  }
}

void handleForm() 
{
  if (server.method() != HTTP_POST) 
  {
    digitalWrite(led, 1);
    server.send(405, "text/plain", "Method Not Allowed");
    digitalWrite(led, 0);
  } 
  else 
  {
    digitalWrite(led, 1);
    String message = "POST form was:\n";
    for (uint8_t i = 0; i < server.args(); i++) 
    {
      message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
    }
    server.send(200, "text/plain", message);
    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 setup(void) 
{
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  
  Serial.begin(115200);
  delay(1000);
  Serial.println("\nStarting POSTServer");

  // start the ethernet connection and the server:
  Ethernet.begin(mac, ip);
  
  server.on("/", handleRoot);

  server.on("/postplain/", handlePlain);

  server.on("/postform/", handleForm);

  server.onNotFound(handleNotFound);

  server.begin();
  
  Serial.print(F("HTTP EthernetWebServer started @ IP : "));
  Serial.println(Ethernet.localIP());
}

void loop(void) 
{
  server.handleClient();
}

New in Version v1.0.1

  1. Add support to popular W5x00 Ethernet shields to all STM32 boards having 64+K bytes Flash.

New in Version v1.0.2

  1. Remove dependency on Functional-Vlpp library

  2. Enhance examples, fix indentation and update README.md

New in v1.0.3

  1. Fix bug not closing client and releasing socket.
  2. Merge new features from latest ESP8266WebServer
  3. Add and enhance examples.
  4. Add dependency to Functional-VLPP library back.

Major Release v1.0.5

  1. Add support to new EthernetENC Library for ENC28J60.
  2. Add support to Ethernet2, Ethernet3 and EthernetLarge libraries on top of Ethernet Library
  3. Add debug feature. Clean up code. Restructure examples.

New in v1.0.4

  1. Add support to all STM32 boards (STM32F/L/H/G/WB/MP1) with 32K+ Flash.
  • STM32L0, STM32L1, STM32L4
  • STM32G0, STM32G4
  • STM32H7
  • STM32WB
  • STM32MP1

Question: do the functions stay the same as in the Ethernet library or are there differences too ?

This is the WebServer library for boards using Ethernet (W5x00, ENC28J60, LAN8742A) modules or shields.

The functions are similar and compatible to ESP8266/ESP32 WebServer libraries to make it much easier to port WebServer-related sketches from ESP8266/ESP32.

The library just uses the functions provided by Ethernetx libraries. Have a quick look at the examples to have better feelings of the features.

Thank you. If I understand correctly, it is possible to use a W5x00 shield with STM32 boards which don't feature built-in Ethernet out of the box. The reason I'd need this this is because I'd need some STM32 boards which have a small form factor (similar to the Nano), instead of the rather large Nucleo 144 which have Ethernet already on them. The STM32 allows me to "graduate" from Arduino later on and expand, since you might be asking why not just use a Nano with Eth. shield...

You understand correctly and the best way to do now is to start with an example using one of the **Supporting Boards **with 32+K Flash and W5x00.
Also have a good look at **Configuration Notes **

Release v1.0.6

  1. Add support to PROGMEM-related commands, such as sendContent_P() and send_P()
  2. Update Platform.ini to support PlatformIO 5.x owner-based dependency declaration.
  3. Clean up code.

I discovered calling Ethernet.linkStatus() doesn't seem to work.
Or I'm doing something wrong...
I tested this by creating a while loop in setup() which I was expecting to hang if the network cable is unplugged:

while (Ethernet.linkStatus() == LinkOFF) 
{ ; }

I was expecting this to stop the program, but instead it made it into the loop....is there a way to check if the network status changes ?

This issue has nothing to do with the EthernetWebServer_STM32 library, because this is controlled by the underlying Ethernetx libraries.

You can have a look at the relating Ethernetx library source code, all in public domain:

  1. Ethernet, EthernetLarge, EthernerENC and STM32Ethernet Libraries all have that function
enum EthernetLinkStatus 
{
Unknown,
LinkON,
LinkOFF
};

...

EthernetLinkStatus linkStatus();

Be careful that there is a state Unknown. You have to modify your code to

while (Ethernet.linkStatus() != LinkON);

to do what you want

  1. Ethernet2 has no function to detect the link status. I'll post the patch on the library soon. Done. See post #12

  2. Ethernet3 (and Ethernet2 after fixed) library has the similar function you can use

// returns the linkstate, 1 = linked, 0 = no link
uint8_t EthernetClass::link() 
{
 return bitRead(w5500.getPHYCFGR(), 0);
}

// returns the link status as a string "LINK" or "NO LINK"

const char* EthernetClass::linkReport() 
{
 if (bitRead(w5500.getPHYCFGR(), 0) == 1) 
   return "LINK";
 else 
   return "NO LINK";
}

You have to use

while (Ethernet.link() != 1);

to do what you want.

  1. To cover all the cases, it's better to use as follows:
#if (USE_ETHERNET2 || USE_ETHERNET3)
 // To modify Ethernet2 library to add function link()
 while (Ethernet.link() != 1);
#else
 while (Ethernet.linkStatus() != LinkON);
#endif

Good Luck,

The Ethernet2 library patch has been posted on this EthernetWebServer_STM32 Library to provide link status:

  1. Ethernet2.h
  2. Ethernet2.cpp

Thank you for your help. I'm still new to libraries and how to use them/open them/inspect them, so finding stuff in them is not easy for me. I know they go in my documents/Arduino, but that's as far as I dug for now.....

This is a good chance for you to start spending some time, if possible, exploring the related libraries to understand what they are doing and providing.

You'll see that will benefit you throughout your future works. Don't treat them as fast food.

I started doing this. This was for a project I had to get underway ASAP, so time was short. I got it off the ground anyway, but some stuff needs improving still, hence why I'm looking into it.

The board is connected to a GPON. If I power on both the board and GPON at the same time, the GPON initializes a lot later than the board itself, so by that point the board is past the part in setup where it tries to obtains an IP address and enters the loop without one, so it never connects to anything. I was thinking of a way to detect if there's a link state change and THEN run the Ethernet.begin() line....

I got it to execute the while loop for an unplugged cable by doing this:

while (Ethernet.linkStatus() != LinkON);

So I had to explicitly state that the link should be ON and ONLY ON to exit that while loop. Turns out that function can also return a third result - "unknown". Not sure WHY there's two separate states (a LinkOFF and an Unknown) and what the difference is. LinkOFF made perfect sense when the cable is unplugged entirely and what I was expecting to get in my initial code which checked for LinkOFF, but turns out I was getting "unknown" all along, so the loop was exiting....

There are too many different implementations and minute details that there is no easy way (and also not interesting job) for library writers to document and satisfy every user and all use cases. That's why the open-source community will come to help you by making the source code public. If you have some implementation issue, consult and learn from the source code, then write some short simple code to verify your assumption.

You can look at the following code snippet, taken from Ethernet_STM32 example of new Ethernet_Manager_STM32 library for a way to handle the LinkStatus

void heartBeatPrint()
{
  static int num        = 1;
  static int linkStatus = 0;
  
  localEthernetIP = Ethernet.localIP();
  
#if (USE_ETHERNET2 || USE_ETHERNET3)
  // To modify Ethernet2 library
  linkStatus = Ethernet.link();
  ET_LOGINFO3(F("localEthernetIP = "), localEthernetIP, F(", linkStatus = "), (linkStatus == 1) ? F("LinkON") : F("LinkOFF") );
  
  if ( ( linkStatus == 1 ) && ((uint32_t) localEthernetIP != 0) )
#else

  // The linkStatus() is not working with W5100. Just using IP != 0.0.0.0
  // Better to use ping for W5100
  linkStatus = (int) Ethernet.linkStatus();
  ET_LOGINFO3(F("localEthernetIP = "), localEthernetIP, F(", linkStatus = "), (linkStatus == LinkON) ? F("LinkON") : F("LinkOFF") );
  
  if ( ( (linkStatus == LinkON) || !isW5500 ) && ((uint32_t) localEthernetIP != 0) )
#endif
  {
    Serial.print(F("H"));
  }
  else
    Serial.print(F("F"));  

  if (num == 80)
  {
    Serial.println();
    num = 1;
  }
  else if (num++ % 10 == 0)
  {
    Serial.print(F(" "));
  }
}

I thought I had it sorted out like this

while (Ethernet.linkStatus() != LinkON) {
    Serial.println("No link. Check ethernet cable...");
delay(2000);}

It does indeed work when the cable is disconnected, but I'm still missing something because now it's the opposite: I can't get OUT of the loop, even when the cable IS connected, so I ge the same message over and over every 2s...

Like I said, I'm still an absolute novice, much more so with complex stuff like this, so I'm also not sure what I should be including in my sketch...at the top of my sketch I have these two.

#include <LwIP.h>
#include <STM32Ethernet.h>

If I navigate to My Documents >Arduino>libraries I can indeed have two folders called "STM32duino_STM32Ethernet" and "STM32duino_LwIP" respectivelly, which makes sense.

If I dig deeper inside the "src" folder of the "STM32duino_STM32Ethernet" folder, I also see the "STM32Ethernet.h" file which the IDE gives in the #include, which also makes sense.
Opening this with a text editor, I also found those lines you mentioned

enum EthernetLinkStatus {
  Unknown,
  LinkON,
  LinkOFF
};

That's as far as I'm able to follow though and it all seems to make sense. I think I need to do something to the original Ethernet library found in ProgramFiles>Arduino>libraries, since it may not work with the LAN IC the Nucleo has (it's made for W5xxx). Am I on the right path at least ? I'm on a Nucleo F767ZI, since there's a lot of other possible platforms for this :o . Thought I'd give more precise details this time about what I actually have in front of my eyes, instead of just speculating. This comes at the expense of annoying people, so sorry for wall of text :slight_smile:

Releases v1.2.0

  1. Add support to LAN8720 Ethernet for many STM32F4 (F407xx, NUCLEO_F429ZI) and STM32F7 (DISCO_F746NG, NUCLEO_F746ZG, NUCLEO_F756ZG) boards.
  2. Add LAN8720 examples
  3. Add Packages' Patches for STM32 to use LAN8720 with STM32Ethernet and LwIP libraries

Releases v1.1.1

  1. Clean-up all compiler warnings possible.
  2. Add Table of Contents
  3. Add examples
  4. Add Version String