Skip to main content

2.3 MicroPython

Mikrokontroléry môžeme programovať rôznymi spôsobmi, spomínali sme si už blokové (vizuálne) i textové programovanie. Z klasických textových programovacích jazykov sú k dispozícii:

  • C++ v známom prostredí Arduino IDE;
  • MicroPython ako špeciálna verzia Python pre MCU;
  • CircuitPython ako špeciálna pohodlná verzia MicroPython.

Predstavenie jazyka Python

Python je jazyk interpretovaný, rovnako jeho MCU verzie, čo má svoje výhody i nevýhody, ktorými sme sa už vo všeobecnosti zaoberali. Najväčšími strašiakmi Pythonu v MCU sú:

Python je pomalší ako C.
Python spotrebuje veľa operačnej pamäte.
Zariadenie s Python nevydrží dlho bežať na batériu.

Tieto tvrdenia v súčasnosti nie sú celkom pravdivé. Výkon je možné zvýšiť až na úroveň skompilovaného kódu - výkonovo kritické časti je možné viacerými spôsobmi optimalizovať, či dokonca „predkompilovať“, čo pre bežné funkcie využíva aj samotný MicroPython. K zaujímavým výsledkom merania výkonu prišiel Vláďa Smitka z Makerclass, ako vidno v jeho prezentácii „Python na MCU - Je to dobrý nápad?“. V dokumentácii MicroPython je možné nájsť pokročilé tipy pre zvýšenie rýchlosti programu - nie je to však čítanie pre začiatočníkov! 🙂 Takejto vysokej úrovni optimalizácia sa v tomto kurze nebudeme venovať.

Vyššia spotreba pamäte je fakt - prekladač musí byť aktívny v pamäti, no samotný program už nemá výrazne vyššie požiadavky na pamäť. Keďže sú však už bežne dostupné lacné MCU rádovo s MiB operačnej pamäte, tento problém sa postupne stáva nepodstatným.

Čo sa týka výdrže batérie, teda spotreby energie, najpodstatnejšie je písať programy rozumne, s dôrazom na šetrenie energie a s využívaním „spánku“. Až na druhom mieste je spotreba energie pre samotný beh programu - tu je možné použiť podobné optimalizácie, ako pri výkone.

Každopádne, výkon behu programu nie je všetko a niekedy je oveľa podstatnejšia rýchlosť vývoja aplikácie. A koľko trvá kompilácia C++ pre MCU, sme mali možnosť vidieť a zažiť s ESPHome. 😒

MicroPython vs. Python

MicroPython je jednoduchou a efektívnou implementáciou programovacieho jazyka Python 3.4, pričom obsahuje aj niektoré novšie prvky z vyšších verzií - v dokumentácii MicroPython je možné nájsť rozdiely oproti štandardnému Python. Zahŕňa len časť štandardných knižníc Python, má však knižnice pre prácu s hardvérom a je optimalizovaný pre mikrokontroléry s obmedzenými zdrojmi. Snaží sa byť kompatibilný s plnohodnotným jazykom Python.

Základné konštrukcie jazyka MicroPython sú totožné s Python a môžeme teda využívať dokumentáciu jazyka Python. Oplatí sa však oboznámiť so špecifikami MicroPython - či už po stránke optimalizácie zápisov alebo špeciálnych knižníc určených pre ovládanie hardvéru.

Základné konštrukcie a syntax MicroPython

Pre komentár v jazyku Python slúži znak „#“ a pre viacriadkový komentár je možné využiť viacriadkový text, ktorý začína i končí trojicou apostrofov ('''):

# toto je bežný jednoriadkový komentár

'''
A toto je viacriadkový komentár.
Ono to vlastne ani nie je komentár, ale len tak „voľne pohodený“ viacriadkový textový reťazec.
Na rýchle znefunkčnenie časti programu však výborne poslúži.
'''

Operátory

