当前位置: 首页 > news >正文

ESP32学习笔记(基于IDF):连接手机热点,用TCP协议实现数据双向通信

目录

  • 前言
  • 实验一:ESP32向手机发送数据
  • 实验二:手机向ESP32发送指令

前言

ESP32学习笔记(基于IDF):IOT应用——WIFI连接
前面学习了如何将ESP32连接的手机热点,现在打算尝试ESP32跟手机之间的通信

实验一:ESP32向手机发送数据

1、在sta.c中写入如下代码

#include <stdio.h>
#include "nvs_flash.h"// NVS闪存初始化库(用于存储Wi-Fi等配置信息的非易失性存储)
#include "esp_wifi.h"// Wi-Fi功能库(ESP32 Wi-Fi相关API)
#include "esp_event.h"// 事件处理库(用于处理Wi-Fi连接、IP获取等事件)
#include "esp_log.h"// 日志库(用于打印调试信息)
#include "esp_err.h"// 错误处理库(定义ESP32错误码)
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "lwip/sockets.h"  //TCP通信需要的头文件#define TEST_SSID "Xiaomi 14 Pro"// 要连接的Wi-Fi热点名称(SSID)
#define TEST_PASSWORD "12345678"// 要连接的Wi-Fi密码#define TAG "sta"// 日志标签(打印日志时的标识,方便筛选)// 手机TCP服务器信息(手机IP和端口)
#define PHONE_IP "192.168.1.99"  // 手机IP(网关地址)
#define PHONE_PORT 8080             // 手机端口(和调试助手一致)// 全局变量:标记是否已获取IP(用于判断是否可以发送数据)
static bool has_ip = false;/*** Wi-Fi事件处理函数* 用于响应Wi-Fi连接状态变化、IP地址获取等事件* @param event_handler_arg: 事件处理参数(未使用)* @param event_base: 事件类型(如WIFI_EVENT、IP_EVENT)* @param event_id: 具体事件ID(如连接成功、断开连接)* @param event_data: 事件相关数据(未使用)*/
void wifi_event_handle(void* event_handler_arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if(event_base == WIFI_EVENT)// 处理Wi-Fi相关事件(WIFI_EVENT类型){switch(event_id){case WIFI_EVENT_STA_START: //事件1:STA模式启动完成(Wi-Fi已准备好连接)esp_wifi_connect();// 调用连接函数,尝试连接到指定Wi-Fibreak;case WIFI_EVENT_STA_CONNECTED:// 事件2:STA成功连接到Wi-Fi热点ESP_LOGI(TAG,"esp32 connected to ap!");break;case WIFI_EVENT_STA_DISCONNECTED:// 事件3:STA与Wi-Fi热点断开连接ESP_LOGE(TAG,"esp32 connect the ap faild! retry!");vTaskDelay(pdMS_TO_TICKS(1000));esp_wifi_connect(); // 自动重试连接has_ip = false;  // 断开连接后重置IP标记break;default:break;}}else if(event_base == IP_EVENT)//处理IP相关事件(IP_EVENT类型){switch(event_id){case IP_EVENT_STA_GOT_IP://事件:STA成功获取IP地址(此时可正常联网)ESP_LOGI(TAG,"esp32 get ip address");has_ip = true; //获取IP后标记为可发送数据break;}}
}// 发送数据的任务:周期性向手机发送数据
static void send_data_task(void *arg)
{int sockfd;  //  socket描述符struct sockaddr_in server_addr;while (1) {//等待获取IP后再连接服务器if (!has_ip) {vTaskDelay(pdMS_TO_TICKS(1000));continue;}// 1. 创建TCP socketsockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {ESP_LOGE(TAG, "Failed to create socket");vTaskDelay(pdMS_TO_TICKS(2000));continue;}// 2. 配置服务器地址(手机IP和端口)memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;   //使用 IPv4 地址格式,例如:192.168.1.100,这样的格式server_addr.sin_port = htons(PHONE_PORT);  // 端口转换为网络字节序// 将手机IP字符串转换为网络地址if (inet_pton(AF_INET, PHONE_IP, &server_addr.sin_addr) <= 0) {ESP_LOGE(TAG, "Invalid phone IP address");close(sockfd);vTaskDelay(pdMS_TO_TICKS(2000));continue;}// 3. 连接手机的TCP服务器if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {ESP_LOGE(TAG, "Failed to connect to phone server");close(sockfd);vTaskDelay(pdMS_TO_TICKS(2000));continue;}ESP_LOGI(TAG, "Connected to phone server!");// 4. 循环发送模拟的温度数据(每2秒一次)while (has_ip) {// 生成随机温度(10~30℃)int temp = rand() % 21 + 10;  // 随机数:10-30char data[50];sprintf(data, "ESP32温度: %d℃\n", temp);  // 格式化数据// 发送数据到手机int send_len = send(sockfd, data, strlen(data), 0);if (send_len < 0) {ESP_LOGE(TAG, "Failed to send data");break;  // 发送失败则断开重连}ESP_LOGI(TAG, "发送数据: %s", data);vTaskDelay(pdMS_TO_TICKS(2000));  // 每2秒发一次}// 5. 断开连接close(sockfd);ESP_LOGI(TAG, "Disconnected from server");vTaskDelay(pdMS_TO_TICKS(1000));}
}void app_main(void)
{// 1. 初始化NVS闪存(用于存储Wi-Fi配置等信息,必须先初始化)// ESP_ERROR_CHECK:检查函数返回值,若出错则终止程序并打印错误ESP_ERROR_CHECK(nvs_flash_init());//2. 初始化网络接口(创建网络协议栈,如TCP/IP)ESP_ERROR_CHECK(esp_netif_init());// 3. 创建默认事件循环(用于处理Wi-Fi、IP等事件的消息队列)ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. 创建默认的STA模式网络接口(绑定Wi-Fi STA与TCP/IP协议栈)esp_netif_create_default_wifi_sta();// 5. 初始化Wi-Fi配置(使用默认配置,包含MAC地址、信道等基础参数)wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// 应用Wi-Fi初始化配置// 6. 注册事件处理器(将事件与处理函数绑定)// 注册所有Wi-Fi事件(ESP_EVENT_ANY_ID)到wifi_event_handle函数esp_event_handler_register(WIFI_EVENT,ESP_EVENT_ANY_ID,wifi_event_handle,NULL);// 注册IP获取事件(IP_EVENT_STA_GOT_IP)到wifi_event_handle函数esp_event_handler_register(IP_EVENT,IP_EVENT_STA_GOT_IP,wifi_event_handle,NULL);// 7. 配置要连接的Wi-Fi参数(热点名称、密码、加密方式等)wifi_config_t wifi_config = {.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK,// 加密方式(WPA2-PSK,常见家用Wi-Fi加密).sta.pmf_cfg.capable = true,// 支持PMF.sta.pmf_cfg.required = false,// 不强制要求路由器支持PMF};// 清空SSID数组(避免残留垃圾数据)memset(&wifi_config.sta.ssid,0,sizeof(wifi_config.sta.ssid));// 将TEST_SSID(热点名称)复制到Wi-Fi配置中memcpy(wifi_config.sta.ssid,TEST_SSID,strlen(TEST_SSID));// 清空密码数组(避免残留垃圾数据)memset(&wifi_config.sta.password,0,sizeof(wifi_config.sta.password));// 将TEST_PASSWORD(密码)复制到Wi-Fi配置中memcpy(wifi_config.sta.password,TEST_PASSWORD,strlen(TEST_PASSWORD));// 8. 设置Wi-Fi工作模式为STA(客户端模式,连接其他热点)ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));// 应用Wi-Fi配置(将上面设置的热点信息传入Wi-Fi驱动)ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));// 启动Wi-Fi(开始执行连接流程)ESP_ERROR_CHECK(esp_wifi_start());// 创建发送数据的任务(独立于main_task,后台运行)xTaskCreate(send_data_task, "send_data_task", 4096, NULL, 5, NULL);}

在这里插入图片描述
2、主要关注的是这里,这里要写手机TCP服务器的IP跟端口号
在这里插入图片描述
3、在手机上下载一个网络调试助手,设置成TCP服务端,然后这里的主机IP跟端口号我们写到代码里面
在这里插入图片描述
4、把程序下进开发板,手机上就能收到数据了
在这里插入图片描述

实验二:手机向ESP32发送指令

1、在sta.c中写入如下代码

#include <stdio.h>
#include "nvs_flash.h"// NVS闪存初始化库(用于存储Wi-Fi等配置信息的非易失性存储)
#include "esp_wifi.h"// Wi-Fi功能库(ESP32 Wi-Fi相关API)
#include "esp_event.h"// 事件处理库(用于处理Wi-Fi连接、IP获取等事件)
#include "esp_log.h"// 日志库(用于打印调试信息)
#include "esp_err.h"// 错误处理库(定义ESP32错误码)
#include <string.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "lwip/sockets.h"  //TCP通信需要的头文件#define TEST_SSID "Xiaomi 14 Pro"// 要连接的Wi-Fi热点名称(SSID)
#define TEST_PASSWORD "12345678"// 要连接的Wi-Fi密码#define TAG "sta"// 日志标签(打印日志时的标识,方便筛选)// 手机TCP服务器信息(手机IP和端口)
#define PHONE_IP "192.168.1.99"  // 手机IP(网关地址)
#define PHONE_PORT 8080             // 手机端口(和调试助手一致)// 全局变量:标记是否已获取IP(用于判断是否可以发送数据)
static bool has_ip = false;/*** Wi-Fi事件处理函数* 用于响应Wi-Fi连接状态变化、IP地址获取等事件* @param event_handler_arg: 事件处理参数(未使用)* @param event_base: 事件类型(如WIFI_EVENT、IP_EVENT)* @param event_id: 具体事件ID(如连接成功、断开连接)* @param event_data: 事件相关数据(未使用)*/
void wifi_event_handle(void* event_handler_arg,esp_event_base_t event_base,int32_t event_id,void* event_data)
{if(event_base == WIFI_EVENT)// 处理Wi-Fi相关事件(WIFI_EVENT类型){switch(event_id){case WIFI_EVENT_STA_START: //事件1:STA模式启动完成(Wi-Fi已准备好连接)esp_wifi_connect();// 调用连接函数,尝试连接到指定Wi-Fibreak;case WIFI_EVENT_STA_CONNECTED:// 事件2:STA成功连接到Wi-Fi热点ESP_LOGI(TAG,"esp32 connected to ap!");break;case WIFI_EVENT_STA_DISCONNECTED:// 事件3:STA与Wi-Fi热点断开连接ESP_LOGE(TAG,"esp32 connect the ap faild! retry!");vTaskDelay(pdMS_TO_TICKS(1000));esp_wifi_connect(); // 自动重试连接has_ip = false;  // 断开连接后重置IP标记break;default:break;}}else if(event_base == IP_EVENT)//处理IP相关事件(IP_EVENT类型){switch(event_id){case IP_EVENT_STA_GOT_IP://事件:STA成功获取IP地址(此时可正常联网)ESP_LOGI(TAG,"esp32 get ip address");has_ip = true; //获取IP后标记为可发送数据break;}}
}// 发送数据的任务:周期性向手机发送数据
static void send_data_task(void *arg)
{int sockfd;  //  socket描述符struct sockaddr_in server_addr;char recv_buf[100];  // 接收缓冲区while (1) {//等待获取IP后再连接服务器if (!has_ip) {vTaskDelay(pdMS_TO_TICKS(1000));continue;}// 1. 创建TCP socketsockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {ESP_LOGE(TAG, "Failed to create socket");vTaskDelay(pdMS_TO_TICKS(2000));continue;}// 2. 配置服务器地址(手机IP和端口)memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;   //使用 IPv4 地址格式,例如:192.168.1.100,这样的格式server_addr.sin_port = htons(PHONE_PORT);  // 端口转换为网络字节序// 将手机IP字符串转换为网络地址if (inet_pton(AF_INET, PHONE_IP, &server_addr.sin_addr) <= 0) {ESP_LOGE(TAG, "Invalid phone IP address");close(sockfd);vTaskDelay(pdMS_TO_TICKS(2000));continue;}// 3. 连接手机的TCP服务器if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {ESP_LOGE(TAG, "Failed to connect to phone server");close(sockfd);vTaskDelay(pdMS_TO_TICKS(2000));continue;}ESP_LOGI(TAG, "Connected to phone server! 可发送指令控制(LED_ON/LED_OFF)");// 循环:同时发送温度数据和接收手机指令while (has_ip) {// 生成随机温度(10~30℃)int temp = rand() % 21 + 10;  // 随机数:10-30char data[50];sprintf(data, "ESP32温度: %d℃\n", temp);  // 格式化数据// 发送数据到手机int send_len = send(sockfd, data, strlen(data), 0);if (send_len < 0) {ESP_LOGE(TAG, "Failed to send data");break;  // 发送失败则断开重连}ESP_LOGI(TAG, "发送数据: %s", data);// 2. 接收手机指令(非阻塞,超时1秒)struct timeval timeout = {1, 0};  // 1秒超时setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf)-1, 0);if (recv_len > 0) {recv_buf[recv_len] = '\0';  // 加结束符ESP_LOGI(TAG, "收到手机指令: %s", recv_buf);// 模拟控制LEDif (strcmp(recv_buf, "LED_ON") == 0) {ESP_LOGI(TAG, "指令执行: LED点亮");} else if (strcmp(recv_buf, "LED_OFF") == 0) {ESP_LOGI(TAG, "指令执行: LED熄灭");} else {ESP_LOGI(TAG, "指令未知: %s", recv_buf);}}vTaskDelay(pdMS_TO_TICKS(2000));  // 每2秒发一次}// 5. 断开连接close(sockfd);ESP_LOGI(TAG, "Disconnected from server");vTaskDelay(pdMS_TO_TICKS(1000));}
}void app_main(void)
{// 1. 初始化NVS闪存(用于存储Wi-Fi配置等信息,必须先初始化)// ESP_ERROR_CHECK:检查函数返回值,若出错则终止程序并打印错误ESP_ERROR_CHECK(nvs_flash_init());//2. 初始化网络接口(创建网络协议栈,如TCP/IP)ESP_ERROR_CHECK(esp_netif_init());// 3. 创建默认事件循环(用于处理Wi-Fi、IP等事件的消息队列)ESP_ERROR_CHECK(esp_event_loop_create_default());// 4. 创建默认的STA模式网络接口(绑定Wi-Fi STA与TCP/IP协议栈)esp_netif_create_default_wifi_sta();// 5. 初始化Wi-Fi配置(使用默认配置,包含MAC地址、信道等基础参数)wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();ESP_ERROR_CHECK(esp_wifi_init(&cfg));// 应用Wi-Fi初始化配置// 6. 注册事件处理器(将事件与处理函数绑定)// 注册所有Wi-Fi事件(ESP_EVENT_ANY_ID)到wifi_event_handle函数esp_event_handler_register(WIFI_EVENT,ESP_EVENT_ANY_ID,wifi_event_handle,NULL);// 注册IP获取事件(IP_EVENT_STA_GOT_IP)到wifi_event_handle函数esp_event_handler_register(IP_EVENT,IP_EVENT_STA_GOT_IP,wifi_event_handle,NULL);// 7. 配置要连接的Wi-Fi参数(热点名称、密码、加密方式等)wifi_config_t wifi_config = {.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK,// 加密方式(WPA2-PSK,常见家用Wi-Fi加密).sta.pmf_cfg.capable = true,// 支持PMF.sta.pmf_cfg.required = false,// 不强制要求路由器支持PMF};// 清空SSID数组(避免残留垃圾数据)memset(&wifi_config.sta.ssid,0,sizeof(wifi_config.sta.ssid));// 将TEST_SSID(热点名称)复制到Wi-Fi配置中memcpy(wifi_config.sta.ssid,TEST_SSID,strlen(TEST_SSID));// 清空密码数组(避免残留垃圾数据)memset(&wifi_config.sta.password,0,sizeof(wifi_config.sta.password));// 将TEST_PASSWORD(密码)复制到Wi-Fi配置中memcpy(wifi_config.sta.password,TEST_PASSWORD,strlen(TEST_PASSWORD));// 8. 设置Wi-Fi工作模式为STA(客户端模式,连接其他热点)ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));// 应用Wi-Fi配置(将上面设置的热点信息传入Wi-Fi驱动)ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA,&wifi_config));// 启动Wi-Fi(开始执行连接流程)ESP_ERROR_CHECK(esp_wifi_start());// 创建发送数据的任务(独立于main_task,后台运行)xTaskCreate(send_data_task, "send_data_task", 4096, NULL, 5, NULL);}

2、代码中接收手机信息逻辑是这些

            // 2. 接收手机指令(非阻塞,超时1秒)struct timeval timeout = {1, 0};  // 1秒超时setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));int recv_len = recv(sockfd, recv_buf, sizeof(recv_buf)-1, 0);if (recv_len > 0) {recv_buf[recv_len] = '\0';  // 加结束符ESP_LOGI(TAG, "收到手机指令: %s", recv_buf);// 模拟控制LEDif (strcmp(recv_buf, "LED_ON") == 0) {ESP_LOGI(TAG, "指令执行: LED点亮");} else if (strcmp(recv_buf, "LED_OFF") == 0) {ESP_LOGI(TAG, "指令执行: LED熄灭");} else {ESP_LOGI(TAG, "指令未知: %s", recv_buf);}}

3、将程序下入开发板,成功连接手机后,用网络调试助手向ESP32发送LED_ON或LED_OFF
在这里插入图片描述
4、ESP32这边成功收到指令,该实验模拟了用手机控制LED灯的开灯和熄灭
在这里插入图片描述

http://www.dtcms.com/a/506664.html

相关文章:

  • 一个小程序轻量AR体感游戏,开发实现解决方案
  • java整合itext pdf实现固定模版pdf导出
  • 26考研数学一、二、三真题试卷及答案PDF电子版(1987-2025年)
  • Django Web 开发系列(二):视图进阶、快捷函数与请求响应处理
  • 重庆哪些网站推广公司wordpress获取用户名
  • Bevy 渲染系统 Bindless 实现与交互逻辑
  • K8s控制器终极对比:StatefulSet与Deployment详解
  • [Agent可视化] docs | go/rust/py混构 | Temporal编排 | WASI沙箱
  • Linux服务器编程实践55-网络信息API:gethostbyname与gethostbyaddr实现主机名解析
  • Godot 2D游戏开发全流程实战
  • 自动驾驶工程师面试(定位、感知向)
  • Cocos学习——摄像机Camera
  • 千秋网站建设公司百度如何快速收录
  • 深圳大型论坛网站建设免费行情网站在线
  • 《软件测试分类指南(下):从测试阶段到地域适配,拆解落地核心维度》
  • Python 查询网站开发3g小说网站
  • 基于Python的Word文档模板自动化处理:从占位符提取到智能填充
  • vue3子组件向父组件传递参数
  • 阿里云云代理商:阿里云CDN刷新机制是什么?
  • FFmpeg 基本数据结构 AVFormatConext 分析
  • 使用 DrissionPage——实现同花顺股票数据自动化爬虫
  • 基于位置式PID算法调节PWM占空比实现电机转速控制
  • FFmpeg+QT输出音频
  • 友点企业网站管理系统微信商城在哪里找
  • 深度学习(5)-PyTorch 张量详细介绍
  • 西安市建设厅网站软文营销的经典案例
  • Agent 开发设计模式(Agentic Design Patterns )第8章: 智能体记忆管理(Memory Management)
  • Linux 下使用 Docker-Compose 安装 Kafka 和 Kafka-UI(KRaft 模式)
  • 【C++入门篇 - 10】:模板
  • [Linux]学习笔记系列 -- [kernel][lock]mutex