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

Dependencies:   UIPEthernet

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 the mbed board, on/off by using a web browser. An inexpensive ENC28J60 Ethernet module is used to assure connection between the mbed board and the Ethernet network (Internet). The ENC28J60 Ethernet module is driven by the UIPEthernet library.

Needed parts:

  • mbed board
  • ENC28J60 Ethernet module
  • Wires
  • Web browser (Internet Explorer, Safari, Firefox, Chrome ...) running on Windows, Mac, Linux, iPhone or Android device.
/media/uploads/hudakz/webswitch_enc.jpg/media/uploads/hudakz/webswitch_mobile01.jpg

Notice that DHCP is turned on by default. If you prefer to use static IP address then uncomment line 234

The IP address assigned to the WebSwitch server along with an instruction how to use it is printed in the connected PC's serial terminal window during program start up.

Warning

Please notice that the 3.3V power supply chip (RT8183-B) installed on an STM32F103C8T6 board is not rated to power also the ENC28J60 board.


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

NOTE:

Revision:
10:b47c7921346f
Parent:
5:0ab8292e37da
Child:
11:6c0b20227ca2
--- a/main.cpp	Mon Mar 16 09:43:13 2015 +0000
+++ b/main.cpp	Tue Aug 27 15:03:19 2019 +0000
@@ -1,100 +1,69 @@
-/* 
- * In this project LED1 on the mbed board is switched on/off using a web browser.
- * However, you can easily modify the project to remotely switch on/off any external device.
- * The HTTP server is built from an mbed board and an ENC28J60 board.
- * ENC28J60 is driven by the UIPEthernet library <https://github.com/ntruchsess/arduino_uip>.
- * The example is based on the Tuxgraphics Web Switch <http://www.tuxgraphics.org/>.
- */
-#include <mbed.h>
-#include <UIPEthernet.h>
-#include <UIPServer.h>
-#include <UIPClient.h>
+#include "mbed.h"
 #include <string>
-
-using namespace     std;
+#include "UipEthernet.h"
+#include "TcpServer.h"
+#include "TcpClient.h"
 
-// UIPEthernet is the name of a global instance of UIPEthernetClass.
-// Do not change the name! It is used within the UIPEthernet library.
-#if defined(TARGET_LPC1768)
-UIPEthernetClass    UIPEthernet(p11, p12, p13, p8);         // mosi, miso, sck, cs
-#elif defined(TARGET_LPC1114)
-UIPEthernetClass    UIPEthernet(dp2, dp1, dp6, dp25);       // mosi, miso, sck, cs
-#elif defined(TARGET_LPC11U68)
-UIPEthernetClass    UIPEthernet(P0_9, P0_8, P1_29, P0_2);   // mosi, miso, sck, cs
-#elif defined(TARGET_NUCLEO_F103RB)
-UIPEthernetClass    UIPEthernet(PB_5, PB_4, PB_3, PB_6);    // mosi, miso, sck, cs
-#elif defined(TARGET_NUCLEO_F401RE)
-UIPEthernetClass    UIPEthernet(PB_5, PB_4, PB_3, PB_6);    // mosi, miso, sck, cs
-#elif defined(TARGET_NUCLEO_F411RE)
-UIPEthernetClass    UIPEthernet(PB_5, PB_4, PB_3, PB_6);    // mosi, miso, sck, cs
+using namespace std;
 
-// If your board/plaform is not present yet then uncomment
-// the following two lines and replace TARGET_YOUR_BOARD as appropriate.
-
-//#elif defined(TARGET_YOUR_BOARD)
-//UIPEthernetClass    UIPEthernet(SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS);   // mosi, miso, sck, cs
-
-#endif
+// To be used when static IP is needed
+#define IP      "192.168.1.35"
+#define GATEWAY "192.168.1.1"
+#define NETMASK "255.255.255.0"
+#define PORT    80
 
-// Note:
-// If it happends that any of the SPI_MOSI, SPI_MISO, SPI_SCK, SPI_CS pins collide with LED1 pin
-// then either use different SPI port (if available on the board) and change the pin names
-// in the constructor UIPEthernet(...) accordingly or instead of using LED1 pin, select
-// a free pin (not used by SPI port) and connect to it an external LED which is connected
-// to a 220 Ohm resitor that is connected to the groud.
-// In the second case remember to replace LED1 in sw(LED1) constructor (see below).
-// MAC number must be unique within the connected network. Modify as appropriate.
-const uint8_t       MY_MAC[6] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06 };
-
-// IP address must be also unique and compatible with your network. Change as appropriate.
-const IPAddress     MY_IP(192, 168, 1, 181);
-const uint16_t      MY_PORT = 80;           // for HTTP connection
-EthernetServer      myServer = EthernetServer(MY_PORT);
-
-// In this example we are turning on/off LED1.
-DigitalOut          sw(LED1);               // Change LED1 to a pin of your choice.
+// The ENC28J60 chip doesn't have a built-in MAC address.
+// It's up to us to provide one which is unique within the connected network.
+// So modify the one below accordingly.
+const uint8_t   MAC[6] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+UipEthernet     net(MAC, D11, D12, D13, D10);       // mosi, miso, sck, cs
+TcpServer       server;                             // TCP server running on this mbed device
+TcpClient*      client;                             // TCP client communicating with a HTTP Browser running on a PC or Mobile device
+const int       OFF = 0;
+const int       ON = 1;
+DigitalOut      output(D3);                         // A digital output to be switched on/off
+float           roomTemp = 21.8;                    // A dummy temperature sensor output
+const string    PASSWORD = "secret";                // Change as you like
+string          httpHeader;                         // HTTP header
+string          httpContent;                        // HTTP content
 
