Skip to content

Readme #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PYTHONPATH=src
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: MicroPython CI

on:
push:
branches:
- main
pull_request:
branches:
- "*"

jobs:
test:
name: Run Tests and Linting
runs-on: ubuntu-latest

steps:
# Step 1: Checkout the repository
- name: Checkout Code
uses: actions/checkout@v3

# Step 2: Set up Python
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11

# Step 3: Install dependencies
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt

# Step 4: Run linting
- name: Run Linting
run: |
ruff format src tests
ruff check src tests --fix --exit-zero --line-length 100 --target-version py38

# Step 5: Run tests
- name: Run Tests
run: |
PYTHONPATH=src pytest -svv
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
__pycache__
.mypy_cache
.coverage
htmlcov
wifi.dat
19 changes: 19 additions & 0 deletions .justfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
run-test:
PYTHONPATH=src pytest -svv
run-test-filter TEST:
PYTHONPATH=src pytest -svv -k "{{TEST}}"
lint:
ruff format src tests
ruff check src tests --fix --exit-zero --line-length 100 --target-version py38

install-requirement:
pip install -r requirements.txt
list:
mpremote ls
upload:
mpremote mkdir :lib || echo "Directory already exists."
mpremote cp src/wifi_manager/*.py :lib/wifi_manager/
# just to check if the files are uploaded
mpremote ls :lib/wifi_manager/
mount_and_run:
mpremote mount src/ run src/main.py
20 changes: 20 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"editor.rulers": [
100,
],
"[python]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "charliermarsh.ruff",
},
"pylint.enabled": false,
"ruff.lint.enable": true,
"ruff.lint.select": ["E501"],
"python.testing.pytestArgs": [
"tests",
],
"python.envFile": "${workspaceFolder}/.env",
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true,
"editor.fontSize": 14,
"flake8.enabled": false,
}
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
MIT License

Copyright (c) 2025 Youngmin Kim
Copyright (c) 2021 Igor Ferreira

Permission is hereby granted, free of charge, to any person obtaining a copy
Expand Down
43 changes: 11 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,29 @@
# WiFi Manager

WiFi Manager for ESP8266 and ESP32 using MicroPython. It might work in any other board since it only uses standard MicroPython libraries, but that's not tested.
WiFi Manager for ESP32 using MicroPython. It might work in any other board since it only uses standard MicroPython libraries, but that's not tested.

![ESP8266](https://img.shields.io/badge/ESP-8266-000000.svg?longCache=true&style=flat&colorA=CC101F)
![ESP32](https://img.shields.io/badge/ESP-32-000000.svg?longCache=true&style=flat&colorA=CC101F)

## What's new?

Version 2.0 comes with some improvements:
- Better documentation (I hope);
- Some aesthetical changes in the code;
- Removal of unnecessary messages;
- Removal of the ability to set the ip address (to avoid unexpected problems);
- Option to reboot after network configuration (needs improvement);

## Wishlist

- [ ] Allow user to customize CSS;
- [ ] Custom fields for extra configuration (like mqtt server, etc)
- [ ] Turn this into a real python library with the option to be installed using pip;
![CI](https://github.com/ymkim92/micropython-wifi_manager/actions/workflows/ci.yml/badge.svg)

## How It Works

- When your device starts up, it will try to connect to a previously saved wifi.
- If there is no saved network or if it fails to connect, it will start an access point;
- By connecting to the access point and going to the address 192.168.4.1 you be able to find your network and input the credentials;
- By connecting to the access point and going to the address `192.168.4.1` you be able to find your network and input the credentials;
- It will try to connect to the desired network, and if it's successful, it will save the credentials for future usage;
- Be aware that the wifi credentials will be saved in a plain text file, and this can be a security fault depending on your application;

## Installation and Usage

I use `justfile` to upload the scripts to a target device:

```python
# Download the "wifi_manager.py" file to your device;

# Import the library:
from wifi_manager import WifiManager

# Initialize it
wm = WifiManager()

# By default the SSID is WiFiManager and the password is wifimanager.
# You can customize the SSID and password of the AP for your needs:
wm = WifiManager(ssid="my ssid",password="my password")
```sh
$ just upload
```

# Start the connection:
wm.connect()
You may want to use `mount` of mpremote for test or debugging:
```sh
$ just mount_and_run
```

## Methods
Expand Down
134 changes: 134 additions & 0 deletions just.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
_just() {
local i cur prev opts cmds
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
cmd=""
opts=""

for i in ${COMP_WORDS[@]}
do
case "${i}" in
"$1")
cmd="just"
;;

*)
;;
esac
done

case "${cmd}" in
just)
opts=" -n -q -u -v -e -l -h -V -f -d -c -s --check --yes --dry-run --highlight --no-dotenv --no-highlight --quiet --shell-command --clear-shell-args --unsorted --unstable --verbose --changelog --choose --dump --edit --evaluate --fmt --init --list --summary --variables --help --version --chooser --color --command-color --dump-format --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --completions --show --dotenv-filename --dotenv-path <ARGUMENTS>... "
if [[ ${cur} == -* ]] ; then
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
elif [[ ${COMP_CWORD} -eq 1 ]]; then
local recipes=$(just --summary 2> /dev/null)

if echo "${cur}" | \grep -qF '/'; then
local path_prefix=$(echo "${cur}" | sed 's/[/][^/]*$/\//')
local recipes=$(just --summary 2> /dev/null -- "${path_prefix}")
local recipes=$(printf "${path_prefix}%s\t" $recipes)
fi

if [[ $? -eq 0 ]]; then
COMPREPLY=( $(compgen -W "${recipes}" -- "${cur}") )
return 0
fi
fi
case "${prev}" in

--chooser)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--color)
COMPREPLY=($(compgen -W "auto always never" -- "${cur}"))
return 0
;;
--command-color)
COMPREPLY=($(compgen -W "black blue cyan green purple red yellow" -- "${cur}"))
return 0
;;
--dump-format)
COMPREPLY=($(compgen -W "just json" -- "${cur}"))
return 0
;;
--list-heading)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--list-prefix)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--justfile)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-f)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--set)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--shell)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--shell-arg)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--working-directory)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-d)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--command)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-c)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--completions)
COMPREPLY=($(compgen -W "zsh bash fish powershell elvish" -- "${cur}"))
return 0
;;
--show)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
-s)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--dotenv-filename)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
--dotenv-path)
COMPREPLY=($(compgen -f "${cur}"))
return 0
;;
*)
COMPREPLY=()
;;
esac
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
return 0
;;

esac
}

complete -F _just -o bashdefault -o default just
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[tool.ruff]
line-length = 100
# keep normal rules and add this extra one.
lint.extend-select = ["E501"]
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
pythonpath = src
addopts = -svv
3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pytest
mpremote
ruff
6 changes: 3 additions & 3 deletions main.py → src/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from wifi_manager import WifiManager
from wifi_manager.manager import WifiManager
import utime

# Example of usage
Expand All @@ -8,7 +8,7 @@

while True:
if wm.is_connected():
print('Connected!')
print("Connected!")
else:
print('Disconnected!')
print("Disconnected!")
utime.sleep(10)
Empty file added src/wifi_manager/__init__.py
Empty file.
67 changes: 67 additions & 0 deletions src/wifi_manager/manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import network
import time
from wifi_manager.network_utils import read_credentials
from wifi_manager.webserver import WebServer


class WifiManager:
def __init__(self, ssid="WifiManager", password="wifimanager", reboot=True, debug=False):
self.wlan_sta = network.WLAN(network.STA_IF)
self.wlan_sta.active(True)
self.wlan_ap = network.WLAN(network.AP_IF)

if len(ssid) > 32:
raise Exception("The SSID cannot be longer than 32 characters.")
else:
self.ap_ssid = ssid
if len(password) < 8:
raise Exception("The password cannot be less than 8 characters long.")
else:
self.ap_password = password

self.ap_authmode = 3
self.wifi_credentials = "wifi.dat"
self.wlan_sta.disconnect()
self.reboot: bool = reboot
self.debug: bool = debug

def connect(self):
if self.wlan_sta.isconnected():
return
profiles = read_credentials(self.wifi_credentials, self.debug)
for ssid, *_ in self.wlan_sta.scan():
ssid = ssid.decode("utf-8")
if ssid in profiles:
password = profiles[ssid]
if self.wifi_connect(ssid, password):
return
print("Could not connect to any WiFi network. Starting the configuration portal...")
self.web_server()

def disconnect(self):
if self.wlan_sta.isconnected():
self.wlan_sta.disconnect()

def is_connected(self):
return self.wlan_sta.isconnected()

def get_address(self):
return self.wlan_sta.ifconfig()

def wifi_connect(self, ssid, password):
print("Trying to connect to:", ssid)
self.wlan_sta.connect(ssid, password)
for _ in range(100):
if self.wlan_sta.isconnected():
print("\nConnected! Network information:", self.wlan_sta.ifconfig())
return True
else:
print(".", end="")
time.sleep_ms(100)
print("\nConnection failed!")
self.wlan_sta.disconnect()
return False

def web_server(self):
server = WebServer(self)
server.run()
Loading