添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
# (Not part of the boilerplate)
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(tests)

View File

@@ -0,0 +1,11 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := tests
EXTRA_COMPONENT_DIRS = $(IDF_PATH)/examples/common_components/protocol_examples_common
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,163 @@
#!/usr/bin/env python
#
# Copyright 2018 Espressif Systems (Shanghai) PTE LTD
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import re
import os
from tiny_test_fw import Utility
import ttfw_idf
from idf_http_server_test import test as client
# When running on local machine execute the following before running this script
# > make app bootloader
# > make print_flash_cmd | tail -n 1 > build/download.config
# Due to connectivity issues (between runner host and DUT) in the runner environment,
# some of the `advanced_tests` are ignored. These tests are intended for verifying
# the expected limits of the http_server capabilities, and implement sending and receiving
# of large HTTP packets and malformed requests, running multiple parallel sessions, etc.
# It is advised that all these tests be run locally, when making changes or adding new
# features to this component.
@ttfw_idf.idf_example_test(env_tag="Example_WIFI")
def test_examples_protocol_http_server_advanced(env, extra_data):
# Acquire DUT
dut1 = env.get_dut("http_server", "examples/protocols/http_server/advanced_tests", dut_class=ttfw_idf.ESP32DUT)
# Get binary file
binary_file = os.path.join(dut1.app.binary_path, "tests.bin")
bin_size = os.path.getsize(binary_file)
ttfw_idf.log_performance("http_server_bin_size", "{}KB".format(bin_size // 1024))
ttfw_idf.check_performance("http_server_bin_size", bin_size // 1024, dut1.TARGET)
# Upload binary and start testing
Utility.console_log("Starting http_server advanced test app")
dut1.start_app()
# Parse IP address of STA
Utility.console_log("Waiting to connect with AP")
got_ip = dut1.expect(re.compile(r"(?:[\s\S]*)IPv4 address: (\d+.\d+.\d+.\d+)"), timeout=30)[0]
got_port = dut1.expect(re.compile(r"(?:[\s\S]*)Started HTTP server on port: '(\d+)'"), timeout=15)[0]
result = dut1.expect(re.compile(r"(?:[\s\S]*)Max URI handlers: '(\d+)'(?:[\s\S]*)Max Open Sessions: " # noqa: W605
r"'(\d+)'(?:[\s\S]*)Max Header Length: '(\d+)'(?:[\s\S]*)Max URI Length: "
r"'(\d+)'(?:[\s\S]*)Max Stack Size: '(\d+)'"), timeout=15)
# max_uri_handlers = int(result[0])
max_sessions = int(result[1])
max_hdr_len = int(result[2])
max_uri_len = int(result[3])
max_stack_size = int(result[4])
Utility.console_log("Got IP : " + got_ip)
Utility.console_log("Got Port : " + got_port)
# Run test script
# If failed raise appropriate exception
failed = False
Utility.console_log("Sessions and Context Tests...")
if not client.spillover_session(got_ip, got_port, max_sessions):
Utility.console_log("Ignoring failure")
if not client.parallel_sessions_adder(got_ip, got_port, max_sessions):
Utility.console_log("Ignoring failure")
if not client.leftover_data_test(got_ip, got_port):
failed = True
if not client.async_response_test(got_ip, got_port):
failed = True
if not client.recv_timeout_test(got_ip, got_port):
failed = True
if not client.arbitrary_termination_test(got_ip, got_port):
failed = True
# This test fails a lot! Enable when connection is stable
# test_size = 50*1024 # 50KB
# if not client.packet_size_limit_test(got_ip, got_port, test_size):
# Utility.console_log("Ignoring failure")
Utility.console_log("Getting initial stack usage...")
if not client.get_hello(got_ip, got_port):
failed = True
inital_stack = int(dut1.expect(re.compile(r"(?:[\s\S]*)Free Stack for server task: '(\d+)'"), timeout=15)[0])
if inital_stack < 0.1 * max_stack_size:
Utility.console_log("More than 90% of stack being used on server start")
failed = True
Utility.console_log("Basic HTTP Client Tests...")
if not client.get_hello(got_ip, got_port):
failed = True
if not client.post_hello(got_ip, got_port):
failed = True
if not client.put_hello(got_ip, got_port):
failed = True
if not client.post_echo(got_ip, got_port):
failed = True
if not client.get_echo(got_ip, got_port):
failed = True
if not client.put_echo(got_ip, got_port):
failed = True
if not client.get_hello_type(got_ip, got_port):
failed = True
if not client.get_hello_status(got_ip, got_port):
failed = True
if not client.get_false_uri(got_ip, got_port):
failed = True
if not client.get_test_headers(got_ip, got_port):
failed = True
Utility.console_log("Error code tests...")
if not client.code_500_server_error_test(got_ip, got_port):
failed = True
if not client.code_501_method_not_impl(got_ip, got_port):
failed = True
if not client.code_505_version_not_supported(got_ip, got_port):
failed = True
if not client.code_400_bad_request(got_ip, got_port):
failed = True
if not client.code_404_not_found(got_ip, got_port):
failed = True
if not client.code_405_method_not_allowed(got_ip, got_port):
failed = True
if not client.code_408_req_timeout(got_ip, got_port):
failed = True
if not client.code_414_uri_too_long(got_ip, got_port, max_uri_len):
Utility.console_log("Ignoring failure")
if not client.code_431_hdr_too_long(got_ip, got_port, max_hdr_len):
Utility.console_log("Ignoring failure")
if not client.test_upgrade_not_supported(got_ip, got_port):
failed = True
Utility.console_log("Getting final stack usage...")
if not client.get_hello(got_ip, got_port):
failed = True
final_stack = int(dut1.expect(re.compile(r"(?:[\s\S]*)Free Stack for server task: '(\d+)'"), timeout=15)[0])
if final_stack < 0.05 * max_stack_size:
Utility.console_log("More than 95% of stack got used during tests")
failed = True
if failed:
raise RuntimeError
if __name__ == '__main__':
test_examples_protocol_http_server_advanced()

View File

@@ -0,0 +1,3 @@
idf_component_register(SRCS "main.c"
"tests.c"
INCLUDE_DIRS "." "include")

View File

@@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@@ -0,0 +1,9 @@
#ifndef __HTTPD_TESTS_H__
#define __HTTPD_TESTS_H__
#include <esp_http_server.h>
extern httpd_handle_t start_tests(void);
extern void stop_tests(httpd_handle_t hd);
#endif // __HTTPD_TESTS_H__

View File

@@ -0,0 +1,72 @@
/* HTTP Server Tests
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "protocol_examples_common.h"
#include "tests.h"
static const char *TAG = "example";
static void disconnect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server) {
ESP_LOGI(TAG, "Stopping webserver");
stop_tests(*server);
*server = NULL;
}
}
static void connect_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
httpd_handle_t* server = (httpd_handle_t*) arg;
if (*server == NULL) {
ESP_LOGI(TAG, "Starting webserver");
*server = start_tests();
}
}
void app_main(void)
{
static httpd_handle_t server = NULL;
ESP_ERROR_CHECK(nvs_flash_init());
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
/* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
* Read "Establishing Wi-Fi or Ethernet Connection" section in
* examples/protocols/README.md for more information about this function.
*/
ESP_ERROR_CHECK(example_connect());
/* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,
* and re-start it upon connection.
*/
#ifdef CONFIG_EXAMPLE_CONNECT_WIFI
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_WIFI
#ifdef CONFIG_EXAMPLE_CONNECT_ETHERNET
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &connect_handler, &server));
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ETHERNET_EVENT_DISCONNECTED, &disconnect_handler, &server));
#endif // CONFIG_EXAMPLE_CONNECT_ETHERNET
/* Start the server for the first time */
server = start_tests();
}

