ESP32之wifi_HTTP
前言
国产的芯片的,性价比 真是高,ESP32-3C 经典版 12.9 SRAM 400K flash 4M ,主频160MHZ,
这跟STM32 比,吓死人, 自带蓝牙与WIFI,连模块钱省了,不过,
看下各自占用的资源对比吧 需要联网 不是太苛刻的环境下,首选择ESP32,还有GD32(主频高点),这个也是基于ARM,对比STM32 也不便宜,国产替补品,STM32好多也是国产芯片
STM32 智能小车 RAM 20K flash 64k ARM指令 8-10元
ESP32 -3C HTTP client RAM 400k flash 4M 基于RISC-V指令 11-13元
1:环境
ESP32 3C 经典版 TYPEC 可以下载也可以当UART0 使用 (GPIO 8,9)
VSCODE+IDF5.4.1 安装可能需要科学上网
2:直接上代码
main.c
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_http_client.h"
#include "esp_netif.h" // 替代 tcpip_adapter 的新网络接口组件// WiFi 配置(替换为实际的 WiFi 名称和密码)
#define WIFI_SSID "自己的SSID"
#define WIFI_PASSWORD "自己的密码"// 日志标签
static const char *TAG = "WIFI_HTTP_EXAMPLE";
#define REQUEST_URL "http://192.168.1.3:8080/hello"// 事件组:用于等待 WiFi 连接成功
static EventGroupHandle_t s_wifi_event_group;
const int WIFI_CONNECTED_BIT = BIT0;// WiFi 事件处理函数
static void event_handler(void* arg, esp_event_base_t event_base,int32_t event_id, void* event_data)
{if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {// WiFi 启动后开始连接esp_wifi_connect();} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {// 连接断开后重试esp_wifi_connect();xEventGroupClearBits(s_wifi_event_group, WIFI_CONNECTED_BIT);ESP_LOGW(TAG, "WiFi disconnect,try again...");} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {// 获取 IP 地址后标记连接成功ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;ESP_LOGI(TAG, "get IP address: " IPSTR, IP2STR(&event->ip_info.ip));xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);}
}// 初始化 WiFi 为 Station 模式
void wifi_init_sta(void)
{// 1. 初始化事件组(用于同步 WiFi 连接状态)s_wifi_event_group = xEventGroupCreate();// 2. 初始化 esp-netif(替代旧的 tcpip_adapter_init())ESP_ERROR_CHECK(esp_netif_init());// 3. 创建默认事件循环(处理 WiFi 和 IP 事件)ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. 创建默认的 WiFi Station 网络接口esp_netif_create_default_wifi_sta();// 5. 初始化 WiFi 驱动wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// 6. 注册事件处理函数(监听 WiFi 和 IP 事件)ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));// 7. 配置 WiFi 连接参数(SSID 和密码)wifi_config_t wifi_config = {.sta = {.ssid = WIFI_SSID,.password = WIFI_PASSWORD,},};ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); // 设置为 Station 模式ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); // 应用配置ESP_ERROR_CHECK(esp_wifi_start()); // 启动 WiFiESP_LOGI(TAG, "WiFi init finish,connect %s...", WIFI_SSID);
}// HTTP 事件回调函数(处理服务器响应数据)
// HTTP 请求的处理函数
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{// 缓存http响应的buffer// static char *output_buffer;// 已经读取的字节数static int output_len;switch(evt->event_id) {case HTTP_EVENT_ERROR:ESP_LOGD(TAG, "HTTP_EVENT_ERROR");break;case HTTP_EVENT_ON_CONNECTED:ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");break;case HTTP_EVENT_HEADER_SENT:ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");break;case HTTP_EVENT_ON_HEADER:ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);break;case HTTP_EVENT_ON_DATA:ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);if (!esp_http_client_is_chunked_response(evt->client)) {// 如果配置了user_data buffer,则把响应复制到该buffer中if (evt->user_data) {memcpy(evt->user_data + output_len, evt->data, evt->data_len);} else {// if (output_buffer == NULL) {// output_buffer = (char *) malloc(esp_http_client_get_content_length(evt->client));// output_len = 0;// if (output_buffer == NULL) {// ESP_LOGE(TAG, "Failed to allocate memory for output buffer");// return ESP_FAIL;// }// }// memcpy(output_buffer + output_len, evt->data, evt->data_len);ESP_LOGD(TAG,"%.*s", evt->data_len, (char*)evt->data);}output_len += evt->data_len;}break;case HTTP_EVENT_ON_FINISH:ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");// if (output_buffer != NULL) {// // Response is accumulated in output_buffer. Uncomment the below line to print the accumulated response// // ESP_LOG_BUFFER_HEX(TAG, output_buffer, output_len);// free(output_buffer);// output_buffer = NULL;// }output_len = 0;break;case HTTP_EVENT_DISCONNECTED:ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");// if (output_buffer != NULL) {// free(output_buffer);// output_buffer = NULL;// }output_len = 0;break;default:ESP_LOGI(TAG, "HTTP_EVENT_OTHER");}return ESP_OK;
}char *str_to_hex_str(const char *input,int inputlen,char* output) {if (input == NULL) return NULL;size_t len = inputlen;//strlen(input);// 每个字符转十六进制占 2 个字符,再加上可能的分隔符、字符串结束符 '\0'// 这里示例没加分隔符,直接连续存,所以总长度是 len * 2 + 1// char *hex_buf = (char *)malloc(len * 2 + 1);// if (hex_buf == NULL) return NULL;char *p = output;//hex_buf;for (size_t i = 0; i < len; i++) {// 逐个字符格式化到 hex_bufp += sprintf(p, "%02x", (unsigned char)input[i]);}return output;
}// 发送 HTTP GET 请求
void http_get_request(const char *url)
{// 响应结果放在这里char local_response_buffer[2048] = {0};// 创建一个 HTTP 客户端配置esp_http_client_config_t config = {.method = HTTP_METHOD_GET,.url = url,.event_handler = _http_event_handler,.user_data = local_response_buffer,.disable_auto_redirect = true,};// 创建一个 HTTP 客户端并执行 GET 请求// 创建一个 HTTP 客户端并执行 GET 请求esp_http_client_handle_t client = esp_http_client_init(&config);esp_err_t err = esp_http_client_perform(client);// 检查请求是否成功if (err == ESP_OK) {int len = esp_http_client_get_content_length(client);ESP_LOGI(TAG, "Status = %d, content_length = %d",esp_http_client_get_status_code(client),//状态码len);//数据长度if(len>0){local_response_buffer[len]=0;ESP_LOGI(TAG, "recv[%s]\n",local_response_buffer);// for(int i=0;i<len;i++){// // ESP_LOGI(TAG, "recv server respon[%x]", sztemphex[i]);// ESP_LOGI(TAG,"%02x",(unsigned char)(local_response_buffer[i]));// }// char sztemphex[2048]={0,};// memset(sztemphex,0,sizeof(sztemphex));// str_to_hex_str(local_response_buffer,len,sztemphex);// ESP_LOGI(TAG, "recv server respon[%s]", sztemphex);}} else {printf("HTTP GET request failed: %s\n", esp_err_to_name(err));}//printf("Response: %.*s\n", strlen(local_response_buffer), local_response_buffer);// ESP_LOGI(TAG, "recv server respon[%d]:%s",strlen(local_response_buffer), local_response_buffer);//断开并释放资源esp_http_client_cleanup(client);}void http_get_request_bak(const char *url)
{// 配置 HTTP 客户端// esp_http_client_config_t config = {// .url = url, // 目标网址// .event_handler = _http_event_handler, // 响应处理回调// .timeout_ms = 10000, // 超时时间(10秒)// };esp_http_client_config_t config = {.url = REQUEST_URL,.event_handler = _http_event_handler,.timeout_ms = 10000,// 关键:显式指定 HTTP 版本为 1.1(避免自动降级).transport_type = HTTP_TRANSPORT_OVER_TCP,
};// 初始化客户端并发送请求esp_http_client_handle_t client = esp_http_client_init(&config);esp_http_client_set_header(client, "User-Agent", "ESP32-HTTP-Example/1.0");esp_err_t err = esp_http_client_perform(client);if (err == ESP_OK) {ESP_LOGI(TAG, "HTTP state code: %d", esp_http_client_get_status_code(client));// 读取并打印响应内容//char response_buf[1024] = {0}; // 缓冲区,根据实际响应大小调整// int read_len = esp_http_client_read(client, response_buf, sizeof(response_buf) - 1);int total_len = 0;char buffer[1024] = {0};while (1) {int read_len = esp_http_client_read(client, buffer + total_len, sizeof(buffer) - total_len - 1);if (read_len <= 0) break; // 读取完毕或出错total_len += read_len;// 防止缓冲区溢出(可选,根据需求调整)if (total_len >= sizeof(buffer) - 1) break; }buffer[total_len] = '\0'; // 确保字符串结尾ESP_LOGI(TAG, "recv server respon[%d]:%s",total_len,buffer);// if (read_len > 0) {// ESP_LOGI(TAG, "recv server respon:%s",response_buf);// // printf("%s\n", response_buf); // 直接打印原始响应// } else if (read_len == 0) {// ESP_LOGI(TAG, "server respon NULL");// } else {// ESP_LOGE(TAG, "server respon fail");// }} else {ESP_LOGE(TAG, "HTTP request fail: %s", esp_err_to_name(err));}// 清理资源esp_http_client_cleanup(client);
}void app_main(void)
{// 1. 初始化 NVS(存储 WiFi 配置等信息)esp_err_t ret = nvs_flash_init();if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {ESP_ERROR_CHECK(nvs_flash_erase()); // 若 NVS 满或版本不兼容,先擦除ret = nvs_flash_init();}ESP_ERROR_CHECK(ret);// 2. 初始化 WiFi 并连接wifi_init_sta();// 3. 等待 WiFi 连接成功(阻塞直到获取 IP)xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT,pdFALSE, pdTRUE, portMAX_DELAY);ESP_LOGI(TAG, "WiFi connect success,start send HTTP request...");// 4. 访问目标网址(http://www.ip.cn/ 会重定向到 https://myip.xyz/zh/)// const char *url = "http://www.ip.cn/";ESP_LOGI(TAG, "vist url: %s", REQUEST_URL);http_get_request(REQUEST_URL);// 5. 程序保持运行(可添加循环请求逻辑)while (1) {vTaskDelay(pdMS_TO_TICKS(8000)); // 每 8 秒循环一次http_get_request(REQUEST_URL); // 取消注释可实现定时重复请求}
}
golang server代码
package mainimport ("context" // 添加缺失的context包"fmt""log""net/http""os""os/signal""syscall""time"
)func main() {// 创建一个带有超时设置的服务器实例server := &http.Server{Addr: ":8080",Handler: setupRouter(),ReadTimeout: 10 * time.Second,WriteTimeout: 10 * time.Second,IdleTimeout: 30 * time.Second,}// 启动服务器的goroutinego func() {log.Printf("服务器启动,监听端口: %s\n", server.Addr)if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {log.Fatalf("服务器启动失败: %v\n", err)}}()// 优雅关闭服务器waitForShutdown(server)
}// 设置路由和中间件func setupRouter() http.Handler {mux := http.NewServeMux()// 注册路由mux.HandleFunc("/", homeHandler)mux.HandleFunc("/hello", helloHandler)// 使用中间件包装路由return loggingMiddleware(mux)
}// 首页处理器func homeHandler(w http.ResponseWriter, r *http.Request) {w.Write([]byte("欢迎来到Go HTTP服务器!\n"))
}// 问候处理器func helloHandler(w http.ResponseWriter, r *http.Request) {curtime := time.Now().Unix()log.Printf("Received request from %s, Method: %s, Headers: %v",r.RemoteAddr, r.Method, r.Header)rep := fmt.Sprintf("hello_time=%v", curtime)w.Write([]byte(rep))//w.Write([]byte("hello,world!\n"))log.Println(fmt.Sprintf("recv request from %v time=%v len(rep)=%v_%v", r.RemoteAddr, curtime, len(rep), rep))
}// 等待关闭信号并优雅地关闭服务器func waitForShutdown(server *http.Server) {interruptChan := make(chan os.Signal, 1)signal.Notify(interruptChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)// 等待中断信号<-interruptChanlog.Println("接收到关闭信号,开始优雅关闭服务器...")// 创建一个5秒的超时时间来关闭服务器ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()// 优雅关闭服务器,等待现有请求完成if err := server.Shutdown(ctx); err != nil {log.Printf("服务器关闭错误: %v\n", err)} else {log.Println("服务器已优雅关闭")}
}
3:测试结果 如果对你又帮助,麻烦点个赞,加个关注