webserver.c (10624B)
1 /* 2 Wifi connection & web server 3 4 The wifi code is nearly identicial to the examples 5 examples/wifi/getting_started/station/main/station_example_main.c 6 and examples/wifi/getting_started/softAP/main/softap_example_main.c 7 which are part of the esp-idf distribution. This example code is in 8 the Public Domain (or CC0 licensed, at your option.) 9 10 Functions http_server_netconn_serve and http_server are (partly) 11 taken from 12 https://github.com/cmmakerclub/esp32-webserver/blob/master/main/main.c, 13 copyright (C) 2016 Espressif Systems, licensed under the Apache 14 License 2.0. 15 16 Everything else: 17 18 Copyright © 2021 Gerd Beuster <gerd@frombelow.net> 19 20 Licensed under the Apache License, Version 2.0 (the "License"); 21 you may not use this file except in compliance with the License. 22 You may obtain a copy of the License at 23 24 http://www.apache.org/licenses/LICENSE-2.0 25 26 Unless required by applicable law or agreed to in writing, software 27 distributed under the License is distributed on an "AS IS" BASIS, 28 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 See the License for the specific language governing permissions and 30 limitations under the License. 31 */ 32 33 #include <string.h> 34 #include "freertos/FreeRTOS.h" 35 #include "freertos/task.h" 36 #include "freertos/event_groups.h" 37 #include "esp_system.h" 38 #include "esp_wifi.h" 39 #include "esp_event.h" 40 #include "esp_log.h" 41 #include "nvs_flash.h" 42 43 #include "lwip/err.h" 44 #include "lwip/sys.h" 45 46 #include "webserver.h" 47 #include "debug.h" 48 49 50 /* FreeRTOS event group to signal when we are connected*/ 51 static EventGroupHandle_t s_wifi_event_group; 52 53 /* The event group allows multiple bits for each event, but we only care about two events: 54 * - we are connected to the AP with an IP 55 * - we failed to connect after the maximum amount of retries */ 56 #define WIFI_CONNECTED_BIT BIT0 57 #define WIFI_FAIL_BIT BIT1 58 59 static const char *TAG = "webserver"; 60 61 static void event_handler_join_network(void* arg, esp_event_base_t event_base, 62 int32_t event_id, void* event_data) 63 { 64 if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { 65 esp_wifi_connect(); 66 } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { 67 esp_wifi_connect(); 68 } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { 69 ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 70 ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip)); 71 xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); 72 } 73 } 74 75 static void event_handler_run_ap(void* arg, esp_event_base_t event_base, 76 int32_t event_id, void* event_data) 77 { 78 if (event_id == WIFI_EVENT_AP_STACONNECTED) { 79 wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; 80 ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", 81 MAC2STR(event->mac), event->aid); 82 } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { 83 wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; 84 ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", 85 MAC2STR(event->mac), event->aid); 86 } 87 } 88 89 void initialise_wifi(uint8_t wifi_ap, char *wifi_essid, 90 char *wifi_password) 91 { 92 s_wifi_event_group = xEventGroupCreate(); 93 ESP_ERROR_CHECK(esp_netif_init()); 94 ESP_ERROR_CHECK(esp_event_loop_create_default()); 95 DEBUG_MSG(DEBUG, ("wifi_ap is %d\n", wifi_ap)); 96 if(wifi_ap){ 97 DEBUG_MSG(DEBUG, ("Starting wifi access point.\n")); 98 // Run our own WIFI access point. 99 esp_netif_create_default_wifi_ap(); 100 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 101 ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 102 ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, 103 &event_handler_run_ap, NULL)); 104 wifi_config_t wifi_config = { 105 .ap = { 106 .channel = 0, 107 .max_connection = 4, 108 .authmode = WIFI_AUTH_WPA_WPA2_PSK 109 }, 110 }; 111 if (strlen((char *) wifi_password) == 0) { 112 wifi_config.ap.authmode = WIFI_AUTH_OPEN; 113 } 114 else { 115 strncpy((char *) wifi_config.ap.password, wifi_password, 64); 116 } 117 strncpy((char *) wifi_config.ap.ssid, wifi_essid, 32); 118 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); 119 ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); 120 ESP_ERROR_CHECK(esp_wifi_start()); 121 ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s", 122 wifi_config.ap.ssid, wifi_config.ap.password); 123 } 124 else{ 125 // Connect to existing WIFI network. 126 esp_netif_create_default_wifi_sta(); 127 wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 128 ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 129 ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, 130 &event_handler_join_network, 131 NULL)); 132 ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, 133 &event_handler_join_network, 134 NULL)); 135 wifi_config_t wifi_config = { 136 .sta = { 137 /* Setting a password implies 138 * station will connect to 139 * all security modes 140 * including WEP/WPA. 141 * However these modes are 142 * deprecated and not 143 * advisable to be 144 * used. Incase your Access 145 * point doesn't support 146 * WPA2, these mode can be 147 * enabled by commenting 148 * below line */ 149 .threshold.authmode = WIFI_AUTH_WPA2_PSK, 150 .pmf_cfg = { 151 .capable = true, 152 .required = false 153 }, 154 }, 155 }; 156 strncpy((char *) wifi_config.sta.ssid, wifi_essid, 32); 157 strncpy((char *) wifi_config.sta.password, wifi_password, 64); 158 ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 159 ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); 160 ESP_ERROR_CHECK(esp_wifi_start() ); 161 162 ESP_LOGI(TAG, "wifi_init_sta finished."); 163 164 /* Waiting until either the connection is established 165 * (WIFI_CONNECTED_BIT) or connection failed for the maximum 166 * number of re-tries (WIFI_FAIL_BIT). The bits are set by 167 * event_handler_join_network() (see above) */ 168 EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, 169 WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, 170 pdFALSE, 171 pdFALSE, 172 portMAX_DELAY); 173 174 /* xEventGroupWaitBits() returns the bits before the call 175 * returned, hence we can test which event actually happened. */ 176 if (bits & WIFI_CONNECTED_BIT) { 177 ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", 178 wifi_essid, wifi_password); 179 } else if (bits & WIFI_FAIL_BIT) { 180 ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", 181 wifi_essid, wifi_password); 182 } else { 183 ESP_LOGE(TAG, "UNEXPECTED EVENT"); 184 } 185 186 ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, 187 &event_handler_join_network)); 188 ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, 189 &event_handler_join_network)); 190 vEventGroupDelete(s_wifi_event_group); 191 } 192 } 193 194 195 196 /* {{{ Webserver */ 197 198 // Retrieve value of parameter key starting at begin. 199 // Begin and end of the value string are stored in value_begin and value_end. 200 // The function returns true if the key was found, false otherwise. 201 bool url_key_value(char *begin, const char *key, 202 char **value_begin, char **value_end) { 203 if ((begin == NULL) || (begin[0] == '\0') || (begin[0] == ' ')) { 204 // End of GET request reached and key not found. 205 return (false); 206 } 207 else { 208 if ((strncmp(key, begin, strlen(key)) == 0) && 209 (*(begin+strlen(key)) == '=')) { 210 // Key found! Return value! 211 *value_begin = begin+strlen(key)+1; 212 *value_end = *value_begin + strcspn(*value_begin, " &"); 213 return true; 214 } 215 else { 216 // Continue search recursively 217 begin = begin + strcspn(begin, " &"); 218 if((begin[0] != '\0') && (begin[0] != ' ')) { 219 begin++; 220 } 221 return url_key_value(begin, key, value_begin, value_end); 222 } 223 } 224 } 225 226 // Get URL without any parameters. 227 void base_url(char *url_with_parameters, 228 char **base_url_begin, char **base_url_end, 229 char **parameters_start) { 230 bool get_request = !strncmp(url_with_parameters, "GET", 3); 231 if(get_request){ 232 *base_url_begin = url_with_parameters + 4; 233 *base_url_end = *base_url_begin + strcspn(*base_url_begin, " ?"); 234 *parameters_start = *base_url_end+1; 235 } 236 else{ 237 *base_url_begin = url_with_parameters + 5; 238 *base_url_end = *base_url_begin + strcspn(*base_url_begin, " ?"); 239 *parameters_start = strstr(*base_url_end, "\r\n\r\n")+4; 240 } 241 } 242 243 static void http_server_netconn_serve(struct netconn *conn) 244 { 245 struct netbuf *inbuf; 246 char *buf; 247 u16_t buflen; 248 err_t err; 249 250 /* Read the data from the port, blocking if nothing yet there. 251 We assume the request (the part we care about) is in one netbuf */ 252 err = netconn_recv(conn, &inbuf); 253 254 if (err == ERR_OK) { 255 netbuf_data(inbuf, (void**)&buf, &buflen); 256 // Terminate buffer string. 257 *(buf+buflen) ='\0'; 258 DEBUG_MSG(DEBUG, ("Receveid HTTP request:\n%s\n", buf)); 259 char *base_url_begin, *base_url_end, *parameters_start; 260 base_url(buf, &base_url_begin, &base_url_end, ¶meters_start); 261 // Dispatch page handler 262 uint8_t i = 0; 263 while ((i < number_of_page_handlers) && 264 strncmp(page_handlers[i].url, base_url_begin, 265 base_url_end-base_url_begin)){ 266 i++; 267 }; 268 if(i == number_of_page_handlers){ 269 // No handler for this URL. Return error page. 270 const char error404[] = 271 "HTTP/1.1 404 Not Found\r\n\r\n<html><head><title>Not Found!</title></head><body><h1>Not Found!</body></html>\r\n"; 272 netconn_write(conn, error404, sizeof(error404)-1, NETCONN_NOCOPY); 273 } 274 else{ 275 // Serve page 276 (page_handlers[i].handler)(conn, base_url_begin, base_url_end, 277 parameters_start); 278 } 279 } 280 /* Close the connection (server closes in HTTP) */ 281 netconn_close(conn); 282 283 /* Delete the buffer (netconn_recv gives us ownership, 284 so we have to make sure to deallocate the buffer) */ 285 netbuf_delete(inbuf); 286 } 287 288 void http_server(void *pvParameters) 289 { 290 struct netconn *conn, *newconn; 291 err_t err; 292 conn = netconn_new(NETCONN_TCP); 293 netconn_bind(conn, NULL, 80); 294 netconn_listen(conn); 295 do { 296 err = netconn_accept(conn, &newconn); 297 if (err == ERR_OK) { 298 http_server_netconn_serve(newconn); 299 netconn_delete(newconn); 300 } 301 } while(err == ERR_OK); 302 netconn_close(conn); 303 netconn_delete(conn); 304 } 305 306 /* }}} */