-// However, make sure that it does not collide with any of the SPI pins
-// already used in the UIPEthernet(...) constructor above!
-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
+ *         -2 no command given but password valid
+ *         -1 invalid password
+ *          0 switch off
+ *          1 switch on
+ */
+int8_t analyseURL(string& url)
+{
+    if (url.length() < 5 + PASSWORD.size() + 1)
+        return(-1);
 
-// analyse the url given
-// return values: -1 invalid password
-//                -2 no command given but password valid
-//                -3 just refresh page
-//                 0 switch off
-//                 1 switch on
-//
-//                The string passed to this function will look like this:
-//                GET /password HTTP/1.....
-//                GET /password/ HTTP/1.....
-//                GET /password/?sw=1 HTTP/1.....
-//                GET /password/?sw=0 HTTP/1.....
-int8_t analyse_get_url(string& str) {
-    if(str.substr(5, PASSWORD.size()) != PASSWORD)
+    if (url.substr(5, PASSWORD.size()) != PASSWORD)
         return(-1);
 
     uint8_t pos = 5 + PASSWORD.size();
 
-    if(str.substr(pos, 1) == " ")
-        return(-2);
-
-    if(str.substr(pos, 1) != "/")
+    if (url.substr(pos, 1) != "/")
         return(-1);
 
-    pos++;
+    if (url.substr(pos++, 1) == " ")
+        return(-2);
 
-    string  cmd(str.substr(pos, 5));
+    string  cmd(url.substr(pos, 5));
 
-    if(cmd == "?sw=0")
+    if (cmd == "?sw=0")
         return(0);
 
-    if(cmd == "?sw=1")
+    if (cmd == "?sw=1")
         return(1);
 
     return(-3);
@@ -106,8 +75,9 @@
  * @param
  * @retval
  */
-string& moved_perm(uint8_t flag) {
-    if(flag == 1)
+string& movedPermanently(uint8_t flag)
+{
+    if (flag == 1)
         httpContent = "/" + PASSWORD + "/";
     else
         httpContent = "";
@@ -123,43 +93,92 @@
  * @param
  * @retval
  */
-string& view(uint8_t status) {
-    httpContent = "<h2>Web Switch</h2>\r\n";
+string& showWebPage(int status)
+{
+    char    roomTempStr[5];
+
+    //roomTemp = ds1820.read();
+    sprintf(roomTempStr, "%3.1f", roomTemp);
 
-    if(status == 1) {
-        httpContent += "<pre>\r\n  <font color=#FF0000>ON</font>";
-        httpContent += " <a href=\"./?sw=0\">[switch off]</a>\r\n";
+    /*$off*/
+    httpContent  =
+        "<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>"
+        "</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";
+
+    if(status == ON) {
+        httpContent +=
+            "<a href=\"./?sw=0\" class=\"switch\"> "
+            "<input type=\"checkbox\" checked>";
     }
     else {
-        httpContent += "<pre>\r\n  <font color=#00FF00>OFF</font>";
-        httpContent += " <a href=\"./?sw=1\">[switch on]</a>\r\n";
+        httpContent +=
+            "<a href=\"./?sw=1\" class=\"switch\"> "
+            "<input type=\"checkbox\">";
     }
 
-    httpContent += "  <a href=\".\">[refresh status]</a>\r\n";
-    httpContent += "</pre>\r\n";
-    httpContent += "<hr>\r\n";
-    return httpContent;
-}
+    httpContent +=
+        "<div class=\"slider\"></div>"
+        "</a>"
+        "</pre>"
+        "<hr>"
+        "<pre>2017 ARMmbed</pre>"
+        "</body>";
 
-/**
- * @brief
- * @note
- * @param
- * @retval
- */
-void http_send(EthernetClient& client, string& header, string& content) {
-    char    content_length[5] = { };
-
-    header += "\r\nContent-Type: text/html\r\n";
-    header += "Content-Length: ";
-    sprintf(content_length, "%d", content.length());
-    header += string(content_length) + "\r\n";
-    header += "Pragma: no-cache\r\n";
-    header += "Connection: About to close\r\n";
-    header += "\r\n";
-
-    string  webpage = header + content;
-    client.write((uint8_t*)webpage.c_str(), webpage.length());
+    return httpContent;
+    /*$on*/
 }
 
 /**
@@ -168,60 +187,124 @@
  * @param
  * @retval
  */
-int main(void) {
-    UIPEthernet.begin(MY_MAC, MY_IP);
-    myServer.begin();
-    while(1) {
-        EthernetClient  client = myServer.available();
-        if(client) {
-            size_t  size = client.available();
-            if(size > 0) {
+void sendHTTP(TcpClient& client, string& header, string& content)
+{
+    /*$off*/
+    char    content_length[5] = { };
+
+    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.write((uint8_t*)webpage.c_str(), webpage.length());
+    /*$on*/
+}
+
+/**
+ * @brief
+ * @note
+ * @param
+ * @retval
+ */
+int main(void)
+{
+    printf("Starting ...\r\n");
+
+    // IP address must be unique and compatible with your network.
+    //net->set_network(IP, NETMASK, GATEWAY);  // include this for using static IP address
+    net.connect();
+
+    // Show the network address
+    const char*     ip = net.get_ip_address();
+    const char*     netmask = net.get_netmask();
+    const char*     gateway = net.get_gateway();
+
+    printf("IP address: %s\r\n", ip ? ip : "None");
+    printf("Netmask: %s\r\n", netmask ? netmask : "None");
+    printf("Gateway: %s\r\n\r\n", gateway ? gateway : "None");
+    printf("------------------------------------------------------\r\n");
+    printf("Usage: Type %s/%s/ into your web browser and hit ENTER\r\n\r\n", net.get_ip_address(), PASSWORD.c_str());
+    printf("------------------------------------------------------\r\n");
+
+    /* Open the server on ethernet stack */
+    server.open(&net);
+
+    /* Bind the HTTP port (TCP 80) to the server */
+    server.bind(PORT);
+
+    /* Can handle 5 simultaneous connections */
+    server.listen(5);
+
+    while (1) {
+        client = server.accept();
+
+        if (client) {
+            size_t  size = client->available();
+
+            if (size > 0) {
                 uint8_t*    buf = (uint8_t*)malloc(size);
-                size = client.read(buf, size);
+                size = client->read(buf, size);
+
                 string  received((char*)buf);
                 free(buf);
-                
-                if(received.substr(0, 3) != "GET") {
-                    httpHeader = HTTP_OK;
+
+                if (received.substr(0, 3) != "GET") {
+                    httpHeader = "HTTP/1.0 200 OK";
                     httpContent = "<h1>200 OK</h1>";
-                    http_send(client, httpHeader, httpContent);
-                    continue;
-                }
-
-                if(received.substr(0, 6) == "GET / ") {
-                    httpHeader = HTTP_OK;
-                    httpContent = "<p>Usage: http://host_or_ip/password</p>\r\n";
-                    http_send(client, httpHeader, httpContent);
+                    sendHTTP(*client, httpHeader, httpContent);
+                    client->close();
                     continue;
                 }
 
-                int cmd = analyse_get_url(received);
-
-                if(cmd == -2) {
-                    // redirect to the right base url
-                    httpHeader = MOVED_PERM;
-                    http_send(client, httpHeader, moved_perm(1));
+                if (received.substr(0, 6) == "GET / ") {
+                    httpHeader = "HTTP/1.0 200 OK";
+                    httpContent = "<p>Usage: http://host_or_ip/password</p>\r\n";
+                    sendHTTP(*client, httpHeader, httpContent);
+                    client->close();
                     continue;
                 }
 
-                if(cmd == -1) {
-                    httpHeader = UNAUTHORIZED;
-                    httpContent = "<h1>401 Unauthorized</h1>";
-                    http_send(client, httpHeader, httpContent);
-                    continue;
-                }
+                int cmd = analyseURL(received);
+
+                switch (cmd) {
+                    case -3:
+                        // update webpage
+                        httpHeader = "HTTP/1.0 200 OK";
+                        sendHTTP(*client, httpHeader, showWebPage(output));
+                        break;
+
+                    case -2:
+                        // redirect to the right base url
+                        httpHeader = "HTTP/1.0 301 Moved Permanently\r\nLocation: ";
+                        sendHTTP(*client, httpHeader, movedPermanently(1));
+                        break;
 
-                if(cmd == 1) {
-                    sw = 1; // switch on
-                }
+                    case -1:
+                        httpHeader = "HTTP/1.0 401 Unauthorized";
+                        httpContent = "<h1>401 Unauthorized</h1>";
+                        sendHTTP(*client, httpHeader, httpContent);
+                        break;
 
-                if(cmd == 0) {
-                    sw = 0; // switch off
-                }
+                    case 0:
+                        output = OFF;       // output off
+                        httpHeader = "HTTP/1.0 200 OK";
+                        sendHTTP(*client, httpHeader, showWebPage(output));
+                        break;
 
-                httpHeader = HTTP_OK;
-                http_send(client, httpHeader, view(sw));
+                    case 1:
+                        output = ON;        // output on
+                        httpHeader = "HTTP/1.0 200 OK";
+                        sendHTTP(*client, httpHeader, showWebPage(output));
+                        break;
+                }
             }
+            client->close();
         }
     }
 }