... from your ESP32
"); // The image resource is created in the awesomeCallback some lines below res->println("System has been up for "); res->print((int)(millis()/1000), DEC); res->println(" seconds.
"); res->println(""); res->println(""); } /** * Binary data is also supported. We use it to provide a fancy * favicon at /favicon.ico. * * Again, calls to setHeader etc. have to be made before using * write or print. */ void faviconCallback(HTTPRequest * req, HTTPResponse * res) { res->setHeader("Content-Type", "image/vnd.microsoft.icon"); res->write(FAVICON_DATA, FAVICON_LENGTH); } /** * The URL Param Callback demonstrates the usage of placeholders in the URL. * * This callback function is mapped to "param/ * / *" (ignore the spaces, they are required * because of the C comment syntax). * * The placeholder values can be accessed through HTTPRequest::getParams. They are indexed * beginning from 0. */ void urlParamCallback(HTTPRequest * req, HTTPResponse * res) { // Get access to the parameters ResourceParameters * params = req->getParams(); // Set a simple content type res->setHeader("Content-Type", "text/plain"); // Print the first parameter res->print("Parameter 1: "); res->printStd(params->getUrlParameter(0)); // Print the second parameter res->print("\nParameter 2: "); res->printStd(params->getUrlParameter(1)); } /** * This callback responds with an SVG image to a GET request. The icon is the awesome smiley. * * If the color request parameter is set (so the URL is like awesome.svg?color=fede58), the * background of our awesome face is changed. */ void awesomeCallback(HTTPRequest * req, HTTPResponse * res) { // Get access to the parameters ResourceParameters * params = req->getParams(); // Set SVG content type res->setHeader("Content-Type", "image/svg+xml"); // Check if there is a suitabel fill color in the parameter: std::string fillColor = "fede58"; // Get request parameter std::string colorParamName = "color"; if (params->isRequestParameterSet(colorParamName)) { std::string requestColor = params->getRequestParameter(colorParamName); if (requestColor.length()==6) { bool colorOk = true; for(int i = 1; i < 6 && colorOk; i++) { if (!( (requestColor[i]>='0' && requestColor[i]<='9' ) || (requestColor[i]>='a' && requestColor[i]<='f' ) )) { colorOk = false; } } if (colorOk) { fillColor = requestColor; } } } // Print the data // Source: https://commons.wikimedia.org/wiki/File:718smiley.svg res->print(""); res->print(""); } /** * This callback is configured to match all OPTIONS requests (see pattern configuration below) * * This allows to define headers there that are required to allow cross-domain-xhr-requests, * which enabled a REST-API that can be used on the esp32, while the WebInterface is hosted * somewhere else (on a host with more storage space to provide huge JS libraries etc.) * * An example use case would be an IoT dashboard that connects to a bunch of local esp32s, * which provide data via their REST-interfaces that is aggregated in the dashboard. */ void corsCallback(HTTPRequest * req, HTTPResponse * res) { res->setHeader("Access-Control-Allow-Methods", "HEAD,GET,POST,DELETE,PUT,OPTIONS"); res->setHeader("Access-Control-Allow-Origin", "*"); res->setHeader("Access-Control-Allow-Headers", "*"); } /** * This callback simply copies the requests body into the response body. * * It can be used to test POST and PUT functionality and is configured to reply to * POST /echo and PUT /echo */ void echoCallback(HTTPRequest * req, HTTPResponse * res) { res->setHeader("Content-Type","text/plain"); byte buffer[256]; while(!(req->requestComplete())) { size_t s = req->readBytes(buffer, 256); res->write(buffer, s); } } /** * This callback will be registered as default callback. The default callback is used * if no other node matches the request. * * Again, another content type is shown (json). */ void notfoundCallback(HTTPRequest * req, HTTPResponse * res) { // Discard the request body, as the 404-handler may also be used for put and post actions req->discardRequestBody(); res->setStatusCode(404); res->setStatusText("Not found"); res->setHeader("Content-Type", "application/json"); res->print("{\"error\":\"not found\", \"code\":404}"); } //The setup function is called once at startup of the sketch void setup() { Serial.begin(115200); Serial.println("setup()"); // Setup wifi. We use the configuration stored at data/wifi/wifi.h // If you don't have that file, make sure to copy the wifi.example.h, // rename it and also configure your wifi settings there. Serial.print("Connecting WiFi"); WiFi.begin(WIFI_SSID, WIFI_PSK); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(100); } Serial.println(" connected."); // Setup the server as a separate task. // // Important note: If the server is launched as a different task, it has its own // stack. This means that we cannot use globally instantiated Objects there. // -> Make sure to create Server, ResourceNodes, etc. in the function where they // are used (serverTask() in this case). // Another alternative would be to instantiate the objects on the heap. This is // especially important for data that should be accessed by the main thread and // the server. Serial.println("Creating server task... "); // If stack canary errors occur, try to increase the stack size (3rd parameter) // or to put as much stuff as possible onto the heap (ResourceNodes etc) xTaskCreatePinnedToCore(serverTask, "https443", 4096, NULL, 1, NULL, ARDUINO_RUNNING_CORE); Serial.println("Beginning to loop()..."); } // The loop function is called in an endless loop void loop() { // Use your normal loop without thinking of the server in the background Serial.println("Hello from main loop!"); // Delay for about half a minute and print some message on the Serial console. delay(30*1000); } /** * As mentioned above, the serverTask method contains the code to start the server. * * The infinite loop in the function is the equivalent for the loop() function of a * regular Arduino sketch. */ void serverTask(void *params) { Serial.println("Configuring Server..."); // Define the certificate that should be used // See files in tools/cert on how to create the headers containing the certificate. // Because they are just byte arrays, it would also be possible to store and load them from // non-volatile memory after creating them on the fly when the device is launched for the // first time. SSLCert cert = SSLCert( example_crt_DER, example_crt_DER_len, example_key_DER, example_key_DER_len ); // The faviconCallback now is assigned to the /favicon.ico node, when accessed by GET // This means, it can be accessed by opening https://myesp/favicon.ico in all // web browsers. Most browser fetch this file in background every time a new webserver // is used to show the icon in the tab of that website. ResourceNode * faviconNode = new ResourceNode("/favicon.ico", "GET", &faviconCallback); // The awesomeCallback is very similar to the favicon. ResourceNode * awesomeNode = new ResourceNode("/images/awesome.svg", "GET", &awesomeCallback); // A simple callback showing URL parameters. Every asterisk (*) is a placeholder value // So, the following URL has two placeholders that have to be filled. // This is especially useful for REST-APIs where you want to represent an object ID in the // url. Placeholders are arbitrary strings, but may be converted to integers (Error handling // is up to the callback, eg. returning 404 if there is no suitable resource for that placeholder // value) ResourceNode * urlParamNode = new ResourceNode("/param/*/*", "GET", &urlParamCallback); // The echoCallback is configured on the path /echo for POST and PUT requests. It just copies request // body to response body. To enable it for both methods, two nodes have to be created: ResourceNode * echoNodePost = new ResourceNode("/echo", "POST", &echoCallback); ResourceNode * echoNodePut = new ResourceNode("/echo", "PUT", &echoCallback); // The root node (on GET /) will be called when no directory on the server is specified in // the request, so this node can be accessed through https://myesp/ ResourceNode * rootNode = new ResourceNode("/", "GET", &testCallback); // As mentioned above, we want to answer all OPTIONS requests with a response that allows // cross-domain XHR. To do so, we bind the corsCallback to match all options request // (we can exploit the asterisk functionality for this. The callback is not required to // process the parameters in any way.) // Note the difference to the "/" in the rootNode above - "/" matches ONLY that specific // resource, while slash and asterisk is more or less provides a catch all behavior ResourceNode * corsNode = new ResourceNode("/*", "OPTIONS", &corsCallback); // The not found node will be used when no other node matches, and it's configured as // defaultNode in the server. // Note: Despite resource and method string have to be specified when a node is created, // they are ignored for the default node. However, this makes it possible to register another // node as default node as well. ResourceNode * notFoundNode = new ResourceNode("/", "GET", ¬foundCallback); // Create the server. The constructor takes some optional parameters, eg. to specify the TCP // port that should be used. However, defining a certificate is mandatory. HTTPSServer server = HTTPSServer(&cert); // Register the nodes that have been configured on the web server. server.setDefaultNode(notFoundNode); server.registerNode(rootNode); server.registerNode(faviconNode); server.registerNode(awesomeNode); server.registerNode(urlParamNode); server.registerNode(echoNodePost); server.registerNode(echoNodePut); server.registerNode(corsNode); // Add a default header to the server that will be added to every response. In this example, we // use it only for adding the server name, but it could also be used to add CORS-headers to every response server.setDefaultHeader("Server", "esp32-http-server"); // The web server can be start()ed and stop()ed. When it's stopped, it will close its server port and // all open connections and free the resources. Theoretically, it should be possible to run multiple // web servers in parallel, however, there might be some restrictions im memory. Serial.println("Starting Server..."); server.start(); // We check whether the server did come up correctly (it might fail if there aren't enough free resources) if (server.isRunning()) { // If the server is started, we go into our task's loop Serial.println("Server started."); while(1) { // Run the server loop. // This loop function accepts new clients on the server socket if there are connection slots available // (see the optional parameter maxConnections on the HTTPSServer constructor). // It also frees resources by connections that have been closed by either the client or the application. // Finally, it calls the loop() function of each active HTTPSConnection() to process incoming requests, // which will finally trigger calls to the request handler callbacks that have been configured through // the ResourceNodes. server.loop(); delay(1); } } else { // For the sake of this example, we just restart the ESP in case of failure and hope it's getting better // next time. Serial.println("Starting Server FAILED! Restart in 10 seconds"); delay(10000); ESP.restart(); } }