HTTP-Server based on TCPSocket (mbed os 6!)

hi im searching for a working example where my target acts as server (read incoming, answer) for a restful http server.

wherever you look you find deprectated stuff that does not work anymore.

do you have a minimal example on how to run a restful server for mbed os 6, not 5.

Hello Jibba,

Provided your target supports the Mbed’s EMAC (Ethernet Phy) driver the following “Web server echo” example should work with Mbed OS 6:

#include "mbed.h"
#include "EthernetInterface.h"
#include "TCPSocket.h"

#define IP      "192.168.1.181"
#define GATEWAY "192.168.1.1"
#define NETMASK "255.255.255.0"
#define PORT    80

EthernetInterface*  net;

TCPSocket           server;
TCPSocket*          clientSocket;
SocketAddress       clientAddress;
char                rxBuf[512] = { 0 };
char                txBuf[512] = { 0 };

int main(void)
{
    printf("Starting\r\n");

    net = new EthernetInterface;

    if (!net) {
        printf("Error! No network inteface found.\n");
        return 0;
    }

    //net->set_network (IP, NETMASK, GATEWAY);  // include to use static IP address
    nsapi_size_or_error_t   r = net->connect();
    if (r != 0) {
        printf("Error! net->connect() returned: %d\n", r);
        return r;
    }

    // Show the network address
    SocketAddress   ip;
    SocketAddress   netmask;
    SocketAddress   gateway;

    net->get_ip_address(&ip);
    net->get_netmask(&netmask);
    net->get_gateway(&gateway);

    ip.set_port(PORT);

    const char*     ipAddr = ip.get_ip_address();
    const char*     netmaskAddr = netmask.get_ip_address();
    const char*     gatewayAddr = gateway.get_ip_address();

    printf("IP address: %s\r\n", ipAddr ? ipAddr : "None");
    printf("Netmask: %s\r\n", netmaskAddr ? netmaskAddr : "None");
    printf("Gateway: %s\r\n\r\n", gatewayAddr ? gatewayAddr : "None");

    /* Open the server on ethernet stack */
    server.open(net);

    /* Bind the HTTP port (TCP 80) to the server */
    server.bind(ip);

    /* Can handle 5 simultaneous connections */
    server.listen(5);

    //listening for http GET request
    while (true) {
        printf("=========================================\r\n");

        nsapi_error_t   error = 0;

        clientSocket = server.accept(&error);
        if (error != 0) {
            printf("Connection failed!\r\n");
        }
        else {
            //clientSocket->set_timeout(200);
            clientSocket->getpeername(&clientAddress);
            printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
            error = clientSocket->recv(rxBuf, sizeof(rxBuf));

            switch (error) {
                case 0:
                    printf("Recieved buffer is empty.\r\n");
                    break;

                case -1:
                    printf("Failed to read data from client.\r\n");
                    break;

                default:
                    printf("Recieved Data: %d\n\r\n\r%.*s\r\n\n\r", strlen(rxBuf), strlen(rxBuf), rxBuf);
                    if (rxBuf[0] == 'G' && rxBuf[1] == 'E' && rxBuf[2] == 'T') {
                        //setup http response header & data
                        sprintf
                        (
                            txBuf,
                            "HTTP/1.1 200 OK\nContent-Length: %d\r\nContent-Type: text\r\nConnection: Close\r\n\r\n",
                            strlen(rxBuf)
                        );
                        strcat(txBuf, rxBuf);
                        clientSocket->send(txBuf, strlen(txBuf));

                        // printf("------------------------------------\r\n");
                        // printf("Sent:\r\n%s\r\n", txBuf);
                        printf("echo done.\r\n");
                    }
                    break;
            }
        }

        clientSocket->close();
        printf("Client socket closed\r\n");
    }
}

To test the program type the Web Server’s IP address, printed to the serial console at start up, to your web browser and hit enter.

thx

