Lab #4: Používateľské nastavenia a pripojenie do internetu

o pripojení do internetu, vytvorení vlastného hotspot-u, synchronizácii času, externom module reálneho času, a stiahnutí rozširujúcich modulov pre MicroPython, dataclasses

Motivácia

  • o pouzivatelskych nastaveniach
  • o wifi
  • o instalacii rozsirujucich balikov

Ciele

  1. naučiť sa pripojiť mikrokontrolér k WiFi v jazyku MicroPython

  2. naučiť sa nainštalovať rozširujúce moduly na MicroPython zariadenie

Postup

Používateľské nastavenia

Používateľ musí mať možnosť zmeniť správanie chytrého zariadenia. Môže ísť napríklad o nastavenie pripojenia k WiFi, jednotiek teploty alebo intervalu merania. Na rozdiel od statickej konfigurácie v module constants.py môže používateľské nastavenia meniť používateľ za behu zariadenia.

Na reprezentáciu nastavení vytvoríme dátovú triedu s názvom Settings (pozri Dátové triedy). Všetky dátové triedy sa budú nachádzať v balíku models, kde je aj trieda BaseModel – predok každej dátovej triedy. Nastavenia budú uložené v súbore settings.json a pri každom zapnutí zariadenia sa z neho načítajú.

Task

Vytvorte triedu Settings, ktorá bude reprezentovať model pre prácu s používateľskými nastaveniami.

Triedu vytvorte v module settings.py balíka models. Momentálne nás bude v nastaveniach zaujímať:

  • nastavenie pripojenia k WiFi (názov siete, heslo a časový limit pripojenia v sekundách),
  • jednotka merania teploty, a
  • identifikátor zariadenia, ktorý ho odlíši od ostatných zariadení – využijeme ho neskôr pri komunikácii so svetom.

Trieda Settings bude zatiaľ vyzerať takto:

import binascii
import machine

from hw.mixins.sensors.temperature import TemperatureUnit
from models.base import BaseModel, Field


class WiFi(BaseModel):
    ssid: str = Field(default=None, type=str, optional=True)
    password: str = Field(default=None, type=str, optional=True)
    timeout: int = 10


class Settings(BaseModel):
    wifi: WiFi = Field(default_factory=WiFi, type=WiFi)
    units: str = TemperatureUnit.METRIC
    device_id: str = binascii.hexlify(machine.unique_id()).decode()

Predvolenou hodnotou identifikátora zariadenia je sériové číslo čipu mikrokontroléra, ktoré vráti funkcia machine.unique_id(). Každé zariadenie tak má jedinečný identifikátor aj vtedy, keď ho používateľ nezmení. Vy si však doň neskôr nastavíte svoj univerzitný študentský identifikátor (jedinečný login), aby bolo zrejmé, ktoré zariadenie je vaše.

Význam parametrov funkcie Field() (napr. prečo premenná wifi používa parameter default_factory) nájdete v kapitole Dátové triedy.

Task

Overte funkčnosť vytvorenej triedy Settings.

Triedu môžete vyskúšať v REPL jazyka MicroPython. Vytvorte predvolené nastavenia, zmeňte jednotku teploty a vytvorte nastavenia aj zo slovníka, ako keby boli načítané zo súboru settings.json:

>>> from models.settings import Settings
>>> settings = Settings()
>>> settings
Settings(wifi=WiFi(ssid=None,password=None,timeout=10),units='metric',device_id='e6614c311b7e6a2b')
>>> settings.units = 'imperial'
>>> settings.units
'imperial'
>>> settings = Settings(**{'wifi': {'ssid': 'my-network', 'password': 'secret'}})
>>> settings.wifi.ssid
'my-network'

Ak ste postupovali správne, predvolené nastavenia budú mať prázdne nastavenie WiFi, jednotky "metric" a identifikátor zariadenia zhodný so sériovým číslom čipu (na vašom zariadení bude iný ako v ukážke) a slovník s nastavením WiFi sa prevedie na objekt triedy WiFi.

Task

Vytvorte validátor s názvom check_units() pre členskú premennú units, ktorý overí, či je zadávaná jednotka teploty platná.

Validátor je metóda, ktorá sa spustí pri priraďovaní hodnoty do členskej premennej. Predtým, ako sa hodnota priradí, sa najprv overí. Ak nie je platná, validátor vyvolá výnimku. V opačnom prípade sa hodnota priradí.

