ESP8266学习笔记(20)——HTTP服务器(RTOS SDK)

一、背景

首先手机APP连接智能插座热点(AP)将网关的SSID和密码通过HTTP协议配置到插座,完成配置后智能插座连接网关。

Post请求和Get请求:

二、API说明

以下软件定时器接口位于 esp_http_server/include/esp_http_server.h

2.1 httpd_start

2.2 httpd_register_uri_handler

通过传递类型httpd_uri_t结构的对象来注册URI处理程序,该对象具有包括uri名称,method类型(例如,HTTPD_GET/HTTPD_POST/HTTPD_PUT等等),类型的函数指针和指向用户上下文数据的指针的成员。esp_err_t *handler (httpd_req_t *req) user_ctx

2.3 httpd_req_get_url_query_len

2.4 httpd_req_get_url_query_str

2.5 httpd_req_recv

2.6 httpd_resp_set_status

2.7 httpd_resp_send

三、移植文件

百度网盘:https://pan.baidu.com/s/1ku_qiKOEuOdLBcNbswL7WQ[https://pan.baidu.com/s/1ku_qiKOEuOdLBcNbswL7WQ] 提取码:490n

3.1 user_http_server.c

/*********************************************************************
 * INCLUDES
 */
#include <sys/param.h>

#include "esp_http_server.h"
#include "esp_timer.h"
#include "cJSON.h"
#include "esp_err.h"
#include "esp_log.h"

#include "user_http_server.h"
#include "user_wifi.h"
#include "common.h"

static esp_err_t handleGetUrlPath(httpd_req_t *pRequest);
static esp_err_t handlePostUrlPath(httpd_req_t *pRequest);
static esp_err_t findRequestData(char *pRequestData);
static void queryClientInfo(void);
static void configWifi(char *pRequestData);
static void configCloudServer(char *pRequestData);
static void configBleInit(char *pRequestData);
static void sendGetResponse(bool responseOk, char *pResponseData);
static void sendPostResponse(bool responseOk, char *pResponseData);
static void jsonPackageResponseData(bool responseOk, char *pSendData);
static void jsonPackageClientInfoData(bool responseOk, char *pSendData);
static void startDelayHandleTimer(void);
static void delayHandleTimerCallback(void *arg);

/*********************************************************************
 * LOCAL VARIABLES
 */
static httpd_req_t *s_pRequest = NULL;

static httpd_uri_t s_uriClientGet = 
{
    .uri       = "/client",
    .method    = HTTP_GET,
    .handler   = handleGetUrlPath,
    .user_ctx  = NULL
};

static httpd_uri_t s_uriConfigPost = 
{
    .uri       = "/config",
    .method    = HTTP_POST,
    .handler   = handlePostUrlPath,
    .user_ctx  = NULL
};

// 延迟处理定时器,确保可以回复响应成功
esp_timer_handle_t s_delayHandleTimer = 0;
esp_timer_create_args_t s_delayHandleTimerArg = 
{ 
    .callback = &delayHandleTimerCallback,                                              // 设置回调函数
    .arg = NULL,                                                                        // 不携带参数
    .name = "DelayHandleTimer"                                                          // 定时器名字
};

char s_configSsid[SSID_MAX_LEN] = {0};
char s_configPassword[PASSWORD_MAX_LEN] = {0};

static const char *TAG = "user_http_server";

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 @brief HTTP服务器初始化
 @param 无
 @return 无
*/
httpd_handle_t HttpServerInit(void)
{
	httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // 启动HTTP服务器
    ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port);
    if(httpd_start(&server, &config) == ESP_OK)                                              
    {
        // 注册URI处理程序
        httpd_register_uri_handler(server, &s_uriConfigPost);
		httpd_register_uri_handler(server, &s_uriClientGet);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}


/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief HTTP GET请求URL处理
 @param pRequest -[in] HTTP请求结构体
 @return 错误码
