找回密码
 注册
查看: 608|回复: 3
打印 上一主题 下一主题

用ESP8266搭建简易web服务器

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2021-10-22 14:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
ESP8266是个集成了LWIP协议栈的WIFI模块,利用它很容易就完成了物联网的功能。
要搭建web服务器就必须支持HTTP协议。
但是ESP8266官网提供的例子只能支持TCP,不直接支持HTTP。
那么,就需要自己利用TCP来完成HTTP的请求包和响应包。
一、搭建ESP8266的SDK开发环境。
下载并安装集成IDE,AiThinkerIDE_V0.5。
下载并安装Flash下载工具。
下载并解压官方提供的SDK例子,ESP8266 NONOS SDK。
二、利用IoT Demo来完成TCP协议。
进入esp8266_nonos_sdk目录,把driver_lib目录改名为app,把examples->IoT_Demo中的所有文件及文件夹都拷贝到app目录下(直接覆盖)。
在AiThinker_IDE下,打开esp8266_nonos_sdk工程,编译(Build Project),生成.bin文件。
用ESPFlashDownloadTool烧写该.bin文件,可以在WiFi中看到ESP8266的热点,此为开放热点,可以直接连接。
每次编译成功,IDE都会提示.bin文件的烧写地址,如下图所示,eagle.flash.bin->0x00000,eagle.rom0text.bin->0x10000,把这些文件的路径和地址填在烧写工具就可以了。


把ESP8266的GPIO0置低,GPIO2置高,GPIO15置低,重新上电,即可进入烧写模式。
能看到ESP8266的热点,说明硬件已经没有问题了。接下来要修改SDK的源码。
三、利用TCP协议来完成HTTP协议。
在NONOS工程中新建user_tcpserver.c文件,并添加下面的代码。
#include "osapi.h"
#include "at_custom.h"
#include "user_inteRFace.h"
#include "espconn.h"
#include "ets_sys.h"
#include "mem.h"

uint8_t http_head[] = {
  "HTTP/1.0 200 OK\r\n"\
  "Content-Type: text/html;charset=gbk\r\n"\
  "Cache-Control: private\r\n"\
  "Connection: close\r\n"\
  "\r\n"\
  "<!DOCTYPE html>"\
  "<html>" \
  "<head>" \
  "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">" \
  "<meta name=\"viewport\" content=\"width=device-width,height=device-height,inital-scale=1.0,maximum-scale=1.0,user-scalable=no;\">" \
  "<meta name=\"renderer\" content=\"webkit|ie-comp|ie-stand\">" \
  "<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">" \
  "<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">" \
  "<meta name=\"format-detection\" content=\"telephone=no\">" \
  "<meta content=\"email=no\" name=\"format-detection\" />" \
  "<title></title>" \
  "</head>" \
  "<style type=\"text/css\">" \
  "body{overflow-x:hidden;}" \
  "img{width: 100%;display: block;}" \
  "</style>" \
  "<body>" \
  "<a>点击按钮</a>" \
  "<form action=\"\" method=\"post\">" \
  "<button name=\"btn\" value=\"down\">下载</button>" \
  "</form>" \
  "</body>" \
  "</html>" \
  ""
  };

struct espconn tcpserver;
uint8_t http_data[] = {
  "HTTP/1.1 200 OK\r\n"\
  "Content-Type:text/plain;charset=UTF-8\r\n"\
  "Content-Disposition:attachment;filename=1.txt\r\n"\
  "\r\n"\
  "get data"\
  "\r\n"
};

void ICACHE_FLASH_ATTR
tcpserver_recon_cb(void *arg, sint8 errType)//异常断开回调
{
struct espconn *pespconn = (struct espconn *)arg;
os_printf("\r\n异常断开");
}

void ICACHE_FLASH_ATTR
tcpserver_discon_cb(void *arg)//正常断开回调
{
struct espconn *pespconn = (struct espconn *)arg;
os_printf("\r\n正常断开");
}

void ICACHE_FLASH_ATTR
tcpclient_sent_cb(void *arg)//发送回调
{
struct espconn *pespconn = (struct espconn *)arg;
espconn_disconnect(pespconn);//断开连接
os_printf("\r\n发送回调");
}

