Browse Source

New AP list implementation

- AP List now uses cJSON, which should be more robust than sprintf.
- Max number of access points reported now aligned with espressif's max.
- WiFi logs no longer suppressed by the wifi_manager
Sebastien 5 years ago
parent
commit
e4374f8554

+ 3 - 2
components/wifi-manager/CMakeLists.txt

@@ -1,7 +1,8 @@
-idf_component_register(SRCS "dns_server.c" "http_server.c" "json.c" "wifi_manager.c"
+idf_component_register(SRCS "dns_server.c" "http_server.c" "wifi_manager.c"
                        INCLUDE_DIRS .
                        REQUIRES esp_common 
-                       PRIV_REQUIRES newlib freertos  spi_flash nvs_flash mdns pthread wpa_supplicant cmd_system json
+                       PRIV_REQUIRES newlib freertos  spi_flash nvs_flash mdns pthread wpa_supplicant cmd_system 
                        EMBED_FILES style.css code.js index.html bootstrap.min.css.gz jquery.min.js.gz popper.min.js.gz bootstrap.min.js.gz
 
 )
+ 

+ 0 - 144
components/wifi-manager/json.c

@@ -1,144 +0,0 @@
-/*
-@file json.c
-@brief handles very basic JSON with a minimal footprint on the system
-
-This code is a lightly modified version of cJSON 1.4.7. cJSON is licensed under the MIT license:
-Copyright (c) 2009 Dave Gamble
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
-OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-@see https://github.com/DaveGamble/cJSON
-*/
-
-#include "json.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdbool.h>
-
-
-bool json_print_string(const unsigned char *input, unsigned char *output_buffer)
-{
-	const unsigned char *input_pointer = NULL;
-	unsigned char *output = NULL;
-	unsigned char *output_pointer = NULL;
-	size_t output_length = 0;
-	/* numbers of additional characters needed for escaping */
-	size_t escape_characters = 0;
-
-	if (output_buffer == NULL)
-	{
-		return false;
-	}
-
-	/* empty string */
-	if (input == NULL)
-	{
-		//output = ensure(output_buffer, sizeof("\"\""), hooks);
-		if (output == NULL)
-		{
-			return false;
-		}
-		strcpy((char*)output, "\"\"");
-
-		return true;
-	}
-
-	/* set "flag" to 1 if something needs to be escaped */
-	for (input_pointer = input; *input_pointer; input_pointer++)
-	{
-		if (strchr("\"\\\b\f\n\r\t", *input_pointer))
-		{
-			/* one character escape sequence */
-			escape_characters++;
-		}
-		else if (*input_pointer < 32)
-		{
-			/* UTF-16 escape sequence uXXXX */
-			escape_characters += 5;
-		}
-	}
-	output_length = (size_t)(input_pointer - input) + escape_characters;
-
-	/* in the original cJSON it is possible to realloc here in case output buffer is too small.
-	 * This is overkill for an embedded system. */
-	output = output_buffer;
-
-	/* no characters have to be escaped */
-	if (escape_characters == 0)
-	{
-		output[0] = '\"';
-		memcpy(output + 1, input, output_length);
-		output[output_length + 1] = '\"';
-		output[output_length + 2] = '\0';
-
-		return true;
-	}
-
-	output[0] = '\"';
-	output_pointer = output + 1;
-	/* copy the string */
-	for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
-	{
-		if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
-		{
-			/* normal character, copy */
-			*output_pointer = *input_pointer;
-		}
-		else
-		{
-			/* character needs to be escaped */
-			*output_pointer++ = '\\';
-			switch (*input_pointer)
-			{
-			case '\\':
-				*output_pointer = '\\';
-				break;
-			case '\"':
-				*output_pointer = '\"';
-				break;
-			case '\b':
-				*output_pointer = 'b';
-				break;
-			case '\f':
-				*output_pointer = 'f';
-				break;
-			case '\n':
-				*output_pointer = 'n';
-				break;
-			case '\r':
-				*output_pointer = 'r';
-				break;
-			case '\t':
-				*output_pointer = 't';
-				break;
-			default:
-				/* escape and print as unicode codepoint */
-				sprintf((char*)output_pointer, "u%04x", *input_pointer);
-				output_pointer += 4;
-				break;
-			}
-		}
-	}
-	output[output_length + 1] = '\"';
-	output[output_length + 2] = '\0';
-
-	return true;
-}
-

+ 0 - 47
components/wifi-manager/json.h

@@ -1,47 +0,0 @@
-/*
-@file json.h
-@brief handles very basic JSON with a minimal footprint on the system
-
-This code is a lightly modified version of cJSON 1.4.7. cJSON is licensed under the MIT license:
-Copyright (c) 2009 Dave Gamble
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
-INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
-PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
-OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-@see https://github.com/DaveGamble/cJSON
-*/
-
-#ifndef JSON_H_INCLUDED
-#define JSON_H_INCLUDED
-#include <stdbool.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @brief Render the cstring provided to a JSON escaped version that can be printed.
- * @param input the input buffer to be escaped.
- * @param output_buffer the output buffer to write to. You must ensure it is big enough to contain the final string.
- * @see cJSON equivlaent static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
- */
-bool json_print_string(const unsigned char *input, unsigned char *output_buffer);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* JSON_H_INCLUDED */

+ 62 - 48
components/wifi-manager/wifi_manager.c

@@ -38,7 +38,6 @@ Contains the freeRTOS task and all necessary support
 
 #include "dns_server.h"
 #include "http_server.h"
-#include "json.h"
 #include "esp_system.h"
 #include "freertos/FreeRTOS.h"
 #include "freertos/task.h"
@@ -82,8 +81,8 @@ SemaphoreHandle_t wifi_manager_json_mutex = NULL;
 SemaphoreHandle_t wifi_manager_sta_ip_mutex = NULL;
 char *wifi_manager_sta_ip = NULL;
 uint16_t ap_num = MAX_AP_NUM;
-wifi_ap_record_t *accessp_records;
-char *accessp_json = NULL;
+wifi_ap_record_t *accessp_records=NULL;
+cJSON * accessp_cjson=NULL;
 char *ip_info_json = NULL;
 char *host_name = NULL;
 cJSON * ip_info_cjson=NULL;
@@ -161,16 +160,10 @@ void wifi_manager_disconnect_async(){
 
 void wifi_manager_start(){
 
-	/* disable the default wifi logging */
-	esp_log_level_set("wifi", ESP_LOG_NONE);
-
-
 	/* memory allocation */
 	wifi_manager_queue = xQueueCreate( 3, sizeof( queue_message) );
 	wifi_manager_json_mutex = xSemaphoreCreateMutex();
-	accessp_records = (wifi_ap_record_t*)malloc(sizeof(wifi_ap_record_t) * MAX_AP_NUM);
-	accessp_json = (char*)malloc(MAX_AP_NUM * JSON_ONE_APP_SIZE + 4); /* 4 bytes for json encapsulation of "[\n" and "]\0" */
-	wifi_manager_clear_access_points_json();
+	accessp_cjson = wifi_manager_clear_ap_list_json(&accessp_cjson);
 	ip_info_json = NULL;
 	ip_info_cjson = wifi_manager_clear_ip_info_json(&ip_info_cjson);
 	wifi_manager_config_sta = (wifi_config_t*)malloc(sizeof(wifi_config_t));
@@ -323,6 +316,16 @@ cJSON * wifi_manager_get_new_json(cJSON **old){
 	ESP_LOGD(TAG,"wifi_manager_get_new_json done");
 	 return cJSON_CreateObject();
 }
+cJSON * wifi_manager_get_new_array_json(cJSON **old){
+	ESP_LOGD(TAG,"wifi_manager_get_new_array_json called");
+	cJSON * root=*old;
+	if(root!=NULL){
+	    cJSON_Delete(root);
+	    *old=NULL;
+	}
+	ESP_LOGD(TAG,"wifi_manager_get_new_array_json done");
+	return cJSON_CreateArray();
+}
 cJSON * wifi_manager_get_basic_info(cJSON **old){
 	const esp_app_desc_t* desc = esp_ota_get_app_description();
 	ESP_LOGD(TAG,"wifi_manager_get_basic_info called");
@@ -343,6 +346,13 @@ cJSON * wifi_manager_clear_ip_info_json(cJSON **old){
 	ESP_LOGD(TAG,"wifi_manager_clear_ip_info_json done");
  	 return root;
 }
+cJSON * wifi_manager_clear_ap_list_json(cJSON **old){
+	ESP_LOGD(TAG,"wifi_manager_clear_ap_list_json called");
+	cJSON *root = wifi_manager_get_new_array_json(old);
+	ESP_LOGD(TAG,"wifi_manager_clear_ap_list_json done");
+ 	return root;
+}
+
 
 
 void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code){
@@ -372,38 +382,39 @@ void wifi_manager_generate_ip_info_json(update_reason_code_t update_reason_code)
 	ESP_LOGD(TAG,"wifi_manager_generate_ip_info_json done");
 }
 
-
-void wifi_manager_clear_access_points_json(){
-	strcpy(accessp_json, "[]\n");
-}
-
-void wifi_manager_generate_acess_points_json(){
-	strcpy(accessp_json, "[");
-
-
-	const char oneap_str[] = ",\"chan\":%d,\"rssi\":%d,\"auth\":%d}%c\n";
-
-	/* stack buffer to hold on to one AP until it's copied over to accessp_json */
-	char one_ap[JSON_ONE_APP_SIZE];
+void wifi_manager_generate_access_points_json(cJSON ** ap_list){
+	char szMacStr[15]={0};
+	*ap_list = wifi_manager_get_new_array_json(ap_list);
+	if(*ap_list==NULL) return;
 	for(int i=0; i<ap_num;i++){
-
-		wifi_ap_record_t ap = accessp_records[i];
-
-		/* ssid needs to be json escaped. To save on heap memory it's directly printed at the correct address */
-		strcat(accessp_json, "{\"ssid\":");
-		json_print_string( (unsigned char*)ap.ssid,  (unsigned char*)(accessp_json+strlen(accessp_json)) );
-
-		/* print the rest of the json for this access point: no more string to escape */
-		snprintf(one_ap, (size_t)JSON_ONE_APP_SIZE, oneap_str,
-				ap.primary,
-				ap.rssi,
-				ap.authmode,
-				i==ap_num-1?']':',');
-
-		/* add it to the list */
-		strcat(accessp_json, one_ap);
+		cJSON * ap = cJSON_CreateObject();
+		if(ap == NULL) {
+			ESP_LOGE(TAG,"Unable to allocate memory for access point entry #%d",i);
+			return;
+		}
+		cJSON * radio = cJSON_CreateObject();
+		if(radio == NULL) {
+			ESP_LOGE(TAG,"Unable to allocate memory for access point entry #%d",i);
+			cJSON_Delete(ap);
+			return;
+		}
+		wifi_ap_record_t ap_rec = accessp_records[i];
+		cJSON_AddNumberToObject(ap, "chan", ap_rec.primary);
+		cJSON_AddNumberToObject(ap, "rssi", ap_rec.rssi);
+		cJSON_AddNumberToObject(ap, "auth", ap_rec.authmode);
+		cJSON_AddItemToObject(ap, "ssid", cJSON_CreateString((char *)ap_rec.ssid));
+		memset(szMacStr, 0x00, sizeof(szMacStr));
+		snprintf(szMacStr, sizeof(szMacStr)-1,MACSTR, MAC2STR(ap_rec.bssid));
+		cJSON_AddItemToObject(ap, "bssid", cJSON_CreateString(szMacStr));
+		cJSON_AddNumberToObject(radio, "b", ap_rec.phy_11b?1:0);
+		cJSON_AddNumberToObject(radio, "g", ap_rec.phy_11g?1:0);
+		cJSON_AddNumberToObject(radio, "n", ap_rec.phy_11n?1:0);
+		cJSON_AddNumberToObject(radio, "low_rate", ap_rec.phy_lr?1:0);
+		cJSON_AddItemToObject(ap,"radio", radio);
+		cJSON_AddItemToArray(*ap_list, ap);
+		ESP_LOGD(TAG,"New access point found: %s", cJSON_Print(ap));
 	}
-
+	ESP_LOGD(TAG,"Full access point list: %s", cJSON_Print(*ap_list));
 }
 
 bool wifi_manager_lock_sta_ip_string(TickType_t xTicksToWait){
@@ -470,7 +481,7 @@ void wifi_manager_unlock_json_buffer(){
 }
 
 char* wifi_manager_get_ap_list_json(){
-	return accessp_json;
+	return cJSON_Print(accessp_cjson);
 }
 
 esp_err_t wifi_manager_event_handler(void *ctx, system_event_t *event)
@@ -589,13 +600,11 @@ void wifi_manager_destroy(){
 	task_wifi_manager = NULL;
 	free(host_name);
 	/* heap buffers */
-	free(accessp_records);
-	accessp_records = NULL;
-	free(accessp_json);
-	accessp_json = NULL;
 	free(ip_info_json);
 	cJSON_Delete(ip_info_cjson);
+	cJSON_Delete(accessp_cjson);
 	ip_info_cjson=NULL;
+	accessp_cjson=NULL;
 	free(wifi_manager_sta_ip);
 	wifi_manager_sta_ip = NULL;
 	if(wifi_manager_config_sta){
@@ -786,20 +795,25 @@ void wifi_manager( void * pvParameters ){
 				/* As input param, it stores max AP number ap_records can hold. As output param, it receives the actual AP number this API returns.
 				 * As a consequence, ap_num MUST be reset to MAX_AP_NUM at every scan */
 				ESP_LOGD(TAG,"Getting AP list records");
-				ap_num = MAX_AP_NUM;
-				ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, accessp_records));
+				ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_num));
+				if(ap_num>0){
+					accessp_records = (wifi_ap_record_t*)malloc(sizeof(wifi_ap_record_t) * ap_num);
+					ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_num, accessp_records));
+				}
 				/* make sure the http server isn't trying to access the list while it gets refreshed */
 				ESP_LOGD(TAG,"Preparing to build ap JSON list");
 				if(wifi_manager_lock_json_buffer( pdMS_TO_TICKS(1000) )){
 					/* Will remove the duplicate SSIDs from the list and update ap_num */
 					wifi_manager_filter_unique(accessp_records, &ap_num);
-					wifi_manager_generate_acess_points_json();
+					wifi_manager_generate_access_points_json(&accessp_cjson);
 					wifi_manager_unlock_json_buffer();
 					ESP_LOGD(TAG,"Done building ap JSON list");
+
 				}
 				else{
 					ESP_LOGE(TAG, "could not get access to json mutex in wifi_scan");
 				}
+				free(accessp_records);
 
 				/* callback */
 				if(cb_ptr_arr[msg.code]) {

+ 2 - 2
components/wifi-manager/wifi_manager.h

@@ -278,7 +278,7 @@ void wifi_manager( void * pvParameters );
 
 char* wifi_manager_get_ap_list_json();
 char* wifi_manager_get_ip_info_json();
-
+cJSON * wifi_manager_clear_ap_list_json(cJSON **old);
 
 /**
  * @brief saves the current STA wifi config to flash ram storage.
@@ -352,7 +352,7 @@ cJSON * wifi_manager_get_new_json(cJSON **old);
  * @brief Generates the list of access points after a wifi scan.
  * @note This is not thread-safe and should be called only if wifi_manager_lock_json_buffer call is successful.
  */
-void wifi_manager_generate_acess_points_json();
+void wifi_manager_generate_access_points_json(cJSON ** ap_list);
 
 /**
  * @brief Clear the list of access points.