Validátor nemusíte volať sami – trieda BaseModel ho spustí automaticky pri každom priradení hodnoty do danej členskej premennej, aj pri vytváraní objektu.

Validátor na overenie hodnoty členskej premennej units môže vyzerať nasledovne:

from models.base import validator

# ...

class Settings(BaseModel):
    # ...

    @validator('units')
    def check_units(self, value):
        if value not in (
            TemperatureUnit.METRIC,
            TemperatureUnit.IMPERIAL,
            TemperatureUnit.STANDARD
        ):
            raise ValueError(f'Unknown temperature unit "{value}".')

Task

Overte vytvorený validátor.

Validátor vyskúšajte v REPL jazyka MicroPython tak, že do premennej units priradíte neplatnú jednotku teploty:

>>> from models.settings import Settings
>>> settings = Settings()
>>> settings.units = 'kelvin'
Traceback (most recent call last):
  ...
ValueError: Unknown temperature unit "kelvin".
>>> settings.units
'metric'

Ak ste postupovali správne, validátor odmietne neplatnú jednotku teploty a premenná units si ponechá pôvodnú hodnotu.

Task

Podobným spôsobom vytvorte validátor positive() pre členskú premennú timeout triedy WiFi, ktorý overí, či je jej hodnota kladná.

Ak hodnota nie je kladná, validátor vyvolá výnimku ValueError so správou "Timeout must be positive.".

Ak ste postupovali správne, validátor odmietne nulovú aj zápornú hodnotu a premenná timeout si ponechá pôvodnú hodnotu:

>>> from models.settings import Settings
>>> settings = Settings()
>>> settings.wifi.timeout = 0
Traceback (most recent call last):
  ...
ValueError: Timeout must be positive.
>>> settings.wifi.timeout
10

Task

Vytvorte validátor check_device_id() pre členskú premennú device_id, ktorý overí, či je identifikátor zariadenia platný.

Identifikátor zariadenia bude neskôr súčasťou adries, na ktoré bude zariadenie posielať údaje. Preto nesmie byť prázdny a môže obsahovať len písmená bez diakritiky, číslice a znaky - a _. Ak hodnota tieto podmienky nespĺňa, validátor vyvolá výnimku ValueError so správou "Invalid device ID \"<hodnota>\".".

Ak ste postupovali správne, validátor odmietne neplatný identifikátor a premenná device_id si ponechá pôvodnú hodnotu:

>>> from models.settings import Settings
>>> settings = Settings()
>>> settings.device_id = 'ab123cd'
>>> settings.device_id = 'ab 123/cd'
Traceback (most recent call last):
  ...
ValueError: Invalid device ID "ab 123/cd".
>>> settings.device_id
'ab123cd'

Prvé spustenie a obnova výrobných nastavení

Používateľské nastavenia sú pre fungovanie zariadenia veľmi dôležité. Nastavenia budú na zariadení uložené v súbore, čím sa zabezpečí ich perzistencia. Cesta k súboru s používateľskými nastaveniami sa nachádza v konštante SETTINGS_FILE.

Súbor s nastaveniami bude uložený v priečinku DATA_FOLDER. Do neho bude chytré zariadenie ukladať všetky údaje, ktoré vzniknú počas jeho práce, napr. používateľské nastavenia, merania či záznamy (logy).

V tomto kroku vyriešime tieto dôležité situácie:

  • prvé spustenie zariadenia, keď súbor s používateľskými nastaveniami ešte neexistuje,
  • poškodený súbor s nastaveniami, ktorý nie je možné načítať, a
  • ručné vyvolanie obnovy výrobných nastavení.

Task

Vytvorte nový stav s názvom FactoryReset, ktorý vytvorí predvolené nastavenia.

Stav FactoryReset vytvorte v module factory_reset.py balíka states.

V tomto stave vytvoríte predvolené nastavenia a uložíte ich do súboru settings.json v priečinku DATA_FOLDER. Celá cesta k tomuto súboru sa nachádza v konštante SETTINGS_FILE.

Predvolené nastavenia vytvoríte serializáciou inštancie triedy Settings. Ako na to, nájdete v kapitole Dátové triedy.

V tomto stave teda zabezpečte nasledovné:

  1. Ak neexistuje priečinok DATA_FOLDER, tak ho vytvorte.
  2. V súbore s cestou SETTINGS_FILE vytvorte predvolené nastavenia.
  3. Natvrdo reštartujte zariadenie.

