一、背景
首先手机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 日
网友评论