{"id":13781486,"url":"https://github.com/ovidiucp/TinyWebServer","last_synced_at":"2025-05-11T14:35:06.039Z","repository":{"id":142826241,"uuid":"2204224","full_name":"ovidiucp/TinyWebServer","owner":"ovidiucp","description":"Small web server for Arduino, fits in 10KB ROM, less than 512 bytes RAM","archived":false,"fork":false,"pushed_at":"2015-12-22T20:19:41.000Z","size":303,"stargazers_count":245,"open_issues_count":11,"forks_count":65,"subscribers_count":28,"default_branch":"master","last_synced_at":"2024-11-17T16:42:58.041Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://www.webweavertech.com/ovidiu/weblog/archives/000484.html","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-2.1","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ovidiucp.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-08-14T04:16:19.000Z","updated_at":"2024-10-18T05:40:17.000Z","dependencies_parsed_at":"2023-03-23T23:05:27.562Z","dependency_job_id":null,"html_url":"https://github.com/ovidiucp/TinyWebServer","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovidiucp%2FTinyWebServer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovidiucp%2FTinyWebServer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovidiucp%2FTinyWebServer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ovidiucp%2FTinyWebServer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ovidiucp","download_url":"https://codeload.github.com/ovidiucp/TinyWebServer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253580400,"owners_count":21930937,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-03T18:01:26.429Z","updated_at":"2025-05-11T14:35:05.575Z","avatar_url":"https://github.com/ovidiucp.png","language":"C++","readme":"Date: 2015-03-06 14:58:18\nArduino TinyWebServer\n\nA small web server for Arduino.\n\nCheck out this video demonstrating what you can do with it:\n\nhttp://www.youtube.com/watch?v=qZXKk6nCYuM\n\nYou can read up on TinyWebServer's history here:\n\nhttp://www.webweavertech.com/ovidiu/weblog/archives/000484.html\n\nhttp://www.webweavertech.com/ovidiu/weblog/archives/000477.html\n\nThe library is licensed under the terms of LGPL 2.1. Which means\nyou're free to use it in your projects (including commercial ones) as\nlong as you're sending back the changes you make to the library.\n\nExternal dependencies\n====================\n\nTinyWebServer depends on the external library Flash version 5.0, which\nis found here:\n\nhttp://arduiniana.org/libraries/flash/\n\nMake sure you dowload the Flash library and install it in your\nArduino's `libraries` directory, as described in this document:\n\nhttp://arduino.cc/en/Guide/Libraries\n\nIf you're using a version of Arduino IDE newer than 1.5, you need to\nmodify the `Flash.h` file to include the following lines just after\nthe `#include \u003cavr/pgmspace.h\u003e` line:\n\n```\n#if ARDUINO \u003e= 150\ntypedef char prog_char __attribute__((__progmem__));\n#endif\n```\n\nIf you're using an ARM Cortex M0-based board, such as the Arduino Zero\nor the MKR1000, you need to modify the `Flash.h` file to include the\nfollowing:\n\n```\n#if ARDUINO_ARCH_SAMD\nextern char* strncpy_P(char* dest, const char* src, int size);\n#endif\n```\n\nBasic web server\n================\n\nTo make use of the TinyWebServer library, you need to include the\nfollowing your sketch:\n\n    #include \u003cEthernet.h\u003e\n    #include \u003cFlash.h\u003e\n    #include \u003cSD.h\u003e\n    #include \u003cTinyWebServer.h\u003e\n\nTWS is implemented by the TinyWebServer class. The constructor method\ntakes two arguments. The first one is a list of handlers, functions to\nbe invoked when a particular URL is requested by an HTTP client. The\nsecond one is a list of HTTP header names that are needed by the\nimplementation of your handlers. More on these later.\n\nAn HTTP handler is a simple function that takes as argument a\nreference to the TinyWebServer object. When you create the\nTinyWebServer class, you need to pass in the handlers for the various\nURLs. Here is a simple example of a web server with a single handler.\n\n    static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };\n\n    boolean index_handler(TinyWebServer\u0026 web_server) {\n      web_server.send_error_code(200);\n      web_server \u003c\u003c F(\"\u003chtml\u003e\u003cbody\u003e\u003ch1\u003eHello World!\u003c/h1\u003e\u003c/body\u003e\u003c/html\u003e\\n\");\n      return true;\n    }\n\n    TinyWebServer::PathHandler handlers[] = {\n      // Register the index_handler for GET requests on /\n      {\"/\", TinyWebServer::GET, \u0026index_handler },\n      {NULL}, // The array has to be NULL terminated this way\n    };\n\n    // Create an instance of the web server. No HTTP headers are requested\n    // by the HTTP request handlers.\n    TinyWebServer web = TinyWebServer(handlers, NULL);\n\n    void setup() {\n      Serial.begin(115200);\n      EthernetDHCP.begin(mac);\n      web.begin();\n    }\n\n    void loop() {\n      EthernetDHCP.maintain();\n      web.process();\n    }\n\nIn the loop() function we need the call to the process() to make sure\nHTTP requests are serviced. If there is no new request, the method\nreturns immediately. Otherwise the process() method blocks until the\nrequest is handled.\n\nFor a complete working example look in\nTinyWebServer/example/SimpleWebServer.\n\nServing files from the SD card\n==============================\n\nNow that we've seen the basics, let's see how we can extend this web\nserver to serve files stored on the SD card. The idea is to register a\nhandler that serves any URLs. Once the handler is invoked, it\ninterprets the URL path as a file name on the SD card and returns\nthat.\n\n    boolean file_handler(TinyWebServer\u0026 web_server) {\n      char* filename = TinyWebServer::get_file_from_path(web_server.get_path());\n      if (!filename) {\n        web_server.send_error_code(404);\n        web_server \u003c\u003c \"Could not parse URL\";\n      } else {\n        TinyWebServer::MimeType mime_type\n          = TinyWebServer::get_mime_type_from_filename(filename);\n        web_server.send_error_code(mime_type, 200);\n        if (file.open(filename, O_READ)) {\n          web_server.send_file(file);\n          file.close();\n        } else {\n          web_server \u003c\u003c \"Could not find file: \" \u003c\u003c filename \u003c\u003c \"\\n\";\n        }\n        free(filename);\n      }\n      return true;\n    }\n\nWe can now register this in the handlers array:\n\n    TinyWebServer::PathHandler handlers[] = {\n      {\"/\" \"*\", TinyWebServer::GET, \u0026file_handler },\n      {NULL},\n    };\n\nNote how the URL for the HTTP request is specified. We want it to\nbe /*, very much like a regular expression. However Arduino's IDE\npreprocessor has a bug in how it handles /* inside strings. By\nspecifying the string as \"/\" \"*\" we avoid the bug, while letting the\ncompiler optimize and concatenate the two strings into a single one.\n\nThe * works only at the end of a URL, anywhere else it would be\ninterpreted as part of the URL. If the * is at the end of the URL, the\ncode in TinyWebServer assumes the handler can process requests that\nmatch the URL prefix. For example, if the URL string was /html/* then\nany URL starting with /html/ would be handled by the specified\nhandler. In our case, since we specified /*, any URL starting with /\n(except for the top level / URL) will invoke the specified handler.\n\nUploading files to the web server and store them on SD card's file system\n=========================================================================\n\nNow wouldn't it be nice to update Arduino's Web server files using\nHTTP? This way we can focus on building the actual interface with the\nhardware, and provide just enough HTTP handlers to interact with\nit. After we implement a minimal user interface, we can iterate it\nwithout having to remove the SD card from the embedded project, copy\nthe HTML, JavaScript and/or image files on a computer, and plug it\nback in. We could do this remotely from the computer, using a simple\nscript.\n\nTinyWebServer provides a simple file upload HTTP handler that uses the\nHTTP 1.0 PUT method. This allows you to implement an Ajax interface\nusing XMLHttpRequest or simply use a tool like curl to implement file\nuploads.\n\nHere's how you add file uploads to your Arduino web server:\n\n    TinyWebServer::PathHandler handlers[] = {\n      // `put_handler' is defined in TinyWebServer\n      {\"/upload/\" \"*\", TinyWebServer::PUT, \u0026TinyWebPutHandler::put_handler },\n      {\"/\" \"*\", TinyWebServer::GET, \u0026file_handler },\n      {NULL},\n\nNote that the order in which you declare the handlers is\nimportant. The URLs are matched in the order in which they are\ndeclared.\n\nThis is where the headers array mentioned before comes into\npicture. The put_handler makes use of the Content-Length. To avoid\nunnecessary work and minimize precious memory usage, TinyWebServer\ndoes not do any header processing unless it's instructed. To do so,\nyou need to declare an array of header names your handlers are\ninterested in. In this case, we need to add Content-Length.\n\n    const char* headers[] = {\n      \"Content-Length\",\n      NULL\n    };\n\nAnd we now initialize the instance of TinyWebServer like this:\n\n    TinyWebServer web = TinyWebServer(handlers, headers);\n\nThe put_handler method is really generic, it doesn't actually\nimplement the code to write the file to disk. Instead the method\nrelies on a user provided function that implements the actual\nlogic. This allows you to use a different file system implementation\nthan Fat16 or do something totally different than write the file to\ndisk.\n\nThe user provided function take 4 parameters. The first is a reference\nto the TinyWebServer instance. The second is a PutAction enum which\ncould be either START, WRITE or END. START and END are called exactly\nonce during a PUT handler's execution, while WRITE is called multiple\ntimes. Each time the function is called with the WRITE param, the\nthird and fourth parameters are set to a buffer and a number of bytes\nin this buffer that should be used.\n\nHere is a small example of a user provided function that writes the\nPUT request's content to a file:\n\n    void file_uploader_handler(TinyWebServer\u0026 web_server,\n                               TinyWebPutHandler::PutAction action,\n                               char* buffer, int size) {\n      static uint32_t start_time;\n\n      switch (action) {\n      case TinyWebPutHandler::START:\n        start_time = millis();\n        if (!file.isOpen()) {\n          // File is not opened, create it. First obtain the desired name\n          // from the request path.\n          char* fname = web_server.get_file_from_path(web_server.get_path());\n          if (fname) {\n            Serial \u003c\u003c \"Creating \" \u003c\u003c fname \u003c\u003c \"\\n\";\n            file.open(fname, O_CREAT | O_WRITE | O_TRUNC);\n            free(fname);\n          }\n        }\n        break;\n\n      case TinyWebPutHandler::WRITE:\n        if (file.isOpen()) {\n          file.write(buffer, size);\n        }\n        break;\n\n      case TinyWebPutHandler::END:\n        file.sync();\n        Serial \u003c\u003c \"Wrote \" \u003c\u003c file.fileSize() \u003c\u003c \" bytes in \"\n               \u003c\u003c millis() - start_time \u003c\u003c \" millis\\n\";\n        file.close();\n      }\n    }\n\nTo activate this user provided function, assign its address to\nput_handler_fn, like this:\n\n    void setup() {\n      // ...\n\n      // Assign our function to `upload_handler_fn'.\n      TinyWebPutHandler::put_handler_fn = file_uploader_handler;\n\n      // ...\n    }\n\nYou can now test uploading a file using curl:\n\n*Note that since the handler in the source looks like this:\n\n{\"/upload/\" \"*\", TinyWebServer::PUT, \u0026TinyWebPutHandler::put_handler }\n\nyou must ensure that the path '/upload/' is in your submitted URL\n\n\n    curl -0 -T index.htm http://my-arduino-ip-address/upload/\n\nFor a complete working example of the file upload and serving web\nserver, look in TinyWebServer/examples/FileUpload.\n\nAdvanced topic: persistent HTTP connections\n===========================================\n\nSometimes it's useful to have an HTTP client start a request. For\nexample, I need to be able to enter an IR learning process. This means\nthat I cannot afford TinyWebServer's process() to block while serving\nmy /learn request that initiated the IR learning process. Instead I\nwant the handler of the /learn request to set a variable in the code\nthat indicates that IR learning is active, and then return\nimmediately.\n\nIf you noticed the HTTP handlers return a boolean. If the returned\nvalue is true, as it was the case in our examples above, the\nconnection to the HTTP client is closed immediately. If the returned\nvalue is false the connection is left open. Your handler should save\nthe Client object handling the HTTP connection with the original\nrequest. Your code becomes responsible with closing it when it's no\nlonger needed.\n\nTo obtain the Client object, use the get_client() method while in the\nHTTP handler. You can write asynchronously to the client, to update it\nwith the state of the web server.\n\nIn my remotely controlled projection screen application, I have\nanother handler on /cancel that closes the /learn client\nforcibly. Otherwise the /learn's Client connection is closed at the\nend of the IR learning procedure. Since the Ethernet shield only\nallows for 4 maximum HTTP clients open at the same time (because of 4\nmaximum client sockets), in my application I allow only one /learn\nhandler to be active at any given time.\n","funding_links":[],"categories":["Libraries"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovidiucp%2FTinyWebServer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fovidiucp%2FTinyWebServer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fovidiucp%2FTinyWebServer/lists"}