基于Freertos的ESP-IDF开发——8.使用wifi访问HTTP服务器

news2024/11/28 11:47:15

目录

  • 0. 前言
    • 其他ESP-IDF文章
  • 1. 前期准备
    • 1.1头文件准备
    • 1.2 http 服务器搭建
  • 2. 连接 wifi
  • 3.http访问任务
  • 4. 完整代码

0. 前言

使用ESP32使用 wifi 访问 http 服务器

开发环境:ESP-IDF 4.2
操作系统:Ubuntu22.04
开发板:自制的ESP32-WROOM-32E

其他ESP-IDF文章

Windows下espidf的环境搭建(超详细,看完一定会!)

Ubuntu下ESP-IDF的环境搭建

基于Freertos的ESP-IDF开发——1.HelloWorld
基于Freertos的ESP-IDF开发——2.点亮一颗LED
基于Freertos的ESP-IDF开发——3.使用任务(上)
基于Freertos的ESP-IDF开发——3.使用任务(中)
基于Freertos的ESP-IDF开发——3.使用任务(下)
基于Freertos的ESP-IDF开发——4.使用任务的方式来点亮LED灯
基于Freertos的ESP-IDF开发——5.使用按键[不带消抖、带消抖、长按短按识别]
基于Freertos的ESP-IDF开发——6.使用DHT1温湿度传感器
基于Freertos的ESP-IDF开发——7.WS2812B彩色灯循环

1. 前期准备

1.1头文件准备

准备好头文件以及全局变量(wifi名和密码)

#include <stdio.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 "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

/* Wi-Fi网络SSID和密码 */
#define WIFI_SSID "Xiaomi_E417"
#define WIFI_PASSWORD "********"

/* 目标IP地址 */
#define DEST_IP "192.168.31.58"

/* 目标域名 */
#define DEST_DOMAIN "192.168.31.58"

/* 事件循环标签 */
static const char *TAG = "main";

1.2 http 服务器搭建

HTTP(Hyper Text Transfer Protocol)是一种用于传输超文本的协议。它是客户端和服务器之间通信的基础,是访问万维网的标准协议。HTTP 协议不涉及数据包的传输,只负责规定了客户端和服务器之间的通信格式。

HTTP 协议的主要特点包括:

支持客户端/服务器模式。客户端和服务器之间通过 HTTP 协议通信,从而实现信息传输。

使用面向连接或无连接的方式进行通信。无连接方式即每次请求服务时都要建立连接,请求结束即断开连接;而面向连接方式则是先建立连接,再发送请求和接收响应。大部分的 Web 服务都是采用无连接方式。

使用请求/响应模型。客户端发送请求,服务器接收请求并发送响应,响应包括状态码、响应头和实体。

采用ASCII码传输。HTTP 协议的传输数据都是 ASCII 码,并不支持二进制传输。

HTTP 采用明文传输数据。因此对于机密性要求高的信息,需要在传输层进行加密,如使用 HTTPS 协议。

HTTP 协议通常使用 TCP/IP 协议族进行数据传输,但是也可以使用其他协议。在 HTTP/2 标准出现之前,HTTP 通常是基于文本的,但是在 HTTP/2 中,采用了二进制格式,这样可以提高性能。

使用Python的Flask框架搭建一个简单的http服务器,它的默认端口是80,由于我们是Linux,需要使用root权限才能启用1024以下的端口,所以我需要为root用户安装Flask库

sudo pip3 install flask

windows用户请移步至此查看第三方库的安装方法:

Python第三方库安装——使用vscode、pycharm安装Python第三方库

安装好flask库之后编写简单的代码来实现http服务器的搭建:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_world():
    return "This is IoT_H2's Blog!"

if __name__ == "__main__":
    app.run(host='0.0.0.0',port = 80)

如此以来,就能当你访问以自身IP为域名,80为端口访问自身的时候,页面上就会有提示This is IoT_H2’s Blog!,host设置为0.0.0.0是为了使除自己之外的人也能访问

现在运行此段代码,然后在浏览器地址栏输入自身IP然后回车,看看会有什么样的结果:
在这里插入图片描述
可以看到出现了如我们预期的那样的结果.

现在来编写代码,让ESP32联网然后访问我们的本地服务器来获取这段内容

2. 连接 wifi

使用下面这段代码来连接wifi

/* Wi-Fi连接任务 */
static void wifi_connect_task(void *arg)
{
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASSWORD,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    esp_wifi_connect();
    vTaskDelete(NULL);
}

再创建一个中断函数用于wifi断线重连