View File

@@ -0,0 +1,412 @@
#include <stdlib.h>
#include <stdbool.h>
#include <esp_log.h>
#include <esp_system.h>
#include <esp_http_server.h>
#include "tests.h"
static const char *TAG = "TESTS";
static int pre_start_mem, post_stop_mem;
struct async_resp_arg {
httpd_handle_t hd;
int fd;
};
/********************* Basic Handlers Start *******************/
static esp_err_t hello_get_handler(httpd_req_t *req)
{
#define STR "Hello World!"
ESP_LOGI(TAG, "Free Stack for server task: '%d'", uxTaskGetStackHighWaterMark(NULL));
httpd_resp_send(req, STR, strlen(STR));
return ESP_OK;
#undef STR
}
/* This handler is intended to check what happens in case of empty values of headers.
* Here `Header2` is an empty header and `Header1` and `Header3` will have `Value1`
* and `Value3` in them. */
static esp_err_t test_header_get_handler(httpd_req_t *req)
{
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
int buf_len;
char *buf;
buf_len = httpd_req_get_hdr_value_len(req, "Header1");
if (buf_len > 0) {
buf = malloc(++buf_len);
if (!buf) {
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", buf_len);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed");
return ESP_ERR_NO_MEM;
}
/* Copy null terminated value string into buffer */
if (httpd_req_get_hdr_value_str(req, "Header1", buf, buf_len) == ESP_OK) {
ESP_LOGI(TAG, "Header1 content: %s", buf);
if (strcmp("Value1", buf) != 0) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Wrong value of Header1 received");
free(buf);
return ESP_ERR_INVALID_ARG;
} else {
ESP_LOGI(TAG, "Expected value and received value matched for Header1");
}
} else {
ESP_LOGE(TAG, "Error in getting value of Header1");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Error in getting value of Header1");
free(buf);
return ESP_FAIL;
}
free(buf);
} else {
ESP_LOGE(TAG, "Header1 not found");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Header1 not found");
return ESP_ERR_NOT_FOUND;
}
buf_len = httpd_req_get_hdr_value_len(req, "Header3");
if (buf_len > 0) {
buf = malloc(++buf_len);
if (!buf) {
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", buf_len);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed");
return ESP_ERR_NO_MEM;
}
/* Copy null terminated value string into buffer */
if (httpd_req_get_hdr_value_str(req, "Header3", buf, buf_len) == ESP_OK) {
ESP_LOGI(TAG, "Header3 content: %s", buf);
if (strcmp("Value3", buf) != 0) {
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Wrong value of Header3 received");
free(buf);
return ESP_ERR_INVALID_ARG;
} else {
ESP_LOGI(TAG, "Expected value and received value matched for Header3");
}
} else {
ESP_LOGE(TAG, "Error in getting value of Header3");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Error in getting value of Header3");
free(buf);
return ESP_FAIL;
}
free(buf);
} else {
ESP_LOGE(TAG, "Header3 not found");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Header3 not found");
return ESP_ERR_NOT_FOUND;
}
buf_len = httpd_req_get_hdr_value_len(req, "Header2");
buf = malloc(++buf_len);
if (!buf) {
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", buf_len);
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Memory allocation failed");
return ESP_ERR_NO_MEM;
}
if (httpd_req_get_hdr_value_str(req, "Header2", buf, buf_len) == ESP_OK) {
ESP_LOGI(TAG, "Header2 content: %s", buf);
httpd_resp_send(req, buf, strlen(buf));
} else {
ESP_LOGE(TAG, "Header2 not found");
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Header2 not found");
return ESP_FAIL;
}
return ESP_OK;
}
static esp_err_t hello_type_get_handler(httpd_req_t *req)
{
#define STR "Hello World!"
httpd_resp_set_type(req, HTTPD_TYPE_TEXT);
httpd_resp_send(req, STR, strlen(STR));
return ESP_OK;
#undef STR
}
static esp_err_t hello_status_get_handler(httpd_req_t *req)
{
#define STR "Hello World!"
httpd_resp_set_status(req, HTTPD_500);
httpd_resp_send(req, STR, strlen(STR));
return ESP_OK;
#undef STR
}
static esp_err_t echo_post_handler(httpd_req_t *req)
{
ESP_LOGI(TAG, "/echo handler read content length %d", req->content_len);
char* buf = malloc(req->content_len + 1);
size_t off = 0;
int ret;
if (!buf) {
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", req->content_len + 1);
httpd_resp_send_500(req);
return ESP_FAIL;
}
while (off < req->content_len) {
/* Read data received in the request */
ret = httpd_req_recv(req, buf + off, req->content_len - off);
if (ret <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
httpd_resp_send_408(req);
}
free (buf);
return ESP_FAIL;
}
off += ret;
ESP_LOGI(TAG, "/echo handler recv length %d", ret);
}
buf[off] = '\0';
if (req->content_len < 128) {
ESP_LOGI(TAG, "/echo handler read %s", buf);
}
/* Search for Custom header field */
char* req_hdr = 0;
size_t hdr_len = httpd_req_get_hdr_value_len(req, "Custom");
if (hdr_len) {
/* Read Custom header value */
req_hdr = malloc(hdr_len + 1);
if (!req_hdr) {
ESP_LOGE(TAG, "Failed to allocate memory of %d bytes!", hdr_len + 1);
httpd_resp_send_500(req);
return ESP_FAIL;
}
httpd_req_get_hdr_value_str(req, "Custom", req_hdr, hdr_len + 1);
/* Set as additional header for response packet */
httpd_resp_set_hdr(req, "Custom", req_hdr);
}
httpd_resp_send(req, buf, req->content_len);
free (req_hdr);
free (buf);
return ESP_OK;
}
static void adder_free_func(void *ctx)
{
ESP_LOGI(TAG, "Custom Free Context function called");
free(ctx);
}
/* Create a context, keep incrementing value in the context, by whatever was
* received. Return the result
*/
static esp_err_t adder_post_handler(httpd_req_t *req)
{
char buf[10];
char outbuf[50];
int ret;
/* Read data received in the request */
ret = httpd_req_recv(req, buf, sizeof(buf));
if (ret <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
httpd_resp_send_408(req);
}
return ESP_FAIL;
}
buf[ret] = '\0';
int val = atoi(buf);
ESP_LOGI(TAG, "/adder handler read %d", val);
if (! req->sess_ctx) {
ESP_LOGI(TAG, "/adder allocating new session");
req->sess_ctx = malloc(sizeof(int));
req->free_ctx = adder_free_func;
*(int *)req->sess_ctx = 0;
}
int *adder = (int *)req->sess_ctx;
*adder += val;
snprintf(outbuf, sizeof(outbuf),"%d", *adder);
httpd_resp_send(req, outbuf, strlen(outbuf));
return ESP_OK;
}
static esp_err_t leftover_data_post_handler(httpd_req_t *req)
{
/* Only echo the first 10 bytes of the request, leaving the rest of the
* request data as is.
*/
char buf[11];
int ret;
/* Read data received in the request */
ret = httpd_req_recv(req, buf, sizeof(buf) - 1);
if (ret <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
httpd_resp_send_408(req);
}
return ESP_FAIL;
}
buf[ret] = '\0';
ESP_LOGI(TAG, "leftover data handler read %s", buf);
httpd_resp_send(req, buf, strlen(buf));
return ESP_OK;
}
extern int httpd_default_send(httpd_handle_t hd, int sockfd, const char *buf, unsigned buf_len, int flags);
static void generate_async_resp(void *arg)
{
char buf[250];
struct async_resp_arg *resp_arg = (struct async_resp_arg *)arg;
httpd_handle_t hd = resp_arg->hd;
int fd = resp_arg->fd;
#define HTTPD_HDR_STR "HTTP/1.1 200 OK\r\n" \
"Content-Type: text/html\r\n" \
"Content-Length: %d\r\n"
#define STR "Hello Double World!"
ESP_LOGI(TAG, "Executing queued work fd : %d", fd);
snprintf(buf, sizeof(buf), HTTPD_HDR_STR,
strlen(STR));
httpd_default_send(hd, fd, buf, strlen(buf), 0);
/* Space for sending additional headers based on set_header */
httpd_default_send(hd, fd, "\r\n", strlen("\r\n"), 0);
httpd_default_send(hd, fd, STR, strlen(STR), 0);
#undef STR
free(arg);
}
static esp_err_t async_get_handler(httpd_req_t *req)
{
#define STR "Hello World!"
httpd_resp_send(req, STR, strlen(STR));
/* Also register a HTTPD Work which sends the same data on the same
* socket again
*/
struct async_resp_arg *resp_arg = malloc(sizeof(struct async_resp_arg));
resp_arg->hd = req->handle;
resp_arg->fd = httpd_req_to_sockfd(req);
if (resp_arg->fd < 0) {
return ESP_FAIL;
}
ESP_LOGI(TAG, "Queuing work fd : %d", resp_arg->fd);
httpd_queue_work(req->handle, generate_async_resp, resp_arg);
return ESP_OK;
#undef STR
}
static const httpd_uri_t basic_handlers[] = {
{ .uri = "/hello/type_html",
.method = HTTP_GET,
.handler = hello_type_get_handler,
.user_ctx = NULL,
},
{ .uri = "/test_header",
.method = HTTP_GET,
.handler = test_header_get_handler,
.user_ctx = NULL,
},
{ .uri = "/hello",
.method = HTTP_GET,
.handler = hello_get_handler,
.user_ctx = NULL,
},
{ .uri = "/hello/status_500",
.method = HTTP_GET,
.handler = hello_status_get_handler,
.user_ctx = NULL,
},
{ .uri = "/echo",
.method = HTTP_POST,
.handler = echo_post_handler,
.user_ctx = NULL,
},
{ .uri = "/echo",
.method = HTTP_PUT,
.handler = echo_post_handler,
.user_ctx = NULL,
},
{ .uri = "/leftover_data",
.method = HTTP_POST,
.handler = leftover_data_post_handler,
.user_ctx = NULL,
},
{ .uri = "/adder",
.method = HTTP_POST,
.handler = adder_post_handler,
.user_ctx = NULL,
},
{ .uri = "/async_data",
.method = HTTP_GET,
.handler = async_get_handler,
.user_ctx = NULL,
}
};
static const int basic_handlers_no = sizeof(basic_handlers)/sizeof(httpd_uri_t);
static void register_basic_handlers(httpd_handle_t hd)
{
int i;
ESP_LOGI(TAG, "Registering basic handlers");
ESP_LOGI(TAG, "No of handlers = %d", basic_handlers_no);
for (i = 0; i < basic_handlers_no; i++) {
if (httpd_register_uri_handler(hd, &basic_handlers[i]) != ESP_OK) {
ESP_LOGW(TAG, "register uri failed for %d", i);
return;
}
}
ESP_LOGI(TAG, "Success");
}
static httpd_handle_t test_httpd_start(void)
{
pre_start_mem = esp_get_free_heap_size();
httpd_handle_t hd;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
/* Modify this setting to match the number of test URI handlers */
config.max_uri_handlers = 9;
config.server_port = 1234;
/* This check should be a part of http_server */
config.max_open_sockets = (CONFIG_LWIP_MAX_SOCKETS - 3);
if (httpd_start(&hd, &config) == ESP_OK) {
ESP_LOGI(TAG, "Started HTTP server on port: '%d'", config.server_port);
ESP_LOGI(TAG, "Max URI handlers: '%d'", config.max_uri_handlers);
ESP_LOGI(TAG, "Max Open Sessions: '%d'", config.max_open_sockets);
ESP_LOGI(TAG, "Max Header Length: '%d'", HTTPD_MAX_REQ_HDR_LEN);
ESP_LOGI(TAG, "Max URI Length: '%d'", HTTPD_MAX_URI_LEN);
ESP_LOGI(TAG, "Max Stack Size: '%d'", config.stack_size);
return hd;
}
return NULL;
}
static void test_httpd_stop(httpd_handle_t hd)
{
httpd_stop(hd);
post_stop_mem = esp_get_free_heap_size();
ESP_LOGI(TAG, "HTTPD Stop: Current free memory: %d", post_stop_mem);
}
httpd_handle_t start_tests(void)
{
httpd_handle_t hd = test_httpd_start();
if (hd) {
register_basic_handlers(hd);
}
return hd;
}
void stop_tests(httpd_handle_t hd)
{
ESP_LOGI(TAG, "Stopping httpd");
test_httpd_stop(hd);
}