我正在尝试在ESP8266-01(1MB闪存)上设置一个简单的HTTP Web服务器,它具有最新的1.9.3 MicroPython固件 . 目的是能够为STA接口最终连接的家庭网络配置凭证 .

所以高级代码执行此操作:

  • 打开AP界面

  • 有人会连接到 192.168.0.1/index.html ,它会有一个用户名和密码的表单 . 我们只需要输入admin / admin . 单击“提交”按钮应该对 192.168.0.1/configure.html 进行POST

  • Configure.html 是一个带有表单的网页,其中将输入SSID和密码 . 我希望您可以通过查看下面的代码来了解更多详细信息

我面临两个问题:

  • 提交 index.html 表单时收到的总字节数未完成 . 我绕过Referrer(太偏了),总共大约560个字节 . 这是我从移动浏览器做的时候 . 有趣的是,它总是得到那么多字节 . 如果这会有所帮助,我可以分享我得到的东西 .

  • 看门狗计时器有时会重新启动我的模块 . 我'm doing most of the suggested changes in my code - using small sleeps. Is there any way in MicroPython on ESP8266, through which I can '喂' the WDT so that it does not '超时'然后重新启动我的模块?

以下是我的代码:

import gc
import network
gc.collect()
import machine
gc.collect()
import ubinascii
gc.collect()
import ujson
gc.collect()
import uos
gc.collect()
import utime
gc.collect()
import socket
gc.collect()
import select
gc.collect()

html = """<!DOCTYPE html>
<html>
    <head> <title>Ouroboros IoT Login</title> </head>
    <body>
        <form action="configure.html" method="POST">
            Username : <input type="text"  name="username"></br>
            Password: <input type="password" name="password" ></br>
                <input type="submit" value="submit" name="submit">
        </form>
    </body>
</html>
"""

login_fail_html = """<!DOCTYPE html>
<html>
    <head> <title>Ouroboros IoT Login</title> </head>
    <body>
        <h2>Incorrect Credentials!</h2><br>Please login<br>
        <form action="configure.html" method="POST">
            Username : <input type="text"  name="username"></br>
            Password: <input type="password" name="password" ></br>
                <input type="submit" value="submit" name="submit">
        </form>
    </body>
</html>
"""

# Check if file exists
def fileExists(fileName):
    try:
        uos.stat(fileName)
        print("File " + fileName + " found!")
        return True
    except OSError:
        print("No file " + fileName + " found!")
        return False

# Turns WiFi ON for configuration
def turn_wifi_on():
    # Setup the AP interface
    ap_if = network.WLAN(network.AP_IF)
    ap_if.active(False)
    ap_if.active(True)
    # Get the MACADDRESS - without any spaces
    macaddress = ubinascii.hexlify(ap_if.config('mac'),'').decode()
    ap_if.config(essid="OUB1_"+macaddress, password="12345678")
    #ap_if.config(essid="OUB1_"+macaddress)
    ap_if.ifconfig(('192.168.0.1', '255.255.255.0', '192.168.0.1', '192.168.0.1'))
    # Configure the AP to static IPs

def turn_wifi_off():
    ap_if = network.WLAN(network.AP_IF)
    ap_if.active(False)

# Find out the stored IoT secret content
def get_iot_secret():
    fileName = 'alpha.txt'
    if fileExists(fileName):
        f = open(fileName)
        content_str = f.read()
        f.close()
        return content_str
    else:
        return 'asdasrefwefefergf9rerf3n4r23irn1n32f'

# Find out the stored home network credential if exist
def get_wifi_config():
    fileName = 'wifi.conf'
    if fileExists(fileName):
        f = open(fileName)
        content_str = f.read()
        f.close()
        content = ujson.loads(content_str)
        return content
    else:
        return None

# Set the home network credentials
def save_wifi_config(essid, passphrase):
    f = open('wifi.conf', 'w')
    config = {'essid':essid, 'passphrase':passphrase}
    config_str = ujson.dumps(config)
    f.write(config_str)
    f.close()


# Find out the stored login credentials
def get_login_config():
    fileName = 'login.conf'
    if fileExists(fileName):
        f = open(fileName)
        content_str = f.read()
        f.close()
        content = ujson.loads(content_str)
        return content
    else:
        # No file exists so far, so use the admin/admin credentials
        return {'user':'admin','password':'admin'}

