基于ESP32-C3,开发这样一个功能,首先 ESP32-C3出一个热点,然后通过手机访问这个热点 ,打开一个页面,通过页面上传手机中的文件到ESP32-C3上面,类似于无线U盘

要在 ESP32-C3 上开发一个类似无线 U 盘的功能,包括:

  • ESP32-C3 启动 Wi-Fi 热点模式(AP Mode)
  • 通过手机连接到此热点
  • 手机打开浏览器访问 ESP32-C3 提供的网页
  • 在网页上上传手机中的文件到 ESP32-C3

下面,我们将详细介绍如何实现这个功能。这个过程包含以下几个步骤:

  1. 配置 ESP32-C3 Wi-Fi 为热点模式
  2. 设置 HTTP 服务器以提供网页和处理文件上传
  3. 创建用于上传文件的网页
  4. 处理上传的文件并存储
  5. 完整代码示例
  6. 附加注意事项

配置 ESP32-C3 Wi-Fi 为热点模式

首先,我们需要将 ESP32-C3 设置为 Wi-Fi 热点模式(AP Mode),这样手机可以连接到 ESP32-C3。使用 ESP-IDF 提供的 esp_wifi 库,我们可以按照以下步骤配置热点模式。

代码示例:

#include <stdio.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"

#define WIFI_SSID "ESP32C3_AP"
#define WIFI_PASS "12345678" // 至少8个字符

static void wifi_init_softap(void) {
    esp_netif_create_default_wifi_ap();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = WIFI_SSID,
            .ssid_len = strlen(WIFI_SSID),
            .password = WIFI_PASS,
            .max_connection = 4, // 热点可连接的设备数量
            .authmode = WIFI_AUTH_WPA_WPA2_PSK,
        },
    };

    if (strlen(WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN; // 开放网络,不需要密码
    }

    esp_wifi_set_mode(WIFI_MODE_AP);
    esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
    esp_wifi_start();

    printf("ESP32-C3 AP started. SSID:%s password:%s\n", WIFI_SSID, WIFI_PASS);
}

设置 HTTP 服务器以提供网页和处理文件上传

ESP32-C3 将启动一个 HTTP 服务器,提供网页给连接的客户端(如手机),并处理文件上传请求。

我们使用 ESP-IDF 中的 esp_http_server 库来实现 HTTP 服务器功能。这个库允许我们创建路由来处理不同的 HTTP 请求。

代码示例:

#include <esp_http_server.h>

// 创建 HTTP 服务器句柄
static httpd_handle_t start_webserver(void) {
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_handle_t server = NULL;

    if (httpd_start(&server, &config) == ESP_OK) {
        // 注册静态网页的处理函数(GET 请求)
        httpd_uri_t webpage_uri = {
            .uri       = "/",
            .method    = HTTP_GET,
            .handler   = webpage_get_handler, // 我们将编写这个函数来提供网页
            .user_ctx  = NULL
        };
        httpd_register_uri_handler(server, &webpage_uri);

        // 注册文件上传处理函数(POST 请求)
        httpd_uri_t upload_uri = {
            .uri       = "/upload",
            .method    = HTTP_POST,
            .handler   = file_upload_post_handler, // 我们将编写这个函数来处理文件上传
            .user_ctx  = NULL
        };
        httpd_register_uri_handler(server, &upload_uri);
    }

    return server;
}

创建用于上传文件的网页

为了让用户能够通过网页上传文件,我们需要提供一个 HTML 表单。这个网页将在访问 ESP32-C3 的 IP 地址时加载。

HTML 网页示例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>ESP32-C3 File Upload</title>
</head>
<body>
    <h1>Upload File to ESP32-C3</h1>
    <form action="/upload" method="POST" enctype="multipart/form-data">
        <input type="file" name="file" />
        <input type="submit" value="Upload" />
    </form>
</body>
</html>

代码提供网页(webpage_get_handler):

static esp_err_t webpage_get_handler(httpd_req_t *req) {
    const char* webpage =
        "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<meta charset=\"UTF-8\">"
        "<title>ESP32-C3 File Upload</title>"
        "</head>"
        "<body>"
        "<h1>Upload File to ESP32-C3</h1>"
        "<form action=\"/upload\" method=\"POST\" enctype=\"multipart/form-data\">"
        "<input type=\"file\" name=\"file\" />"
        "<input type=\"submit\" value=\"Upload\" />"
        "</form>"
        "</body>"
        "</html>";

    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, webpage, strlen(webpage));
}