Reštart zabezpečí, že sa zariadenie spustí s novými nastaveniami a prejde bežným štartom cez stav Init, ktorý nastavenia zo súboru načíta.

Task

Overte správnosť implementácie stavu FactoryReset.

Stav ešte nie je prepojený so stavom Init, preto ho spustite priamo z REPL jazyka MicroPython. Využijete pri tom to, že kontext dostáva počiatočný stav ako parameter:

>>> from context import Context
>>> from main import register_devices
>>> from states.factory_reset import FactoryReset
>>> Context(register_devices(), FactoryReset).run()

Zariadenie sa po vytvorení nastavení reštartuje, takže sa k nemu bude potrebné znova pripojiť. Potom overte, či súbor s nastaveniami vznikol a čo obsahuje:

>>> from constants import SETTINGS_FILE
>>> print(open(SETTINGS_FILE).read())
{"wifi": {"ssid": null, "password": null, "timeout": 10}, "units": "metric", "device_id": "e6614c311b7e6a2b"}

Ak ste postupovali správne, v priečinku DATA_FOLDER vznikne súbor s predvolenými nastaveniami vo formáte JSON. Poradie kľúčov v súbore sa môže od ukážky líšiť a identifikátor zariadenia bude zhodný so sériovým číslom čipu vášho zariadenia.

Task

Zabezpečte prechod zo stavu Init do stavu FactoryReset na základe podmienok prechodu.

Zariadenie prejde do stavu FactoryReset, ak:

  • neexistuje súbor s nastaveniami,
  • sa súbor s nastaveniami nepodarí načítať z ľubovoľného dôvodu, alebo
  • je pri štarte ovládacie tlačidlo stlačené aspoň tak dlho, ako určuje konštanta LONG_PRESS_DURATION (v milisekundách).

V opačnom prípade bude pokračovať stavom Measurement.

Stavový diagram zariadenia s novým stavom sa nachádza na obrázku 1.

---
# spolocna konfiguracia stavovych diagramov THSensor; vklada sa ako prvy
# riadok bloku ```mermaid, za nim nasleduje "flowchart LR" a samotny diagram
config:
  flowchart:
    curve: basis
    nodeSpacing: 40
    rankSpacing: 60
  themeVariables:
    fontSize: 20px
  themeCSS: ".edgeLabel, .edgeLabel p, .labelBkg { background-color: var(--bs-body-bg) !important; }"
---

flowchart LR
    start@{ shape: f-circ } --> init(Init)
    init --> measurement(Measurement)
    init -- "<code>SVC_PIN</code><br>grounded" --> terminal("Service<br>Terminal")
    init -- "no/faulty<br><code>settings.json</code> /<br>long button press" --> reset("Factory<br>Reset")
    measurement --> sleep(Sleep)
    sleep --> sleepEnd@{ shape: fr-circ }
    terminal --> terminalEnd@{ shape: fr-circ }
    reset --> resetEnd@{ shape: fr-circ }

    %% spolocne styly stavovych diagramov THSensor (farby podla
    %% images/thsensor-state.diagram.complete.png); vklada sa na koniec diagramu
    %% a stavy sa k nim priradia cez "class <stavy> <styl>"
    classDef state fill:#cfe2ff,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef service fill:#cbc3a3,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef reset fill:#ffd6f5,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef config fill:#fddba7,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef error fill:#ffd7d9,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef ota fill:#c3f5c8,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef initial fill:#000,stroke:#fff,stroke-width:2px
    classDef final fill:#fff,stroke:#000,stroke-width:2px

    class init,measurement,sleep state
    class terminal service
    class reset reset
    class start initial
    class sleepEnd,terminalEnd,resetEnd final

Obr. 1: Stavový diagram zariadenia so stavom FactoryReset

Ovládacie tlačidlo je v správcovi zariadení dostupné pod aliasom ALIAS_CONTROL_BTN. Ak sa toto tlačidlo vo vašom správcovi zariadení nenachádza, zaregistrujte ho (je pripojené na pin BTN_PIN).

Diagnostická LED dióda svieti pri zapnutí nazeleno. Ak bude používateľ pri zapnutí držať ovládacie tlačidlo stlačené dlhšie, ako určuje konštanta LONG_PRESS_DURATION, zmeňte jej farbu na fialovú (konštanta Color.PURPLE). Vďaka tomu bude používateľ vedieť, že keď tlačidlo uvoľní, naozaj sa spustí obnova výrobných nastavení.

