
Tiny HTTP server controlling a DigitalOutput.
Turn LED1, or other digital output, on/off using a web browser.
In this example we create a HTTP server for the STM32F407VET6 black board that will serve a simple Web page to remotely turn LED1, or other digital output, on/off by using a web browser.
![]() | ![]() | ||
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.
A DP83848 module is used as Ethernet interface.
Wiring
DP83848 module | STM32F407VET6 board | |||
---|---|---|---|---|
VCC | <=> | +3.3V | ||
GND | <=> | GND | ||
MDIO | <=> | PA_2 | ||
MDC | <=> | PC_1 | ||
OSCIN | <=> | PA_1 | ||
CRS | <=> | PA_7 | ||
RX0 | <=> | PC_4 | ||
RX1 | <=> | PC_5 | ||
TX_EN | <=> | PB_11 | ||
TX0 | <=> | PB_12 | ||
TX1 | <=> | PB_13 |
Notice that because the RX_ER
line is not used the DP83848 module doesn't have to be modified.
The project was inspired by the Tuxgraphics Web Switch. Thank you Guido!
Revision 10:6860e839277e, committed 2020-10-29
- Comitter:
- hudakz
- Date:
- Thu Oct 29 19:20:01 2020 +0000
- Parent:
- 9:3716ffcf2f27
- Commit message:
- Tiny HTTP server controlling a DigitalOutput.
Changed in this revision
diff -r 3716ffcf2f27 -r 6860e839277e main.cpp --- a/main.cpp Sun Nov 24 18:58:57 2019 +0000 +++ b/main.cpp Thu Oct 29 19:20:01 2020 +0000 @@ -1,49 +1,35 @@ #include "mbed.h" #include "EthernetInterface.h" +#include "TCPSocket.h" +#include <stdio.h> #include <string> -using namespace std; +DigitalOut led(PA_6); -#define IP "192.168.1.181" -#define GATEWAY "192.168.1.1" -#define NETMASK "255.255.255.0" +FileHandle* mbed::mbed_override_console(int) +{ + static BufferedSerial arch_max_serial1(PA_9, PA_10, 9600); + return &arch_max_serial1; +} + #define PORT 80 - -Serial pc(PC_6, PC_7); +/* */ EthernetInterface* net; TCPSocket server; -TCPSocket* clientSocket; -SocketAddress clientAddress; -char receiveBuf[1024] = { }; -const int OFF = 1; -const int ON = 0; -DigitalOut sw(PA_6); // A switch -float roomTemp = 21.8f; // 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 +TCPSocket* client; +char httpBuf[1500]; +char httpHeader[256]; +const int OFF = 0; +const int ON = 1; +const char PASSWORD[] = "secret"; // Change as you like +DigitalOut output(LED1); +float roomTemp = 21.8; // A temperature sensor output -/** - * @brief Defines a custom MAC address - * @note Uncomment the code below to define a unique MAC address. - * Modify the mac array items as needed. - * @param - * @retval - */ -//uint8_t mbed_otp_mac_address(char *mac) -//{ -// mac[0] = 0x00; -// mac[1] = 0x01; -// mac[2] = 0x02; -// mac[3] = 0x03; -// mac[4] = 0x04; -// mac[5] = 0x05; - -// return 1; -//} +FileHandle* mbed_override_console(int) +{ + static BufferedSerial arch_max_serial1(PA_9, PA_10, 9600); + return &arch_max_serial1; +} /** * @brief Analyses the received URL @@ -59,30 +45,33 @@ * 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); } @@ -92,110 +81,17 @@ * @param * @retval */ -string& movedPermanently(uint8_t flag) -{ - if (flag == 1) - httpContent = "/" + PASSWORD + "/"; - else - httpContent = ""; - - httpContent += "<h1>301 Moved Permanently</h1>\r\n"; - - return(httpContent); -} - -/** - * @brief - * @note - * @param - * @retval - */ -string& showWebPage(uint8_t status) +char* movedPermanently(uint8_t flag) { - char roomTempStr[5]; - - //roomTemp = ds1820.read(); - sprintf(roomTempStr, "%3.1f", roomTemp); - - /*$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) + "°C</pre>" - "<pre>Heating:\t"; - - if(status == ON) { - httpContent += - "<a href=\"./?sw=0\" class=\"switch\"> " - "<input type=\"checkbox\" checked>"; - } - else { - httpContent += - "<a href=\"./?sw=1\" class=\"switch\"> " - "<input type=\"checkbox\">"; + memset(httpBuf, 0, sizeof(httpBuf)); + if (flag == 1) { + strcpy(httpBuf, "/"); + strcat(httpBuf, PASSWORD); + strcat(httpBuf, "/"); } - httpContent += - "<div class=\"slider\"></div>" - "</a>" - "</pre>" - "<hr>" - "<pre>2017 ARMmbed</pre>" - "</body>"; - - return httpContent; - /*$on*/ + strcat(httpBuf, "<h1>301 Moved Permanently</h1>\r\n"); + return(httpBuf); } /** @@ -204,24 +100,109 @@ * @param * @retval */ -void sendHTTP(TCPSocket& client, string& header, string& content) +char* showWebPage(int status) { - /*$off*/ - char content_length[5] = { }; + char roomTempStr[10] = { }; + + //roomTemp = ds1820.read(); + + sprintf(roomTempStr, "%3.1f", roomTemp); + memset(httpBuf, 0, sizeof(httpBuf)); - 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"; + /*$off*/ + 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>" + "</head>" - string webpage = header + content; - client.send((char*)webpage.c_str(), webpage.length()); - pc.printf("HTTP message sent to client.\n\r"); + "<body>" + "<h2><a href=\".\" title=\"Click to refresh the page\">Smart Home</a></h2>" + "<pre>Temperature:\t" + ); + strcat(httpBuf, roomTempStr); + strcat(httpBuf, "°C</pre>"); + strcat + ( + httpBuf, + "<pre>Heating:\t" + ); + if(status == ON) { + strcat + ( + httpBuf, + "<a href=\"./?sw=0\" class=\"switch\"> " + "<input type=\"checkbox\" checked>" + ); + } + else { + strcat + ( + httpBuf, + "<a href=\"./?sw=1\" class=\"switch\"> " + "<input type=\"checkbox\">" + ); + } + strcat + ( + httpBuf, + "<div class=\"slider\"></div>" + "</a>" + "</pre>" + "<hr>" + "<pre>2017 ARMmbed</pre>" + "</body>" + ); /*$on*/ + return httpBuf; } /** @@ -230,112 +211,124 @@ * @param * @retval */ -int main(void) +void sendHTTP(TCPSocket* client, char* header, char* content) { - pc.baud(115200); - pc.printf("Starting.. \r\n\r\n"); + 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"); + + 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)); +} + +/** + * @brief + * @note + * @param + * @retval + */ +int main() +{ + printf("starting\r\n"); 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* mac = net->get_mac_address(); - const char* ip = net->get_ip_address(); - const char* netmask = net->get_netmask(); - const char* gateway = net->get_gateway(); - - pc.printf("MAC address: %s\r\n", mac ? mac : "None"); - 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; - } + 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; - 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; - } + case -2: + // redirect to the right base url + strcpy(httpHeader, "HTTP/1.0 301 Moved Permanently\r\nLocation: "); + sendHTTP(client, httpHeader, movedPermanently(1)); + break; - int cmd = analyseURL(received); + case -1: + strcpy(httpHeader, "HTTP/1.0 401 Unauthorized"); + strcpy(httpBuf, "<h1>401 Unauthorized</h1>"); + sendHTTP(client, httpHeader, httpBuf); + break; - switch (cmd) { - case -3: - httpHeader = HTTP_OK; - break; - - case -2: - // redirect to the right base url - httpHeader = MOVED_PERM; - break; - - case -1: - httpHeader = UNAUTHORIZED; - httpContent = "<h1>401 Unauthorized</h1>"; - break; - - case 0: - httpHeader = HTTP_OK; - sw = OFF; // turn the switch off - break; - - case 1: - httpHeader = HTTP_OK; - sw = ON; // turn the switch on - break; + case 0: + output = OFF; // output off + strcpy(httpHeader, "HTTP/1.0 200 OK"); + sendHTTP(client, httpHeader, showWebPage(output)); + break; + + case 1: + output = ON; // output on + strcpy(httpHeader, "HTTP/1.0 200 OK"); + sendHTTP(client, httpHeader, showWebPage(output)); + break; + } + } + + client->close(); } - - sendHTTP(*clientSocket, httpHeader, showWebPage(sw)); - clientSocket->close(); } }
diff -r 3716ffcf2f27 -r 6860e839277e mbed-os.lib --- a/mbed-os.lib Sun Nov 24 18:58:57 2019 +0000 +++ b/mbed-os.lib Thu Oct 29 19:20:01 2020 +0000 @@ -1,1 +1,1 @@ -https://github.com/ARMmbed/mbed-os/#cf4f12a123c05fcae83fc56d76442015cb8a39e9 +https://github.com/ARMmbed/mbed-os/#8ef0a435b2356f8159dea8e427b2935d177309f8
diff -r 3716ffcf2f27 -r 6860e839277e mbed_app.json --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed_app.json Thu Oct 29 19:20:01 2020 +0000 @@ -0,0 +1,7 @@ +{ + "target_overrides": { + "*": { + "stm32-emac.eth-phy-address": 1 + } + } +}