HTTP Server serving a simple webpage which enables to remotely turn LED1 on/off. Compile, download, run and type 'IP_address/secret/' (don't forget the last '/') into your web browser and hit ENTER.

Fork of WebSwitch_mbed-os by Zoltan Hudak

Turn LED1, or other digital output, on/off using a web browser.

In this example we create a HTTP server that will serve a simple Web page to remotely turn LED1, or other digital output, on/off by using a web browser.

/media/uploads/hudakz/webswitch03.png/media/uploads/hudakz/webswitch_mobile01.jpg

Notice that DHCP is turned on by default. The IP address assigned to the WebSwitch server along with an instruction how to use it is printed to the connected PC's serial terminal window during program start up.
To use static IP address uncomment and adjust line #221 in main.cpp.

The project was inspired by the Tuxgraphics Web Switch. Thank you Guido!

For a Web Switch using

Revision:
11:101be77aa5db
Parent:
9:161bed13b17e
Child:
12:138b2b3d041a
--- a/main.cpp	Tue Oct 27 14:39:27 2020 +0000
+++ b/main.cpp	Tue Oct 27 16:39:39 2020 +0000
@@ -3,70 +3,66 @@
 #include "TCPSocket.h"
 #include <stdio.h>
 #include <string>
-
 using namespace std;
 
 #define IP      "192.168.1.181"
 #define GATEWAY "192.168.1.1"
 #define NETMASK "255.255.255.0"
+
 #define PORT    80
-
-Serial              pc(USBTX, USBRX);
+/* */
 EthernetInterface*  net;
 TCPSocket           server;
-TCPSocket*          clientSocket;
-SocketAddress       clientAddress;
-char                receiveBuf[1024];
+TCPSocket*          client;
+char                httpBuf[1500];
+char                httpHeader[256];
 const int           OFF = 0;
 const int           ON = 1;
-DigitalOut          sw(LED1);               // A digital output to be switched on/off
+const char          PASSWORD[] = "secret";  // Change as you like
+DigitalOut          output(LED1);
 float               roomTemp = 21.8;        // A temperature sensor output
-const string        PASSWORD = "secret";    // Change as you like
-const string        HTTP_OK = "HTTP/1.0 200 OK";
-const string        MOVED_PERM = "HTTP/1.0 301 Moved Permanently\r\nLocation: ";
-const string        UNAUTHORIZED = "HTTP/1.0 401 Unauthorized";
-string              httpHeader;             // HTTP header
-string              httpContent;            // HTTP content
 
 /**
  * @brief   Analyses the received URL
  * @note    The string passed to this function will look like this:
- *          GET /HTTP/1.....
  *          GET /password HTTP/1.....
  *          GET /password/ HTTP/1.....
  *          GET /password/?sw=1 HTTP/1.....
  *          GET /password/?sw=0 HTTP/1.....
  * @param   url URL string
- * @retval -3 just refresh page
+ * @retval -1 invalid password
  *         -2 no command given but password valid
- *         -1 invalid password
+ *         -3 just refresh page
  *          0 switch off
  *          1 switch on
  */
-int8_t analyseURL(string& url)
+int8_t analyseURL(char* url)
 {
-    if (url.length() < 5 + PASSWORD.size() + 1)
+    if (strlen(url) < (5 + strlen(PASSWORD) + 1))
         return(-1);
 
-    if (url.substr(5, PASSWORD.size()) != PASSWORD)
+    //if (url.substr(5, PASSWORD.size()) != PASSWORD)
+    if (strncmp(url + 5, PASSWORD, strlen(PASSWORD)) != 0)
         return(-1);
 
-    uint8_t pos = 5 + PASSWORD.size();
+    uint8_t pos = 5 + strlen(PASSWORD);
 
-    if (url.substr(pos, 1) != "/")
+    //if (url.substr(pos, 1) != "/")
+
+    if (*(url + pos) != '/')
         return(-1);
 
-    if (url.substr(pos++, 1) == " ")
+    //if (url.substr(pos++, 1) == " ")
+    if (*(url + pos++) == ' ')
         return(-2);
 
-    string  cmd(url.substr(pos, 5));
-
-    if (cmd == "?sw=0")
+    //string  cmd(url.substr(pos, 5));
+    *(url + pos + 5) = '\0';    // terminate the cmd string
+    char*   cmd = ((url + pos));
+    if (strcmp(cmd, "?sw=0") == 0)
         return(0);
-
-    if (cmd == "?sw=1")
+    if (strcmp(cmd, "?sw=1") == 0)
         return(1);
-
     return(-3);
 }
 
@@ -76,16 +72,17 @@
  * @param
  * @retval
  */
-string& movedPermanently(uint8_t flag)
+char* movedPermanently(uint8_t flag)
 {
-    if (flag == 1)
-        httpContent = "/" + PASSWORD + "/";
-    else
-        httpContent = "";
+    memset(httpBuf, 0, sizeof(httpBuf));
+    if (flag == 1) {
+        strcpy(httpBuf, "/");
+        strcat(httpBuf, PASSWORD);
+        strcat(httpBuf, "/");
+    }
 
-    httpContent += "<h1>301 Moved Permanently</h1>\r\n";
-
-    return(httpContent);
+    strcat(httpBuf, "<h1>301 Moved Permanently</h1>\r\n");
+    return(httpBuf);
 }
 
 /**
@@ -94,92 +91,109 @@
  * @param
  * @retval
  */
-string& showWebPage(int status)
+char* showWebPage(int status)
 {
-    char    roomTempStr[5];
+    char    roomTempStr[10] = { };
 
     //roomTemp = ds1820.read();
+
     sprintf(roomTempStr, "%3.1f", roomTemp);
+    memset(httpBuf, 0, sizeof(httpBuf));
 
     /*$off*/
-    httpContent  =
+    strcat
+    (
+        httpBuf,
         "<head>"
-        "<meta charset=\"utf-8\">"
-        "<meta name=\"viewport\" content=\" initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=0;\"/>"
-        "<title>Smart Home</title>"
-        "<link href='http://fonts.googleapis.com/css?family=Droid+Sans&v1' rel='stylesheet' type='text/css'>"
-        "<style>"
-        ".switch {"
-            "position: relative;"
-            "display: inline-block;"
-            "width: 60px;"
-            "height: 34px;"
-        "}"
-        ".switch input {display:none;}"
-        ".slider {"
-            "position: absolute;"
-            "cursor: pointer;"
-            "top: 0;"
-            "left: 0;"
-            "right: 0;"
-            "bottom: 0;"
-            "border-radius: 34px;"
-            "background-color: #ccc;"
-            "-webkit-transition: .4s;"
-            "transition: .4s;"
-        "}"
-        ".slider:before {"
-            "position: absolute;"
-            "content: \"\";"
-            "height: 26px;"
-            "width: 26px;"
-            "left: 4px;"
-            "bottom: 4px;"
-            "border-radius: 50%;"
-            "background-color: white;"
-            "-webkit-transition: .4s;"
-            "transition: .4s;"
-        "}"
-        "input:checked + .slider {"
-            "background-color: #8ce196;"
-        "}"
-        "input:focus + .slider {"
-            "box-shadow: 0 0 1px #8ce196;"
-        "}"
-        "input:checked + .slider:before {"
-            "-webkit-transform: translateX(26px);"
-            "-ms-transform: translateX(26px);"
-            "transform: translateX(26px);"
-        "}"
-        "</style>"
+            "<meta charset=\"utf-8\">"
+            "<meta name=\"viewport\" content=\" initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=0;\"/>"
+            "<title>Smart Home</title>"
+            "<link href='http://fonts.googleapis.com/css?family=Droid+Sans&v1' rel='stylesheet' type='text/css'>"
+            "<style>"
+            ".switch {"
+                "position: relative;"
+                "display: inline-block;"
+                "width: 60px;"
+                "height: 34px;"
+            "}"
+            ".switch input {display:none;}"
+            ".slider {"
+                "position: absolute;"
+                "cursor: pointer;"
+                "top: 0;"
+                "left: 0;"
+                "right: 0;"
+                "bottom: 0;"
+                "border-radius: 34px;"
+                "background-color: #ccc;"
+                "-webkit-transition: .4s;"
+                "transition: .4s;"
+            "}"
+            ".slider:before {"
+                "position: absolute;"
+                "content: \"\";"
+                "height: 26px;"
+                "width: 26px;"
+                "left: 4px;"
+                "bottom: 4px;"
+                "border-radius: 50%;"
+                "background-color: white;"
+                "-webkit-transition: .4s;"
+                "transition: .4s;"
+            "}"
+            "input:checked + .slider {"
+                "background-color: #8ce196;"
+            "}"
+            "input:focus + .slider {"
+                "box-shadow: 0 0 1px #8ce196;"
+            "}"
+            "input:checked + .slider:before {"
+                "-webkit-transform: translateX(26px);"
+                "-ms-transform: translateX(26px);"
+                "transform: translateX(26px);"
+            "}"
+            "</style>"
         "</head>"
 
         "<body>"
-        "<h2><a href=\".\" title=\"Click to refresh the page\">Smart Home</a></h2>"
-        "<pre>Temperature:\t" + string(roomTempStr) + "&deg;C</pre>"
-        "<pre>Heating:\t";
-
+            "<h2><a href=\".\" title=\"Click to refresh the page\">Smart Home</a></h2>"
+            "<pre>Temperature:\t"
+    );
+    strcat(httpBuf, roomTempStr);
+    strcat(httpBuf, "&deg;C</pre>");
+    strcat
+    (
+       httpBuf,
+       "<pre>Heating:\t"
+    );
     if(status == ON) {
-        httpContent +=
-            "<a href=\"./?sw=0\" class=\"switch\"> "
-            "<input type=\"checkbox\" checked>";
+       strcat
+       (
+           httpBuf,
+           "<a href=\"./?sw=0\" class=\"switch\"> "
+           "<input type=\"checkbox\" checked>"
+       );
     }
     else {
-        httpContent +=
-            "<a href=\"./?sw=1\" class=\"switch\"> "
-            "<input type=\"checkbox\">";
+       strcat
+       (
+           httpBuf,
+           "<a href=\"./?sw=1\" class=\"switch\"> "
+           "<input type=\"checkbox\">"
+       );
     }
-
-    httpContent +=
-        "<div class=\"slider\"></div>"
-        "</a>"
-        "</pre>"
-        "<hr>"
-        "<pre>2017 ARMmbed</pre>"
-        "</body>";
-
-    return httpContent;
+    strcat
+    (
+       httpBuf,
+           "<div class=\"slider\"></div>"
+           "</a>"
+           "</pre>"
+           "<hr>"
+           "<pre>2017 ARMmbed</pre>"
+       "</body>"
+    );
     /*$on*/
+    return httpBuf;
 }
 
 /**
@@ -188,24 +202,21 @@
  * @param
  * @retval
  */
-void sendHTTP(TCPSocket& client, string& header, string& content)
+void sendHTTP(TCPSocket* client, char* header, char* content)
 {
-    /*$off*/
-    char    content_length[5] = { };
+    char    content_length[10] = { };
+    sprintf(content_length, "%u\r\n", strlen(content));
+    strcat(header, "\r\nContent-Type: text/html\r\n");
+    strcat(header, "Content-Length: ");
+    strcat(header, content_length);
+    strcat(header, "Pragma: no-cache\r\n");
+    strcat(header, "Connection: About to close\r\n\r\n");
 
-    header +=
-        "\r\nContent-Type: text/html\r\n"
-        "Content-Length: ";
-    sprintf(content_length, "%d", content.length());
-    header +=
-        string(content_length) + "\r\n"
-        "Pragma: no-cache\r\n"
-        "Connection: About to close\r\n\r\n";
-
-    string  webpage = header + content;
-    client.send((char*)webpage.c_str(), webpage.length());
-    pc.printf("HTTP message sent to client.\n\r");
-    /*$on*/
+    char    c = content[0];
+    memmove(httpBuf + strlen(header), httpBuf, strlen(content));    // make room for the header
+    strcpy(httpBuf, header);                                        // copy the header on front of the content
+    httpBuf[strlen(header)] = c;
+    client->send((uint8_t*)httpBuf, strlen(httpBuf));
 }
 
 /**
@@ -216,112 +227,101 @@
  */
 int main(void)
 {
-    pc.printf("Starting.. \r\n\r\n");
+    printf("\r\n Starting \r\n");
 
+    //ethLed.start(eth_led);
+    //net = NetworkInterface::get_default_instance();
     net = new EthernetInterface();
 
+    //net->set_network("192.168.1.181","255.255.255.0","192.168.1.1");  // use static IP address, netmask, gateway
     if (!net) {
-        pc.printf("Error! No network inteface found.\n");
+        printf("Error! No network inteface found.\n");
         return 0;
     }
 
     //net->set_network (IP, NETMASK, GATEWAY);  // include this for using static IP address
     nsapi_size_or_error_t   r = net->connect();
-
-    if (r != 0) {
-        pc.printf("Error! net->connect() returned: %d\n", r);
+    if (r != NSAPI_ERROR_OK) {
+        printf("Error! net->connect() returned: %d\n", r);
         return r;
     }
 
     // Show the network address
-    const char*     ip = net->get_ip_address();
-    const char*     netmask = net->get_netmask();
-    const char*     gateway = net->get_gateway();
-    
-    pc.printf("IP address: %s\r\n", ip ? ip : "None");
-    pc.printf("Netmask: %s\r\n", netmask ? netmask : "None");
-    pc.printf("Gateway: %s\r\n\r\n", gateway ? gateway : "None");
-    pc.printf("Usage: Type %s/%s/ into your web browser and hit ENTER\r\n\r\n", ip, PASSWORD.c_str());
+    SocketAddress   addr;
+    net->get_ip_address(&addr);
+    printf("IP address: %s\n", addr.get_ip_address() ? addr.get_ip_address() : "None");
+    net->get_netmask(&addr);
+    printf("Netmask: %s\n", addr.get_ip_address() ? addr.get_ip_address() : "None");
+    net->get_gateway(&addr);
+    printf("Gateway: %s\n", addr.get_ip_address() ? addr.get_ip_address() : "None");
 
     /* Open the server on ethernet stack */
     server.open(net);
 
     /* Bind the HTTP port (TCP 80) to the server */
-    server.bind(ip, PORT);
+    server.bind(PORT);
 
-    /* Can handle 5 simultaneous connections */
-    server.listen(5);
+    /* Listen for clients */
+    server.listen();
 
     //listening for http GET request
     while (true) {
-        pc.printf("=========================================\r\n");
-
-        nsapi_error_t   error;
-
-        clientSocket = server.accept(&error);
-        if (error != 0) {
-            pc.printf("Connection failed!\r\n");
-            clientSocket->close();
-            continue;
-        }
-
-        clientSocket->getpeername(&clientAddress);
-        pc.printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
-        clientSocket->recv(receiveBuf, 1023);
-        pc.printf("Data received:\r\n%s\n\r", receiveBuf);
-
-        string  received(receiveBuf);
-
-        if (received.substr(0, 3) != "GET") {
-            httpHeader = HTTP_OK;
-            httpContent = "<h1>200 OK</h1>";
-            sendHTTP(*clientSocket, httpHeader, httpContent);
-            clientSocket->close();
-            continue;
-        }
-
-        if (received.substr(0, 6) == "GET / ") {
-            httpHeader = HTTP_OK;
-            httpContent = "<p>Usage: Type http://ip_address/password/ into your web browser and hit ENTER</p>\r\n";
-            sendHTTP(*clientSocket, httpHeader, httpContent);
-            clientSocket->close();
-            continue;
-        }
+        printf("=========================================\r\n");
+        printf("Ready to serve clients.\r\n");
+        net->get_ip_address(&addr);
+        printf("Usage: Type http:\/\/%s\/%s\/  into your web browser and hit ENTER\r\n", addr.get_ip_address(), PASSWORD);
+        client = server.accept();
+        if (client) {
+            client->getpeername(&addr);
+            printf("Connection succeeded!\n\rIP: %s\n\r", addr.get_ip_address());
+            client->recv(httpBuf, 1500);
+            if (strncmp(httpBuf, "GET", 3) != 0) {
+                strcpy(httpHeader, "HTTP/1.0 200 OK");
+                strcpy(httpBuf, "<h1>200 OK</h1>");
+                sendHTTP(client, httpHeader, httpBuf);
+            }
+            else
+            if ((strncmp(httpBuf, "GET", 3) == 0) && (strncmp(httpBuf + 3, " / ", 3 == 0))) {
+                strcpy(httpHeader, "HTTP/1.0 200 OK");
+                strcpy(httpBuf, "<p>Usage: http://host_or_ip/password</p>\r\n");
+                sendHTTP(client, httpHeader, httpBuf);
+            }
+            else {
+                int cmd = analyseURL(httpBuf);
+                switch (cmd) {
+                    case -3:
+                        // update webpage
+                        strcpy(httpHeader, "HTTP/1.0 200 OK");
+                        sendHTTP(client, httpHeader, showWebPage(output));
+                        break;
 
-        int cmd = analyseURL(received);
+                    case -2:
+                        // redirect to the right base url
+                        strcpy(httpHeader, "HTTP/1.0 301 Moved Permanently\r\nLocation: ");
+                        sendHTTP(client, httpHeader, movedPermanently(1));
+                        break;
 
-        switch (cmd) {
-            case -3:
-                // update webpage
-                httpHeader = HTTP_OK;
-                sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
-                break;
-
-            case -2:
-                // redirect to the right base url
-                httpHeader = MOVED_PERM;
-                sendHTTP(*clientSocket, httpHeader, movedPermanently(1));
-                break;
+                    case -1:
+                        strcpy(httpHeader, "HTTP/1.0 401 Unauthorized");
+                        strcpy(httpBuf, "<h1>401 Unauthorized</h1>");
+                        sendHTTP(client, httpHeader, httpBuf);
+                        break;
 
-            case -1:
-                httpHeader = UNAUTHORIZED;
-                httpContent = "<h1>401 Unauthorized</h1>";
-                sendHTTP(*clientSocket, httpHeader, httpContent);
-                break;
+                    case 0:
+                        output = OFF;   // output off
+                        strcpy(httpHeader, "HTTP/1.0 200 OK");
+                        sendHTTP(client, httpHeader, showWebPage(output));
+                        break;
 
-            case 0:
-                sw = OFF;   // turn the switch off
-                httpHeader = HTTP_OK;
-                sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
-                break;
+                    case 1:
+                        output = ON;    // output on
+                        strcpy(httpHeader, "HTTP/1.0 200 OK");
+                        sendHTTP(client, httpHeader, showWebPage(output));
+                        break;
+                }
+            }
 
-            case 1:
-                sw = ON;    // turn the switch on
-                httpHeader = HTTP_OK;
-                sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
-                break;
+            client->close();
         }
-
-        clientSocket->close();
     }
 }