Nastavenia zo súboru deserializujte a výsledný objekt typu Settings uložte do členskej premennej kontextu settings. Vďaka tomu budú nastavenia dostupné v celom zariadení prostredníctvom kontextu. Členskú premennú settings vytvorte už v konštruktore kontextu s hodnotou None, aby existovala aj pred načítaním nastavení.

Pri implementácii nezabudnite, že kontrola prechodu do stavu FactoryReset sa musí vykonať až po kontrole servisného tlačidla, aby sa do servisného terminálu dalo dostať aj bez platných nastavení.

Task

Overte vytvorenú implementáciu.

Pri overovaní vyskúšajte všetky situácie, ku ktorým môže pri štarte zariadenia dôjsť:

  1. súbor s nastaveniami neexistuje (zmažte ho) – zariadenie prejde do stavu FactoryReset, vytvorí predvolené nastavenia a po reštarte pokračuje stavom Measurement,
  2. súbor s nastaveniami je poškodený (napr. doň zapíšte neplatný JSON alebo neznámy kľúč) – zariadenie prejde do stavu FactoryReset,
  3. súbor s nastaveniami je platný – zariadenie pokračuje stavom Measurement,
  4. ovládacie tlačidlo je pri štarte stlačené dlhšie ako LONG_PRESS_DURATION – diagnostická LED dióda sa rozsvieti nafialovo a po uvoľnení tlačidla zariadenie prejde do stavu FactoryReset,
  5. servisný pin SVC_PIN je uzemnený a súbor s nastaveniami neexistuje – zariadenie prejde do servisného terminálu.

Príkaz settings

K používateľským nastaveniam bude potrebné pristupovať aj zo servisného terminálu. Technik musí mať možnosť hodnoty nastavení zobraziť a zmeniť, zmenené nastavenia uložiť do súboru a v prípade potreby ich zo súboru znova načítať.

V tomto kroku preto vytvoríme príkaz settings s niekoľkými podpríkazmi. Zmeny vykonané v servisnom termináli sa prejavia v nastaveniach zariadenia hneď, do súboru sa však uložia až podpríkazom na uloženie. Technik tak môže zmeniť viacero hodnôt naraz, alebo neuložené zmeny zahodiť opätovným načítaním nastavení zo súboru.

Task

V module commands/settings.py vytvorte triedu Settings, ktorá bude reprezentovať príkaz settings.

O tejto triede bude platiť:

  • bude potomkom triedy BaseCommand,
  • názov príkazu bude "settings",
  • opis príkazu bude "Manage user settings.",
  • prvým parametrom príkazu bude názov podpríkazu (show, get, set, save alebo load) a ďalšie parametre dostane vybraný podpríkaz,
  • ak príkaz nedostane žiadny parameter alebo dostane neznámy podpríkaz, vypíše správu "Error: Wrong usage."

Príkaz zaregistrujte rovnako ako ostatné príkazy.

Task

Rozšírte príkaz settings o podpríkaz show, ktorý zobrazí aktuálnu podobu používateľských nastavení.

Výstup príkazu môže vyzerať nasledovne:

> settings show
wifi:
  ssid: "iot-lab"
  password: "Secret.Password"
  timeout: 10
units: "metric"
device_id: "ab123cd"

Task

Rozšírte príkaz settings o podpríkaz get, ktorý vráti aktuálnu hodnotu nastavenia na základe jeho kľúča.

Kľúč bude reprezentovaný ako cesta k požadovanej hodnote v JSON štruktúre nastavení, pričom jednotlivé úrovne sa oddeľujú bodkou (napr. wifi.ssid). Ak daný kľúč neexistuje, príkaz vypíše chybovú správu "Error: Key \"<kľúč>\" not found.". Ak podpríkaz nedostane práve jeden parameter, vypíše správu "Error: Wrong usage.".

Príklad použitia príkazu môže vyzerať nasledovne:

> settings get units
metric
> settings get wifi.ssid
iot-lab
> settings get wifi
ssid: "iot-lab"
password: "Secret.Password"
timeout: 10
> settings get wifi.port
Error: Key "wifi.port" not found.
> settings get
Error: Wrong usage.

Task

Rozšírte príkaz settings o podpríkaz set, pomocou ktorého bude možné nastaviť novú hodnotu pre zadaný kľúč.

