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);//发光管控制
}
配置网络可以使用微信扫描下图二维码按提示操作
测试图:
|
|