├── .gitignore ├── LICENSE ├── README.md ├── WiFi_Manager.png ├── main.py ├── package.json └── wifimgr.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tayfun ULU 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WiFi Manager 2 | 3 | Lang : Micropython 4 | Tested : 1.8 and 1.9.3 5 | 6 | Description : WiFi manager for ESP8266 - ESP12 - ESP32 for micropython 7 | 8 | Main Features: 9 | 10 | - Web based connection manager 11 | - Save wifi password in "wifi.dat" (csv format) 12 | - Easy to apply 13 | 14 | Usage: 15 | 16 | Upload main.py and wifimgr.py to ESP. 17 | Write your code into main.py or import it from main.py. 18 | 19 | Logic: 20 | 1. step: Check "wifi.dat" file and try saved networks/passwords. 21 | 2. step: Publish web page to configure new wifi. 22 | 3. step: Save network/password to "wifi.dat" file. 23 | 4. step: Run user code. 24 | 25 | ![alt text](https://github.com/tayfunulu/WiFiManager/blob/master/WiFi_Manager.png) 26 | 27 | **web server based on code of CPOPP - https://github.com/cpopp/MicroPythonSamples 28 | -------------------------------------------------------------------------------- /WiFi_Manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tayfunulu/WiFiManager/552bfecd457a4cb4e2be93b7c698baf1751261ae/WiFi_Manager.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import wifimgr 2 | 3 | 4 | wlan = wifimgr.get_connection() 5 | if wlan is None: 6 | print("Could not initialize the network connection.") 7 | while True: 8 | pass # you shall not pass :D 9 | 10 | 11 | # Main Code goes here, wlan is a working network.WLAN(STA_IF) instance. 12 | print("ESP OK") 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "urls": [ 3 | ["wifimgr.py", "github:tayfunulu/WiFiManager/wifimgr.py"] 4 | ], 5 | "version": "1.0.0", 6 | "deps": [] 7 | } -------------------------------------------------------------------------------- /wifimgr.py: -------------------------------------------------------------------------------- 1 | import network 2 | import socket 3 | import ure 4 | import time 5 | 6 | ap_ssid = "WifiManager" 7 | ap_password = "tayfunulu" 8 | ap_authmode = 3 # WPA2 9 | 10 | NETWORK_PROFILES = 'wifi.dat' 11 | 12 | wlan_ap = network.WLAN(network.AP_IF) 13 | wlan_sta = network.WLAN(network.STA_IF) 14 | 15 | server_socket = None 16 | 17 | 18 | def get_connection(): 19 | """return a working WLAN(STA_IF) instance or None""" 20 | 21 | # First check if there already is any connection: 22 | if wlan_sta.isconnected(): 23 | return wlan_sta 24 | 25 | connected = False 26 | try: 27 | # ESP connecting to WiFi takes time, wait a bit and try again: 28 | time.sleep(3) 29 | if wlan_sta.isconnected(): 30 | return wlan_sta 31 | 32 | # Read known network profiles from file 33 | profiles = read_profiles() 34 | 35 | # Search WiFis in range 36 | wlan_sta.active(True) 37 | networks = wlan_sta.scan() 38 | 39 | AUTHMODE = {0: "open", 1: "WEP", 2: "WPA-PSK", 3: "WPA2-PSK", 4: "WPA/WPA2-PSK"} 40 | for ssid, bssid, channel, rssi, authmode, hidden in sorted(networks, key=lambda x: x[3], reverse=True): 41 | ssid = ssid.decode('utf-8') 42 | encrypted = authmode > 0 43 | print("ssid: %s chan: %d rssi: %d authmode: %s" % (ssid, channel, rssi, AUTHMODE.get(authmode, '?'))) 44 | if encrypted: 45 | if ssid in profiles: 46 | password = profiles[ssid] 47 | connected = do_connect(ssid, password) 48 | else: 49 | print("skipping unknown encrypted network") 50 | else: # open 51 | connected = do_connect(ssid, None) 52 | if connected: 53 | break 54 | 55 | except OSError as e: 56 | print("exception", str(e)) 57 | 58 | # start web server for connection manager: 59 | if not connected: 60 | connected = start() 61 | 62 | return wlan_sta if connected else None 63 | 64 | 65 | def read_profiles(): 66 | with open(NETWORK_PROFILES) as f: 67 | lines = f.readlines() 68 | profiles = {} 69 | for line in lines: 70 | ssid, password = line.strip("\n").split(";") 71 | profiles[ssid] = password 72 | return profiles 73 | 74 | 75 | def write_profiles(profiles): 76 | lines = [] 77 | for ssid, password in profiles.items(): 78 | lines.append("%s;%s\n" % (ssid, password)) 79 | with open(NETWORK_PROFILES, "w") as f: 80 | f.write(''.join(lines)) 81 | 82 | 83 | def do_connect(ssid, password): 84 | wlan_sta.active(True) 85 | if wlan_sta.isconnected(): 86 | return None 87 | print('Trying to connect to %s...' % ssid) 88 | wlan_sta.connect(ssid, password) 89 | for retry in range(200): 90 | connected = wlan_sta.isconnected() 91 | if connected: 92 | break 93 | time.sleep(0.1) 94 | print('.', end='') 95 | if connected: 96 | print('\nConnected. Network config: ', wlan_sta.ifconfig()) 97 | 98 | else: 99 | print('\nFailed. Not Connected to: ' + ssid) 100 | return connected 101 | 102 | 103 | def send_header(client, status_code=200, content_length=None ): 104 | client.sendall("HTTP/1.0 {} OK\r\n".format(status_code)) 105 | client.sendall("Content-Type: text/html\r\n") 106 | if content_length is not None: 107 | client.sendall("Content-Length: {}\r\n".format(content_length)) 108 | client.sendall("\r\n") 109 | 110 | 111 | def send_response(client, payload, status_code=200): 112 | content_length = len(payload) 113 | send_header(client, status_code, content_length) 114 | if content_length > 0: 115 | client.sendall(payload) 116 | client.close() 117 | 118 | 119 | def handle_root(client): 120 | wlan_sta.active(True) 121 | ssids = sorted(ssid.decode('utf-8') for ssid, *_ in wlan_sta.scan()) 122 | send_header(client) 123 | client.sendall("""\ 124 | 125 |

126 | 127 | Wi-Fi Client Setup 128 | 129 |

130 |
131 | 132 | 133 | """) 134 | while len(ssids): 135 | ssid = ssids.pop(0) 136 | client.sendall("""\ 137 | 138 | 141 | 142 | """.format(ssid)) 143 | client.sendall("""\ 144 | 145 | 146 | 147 | 148 | 149 |
139 | {0} 140 |
Password:
150 |

151 | 152 |

153 |
154 |

 

155 |
156 |
157 | 158 | Your ssid and password information will be saved into the 159 | "%(filename)s" file in your ESP module for future usage. 160 | Be careful about security! 161 | 162 |
163 |
164 |

165 | Some useful infos: 166 |

167 | 177 | 178 | """ % dict(filename=NETWORK_PROFILES)) 179 | client.close() 180 | 181 | 182 | def handle_configure(client, request): 183 | match = ure.search("ssid=([^&]*)&password=(.*)", request) 184 | 185 | if match is None: 186 | send_response(client, "Parameters not found", status_code=400) 187 | return False 188 | # version 1.9 compatibility 189 | try: 190 | ssid = match.group(1).decode("utf-8").replace("%3F", "?").replace("%21", "!") 191 | password = match.group(2).decode("utf-8").replace("%3F", "?").replace("%21", "!") 192 | except Exception: 193 | ssid = match.group(1).replace("%3F", "?").replace("%21", "!") 194 | password = match.group(2).replace("%3F", "?").replace("%21", "!") 195 | 196 | if len(ssid) == 0: 197 | send_response(client, "SSID must be provided", status_code=400) 198 | return False 199 | 200 | if do_connect(ssid, password): 201 | response = """\ 202 | 203 |
204 |

205 |

206 | 207 | ESP successfully connected to WiFi network %(ssid)s. 208 | 209 |

210 |

211 |
212 | 213 | """ % dict(ssid=ssid) 214 | send_response(client, response) 215 | time.sleep(1) 216 | wlan_ap.active(False) 217 | try: 218 | profiles = read_profiles() 219 | except OSError: 220 | profiles = {} 221 | profiles[ssid] = password 222 | write_profiles(profiles) 223 | 224 | time.sleep(5) 225 | 226 | return True 227 | else: 228 | response = """\ 229 | 230 |
231 |

232 | 233 | ESP could not connect to WiFi network %(ssid)s. 234 | 235 |

236 |

237 |
238 | 239 |
240 |
241 | 242 | """ % dict(ssid=ssid) 243 | send_response(client, response) 244 | return False 245 | 246 | 247 | def handle_not_found(client, url): 248 | send_response(client, "Path not found: {}".format(url), status_code=404) 249 | 250 | 251 | def stop(): 252 | global server_socket 253 | 254 | if server_socket: 255 | server_socket.close() 256 | server_socket = None 257 | 258 | 259 | def start(port=80): 260 | global server_socket 261 | 262 | addr = socket.getaddrinfo('0.0.0.0', port)[0][-1] 263 | 264 | stop() 265 | 266 | wlan_sta.active(True) 267 | wlan_ap.active(True) 268 | 269 | wlan_ap.config(essid=ap_ssid, password=ap_password, authmode=ap_authmode) 270 | 271 | server_socket = socket.socket() 272 | server_socket.bind(addr) 273 | server_socket.listen(1) 274 | 275 | print('Connect to WiFi ssid ' + ap_ssid + ', default password: ' + ap_password) 276 | print('and access the ESP via your favorite web browser at 192.168.4.1.') 277 | print('Listening on:', addr) 278 | 279 | while True: 280 | if wlan_sta.isconnected(): 281 | wlan_ap.active(False) 282 | return True 283 | 284 | client, addr = server_socket.accept() 285 | print('client connected from', addr) 286 | try: 287 | client.settimeout(5.0) 288 | 289 | request = b"" 290 | try: 291 | while "\r\n\r\n" not in request: 292 | request += client.recv(512) 293 | except OSError: 294 | pass 295 | 296 | # Handle form data from Safari on macOS and iOS; it sends \r\n\r\nssid=&password= 297 | try: 298 | request += client.recv(1024) 299 | print("Received form data after \\r\\n\\r\\n(i.e. from Safari on macOS or iOS)") 300 | except OSError: 301 | pass 302 | 303 | print("Request is: {}".format(request)) 304 | if "HTTP" not in request: # skip invalid requests 305 | continue 306 | 307 | # version 1.9 compatibility 308 | try: 309 | url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).decode("utf-8").rstrip("/") 310 | except Exception: 311 | url = ure.search("(?:GET|POST) /(.*?)(?:\\?.*?)? HTTP", request).group(1).rstrip("/") 312 | print("URL is {}".format(url)) 313 | 314 | if url == "": 315 | handle_root(client) 316 | elif url == "configure": 317 | handle_configure(client, request) 318 | else: 319 | handle_not_found(client, url) 320 | 321 | finally: 322 | client.close() 323 | --------------------------------------------------------------------------------