Podpríkaz dostane dva parametre: kľúč v rovnakom tvare ako pri podpríkaze get a novú hodnotu. Keďže hodnota zadaná v termináli je vždy reťazec, pred priradením ju preveďte na typ aktuálnej hodnoty nastavenia (napr. na int). Podpríkaz vypíše chybovú správu:

  • "Error: Wrong usage.", ak nedostane práve dva parametre,
  • "Error: Key \"<kľúč>\" not found.", ak daný kľúč neexistuje,
  • "Error: \"<kľúč>\" is a section, not a value.", ak kľúč neukazuje na hodnotu, ale na celú sekciu nastavení,
  • text výnimky, ak nová hodnota nie je platná (napr. ju odmietne validátor).

Zmena sa prejaví len v nastaveniach v pamäti zariadenia. Do súboru sa uloží až podpríkazom save.

Príklad jeho použitia môže vyzerať nasledovne:

> settings set wifi.ssid iot-lab
wifi.ssid = iot-lab
> settings set units imperial
units = imperial
> settings set device_id ab123cd
device_id = ab123cd
> settings set units kelvin
Error: Unknown temperature unit "kelvin".
> settings set wifi iot-lab
Error: "wifi" is a section, not a value.
> settings set wifi.port 80
Error: Key "wifi.port" not found.

Task

Rozšírte príkaz settings o podpríkaz load, ktorý načíta používateľské nastavenia zo súboru vo formáte JSON.

Podpríkaz načíta nastavenia zo súboru SETTINGS_FILE a nahradí nimi aktuálne nastavenia v kontexte. Všetky zmeny, ktoré ešte neboli uložené podpríkazom save, sa tým zahodia. Podpríkaz vypíše:

  • "Settings loaded.", ak sa nastavenia podarilo načítať,
  • "Error: Settings file not found.", ak súbor s nastaveniami neexistuje,
  • text výnimky, ak súbor s nastaveniami nie je možné načítať (napr. je poškodený); aktuálne nastavenia v kontexte sa v takom prípade nezmenia.

Príklad použitia podpríkazu môže vyzerať nasledovne:

> settings set units imperial
units = imperial
> settings load
Settings loaded.
> settings get units
metric

Task

Rozšírte príkaz settings o podpríkaz save, ktorý aktuálnu verziu používateľských nastavení uloží do súboru vo formáte JSON.

Podpríkaz serializuje aktuálne nastavenia z kontextu a uloží ich do súboru SETTINGS_FILE, čím sa prepíše jeho pôvodný obsah. Po úspešnom uložení vypíše správu "Settings saved.". Uložené nastavenia sa použijú pri ďalšom zapnutí zariadenia, prípadne po zavolaní podpríkazu load.

Príklad použitia podpríkazu môže vyzerať nasledovne:

> settings set wifi.ssid iot-lab
wifi.ssid = iot-lab
> settings save
Settings saved.
> settings load
Settings loaded.
> settings get wifi.ssid
iot-lab

Ak ste postupovali správne, zmena hodnoty zostane zachovaná aj po opätovnom načítaní nastavení zo súboru, prípadne po reštarte zariadenia.

Nakoniec si do identifikátora zariadenia nastavte svoje študentské prihlasovacie meno a nastavenia uložte. Vďaka tomu bude vaše zariadenie odlíšiteľné od zariadení ostatných študentov.

WiFi modul

Vývojová doska RPi Pico 2 WH obsahuje aj WiFi modul s čipom CYW43439. Práve pomocou neho budeme komunikovať so svetom a pripojíme chytré zariadenie do internetu.

V jazyku MicroPython sa s WiFi modulom pracuje pomocou triedy network.WLAN. V tomto kroku ju zabalíme do samostatnej triedy, aby sme z WiFi modulu spravili perifériu nášho projektu. Jej schopnosti opisujú mixiny NetworkMixin a WiFiMixin, ktoré sú súčasťou kostry projektu.

Task

Vytvorte triedu CYW43439, ktorá zapúzdri prístup k inštancii triedy network.WLAN na prácu s WiFi modulom.

O tejto triede bude platiť nasledovné:

  • jej implementácia sa bude nachádzať v module cyw43439.py,
  • názov zariadenia bude "CYW43439",
  • opis zariadenia bude "Built-in WiFi module (Raspberry Pi Pico 2 W)",
  • bude implementovať mixiny NetworkMixin a WiFiMixin a bude potomkom triedy BaseDevice,
  • konštruktor bude mať len voliteľný parameter alias – WiFi modul je na doske zapojený napevno, preto mu piny pri vytváraní neodovzdávame,
  • metódy implementovaných mixinov prenechajú volanie inštancii triedy network.WLAN.