/* Wi-Fi事件处理程序 */
static void wifi_event_handler(void* arg, esp_event_base_t event_base, 
                               int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_STA_START) {
        // 在STA模式下,启动Wi-Fi连接任务
        xTaskCreate(&wifi_connect_task, "wifi_connect_task", 4096, NULL, 2, NULL);
    } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // 如果Wi-Fi断开连接,重新连接
        esp_wifi_connect();
    }
}

3.http访问任务

static void network_access_task(void *arg)
{
    struct sockaddr_in dest_addr;// sockaddr_in 用于定义Internet地址结构的结构体
    dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);// 目标IP
    dest_addr.sin_family = AF_INET;//IPV4
    dest_addr.sin_port = htons(80);//端口号
    
    while (1) {
        // 创建通信套接字
        int sockfd = -1;
        sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        
        // 连接到目标地址
        if (connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) == 0) {
            // 发送数据
            const char *message = "GET / HTTP/1.0\r\nHost: " DEST_DOMAIN "\r\n\r\n";
            send(sockfd, message, strlen(message), 0);
            
            // 接收响应数据
            char buffer[1024];
            int recvlen = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
            if (recvlen > 0) {
                buffer[recvlen] = 0;
                ESP_LOGI(TAG, "%s", buffer);//串口打印缓冲区接受到的信息
            }
        }
        
        // 关闭套接字
        shutdown(sockfd, 0);
        close(sockfd);
        
        // 暂停一段时间再次尝试连接,即每隔5s访问一次
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

结构体**sockaddr_in **的解释如下:

struct sockaddr_in {
    uint8_t         sin_len;        /* 结构体总长度 */
    sa_family_t     sin_family;     /* 地址族(AF_INET) */
    in_port_t       sin_port;       /* 端口号 */
    struct  in_addr sin_addr;      /* IP地址 */
    char            sin_zero[8];    /* 保留字节 */
};

sin_len: 结构体的总长度。
sin_family: 地址族,通常为AF_INET表示IPv4协议。
sin_port: 网络字节序的16位端口号。
sin_addr: 网络字节序的32位IP地址。
sin_zero是一个长度为8的保留字段,用于在结构体长度不足时增加填充。

4. 完整代码

除main函数,其他都已经在上面解释过,现在在app_main函数中进行任务创建:

#include <stdio.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 "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"

/* Wi-Fi网络SSID和密码 */
#define WIFI_SSID "Xiaomi_E417"
#define WIFI_PASSWORD "703703703"

/* 目标IP地址 */
#define DEST_IP "192.168.31.58"

/* 目标域名 */
#define DEST_DOMAIN "192.168.31.58"

/* 事件循环标签 */
static const char *TAG = "main";

/* Wi-Fi连接任务 */
static void wifi_connect_task(void *arg)
{
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = WIFI_SSID,
            .password = WIFI_PASSWORD,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    esp_wifi_connect();
    vTaskDelete(NULL);
}

/* 网络访问任务 */
static void network_access_task(void *arg)
{
    struct sockaddr_in dest_addr;
    dest_addr.sin_addr.s_addr = inet_addr(DEST_IP);
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons(80);
    
    while (1) {
        // 创建通信套接字
        int sockfd = -1;
        sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        
        // 连接到目标地址
        if (connect(sockfd, (struct sockaddr *)&dest_addr, sizeof(dest_addr)) == 0) {
            // 发送数据
            const char *message = "GET / HTTP/1.0\r\nHost: " DEST_DOMAIN "\r\n\r\n";
            send(sockfd, message, strlen(message), 0);
            
            // 接收响应数据
            char buffer[1024];
            int recvlen = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
            if (recvlen > 0) {
                buffer[recvlen] = 0;
                ESP_LOGI(TAG, "%s", buffer);
            }
        }
        
        // 关闭套接字
        shutdown(sockfd, 0);
        close(sockfd);
        
        // 暂停一段时间再次尝试连接
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

/* Wi-Fi事件处理程序 */
static void wifi_event_handler(void* arg, esp_event_base_t event_base, 
                               int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_STA_START) {
        // 在STA模式下,启动Wi-Fi连接任务
        xTaskCreate(&wifi_connect_task, "wifi_connect_task", 4096, NULL, 2, NULL);
    } else if (event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // 如果Wi-Fi断开连接,重新连接
        esp_wifi_connect();
    }
}

/* 应用程序入口 */
void app_main()
{
    // 初始化非易失性存储(NVS)区域
    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());
        ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    
    // 初始化TCP/IP协议栈
    tcpip_adapter_init();
    
    // 创建默认事件循环
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    
    // 初始化Wi-Fi
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    
    // 注册Wi-Fi事件处理程序
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_START, &wifi_event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL));
    
    // 启动Wi-Fi
    ESP_ERROR_CHECK(esp_wifi_start());
    
    // 启动网络访问任务
    xTaskCreate(&network_access_task, "network_access_task", 4096, NULL, 2, NULL);
}