void ICACHE_FLASH_ATTR
tcpserver_recv(void *arg, char *pdata, unsigned short len)//接收函数
{
char * http_flg = NULL;

unsigned short i,web_len = 0;
struct espconn *pespconn = (struct espconn *)arg;
os_printf("\r\n接收函数,%s",pdata);

http_flg = strstr(pdata,"btn=down");
if (http_flg != NULL) {
  espconn_send(pespconn, http_data, sizeof(http_data));
} else {
  http_flg = strstr(pdata,"HTTP");
  if(http_flg != NULL){
   espconn_send(pespconn,http_head,sizeof(http_head));
  }
}

}

void ICACHE_FLASH_ATTR
tcpserver_listen(void *arg)//服务器被链接回调
{
struct espconn *pespconn = (struct espconn *)arg;

espconn_regist_reconcb(pespconn, tcpserver_recon_cb);//开启异常断开回调
espconn_regist_disconcb(pespconn, tcpserver_discon_cb);//开启正常断开回调
espconn_regist_recvcb(pespconn, tcpserver_recv);//开启接收回调
espconn_regist_sentcb(pespconn, tcpclient_sent_cb);//开启发送成功回调
}

void ICACHE_FLASH_ATTR
tcp_server(void)//开启tcp服务器
{
tcpserver.proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
tcpserver.proto.tcp->local_port = 80;//监听本地端口号

tcpserver.type = ESPCONN_TCP;
tcpserver.state = ESPCONN_NONE;

espconn_regist_connectcb(&tcpserver, tcpserver_listen);//链接成功回调
espconn_accept(&tcpserver);//开启TCP服务器
espconn_regist_time(&tcpserver, 1, 0);//设置服务器超时时间为1秒
}

把user_main.c全部改为下面的代码。
#include "ets_sys.h"
#include "osapi.h"

#include "user_interface.h"

#include "user_devicefind.h"
#include "user_webserver.h"

#include "driver/uart.h"
#if ESP_PLATFORM
#include "user_esp_platform.h"
#endif

uint32 priv_param_start_sec;

struct softap_config softap_cfg;
uint8 ssid[]="esp8266_wifi";         //wifi名
uint8 password[]="12345678";     //wifi密码

void ICACHE_FLASH_ATTR
user_set_softap_config(void)
{
wifi_softap_get_config(&softap_cfg); // Get config first.
    wifi_set_opmode(SOFTAP_MODE);           //设置为AP MODE
    os_strcpy(softap_cfg.ssid, ssid);          //ssid名称
    os_strcpy(softap_cfg.password, password);  //密码
    softap_cfg.authmode = AUTH_WPA_WPA2_PSK;
    softap_cfg.ssid_len = 0; // or its actual length
    softap_cfg.max_connection = 4; // how many stations can connect to ESP8266 softAP at most.
    wifi_softap_set_config(&softap_cfg);      //设置WIFI帐号和密码
}

/******************************************************************************
* FunctionName : user_rf_cal_sector_set
* Description  : SDK just reversed 4 sectors, used for rf init data and paramters.
*                We add this function to force users to set rf cal sector, since
*                we don't know which sector is free in user's application.
*                sector map for last several sectors : ABCCC
*                A : rf cal
*                B : rf init data
*                C : sdk parameters
* Parameters   : none
* Returns      : rf cal sector
*******************************************************************************/
uint32 ICACHE_FLASH_ATTR
user_rf_cal_sector_set(void)
{
    enum flash_size_map size_map = system_get_flash_size_map();
    uint32 rf_cal_sec = 0;

    switch (size_map) {
        case FLASH_SIZE_4M_MAP_256_256:
            rf_cal_sec = 128 - 5;
            priv_param_start_sec = 0x3C;
            break;

        case FLASH_SIZE_8M_MAP_512_512:
            rf_cal_sec = 256 - 5;
            priv_param_start_sec = 0x7C;
            break;

        case FLASH_SIZE_16M_MAP_512_512:
            rf_cal_sec = 512 - 5;
            priv_param_start_sec = 0x7C;
            break;
        case FLASH_SIZE_16M_MAP_1024_1024:
            rf_cal_sec = 512 - 5;
            priv_param_start_sec = 0xFC;
            break;

        case FLASH_SIZE_32M_MAP_512_512:
            rf_cal_sec = 1024 - 5;
            priv_param_start_sec = 0x7C;
            break;
        case FLASH_SIZE_32M_MAP_1024_1024:
            rf_cal_sec = 1024 - 5;
            priv_param_start_sec = 0xFC;
            break;

        case FLASH_SIZE_64M_MAP_1024_1024:
            rf_cal_sec = 2048 - 5;
            priv_param_start_sec = 0xFC;
            break;
        case FLASH_SIZE_128M_MAP_1024_1024:
            rf_cal_sec = 4096 - 5;
            priv_param_start_sec = 0xFC;
            break;
        default:
            rf_cal_sec = 0;
            priv_param_start_sec = 0;
            break;
    }

    return rf_cal_sec;
}