Metódy, ktoré treba implementovať, predpisujú mixiny NetworkMixin a WiFiMixin. Väčšina z nich len odovzdá volanie rovnomennej metóde inštancie triedy network.WLAN.

Task

Vytvorené zariadenie zaregistrujte v správcovi zariadení a otestujte ho.

Pri registrácii použite alias ALIAS_WIFI. Zariadenie môžete otestovať priamo z prostredia REPL napr. takto:

>>> from constants import ALIAS_WIFI
>>> from main import register_devices
>>> wifi = register_devices().get(ALIAS_WIFI)
>>> wifi.capabilities()
['NetworkMixin', 'WiFiMixin']
>>> wifi.active(True)
>>> wifi.active()
True
>>> wifi.isconnected()
False
>>> for network in wifi.scan():
...     print(network[0], network[3])
...
b'iot-lab' -52
b'eduroam' -67

Ak ste postupovali správne, WiFi modul sa aktivuje a metóda .scan() vráti zoznam WiFi sietí v okolí. Každá sieť je n-tica, ktorej prvou položkou je názov siete (SSID) a štvrtou sila signálu (RSSI) v dBm.

Príkaz net

Na prácu so sieťovým rozhraním vytvoríme v servisnom termináli samostatný príkaz net. Technikovi umožní zistiť, či je zariadenie pripojené do siete a s akou adresou, vyhľadať dostupné WiFi siete alebo sa do niektorej z nich ručne pripojiť – napríklad aby vyskúšal prihlasovacie údaje skôr, ako ich uloží do nastavení.

Príkaz bude mať niekoľko podpríkazov, pomocou ktorých bude možné sieťové rozhranie ovládať a zistiť jeho stav.

Task

V module commands/net.py vytvorte triedu Net, ktorá bude reprezentovať príkaz net.

O tejto triede bude platiť:

  • bude potomkom triedy BaseCommand,
  • názov príkazu bude "net",
  • opis príkazu bude "Manage network interface.",
  • prvým parametrom príkazu bude názov podpríkazu (scan, connect, disconnect, deactivate alebo stat) a ďalšie parametre dostane vybraný podpríkaz,
  • ak príkaz nedostane žiadny parameter alebo dostane neznámy podpríkaz, vypíše správu "Error: Wrong usage.",
  • sieťové rozhranie si vyžiada od správcu zariadení pomocou aliasu ALIAS_WIFI.

Príkaz zaregistrujte rovnako ako ostatné príkazy.

Task

Vytvorte podpríkaz scan, ktorý vyhľadá WiFi siete v okolí.

Podpríkaz najprv aktivuje sieťové rozhranie (ak ešte nie je aktívne) a potom pomocou metódy .scan() zistí dostupné siete. Pre každú nájdenú sieť vypíše:

  • jej názov (SSID); namiesto prázdneho názvu skrytej siete vypíše (hidden),
  • číslo kanála,
  • silu signálu (RSSI) v dBm,
  • spôsob zabezpečenia: open, WEP, WPA, WPA2 alebo WPA/WPA2.

Význam jednotlivých položiek, ktoré metóda .scan() pre každú sieť vráti, nájdete v kapitole Práca s WiFi modulom. Pozor – spôsob zabezpečenia nie je jednoduchý číselný kód, ale bitová maska.

Príklad použitia podpríkazu môže vyzerať nasledovne:

> net scan
SSID                               CH   RSSI  SECURITY
--------------------------------  ---  -----  ---------
iot-lab                             6    -52  WPA2
eduroam                             1    -67  WPA2
TUKE-guest                         11    -74  open
(hidden)                           11    -81  WPA/WPA2

Task

Vytvorte podpríkaz connect, pomocou ktorého sa zariadenie pripojí k zadanej WiFi sieti.

Podpríkaz dostane dva parametre: názov siete (SSID) a heslo. Zadané údaje sa do nastavení neukladajú. Technik si tak môže overiť, či sú prihlasovacie údaje správne.

Metóda .connect() triedy network.WLAN na pripojenie nečaká – len ho spustí a hneď sa vráti. Podpríkaz preto musí počkať, kým sa zariadenie pripojí, alebo kým neuplynie časový limit v sekundách, ktorý je uložený v nastaveniach (wifi.timeout). Priebeh pripájania môžete sledovať metódou .status(), ktorá vráti stav pripojenia (napr. network.STAT_GOT_IP, network.STAT_WRONG_PASSWORD alebo network.STAT_NO_AP_FOUND).