*/
static esp_err_t handleGetUrlPath(httpd_req_t *pRequest)
{
    esp_err_t result = ESP_FAIL;
    char *pBuffer;
    size_t bufferLength;

    /* Read URL query string length and allocate memory for length + 1,
     * extra byte for null termination */
    bufferLength = httpd_req_get_url_query_len(pRequest) + 1;
    if(bufferLength > 1) 
    {
        pBuffer = malloc(bufferLength);
        if(httpd_req_get_url_query_str(pRequest, pBuffer, bufferLength) == ESP_OK) 
        {
            ESP_LOGI(TAG, "Found URL query => %s", pBuffer);
            s_pRequest = pRequest;
            char requestData[REQUEST_DATA_SIZE] = {0};
            result = findRequestData(requestData);

            if(strcmp(pBuffer, "command=info") == 0)
            {
                queryClientInfo();
            }
        }
        free(pBuffer);
    }
    return result;
}

/**
 @brief HTTP POST请求URL处理
 @param pRequest -[in] HTTP请求结构体
 @return 错误码
*/
static esp_err_t handlePostUrlPath(httpd_req_t *pRequest)
{
    esp_err_t result = ESP_FAIL;
    char *pBuffer;
    size_t bufferLength;

    /* Read URL query string length and allocate memory for length + 1,
     * extra byte for null termination */
    bufferLength = httpd_req_get_url_query_len(pRequest) + 1;
    if(bufferLength > 1) 
    {
        pBuffer = malloc(bufferLength);
        if(httpd_req_get_url_query_str(pRequest, pBuffer, bufferLength) == ESP_OK) 
        {
            ESP_LOGI(TAG, "Found URL query => %s", pBuffer);
            s_pRequest = pRequest;
            char requestData[REQUEST_DATA_SIZE] = {0};
            result = findRequestData(requestData);

            if(strcmp(pBuffer, "command=wifi") == 0)
            {
                configWifi(requestData);
            }
            else if(strcmp(pBuffer, "command=cloud_server") == 0)
            {
				configCloudServer(requestData);					
            }
            else if(strcmp(pBuffer, "command=switch") == 0)
            {
                
            }
            else if(strcmp(pBuffer, "command=ble_cmd") == 0)
            {
                
            }
            else if(strcmp(pBuffer, "command=ble_init") == 0)
            {
                configBleInit(requestData);		
            }
        }
        free(pBuffer);
    }
    return result;
}

/**
 @brief 查找请求数据
 @param pRequestData -[out] 请求的数据
 @return 错误码
*/
static esp_err_t findRequestData(char *pRequestData)
{
    int result, remaining = s_pRequest->content_len;

    while(remaining > 0) 
    {
        // 从HTTP请求读取内容数据
        if((result = httpd_req_recv(s_pRequest, pRequestData, MIN(remaining, REQUEST_DATA_SIZE))) <= 0) 
        {
            if(result == HTTPD_SOCK_ERR_TIMEOUT) 
            {
                // 发生超时,继续接收
                continue;
            }
            return ESP_FAIL;
        }
        remaining -= result;

        /* Log data received */
        ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
        ESP_LOGI(TAG, "%.*s", result, pRequestData);
        ESP_LOGI(TAG, "====================================");
    }
    return ESP_OK;
}

/**
 @brief 查询终端信息接口
 @param pRequestData -[in] 请求的数据
 @return 无
*/
static void queryClientInfo(void)
{
	char sendData[RESPONSE_DATA_SIZE] = {0};
	jsonPackageClientInfoData(true, sendData);
	sendGetResponse(true, sendData);
}

