文章
博客 网店

 ESP8266 IDF开发6-智能配网并掉电自动连接


  SmartConfig实现了将WIFI的SSID和密码无线发送到8266设备,对于一个未连接ap的8266设备来说,他所能接收到的数据包应该都是加密无法识别的,但最外层的mac头
是未加密的,所以可以利用这个MAC头中的可使用的数据位将wifi配置发送出去,对于发送方来说一定是广播或向任意一个目标发送MAC头部加载配置信息的数据包,
而需要配网的8266设备需要进入一个监听mac头状态。这是大体原理,更新详细的利用了哪个数据位,如何编码等在网络上很容易搜索到,这里忽略。
根据这个原理不同厂家研发了不同的协议标准,本例使用了最常用的AirKiss方式,需要在示例的menuconfig中选择。

  NVS是ESP_IDF提供的flash当数据存储的一组接口函数,idf在flash内划定了一个区间作为数据存储(eeprom用途),这个在分区表里可以找到,例如
在menuconfig中分区配置中选择了Single factory app, no OTA选项的情况下分区表如下:

boot: ## Label            Usage           Type ST  Offset   Length
boot:  0 nvs              WiFi data        01  02  00009000 00006000
boot:  1 phy_init         RF data          01  01  0000f000 00001000
boot:  2 factory          factory app      00  00  00010000 000f0000

就是将0x9000处的24k字节留给了NVS。就像一个简易的文件系统一样,NVS使用数据区和键值索引和读写数据。

以下为本例源程序,首先是定义的全局变量


static const char * NVS_CUSTOMER="my config";//nvs数据区
static const char * DATA1 = "wifi data";//存储wifi配置的nvs键值
static const char *payload = "Message from ESP8266 ";//测试socket发送内容
static uint32_t g_RetryCount=0;//连接尝试次数
volatile uint32_t g_LedToggleCyc=1000;//LED闪烁周期(ms)
static uint8_t g_WifiConfigStatus=0;//nvs中是否有配置标志


 NVS存取部分:

//读WIFI配网信息
static int LoadWifiConfig(wifi_config_t *cfg)
{
  nvs_handle handle;
  uint32_t len=sizeof(wifi_config_t);
  ESP_LOGI(TAG,"LEN:%d",len);
  
  memset(cfg,0,sizeof(wifi_config_t));
  
  if( nvs_open(NVS_CUSTOMER, NVS_READWRITE, &handle) != ESP_OK )
    return 1;
    
  if( nvs_get_blob(handle, DATA1, cfg, &len) != ESP_OK)
  {
    nvs_close(handle);
    return 2;
  }  
  
  nvs_close(handle);
  return 0;
}

static void ClearWifiConfig(void)
{
  nvs_handle handle;
  if( nvs_open(NVS_CUSTOMER, NVS_READWRITE, &handle) != ESP_OK )
    return ;
    
  if(nvs_erase_key(handle,DATA1)== ESP_OK)
    nvs_commit(handle);
    
  nvs_close(handle);
}

//保存wifi配网信息
static int SaveWifiConfig(wifi_config_t *cfg)
{
  nvs_handle handle;
  if(nvs_open(NVS_CUSTOMER,NVS_READWRITE,&handle)!=ESP_OK)return 1;
  if(nvs_set_blob(handle,DATA1,cfg,sizeof(wifi_config_t))!=ESP_OK)
  {
    nvs_close(handle);
    return 2;
  }
  ESP_ERROR_CHECK( nvs_commit(handle));
  nvs_close(handle);
  return 0;
}


 SmartConfig部分

static void event_handler(void* arg, esp_event_base_t event_base,
                          int32_t event_id, void* event_data)
{
    wifi_config_t wifi_config;
  
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 
    {
      if(g_WifiConfigStatus==0)//如果没有存储的配置
      {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);//开始配网
        do{g_LedToggleCyc=500;}while(g_LedToggleCyc!=500);//0.5秒闪烁
      }  
      else//已有保存的配置
      {
        g_RetryCount=0;
        ESP_ERROR_CHECK(esp_wifi_connect());
        do{g_LedToggleCyc=1000;}while(g_LedToggleCyc!=1000);//1秒闪烁
        ESP_LOGI(TAG, "Connecting ...");
      }  
    } 
    else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED)
    {
      if(g_RetryCount++<5)//尝试5次
      {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
      }
      else
      {
        g_RetryCount=0;
         xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);//开始配网
      }
    } 
    else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 
    {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } 
    else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) 
    {
        ESP_LOGI(TAG, "Scan done");
    } 
    else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) 
    {
        ESP_LOGI(TAG, "Found channel");
    } 
    else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) 
    {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t* evt = (smartconfig_event_got_ssid_pswd_t*)event_data;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;

        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:%s", rvd_data);
        }
        
        ClearWifiConfig();
        SaveWifiConfig(&wifi_config);

        ESP_ERROR_CHECK(esp_wifi_disconnect());
        ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
        ESP_ERROR_CHECK(esp_wifi_connect());
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