void ICACHE_FLASH_ATTR
user_rf_pre_init(void)
{
}

void esp_init_ok(void)
{
    uart0_sendStr("Hello Esp8266!\r\n");
    tcp_server();//开启tcp服务器
}

/******************************************************************************
* FunctionName : user_init
* Description  : entry of user application, init user function here
* Parameters   : none
* Returns      : none
*******************************************************************************/
void ICACHE_FLASH_ATTR
user_init(void)
{
uart_init(BIT_RATE_115200,BIT_RATE_115200);
user_set_softap_config();  //set STATION config
system_init_done_cb(esp_init_ok);

#ifdef SERVER_SSL_ENABLE
    user_webserver_init(SERVER_SSL_PORT);
#else
//    user_webserver_init(SERVER_PORT);
#endif
}
这样,就实现带密码的WiFi热点,在浏览器打开192.168.4.1即可看到网页,点击下载按钮能下载到一个txt文件。
苹果手机由于权限问题不能下载到txt,安卓手机可以下载到txt。
四、其它问题。
1、如何写网页?
下载一个Dreamweaver来编写你的网页。

然后使用小工具把html语言转成c语言,HTML转化成c语言数组,这个小工具转换出来的C语言可能出错,自己检测一下转义字符就可以了。
把生成的c语言数组替换上面源码中的http_head中的内容即可,但是以下的内容不变。
        "HTTP/1.0 200 OK\r\n"\
        "Content-Type: text/html;charset=gbk\r\n"\
        "Cache-Control: private\r\n"\
        "Connection: close\r\n"\
        "\r\n"\
2、关于http协议。
由于官方提供的ESP8266 IoT Demo例子只支持TCP协议,所以我们要在TCP协议的基础上实现HTTP协议。
这里用到HTTP协议的请求头和响应头,请参考《HTTP协议简介》、《HTTP权威指南》
3、为什么我的HTTP请求头和响应头不起作用?
需要在每一句请求头和响应头结尾加上"\r\n"。
4、为什么连接ESP8266的热点会很慢?
不要设置为STATIONAP_MODE,这样STA和AP会互相干扰,改为SOFTAP_MODE即可。
5、不支持input表单。
用以上代码,只能支持button提交的Form表单,而不支持input,也不支持获取其它标签的属性,有待完善。
6、串口不正常。
使用CH340串口模块,发现电脑发送到ESP8266的命令不正常,电压拉不低,最低也有2V左右。
后来换了个串口模块,就正常了。下图为不正常的串口模块。

7、设置ESP8266为串口透传模式。
1)  服务端
以下三条可以提前设置好,永久生效
AT+CWMODE=2
AT+RST
AT+CWSAP="ESP8266","0123456789",11,4
单片机至少要设置以下两条
AT+CIPMUX=1
AT+CIPSERVER=1,8080
2)  客户端
以下四条可以提前设置好,永久生效
AT+CWMODE=1
AT+RST
AT+CWJAP="ESP8266","0123456789"
AT+CIPMUX=0
单片机至少要设置以下三条
AT+CIPMODE=1
AT+CIPSTART="TCP","192.168.4.1",8080
AT+CIPSEND
8、之前用SDK写好的服务器代码,电脑可以连接到ESP8266,后面却发现连接不上,一连接就Leave。
电脑忘记ESP8266的WIFI账号,重新输入密码,连接即可。


该用户从未签到

2#
发表于 2021-10-22 14:21 | 只看该作者
把ESP8266的GPIO0置低,GPIO2置高,GPIO15置低,重新上电,即可进入烧写模式。
  • TA的每日心情
    开心
    2022-12-27 15:07
  • 签到天数: 1 天

    [LV.1]初来乍到

    3#
    发表于 2021-10-22 16:35 | 只看该作者
    要搭建web服务器就必须支持HTTP协议

    该用户从未签到

    4#
    发表于 2021-10-22 16:36 | 只看该作者
    ESP8266是个集成了LWIP协议栈的WIFI模块
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-5-29 03:40 , Processed in 0.093750 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表