/**
 @brief 配置WIFI接口
 @param pRequestData -[in] 请求的数据
 @return 无
*/
static void configWifi(char *pRequestData)
{
	cJSON *pRoot = cJSON_Parse(pRequestData);
	if(!pRoot)
	{
		return ;
	}
   
    char sendData[RESPONSE_DATA_SIZE] = {0};
	cJSON *pRequest = cJSON_GetObjectItem(pRoot, "request");                  			// 解析request字段内容
	if(!pRequest)
	{
        sprintf(sendData, "%s", "No request Item");
		sendPostResponse(false, sendData);
		cJSON_Delete(pRoot);
		return ;
	}

	cJSON *pStation = cJSON_GetObjectItem(pRequest, "station");                         // 解析request子节点station字段内容
	cJSON *pSoftAp = cJSON_GetObjectItem(pRequest, "softap");                           // 解析request子节点softap字段内容
	if(pStation)
	{
		cJSON *pSsid = cJSON_GetObjectItem(pStation, "ssid");
		cJSON *pPassword = cJSON_GetObjectItem(pStation, "password");
		if(!pSsid)
		{
            sprintf(sendData, "%s", "No ssid Item");
			sendPostResponse(false, sendData);
			cJSON_Delete(pRoot);
			return ;
		}
		if(!pPassword)
		{
            sprintf(sendData, "%s", "No password Item");
			sendPostResponse(false, sendData);
			cJSON_Delete(pRoot);
			return ;
		}

		strcpy(s_configSsid, pSsid->valuestring);
		strcpy(s_configPassword, pPassword->valuestring);

		sprintf(sendData, "%s", "Config station succeed");
		sendPostResponse(true, sendData);

	    startDelayHandleTimer();

		// SetDhcpClientFlag(0);
		// ConfigStationMode(ssid, password);                                          // 配置连接路由器

		// UdpSendDeviceInfo();														// UDP发送设备信息
		// EspPlatformCheckIp();														// ESP平台检查IP
	}
	else if(pSoftAp)
	{
	}
	else
	{
        sprintf(sendData, "%s", "No station or softap Item");
		sendPostResponse(false, sendData);
	}

	cJSON_Delete(pRoot);
}

/**
 @brief 配置云服务器接口
 @param pRequestData -[in] 请求的数据
 @return 无
*/
static void configCloudServer(char *pRequestData)
{
	cJSON *pRoot = cJSON_Parse(pRequestData);
	if(!pRoot)
	{
		return ;
	}

	char sendData[RESPONSE_DATA_SIZE] = {0};
	cJSON *pRequest = cJSON_GetObjectItem(pRoot, "request");                            // 解析request字段内容
	if(!pRequest)
	{
		sprintf(sendData, "%s", "No request Item");
		sendPostResponse(false, sendData);
		cJSON_Delete(pRoot);
		return ;
	}

	cJSON *pTcp = cJSON_GetObjectItem(pRequest, "tcp");                                 // 解析request子节点tcp字段内容
	cJSON *pHttp = cJSON_GetObjectItem(pRequest, "http");                               // 解析request子节点http字段内容

	if(pTcp)
	{
		char token[64] = {0};

		cJSON *pIpAddr = cJSON_GetObjectItem(pTcp, "ip");
		cJSON *pPort = cJSON_GetObjectItem(pTcp, "port");
		cJSON *pDomain = cJSON_GetObjectItem(pTcp, "domain");
		cJSON *pToken = cJSON_GetObjectItem(pTcp, "token");
		if(pIpAddr)
		{
			char ip[16] = {0};
			strcpy(ip, pIpAddr->valuestring);
			// g_tcpCloudServerUrl.ip.addr = ipaddr_addr(ip);                              // 点分十进制写入IP结构体
		}
		if(pPort)
		{
			// g_tcpCloudServerUrl.port = pPort->valueint;
		}
		if(pDomain)
		{
			char domain[32] = {0};
			strcpy(domain, pDomain->valuestring);
			// memcpy(g_tcpCloudServerUrl.domain, domain, os_strlen(domain));
		}
		if(pToken)
		{
			strcpy(token, pToken->valuestring);
		}

		sprintf(sendData, "%s", "Config tcp cloud server succeed");
		sendPostResponse(true, sendData);

		// SaveTcpServerParam(&g_tcpCloudServerUrl);                                       // 保存TCP服务器参数到Flash
		// SetFirstConnectEspPlatformFlag(true);
		// EspPlatformCheckIp();                                                           // ESP平台检查IP
	}
	else if(pHttp)
	{
		char ip[16] = {0};

		cJSON *pIpAddr = cJSON_GetObjectItem(pHttp, "ip");
		cJSON *pPort = cJSON_GetObjectItem(pHttp, "port");
		cJSON *pDomain = cJSON_GetObjectItem(pHttp, "domain");
		cJSON *pPath = cJSON_GetObjectItem(pHttp, "path");
		if(pIpAddr)
		{
			strcpy(ip, pIpAddr->valuestring);
			// g_httpCloudServerUrl.ip.addr = ipaddr_addr(ip);							// 点分十进制写入IP结构体
		}
		if(pPort)
		{
			// g_httpCloudServerUrl.port = pPort->valueint;
		}
		if(pDomain)
		{
			// strcpy(g_httpCloudServerUrl.domain, pDomain->valuestring);
		}
		if(pPath)
		{
			// strcpy(g_httpCloudServerUrl.path, pPath->valuestring);
		}

		sprintf(sendData, "%s", "Config http cloud server succeed");
		sendPostResponse(true, sendData);

		// SaveHttpServerParam(&g_httpCloudServerUrl);                                     // 保存HTTP服务器参数到Flash
		// HttpCloudCheckIp();
	}
	else
	{
		sprintf(sendData, "%s", "No tcp or http Item");
		sendPostResponse(false, sendData);
	}

	cJSON_Delete(pRoot);
}

