micropython / micropython-esp32

Old port of MicroPython to the ESP32 -- new port is at https://github.com/micropython/micropython
MIT License
673 stars 216 forks source link

AP mode Socket Server not working #141

Closed 0xDBFB7 closed 7 years ago

0xDBFB7 commented 7 years ago

I'm having a little trouble with AP_IF mode. I'm trying to make a WiFi setup page, such that the user connects to an AP and gets served a configuration page. If I do

ap = network.WLAN(network.AP_IF)
ap.active(True)
ap.config(essid="Setup")

The AP gets created, and the client can connect to it. However, if I then use one of the socket http server examples:

import socket
addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
s = socket.socket()
s.bind(addr)
s.listen(1)
while True:
    cl, addr = s.accept()
    print('client connected from', addr)
    cl_file = cl.makefile('rwb', 0)
    while True:
        line = cl_file.readline()
        if not line or line == b'\r\n':
            break
    rows = ['<tr><td>%s</td><td>%d</td></tr>' % (str(p), p.value()) for p in pins]
    response = html % '\n'.join(rows)
    cl.send(response)
    cl.close()

And try to connect to 0.0.0.0:80 over the softAP, no client connection attempt is registered, and nothing gets sent to the client. In addition, ap.isconnected() gives an OSError: STA required, instead of the behavior documented here.

There's probably something I'm missing in AP/Socket setup - or is AP currently incompatible with the socket module?

Thanks for this incredible project, by the way, it makes my life a lot easier :>

dpgeorge commented 7 years ago

And try to connect to 0.0.0.0:80 over the softAP, no client connection attempt is registered, and nothing gets sent to the client.

You'll need to connect to the IP address of the ESP32, which is probably 192.168.4.1. I tried it and it works OK for me. If that's still not working then please provide more info about your set up and exactly how the test is run.

In addition, ap.isconnected() gives an OSError: STA required, instead of the behavior documented here.

Thanks for the report, that problem is now fixed by 7895a03736ffa276b63d335558b036b25be2c3d2

0xDBFB7 commented 7 years ago

@dpgeorge Ah, that's what I was missing, thank you!

stefanache commented 6 years ago

Hi, I tried this but when not can call from browser with http://192.168.4.1 How test it ? Thanks.

0xDBFB7 commented 6 years ago

@stefanache This is what I use now:

def config_ap():
    gc.collect()
    if(sta_if.isconnected()):
        print("already connected!")
        ap.active(False)
        return 0
    sta_if.active(False)
    ap.active(True)
    sta_if.active(True)

    ap.config(essid="SafeSump Setup")
    ap.ifconfig(('1.1.1.1', '255.255.255.0', '1.0.0.0', '8.8.8.8'))
    addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1]
    s = socket.socket()
    try:
        s.bind(addr)
        s.listen(1)
        s.settimeout(50)
        wdt.feed()
        scan_results = sta_if.scan()
        content_length = 0
        while True:
            wdt.feed()
            try:
                cl, addr = s.accept()
            except:  # timeout
                print("Timeout")
                s.close()
                ap.active(False)
                return 0
            print('client connected from', addr)
            cl_file = cl.makefile('rwb', 0)
            post = 0
            while(True):
                line = cl_file.readline()
                print(line)
                if(b'POST' in line):
                    post = 1
                if(b'Content-Length:' in line):
                    content_length = int(line.decode().replace('Content-Length: ',''))
                if(post and line == b'\r\n'):
                    line = cl_file.read(content_length)
                    print(line)
                    line = line.decode().split('&')
                    wifi_dict = {}
                    if(not [idx for idx, t in enumerate(line) if ('SSID=' in t and t.replace('SSID=', ''))] or not [idx for idx, t in enumerate(line) if ('password=' in t and t.replace('password=', ''))]):
                        scan_results = sta_if.scan()
                        response = ""
                        for i in scan_results:
                            response += '<input type="radio" name="SSID" value="{}">{}<br>'.format(i[0].decode(),i[0].decode())
                        cl.send(open('ap.html').read().format("<h1>Try again - nothing was entered.</h1>",response))
                        cl.close()
                        utime.sleep(2)
                        break
                    wifi_dict['ssid'] = urldecode(line[([idx for idx, t in enumerate(line) if ('SSID=' in t and t.replace('SSID=', ''))][0])].replace('SSID=', ''))
                    wifi_dict['password'] = urldecode(line[([idx for idx, t in enumerate(line) if ('password=' in t and t.replace('password=', ''))][0])].replace('password=', ''))
                    print(wifi_dict)
                    if(not wifi_dict['ssid'] or not wifi_dict['ssid']):
                        scan_results = sta_if.scan()
                        response = ""
                        for i in scan_results:
                            response += '<input type="radio" name="SSID" value="{}">{}<br>'.format(i[0].decode(),i[0].decode())
                        cl.send(open('ap.html').read().format("<h1>Try again, couldn't connect</h1>",response))
                        cl.close()
                        utime.sleep(2)
                        break
                    sta_if.connect(wifi_dict["ssid"], wifi_dict["password"])
                    wdt.feed()
                    utime.sleep(15)
                    if(sta_if.isconnected()):
                        f = open('wifi_settings.json', 'w+')
                        f.write(ujson.dumps(wifi_dict))
                        f.close()
                        cl.send(open('ap.html').read().format('<h1>Connected!</h1>',''))
                        cl.close()
                        utime.sleep(5)
                        ap.active(False)
                        import machine
                        machine.reset()
                    else:
                        scan_results = sta_if.scan()
                        response = ""
                        for i in scan_results:
                            response += '<input type="radio" name="SSID" value="{}">{}<br>'.format(i[0].decode(),i[0].decode())
                        cl.send(open('ap.html').read().format("<h1>Try again, couldn't connect</h1>",response))
                        cl.close()
                        utime.sleep(2)
                elif(not line or line == b'\r\n'):
                    break
            if(not post):
                response = ""
                for i in scan_results:
                    response += '<input type="radio" name="SSID" value="{}">{}<br>'.format(i[0].decode(),i[0].decode())
                cl.send(open('ap.html').read().format('',response))
                cl.close()
    except Exception as e:
        sys.print_exception(e)
        import machine
        machine.reset()