//智能配置任务
static void smartconfig_example_task(void* parm)
{
    EventBits_t uxBits;
    
    ESP_ERROR_CHECK(esp_smartconfig_set_type(EXAMPLE_ESP_SMARTCOFNIG_TYPE));
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_smartconfig_start(&cfg));

    //等待连接
    while (1) 
    {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);

        if (uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }

        if (uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();

            vTaskDelete(NULL);
        }
    }
}


连接后测试网络部分


//用户通信任务
static void smartconfig_usr_task(void *parm)
{
  EventBits_t uxBits;
  int addr_family;
  int ip_protocol;
  char addr_str[128]; 
  
  //等待wifi连接成功
  while(1)
  {
    uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
    if(uxBits & CONNECTED_BIT)
      break;
  }
  
  do{g_LedToggleCyc=2000;}while(g_LedToggleCyc!=2000);//wifi连接完成,2秒闪烁周期

  struct sockaddr_in destAddr;
  destAddr.sin_addr.s_addr = inet_addr("192.168.0.102");
  destAddr.sin_family = AF_INET;
  destAddr.sin_port = htons(8000);
  addr_family = AF_INET;
  ip_protocol = IPPROTO_IP;
  inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);

  int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
  if (sock < 0) {
      ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
      return;
  }
  ESP_LOGI(TAG, "Socket created"); 
  
  int err;
  while(1)
  {
    vTaskDelay(10000 / portTICK_RATE_MS);//10秒发送一次
    err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&destAddr, sizeof(destAddr));
    if (err < 0) {
        ESP_LOGE(TAG, "Error occured during sending: errno %d", errno);
        break;
    }
    ESP_LOGI(TAG, "Message sent");
  }
  ESP_LOGI(TAG,"Task exit");
  vTaskDelete(NULL);  
}


主程序部分


static void initialise_wifi(void)
{
    tcpip_adapter_init();
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_event_loop_create_default());

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    
    wifi_config_t wifi_config;
    if(!LoadWifiConfig(&wifi_config))//从nvs装入配置
    {
      ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
      g_WifiConfigStatus=1;
    }  
    
    ESP_ERROR_CHECK(esp_wifi_start());
}

void io_init(void)
{
  gpio_config_t io_conf;
  io_conf.intr_type = GPIO_INTR_DISABLE;
  io_conf.mode = GPIO_MODE_OUTPUT;
  io_conf.pin_bit_mask = (1UL<   io_conf.pull_down_en = 0;
  io_conf.pull_up_en = 0;
  gpio_config(&io_conf);

  gpio_set_level(GPIO_LED_PIN, 0);//初始值
}

//发光管闪烁控制
void led_task(void *parm)
{
  uint32_t lv;
  while(1)
  {
    if((g_LedToggleCyc < 100)||(g_LedToggleCyc>10000))
      g_LedToggleCyc=100;
      
    vTaskDelay(g_LedToggleCyc / portTICK_RATE_MS);//延时
    
    lv=gpio_get_level(GPIO_LED_PIN);
    lv=lv?0:1;
    gpio_set_level(GPIO_LED_PIN,lv);
  }
}

void app_main()
{
  io_init();
  ESP_ERROR_CHECK(nvs_flash_init());
  initialise_wifi();
  xTaskCreate(smartconfig_usr_task, "smartconfig_usr_task", 4096, NULL, 3, NULL);//用户通信任务
  xTaskCreate(led_task, "smartconfig_led_task", 4096, NULL, 3, NULL);//发光管控制
}


配置网络可以使用微信扫描下图二维码按提示操作


测试图:





芯艺工作室    蒙ICP备06005492号
Copyright© 2004-2023 ChipArt Studio All Rights Reserved