# Set the login credentials
def save_login_config(user, password):
    f = open('login.conf', 'w')
    config = {'user':user, 'password':password}
    config_str = ujson.dumps(config)
    f.write(config_str)
    f.close()

def turn_gpio_on(device_num):
    # Device Num to Pin Mapping
    if device_num == 0:
        pin_num = 0
    elif device_num == 1:
        pin_num = 2
    # Check Pin
    pin = machine.Pin(pin_num) 
    if pin.value() == 0:
        pin.on()
    # else it is already at HIGH state, nothing to do

def turn_gpio_off(device_num):
    # Device Num to Pin Mapping
    if device_num == 0:
        pin_num = 0
    elif device_num == 1:
        pin_num = 2
    # Check Pin
    pin = machine.Pin(pin_num) 
    if pin.value() == 1:
        pin.off()
    # else it is already at LOW state, nothing to do

def init_pin(device_num):
    # Device Num to Pin Mapping
    if device_num == 0:
        pin_num = 0
    elif device_num == 1:
        pin_num = 2
    #open GPIO0 in output mode & turn it off by default
    pin = machine.Pin(pin_num, machine.Pin.OUT) 
    # Turn off both GPIO initially
    turn_gpio_off(device_num)

# Find out the post parameters in a dictionary
def get_post_params(req):
    print("Inside GET POST PARAMS : req = " + req)
    post_params = req.split('\r\n')[-1:][0]
    # Check if the post body contains the necessary fields
    # Split the post_params by &
    # params : ['username=', 'password=', 'method=POST', 'url=http%3A%2F%2Ftwig-me.com%2Fv1%2Fusergroups%2FWKMUYXELA9LCC', 'jsondata=', 'submit=submit']
    print("post_params : " + post_params)
    params = post_params.split('&')
    print("Params")
    print(params)
    # Initialize the key value pair dict
    post_dict = {}
    # Iterate on each param
    for param in params:
        # Each param would be like 'method=POST', etc
        key_val = param.split('=')
        print("Key Val :")
        print(key_val)
        key = key_val[0]
        val = key_val[1]
        # Update post_dict
        post_dict[key] = val
    return post_dict

