ESP 8266 MicroPython获取POST

时间:2018-04-23 11:16:08

标签: esp8266 watchdog micropython

我正在尝试在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和密码。我希望您可以通过查看以下代码来了解更多详情

我面临两个问题:

  1. 提交index.html表单时收到的总字节数不完整。我绕过Referrer(太偏了),总共大约560个字节。这是我从移动浏览器做的时候。有趣的是,它总是得到那么多字节。如果有帮助,我可以分享我得到的东西。
  2. 看门狗计时器有时会重新启动我的模块。我在我的代码中做了大部分建议的更改 - 使用小睡眠。在ESP8266的MicroPython中是否有任何方法,通过它我可以“提供”WDT,以便它不会“超时”并重新启动我的模块?
  3. 以下是我的代码:

    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字节的有效载荷是否太多?有什么办法可以删除浏览器发送的不必要的标题吗?任何方式获得所有数据,如果其碎片?

1 个答案:

答案 0 :(得分:0)

我遇到了类似的问题,不过我的配置略有不同。

我从 html_files.py 导入我的 html,如下所示。

cred_prompt = """
<!DOCTYPE html>
<html>
    <head>
        <title>ESP8266 connection</title>
    </head>
    <body>
        <h2>
            Enter the UID and Password to connect to WiFi
        </h2>
        <form action="/post">
            uid: <input type="text" name="uid">
            password: <input type="text" name="password">
            <input type="submit" value="Submit">
        </form><br>
    </body>
</html>
"""

这是我用来获取凭据的函数。我稍后将它们保存到一个文件中(连续引导将使用它们)

def get_new_creds():
    sta_if.disconnect()
    print("Setting up webpage to get new credentials")
    addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
    soc = socket.socket()
    soc.bind(addr)
    soc.listen(5)
    print("Listening on", addr)
    while True:
        client, addr = soc.accept()
        print("Client connected from", addr)
        request = client.recv(1024)
        request = request.decode().split()
        uid, pwd = '', ''
        if 'uid' in request[1]:
            uid = request[1].split('&')[0].split('=')[1]
            pwd = request[1].split('&')[1].split('=')[1]
            write_new_creds(uid, pwd)
            connect_to_wifi()
            print("The UID is", uid, "and the Password is", pwd)
            client.send('HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n')
            client.send(html_files.connection_response.format(sta_if.ifconfig()[0]))
            return uid, pwd
        print(request)
        client.send('HTTP/1.1 200 OK\r\nContent-type: text/html\r\n\r\n')
        client.send(html_files.cred_prompt)
        client.close()

如果有帮助,这是整个 boot.py 代码。