Podpríkaz vypíše:

  • "Connecting to <ssid>..." na začiatku pripájania,
  • po úspešnom pripojení názov siete, IP adresu, masku siete, adresu brány a DNS servera,
  • "Error: Wrong password.", ak je heslo nesprávne,
  • "Error: Network not found.", ak sieť so zadaným názvom nie je v dosahu,
  • "Error: Connection timed out.", ak sa zariadenie nepripojí v časovom limite,
  • "Error: Wrong usage.", ak podpríkaz nedostane práve dva parametre.

Príklad použitia podpríkazu môže vyzerať nasledovne:

> net connect iot-lab Secret.Password
Connecting to iot-lab...
SSID      : iot-lab
IP        : 192.168.1.42
Mask      : 255.255.255.0
Gateway   : 192.168.1.1
DNS       : 192.168.1.1
> net connect iot-lab wrong-password
Connecting to iot-lab...
Error: Wrong password.
> net connect no-such-network secret
Connecting to no-such-network...
Error: Network not found.

Task

Rozšírte príkaz net o podpríkazy disconnect a deactivate.

O týchto podpríkazoch bude platiť:

  • disconnect – odpojí zariadenie od WiFi siete a vypíše správu "Disconnecting from network..."; ak zariadenie nie je pripojené, vypíše správu "Error: Not connected.",
  • deactivate – vypne sieťové rozhranie a vypíše správu "Deactivating interface.".

Príklad použitia podpríkazov môže vyzerať nasledovne:

> net disconnect
Disconnecting from network...
> net disconnect
Error: Not connected.
> net deactivate
Deactivating interface.

Task

Vytvorte podpríkaz stat, ktorý zobrazí aktuálny stav sieťového rozhrania.

Podpríkaz vypíše:

  • či je sieťové rozhranie aktívne,
  • stav pripojenia, napr. idle, connecting, connected, wrong password alebo no AP found (číselné hodnoty stavov zodpovedajú konštantám network.STAT_*),
  • MAC adresu rozhrania.

Ak je zariadenie pripojené do siete, vypíše navyše:

  • názov siete (SSID), číslo kanála a silu signálu (RSSI),
  • IP adresu, masku siete, adresu brány a DNS servera.

Príklad použitia podpríkazu môže vyzerať nasledovne:

> net stat
Active    : no
Status    : idle
MAC       : 28:cd:c1:0f:3a:52
> net connect iot-lab Secret.Password
...
> net stat
Active    : yes
Status    : connected
MAC       : 28:cd:c1:0f:3a:52
SSID      : iot-lab
Channel   : 6
RSSI      : -52 dBm
IP        : 192.168.1.42
Mask      : 255.255.255.0
Gateway   : 192.168.1.1
DNS       : 192.168.1.1
> net disconnect
Disconnecting from network...
> net stat
Active    : yes
Status    : idle
MAC       : 28:cd:c1:0f:3a:52
> net deactivate
Deactivating interface.
> net stat
Active    : no
Status    : idle
MAC       : 28:cd:c1:0f:3a:52

Pripojenie do WiFi siete a odpojenie od nej

Stavový diagram zariadenia s novými stavmi ConnectNetwork a DisconnectNetwork sa nachádza na obrázku 2.

---
# spolocna konfiguracia stavovych diagramov THSensor; vklada sa ako prvy
# riadok bloku ```mermaid, za nim nasleduje "flowchart LR" a samotny diagram
config:
  flowchart:
    curve: basis
    nodeSpacing: 40
    rankSpacing: 60
  themeVariables:
    fontSize: 20px
  themeCSS: ".edgeLabel, .edgeLabel p, .labelBkg { background-color: var(--bs-body-bg) !important; }"
---