/**
 @brief 配置BLE初始化接口
 @param pRequestData -[in] 请求的数据
 @return 无
*/
static void configBleInit(char *pRequestData)
{
	cJSON *pRoot = cJSON_Parse(pRequestData);
	if(!pRoot)
	{
		return ;
	}

	char sendData[RESPONSE_DATA_SIZE] = {0};
	cJSON *pRequest = cJSON_GetObjectItem(pRoot, "request");							// 解析request字段内容
	if(!pRequest)
	{
		sprintf(sendData, "%s", "No request Item");
		sendPostResponse(false, sendData);
		cJSON_Delete(pRoot);
		return ;
	}

	cJSON *pBleInit = cJSON_GetObjectItem(pRequest, "ble_init");						// 解析request子节点ble_init字段内容
	cJSON *pBleSupervRssi = cJSON_GetObjectItem(pRequest, "ble_superv_rssi");			// 解析request子节点ble_superv_rssi字段内容
	if(pBleInit)
	{
		char buffer[64] = {0};
		cJSON *pBleInitData = cJSON_GetObjectItem(pBleInit, "ble_init_data");

		if(pBleInitData)
		{
			strcpy(buffer, pBleInitData->valuestring);
			// UartSendLanBleInit(buffer);
		}
		else
		{
			sprintf(sendData, "%s", "No ble_init_data Item");
			sendPostResponse(false, sendData);
			cJSON_Delete(pRoot);
			return ;
		}

		sprintf(sendData, "%s", "Config ble init succeed");
		sendPostResponse(true, sendData);
	}
	else if(pBleSupervRssi)
	{
		cJSON *pRssi = cJSON_GetObjectItem(pBleSupervRssi, "rssi");

		if(pRssi)
		{
			// g_bleDeviceRssiStandrdValue = pRssi->valueint;
			// SaveBleDeviceRssiStandrdValue(g_bleDeviceRssiStandrdValue);
		}
		else
		{
			sprintf(sendData, "%s", "No rssi Item");
			sendPostResponse(false, sendData);
			cJSON_Delete(pRoot);
			return ;
		}

		sprintf(sendData, "%s", "Config ble superv rssi succeed");
		sendPostResponse(true, sendData);
	}
	else
	{
		sprintf(sendData, "%s", "No ble_init or ble_superv_rssi Item");
		sendPostResponse(false, sendData);
	}

	cJSON_Delete(pRoot);
}

/**
 @brief 发送GET请求HTTP响应
 @param responseOk -[in] 响应是否成功
 @param pResponseData -[in] 响应数据
 @return 无
*/
static void sendGetResponse(bool responseOk, char *pResponseData)
{
	if(responseOk)
    {
		httpd_resp_set_status(s_pRequest, HTTPD_200);                                   // 设置响应状态200 OK
    }
	else
	{
    	httpd_resp_set_status(s_pRequest, HTTPD_400);                                   // 设置响应状态400 Bad Request
	}

    httpd_resp_send(s_pRequest, pResponseData, strlen(pResponseData));                  // 发送响应
}