# This web server takes care of the WiFi configuration
# max_run_sec 
def web_server(max_run_sec = None):
    # Turn wifi interface ON
    turn_wifi_on()
    # Create server socket
    addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
    s = socket.socket()
    # TODO : If both the wifi and sta are operating simultaneously, then bind only to WiFi
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(addr)
    s.listen(1)
    # s.settimeout(1)

    poller = select.poll()
    poller.register(s, select.POLLIN)

    # Get the current time since epoch
    startTimeEpoch = utime.time()

    while True:
        events = poller.poll(200)  # time in milliseconds
        if events:
            try:
                gc.collect()
                res = s.accept()
                client_s = res[0]
                client_addr = res[1]
                req = ''
                #while True:
                #   data = client_s.recv(200)
                #   if data:
                #       req += str(data, 'utf8')
                #   else:
                #       break
                #   utime.sleep_ms(50)
                req = client_s.recv(4096)
                req = req.decode()
                print(req)
                req = str(req)
                # Came here means that there has been some connection!
                # Reset the start time epoch in such a case:
                startTimeEpoch = utime.time()
                # Check route now
                if req.find('configure.html') != -1:
                    print("Got configure request!\r\n")
                    # Check if the username and password are correct, if not, configure:
                    login_config = get_login_config()
                    username = login_config['user']
                    pwd = login_config['password']
                    print("Username : " + username + ", pwd : " + pwd)
                    # Find the POST PARAMETERS sent
                    # There would be just one entry in the array, so get the 0th index directly
                    # post_params : 'username=&password=&method=POST&url=http%3A%2F%2Ftwig-me.com%2Fv1%2Fusergroups%2FWKMUYXELA9LCC&jsondata=&submit=submit'
                    print("Came here A")
                    post_dict = get_post_params(req)

                    # Now check if the post_dict has the key and value for username and password as needed?
                    username_post = post_dict['username']
                    password_post = post_dict['password']

                    print("Came here B")

                    # Check if the password is same as expected
                    if (username_post == username) and (password_post == pwd):
                        hidden_input = '<input type="hidden" name="username" value="' + username + '"><input type="hidden" name="passphrase" value="' + pwd + '">'
                        # Send the login username and password inside the hidden input field
                        configure_html = "<!DOCTYPE html><html><head> <title>Ouroboros IoT WiFi Configuration Page</title> </head><body><form action=\"configure_wifi.html\" method=\"POST\">WiFi SSID : <input type=\"text\"  name=\"essid\"></br>WiFi Password: <input type=\"password\" name=\"passphrase\" ></br>" + hidden_input + "<input type=\"submit\" value=\"submit\" name=\"submit\"></form></body></html>"
                        # TODO : Also show link to webpage, where from we can change the login credentials
                        client_s.send(configure_html)   
                    else:
                        client_s.send(login_fail_html)
                elif req.find('configure_wifi.html') != -1:
                    # Check if the username and password are correct, if not, configure:
                    login_config = get_login_config()
                    username = login_config['user']
                    pwd = login_config['password']
                    # Get post parameters
                    post_dict = get_post_params(req)
                    # Now check if the post_dict has the key and value for username and password as needed?
                    username_post = post_dict['username']
                    password_post = post_dict['password']

                    # Check if the password is same as expected
                    if (username_post == username) and (password_post == pwd):
                        # Do some sanity check for handling the new wifi ssid and password
                        new_wifi_ssid = post_dict['essid']
                        new_wifi_passphrase = post_dict['passphrase']
                        # Set the wifi credentials
                        save_wifi_config(new_wifi_ssid, new_wifi_passphrase)
                        client_s.send('<!DOCTYPE html><html><head> <title>Ouroboros IoT WiFi Configuration Success</title> </head><body>Configuration successful!<br>Device would go into reboot now!</body></html>')
                        # Reboot device now
                        machine.reset()
                    else:
                        client_s.send(login_fail_html)
                elif req.find('index.html') != -1:
                    print("Got index.html request!\r\n")
                    client_s.send(html)
                else :
                    # Do nothing
                    print("Invalid request received! Show the login page again!\r\n")
                    client_s.send(html)

                client_s.close()
                machine.idle()
            except OSError:
                # Got no request and it timedout!
                print("Timed-out, no request received!\r\n")
            except Exception as e:
                print("Got some exception\r\n")
                print(str(e))
            finally:
                if max_run_sec is not None:
                    elapsedTime = utime.time() - startTimeEpoch
                    if elapsedTime >  max_run_sec:
                        # Max run time of web server has elapsed, time to exit this mode!
                        break
        utime.sleep_ms()
        machine.idle()

    # When while loop ends!
    s.close()
    # Turn wifi interface OFF
    turn_wifi_off()

# Starts a thread which runs the web server to handle WiFi
def start_web_server(max_run_sec = None):
    # start_new_thread(web_server, (max_run_sec))
    web_server(max_run_sec)



############# MAIN ##########################
# Initialize two pins to INPUT and OFF by default
init_pin(0)
init_pin(1)
#turn_wifi_off()

# Check if the home wifi network has been setup
# Check if home wifi config is valid, if so, connect to it
# If home wifi is not configured, then use the Web server all the time. 
if get_wifi_config() is None:
    # Came here means the wifi is not configured
    # Start the web server
    print("Starting web server")
    start_web_server()

编辑1:

我能够设置WDT并提供它 . 所以没有更多的WDT重启 . 但是,POST问题仍然存在:

仅供参考,以下是回复:

POST /configure.html HTTP/1.1
Host: 192.168.0.1
Connection: keep-alive
Content-Length: 43
Cache-Control: max-age=0
Origin: http://192.168.0.1
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Linux; Android 5.1.1; Redmi Note 3 Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.123 Mobile Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://192.168.0.1/index.html
Accept-Encoding: g

可以看出,接收的数据包是部分的,Content-Length头部表示43字节的有效负载 . 但它没有收到 . 使用“nc”并在本地运行服务器时,收到的数据包如下:

POST /configure.html HTTP/1.1
Host: 192.168.0.1
Connection: keep-alive
Content-Length: 43
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/65.0.3325.181 Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8

username=admin&password=admin&submit=submit

这里,可以很容易地看到43字节长度的有效载荷 .

所以我的问题是,对于ESP8266来说,几乎800字节的有效载荷是不是太大了?有什么办法可以删除浏览器发送的不必要的 Headers 吗?任何方式获得所有数据,如果其碎片?