效果演示:
在这里插入图片描述

你也可以让它变成获取实时时间:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/590377.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

流行框架(二)网络请求库 OKhttp

文章目录 概述HttpURLConnectionGET和POST获取文本数据GETPOST OKHttp基本使用依赖与权限发起一个get请求重要概念OkHttpClientRequestCallRealCallAsyncCall 请求调度器Dispatcher同步请求execute的执行异步请求enqueue的执行两种请求方式的总结 OkHttp拦截器链拦截器种类addI…

字节狂问1小时,小伙offer到手,太狠了!(字节面试真题)

前言&#xff1a; 在尼恩的&#xff08;50&#xff09;读者社群中&#xff0c;经常有小伙伴&#xff0c;需要面试 头条、美团、阿里、京东等大厂。 下面是一个小伙伴成功拿到字节飞书offer&#xff0c;通过一小时拷问的面试经历&#xff0c;就两个字&#xff1a; 深&#xf…

基于STM32的SYN6288语音播报模块驱动实验(代码开源)

前言&#xff1a;本文为手把手教学 SYN6288 语音播报模块的驱动实验&#xff0c;本教程的 MCU 采用STM32F103ZET6。通过 CubeMX 软件配置 UART 串口协议驱 SYN6288 模块进行规定的语音播报。考虑到 SYN6288 模块的集成化与智能化很高&#xff0c;所以该模块的使用是极其便利的。…

【HarmonyOS】初识低代码平台开发元服务

【关键字】 HarmonyOS、低代码平台、元服务开发、拖拽式开发 【写在前面】 今天要分享的是HarmonyOS中的低代码开发相关的内容&#xff0c;低代码开发是DevEco Studio提供的一种UI界面可视化的构建方式&#xff0c;通过图形化的自由拖拽数据的参数化配置&#xff0c;可以快速…

【Java项目】基于SpringBoot+Vue的校园二手商品交易平台

文章目录 功能简述功能展示用户模块购物车模块管理员模块物物对价功能实现 代码 视频演示 代码下载 项目内含有 功能简述 系统登录界面的实现 系统首页界面的实现 用户信息管理界面的实现 商品购物功能的实现 购物车管理功能及支付功能的实现 物物对价功能的实现 用户安全设置…

【面试需了解】jvm垃圾回收机制-GC基础知识、jvm基本组成、查看、排查

前言 jvm垃圾回收机制-GC基础知识、jvm基本组成、查看、排查 文章目录 前言GC基础知识概述 JVM基本组成1. 虚拟机的组成2. jvm的内存区域 查看jvm排查jvm问题1. 正常运行的系统2. 对于已经发生了OOM的系统 GC基础知识 概述 什么是垃圾 一个对象没有被引用&#xff0c;没有任何…

Spring MVC详解(学习总结)

一、Sprig MVC简介1.1介绍1.2MVC是什么 二、Spring MVC实现原理2.1核心组件2.2工作流程 三、第一个Spring MVC四、常用注解五、参数绑定5.1URL风格参数绑定5.2RESTful风格的URL参数获取5.3映射Cookie5.4使用POJO绑定参数5.5JSP页面的转发和重定向 六、Spring MVC数据绑定6.1基本…

vulnstack(红日)内网渗透靶场二: 免杀360拿下域控

前言 在我之前的文章vulnstack(一)打靶&#xff0c;我主要依赖Cobalt Strike进行后期渗透测试&#xff0c;这次我计划使用Metasploit框架(MSF)来进行这个阶段的工作。这个靶场与之前的不同之处在于它的WEB服务器安装了360安全卫士。虽然这增加了挑战的难度&#xff0c;但只要我…

Shell脚本攻略:循环语句while、until

目录 一、理论 1.while 2.until 3.break 4.continue 二、实验 1.实验一 2.实验二 3.实验三 4.实验四 5.实验五 一、理论 1.while (1)while用法 while循环满足条件执行&#xff0c;不满足不执行。 用于不知道循环次数&#xff0c;需要主动结束循环或达到条件结束…

二开项目权限应用全流程-按钮级控制