Hello, I came across this post and tried your code. It works fine as is, but if I try to use it in a thread it keeps getting stuck due to a NSAPI_ERROR_WOULD_BLOCK condition in the recv() function. Strangely enough this only happens after the webpage is reloaded after the first time. So in other words, it works for the first time but gives issues after that. As far as I can find online the socket should be blocking by default and manually setting it to blocking also doesn’t fix it. Do you have any clue why this would happen?

  • What do you mean by “if I try to use it in a thread 
”?

  • The example above is waiting in the main thread to accept a connection on a socket:

while (true) {
...
    clientSocket = server.accept(&error);
...

    clientSocket->close();
...
}

Mbed explains the accept function as follows:

    /** Accepts a connection on a socket.
     *
     *  The server socket must be bound and set to listen for connections.
     *  On a new connection, returns connected network socket which user is expected to call close()
     *  and that deallocates the resources. Referencing a returned pointer after a close()
     *  call is not allowed and leads to undefined behavior.
     *
     *  By default, accept blocks until incoming connection occurs. If socket is set to
     *  non-blocking or times out, error is set to NSAPI_ERROR_WOULD_BLOCK.
     *
     *  @param error      pointer to storage of the error value or NULL:
     *                    NSAPI_ERROR_OK on success
     *                    NSAPI_ERROR_WOULD_BLOCK if socket is set to non-blocking and would block
     *                    NSAPI_ERROR_NO_SOCKET if the socket was not open
     *  @return           pointer to a socket
     */
    TCPSocket *accept(nsapi_error_t *error = NULL) override;

First of all, thanks for your fast response!

By using it in a thread I meant that I put all the code from the main function inside a new function called “web_server”. The main function is replaced by an almost empty one which is used to start a new thread that executes this new “web_server” function. Apart from starting the “web_server” thread, the main only has the infinite while loop left. I also tried adding a blinky thread and that kept running fine.

The accept function seems to be working fine. It’s the recv function that is causing the issues and hangs at the NSAPI_ERROR_WOULD_BLOCK condition.

I moved the web server to a webServerTask running in a webServerThread. At the same time, an LED is toggled in the main task (which is running in the main thread).
No errors were reported when trying to connect to the web server multiple times:

#include "mbed.h"
#include "EthernetInterface.h"
#include "TCPSocket.h"

#define IP      "192.168.1.181"
#define GATEWAY "192.168.1.1"
#define NETMASK "255.255.255.0"
#define PORT    80

DigitalOut          led1(LED1);

EthernetInterface*  net;
TCPSocket           server;
TCPSocket*          clientSocket;
SocketAddress       clientAddress;
char                rxBuf[512] = { 0 };
char                txBuf[512] = { 0 };

Thread              webServerThread;
void                webServerTask();

/**
 * @brief
 * @note
 * @param
 * @retval
 */
void webServerTask()
{
    net = new EthernetInterface;

    if (!net) {
        printf("Error! No network inteface found.\n");
        return;
    }

    //net->set_network (IP, NETMASK, GATEWAY);  // include to use static IP address
    nsapi_size_or_error_t   r = net->connect();
    if (r != 0) {
        printf("Error! net->connect() returned: %d\n", r);
        return;
    }

    // Show the network address
    SocketAddress   ip;
    SocketAddress   netmask;
    SocketAddress   gateway;

    net->get_ip_address(&ip);
    net->get_netmask(&netmask);
    net->get_gateway(&gateway);

    ip.set_port(PORT);

    const char*     ipAddr = ip.get_ip_address();
    const char*     netmaskAddr = netmask.get_ip_address();
    const char*     gatewayAddr = gateway.get_ip_address();

    printf("IP address: %s\r\n", ipAddr ? ipAddr : "None");
    printf("Netmask: %s\r\n", netmaskAddr ? netmaskAddr : "None");
    printf("Gateway: %s\r\n\r\n", gatewayAddr ? gatewayAddr : "None");

    /* Open the server on ethernet stack */
    server.open(net);

    /* Bind the HTTP port (TCP 80) to the server */
    server.bind(ip);

    /* Can handle 5 simultaneous connections */
    server.listen(5);

    //listening for http GET request
    while (true) {
        printf("=========================================\r\n");

        nsapi_error_t   error = 0;

        clientSocket = server.accept(&error);
        if (error != 0) {
            printf("Connection failed!\r\n");
        }
        else {
            //clientSocket->set_timeout(200);
            clientSocket->getpeername(&clientAddress);
            printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
            error = clientSocket->recv(rxBuf, sizeof(rxBuf));

            switch (error) {
                case 0:
                    printf("Recieved buffer is empty.\r\n");
                    break;

                case -1:
                    printf("Failed to read data from client.\r\n");
                    break;

                default:
                    printf("Recieved Data: %d\n\r\n\r%.*s\r\n\n\r", strlen(rxBuf), strlen(rxBuf), rxBuf);
                    if (rxBuf[0] == 'G' && rxBuf[1] == 'E' && rxBuf[2] == 'T') {
                        //setup http response header & data
                        sprintf
                        (
                            txBuf,
                            "HTTP/1.1 200 OK\nContent-Length: %d\r\nContent-Type: text\r\nConnection: Close\r\n\r\n",
                            strlen(rxBuf)
                        );
                        strcat(txBuf, rxBuf);
                        clientSocket->send(txBuf, strlen(txBuf));

                        // printf("------------------------------------\r\n");
                        // printf("Sent:\r\n%s\r\n", txBuf);
                        printf("echo done.\r\n");
                    }
                    break;
            }
        }

        clientSocket->close();
        printf("Client socket closed\r\n");
    }
}

