Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

4. Zpracování dat

Před cvičení si prosím nainstalujte Python 3.x s následujícími moduly: pyserial, matplotlib, pyqt6, pysqlite3, bleak, paho-mqtt, numpy

Instalace modulů:

$ pip install pyserial matplotlib pyqt6 pysqlite3 bleak paho-mqtt numpy

V případě, že nemáte administrátorská práva na počítači:

$ pip install --user pyserial matplotlib pyqt6 pysqlite3 bleak paho-mqtt numpy

Cíle cvičení

  1. Vytvořit program pro Thunderboard, který bude v pravidelných intervalech odesílat data po UART
  2. Data v reálném čase vykreslovat pomocí aplikace v Pythonu
  3. Navrhout protokol, který umožní přenos dat z různých senzorů
  4. Data ukládat do databáze na PC k pozdějšímu použití

Ukázkové aplikace v Pythonu

Řada aplikací bude napsána v Pythonu, flexibilním programovacím jazyku vhodném pro rychlé prototypování. Vždy bude připravena kostra programu, kterou lze přímo použít pro vlastní projekt. Pokud byste se chtěli dovědět něco víc o Pythonu, je možné využít např. podklady předmětu BAB37ZPR: 2021/22, 2020/21. Lze také doporučit volně dostupnou knihu Python Programming And Numerical Methods: A Guide For Engineers And Scientists.

Jako zdroj dat lze využít např. generátor harmonického signálu na gitlabu. Aplikace pro PC v jazyce Python jsou také k dispozici na gitlabu.

Komunikace po rozhraní UART

Pro UART komunikaci mezi embedded zařízením a PC použijeme knihovnu PySerial. Připojení proběhne vytvoření instance třídy, parametry konstruktoru jsou název sériového portu a rychlost komunikace. Je celkem vhodné ošetřit připojení pro případ, že spojení nelze uskutečnit. V následujícím kódo je zařízení připojeno k portu COMXXX (za XXX dosaďte podle potřeby) a komunikuje rychlostí 115200 baudů.

import serial
 
try:
    ser = serial.Serial('COMXXX', 115200)
    ser.flushInput()
except:
    print("unable to open COM port")

Se zařízením je možné komunikovat na úrovni bytů nebo znaků. Pokud zařízení posílá data ve formě znaků (tj. posílá byty s významem ASCII kódů), lze data přijmout třeba takto:

data = ser.readline().decode()
print(data)

Pokud je z hlediska aplikace výhodnější používat byty ve s významem čísla (např. v rozsahu 0-255), je možné přijmout a dekódovat takto:

byte = ser.read()
decoded_bytes = int.from_bytes(byte, byteorder = 'little')

Argumentem metody read() je počet bytů, které jsou najednou přečteny. Lze tedy např. předpokládat, že embedded zařízení posílá dva byty současně a tyto dva byty buď mohou reprezentovat číslo typu short, nebo dva samostatné byty a a b, které je třeba dekódovat:

byte = ser.read(2)
decoded_bytes = int.from_bytes(byte, byteorder = 'little')
a = decoded_bytes & 255;
b = (decoded_bytes >> 8) & 255;

Přenos struktrovaných dat

Pokud bychom potřebovali přenést po UART hodnoty různých datových typů, můžeme je na straně mikrokontroléru uložit do struktury.

BufferedSerial pc(USBTX, USBRX, 115200);
//--
typedef struct data {
    unsigned int Si7021_t;
    unsigned int Si7021_h;
    int Si7210_t;
    float BMP280_t; 
};
// ---
data a;
// --
pc.write(&a, sizeof(a));

V Pythonu pak lze pro dekódování dat ve struktuře využít knihovny ctypes nebo struct.

from ctypes import Structure, c_uint, c_int, c_float, sizeof
 
class DataPoint(Structure):
    _pack_ = 1
    _fields_ = [
        ("Si7021_t", c_uint),
        ("Si7021_h", c_uint),
        ("Si7210_t", c_int),
        ("BMP280_t", c_float)
    ]
#
byte = ser.read(16)
buffer = bytearray(byte)
Point = DataPoint.from_buffer(buffer)
# teplota z Si7021 je pak dostupná jako Point.Si7021_t/1000,

Vykreslování dat

K vykreslení dat přicházejících po UART rozhraní použijeme knihovnu matplotlib. Data pro vykreslení jsou uložena ve vektoru vytvořeném pomocí knihovny NumPy. Vektor má délku odpovídající délce okna, a v cyklu jsou na začátek vektoru přidávána data metodou append. V cyklu jsou současně vykreslována data ze začátku vektoru.

import matplotlib.pyplot as plt
import numpy as np
 
plot_window = 20
y_var = np.array(np.zeros([plot_window]))
 
plt.ion()
fig, ax = plt.subplots()
line, = ax.plot(y_var)
ax.set_ylim([0, 255])
 
while True:
    # zde by mel byt kod pro ziskani a dekodovani dat
    y_var = np.append(y_var, data)
    y_var = y_var[1:plot_window+1]
    line.set_ydata(y_var)
    fig.canvas.draw()
    fig.canvas.flush_events()

Pokud bychom chtěli vykreslovat dvě křivky, je třeba v původním kódu udělat následující úpravy:

# konfigurace
fig, ax = plt.subplots(2)
line0, = ax[0].plot(y0_var)
line1, = ax[1].plot(y1_var)
ax[0].set_ylim([0, 255])
ax[1].set_ylim([0, 255])
 
# samotne vykreslovani
y0_var = np.append(y0_var, a)
y1_var = np.append(y1_var, b)
y0_var = y0_var[1:plot_window+1]
y1_var = y1_var[1:plot_window+1]
line0.set_ydata(y0_var)
line1.set_ydata(y1_var)

Ukládání dat do SQL databáze

Pro uložení dat můžeme využít SQLite databázi. Její výhodou je, že pracuje s lokálním binárním souborem a má minimální nároky na paměť a výkon počítače - veškerá potřebná funkcionalita je totiž vytvářena knihovnou, která pracuje s binárním souborem. Pro jednoduchost můžete využít modul nsidb, který vytváří třídu pro ukládání dvou hodnot typu float reprezentující teplotu a vlhkost přečtenoou ze senzoru Si7021. Hodnoty jsou funkci pro zápis předávány jako tuple.

Samotný hlavní program pro uložení dat může vypadat třeba takto:

from nsidb import *
 
conn = db_conn("iot.db")
db_create(conn)
db_insert (conn, (3.14, 25.5))
 
rows = db_fetch (conn)
for row in rows:
    print(row)

courses/b0b37nsi/tutorials/04.txt · Last modified: 2022/05/06 21:50 by viteks