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.

/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.

A DP83848 module is used as Ethernet interface.

/media/uploads/hudakz/dp83848_01.jpg

Wiring

DP83848 moduleSTM32F407VET6 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!

Committer:
hudakz
Date:
Sat May 18 10:25:05 2019 +0000
Revision:
4:c11d58a6c9e2
Parent:
3:6edf142a16a8
Child:
5:92942d4f4ff6
Tiny HTTP server controlling a DigitalOutput.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
hudakz 0:912fb893b03c 1 #include "mbed.h"
hudakz 0:912fb893b03c 2 #include "EthernetInterface.h"
hudakz 0:912fb893b03c 3 #include "TCPSocket.h"
hudakz 0:912fb893b03c 4 #include <stdio.h>
hudakz 0:912fb893b03c 5 #include <string>
hudakz 0:912fb893b03c 6
hudakz 0:912fb893b03c 7 using namespace std;
hudakz 0:912fb893b03c 8
hudakz 0:912fb893b03c 9 #define IP "192.168.1.181"
hudakz 0:912fb893b03c 10 #define GATEWAY "192.168.1.1"
hudakz 0:912fb893b03c 11 #define NETMASK "255.255.255.0"
hudakz 0:912fb893b03c 12 #define PORT 80
hudakz 0:912fb893b03c 13
hudakz 0:912fb893b03c 14 Serial pc(PC_6, PC_7);
hudakz 0:912fb893b03c 15 //Serial pc(PA_9, PA_10);
hudakz 0:912fb893b03c 16
hudakz 0:912fb893b03c 17 EthernetInterface* net;
hudakz 0:912fb893b03c 18
hudakz 0:912fb893b03c 19 TCPSocket server;
hudakz 0:912fb893b03c 20 TCPSocket* clientSocket;
hudakz 0:912fb893b03c 21 SocketAddress clientAddress;
hudakz 0:912fb893b03c 22 char receiveBuf[1024] = { };
hudakz 0:912fb893b03c 23
hudakz 0:912fb893b03c 24 const int OFF = 1;
hudakz 0:912fb893b03c 25 const int ON = 0;
hudakz 0:912fb893b03c 26
hudakz 0:912fb893b03c 27 DigitalOut sw(PA_6);
hudakz 0:912fb893b03c 28 float roomTemp = 21.8; // A temperature sensor output
hudakz 0:912fb893b03c 29
hudakz 0:912fb893b03c 30 const string PASSWORD = "secret"; // change as you like
hudakz 0:912fb893b03c 31 const string HTTP_OK = "HTTP/1.0 200 OK";
hudakz 0:912fb893b03c 32 const string MOVED_PERM = "HTTP/1.0 301 Moved Permanently\r\nLocation: ";
hudakz 0:912fb893b03c 33 const string UNAUTHORIZED = "HTTP/1.0 401 Unauthorized";
hudakz 0:912fb893b03c 34 string httpHeader; // HTTP header
hudakz 0:912fb893b03c 35 string httpContent; // HTTP content
hudakz 0:912fb893b03c 36
hudakz 0:912fb893b03c 37 /**
hudakz 0:912fb893b03c 38 * @brief Analyses the received URL
hudakz 0:912fb893b03c 39 * @note The string passed to this function will look like this:
hudakz 0:912fb893b03c 40 * GET /password HTTP/1.....
hudakz 0:912fb893b03c 41 * GET /password/ HTTP/1.....
hudakz 0:912fb893b03c 42 * GET /password/?sw=1 HTTP/1.....
hudakz 0:912fb893b03c 43 * GET /password/?sw=0 HTTP/1.....
hudakz 0:912fb893b03c 44 * @param url URL string
hudakz 0:912fb893b03c 45 * @retval -1 invalid password
hudakz 0:912fb893b03c 46 * -2 no command given but password valid
hudakz 0:912fb893b03c 47 * -3 just refresh page
hudakz 0:912fb893b03c 48 * 0 switch off
hudakz 0:912fb893b03c 49 * 1 switch on
hudakz 0:912fb893b03c 50 */
hudakz 0:912fb893b03c 51 int8_t analyseURL(string& url) {
hudakz 0:912fb893b03c 52 if(url.substr(5, PASSWORD.size()) != PASSWORD)
hudakz 0:912fb893b03c 53 return(-1);
hudakz 0:912fb893b03c 54
hudakz 0:912fb893b03c 55 uint8_t pos = 5 + PASSWORD.size();
hudakz 0:912fb893b03c 56
hudakz 0:912fb893b03c 57 if(url.substr(pos, 1) == " ")
hudakz 0:912fb893b03c 58 return(-2);
hudakz 0:912fb893b03c 59
hudakz 0:912fb893b03c 60 if(url.substr(pos++, 1) != "/")
hudakz 0:912fb893b03c 61 return(-1);
hudakz 0:912fb893b03c 62
hudakz 0:912fb893b03c 63 string cmd(url.substr(pos, 5));
hudakz 0:912fb893b03c 64
hudakz 0:912fb893b03c 65 if(cmd == "?sw=0")
hudakz 0:912fb893b03c 66 return(0);
hudakz 0:912fb893b03c 67
hudakz 0:912fb893b03c 68 if(cmd == "?sw=1")
hudakz 0:912fb893b03c 69 return(1);
hudakz 0:912fb893b03c 70
hudakz 0:912fb893b03c 71 return(-3);
hudakz 0:912fb893b03c 72 }
hudakz 0:912fb893b03c 73
hudakz 0:912fb893b03c 74 /**
hudakz 0:912fb893b03c 75 * @brief
hudakz 0:912fb893b03c 76 * @note
hudakz 0:912fb893b03c 77 * @param
hudakz 0:912fb893b03c 78 * @retval
hudakz 0:912fb893b03c 79 */
hudakz 0:912fb893b03c 80 string& movedPermanently(uint8_t flag) {
hudakz 0:912fb893b03c 81 if(flag == 1)
hudakz 0:912fb893b03c 82 httpContent = "/" + PASSWORD + "/";
hudakz 0:912fb893b03c 83 else
hudakz 0:912fb893b03c 84 httpContent = "";
hudakz 0:912fb893b03c 85
hudakz 0:912fb893b03c 86 httpContent += "<h1>301 Moved Permanently</h1>\r\n";
hudakz 0:912fb893b03c 87
hudakz 0:912fb893b03c 88 return(httpContent);
hudakz 0:912fb893b03c 89 }
hudakz 0:912fb893b03c 90
hudakz 0:912fb893b03c 91 /**
hudakz 0:912fb893b03c 92 * @brief
hudakz 0:912fb893b03c 93 * @note
hudakz 0:912fb893b03c 94 * @param
hudakz 0:912fb893b03c 95 * @retval
hudakz 0:912fb893b03c 96 */
hudakz 0:912fb893b03c 97 string& showWebPage(uint8_t status) {
hudakz 0:912fb893b03c 98 char roomTempStr[5];
hudakz 0:912fb893b03c 99
hudakz 0:912fb893b03c 100 //roomTemp = ds1820.read();
hudakz 0:912fb893b03c 101 sprintf(roomTempStr, "%3.1f", roomTemp);
hudakz 0:912fb893b03c 102
hudakz 0:912fb893b03c 103 // CSS toggle switch
hudakz 3:6edf142a16a8 104 httpContent = \
hudakz 3:6edf142a16a8 105 "<head>"
hudakz 3:6edf142a16a8 106 "<style>" \
hudakz 3:6edf142a16a8 107 ".switch {" \
hudakz 3:6edf142a16a8 108 "position: relative;" \
hudakz 3:6edf142a16a8 109 "display: inline-block;" \
hudakz 3:6edf142a16a8 110 "width: 60px;" \
hudakz 3:6edf142a16a8 111 "height: 34px;" \
hudakz 3:6edf142a16a8 112 "}" \
hudakz 3:6edf142a16a8 113 ".switch input {display:none;}" \
hudakz 3:6edf142a16a8 114 ".slider {" \
hudakz 3:6edf142a16a8 115 "position: absolute;" \
hudakz 3:6edf142a16a8 116 "cursor: pointer;" \
hudakz 3:6edf142a16a8 117 "top: 0;" \
hudakz 3:6edf142a16a8 118 "left: 0;" \
hudakz 3:6edf142a16a8 119 "right: 0;" \
hudakz 3:6edf142a16a8 120 "bottom: 0;" \
hudakz 3:6edf142a16a8 121 "border-radius: 34px;" \
hudakz 3:6edf142a16a8 122 "background-color: #ccc;" \
hudakz 3:6edf142a16a8 123 "-webkit-transition: .4s;" \
hudakz 3:6edf142a16a8 124 "transition: .4s;" \
hudakz 3:6edf142a16a8 125 "}" \
hudakz 3:6edf142a16a8 126 ".slider:before {" \
hudakz 3:6edf142a16a8 127 "position: absolute;" \
hudakz 3:6edf142a16a8 128 "content: \"\";" \
hudakz 3:6edf142a16a8 129 "height: 26px;" \
hudakz 3:6edf142a16a8 130 "width: 26px;" \
hudakz 3:6edf142a16a8 131 "left: 4px;" \
hudakz 3:6edf142a16a8 132 "bottom: 4px;" \
hudakz 3:6edf142a16a8 133 "border-radius: 50%;" \
hudakz 3:6edf142a16a8 134 "background-color: white;" \
hudakz 3:6edf142a16a8 135 "-webkit-transition: .4s;" \
hudakz 3:6edf142a16a8 136 "transition: .4s;" \
hudakz 3:6edf142a16a8 137 "}" \
hudakz 3:6edf142a16a8 138 "input:checked + .slider {" \
hudakz 3:6edf142a16a8 139 "background-color: #8ce196;" \
hudakz 3:6edf142a16a8 140 "}" \
hudakz 3:6edf142a16a8 141 "input:focus + .slider {" \
hudakz 3:6edf142a16a8 142 "box-shadow: 0 0 1px #8ce196;" \
hudakz 3:6edf142a16a8 143 "}" \
hudakz 3:6edf142a16a8 144 "input:checked + .slider:before {" \
hudakz 3:6edf142a16a8 145 "-webkit-transform: translateX(26px);" \
hudakz 3:6edf142a16a8 146 "-ms-transform: translateX(26px);" \
hudakz 3:6edf142a16a8 147 "transform: translateX(26px);" \
hudakz 3:6edf142a16a8 148 "}" \
hudakz 3:6edf142a16a8 149 "</style>" \
hudakz 3:6edf142a16a8 150 "</head>" \
hudakz 4:c11d58a6c9e2 151
hudakz 4:c11d58a6c9e2 152 // Web page
hudakz 3:6edf142a16a8 153 "<body>"
hudakz 3:6edf142a16a8 154 "<h2><a href=\".\" title=\"Click to refresh the page\">Smart Home</a></h2>" \
hudakz 3:6edf142a16a8 155 "<pre>Temperature:\t" + string(roomTempStr) + "&deg;C</pre>" \
hudakz 3:6edf142a16a8 156 "<pre>Heating:\t";
hudakz 0:912fb893b03c 157
hudakz 0:912fb893b03c 158 if(status == ON) {
hudakz 3:6edf142a16a8 159 httpContent += \
hudakz 3:6edf142a16a8 160 "<a href=\"./?sw=0\" class=\"switch\"> " \
hudakz 3:6edf142a16a8 161 "<input type=\"checkbox\" checked>";
hudakz 0:912fb893b03c 162 }
hudakz 0:912fb893b03c 163 else {
hudakz 3:6edf142a16a8 164 httpContent += \
hudakz 3:6edf142a16a8 165 "<a href=\"./?sw=1\" class=\"switch\"> " \
hudakz 3:6edf142a16a8 166 "<input type=\"checkbox\">";
hudakz 0:912fb893b03c 167 }
hudakz 0:912fb893b03c 168
hudakz 3:6edf142a16a8 169 httpContent += \
hudakz 3:6edf142a16a8 170 "<div class=\"slider\"></div>" \
hudakz 3:6edf142a16a8 171 "</a></pre>" \
hudakz 3:6edf142a16a8 172 "<hr>" \
hudakz 3:6edf142a16a8 173 "<pre>2017 ARMmbed</pre>" \
hudakz 3:6edf142a16a8 174 "</body>";
hudakz 0:912fb893b03c 175
hudakz 0:912fb893b03c 176 return httpContent;
hudakz 0:912fb893b03c 177 }
hudakz 0:912fb893b03c 178
hudakz 0:912fb893b03c 179 /**
hudakz 0:912fb893b03c 180 * @brief
hudakz 0:912fb893b03c 181 * @note
hudakz 0:912fb893b03c 182 * @param
hudakz 0:912fb893b03c 183 * @retval
hudakz 0:912fb893b03c 184 */
hudakz 0:912fb893b03c 185 void sendHTTP(TCPSocket& client, string& header, string& content) {
hudakz 0:912fb893b03c 186 char content_length[5] = { };
hudakz 0:912fb893b03c 187
hudakz 3:6edf142a16a8 188 header += \
hudakz 3:6edf142a16a8 189 "\r\nContent-Type: text/html\r\n" \
hudakz 3:6edf142a16a8 190 "Content-Length: ";
hudakz 0:912fb893b03c 191 sprintf(content_length, "%d", content.length());
hudakz 3:6edf142a16a8 192 header += \
hudakz 3:6edf142a16a8 193 string(content_length) + "\r\n" \
hudakz 3:6edf142a16a8 194 "Pragma: no-cache\r\n" \
hudakz 3:6edf142a16a8 195 "Connection: About to close\r\n\r\n";
hudakz 0:912fb893b03c 196
hudakz 0:912fb893b03c 197 string webpage = header + content;
hudakz 0:912fb893b03c 198 client.send((char*)webpage.c_str(), webpage.length());
hudakz 0:912fb893b03c 199 pc.printf("HTTP message sent to client.\n\r");
hudakz 0:912fb893b03c 200 }
hudakz 0:912fb893b03c 201
hudakz 0:912fb893b03c 202 /**
hudakz 0:912fb893b03c 203 * @brief
hudakz 0:912fb893b03c 204 * @note
hudakz 0:912fb893b03c 205 * @param
hudakz 0:912fb893b03c 206 * @retval
hudakz 0:912fb893b03c 207 */
hudakz 0:912fb893b03c 208 int main(void) {
hudakz 0:912fb893b03c 209 pc.printf("Starting.. \r\n\r\n");
hudakz 0:912fb893b03c 210
hudakz 0:912fb893b03c 211 net = new EthernetInterface();
hudakz 0:912fb893b03c 212
hudakz 0:912fb893b03c 213 if (!net) {
hudakz 0:912fb893b03c 214 pc.printf("Error! No network inteface found.\n");
hudakz 0:912fb893b03c 215 return 0;
hudakz 0:912fb893b03c 216 }
hudakz 0:912fb893b03c 217
hudakz 0:912fb893b03c 218 //net->set_network (IP, NETMASK, GATEWAY); // include this for using static IP address
hudakz 0:912fb893b03c 219
hudakz 0:912fb893b03c 220 nsapi_size_or_error_t r = net->connect();
hudakz 0:912fb893b03c 221 if (r != 0) {
hudakz 0:912fb893b03c 222 pc.printf("Error! net->connect() returned: %d\n", r);
hudakz 0:912fb893b03c 223 return r;
hudakz 0:912fb893b03c 224 }
hudakz 0:912fb893b03c 225
hudakz 0:912fb893b03c 226 // Show the network address
hudakz 0:912fb893b03c 227 const char *ip = net->get_ip_address();
hudakz 0:912fb893b03c 228 const char *netmask = net->get_netmask();
hudakz 0:912fb893b03c 229 const char *gateway = net->get_gateway();
hudakz 0:912fb893b03c 230 pc.printf("IP address: %s\r\n", ip ? ip : "None");
hudakz 0:912fb893b03c 231 pc.printf("Netmask: %s\r\n", netmask ? netmask : "None");
hudakz 0:912fb893b03c 232 pc.printf("Gateway: %s\r\n\r\n", gateway ? gateway : "None");
hudakz 0:912fb893b03c 233 pc.printf("Usage: Type %s/%s/ into your web browser and hit ENTER\r\n\r\n", ip, PASSWORD.c_str());
hudakz 0:912fb893b03c 234
hudakz 0:912fb893b03c 235 /* Open the server on ethernet stack */
hudakz 0:912fb893b03c 236 server.open(net);
hudakz 0:912fb893b03c 237
hudakz 0:912fb893b03c 238 /* Bind the HTTP port (TCP 80) to the server */
hudakz 0:912fb893b03c 239 server.bind(ip, PORT);
hudakz 0:912fb893b03c 240
hudakz 0:912fb893b03c 241 /* Can handle 5 simultaneous connections */
hudakz 0:912fb893b03c 242 server.listen(5);
hudakz 0:912fb893b03c 243
hudakz 0:912fb893b03c 244 //listening for http GET request
hudakz 0:912fb893b03c 245 while(true) {
hudakz 0:912fb893b03c 246 pc.printf("=========================================\r\n");
hudakz 0:912fb893b03c 247 nsapi_error_t error;
hudakz 0:912fb893b03c 248 clientSocket = server.accept(&error);
hudakz 0:912fb893b03c 249 if (error != 0) {
hudakz 0:912fb893b03c 250 pc.printf("Connection failed!\r\n");
hudakz 0:912fb893b03c 251 continue;
hudakz 0:912fb893b03c 252 }
hudakz 0:912fb893b03c 253 clientSocket->getpeername(&clientAddress);
hudakz 0:912fb893b03c 254 pc.printf("Client with IP address %s connected.\r\n\r\n", clientAddress.get_ip_address());
hudakz 0:912fb893b03c 255 clientSocket->recv(receiveBuf, 1023);
hudakz 0:912fb893b03c 256 pc.printf("Data received:\r\n%s\n\r", receiveBuf);
hudakz 0:912fb893b03c 257
hudakz 0:912fb893b03c 258 string received(receiveBuf);
hudakz 0:912fb893b03c 259
hudakz 0:912fb893b03c 260 if(received.substr(0, 3) != "GET") {
hudakz 0:912fb893b03c 261 httpHeader = HTTP_OK;
hudakz 0:912fb893b03c 262 httpContent = "<h1>200 OK</h1>";
hudakz 0:912fb893b03c 263 sendHTTP(*clientSocket, httpHeader, httpContent);
hudakz 0:912fb893b03c 264 continue;
hudakz 0:912fb893b03c 265 }
hudakz 0:912fb893b03c 266
hudakz 0:912fb893b03c 267 if(received.substr(0, 6) == "GET / ") {
hudakz 0:912fb893b03c 268 httpHeader = HTTP_OK;
hudakz 0:912fb893b03c 269 httpContent = "<p>Usage: Type http://ip_address/password/ into your web browser and hit ENTER</p>\r\n";
hudakz 0:912fb893b03c 270 sendHTTP(*clientSocket, httpHeader, httpContent);
hudakz 0:912fb893b03c 271 continue;
hudakz 0:912fb893b03c 272 }
hudakz 0:912fb893b03c 273
hudakz 0:912fb893b03c 274 int cmd = analyseURL(received);
hudakz 0:912fb893b03c 275
hudakz 0:912fb893b03c 276 if(cmd == -2) {
hudakz 0:912fb893b03c 277
hudakz 0:912fb893b03c 278 // redirect to the right base url
hudakz 0:912fb893b03c 279 httpHeader = MOVED_PERM;
hudakz 0:912fb893b03c 280 sendHTTP(*clientSocket, httpHeader, movedPermanently(1));
hudakz 0:912fb893b03c 281 continue;
hudakz 0:912fb893b03c 282 }
hudakz 0:912fb893b03c 283
hudakz 0:912fb893b03c 284 if(cmd == -1) {
hudakz 0:912fb893b03c 285 httpHeader = UNAUTHORIZED;
hudakz 0:912fb893b03c 286 httpContent = "<h1>401 Unauthorized</h1>";
hudakz 0:912fb893b03c 287 sendHTTP(*clientSocket, httpHeader, httpContent);
hudakz 0:912fb893b03c 288 continue;
hudakz 0:912fb893b03c 289 }
hudakz 0:912fb893b03c 290
hudakz 0:912fb893b03c 291 if(cmd == 1) {
hudakz 0:912fb893b03c 292 sw = ON; // turn the switch on
hudakz 0:912fb893b03c 293 }
hudakz 0:912fb893b03c 294
hudakz 0:912fb893b03c 295 if(cmd == 0) {
hudakz 0:912fb893b03c 296 sw = OFF; // turn the switch off
hudakz 0:912fb893b03c 297 }
hudakz 0:912fb893b03c 298
hudakz 0:912fb893b03c 299 httpHeader = HTTP_OK;
hudakz 0:912fb893b03c 300 sendHTTP(*clientSocket, httpHeader, showWebPage(sw));
hudakz 0:912fb893b03c 301 clientSocket->close();
hudakz 0:912fb893b03c 302 }
hudakz 0:912fb893b03c 303 }