/**
 * @brief
 * @note
 * @param
 * @retval
 */
int main(void)
{
    printf("Starting\r\n");
    webServerThread.start(callback(webServerTask));

    while (true) {
        led1 = !led1;
        ThisThread::sleep_for(200ms);
    }
}
1 Like

Thanks for the help so far!

Your code is very similar to the code that I used. I only uncommented the line that gives the server a static IP . Unfortunately it still hangs occasionally (browser keeps showing the spinning loading icon). I tried the version with only the main function again and I noticed that that also has it, so it’s not just this additional thread that’s causing it like I previously thought. It didn’t occur when testing this version previously, so sorry for that
 So in other words, both versions have it. However, setting the timeout to 200 (uncommenting the line) makes the issue almost a non-issue. Without this line uncommented the user has to refresh the page if it gets stuck or wait like 90 seconds.

Unfortunately I started to notice a worse problem. Whenever the user refreshes too fast (so for example spams the F5 key) it will result in a HardFault which makes the server unavailable until the board is reset. This happens on my STM32F746ZG board at least. Do you also experience this on your board?

No matter how hard I was trying to reproduce the failure on my LPC1768 the server always worked correctly. It seems to be something specific to the STM32F746ZG (or maybe STM targets).

Hi Zoltan,
I know this is 11 mo old. I tried your code (from Oct '21) and I’m failing to get it to run.

  • Which OS version were you using?
  • Did you have an mbed_app.json file?
  • Is a working example in a publicly accessible repo that I can simply import?

My trials —

Environment:

  • LPC1768, plugged in to the mbed application board.
  • mbed OS 6.16.0
  • Keil Studio (cloud environment) v1.5.50
  • Known good Ethernet cable

mbed_app.json:

{
    "config": {
        "main-stack-size": {
            "value": 8192
        }
    },
    "target_overrides": {
        "*": {
            "platform.stdio-baud-rate": 115200,
            "platform.stdio-convert-newlines": true,
            "target.network-default-interface-type": "ETHERNET",
            "target.printf_lib": "std"
        }
    }
}

Console Output:

Starting
Error! net->connect() returned: -3010

Environment:
*mbed OS 5.15.0

Console Output:

Starting
Error! net->connect() returned: -3004 (oops, I had unplugged the cable)
Error! net->connect() returned: -3010 (with the cable plugged in)

Environment:
I dropped mbed OS to 5.14.2 (which is about the time of your post), but this one doesn’t compile.

compile main.cpp
/src/main.cpp:49:25: error: too many arguments to function call, expected 0, have 1
    net->get_ip_address(&ip);
    ~~~~~~~~~~~~~~~~~~~ ^~~
/extras/mbed-os.lib/features/netsocket/EMACInterface.h:104:25: note: 'get_ip_address' declared here
    virtual const char *get_ip_address();
                        ^
/src/main.cpp:50:22: error: too many arguments to function call, expected 0, have 1
    net->get_netmask(&netmask);
    ~~~~~~~~~~~~~~~~ ^~~~~~~~
/extras/mbed-os.lib/features/netsocket/EMACInterface.h:118:25: note: 'get_netmask' declared here
    virtual const char *get_netmask();
                        ^
/src/main.cpp:51:22: error: too many arguments to function call, expected 0, have 1
    net->get_gateway(&gateway);
    ~~~~~~~~~~~~~~~~ ^~~~~~~~
/extras/mbed-os.lib/features/netsocket/EMACInterface.h:125:25: note: 'get_gateway' declared here
    virtual const char *get_gateway();
                        ^
3 errors generated.
Internal error.
Build failed
Build failed

Hello David,

Unfortunately, because of my heath problems (I’m on chemo 
 ), I don’t have time to “play” with mbed anymore :frowning:

However, I have re-tested my code above without any modifications (copy&paste). So the IP address is assigned by DHCP when connecting to the network.

Environment:

  • LPC1768 connected to the local network directly over Ethernet pins RD-, RD+, TD-, TD+
  • mbed OS 6.16.0
  • mbed CLI1
  • mbed_app. json file with the following content:
{
    "target_overrides": {
        "*": {
            "target.default_lib": "small",
            "platform.stdio-baud-rate": 115200
        }
    }
}
  • .mbedignore file with the following content (created in the root directory of the project):
/* Bootloader */
mbed-os/features/FEATURE_BOOTLOADER/*

/* BLE */
mbed-os/connectivity/drivers/ble/*
mbed-os/connectivity/FEATURE_BLE/*

/* Cellular */
mbed-os/connectivity/cellular/*
mbed-os/connectivity/drivers/cellular/*
mbed-os/connectivity/netsocket/source/Cellular*.*

/* Device Key */
mbed-os/drivers/device_key/*

/* Experimental */
mbed-os/platform/FEATURE_EXPERIMENTAL_API/*

/* FPGA */
mbed-os/features/frameworks/COMPONENT_FPGA_CI_TEST_SHIELD/*

/* Greentea client */
mbed-os/features/frameworks/greentea-client/*

/* LORAWAN */
mbed-os/connectivity/drivers/lora/*
mbed-os/connectivity/lorawan/*

/* LWIP */
#mbed-os/connectivity/drivers/emac/*
#mbed-os/connectivity/lwipstack/*

/* Mbed-client-cli */
#mbed-os/features/frameworks/mbed-client-cli/*

/* MBED TLS */
#mbed-os/connectivity/drivers/mbedtls/*
#mbed-os/connectivity/mbedtls/*

/* Nanostack */
#mbed-os/connectivity/drivers/emac/*
#mbed-os/connectivity/libraries/mbed-coap/*
#mbed-os/connectivity/libraries/nanostack-libservice/*
#mbed-os/connectivity/libraries/ppp/*
#mbed-os/connectivity/nanostack/*

/* Netsocket */
#mbed-os/connectivity/drivers/emac/*
#mbed-os/connectivity/netsocket/*
#mbed-os/libraries/mbed-coap/*
#mbed-os/libraries/ppp/*

/* NFC */
mbed-os/connectivity/drivers/nfc/*
mbed-os/connectivity/nfc/*

/* RF */
mbed-os/connectivity/drivers/802.15.4_RF/*

/* Storage */
mbed-os/storage/filesystem/*
mbed-os/storage/kvstore/*
mbed-os/storage/platform/*

/* Tests */
mbed-os/platform/test/*
mbed-os/TEST_APPS/*
mbed-os/TESTS/*
mbed-os/UNITTESTS/*

/* Unity */
mbed-os/features/frameworks/unity/*

/* Utest */
mbed-os/features/frameworks/utest/*

/* USB */
mbed-os/drivers/usb/source/*
mbed-os/hal/usb/source/*
mbed-os/hal/usb/TARGET_Templates/*

/* WiFi */
mbed-os/connectivity/drivers/wifi/*

Test results:

Starting
IP address: 192.168.1.244
Netmask: 255.255.255.0
Gateway: 192.168.1.1

=========================================

And after connecting in Firefox to 192.168.1.244:

Client with IP address 192.168.1.11 connected.

Recieved Data: 345


GET / HTTP/1.1
Host: 192.168.1.244
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: keep-alive
Upgrade-Insecure-Requests: 1




echo done.
Client socket closed
=========================================

I hope the above info can help you.

With best regards,

Zoltan

Thank you Zoltan!
And I’m sorry you have health problems - wishing the best for you.
I’ll try this in the next day.

MBED Team,

or anyone that has actually achieved success with Ethernet, LPC1768, and MBED OS 6.16.0, and Keil Studio - online.

Executive Summary

  • LPC1768 and MBED Application Board - A ‘simple’ Ethernet example fails to connect to the network.
  • MBED OS 6.16.0
  • Keil Studio - online version.

For comparison:

  • MBED CLI1, with the exact same code on the LPC1768 works.
  • FRDM-K66F, Keil Studio online - The exact same code works.

** I suspect there is a defect in the code generation for Ethernet when targeting the LPC1768. **

Failed Trials with LPC1768

Environment

  • Keil [online] compiler. ** Significant Difference? ** Zoltan used mbed CLI1
  • mbed OS 6.16.0, also tested with 6.16.0-rc1
  • LPC1768 module on the MBED Application Board (and on another board w/Ethernet)

When I build for the LPC1768, drag-n-drop program and reboot, it takes about 1 minute, and then I get:

Error! net->connect() returned: -3010

The example code (in Zoltan’s text) in main continues to blink the LED as expected.
My test used two different Ethernet boards - the MBED Application Board and a board of my own design. I have an old binary for my own board, and it works with its Ethernet, thus verifying the Ethernet interface.

I tried different firmware:

  • firmware=141212 // this is the latest, and it failed to connect
  • firmware=16457 // this is quite old - and the blink-rate is very slow, so I don’t trust it.
  • firmware=21164 // an ‘intermediate’ version, it blinks as expected, but fails to connect.

Success with a FRDM-K66F - and the exact same source code

  • I swapped my LPC1768 for a FRDM-K66F board.
  • I retargeted Keil [online] studio to this target.
  • I built the exact same code.
  • On boot, this connected successfully to the network after about 12s.

Hi David,

I just quickly tested the example code and the Keil Studio Cloud build binary worked fine.

  • LPC1768 (firmware 141212) with Application board
  • MBED OS 6.16.0
  • Keil Studio - online version.

What I did


  • Open Keil Studio Cloud
  • Create new project from mbed-os-example-blinly
  • Open main.cpp and paste the code
  • Update mbed-os version to 6.16
  • Build the project and copy the binary to my LPC1768 board
  • Press reset button to start app
  • Open client IP address by chrome browser

I also uploaded binaries here, so you can test:

  • Mbed CLI build with GCC_ARM
  • Mbed CLI build with ARM
  • Keil Studio Cloud build (ARM)

These three binaries worked fine with my LPC1768 board.
If the binary doesn’t work, I suggest to erase (format) your Mbed drive and try again.

My test project code here (should be same as yours):

Regards,

Toyo

Solved!

  • The root cause is a failed Ethernet on an LPC1768 Module!
  • For a specific mbed on the application board - I tested several of the IO (display, LEDs, and the RGB LED. All function. This is the module where Ethernet fails to connect, always returning -3010.
  • I have several other LPC1768 modules Ethernet-worked into various devices (that are active) and did not have this problem with them - so taking one out of service to swap modules was my last resort (in large part because of disbelief that I had a failed module).

Toyo, thanks for the binaries, with these I confirmed all three binaries as not working (with my failed module) and working (with an alternate module).

Thanks for the help!

1 Like

Sorry to hear about health issues, Wishing you a speedy recovery.