Matematické operátory sú podobné C++:

  • +, -, *, /, %: sčítanie, odčítanie, násobenie, delenie, zvyšok po delení
  • na rozdiel od C++ delenie celých čísel vráti desatinné číslo;
  • //, **: celočíselné delenie, mocnina - navyše oproti C++.

Operátory porovnávania sú rovnaké ako v C++:

  • ==, !=, <, >, <=, >=: rovný, nerovný, menší, väčší, …

Logické operátory sa píšu slovne:

  • not, or, and: negácia, logický súčet, logický súčin.

Ďalšie informácie: W3Schools - Python Operators

Premenné a dátové typy

V názvoch premenných sa rozlišujú malé a veľké písmená, môžeme používať aj diakritiku. Premenné nie je potrebné deklarovať, priamo môžeme definovať hodnotu: {názov premennej} = {hodnota}

počet = 10
pi = 3.14

Logické hodnoty (boolean) sú označované True a False:

je_horúco = False

Textové reťazce môžu byť v úvodzovkách i apostrofoch - môžeme si vybrať:

meno = "Dušan"
priezvisko = 'Zervan'

Typ premennej je možné zistiť funkciou type({premenná}) - tá vracia objekt, no pre získanie názvu typu vo forme textového reťazca môžeme využiť jeho vlastnosť .__name__:

type(meno).__name__

Je možné priraďovať naraz do viacerých premenných:

meno, vek = "Anna", 15

Vstup a výstup

Výpis z MicroPython na štandardný výstup sa nerealizuje „na obrazovku“, ale na konzolu / sériový port.

Príkaz print({výraz 1}, {výraz 2},) vypíše zadané výrazy oddelené medzerou, na konci bude ENTER:

print(meno, "získal z testu", počet, "bodov.")

Ukončenie riadku určuje parameter end, oddeľovanie sep:

print("údaje v CSV:", end=" ")
print(počet, poradie, meno, sep=";")

Premennú môžeme načítať z konzoly: {premenná} = input({text výzvy})

vek = input("Zadaj vek: ")

Používanie funkcií z modulov

Niektoré funkcie sú priamo v jazyku Python, no viac je v rôznych „moduloch“. Ide o podobnú situáciu ako predstavujú knižnice v C++. Pred použitím funkcií z modulu ich musíme importovať, čo je možné rôzne:

  • môžeme importovať celý modul: import {modul}
  • môžeme importovať konkrétnu funkciu (alebo i viacer0)viaceré) z modulu: from {modul} import {funkcie}
  • prípadne môžeme importovať všetky funkcie z modulu: from {modul} import *

Prvý spôsob, teda import modulu, vyžaduje zadávať pred názov funkcie aj názov modulu:

import time, math
time.sleep(1)
print(time.time_ns(), math.pi)

Import funkcií z modulu umožňuje písať priamo názov funkcie, len je potrebné dať si pozor, aby sa funkcie z rôznych používaných modulov nevolali rovnako:

from time import *
from math import pi
sleep(1)
print(time_ns(), pi)

V oboch variantoch sa takto neimportujú len funkcie, ale aj konštanty a premenné.

Užitočné funkcie

Funkcie pre prácu s časom z modulu time umožňujú nielen získať systémový čas, ale aj pozastaviť vykonávanie programu a šetriť energiu, či merať trvanie úseku programu:

  • sleep({čas}) / sleep_ms({čas}): vykoná úspornú pauzu trvajúcu zadaný počet sekúnd / milisekúnd;
  • time() / time_ns(): vráti aktuálny čas v sekundách / nanosekundách;
  • ticks_ms() / ticks_us(): vráti počet milisekúnd / mikrosekúnd od spustenia;
  • ticks_diff({čas2}, {čas1}): vráti rozdiel časov z funkcií ticks_ms() a ticks_us().

V module machine nájdeme v MicroPython funkcie týkajúce sa hardvéru, pre nás bude potrebných len zopár:

  • unique_id(): vráti identifikátor zariadenia;
  • freq(): vráti frekvenciu procesora (v Hz) alebo ju nastaví na zadaný parameter;
  • reset(): vykoná reštart.

