Arduino a Tcl/Tk - časť 1- HMI cez UART

27.11.2021 | 12:31 | Richard | Richard

Arduino a jeho deriváty sú s nami dlhú dobu a tešia sa stále veľkej popularite. Akoby aj nie, keď existuje silná a aktívna komunita, ktorá vytvorila a vytvára kvantum šikovných knižníc, návodov…
A tak je jednoduché pripojiť k Arduinu LCD displej, malé dotykové TFT displeje,… ale čo takto vizualizácia na počítači?

1 Rýchle predstavenie Tcl/Tk

GUI, t.j. grafické rozhranie pre Arduino je možné realizovať mnohými spôsobmi. Vyhľadávač predhodí v okamihu veľa podôb. V rôznych jazykoch.

Ja som si obľúbil Tcl/Tk, z niekoľkých dôvodov. Hlavným dôvodom je jeho hardvérová nenáročnosť. Proste sa mi nepáčia programy, ktoré si k sebe pribaľujú kvantum knižníc, alebo dokonca celé renderovacie jadro webového prehliadača.
Ďalej je to ľahká prenositeľnosť, rýchly návrh, pomerne široká ponuka nástrojov.

Na Root-e je parádna séria článkov od p. Tišnovského, ktorý opisuje základné nástroje a postupy.

Dovolím si odcitovať trefnú definíciu:

Tcl, neboli plným názvem Tool Command Language, je interpretovaný programovací jazyk vyvinutý Johnem Ousterhoutem v osmdesátých letech minulého století, který se, podobně jako jeho ideový předchůdce Lisp, snaží z faktu, že je interpretovaný, vytěžit maximum. Příkladem může být skutečnost, že samotný jazyk neobsahuje žádné příkazy pro tvorbu smyček, podmínek ani matematických či logických výrazů – tyto elementy, jež nám na první pohled mohou připadat jako neoddělitelná součást jakéhokoli rozumně použitelného programovacího jazyka, jsou v Tcl řešeny pomocí funkcí, které jako své parametry mohou získávat programový kód (ten například tvoří tělo smyčky či výraz představující podmínku). I z tohoto důvodu bude vlastní popis jazyka velmi stručný, protože obsahuje pouze několik poměrně jednoduchých pravidel pro psaní programů (na druhou stranu to však poněkud mate začátečníky, kteří se kvůli neexistenci speciálních syntaktických pravidel pro výrazy a řídicí příkazy špatně orientují v jinak výborné dokumentaci).

Každopádne celý seriál sa oplatí prečítať, aj keby čitateľ nemal ambíciu programovať v Tcl/Tk. Podobne, ako sme si v dávnych časoch v herných časopisoch (Bit, Excalibur, Riki či Level) čítali o hrách pre PC, ale mali sme len 8-bit ☺.
Odporúčam aj Tcl/Tk Tutorial na tutorialspoint.com.

Jeho (Tcl/Tk) veľkou výhodou je, že sa dá použiť len ako grafický interfejs, a výpočtovo náročné úlohy možno spraviť v inom jazyku, napr. C.
A - hoci to v dnešnom príklade nevyžijeme, dokáže veľmi jednoducho čítať a zapisovať na štandarný vstup, takže je možné efektívne reťazenie s inými programami.

Ukážeme si jednoduchý program, ktorý bude bežať v Arduine (alebo inom zariadení, ktoré má Arduino "v srdci", ako napr. Industrial Shields).

 

2 Program bežiaci v Arduine (firmvér)

Program je jednoduchý - v pravidelných intervaloch bliká vstavanou LED na pine č. 13, ďalej číta šum na analógovom pine A0. Stav LED a analógovú hodnotu A0 posiela na sériovú linku.

Relevantné riadky kódu sú uvedené nižšie, celý program bude k dispozícii na stiahnutie v závere.

void loop() {
  if ((millis() - predCasLED) > intervalLED) {
    if (stavLED == true) {
      digitalWrite(pinLED, LOW);
      stavLED = false;
    } else {
      digitalWrite(pinLED, HIGH);
      stavLED = true;
    }
    Serial.print("L");
    Serial.println(stavLED);
    predCasLED = millis();
  }

  if (Serial.available() > 0)
    intervalLED = Serial.readString().toInt();

  if ((millis() - predCasData) > INTERVAL_DATA) {
    Serial.print("A");
    Serial.println(analogRead(pinData));
    predCasData = millis();
  }
} // loop()

