├── screenshot.png
├── README.md
├── html
├── templates
│ ├── wifi-add.tpl
│ ├── wifi-saved.tpl
│ ├── wifi-status.tpl
│ └── wifi-scan.tpl
├── main.css
└── index.html
├── main.py
├── boot.py
├── LICENSE
├── wifi_database.py
└── configserver.py
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carstenblt/micropython-configserver/HEAD/screenshot.png
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # micropython-configserver
2 | Captive portal for micropython including a dumb DNS server and a webserver to configure wifi networks.
3 |
--------------------------------------------------------------------------------
/html/templates/wifi-add.tpl:
--------------------------------------------------------------------------------
1 | {% args req %}
2 | {% set import wifi_database %}
3 | {% if wifi_database.connect_and_add_wifi(req.form['essid'], req.form['password']) is True %}
4 | OK
5 | {% else %}
6 | ERROR
7 | {% endif %}
--------------------------------------------------------------------------------
/html/templates/wifi-saved.tpl:
--------------------------------------------------------------------------------
1 | {% set import wifi_database %}
2 | {% for mynetwork in wifi_database.iter_wifis() %}
3 |
6 | {% endfor %}
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | import uasyncio as asyncio
2 | import configserver
3 |
4 |
5 | loop = asyncio.get_event_loop(5)
6 | loop.create_task(configserver.dns_server())
7 | loop.create_task(app.get_task(host='0.0.0.0'))
8 | loop.run_forever()
--------------------------------------------------------------------------------
/boot.py:
--------------------------------------------------------------------------------
1 | import gc
2 | import webrepl
3 | import network
4 | import wifi_database
5 | import time
6 | gc.collect()
7 |
8 | sta_if = network.WLAN(network.STA_IF)
9 | sta_if.active(True)
10 |
11 | for wifi in wifi_database.iter_wifis():
12 | sta_if.connect(wifi[0], wifi[1])
13 | for i in range(50):
14 | time.sleep_ms(100)
15 | if sta_if.isconnected():
16 | wifi_database.active_wifi = wifi[0]
17 | break
18 | if wifi_database.active_wifi is not None:
19 | break
20 | if wifi_database.active_wifi is None:
21 | sta_if.active(False)
22 |
23 | webrepl.start()
24 | gc.collect()
--------------------------------------------------------------------------------
/html/templates/wifi-status.tpl:
--------------------------------------------------------------------------------
1 | {% set import network %}
2 | {% set import wifi_database %}
3 | {% if network.WLAN(network.STA_IF).isconnected() is True and wifi_database.active_wifi is not None %}
4 | {% set quality_bars = 0 %}
5 | {% for mynetwork in network.WLAN(network.STA_IF).scan() %}
6 | {% if mynetwork[0].decode('utf-8') == wifi_database.active_wifi %}
7 | {% set quality_bars = round(5 * (0 if mynetwork[3] < -100 else (1 if mynetwork[3] > -50 else 2e-2 * (mynetwork[3] + 100)))) %}
8 | {% endif %}
9 | {% endfor %}
10 | {{wifi_database.active_wifi}}
11 | {% for i in range(quality_bars) %}I{% endfor %}{% for i in range(5-quality_bars) %}I{% endfor %}
12 | {% else %}
13 | ERROR
14 | {% endif %}
--------------------------------------------------------------------------------
/html/templates/wifi-scan.tpl:
--------------------------------------------------------------------------------
1 | {% set import network %}
2 | {% set authmodes = ['Open', 'WEP', 'WPA', 'WPA2', 'WPA/WPA2', 'WPA/WPA2'] %}
3 | {% set network.WLAN(network.STA_IF).active(True) %}
4 | {% for mynetwork in sorted(network.WLAN(network.STA_IF).scan(), key=lambda x: x[3], reverse=True) %}
5 | {% set quality_bars = round(5 * (0 if mynetwork[3] < -100 else (1 if mynetwork[3] > -50 else 2e-2 * (mynetwork[3] + 100)))) %}
6 |
7 |
{{mynetwork[0].decode('utf-8')}}
8 |
{{authmodes[mynetwork[4]]}}
9 |
{% for i in range(quality_bars) %}I{% endfor %}{% for i in range(5-quality_bars) %}I{% endfor %}
10 |
13 |
14 | {% endfor %}
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Carsten B. L. Tschense
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 |
--------------------------------------------------------------------------------
/html/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: sans-serif;
3 | font-size: 14px;
4 | margin: 0;
5 | padding: 0;
6 | text-align: center; /* old IE fix */
7 | }
8 |
9 | #content {
10 | width: 500px;
11 | height: 500px;
12 | position: relative;
13 | margin-left: auto;
14 | margin-right: auto;
15 | margin-top: 10px;
16 | text-align: left;
17 | }
18 |
19 | .empty_bars {
20 | color: #aaa;
21 | }
22 |
23 | #current_network {
24 | width: 490px;
25 | height: 40px;
26 | border: 1px solid black;
27 | margin-top: 5px;
28 | margin-bottom: 20px;
29 | padding: 5px;
30 | background-color: #dddddd;
31 | line-height: 45px;
32 | vertical-align: middle;
33 | }
34 |
35 | #current_name {
36 | font-weight: bold;
37 | display: inline-block;
38 | width: 400px;
39 | font-size: 1.5em;
40 | margin-left: 20px;
41 | line-height: normal;
42 | }
43 |
44 | #current_strength {
45 | width: 50px;
46 | display: inline-block;
47 | font-weight: bold;
48 | font-size: 1.5em;
49 | line-height: normal;
50 | }
51 |
52 | #saved_networks {
53 | width: 490px;
54 | height: 100px;
55 | border: 1px solid black;
56 | margin-top: 10px;
57 | padding: 5px;
58 | overflow: scroll;
59 | }
60 |
61 | #networks {
62 | width: 490px;
63 | height: 200px;
64 | border: 1px solid black;
65 | margin-top: 10px;
66 | margin-bottom: 20px;
67 | padding: 5px;
68 | overflow: scroll;
69 | }
70 |
71 | .network {
72 | height: 40px;
73 | background-color: #ccddee;
74 | padding-left: 10px;
75 | margin-bottom: 5px;
76 | line-height: 45px;
77 | vertical-align: middle;
78 | }
79 |
80 | .network:hover {
81 | background-color: #aabbcc;
82 | }
83 |
84 | .network:nth-child(odd) {
85 | background-color: #dddddd;
86 | }
87 |
88 | .network:nth-child(odd):hover {
89 | background-color: #aabbcc;
90 | }
91 |
92 | .network_name {
93 | font-weight: bold;
94 | display: inline-block;
95 | width: 300px;
96 | font-size: 1.5em;
97 | line-height: normal;
98 | }
99 |
100 | .network_encryption {
101 | width: 100px;
102 | display: inline-block;
103 | line-height: normal;
104 | }
105 |
106 | .network_strength {
107 | width: 30px;
108 | display: inline-block;
109 | font-weight: bold;
110 | line-height: normal;
111 | }
112 |
113 | .network_connect {
114 | display: none;
115 | width: 400px;
116 | margin-left: 30px;
117 | }
--------------------------------------------------------------------------------
/wifi_database.py:
--------------------------------------------------------------------------------
1 | import network
2 | import time
3 |
4 | active_wifi = None
5 | database_file = "wifi-database"
6 |
7 | def get_wifi(essid):
8 | try:
9 | myfile = open(database_file, "r")
10 | except OSError:
11 | return None
12 |
13 | line = myfile.readline()
14 | password = None
15 | while line != '':
16 | if line.strip() == essid:
17 | password = myfile.readline().strip()
18 | break
19 | myfile.readline()
20 | line = myfile.readline()
21 | myfile.close()
22 | return password
23 |
24 | def add_wifi(essid, password):
25 | try:
26 | myfile = open(database_file, "r+")
27 | except OSError:
28 | myfile = open(database_file, "w+")
29 |
30 | line = myfile.readline()
31 | content = None
32 | while line != '':
33 | if line.strip() == essid:
34 | position = myfile.tell()
35 | content = myfile.readlines()
36 | content[0] = password + '\n'
37 | myfile.seek(position)
38 | myfile.writelines(content)
39 | break
40 | myfile.readline()
41 | line = myfile.readline()
42 | if content == None:
43 | myfile.write(essid + '\n' + password + '\n')
44 | myfile.close()
45 |
46 | def remove_wifi(essid):
47 | try:
48 | myfile = open(database_file, "r+")
49 | except OSError:
50 | return
51 |
52 | line = myfile.readline()
53 | while line != '':
54 | if line.strip() == essid:
55 | position = myfile.tell() - len(line)
56 | myfile.readline()
57 | content = myfile.readlines()
58 | myfile.seek(position)
59 | myfile.writelines(content)
60 | myfile.truncate()
61 | break
62 | myfile.readline()
63 | line = myfile.readline()
64 | if content == None:
65 | myfile.write(essid + '\n' + password + '\n')
66 | myfile.close()
67 |
68 | def iter_wifis():
69 | try:
70 | myfile = open(database_file, "r")
71 | except OSError:
72 | return None
73 |
74 | line = myfile.readline()
75 | while line != '':
76 | yield (line.strip(), myfile.readline().strip())
77 | line = myfile.readline()
78 | myfile.close()
79 |
80 | def connect_and_add_wifi(essid, password):
81 | sta = network.WLAN(network.STA_IF)
82 | sta.active(True)
83 | sta.connect(essid, password)
84 | for i in range(50):
85 | time.sleep_ms(100)
86 | if sta.isconnected():
87 | add_wifi(essid, password)
88 | active_wifi = essid
89 | return True
90 | sta.active(False)
91 | return False
92 |
93 |
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Config
4 |
5 |
6 |
79 |
80 |
81 |
82 |
Current network
83 |
84 |
Available networks
85 |
86 | loading ...
87 |
88 |
Saved networks
89 |
90 | loading ...
91 |
92 |
93 |
94 |