Though I'm using an old version of MicroPython.

stefanache commented 6 years ago

@BasedOnTechnology Thanks a lot ...I will try on my ESP WROOM 32 module (DevKitC board)... At first view the source I tried in the past ... Somethings is new for me : -wdt(watch dog) for wdt.feed()- must to simulate by soft one (maybe something this class wdog like in this https://forum.micropython.org/viewtopic.php?t=3410) -urldecode(will be simple to create one simple) Ref by lines ap.ifconfig(('1.1.1.1', '255.255.255.0', '1.0.0.0', '8.8.8.8')) addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] seem 0.0.0.0 not work maybe is ok 1.1.1.1? and will call from browser with http://1.1.1.1? or you put any IP random? or you refer to DNS change procedure from internet : https://1.1.1.1/ ?

stefanache commented 6 years ago

Before will be something like import ujson import machine import sys import gc

import wdog def ptrig(): if reset: machine.reset() print('triggered') reset = False wdt = wdog.WDOG() wdt.trig = ptrig wdt.init(2000) # enable it with a timeout of 2s

ap = network.WLAN(network.AP_IF) sta_if = network.WLAN(network.STA_IF)

def urldecode(str): dic = {"%21":"!","%22":'"',"%23":"#","%24":"$","%26":"&","%27":"'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",","%2F":"/","%3A":":","%3B":";","%3D":"=","%3F":"?","%40":"@","%5B":"[","%5D":"]","%7B":"{","%7D":"}"} for k,v in dic.items(): str=str.replace(k,v) return str

stefanache commented 6 years ago

and in file lib/wdog.py will have this content: import machine as mc class WDOG():

def init(self): self.timer = mc.Timer(-1) self.fed = False

def feed(self): self.fed = True

def trig(self): mc.reset()

def wdtcb(self,tmr): if not self.fed: self.deinit() self.trig() self.fed = False

def deinit(self): self.timer.deinit()

def init(self,msec=5000): self.fed = False self.timer.init(period=msec, mode=mc.Timer.PERIODIC, callback=self.wdtcb)

stefanache commented 6 years ago

or this line ap.ifconfig(('1.1.1.1', '255.255.255.0', '1.0.0.0', '8.8.8.8')) refer to static routing?

0xDBFB7 commented 6 years ago

@stefanache I just put in 1.1.1.1 randomly and use http://1.1.1.1. I'm afraid I haven't used micropython in a while - I'm not sure what's happening in your case.

stefanache commented 6 years ago

BasedOnTechnology ok thanks in fact is same problem: esp32(like my esp wroom 32 devkit board) when work as AP_IF(soft_AP) create another network(like 192.168.4.0) and this is not accessible from my smartphone or PC/Laptop Ofcourse the smartphone,pc/laptop tablet work ok in my home wifi-router (which create one network 192.168.0.0) Maybe need one method to forward/routing the trafic from my esp32(192.168.4.1) to my home-router(192.168.0.1) That routing need maybe because the smartphone and other device try to connect to internet after connected at wifi and this esp32(which work as soft-AP) have not internet link (but the my home-router have this connection to my internet provider-WAN)