Espressif 系列:ESP32-C6 系列_(10).ESP32-C6物联网应用开发
ESP32-C6物联网应用开发
目录
-
ESP32-C6简介
-
硬件特性
-
开发环境搭建
-
基本编程入门
-
Wi-Fi连接与配置
-
MQTT协议实现
-
HTTP请求与响应
-
传感器数据采集
-
低功耗模式
-
安全与加密
-
实时操作系统(RTOS)应用
-
蓝牙功能
-
语音识别与处理
-
设备管理与远程控制
-
项目实例:智能家居网关
1. ESP32-C6简介
1.1 概述
ESP32-C6是Espressif Systems推出的一款高性能、低功耗的物联网单片机。它集成了2.4GHz Wi-Fi和低功耗蓝牙(BLE)功能,适用于各种物联网应用。ESP32-C6采用RISC-V架构,具有更高的能效比和更好的代码密度。
1.2 主要特点
高性能处理器:ESP32-C6采用RISC-V架构,主频可达160MHz。
低功耗:深度睡眠模式下的功耗极低,适合电池供电的设备。
丰富的外设接口:包括ADC、DAC、I2C、I2S、SPI、UART等。
内置Wi-Fi和BLE:支持2.4GHz Wi-Fi和低功耗蓝牙。
安全特性:支持多种加密算法,提供硬件加速器和安全启动功能。
开发工具:支持Espressif IDF、Arduino IDE等多种开发环境。
2. 硬件特性
2.1 处理器
ESP32-C6配备了一个RISC-V架构的处理器,主频可达160MHz。RISC-V架构的设计理念是简化指令集,提高能效比,使得ESP32-C6在处理复杂的物联网任务时能够保持较低的功耗。
2.2 内存
SRAM:240KB SRAM,用于存储运行时数据。
ROM:448KB ROM,用于存储固件和系统函数。
Flash:支持外部Flash,最大可扩展到4MB。
2.3 无线功能
Wi-Fi:支持802.11b/g/n标准,提供强大的无线连接能力。
BLE:支持低功耗蓝牙4.2,适用于低功耗无线通信。
2.4 GPIO
ESP32-C6提供了丰富的GPIO接口,可以用于连接各种外设和传感器。GPIO支持多种模式,包括输入、输出、中断等。
2.5 ADC和DAC
ADC:12位精度,支持多个通道,可以用于采集模拟信号。
DAC:8位精度,支持两个通道,可以用于输出模拟信号。
2.6 通信接口
UART:支持3个UART接口,用于串行通信。
SPI:支持2个SPI接口,用于与外设进行高速通信。
I2C:支持2个I2C接口,用于与低速外设通信。
I2S:支持I2S接口,用于音频数据传输。
2.7 安全特性
安全启动:支持安全启动,确保设备固件的完整性。
加密算法:支持多种加密算法,包括AES、SHA、RSA等。
硬件加速器:提供硬件加速器,提高加密性能。
3. 开发环境搭建
3.1 安装Espressif IDF
Espressif IDF(Espressif IoT Development Framework)是Espressif官方提供的开发框架,支持ESP32-C6的开发。以下是安装步骤:
-
安装Python:确保系统中安装了Python 3.6或更高版本。
-
安装依赖:运行以下命令安装必要的依赖:
pip install --user -r $IDF_PATH/requirements.txt
-
下载IDF:从Espressif的GitHub仓库下载IDF:
git clone --recursive https://github.com/espressif/esp-idf.git
-
配置环境变量:将IDF路径添加到环境变量中:
export IDF_PATH=/path/to/esp-idf
-
安装工具链:运行以下命令安装工具链:
. $IDF_PATH/export.sh
3.2 使用Arduino IDE
Arduino IDE是一个流行且易用的开发环境,支持ESP32-C6的开发。以下是安装步骤:
-
安装Arduino IDE:从Arduino官网下载并安装Arduino IDE。
-
添加ESP32板管理器:打开Arduino IDE,进入
文件
->首选项
,在附加板管理器网址
中添加以下URL:https://dl.espressif.com/dl/package_esp32_index.json
-
安装ESP32板支持:进入
工具
->开发板
->开发板管理器
,搜索ESP32
并安装。 -
选择开发板:在
工具
->开发板
中选择ESP32-C6
。
3.3 代码示例:点亮LED
以下是一个简单的代码示例,使用Arduino IDE点亮ESP32-C6上的LED。
// 定义LED引脚
const int LED_PIN = 2;
void setup() {
// 初始化LED引脚为输出模式
pinMode(LED_PIN, OUTPUT);
}
void loop() {
// 点亮LED
digitalWrite(LED_PIN, HIGH);
delay(1000); // 延时1秒
// 熄灭LED
digitalWrite(LED_PIN, LOW);
delay(1000); // 延时1秒
}
4. 基本编程入门
4.1 GPIO编程
ESP32-C6的GPIO编程非常简单,可以使用gpio_set_direction
和gpio_set_level
函数来控制GPIO引脚的状态。
代码示例:控制GPIO引脚
以下是一个使用Espressif IDF控制GPIO引脚的代码示例。
#include "driver/gpio.h"
#include "esp_log.h"
// 定义LED引脚
#define LED_PIN 2
// 日志标签
static const char *TAG = "GPIO_Example";
void app_main(void) {
// 初始化LED引脚为输出模式
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
while (1) {
// 点亮LED
gpio_set_level(LED_PIN, 1);
ESP_LOGI(TAG, "LED is ON");
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
// 熄灭LED
gpio_set_level(LED_PIN, 0);
ESP_LOGI(TAG, "LED is OFF");
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
}
}
4.2 中断编程
ESP32-C6支持GPIO中断,可以通过gpio_install_isr_service
和gpio_isr_handler_add
函数来注册中断处理函数。
代码示例:GPIO中断
以下是一个使用Espressif IDF注册GPIO中断的代码示例。
#include "driver/gpio.h"
#include "esp_log.h"
// 定义按钮引脚
#define BUTTON_PIN 0
// 日志标签
static const char *TAG = "Interrupt_Example";
// 中断处理函数
void IRAM_ATTR gpio_isr_handler(void* arg) {
int pin = (int) arg;
ESP_LOGI(TAG, "Interrupt on pin %d", pin);
}
void app_main(void) {
// 初始化按钮引脚为输入模式
gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
// 安装GPIO中断服务
gpio_install_isr_service(0);
// 注册中断处理函数
gpio_isr_handler_add(BUTTON_PIN, gpio_isr_handler, (void*) BUTTON_PIN);
ESP_LOGI(TAG, "GPIO Interrupt Example Started");
while (1) {
vTaskDelay(1000 / portTICK_PERIOD_MS); // 主循环延时1秒
}
}
4.3 ADC编程
ESP32-C6的ADC可以用于采集模拟信号,通过adc1_get_raw
函数可以读取ADC通道的值。
代码示例:读取ADC值
以下是一个使用Espressif IDF读取ADC值的代码示例。
#include "driver/adc.h"
#include "esp_log.h"
// 定义ADC通道
#define ADC_CHANNEL 0
// 日志标签
static const char *TAG = "ADC_Example";
void app_main(void) {
// 配置ADC
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_0);
while (1) {
// 读取ADC值
int adc_value = adc1_get_raw(ADC_CHANNEL);
ESP_LOGI(TAG, "ADC Value: %d", adc_value);
vTaskDelay(1000 / portTICK_PERIOD_MS); // 主循环延时1秒
}
}
5. Wi-Fi连接与配置
5.1 Wi-Fi模块初始化
ESP32-C6的Wi-Fi模块初始化需要调用esp_wifi_init
函数,并配置Wi-Fi模式(STA、AP或STA+AP)。
代码示例:Wi-Fi模块初始化
以下是一个使用Espressif IDF初始化Wi-Fi模块的代码示例。
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "WiFi_Example";
// Wi-Fi事件处理函数
static void wifi_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) {
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP: %s", ip4addr_ntoa(&event->ip_info.ip));
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Disconnected, trying to reconnect...");
esp_wifi_connect();
}
}
void app_main(void) {
// 初始化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);
// 配置Wi-Fi事件处理
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_init();
esp_netif_create_default_wifi_sta();
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, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL));
// 配置Wi-Fi
wifi_config_t wifi_config = {
.sta = {
.ssid = "your_SSID",
.password = "your_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_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wi-Fi Initialization Done");
}
5.2 连接Wi-Fi
连接Wi-Fi需要调用esp_wifi_connect
函数,并处理连接成功或失败的事件。
代码示例:连接Wi-Fi
以下是一个使用Espressif IDF连接Wi-Fi的代码示例。
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "WiFi_Example";
// Wi-Fi事件处理函数
static void wifi_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) {
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP: %s", ip4addr_ntoa(&event->ip_info.ip));
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Disconnected, trying to reconnect...");
esp_wifi_connect();
}
}
void app_main(void) {
// 初始化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);
// 配置Wi-Fi事件处理
ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_init();
esp_netif_create_default_wifi_sta();
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, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL));
// 配置Wi-Fi
wifi_config_t wifi_config = {
.sta = {
.ssid = "your_SSID",
.password = "your_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_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "Wi-Fi Initialization Done");
}
6. MQTT协议实现
6.1 MQTT简介
MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,适用于物联网设备之间的通信。ESP32-C6可以通过MQTT协议与云平台或其他设备进行数据交换。
6.2 使用MQTT库
Espressif IDF提供了MQTT库,可以方便地实现MQTT协议。以下是使用MQTT库的步骤:
-
安装MQTT库:在
make menuconfig
中启用MQTT库。 -
初始化MQTT客户端:使用
esp_mqtt_client_init
函数初始化MQTT客户端。 -
连接MQTT服务器:使用
esp_mqtt_client_start
函数连接MQTT服务器。 -
订阅和发布消息:使用
esp_mqtt_client_subscribe
和esp_mqtt_client_publish
函数订阅和发布消息。
代码示例:MQTT客户端
以下是一个使用Espressif IDF实现MQTT客户端的代码示例。
#include "esp_mqtt.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "MQTT_Example";
// MQTT事件处理函数
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT Connected");
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT Disconnected");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT Subscribed");
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT Unsubscribed");
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT Published");
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT Data Received");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
void mqtt_app_start(void) {
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = "mqtt://broker.hivemq.com",
.event_handle = mqtt_event_handler_cb,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_start(client);
}
void app_main(void) {
// 初始化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);
// 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = "your_SSID",
.password = "your_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_ERROR_CHECK(esp_wifi_start());
// 启动MQTT客户端
mqtt_app_start();
ESP_LOGI(TAG, "MQTT Client Started");
}
7. HTTP请求与响应
7.1 HTTP客户端
ESP32-C6可以使用HTTP客户端库发送HTTP请求并接收响应。Espressif IDF提供了强大的HTTP客户端库,可以方便地实现与Web服务器的通信。以下是使用HTTP客户端库的步骤:
-
安装HTTP库:在
make menuconfig
中启用HTTP客户端库。 -
初始化HTTP客户端:使用
http_client_init
函数初始化HTTP客户端。 -
发送HTTP请求:使用
http_client_perform
函数发送HTTP请求。 -
处理HTTP响应:通过回调函数处理HTTP响应数据。
代码示例:HTTP客户端
以下是一个使用Espressif IDF实现HTTP客户端的代码示例,该示例将发送一个GET请求并打印响应内容。
#include "esp_http_client.h"
#include "esp_log.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "HTTP_Client_Example";
// HTTP事件处理函数
static esp_err_t _http_event_handle(esp_http_client_event_t *event) {
switch(event->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", event->header_key, event->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", event->data_len);
if (event->data_len) {
// 打印响应数据
for (int i = 0; i < event->data_len; i++) {
putchar(event->data[i]);
}
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
void http_get_request(const char *url) {
esp_http_client_config_t config = {
.url = url,
.event_handler = _http_event_handle,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_http_client_perform(client);
esp_http_client_cleanup(client);
}
void app_main(void) {
// 初始化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);
// 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = "your_SSID",
.password = "your_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_ERROR_CHECK(esp_wifi_start());
// 连接Wi-Fi后发送HTTP请求
while (1) {
if (esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info) == ESP_OK) {
if (ip4_addr_is_any_val(&ip_info->ip)) {
ESP_LOGI(TAG, "Waiting for IP address...");
} else {
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&ip_info->ip));
http_get_request("http://httpbin.org/get");
break;
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // 主循环延时1秒
}
ESP_LOGI(TAG, "HTTP Client Example Done");
}
7.2 HTTP服务器
除了作为HTTP客户端,ESP32-C6还可以作为HTTP服务器,接收来自客户端的HTTP请求并响应。以下是使用HTTP服务器库的步骤:
-
安装HTTP库:在
make menuconfig
中启用HTTP服务器库。 -
初始化HTTP服务器:使用
http_server_init
函数初始化HTTP服务器。 -
注册HTTP处理函数:通过
httpd_register_uri_handler
函数注册处理特定URI的回调函数。 -
启动HTTP服务器:使用
httpd_start
函数启动HTTP服务器。
代码示例:HTTP服务器
以下是一个使用Espressif IDF实现HTTP服务器的代码示例,该示例将处理一个简单的GET请求并返回响应。
#include "esp_http_server.h"
#include "esp_log.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "HTTP_Server_Example";
// 处理GET请求的回调函数
static esp_err_t get_handler(httpd_req_t *req) {
char * resp_str = "Hello, World!";
httpd_resp_send_str(req, resp_str);
return ESP_OK;
}
// 注册HTTP处理函数
static const httpd_uri_t uri_get = {
.uri = "/hello",
.method = HTTP_GET,
.handler = get_handler,
.user_ctx = NULL
};
// 初始化HTTP服务器
static httpd_handle_t start_webserver(void) {
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
// 启动HTTP服务器
if (httpd_start(&server, &config) == ESP_OK) {
// 注册URI处理函数
httpd_register_uri_handler(server, &uri_get);
ESP_LOGI(TAG, "HTTP Server Started");
} else {
ESP_LOGI(TAG, "Error starting HTTP server");
}
return server;
}
void app_main(void) {
// 初始化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);
// 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = "your_SSID",
.password = "your_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_ERROR_CHECK(esp_wifi_start());
// 连接Wi-Fi后启动HTTP服务器
while (1) {
if (esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info) == ESP_OK) {
if (ip4_addr_is_any_val(&ip_info->ip)) {
ESP_LOGI(TAG, "Waiting for IP address...");
} else {
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&ip_info->ip));
start_webserver();
break;
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // 主循环延时1秒
}
ESP_LOGI(TAG, "HTTP Server Example Done");
}
8. 传感器数据采集
8.1 模拟传感器
ESP32-C6的ADC可以用于采集模拟传感器的数据。通过配置ADC通道和读取ADC值,可以实现对模拟传感器的读取。
代码示例:读取模拟传感器
以下是一个使用Espressif IDF读取模拟传感器数据的代码示例。
#include "driver/adc.h"
#include "esp_log.h"
// 定义ADC通道
#define ADC_CHANNEL 0
// 日志标签
static const char *TAG = "Sensor_Example";
void app_main(void) {
// 配置ADC
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_0);
while (1) {
// 读取ADC值
int adc_value = adc1_get_raw(ADC_CHANNEL);
ESP_LOGI(TAG, "ADC Value: %d", adc_value);
vTaskDelay(1000 / portTICK_PERIOD_MS); // 主循环延时1秒
}
}
8.2 数字传感器
数字传感器通过GPIO接口与ESP32-C6进行通信。常见的数字传感器包括温度传感器、湿度传感器、加速度传感器等。
代码示例:读取DHT11温度湿度传感器
以下是一个使用Espressif IDF读取DHT11温度湿度传感器的数据的代码示例。
#include "driver/gpio.h"
#include "sensor/dht.h"
#include "esp_log.h"
// 定义DHT11引脚
#define DHT_PIN 13
// 日志标签
static const char *TAG = "DHT11_Example";
void app_main(void) {
// 初始化DHT11
dht sensor;
dht_init(&sensor, DHT_PIN, DHT11);
while (1) {
// 读取温度湿度
float temperature;
float humidity;
dht_read(&sensor, &temperature, &humidity);
if (sensor.last_read_status == DHT_READ_OK) {
ESP_LOGI(TAG, "Temperature: %.1f °C, Humidity: %.1f %%", temperature, humidity);
} else {
ESP_LOGE(TAG, "DHT11 read failed: %d", sensor.last_read_status);
}
vTaskDelay(2000 / portTICK_PERIOD_MS); // 主循环延时2秒
}
}
9. 低功耗模式
9.1 低功耗模式概述
ESP32-C6支持多种低功耗模式,包括深度睡眠、轻度睡眠和定时唤醒。这些模式可以显著降低设备的功耗,延长电池寿命。
9.2 深度睡眠模式
深度睡眠模式可以将设备的功耗降至极低,适用于长时间不活动的场景。可以通过esp_sleep_enable_timer_wakeup
函数设置定时唤醒。
代码示例:深度睡眠模式
以下是一个使用Espressif IDF实现深度睡眠模式的代码示例。
#include "esp_sleep.h"
#include "esp_log.h"
// 日志标签
static const char *TAG = "Low_Power_Example";
void app_main(void) {
// 设置定时唤醒,每10秒唤醒一次
esp_sleep_enable_timer_wakeup(10 * 1000000); // 10秒 = 10 * 1000000 微秒
// 打印唤醒原因
const esp_sleep_wakeup_cause_t wakeup_reason = esp_sleep_get_wakeup_cause();
switch (wakeup_reason) {
case ESP_SLEEP_WAKEUP_TIMER:
ESP_LOGI(TAG, "Wakeup caused by timer");
break;
case ESP_SLEEP_WAKEUP_GPIO:
ESP_LOGI(TAG, "Wakeup caused by GPIO");
break;
case ESP_SLEEP_WAKEUP_UART:
ESP_LOGI(TAG, "Wakeup caused by UART");
break;
default:
ESP_LOGI(TAG, "Wakeup was not caused by timer/GPIO/UART");
break;
}
// 进入深度睡眠
esp_deep_sleep_start();
}
10. 安全与加密
10.1 安全启动
安全启动确保ESP32-C6设备在启动时加载经过签名的固件,防止恶意软件的加载。可以通过esp_secure_boot_burn_key
函数烧录安全启动密钥。
10.2 加密算法
ESP32-C6支持多种加密算法,包括AES、SHA、RSA等。这些算法可以用于保护数据的安全传输和存储。
代码示例:AES加密
以下是一个使用Espressif IDF实现AES加密的代码示例。
#include "esp_log.h"
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_secure_boot.h"
#include "esp_aes.h"
// 日志标签
static const char *TAG = "Encryption_Example";
void app_main(void) {
// 定义密钥和明文
const uint8_t key[32] = "0123456789abcdef0123456789abcdef";
const uint8_t plaintext[16] = "0123456789abcdef";
uint8_t ciphertext[16];
// 初始化AES加密上下文
esp_aes_context ctx;
esp_aes_init(&ctx);
esp_aes_setkey(&ctx, key, 256);
// 加密数据
esp_aes_crypt_ecb(&ctx, ESP_AES_MODE_ENCRYPT, plaintext, ciphertext);
ESP_LOGI(TAG, "Ciphertext: ");
for (int i = 0; i < 16; i++) {
printf("%02x ", ciphertext[i]);
}
printf("\n");
// 解密数据
esp_aes_crypt_ecb(&ctx, ESP_AES_MODE_DECRYPT, ciphertext, plaintext);
ESP_LOGI(TAG, "Decrypted Text: ");
for (int i = 0; i < 16; i++) {
printf("%c", plaintext[i]);
}
printf("\n");
// 释放AES加密上下文
esp_aes_free(&ctx);
}
11. 实时操作系统(RTOS)应用
11.1 RTOS概述
ESP32-C6内置了FreeRTOS,可以用于实现多任务和实时调度。通过创建任务和管理任务,可以有效地管理设备的资源和任务。
11.2 创建任务
可以通过xTaskCreate
函数创建任务,每个任务可以独立运行,处理特定的任务。
代码示例:创建任务
以下是一个使用FreeRTOS创建任务的代码示例。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
// 日志标签
static const char *TAG = "RTOS_Example";
// 任务1
void task1(void *pvParameter) {
while (1) {
ESP_LOGI(TAG, "Task 1 is running");
vTaskDelay(1000 / portTICK_PERIOD_MS); // 延时1秒
}
}
// 任务2
void task2(void *pvParameter) {
while (1) {
ESP_LOGI(TAG, "Task 2 is running");
vTaskDelay(2000 / portTICK_PERIOD_MS); // 延时2秒
}
}
void app_main(void) {
// 创建任务1
xTaskCreate(task1, "Task 1", 2048, NULL, 1, NULL);
// 创建任务2
xTaskCreate(task2, "Task 2", 2048, NULL, 2, NULL);
ESP_LOGI(TAG, "Tasks Created");
}
12. 蓝牙功能
12.1 蓝牙模块初始化
ESP32-C6支持低功耗蓝牙(BLE),可以通过esp_bt_controller_init
和esp_ble_gap_register_callback
函数初始化蓝牙模块并注册事件回调。以下是一个使用Espressif IDF初始化蓝牙模块的代码示例。
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_gap.h"
#include "esp_log.h"
// 日志标签
static const char *TAG = "Bluetooth_Example";
// 蓝牙事件处理函数
static void gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) {
switch (event) {
case ESP_BT_GAP_EVENT_DISC_STATE_CHANGED:
ESP_LOGI(TAG, "Discovery state changed: %d", param->disc_state_changed.state);
break;
case ESP_BT_GAP_EVENT_DEV_DISC_RES:
ESP_LOGI(TAG, "Device discovered: %s", param->dev_disc_res.remote_bda);
break;
default:
break;
}
}
void app_main(void) {
// 初始化蓝牙控制器
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_CONFIG_DEFAULT();
esp_bt_controller_init(&bt_cfg);
// 注册蓝牙事件处理函数
esp_bt_gap_register_callback(gap_event_handler);
// 启动蓝牙控制器
esp_bt_controller_enable(ESP_BT_MODE_BLE);
ESP_LOGI(TAG, "Bluetooth Initialization Done");
}
12.2 蓝牙服务
可以通过esp_ble_gatts_app_register
函数注册蓝牙服务,并通过esp_ble_gatts_start_service
函数启动服务。以下是一个使用Espressif IDF注册和启动蓝牙服务的代码示例。
#include "esp_bt.h"
#include "esp_bt_main.h"
#include "esp_bt_gap.h"
#include "esp_ble_gatts.h"
#include "esp_log.h"
// 日志标签
static const char *TAG = "Bluetooth_Service_Example";
// 蓝牙服务UUID
#define SVC_UUID 0x00FF
#define CHAR_UUID 0x0001
// 蓝牙服务属性
static esp_gatt_char_prop_t char_prop = {0}; // 读写属性
// 蓝牙服务事件处理函数
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatts_cb_param_t *param) {
switch (event) {
case ESP_GATTS_REG_EVT:
esp_ble_gatts_create_service(param->reg.service_handle, &service_uuid, 1);
break;
case ESP_GATTS_CREATE_EVT:
esp_ble_gatts_add_char(param->create.service_handle, &char_uuid, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE, &char_prop, NULL, NULL);
break;
case ESP_GATTS_ADD_CHAR_EVT:
esp_ble_gatts_start_service(param->add_char.service_handle);
break;
case ESP_GATTS_START_SERVICE_EVT:
ESP_LOGI(TAG, "Service started");
break;
case ESP_GATTS_CONNECT_EVT:
ESP_LOGI(TAG, "Client connected");
break;
case ESP_GATTS_DISCONNECT_EVT:
ESP_LOGI(TAG, "Client disconnected");
break;
case ESP_GATTS_READ_EVT:
ESP_LOGI(TAG, "Read request received");
break;
case ESP_GATTS_WRITE_EVT:
ESP_LOGI(TAG, "Write request received");
break;
default:
break;
}
}
void app_main(void) {
// 初始化蓝牙控制器
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_CONFIG_DEFAULT();
esp_bt_controller_init(&bt_cfg);
// 注册蓝牙事件处理函数
esp_bt_gap_register_callback(gap_event_handler);
// 启动蓝牙控制器
esp_bt_controller_enable(ESP_BT_MODE_BLE);
// 注册GATT服务应用
esp_ble_gatts_register_app(&gatts_event_handler, &gatts_app_id, &gatts_app_reg_id);
ESP_LOGI(TAG, "Bluetooth Initialization Done");
}
13. 语音识别与处理
13.1 语音识别概述
ESP32-C6可以通过I2S接口与麦克风等音频设备进行通信,实现语音识别和处理功能。Espressif提供了多种语音识别和处理的库,支持简单的语音命令识别和音频处理。
13.2 语音识别库
Espressif的语音识别库支持多种语音识别算法,可以通过esp_speech_recognizer_init
函数初始化语音识别器,并通过esp_speech_recognizer_start
函数启动识别。
代码示例:语音识别
以下是一个使用Espressif IDF实现语音识别的代码示例。
#include "esp_speech_recognizer.h"
#include "driver/i2s.h"
#include "esp_log.h"
// 日志标签
static const char *TAG = "Speech_Recognition_Example";
// I2S配置
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
.sample_rate = 16000,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = 1024,
.use_apll = false,
.tx_desc_auto_clear = true,
.fixed_mclk = 0,
};
// I2S引脚配置
i2s_pin_config_t pin_config = {
.bck_io_num = 26,
.ws_io_num = 25,
.data_out_num = I2S_PIN_NO_CHANGE,
.data_in_num = 22
};
// 语音识别回调函数
static void speech_recognition_callback(const char *command) {
ESP_LOGI(TAG, "Recognized command: %s", command);
}
void app_main(void) {
// 初始化I2S
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
// 初始化语音识别器
esp_speech_recognizer_init();
// 注册语音识别回调函数
esp_speech_recognizer_register_callback(speech_recognition_callback);
// 启动语音识别
esp_speech_recognizer_start();
ESP_LOGI(TAG, "Speech Recognition Started");
}
14. 设备管理与远程控制
14.1 设备管理概述
设备管理是指对ESP32-C6设备的状态进行监控和管理,包括固件更新、设备配置、状态报告等。Espressif提供了esp_ota
库来实现固件的空中更新(OTA)。
14.2 OTA固件更新
通过OTA(Over-The-Air)更新,可以在设备运行时通过网络更新固件。以下是使用Espressif IDF实现OTA固件更新的步骤:
-
配置OTA:在
make menuconfig
中启用OTA功能。 -
初始化OTA:使用
esp_ota_begin
函数初始化OTA更新。 -
下载固件:通过HTTP或MQTT等协议下载新的固件。
-
安装固件:使用
esp_ota_end
函数安装新的固件。
代码示例:OTA固件更新
以下是一个使用Espressif IDF实现OTA固件更新的代码示例。
#include "esp_ota_ops.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "OTA_Example";
// HTTP事件处理函数
static esp_err_t _http_event_handle(esp_http_client_event_t *event) {
switch(event->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", event->header_key, event->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", event->data_len);
if (event->data_len) {
// 将数据写入OTA分区
esp_err_t ret = esp_ota_write(ota_handle, event->data, event->data_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Error writing to OTA partition");
return ret;
}
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
void ota_update(void) {
const char *ota_url = "http://your_ota_server/your_firmware.bin";
// 初始化OTA
esp_err_t ret = esp_ota_begin(&update_partition, OTA_SIZE_UNKNOWN, &ota_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed, error=%d", ret);
return;
}
ESP_LOGI(TAG, "esp_ota_begin succeeded");
// 配置HTTP客户端
esp_http_client_config_t config = {
.url = ota_url,
.event_handler = _http_event_handle,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
// 发送HTTP请求
ret = esp_http_client_perform(client);
if (ret == ESP_OK) {
// 完成OTA
ret = esp_ota_end(ota_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_end failed, error=%d", ret);
return;
}
ESP_LOGI(TAG, "esp_ota_end succeeded");
// 重启设备
esp_restart();
} else {
ESP_LOGE(TAG, "esp_http_client_perform failed, error=%d", ret);
}
// 清理HTTP客户端
esp_http_client_cleanup(client);
}
void app_main(void) {
// 初始化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);
// 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));
wifi_config_t wifi_config = {
.sta = {
.ssid = "your_SSID",
.password = "your_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_ERROR_CHECK(esp_wifi_start());
// 连接Wi-Fi后进行OTA更新
while (1) {
if (esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &ip_info) == ESP_OK) {
if (ip4_addr_is_any_val(&ip_info->ip)) {
ESP_LOGI(TAG, "Waiting for IP address...");
} else {
ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&ip_info->ip));
ota_update();
break;
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // 主循环延时1秒
}
ESP_LOGI(TAG, "OTA Update Example Done");
}
15. 项目实例:智能家居网关
15.1 项目概述
智能家居网关是一个典型的ESP32-C6物联网应用。它通过Wi-Fi连接到互联网,采集各种传感器数据,并通过MQTT协议将数据发送到云平台。同时,网关还可以接收来自云平台的控制指令,控制家庭中的各种智能设备。
15.2 系统架构
Wi-Fi模块:连接到互联网,获取IP地址。
传感器模块:采集温度、湿度、光照等数据。
MQTT客户端:通过MQTT协议与云平台通信,发送数据和接收控制指令。
控制模块:根据云平台的指令控制智能设备,如灯光、窗帘等。
低功耗模式:在没有数据传输时进入低功耗模式,节省能源。
15.3 代码实现
以下是一个完整的智能家居网关项目的代码示例。
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_mqtt.h"
#include "driver/adc.h"
#include "sensor/dht.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_sleep.h"
#include "nvs_flash.h"
// 日志标签
static const char *TAG = "Smart_Home_Gateway";
// Wi-Fi配置
const char *ssid = "your_SSID";
const char *password = "your_PASSWORD";
// MQTT配置
const char *mqtt_broker = "mqtt://broker.hivemq.com";
const char *mqtt_topic = "smart_home/gateway";
// DHT11引脚
#define DHT_PIN 13
// ADC通道
#define ADC_CHANNEL 0
// GPIO引脚
#define LIGHT_PIN 2
// MQTT事件处理函数
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event) {
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT Connected");
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT Disconnected");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT Subscribed");
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT Unsubscribed");
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT Published");
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT Data Received");
// 处理接收到的控制指令
if (strcmp(event->topic, mqtt_topic) == 0) {
if (strcmp(event->data, "light_on") == 0) {
gpio_set_level(LIGHT_PIN, 1);
ESP_LOGI(TAG, "Light ON");
} else if (strcmp(event->data, "light_off") == 0) {
gpio_set_level(LIGHT_PIN, 0);
ESP_LOGI(TAG, "Light OFF");
}
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
return ESP_OK;
}
void mqtt_app_start(void) {
const esp_mqtt_client_config_t mqtt_cfg = {
.uri = mqtt_broker,
.event_handle = mqtt_event_handler_cb,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
esp_mqtt_client_start(client);
}
// Wi-Fi事件处理函数
static void wifi_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) {
esp_wifi_connect();
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
ESP_LOGI(TAG, "Got IP: %s", ip4addr_ntoa(&event->ip_info.ip));
mqtt_app_start();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
ESP_LOGI(TAG, "Disconnected, trying to reconnect...");
esp_wifi_connect();
}
}
// 读取传感器数据并发布到MQTT
void read_sensors_and_publish(void *pvParameters) {
dht sensor;
dht_init(&sensor, DHT_PIN, DHT11);
while (1) {
// 读取温度湿度
float temperature;
float humidity;
dht_read(&sensor, &temperature, &humidity);
if (sensor.last_read_status == DHT_READ_OK) {
// 读取ADC值
int adc_value = adc1_get_raw(ADC_CHANNEL);
// 构建消息
char message[100];
sprintf(message, "{\"temperature\": %.1f, \"humidity\": %.1f, \"light\": %d}", temperature, humidity, adc_value);
// 发布消息到MQTT
esp_mqtt_client_publish(mqtt_client, mqtt_topic, message, 0, 1, 0);
ESP_LOGI(TAG, "Sensor Data Published: %s", message);
} else {
ESP_LOGE(TAG, "DHT11 read failed: %d", sensor.last_read_status);
}
vTaskDelay(10000 / portTICK_PERIOD_MS); // 主循环延时10秒
}
}
void app_main(void) {
// 初始化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);
// 初始化GPIO
gpio_set_direction(LIGHT_PIN, GPIO_MODE_OUTPUT);
// 初始化ADC
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(ADC_CHANNEL, ADC_ATTEN_DB_0);
// 初始化Wi-Fi
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA
作者:kkchenkx