在这里插入图片描述

处理上传的文件并存储

当用户通过表单上传文件时,会发送一个 HTTP POST 请求到 /upload。我们需要解析这个请求并将文件内容存储在 ESP32-C3 上(例如,存储到闪存或 SPIFFS 文件系统)。

配置文件系统(SPIFFS)

在处理上传之前,我们需要初始化文件系统来存储文件。这可以通过启用 SPIFFS 来实现。

配置 partitions.csv:

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     0x9000,  0x5000,
otadata,  data, ota,     0xe000,  0x2000,
app0,     app,  ota_0,   0x10000, 0x180000,
spiffs,   data, spiffs,  0x190000,0x70000,

初始化 SPIFFS:

#include "esp_spiffs.h"

static void init_spiffs(void) {
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = NULL,
        .max_files = 5,   // 可以同时打开的文件数
        .format_if_mount_failed = true
    };

    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if (ret != ESP_OK) {
        printf("Failed to initialize SPIFFS (%s)\n", esp_err_to_name(ret));
        return;
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(NULL, &total, &used);
    if (ret == ESP_OK) {
        printf("SPIFFS partition size: total: %d, used: %d\n", total, used);
    } else {
        printf("Failed to get SPIFFS partition information (%s)\n", esp_err_to_name(ret));
    }
}
处理文件上传请求

我们需要在 file_upload_post_handler 函数中处理文件上传请求。HTTP POST 请求中会包含文件数据。我们将使用 httpd_req_recv 函数来读取请求主体数据。

代码示例(file_upload_post_handler):

static esp_err_t file_upload_post_handler(httpd_req_t *req) {
    // 首先,为接收的数据分配足够的缓冲区
    const int MAX_FILE_SIZE = 1024 * 1024; // 假设最大文件大小为1MB
    char *buf = malloc(MAX_FILE_SIZE);
    if (!buf) {
        httpd_resp_send_500(req); // 内存分配失败
        return ESP_FAIL;
    }

    int total_len = req->content_len;
    int received = 0;
    while (received < total_len) {
        int ret = httpd_req_recv(req, buf + received, total_len - received);
        if (ret <= 0) {
            // 收到错误或连接关闭
            free(buf);
            httpd_resp_send_500(req);
            return ESP_FAIL;
        }
        received += ret;
    }

    // 现在,buf 中包含了表单数据,包括文件内容
    // 我们需要解析多部分表单以获取实际文件内容
    // 这里我们假定表单数据简单,并直接处理文件数据
    // 实际上,您需要解析表单边界和头信息以准确提取文件数据

    // 在此简化实现中,我们假设只有文件数据,并将其保存到 SPIFFS 中
    FILE *f = fopen("/spiffs/uploaded_file", "wb");
    if (!f) {
        free(buf);
        httpd_resp_send_500(req);
        return ESP_FAIL;
    }
    fwrite(buf, 1, received, f);
    fclose(f);
    free(buf);

    httpd_resp_sendstr(req, "File uploaded successfully");
    return ESP_OK;
}

注意:实际情况下,HTTP POST 请求的表单数据是多部分格式,需要解析其中的边界和头信息来正确提取文件内容。上面的示例简化了这一过程,但在现实应用中,您需要使用 multipart/form-data 解析器或者手动解析 POST 数据以正确获取文件内容。

完整代码示例

下面的完整代码示例集成了上述步骤:

#include <stdio.h>
#include <string.h>
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_spiffs.h"
#include "esp_http_server.h"

#define WIFI_SSID "ESP32C3_AP"
#define WIFI_PASS "12345678"

static const char *TAG = "HTTP_SERVER";

// 初始化 SPIFFS
static void init_spiffs(void) {
    esp_vfs_spiffs_conf_t conf = {
        .base_path = "/spiffs",
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = true
    };

    esp_err_t ret = esp_vfs_spiffs_register(&conf);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        return;
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(NULL, &total, &used);
    if (ret == ESP_OK) {
        ESP_LOGI(TAG, "SPIFFS partition size: total: %d, used: %d", total, used);
    } else {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
    }
}

// 提供文件上传网页
static esp_err_t webpage_get_handler(httpd_req_t *req) {
    const char* webpage =
        "<!DOCTYPE html>"
        "<html>"
        "<head>"
        "<meta charset=\"UTF-8\">"
        "<title>ESP32-C3 File Upload</title>"
        "</head>"
        "<body>"
        "<h1>Upload File to ESP32-C3</h1>"
        "<form action=\"/upload\" method=\"POST\" enctype=\"multipart/form-data\">"
        "<input type=\"file\" name=\"file\" />"
        "<input type=\"submit\" value=\"Upload\" />"
        "</form>"
        "</body>"
        "</html>";

    httpd_resp_set_type(req, "text/html");
    return httpd_resp_send(req, webpage, strlen(webpage));
}