/**
 @brief 发送POST请求的HTTP响应
 @param responseOk -[in] 响应是否成功
 @param pResponseData -[in] 响应数据
 @return 无
*/
static void sendPostResponse(bool responseOk, char *pResponseData)
{
	jsonPackageResponseData(responseOk, pResponseData);

	if(responseOk)
    {
		httpd_resp_set_status(s_pRequest, HTTPD_200);                                   // 设置响应状态200 OK
    }
	else
	{
    	httpd_resp_set_status(s_pRequest, HTTPD_400);                                   // 设置响应状态400 Bad Request
	}

    httpd_resp_send(s_pRequest, pResponseData, strlen(pResponseData));                  // 发送响应
}

/**
 @brief JSON格式封装响应数据
 @param responseOk -[in] 响应是否成功
 @param pSendData -[in&out] 要封装的发送数据
 @return 无
*/
static void jsonPackageResponseData(bool responseOk, char *pSendData)
{
	cJSON *pRoot = cJSON_CreateObject();

	uint16 statusCode;

	if(responseOk)
	{
		statusCode = 200;
	}
	else
	{
		statusCode = 400;
	}

	cJSON_AddNumberToObject(pRoot, "status", statusCode);
	cJSON_AddStringToObject(pRoot, "message", pSendData);
	char *tempBuffer = cJSON_Print(pRoot);
	sprintf(pSendData, "%s", tempBuffer);

	free((void *) tempBuffer);
	cJSON_Delete(pRoot);
}

/**
 @brief JSON格式封装终端信息数据
 @param responseOk -[in] 响应是否成功
 @param pSendData -[in&out] 要封装的发送数据
 @return 无
*/
static void jsonPackageClientInfoData(bool responseOk, char *pSendData)
{
	cJSON *pRoot = cJSON_CreateObject();

	uint16 statusCode;

	if(responseOk)
	{
		statusCode = 200;
	}
	else
	{
		statusCode = 400;
	}

	char wifiMac[23] = {0};
	char bleMac[23] = {0};
	// GetWifiMacStr(wifiMac);
	// GetBleMacStr(bleMac);

	cJSON_AddNumberToObject(pRoot, "status", statusCode);
	cJSON_AddStringToObject(pRoot, "wifi_mac", "11:11:11:11:11:11");
	cJSON_AddStringToObject(pRoot, "ble_mac", "11:11:11:11:11:11");
	cJSON_AddStringToObject(pRoot, "ble_name", "a");
	cJSON_AddNumberToObject(pRoot, "ble_superv_rssi", 11);
	char *tempBuffer = cJSON_Print(pRoot);
	sprintf(pSendData, "%s", tempBuffer);

	free((void *) tempBuffer);
	cJSON_Delete(pRoot);
}

/**
 @brief 开启延迟处理定时器
 @param 无
 @return 无
*/
static void startDelayHandleTimer(void) 
{
	// 开始创建一个单次周期的定时器并且执行
	ESP_ERROR_CHECK(esp_timer_create(&s_delayHandleTimerArg, &s_delayHandleTimer));
	ESP_ERROR_CHECK(esp_timer_start_once(s_delayHandleTimer, DELAY_HANDLE_PERIOD));		// 1s                    
}

/**
 @brief 延迟处理定时器的回调函数
 @param 无
 @return 无
*/
static void delayHandleTimerCallback(void *arg) 
{
    ESP_LOGI(TAG, "delayHandleTimerCallback");

    ConfigStationMode(s_configSsid, s_configPassword);                                  // 配置连接路由器

	ESP_ERROR_CHECK(esp_timer_delete(s_delayHandleTimer));                              // 使用完后删除定时器                              
}

/****************************************************END OF FILE****************************************************/

3.2 user_http_server.h

