Yet another simple and minimalistic webserver for ESP8266
Yet another simple and minimalistic webserver for ESP8266
In almost all your ESP8266 projects you need to setup WiFi connection parameters(SSID, password) to connect with your home router.
Hardcoding this parameters is definitely not a good idea. If you change router settings, or even take your device somewhere else you will need to recompile your code - sounds bad isn’t it?
esp_nano_httpd will help you ( ͡° ͜ʖ ͡°)
Thats all - its only simple and robust webserver. If you need more features like image hosting, web sockets and so on use fantastic libesphttpd by Sprite_tm.
Copy esp_nano_httpd directory into your project source tree.
Add esp_nano_httpd module to be compiled in Makefile (I recommend to use this Makefile template for NONOS SDK projects)
# which modules (subdirectories) of the project to include in compiling
MODULES = driver user esp_nano_httpd
#include "../esp_nano_httpd/esp_nano_httpd.h
Now it’s time to prepare our HTML content…
If you ever tried manually include HTML code inside C file you know the pain when you mixing two programming languages - all damn backslashes before quotation marks, no web browser preview, etc. (╯°□°)╯︵ ┻━┻
Not this time… I’ll show you the trick ( ͡~ ͜ʖ ͡°)
…
Copy html directory from esp_nano_httpd_basic_example. Add there your own html files. You will find small and handy shell script gen_includes.sh
It will create C header files in include directory from your html files by using Linux xxd tool. Just use it!
Okay. We have our html content easy to include into C code, and esp_nano_httpd . Lets connect them together.
Create url config table inside your user_main.c file:
```c
//include your html pages here
//and create URL config table
const http_callback_t url_conf[] = {
{“/“, send_html, index_html, sizeof(index_html)},
{“/about”, send_html, about_html, sizeof(about_html)},
{0,0,0,0} //last item always 0
};
`const http_callback_t url_conf[]` explained:
| __server path__ | __callback function___ | __callback arg__ | __callback arg lenght__ |
| --------------- |:----------------------:| -----------------:| -----------------------:|
| "/" | send_html | index_html | sizeof(index_html) |
| "/about" | send_html | about_html | sizeof(about_html) |
- __server path__ - http request server path. When internet browser comunicates with ESP8266 device sends HTTP requests to *ESP_IP*__/path__ .
__"/"__ is device root path. When you simply type *ESP_IP* for example http://192.168.4.1 into your web browser address bar it will request device root path and ESP device will respond by sending to the browser __index.html__ page.
If you want to access some other pages by path add it after *ESP_IP* address. To see __about.html__ page type *ESP_IP*__/about__ for eg. http://192.168.4.1/about
- __callback function___ - this function is called when ESP8266 device will recieve HTTP request with matching __server path__
In this example We are using `send_html` function from __esp_nano_httpd__ API to make things easy. You can use your own callback functions, to do device function, or send json data etc. (see below).
- __callback arg__ - calllback function argument passed to __callback function___ in this example its html page code in array index_html.
- __callback arg lenght__ - calllback function argument length. __callback arg__ is not always null terminated string, it might be binary data, and sender needs to know its's length.
7. Now it's time to tell __esp_nano_httpd__ how to handle requests:
```c
esp_nano_httpd_register_content(url_conf);
and start it when needed:
esp_nano_httpd_init_AP(STATIONAP_MODE, "ESP-LED",NULL); //when used as AP*
//or
esp_nano_httpd_init(); //when used as router client
NOTE: Two last bytes of your ESP8266 device MAC address will be added at the end of AP name to create unique AP names for multiple ESP8266 devices.
For example:define NANOHTTPDAPNAME _“ESP-LED”
- device 1 AP name: ESP-LED-13DF
- device 2 AP name: ESP-LED-C0A1
…- device x AP name: ESP-LED-XXXX
The moment and mode when you need to init esp_nano_httpd is dependent on your application.
Typical workflow is:
and/or
void esp_nano_httpd_register_content(const http_callback_t *content_info)
- used to pass to the esp_nano_httpd bullit in URL table with callback functions.
void esp_nano_httpd_init(uint8_t wifi_mode)
- used to initialize esp_nano_httpd web server.
wifi_mode
= STATIONAP_MODE - ESP8266 will create an open Access Point with SSID defined by NANO_HTTPD_AP_NAME
in user_config.h wifi_mode
= STATION_MODE - ESP8266 will only create server and listen HTTP requests on port 80.void send_http_response(struct espconn *conn, const char *code, const char *cont_type, const char *content, uint32_t cont_len)
- used to send various HTTP response types.
struct espconn *conn
- pointer to current connection structureconst char *code
- response HTTP status code const char *cont_type
- response content typeconst char *cont
- pointer to response content uint32_t cont_len
- response content lengthvoid send_html(struct espconn *conn, void *arg, uint32_t len)
- send HTML page(basic callback function - more details below)
void resp_http_ok(struct espconn *conn)
- send HTTP OK status(status code: 200)
void resp_http_404(struct espconn *conn)
- send HTTP Not Found status status(status code: 404)
void resp_http_error(struct espconn *conn)
- send HTTP Internal Error status(status code: 500)
Until now We know how to use esp_nano_httpd to send our html files only. Here is how to write other functions that will handle another requests, and allow us to make some real actions on ESP8266 device like changing options, setting GPIOs, controling peripherials, reading sensors on web browser user demand.
All callback functions should be designed like:
void ICACHE_FLASH_ATTR http_callback_fun(struct espconn *conn, void *arg, uint32_t len)
{
http_request_t *req = conn->reverse; //get parsed request
}
Input arguments:
struct espconn *conn
- current TCP connection. It is used to point where to send requested data. http_request_t *req
- received and parsed HTTP request. Defined in esp_nano_httpd.hUse it to check what type of request has been received, read request path, query string, and content
typedef struct {
enum req_type {
TYPE_UNKNOWN = 0,
TYPE_GET = 1,
TYPE_POST = 2
} type;
const char* path;
const char* query;
const char* content_type;
uint32_t content_len;
void *content;
//things below are used for long requests that doesn't fit into read buffer
enum {
REQ_GOT_HEADER = 0, //is returned when got request header
REQ_CONTENT_PART = 1 //is returned when got part of request content
} read_state;
uint32_t cont_part_len; //length of content part
uint32_t cont_bytes_left; //content bytes left to receive
} http_request_t;
void *arg
- callback function argument. It might be any data used in callback function. It might be pointer to LED driver, because this callback function is used to setup RGB led color, HTML page as in html_send() function.
uint32_t len
- callback function argument length in bytes
This example callback is used to change wifi station settings:
#include "../html/include/wifi_connect_html.h" //here is our wifi connection status page
void ICACHE_FLASH_ATTR wifi_config_cb(struct espconn *conn, void *arg, uint32_t len)
{
struct station_config station_conf = {0};
char *param;
http_request_t *req = conn->reverse; //get parsed request
if(req == NULL)
return;
//We only handle POST requests
if(req->type != TYPE_POST || req->content == NULL)
return resp_http_error(conn);
/* in request content We expect serialized input form query like: ssid=MY_SSID&passwd=MY_PASSWD
Use strtok to divide query into tokens*/
param=strtok(req->content,"&");
do {
if( os_memcmp(param,"ssid=",5) == 0 ) //ssid value found
ets_strncpy(station_conf.ssid, strchr(param,'=')+1,32);
else if( os_memcmp(param,"passwd=",7) == 0 ) //password value found
ets_strncpy(station_conf.password, strchr(param,'=')+1,64);
} while( (param=strtok(NULL,"&")) != NULL);
station_conf.bssid_set = 0; //do not look for specific router MAC address
wifi_station_set_auto_connect(0); //disable autoconnect
wifi_station_set_config(&station_conf); //save new WiFi settings
wifi_station_connect(); //connect to network
send_html(conn, wifi_connect_html, sizeof(wifi_connect_html)); //show HTML page
}
Next we need to add the <form>
in our index.html file to get WiFi SSID and password:
<p><strong>WiFi configuration:<strong></p>
<form method="post" action="/wifi_conf">
SSID<br>
<input type="text" name="ssid" pattern="[A-Za-z0-9]{1,32}" required="required" title="Access Point name"><br>
password<br>
<input type="text" name="passwd" pattern="^\S{0,64}$" title="password 0-64 characters"><br>
<input type="submit" value="WiFi connect">
</form>
When function is ready all you need to do is to connect it to path in url_config in user_main.c
const http_callback_t url_cfg[] = {
{"/", send_html, index_html, sizeof(index_html)},
{"/demo", led_demo_cb, NULL, 0},
{"/wifi_conf", wifi_config_cb, NULL, 0 },
{0,0,0}
};