ESP8266 RTOS_wed配网
虽然Smartconfig配网很多相便利性,但这也是在有APP的情况下。- -安卓的app还好说,苹果的app开发者帐号这让我等业余爱好者无力。
所以wed配网很不错的解决方法:
其实就是尝试读取nvs,然后用取得信息打开sta模式连接路由器> ,在sta模式下连接不上N次时,
转为ap模式,其中不管在那个模式下都打开http服务,然后处理http提交的信息保存到nvs,保存后重启esp8266.
代码如下:(代码有点多,开始混乱了,各种调试信息0.0)
user_wifi.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_err.h"
#include "driver/gpio.h"
#include "driver/uart.h"
#include "driver/i2c.h"
#include "tcpip_adapter.h"
#include "nvs_flash.h"
#include "user_gpio.h"
#include "user_uart.h"
#include "user_nvs.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include <esp_http_server.h>
#include "wed_html.h"
#include <sys/param.h>
//#include "user_ssd1306.h"
void initialise_ap_wifi(void *arg);
const int CONNECTED_BIT = BIT0;
static EventGroupHandle_t wifi_event_group;
#define CONFIG_AP_SSID "ESP8266" //配置AP的SSID 和password
#define CONFIG_AP_PASSWORD "12345678"//配置AP的SSID 和password
uint Reconnection_Num = 0 ;
#if 1
/* HTTP GET处理程序 */
esp_err_t hello_get_handler(httpd_req_t *req)
{
char* buf;
size_t buf_len;
/* 读取URL查询字符串长度,并为长度+ 1分配内存,为空终止分配额外的字节 */
buf_len = httpd_req_get_url_query_len(req) + 1;
if (buf_len > 1) {
buf = malloc(buf_len);
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
os_printf( "Found URL query => %s \r\n", buf);
char param[32];
/* 获得指定的url的query1参数值 */
if (httpd_query_key_value(buf, "query1", param, sizeof(param)) == ESP_OK) {
os_printf( "Found URL query parameter => query1=%s \r\n", param);
}
}
free(buf);
}
/* 方法发送具有自定义标题和正文的响应在用户上下文中传递的字符串*/
const char* resp_str = (const char*) req->user_ctx;
httpd_resp_send(req, resp_str, strlen(resp_str));
/* 发送HTTP响应后,旧的HTTP请求头信息丢失。检查HTTP请求头现在是否可以读取。 */
if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
os_printf( "Request headers lost \r\n");
}
return ESP_OK;
}
httpd_uri_t hello = {
.uri = "/",
.method = HTTP_GET,
.handler = hello_get_handler,
/* 让我们在用户中传递响应字符串 上下文来演示它的用法 二进制html代码 const unsigned char wed_html[] =*/
.user_ctx =(u_char *) & wed_html,
};
/* An HTTP POST handler */
esp_err_t echo_post_handler(httpd_req_t *req)
{
char buf[1000];
int ret, remaining = req->content_len;
while (remaining > 0) {
/* 读取请求的数据 */
if ((ret = httpd_req_recv(req, buf,MIN(remaining, sizeof(buf)))) <= 0) {
if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
/* 如果超时,请重试接收*/
continue;
}
return ESP_FAIL;
}
// /* 发送回相同的数据 */
// httpd_resp_send_chunk(req, buf, ret);
// os_printf(buf);
remaining -= ret;
/* Log data received */
os_printf( "=========== RECEIVED DATA ==========\r\n");
os_printf( "%.*s\r\n", ret, buf); //输出buf,但至少占ret个位置
os_printf( "====================================\r\n");
char param[32];
/* Get value of expected key from query string */
if (httpd_query_key_value(buf, "SSID", param, sizeof(param)) == ESP_OK) {
if (strlen(param)>0){
user_nvs_setkey("ssid",param);
if (httpd_query_key_value(buf, "password", param, sizeof(param)) == ESP_OK) {
user_nvs_setkey("password",param);
}
}
}
if (httpd_query_key_value(buf, "mqtt_addr", param, sizeof(param)) == ESP_OK) {
if (strlen(param)>0)
user_nvs_setkey("mqtt_addr",param);
}
if (httpd_query_key_value(buf, "Dev_id", param, sizeof(param)) == ESP_OK) {
if (strlen(param)>0)
user_nvs_setkey("Dev_id",param);
}
user_nvs_close();
}
// 当无参数时时返回空的html
httpd_resp_send_chunk(req, NULL, 0);
esp_restart();
return ESP_OK;
}
httpd_uri_t echo = {
.uri = "/echo",
.method = HTTP_POST,
.handler = echo_post_handler,
.user_ctx = NULL
};
httpd_handle_t start_webserver(void)
{
httpd_handle_t server = NULL;
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
// Start the httpd server
user_uart0_print( "Starting server on port: '%d'", config.server_port);
if (httpd_start(&server, &config) == ESP_OK) {
// Set URI handlers
os_printf( "Registering URI handlers");
httpd_register_uri_handler(server, &hello);
httpd_register_uri_handler(server, &echo);
return server;
}
os_printf( "Error starting server!");
return NULL;
}
void stop_webserver(httpd_handle_t server)
{
// Stop the httpd server
httpd_stop(server);
}
#endif
//wifi的状态事件
static esp_err_t event_handler(void *ctx, system_event_t *event)
{
httpd_handle_t *server = (httpd_handle_t *) ctx;
/* 用于在断开连接时访问原因代码 */
// system_event_info_t *info = &event->event_info;
switch(event->event_id) {
case SYSTEM_EVENT_STA_START: //表示wifi启动就绪
esp_wifi_connect();
break;
case SYSTEM_EVENT_STA_GOT_IP: //分配得到IP,就表示已经连上路由器咯 启用wed
xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); //设置事件标志位
os_printf("SYSTEM_EVENT_STA_GOT_IP \r\n");
if (*server == NULL) {
*server = start_webserver();
}
break;
case SYSTEM_EVENT_STA_WPS_ER_FAILED: //连接失败时 启用wed
os_printf("SYSTEM_EVENT_STA_WPS_ER_FAILED\r\n");
break;
case SYSTEM_EVENT_STA_DISCONNECTED: //断开连接 wifi密码错了的时候也会调用这里
user_uart0_print ("SYSTEM_EVENT_STA_DISCONNECTED\r\n");
if (Reconnection_Num<2)
{
esp_wifi_connect(); //当esp8266因为异常断开连接时,我们继续连接
Reconnection_Num += 1;
}
else
{
Reconnection_Num = 0 ;
esp_wifi_disconnect();
esp_wifi_stop();
initialise_ap_wifi(ctx);
}
xEventGroupClearBits(wifi_event_group, CONNECTED_BIT); //清除事件标志位,等连接wifi后重新设置
break;
case SYSTEM_EVENT_AP_START: //AP启动好了
os_printf("SYSTEM_EVENT_AP_START.\r\n");
break;
case SYSTEM_EVENT_AP_STOP: //AP停止
os_printf("SYSTEM_EVENT_AP_STOP.\r\n");
break;
case SYSTEM_EVENT_AP_STACONNECTED: //一个连接到ESP8266软ap的事件
os_printf("SYSTEM_EVENT_AP_STACONNECTED, \r\n");
break;
case SYSTEM_EVENT_AP_STADISCONNECTED: //一个断开连接的事件
os_printf("SYSTEM_EVENT_AP_STADISCONNECTED \r\n");
if (*server) {
stop_webserver(*server);
*server = NULL;
}
break;
case SYSTEM_EVENT_AP_STAIPASSIGNED ://ESP8266软ap将一个IP分配给一个被连接的工作站 这里可以启用wed
os_printf("SYSTEM_EVENT_AP_STAIPASSIGNED \r\n");
if (*server == NULL) {
*server = start_webserver();
}
break;
case SYSTEM_EVENT_AP_PROBEREQRECVED: //在软ap接口中接收探测请求包
os_printf("SYSTEM_EVENT_AP_PROBEREQRECVED, .\r\n");
break;
default:
break;
}
return ESP_OK;
}
void initialise_sta_wifi(void *arg)
{
char *ssid_str =user_nvs_getkey("ssid");
char *password_str = user_nvs_getkey("password");
if(ssid_str ==NULL){
initialise_ap_wifi(arg);
return;
}
tcpip_adapter_init(); //初始化tcpip适配器,这将初始化内部的TCPIP堆栈。
wifi_event_group = xEventGroupCreate(); //wifi事件标识位
esp_event_loop_init(event_handler, arg); //这个api会调用event_handler函数来处理wifi的状态事件
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); //使用默认的wifi初始化配置
esp_wifi_init(&cfg); //初始化wifi
esp_wifi_set_storage(WIFI_STORAGE_RAM);
esp_wifi_set_mode(WIFI_MODE_STA) ; //设置wifi模式
wifi_config_t wifi_config = {
.sta = {
.ssid = "ssid",
.password ="password",
.bssid_set = false
}
};
user_uart0_print("ssid=%s,pass=%s",ssid_str,password_str); //测试nvs取值
char ssid_str_ch[32];
strcpy(ssid_str_ch,ssid_str);
char password_str_ch[64];
strcpy(password_str_ch,password_str);
memcpy(wifi_config.sta.ssid,ssid_str_ch,sizeof(ssid_str_ch));
memcpy((void *)wifi_config.sta.password,(void *)password_str_ch,sizeof(password_str_ch));
os_printf( "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
user_nvs_close();
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ; //将配置信息,写入wifi
esp_wifi_start();
}
void initialise_ap_wifi(void *arg)
{
tcpip_adapter_init();//tcp/IP配置
esp_event_loop_init(event_handler, arg) ;//回调,当ESP32检测到某些类型的WiFi相关事件时调用。
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();//定义一个名为cfg的wifi_init_config_t结构体。wifi_init_config_t中的参数可由menuconfig配置
esp_wifi_init(&cfg) ;
esp_wifi_set_storage(WIFI_STORAGE_RAM);//指示ESP32将这些设置记录到闪存
esp_wifi_set_mode(WIFI_MODE_AP);//模式设置为AP
uint8_t ApMac[6];//网卡地址
esp_wifi_get_mac( ESP_IF_WIFI_AP , ApMac );//获取DHCP分配的IP接口的mac。
wifi_config_t ap_config = {
.ap = {
.ssid = CONFIG_AP_SSID,
.password = CONFIG_AP_PASSWORD,
.ssid_len = 0,
.max_connection = 4,
.authmode = WIFI_AUTH_WPA2_PSK
}
};//ap接入点参数配置。
//修改AP防止AP重复,得到AP-ESP8266_随机数
sprintf( (char *)ap_config.ap.ssid , "%s_%02X%02X" , CONFIG_AP_SSID , ApMac[4] , ApMac[5] );
esp_wifi_set_config(WIFI_IF_AP, &ap_config);
esp_wifi_start();
}
#if 1 //socket服务
/* 这是一个socket服务,这个服务会一直循环的工作,*/
static void tcp_server_task(void *pvParameters)
{
char rx_buffer[128]; //接收到的数据 因char占一个字节,可以理解一共128字节的数据
char addr_str[128]; //客户端地址
int addr_family; //协议簇
int ip_protocol; //IP协议
while (1) {
struct sockaddr_in destAddr; //定义目地socket结构体
destAddr.sin_addr.s_addr = htonl(INADDR_ANY); //本机地址
destAddr.sin_family = AF_INET; //协议簇
destAddr.sin_port = htons(8080); //端口号
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1); //直接将其映射到lwip内部函数,注意这是映射。。。
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);//创建一个用于连接的socket
if (listen_sock < 0) {
os_printf("Unable to create socket: errno %d \n", errno);
break;
}
os_printf("Socket created \n");
int err = bind(listen_sock, (struct sockaddr *)&destAddr, sizeof(destAddr)); //绑定一个socket连接到本机上
if (err != 0) {
os_printf("Socket unable to bind: errno %d \n", errno);
break;
}
os_printf("Socket binded \n");
err = listen(listen_sock, 1);//启动监听
if (err != 0) {
os_printf("Error occured during listen: errno %d \n", errno);
break;
}
os_printf("Socket listening \n");
struct sockaddr_in sourceAddr; //来源地址
uint addrLen = sizeof(sourceAddr);
int sock = accept(listen_sock, (struct sockaddr *)&sourceAddr, &addrLen);
if (sock < 0) {
os_printf("Unable to accept connection: errno %d \n", errno);
break;
}
os_printf("Socket accepted \n");
while (1) {
int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
// 接收过程中发生错误
if (len < 0) {
os_printf("recv failed: errno %d \n", errno);
break;
}
// 连接关闭了
else if (len == 0) {
os_printf("Connection closed \n");
break;
}
// 接收到的数据
else {
inet_ntoa_r(((struct sockaddr_in *)&sourceAddr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
rx_buffer[len] = 0; // Null-终止我们接收到的任何内容,并将其视为字符串
// os_printf("Received %d bytes from %s: \n", len, addr_str);
// os_printf("%s \n", rx_buffer); //打印字符
// OLED_Clear();
// OLED_ShowString(0,0,(unsigned char *)rx_buffer);
if( !strcmp(rx_buffer, "led"))
{
//这里开始任务
xTaskCreate(ATaskLED,"LED",1024*4, NULL, 5, NULL);
}
int err = send(sock, rx_buffer, len, 0);
//发送过程中发生错误
if (err < 0) {
os_printf("Error occured during sending: errno %d \n", errno);
break;
}
}
}
//关闭插座并重新启动
if (sock != -1) {
os_printf("Shutting down socket and restarting... \n");
shutdown(sock, 0);
close(sock);
shutdown(listen_sock,0);
close(listen_sock); //清理了socket列表
vTaskDelay(5); //延时一下,让硬件清理完成
}
}
vTaskDelete(NULL);
}
/*这是一个socket任务,调用他就能启用了*/
static void socket_task(void *pvParameters)
{
//等待事件标志位被设置,任务 wifi_event_group 由运行态进入到阻塞态 参数2:要等待的事件组中的位
xEventGroupWaitBits(wifi_event_group, BIT0, false, true, portMAX_DELAY);
xTaskCreate(tcp_server_task, "tcp_server",1024*4, NULL, 10, NULL);
vTaskDelete(NULL);
}
//启动socket
void socket_start (void)
{
xTaskCreate(socket_task, "socket_task",1024*4, NULL, 10, NULL);//通过任务来启动监听,防主进程阻塞
}
#endif
头文件
#ifndef _USER_WIFI_H_
#define _USER_WIFI_H_
void initialise_sta_wifi(void *arg);
void socket_start ();
//void initialise_ap();
#endif
网页问题:make menuconfig-http server-head长度设置长点(来自82925604用户的提醒,谢谢)