Takže v prvej časti, podľa rozdielu vnútorného časovača a nastaveného intervalu, zapína a vypína výstup definovaný pinLED. Pri zmene stavu posiela na sériovú linku reťazec L1 alebo L0, podľa stavu LED.

V ďalšej časti sleduje, či na sériovú linku neprišla správa, ak áno, (očakávaný) reťazec, v našom prípade nový interval, zadaný užívateľom, prevedie na int a vloží ho do relevantnej premennej.

V poslednej časti posiela v pravidelných intervaloch správu Axxx, kde xxx je analógová hodnota na príslušnom pine.

 

3 A teraz program v Tcl/Tk

Ten sa dá rozdeliť do 2 častí: grafického dizajnu a výkonnej časti.

 

3.1 Grafický dizajn

Dizajn vyzerá takto:

Definovanie dizajnu:

label .lblNadpis -text  "Arduino ⇔ Tcl/Tk" -font $pismoNadpis
label .lblLED -text "LED na pine 13" -font $pismoTlacid
label .lblLEDsymbol -text "⚫" -font $pismoTlacid
label .lblDataText -text "AI na pine A0" -font $pismoTlacid
text .lblData -font $pismoTlacid -width 3 -height 1
button .btnStart -text  "ŠTART" -font $pismoTlacid -bg green -command bezi
button .btnStop -text  "STOP" -font $pismoTlacid  -bg red1 -command stoji
button .btnPosliCas -text  "Pošli" -font $pismoTlacid  -bg gray -command posli
spinbox .spinIntervalLED -from 500 -to 5000 -increment 50 -textvariable intervalLED \ 
 -width 5 -font $pismoTlacid
label .lblIntervalLED -text "Interval LED" -font $pismoTlacid

grid .lblNadpis -column 0 -row 0 -columnspan 3 -pady 20
grid .lblLED -column 0 -row 1 -padx 10 -pady 10 -sticky w
grid .lblLEDsymbol -column 1 -row 1 -padx 10 -pady 10 -sticky e
grid .lblDataText -column 0 -row 2 -padx 10 -pady 10 -sticky w
grid .lblData -column 1 -row 2 -padx 10 -pady 10 -sticky e
grid .lblIntervalLED  -column 0 -row 4 -pady 10 -padx 10 -sticky w
grid .spinIntervalLED -column 1 -row 4 -pady 10 -padx 10 -sticky e
grid .btnStart  -column 0 -row 5 -pady 10 -padx 10 -sticky w
grid .btnPosliCas  -column 2 -row 4 -pady 10 -padx 10  -sticky e
grid .btnStop  -column 1 -row 5 -pady 10 -padx 10

Opäť si myslím, že samotný jazyk je veľmi opisný. Ako je vidieť, práca s premennými je podobná ako je tomu v bash-i. Len dodám, že -sticky w (resp. e) je zarovnanie v pomyselnej mriežke (gride) doľava či doprava.

 

3.2 Práca so sériovým portom v Tcl/Tk

Ako bolo povedané, Tcl/Tk je akýsi wrapper nad iným príkazmi, programami. Takže ak by nemal v základe podporu pre prácu s portami, vedeli by sme to obísť napr. príkazmi bash-u.

Napr. na poslanie reťazca na port (ttyACM* resp. ttyUSB*) by sme zadali:

echo "Ahoj Arduino" > /dev/ttyACM0

A na čítanie napr.

tail -f /dev/ttyACM0

alebo

cu -l /dev/ttyACM0  



V Tcl/Tk to môžeme spraviť takto:

proc pripoj {} {
    global portArduino rychlost portPosli
    set portArduino [glob /dev/ttyACM*]
    puts "Arduino pripojené na porte: $portArduino"
    set comPort [open $portArduino w+]
    fconfigure $comPort -mode $rychlost,n,8,1
    fconfigure $comPort -blocking 0
    fconfigure $comPort -buffering none
    fileevent $comPort readable ""
    cakaj 300
    set portPosli $comPort
    puts "Logický kanál: $portPosli"
    return $comPort
}