#ifndef _USER_HTTP_SERVER_H_
#define _USER_HTTP_SERVER_H_

/*********************************************************************
 * INCLUDES
 */
#include <esp_http_server.h>

/*********************************************************************
 * DEFINITIONS
 */
#define REQUEST_DATA_SIZE			512
#define RESPONSE_DATA_SIZE		    256

#define DELAY_HANDLE_PERIOD         1000 * 1000         // 1s

/*********************************************************************
 * API FUNCTIONS
 */
httpd_handle_t HttpServerInit(void);

#endif /* _USER_HTTP_SERVER_H_ */

四、使用例子

cJSON使用部分查看 ESP8266学习笔记(8)——第三方库cJSON使用
定时器使用部分查看 ESP8266学习笔记(19)——定时器接口使用(RTOS SDK)

4.1 初始化HTTP服务器

在main函数中调用HttpServerInit()

void app_main(void)
{
    printf("SDK version:%s\n", esp_get_idf_version());

    esp_timer_init();
    WifiInit();
    HttpServerInit();
}

在HttpServerInit()中启动了HTTP服务器,并注册了两种URI处理程序

httpd_handle_t HttpServerInit(void)
{
	httpd_handle_t server = NULL;
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();

    // 启动HTTP服务器
    ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port);
    if(httpd_start(&server, &config) == ESP_OK)                                              
    {
        // 注册URI处理程序
        httpd_register_uri_handler(server, &s_uriConfigPost);
		httpd_register_uri_handler(server, &s_uriClientGet);
        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

4.2 接收HTTP请求

使用Postman发送

POST /config?command=wifi HTTP/1.1
Host: 192.168.4.1:80
Cache-Control: no-cache

{ "request": { "station": { "ssid":"TP-LINK_576F", "password":"12345678" } } }

由于是Post请求,URI路径为/config,符合已注册好的URI处理程序

static httpd_uri_t s_uriConfigPost = 
{
    .uri       = "/config",
    .method    = HTTP_POST,
    .handler   = handlePostUrlPath,
    .user_ctx  = NULL
};

于是进入handlePostUrlPath()进行处理,在handlePostUrlPath()中查找URL路径,找到command=wifi路径,进入配置WIFI处理函数configWifi()

static esp_err_t handlePostUrlPath(httpd_req_t *pRequest)
{
    esp_err_t result = ESP_FAIL;
    char *pBuffer;
    size_t bufferLength;

    /* Read URL query string length and allocate memory for length + 1,
     * extra byte for null termination */
    bufferLength = httpd_req_get_url_query_len(pRequest) + 1;
    if(bufferLength > 1) 
    {
        pBuffer = malloc(bufferLength);
        if(httpd_req_get_url_query_str(pRequest, pBuffer, bufferLength) == ESP_OK) 
        {
            ESP_LOGI(TAG, "Found URL query => %s", pBuffer);
            s_pRequest = pRequest;
            char requestData[REQUEST_DATA_SIZE] = {0};
            result = findRequestData(requestData);

            if(strcmp(pBuffer, "command=wifi") == 0)
            {
                configWifi(requestData);
            }
            else if(strcmp(pBuffer, "command=cloud_server") == 0)
            {
				configCloudServer(requestData);					
            }
            else if(strcmp(pBuffer, "command=switch") == 0)
            {
                
            }
            else if(strcmp(pBuffer, "command=ble_cmd") == 0)
            {
                
            }
            else if(strcmp(pBuffer, "command=ble_init") == 0)
            {
                configBleInit(requestData);		
            }
        }
        free(pBuffer);
    }
    return result;
}

在configWifi()函数中对请求数据进行JSON解析,获取SSID和密码。
调用sendPostResponse进行响应回复,随后开启定时器,1秒后进入配置WIFI连接路由器startDelayHandleTimer()


• 由 Leung 写于 2020 年 4 月 27 日

• 参考:ESP-IDF编程指南 - HTTP服务器

猜你喜欢

转载自blog.csdn.net/qq_36347513/article/details/105794952
今日推荐