Funkcie z modulu random umožňujú pohodlnú prácu s náhodnými číslami:

  • random({čas}): vráti náhodné reálne číslo v intervale [0.0, 1.0];
  • uniform({a}, {b}): vráti náhodné reálne číslo v intervale [a, b];
  • randint({a}, {b}): vráti náhodné celé číslo v intervale [a, b];
  • randrange({a}, {b}, {n}): vráti náhodný násobok celého čísla n z intervalu [a, b];
  • choice({zoznam}): vráti náhodný prvok zo zoznamu alebo tuply.

Funkcie textového reťazca

Textový reťazec je objekt, jedná sa teda o objektové funkcie. Tieto funkcie vracajú nový objekt, nemenia pôvodný:

  • .upper() / .lower(): prevod na malé / veľké písmená;
  • .strip(): odstráni „biele znaky“ zo začiatku a konca (trim);
  • .replace({čo}, {čím}): nahradí reťazec iným reťazcom;
  • .split({oddeľovač}): rozdelí reťazec do poľa (zoznamu reťazcov);
  • .join({pole}): spojí prvky poľa (zoznam / tupla) do textového reťazca, oddeľovačom je textový reťazec;
  • .format({parametre}): mocná funkcia na spájanie textov s premennými, viď pyformat.info.

Textové reťazce je možné spojiť operátorom + a opakovať operátorom *:

print(3 * "Učiť sa! " + "- povedal súdruh Lenin")

Pre formátovaný výstup je možné využívať takzvaný f-string, ktorý je približne 2× rýchlejší ako funkcia format() a 3×rýchlejší ako spájanie textov cez plus:

print(f"číslo ∏: {pi:.4f} - zaokrúhlené na 4 desatinné miesta")

Ďalšie funkcie pre prácu s textovým reťazcom: W3Schools - Python String Methods

Kolekcie údajov

Python rozlišuje 3 typy „polí“:

  • list (zoznam), zátvorky [] - ako pole v C++, teda prvky majú svoje poradie, môžu sa opakovať a môžu sa meniť ich hodnoty;
  • tuple („tupla“), zátvorky () - ako list, ale prvky sa nemôžu meniť, ide teda o pevne daný nemenný zoznam, rýchlejšie spracovávaný;
  • set (množina), zátvorky {} - prvky nemajú svoje poradie, nie je ich možné indexovať, nemôžu sa opakovať (žiadne duplikáty).

Najbohatšiu ponuku funkcií majú zoznamy, niektoré funkcie sú dostupné aj pre tuple a množiny.

Zoznam (list)

Zoznam v Python sa správa podobne ako pole v C++, prvky sa číslujú od 0. Indexovať prvky je však možné aj od konca (záporným číslom) a prvky nemusia byť rovnakého dátového typu:

čísla = ["dvanásť", 15, "štyri", 3.14]
print(čísla[-1])

Počet prvkov zistí funkcia len({pole}) a súčet hodnôt v poli čísel spočíta funkcia sum({pole}).

Prvky zoznamu je možné uložiť do premenných - musí ich však byť rovnaký počet: {premenná 1}, {premenná 2},= {zoznam}

a, b, c, d = čísla

Zistenie výskytu prvku v poli - vráti bool: {hľadaný prvok} in {pole}

print(15 in čísla)

Delenie poľa od uvedeného začiatočného prvku (vrátane) po konečný prvok (bez neho): {pole}[{začiatok}:{koniec}]

  • môžu byť aj záporné hodnoty (od konca);
  • ak neuvedieme začiatok, začne prvým prvkom (č. 0);
  • ak neuvedieme koniec, skončí posledným prvkom.
print("druhé a tretie číslo:", čísla[1 : 3])

Rovnakým spôsobom môžeme aj prepisovať prvky na uvedenej pozícii:

  • ak uvedieme prvky v rovnakom počte, prepíšu sa;
  • ak ich bude viac, nadbytočné sa vložia za prepísané.