二开项目权限应用全流程-按钮级控制 员工A和员工B都可以访问同一个页面&#xff08;以员工管理为例&#xff09;&#xff0c;但是员工A可以导出excel&#xff0c;员工B就不可以导出excel(看不到按钮) 思路 用户登陆成功后&#xff0c;用户可以访问的按钮级别权限保存在point…

阿里巴巴淘天集团后端暑期实习面经

目录 1.面向对象三大特性2.重写和重载3.protected 关键字和 default 关键字的作用范围4.栈帧中有哪些东西&#xff1f;5.堆中有哪些区域&#xff1f;6.new 一个对象存放在哪里&#xff1f;7.CMS 收集器回收阶段8.CMS 收集器回收过程哪些需要暂停线程&#xff1f;9.HashMap JDK …

手机行业再多一条“鲶鱼”,小度青禾要打一场漂亮突围战?

文 | 智能相对论 作者 | 佘凯文 智能手机到底还是不是一门好生意&#xff1f; 在换机周期被无限拉长、市场竞争越发激烈、高端市场迟迟无法突破等共同背景下&#xff0c;智能手机到底还是不是一门好生意&#xff0c;成为行业内这两年被热议的话题之一。 由TechInsights发布…

腾讯云轻量应用服务器CPU主频多少?型号?

腾讯云轻量应用服务器CPU型号是什么&#xff1f;轻量服务器处理器主频&#xff1f;腾讯云服务器网账号下的CPU处理器型号为2.5GHz主频的Intel(R) Xeon(R) Gold 6133 CPU和2.4GHz主频Intel(R) Xeon(R) CPU E5-26xx v4&#xff0c;腾讯云轻量应用服务器不支持指定底层物理服务器的…

NodeJs内存快照分析

&#xff08;头等人&#xff0c;有本事&#xff0c;没脾气&#xff1b;二等人&#xff0c;有本事&#xff0c;有脾气&#xff1b;末等人&#xff0c;没本事&#xff0c;大脾气。——南怀瑾&#xff09; NodeJs内存分析的必要性 回顾过去&#xff0c;我们排查web应用问题的途径…

36岁大龄程序员全职接单三个月的感触

36岁大龄程序员&#xff0c;原以为逃过35岁危机&#xff0c;没想到在年前被优化&#xff0c;拿了N2&#xff0c;12w薪资后&#xff0c;我开始了全职接单的道路。现在每个月平均收入有个20K&#xff0c;一路走来挺有感触的&#xff0c;把自己的经验分享给大家。 赚钱&#xff0…

【Jmeter】生成html格式接口自动化测试报告

jmeter自带执行结果查看的插件&#xff0c;但是需要在jmeter工具中才能查看&#xff0c;如果要向领导提交测试结果&#xff0c;不够方便直观。 笔者刚做了这方面的尝试&#xff0c;总结出来分享给大家。 这里需要用到ant来执行测试用例并生成HTML格式测试报告。 一、ant下载安…

Android13蓝牙 停用绝对音量功能

Android13蓝牙 停用绝对音量功能 文章目录 Android13蓝牙 停用绝对音量功能一、前言二、代码实现分析过程1、查看SettingsLib源码资源2、查看原生Setitntgs 相关字符&#xff08;1&#xff09;xml 布局文件中的显示&#xff08;2&#xff09; java 代码文件中的控制串口上控制&…

Vue注册界面精美模板分享

文章目录 &#x1f412;个人主页&#x1f3c5;Vue项目常用组件模板仓库&#x1f4d6;前言&#xff1a;&#x1f380;源码如下&#xff1a; &#x1f412;个人主页 &#x1f3c5;Vue项目常用组件模板仓库 &#x1f4d6;前言&#xff1a; 本篇博客主要提供vue组件之注册组件源码…

硬件软件【部署】

开发板和主机 1.功能不同&#xff1a;帮助开发者进行嵌入式系统的开发和调试&#xff0c;具有较强的硬件拓展能力&#xff0c;可以连接各种传感器/执行器等外设。主机为满足一般的计算需求而设计&#xff0c;具备更强的计算和图形处理能力。 2.架构不同&#xff1a;开发板通常…

解决CentOS7用户管理报错问题:密码设为Aa12345@时报错,为什么Bji230309@可以而Aa12345@不行?

场景&#xff1a; 早上博友(CSDN博客朋友)给我发私信&#xff0c;问了一个问题&#xff1a; CentOS7上面运行的项目&#xff0c;在用户管理添加新用户时&#xff0c;密码设置成Aa12345时&#xff0c;会报错&#xff1a;Cannot read property message of undefined。查看/etc/…