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

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);
}

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

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

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是在 Windows 系统下使用命令行窗口下载具有 esp-idf4.0 版本和 camera_web_server 例程的 esp-who 项目的步骤: 1. 首先,确保已经安装了 Git 和 Python 环境。如果没有安装,可以在官网上下载并安装。 2. 打开命令行窗口(在 Windows 中,可以按下 Win+R,输入 cmd 并回车打开命令行窗口)。 3. 使用以下命令克隆 esp-who 项目: ``` git clone --recursive https://github.com/espressif/esp-who.git ``` 4. 进入 esp-who 项目目录: ``` cd esp-who ``` 5. 切换到 esp-idf4.0 分支: ``` git checkout release/v4.0 ``` 6. 安装 esp-idf 工具链和依赖库。这里使用 idf.py 工具进行安装: ``` python -m pip install --user -r ./requirements.txt ``` 7. 设置 esp-idf 工具链的环境变量。以下命令将在当前命令行窗口中设置环境变量: ``` export IDF_PATH=./esp-idf ``` 8. 进入 camera_web_server 例程目录: ``` cd examples/camera_web_server ``` 9. 使用以下命令配置项目: ``` idf.py menuconfig ``` 10. 在配置界面中,选择 `Example Configuration`,配置 Wi-Fi SSID 和密码等参数。 11. 使用以下命令编译和烧录程序: ``` idf.py -p COMx flash monitor ``` 其中,`COMx` 是 ESP32 开发板连接到电脑上的串口号,`monitor` 参数用于打开串口监视器。 12. 等待编译和烧录过程完成,程序将自动运行并输出日志。现在可以通过浏览器访问 ESP32 开发板的 IP 地址,在页面中查看摄像头捕获的图像。 以上就是在 Windows 系统下使用命令行窗口下载具有 esp-idf4.0 版本和 camera_web_server 例程的 esp-who 项目的步骤。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IoT_H2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值