blinkenstrip

Documentation: http://frombelow.net/projects/blinkenstrip/
Clone: git clone https://git.frombelow.net/blinkenstrip.git
Log | Files | Refs | README | LICENSE

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, &parameters_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 /* }}} */