// 处理文件上传
static esp_err_t file_upload_post_handler(httpd_req_t *req) {
    // 将整个请求主体读取到内存中(仅适用于较小的文件)
    const int MAX_FILE_SIZE = 1024 * 1024;
    if (req->content_len > MAX_FILE_SIZE) {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "File too large");
        return ESP_FAIL;
    }

    char *buf = malloc(req->content_len);
    if (!buf) {
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Not enough memory");
        return ESP_FAIL;
    }

    int received = httpd_req_recv(req, buf, req->content_len);
    if (received <= 0) {
        free(buf);
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to receive file");
        return ESP_FAIL;
    }

    // 简化实现:假设文件数据完全在 buf 中
    // 实际应用中需要解析 multipart/form-data
    FILE *f = fopen("/spiffs/uploaded_file", "wb");
    if (!f) {
        free(buf);
        httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to open file for writing");
        return ESP_FAIL;
    }
    fwrite(buf, 1, received, f);
    fclose(f);
    free(buf);

    httpd_resp_send_str(req, "File uploaded successfully");
    return ESP_OK;
}

// 启动 HTTP 服务器
static httpd_handle_t start_webserver(void) {
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    httpd_handle_t server = NULL;

    if (httpd_start(&server, &config) == ESP_OK) {
        // 注册网页处理函数
        httpd_uri_t webpage_uri = {
            .uri       = "/",
            .method    = HTTP_GET,
            .handler   = webpage_get_handler,
            .user_ctx  = NULL
        };
        httpd_register_uri_handler(server, &webpage_uri);

        // 注册文件上传处理函数
        httpd_uri_t upload_uri = {
            .uri       = "/upload",
            .method    = HTTP_POST,
            .handler   = file_upload_post_handler,
            .user_ctx  = NULL
        };
        httpd_register_uri_handler(server, &upload_uri);

        return server;
    }

    ESP_LOGI(TAG, "Error starting server!");
    return NULL;
}

// 初始化 Wi-Fi 热点
static void wifi_init_softap(void) {
    esp_netif_create_default_wifi_ap();
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    esp_wifi_init(&cfg);

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = WIFI_SSID,
            .ssid_len = strlen(WIFI_SSID),
            .password = WIFI_PASS,
            .max_connection = 4,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK,
        },
    };

    if (strlen(WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    esp_wifi_set_mode(WIFI_MODE_AP);
    esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
    esp_wifi_start();

    ESP_LOGI(TAG, "ESP32-C3 AP started. SSID:%s password:%s", WIFI_SSID, WIFI_PASS);
}

void app_main(void) {
    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);

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    wifi_init_softap();
    init_spiffs();

    httpd_handle_t server = start_webserver();
    if (server == NULL) {
        ESP_LOGE(TAG, "Failed to start webserver");
    }
}

附加注意事项

  1. 文件大小限制:ESP32-C3 的存储空间有限,如果文件过大,可能无法存储或处理。我们在代码中设置了 MAX_FILE_SIZE = 1024 * 1024(1MB),您可以根据需要调整。

  2. 文件系统选择:本示例使用 SPIFFS 存储上传的文件,您也可以选择 FATFS(如 wl_fs_fat) 或其他文件系统。不过,必须根据实际需求选择合适的存储方式。

  3. multipart/form-data 解析:上面的代码简化了表单解析过程。在实际应用中,您需要正确解析 multipart/form-data 内容以获取文件名、文件内容等信息。您可以使用现有的multipart 解析库或自行编写解析逻辑。

  4. 安全性:如果只在开发环境中使用,这种实现是可行的。但是,如果您计划在实际环境中使用,请注意安全问题。设置一个合适的密码(如果需要),并且确保网络环境安全。

  5. HTTP 服务器性能:此 HTTP 服务器实现是基本的。如果需要更高的性能或安全性,可以使用 HTTPS(TL
    S 加密)。对于加密流量,需要配置 TLS 证书。


通过上述步骤,您可以实现 ESP32-C3 创建热点、提供网页、接收文件上传并存储到 SPIFFS。这种功能类似于无线 U 盘,使用户能够通过手机浏览器上传文件到 ESP32-C3。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值