flowchart LR
    start@{ shape: f-circ } --> init(Init)
    init --> measurement(Measurement)
    init -- "<code>SVC_PIN</code><br>grounded" --> terminal("Service<br>Terminal")
    init -- "no/faulty<br><code>settings.json</code> /<br>long button press" --> reset("Factory<br>Reset")
    measurement --> connect("Connect<br>Network")
    connect --> disconnect("Disconnect<br>Network")
    disconnect --> sleep(Sleep)
    sleep --> sleepEnd@{ shape: fr-circ }
    terminal --> terminalEnd@{ shape: fr-circ }
    reset --> resetEnd@{ shape: fr-circ }

    %% spolocne styly stavovych diagramov THSensor (farby podla
    %% images/thsensor-state.diagram.complete.png); vklada sa na koniec diagramu
    %% a stavy sa k nim priradia cez "class <stavy> <styl>"
    classDef state fill:#cfe2ff,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef service fill:#cbc3a3,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef reset fill:#ffd6f5,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef config fill:#fddba7,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef error fill:#ffd7d9,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef ota fill:#c3f5c8,stroke:#9aa3ad,stroke-width:3px,color:#343a40,font-weight:bold
    classDef initial fill:#000,stroke:#fff,stroke-width:2px
    classDef final fill:#fff,stroke:#000,stroke-width:2px

    class init,measurement,connect,disconnect,sleep state
    class terminal service
    class reset reset
    class start initial
    class sleepEnd,terminalEnd,resetEnd final

Obr. 2: Stavový diagram zariadenia so stavmi ConnectNetwork a DisconnectNetwork

Vytvorenie príkazu pip

Podobne ako jazyk Python je možné aj jazyk MicroPython rozširovať pomocou balíkov, ktoré je možné doinštalovať. V jazyku Python na to slúži nástroj príkazového riadka pip. V prostredí jazyka MicroPython máme niekoľko spôsobov, ako na mikrokontroléri inštalovať nové balíky. Nás v tomto kroku bude zaujímať modul mip, ktorý sa používa podobne ako nástroj pip. Keďže balíky sťahuje z internetu, zariadenie musí byť pri inštalácii pripojené do siete.

V tomto kroku vytvoríme v servisnom termináli samostatný príkaz pip, pomocou ktorého bude možné rozšíriť aktuálne schopnosti mikrokontroléra o nové balíky.

Viac informácií o inštalovaní nových balíkov v prostredí jazyka MicroPython sa dozviete v kapitole Inštalácia balíkov.

Task

V module pip.py vytvorte triedu Pip, ktorá bude reprezentovať príkaz pip na inštaláciu nových balíkov.

O tejto triede bude platiť:

  • bude potomkom triedy BaseCommand,
  • názov príkazu bude "pip",
  • opis príkazu bude "Install packages.",
  • podpríkaz install <balík> nainštaluje zadaný balík pomocou funkcie mip.install(),
  • pred inštaláciou overí, či je zariadenie pripojené do siete (WiFi modul s aliasom ALIAS_WIFI); ak nie je, vypíše správu "Error: Not connected." a inštaláciu nespustí,
  • ak príkaz nedostane podpríkaz install s práve jedným názvom balíka, vypíše správu "Error: Wrong usage."

Príkaz zaregistrujte rovnako ako ostatné príkazy.

Task

Overte funkčnosť príkazu pip.

Najprv vyskúšajte inštaláciu bez pripojenia do siete a potom sa pripojte a nainštalujte balík pathlib zo štandardnej knižnice jazyka MicroPython (micropython-lib):

> pip install pathlib
Error: Not connected.
> net connect iot-lab Secret.Password
Connecting to iot-lab...
SSID      : iot-lab
IP        : 192.168.1.42
Mask      : 255.255.255.0
Gateway   : 192.168.1.1
DNS       : 192.168.1.1
> pip install pathlib
Installing pathlib...
Done.

Nainštalované balíky sa uložia do priečinka /lib na mikrokontroléri, odkiaľ ich je možné importovať rovnako ako vstavané moduly. Či sa balík naozaj nainštaloval, môžete overiť priamo z prehliadača súborov vášho IDE alebo z prostredia REPL jazyka MicroPython:

>>> import os
>>> os.listdir('/lib')
['pathlib.mpy']
>>> from pathlib import Path

Ak ste postupovali správne, priečinok /lib bude obsahovať nainštalovaný balík a jeho import prebehne bez chyby.

Ďalšie úlohy

  1. Rozšírte príkaz reset o podpríkaz factory, ktorý obnoví výrobné nastavenia zariadenia.

Ďalšie zdroje

  1. Práca s WiFi modulom – scan WiFi sietí, pripojenie k WiFi, vytvorenie prístupového bodu

  2. Inštalácia balíkov – inštalácia balíkov do mikrokontroléra v jazyku MicroPython

  3. Dátové triedy – ako na dátové triedy pomocou triedy BaseModel