V skratke vysvetlenie:

príkazvýznam
global portArduino …pre používanie globálnych premenných v tele funkcie
[glob /dev/ttyACM*]

príkaz glob vráti zoznam názvov súborov ktoré spĺňajú masku

(predpokladáme, že je pripojené iba jedno Arduino)

puts "…výpis do textovej konzoly
[open $portArduino w+]

otvorí kanál pre čítanie i zápis (preto w+)

(len na okraj, výraz v [ ] sa vyhodnotí podobne ako výraz v bash-i uzavretý v ` a `)

fconfigure … -modedefinujeme prenosovú rýchlosť v baudoch (musí byť taká istá ako v programe pre Arduino (Serial.begin(<baud>) a paritu, počet bitov a počet stop-bitov
fconfigure … -blockingumožní nám asynchrónnu operáciu s puts a flush a i.
fconfigure … -flushpo skončení prenosu sa kanál automaticky vyprázdni
set portPosli $comPortpotrebujeme jednu globálnu premennú portPosli, aby sme vedeli ktorý kanál je otvorený

 

Ďalšia procedúra načíta z portu dáta.

proc nacitajHodnoty {port} {
    cakaj 50
    return [read -nonewline $port]
}

 

Hm, a načo je tam to cakaj 50?
Pretože inak si čítanie z pripojeného zariadenia uzurpuje všetok procesorový čas programu (nie celého systému) pre seba. Možno by sa to dalo nejako šikovnejšie poriešiť…
Samotná funkcia cakaj je:

proc cakaj {cas} {
    after $cas set stop_wait 1
    vwait stop_wait
}

 

3.3 Zobrazovanie a posielanie dát

Následné riadky zrejme nepotrebujú komentár. V princípe pomocou string index zisťujú, aký je prvý znak správy a ak je to L a následne 1 alebo 0, tak zafarbia text na červeno či čierno.
A pomocou string trimleft odstránia znak A a zvyšok reťazca zobrazia.
Výhodou (z hľadiska jednoduchosti) je beztypovosť Tcl. Všetky premenné sú považované za reťazce, a v prípade potreby (napr. matematické kalkukácie pomocou expr) dochádza k automatickému pretypovaniu.

proc zobrazujGUI {} {
    global bezi
    set port [pripoj]
    while {$bezi == 1} {
        set data [nacitajHodnoty $port]
        if { [string index $data 0] == "L" } {
            if { [string index $data 1] == "1"} {
                .lblLEDsymbol configure -foreground "red"
            } else {
                .lblLEDsymbol configure -foreground "black"           
            }
        } else {
            .lblData insert 0.0 [string trimleft $data "A"]
        }
    }
}

No a posledná procedúra po stlačení tlačidla Pošli pošle na port (defacto kanál) obsah premmenej intervalLED, ktorá sa sama pretypuje na požadovaný reťazec.

proc posli {} {
    global portPosli intervalLED
    puts -nonewline $portPosli $intervalLED
    cakaj 50
    puts -nonewline $portPosli "\n"
    flush $portPosli
}

 

4 Záver

Možností ako komunikovať medzi PC a Arduinom je niekoľko a sériová komunikácia je asi najjednoduchšia.
Namiesto Arduina si však môžme predstaviť iný mikrokontrolér alebo PLC na ňom založené a namiesto PC to môže byť dotykové Raspberry Pi (ako HMI).
Ak to pôjde do priemyselných podmienok a nebodaj HMI bude od PLC vzdialené viac ako pol metra, potom je lepšie spraviť sériovú komunikáciu napr. cez RS485 (mám skúsenosť s dosahom cca 1 km po metalickom vedení (krútená dvojlinka), v zarušenom prostredí), čo si vyžaduje hardvér navyše (ale napr. spomínané Industrial Shields majú RS485 v základe).
Tam už napr. I2C či SPI nie je vhodné.

Častokrát sme svedkami, keď PLC a HMI komunikujú po sieti. A to bude obsahom ďalšieho blogu vo vzdialenej budúcnosti.

Osobne si myslím, že práca s Tcl/Tk je rýchla a príjemná. Isteže, grafický dizajn sa nedá "uplácať" naťahaním komponentov ako to poznáme z niektorých iných jazykov. Ale zameranie Tcl/Tk je iné - rýchle grafické nadstavby tam, kde textové (ncurses) rozhranie nestačí (hoci existujú aj Tcl/Tk aj rozsiahle projekty ala CAD).
Raz som hľadal Pomodoro časovač, a primitívny program, spravený v Electrone, mal takmer 100 MB (ak sa pamätám správne).
Napriek rýchlym počítačom a obrovskému miestu na disku to nepovažujem za OK.


Zdrojové kódy som nahral tu: Súbory pre Arduino i PC

 

5 Poznámky

Pozn. č. 1: Jasné, sú to malé programy s malými nárokmi, preto mi hádam budú odpustené globálne premenné ☺.

Pozn. č. 2: Mal som kedysi aj konto na GitHube, ale vieme ako sa do toho zamiešal Microsoft. A inde (napr. GitLab) sa to mi nechcelo zriaďovať (hoci verzovanie používam i lokálne (a Emacs je v tomto nápomocný, čo je námet na blog) a vzdialene len ako zálohu).

Pozn. č. 3: V Emacse je na prácu s Tcl pochopiteľne vytvorený mód. Ale ak používate etags na vytvorenie TAG-ov na skákanie medzi definíciami, tak etags defaultne nepozná kľúčové slová jazyka Tcl.
Pomôže tento trik

etags -l none -r '/proc[ \t]+\([^ \t]+\)/\1/'


Pozn. č. 4: Či skôr otázka: "Ak pracujete samostatne na nejakom programe, používate slovenské alebo anglické názvy premenných?"

    • RE: Arduino a Tcl/Tk - časť 1 27.11.2021 | 17:03
      Avatar Pavel Q4OS KDE  Administrátor

      Pre nás neznalých by to chcelo hneď úvodom, čo to Arduino je :-) Inak pekný blog, určite dal zabrať.

      • RE: Arduino a Tcl/Tk - časť 1 28.11.2021 | 09:08
        Avatar Livan Manjaro s XFCE  Používateľ

        Arduino je toto. Je to pomerne lacná (od cca 2 €) doska obsahujúca programovateľný procesor so vstupmi a výstupmi. Arduino dokáže vyhodnocovať údaje na vstupoch a podľa programu riadiť výstupy. Používá sa hlavne na ovládanie a riadenie rôznych zariadení. Existujú preň hotové moduly a knižnice vo veľkom počte.

    • RE: Arduino a Tcl/Tk - časť 1 11.12.2021 | 15:44
      Avatar LUcoRP Debian, *Ubuntu, Android  Administrátor

      Arduinko je super vec. Najzaujimavejsi projekt co som na nom robil bola asi Citacka RFID kariet ovladana cez raspberry, ktore bolo pomocou GPIO prepojene prave na Arduino a cele to bolo naviazane na externy server s vystupom na Rpi Displej a periferne zariadenie. Taky pekny IoT projekt.

      Co sa tyka premennych, tak jednoznacne pomenovavam v anglictine. Slovensky hovoriacich ludi je na svete zanebatelne mnozstvo a nikdy nevies, kto sa bude potrebovat prehrabat cez tvoj stary kod. Tu je popis napr. pre spravne pomenovavanie premennych v Pythone, ale vo vacsine jazykov je to podobne, s tym ze sa meni maximalne tak konvencia notacie.

      git blame | Muj Desvorc je vetsi nez tvuj!
      • RE: Arduino a Tcl/Tk - časť 1 11.12.2021 | 17:33
        Avatar Richard Antix  Používateľ

        a nikdy nevies, kto sa bude potrebovat prehrabat cez tvoj stary kod

        Ehm, áno, raz upravovali môj kód, ktorý bežal pod Win98, GUI bolo ešte v legendárnom Delphi. A tam boli premenné v SK. Ale dali si rady (ale neviem, či ma náhodou aj parkrát neprekliali, no).