image.png

或许连接操作也可由用户发起, 全部保存的一个变量记录当前设备是否联网即可。

#include "esp_err.h"
#include "esp_event.h"
#include "esp_http_server.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_wifi.h"
#include "esp_wifi_default.h"
#include "esp_wifi_types_generic.h"
#include "freertos/FreeRTOS.h"
#include "freertos/idf_additions.h"
#include "freertos/projdefs.h"
#include "freertos/stream_buffer.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include "portmacro.h"
#include <assert.h>
#include <stdint.h>
#include <stdio.h>

static const char *TAG = "scan";
// 定义事件组, 这里用于阻塞联网后的操作,也就是等待联网后才开始干活
static EventGroupHandle_t s_wifi_event_group;
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
// 记录重试
static int s_retry_num = 0;

const char hello[] = "<h1>Hello, world</h1><p>ESP32C3 is speaking</p>";

static void 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 == WIFI_EVENT &&
             event_id == WIFI_EVENT_STA_DISCONNECTED) {
    if (s_retry_num < 5) {
      esp_wifi_connect();
      s_retry_num += 1;
      ESP_LOGI("STA", "retry connect to the AP");
    } else {
      // 超时
      xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
    }
  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_CONNECTED) {
    wifi_event_sta_connected_t *event =
        (wifi_event_sta_connected_t *)event_data;
    ESP_LOGI("STA", "connected: %s", event->ssid);
  } 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:" IPSTR, IP2STR(&event->ip_info.ip));
    s_retry_num = 0;
    // 拿到 IP, 应该可以冲浪了
    xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
  }
}

/**
 * @brief init wifi with default
 */
void wifiInit() {
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());

  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
}

/**
 * @brief Init wifi sta with default config
 *
 * register event_handler,
 */
void initSta() {
  // 先安装事件
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL, NULL));
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL, NULL));

  // Initialize default station as network interface instance (esp-netif)
  esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
  assert(sta_netif);

  // Initialize and start WiFi
  wifi_config_t wifi_config = {
      .sta =
          {
              .ssid = "Man_2.4GHz",
              .password = "liang114514",
          },
  };
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));

  ESP_LOGI("STA", "wifi_init_sta finished.");
}

/**
 * @brief Connet wifi
 */
void connectWifi() {
  // 先创建事件组
  s_wifi_event_group = xEventGroupCreate();
  ESP_ERROR_CHECK(esp_wifi_start());
  // 无限等待, 返回
  EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
                                         WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
                                         pdFALSE, pdFALSE, portMAX_DELAY);

  if (bits & WIFI_CONNECTED_BIT) {
    ESP_LOGI("STA", "connected to ap");
  } else if (bits & WIFI_FAIL_BIT) {
    ESP_LOGI("STA", "Failed to connect to ap");
  } else {
    ESP_LOGE("Error", "UNEXPECTED EVENT");
  }
  vEventGroupDelete(s_wifi_event_group);
}

// GET /
static esp_err_t get_root(httpd_req_t *req) {
  return httpd_send(req, hello, sizeof(hello));
}
const httpd_uri_t uri_get = {
    .uri = "/", .method = HTTP_GET, .handler = get_root, .user_ctx = NULL};

httpd_handle_t setup_server() {
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  httpd_handle_t server = NULL;

  if (httpd_start(&server, &config) == ESP_OK) {
    // 绑定http请求处理函数,httpd自动解析path和method匹配处理
    httpd_register_uri_handler(server, &uri_get);
  }
  return server;
}

void app_main() {
  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);
  wifiInit();
  initSta();
  connectWifi();
  setup_server();
}

本省 app_main 就是个线程, 其中 我们启用了事件轮训和 httpd_start 这里会创建其他的线程, 看门狗不会饿死的,所以app_main可以直接结束,这就是事件驱动。