Rovnako ako textové reťazce, aj zoznamy je možné spojiť operátorom + a opakovať operátorom *. A rovnako aj zoznam predstavuje objekt, vloženie prvku do zoznamu je možné vykonať objektovými funkciami:

  • .append({prvok}) - na koniec zoznamu;
  • .insert({pozícia}, {prvok}) - na uvedenú pozíciu.

Odobratie prvku zo zoznamu:

  • .pop({pozícia}) - z uvedenej pozície (ak neuvedieme, z konca);
  • .remove({hodnota}) - prvý prvok obsahujúci uvedenú hodnotu;
  • .clear() - vyprázdni celý zoznam (zostane prázdny).

Ďalšie zaujímavé funkcie zoznamu:

  • .reverse() - v zozname otočí poradie prvkov;
  • .sort() - abecedne zoradí zoznam, voliteľný parameter reverse zariadi opačné poradie;
  • .count({hodnota}) - vráti počet prvkov s uvedenou hodnotou.

Viac funkcií pre prácu so zoznamom: W3Schools - Python List Methods

Tupla (tuple): zátvorky ()

Tupla je podobná kolekcia údajov ako zoznam, ale prvky sa nemôžu meniť, ide teda o pevne daný - nemenný zoznam, rýchlejšie spracovávaný. Môže sa jednať aj o jediný prvok, no vtedy musí končiť čiarkou: {tupla} = ({prvok},)

Tupla poskytuje tie isté operácie a funkcie, ako majú zoznamy - s výnimkou tých, ktoré menia hodnoty.

Viac funkcií pre tuple: W3Schools - Python Tuple Methods

Množina (set): zátvorky {}

Na rozdiel od zoznamu a tuply, prvky množiny nemajú svoje poradie, teda nie je ich možné indexovať a nemôžu sa opakovať (žiadne duplikáty).

Do množiny môžeme prvok pridávať:

  • .add({prvok}) - pridá jeden prvok;
  • .update({pole}) - pridá celú množinu / zoznam / tuplu.

A tiež odoberať:

  • .remove({hodnota}) - ak prvok s uvedenou hodnotou neexistuje, spôsobí chybu;
  • .discard({hodnota}) - ak neexistuje, ignoruje.

Viac funkcií pre množinu: W3Schools - Python Set Methods

Slovník (dictionary)

Slovník v Python organizuje údaje v pároch kľúč:hodnota rovnako ako JSON: {slovník} = { {kľúč}: {hodnota},}
Využiť ho možno podobne ako štruktúry v C++:

osoba = {
    "meno": "Fero",
    "vek": 12
}
print(osoba["meno"])

Ide o podobná formu ako množina - neumožňuje duplikáty, čiže dva údaje nemôžu mať rovnaký kľúč. Prístup ku jednotlivým prvkom je podobný ako pri zozname, no namiesto indexu uvádzame kľúč.

Prehľad funkcií pre slovník: W3Schools - Python Dictionary Methods

Podmienený príkaz: if - elif - else

Je veľmi podobný C++, má mierne odlišnú syntax a je ho nutné správne predsadiť!

  • if {podmienka 1}:
    • {príkazy pri splnení podmienky 1}
  • elif {podmienka 2}:
    • {príkazy pri splnení podmienky 2}
  • else:
    • {príkazy pri nesplnení žiadnej podmienky}

Časť elif sa môže opakovať viackrát, no nie je povinná ani časť elif, ani časť else. Môžeme samozrejme využívať aj vnorené podmienky, opäť treba správne predsadiť.

Cyklus podľa podmienky: while

funguje rovnako ako v C++, mierne odlišná syntax:

⁘ while {podmienka}:

⁘ {príkazy tela cyklu}

tiež je možné použiť break (predčasné ukončenie cyklu) a continue (predčasný prechod na ďalšiu iteráciu) - rovnako ako v C++

navyše je výraz pass, ktorý nerobí nič (na formálne vyplnenie prázdneho tela cyklu)

cyklus môže obsahovať aj časť else: - vykoná sa po poslednej iterácii (teda nie po ukončení cez break)

Cyklus pre zoznam a interval hodnôt: for

odlišuje sa od C++, používa sa na prechod sekvenciou (zoznam, reťazec):

⁘ for {premenná} in {zoznam}:

⁘ {príkazy tela cyklu}

⁘ for x in [1, 2, 4, 8]:

⁘ print(x)

vytvorí lokálnu premennú, bude v nej iterovať hodnoty zo zoznamu

výrazy break, continue a else je možné používať rovnako ako v cykle while

pre vytvorenie zoznamu čísel môžeme využiť funkciu range():

⁘ range({počet}) - od čísla 0 po {počet} - 1

⁘ range({začiatok}, {koniec}) - od začiatku (vrátane) po koniec (bez)

⁘ range({začiatok}, {koniec}, {krok}) - s uvedeným krokom

príklad:

⁘ for x in range(10, 30, 5):

⁘ print(x) # 10 15 20 25

Definícia funkcie

pri definícii funkcie neuvádzame ani návratovú hodnotu, ani typ parametrov, inak je podobná C++

⁘ def {názov} ({parametre}):

⁘ {príkazy tela funkcie}

funkcia nemusí mať parametre, zátvorky musia byť uvedené

parametre môžu byť nepovinné (mať predvolenú hodnotu)

výsledok (jedinú návratovú hodnotu) je možné odovzdať a funkciu ukončiť cez return

viac výsledkov je možné odovzdať cez yield - ide o generátor, funkcia sa neukončí

funkcia môže používať globálne premenné, deklaruje ich výraz global

volanie (použitie) funkcie je podobné C++

Parametre funkcie

parametre je možné volať s menom v ľubovoľnom poradí, napr.:

⁘ def Priemer(sucet, pocet):

⁘ return sucet / pocet

⁘ print(Priemer(pocet=10, sucet=45)) # 4.5

funkcia môže mať aj viacnásobný parameter:

- ak názov začína znakom *, používa sa ako tuple

- ak názov začína znakmi **, používa sa ako slovník s názvom parametra pri volaní

Lambda

lambda je jednoduchá anonymná funkcia, ktorú formálne môžeme priradiť do premennej:

⁘ lambda {parametre} : {výraz s parametrami}

príklad:

⁘ SúčetČísel = lambda x : (1 + x) * x / 2

⁘ print(SúčetČísel(10))

viac: https://www.w3schools.com/python/python_lambda.asp

Zachytávanie výnimiek (chýb)

princíp rovnaký ako v C++, mierne odlišná syntax:

⁘ try:

⁘ {príkazy}

⁘ except:

⁘ {príkazy, ak nastala chyba}

⁘ else:

⁘ {príkazy, ak nenastala chyba}

⁘ finally:

⁘ {príkazy spustené vždy na záver}

časti else a finally nie sú povinné

príkazy finally sa vykonajú vždy, a to aj v prípade predčasného ukončenia cez break a return

za výrazom except môže byť uvedený typ zachytávanej chyby

viac: https://www.w3schools.com/python/python_try_except.asp

Inštalácia rozširujúcich modulov

rozširujúce moduly môžeme nahrať ručne do priečinku /lib alebo do aktuálneho / koreňového priečinku

ak je modul prítomný na viacerých miestach, uplatní sa priorita definovaná v sys.path

predvolená priorita v MicroPython je: 1. aktuálny priečinok, 2. zabudovaný modul (frozen), 3. priečinok /lib

v školskej inštalácii je priorita zmenená nasledovne: 1. aktuálny priečinok, 2. priečinok /lib, 3. zabudovaný

pokiaľ je aktívne pripojenie do internetu (na Wi-Fi), je možné automatizovane stiahnuť modul do /lib, stačí poznať jeho názov:

⁘ import mip

⁘ mip.install("{názov modulu}")

Trieda (class)

...