Table of Contents

5. IoT zařízení

Cíle cvičení

  1. Vytvoření programu pro Raspberry Pi Pico
  2. UART komunikace s RPi Pico
  3. IoT systém

Micropython na Raspberry Pi Pico W

https://micropython.org/download/rp2-pico-w/

https://thonny.org/

https://github.com/cpwood/Pico-Go

from machine import Pin, Timer
led = Pin('LED', Pin.OUT)
timer = Timer()
 
def blink(timer):
    led.toggle()
 
timer.init(freq=2.5, mode=Timer.PERIODIC, callback=blink)

Komunikace po UART

Cílem dílčí úlohy je poslat prostřednictvím rozhraní UART informaci o tom, jakou rychlostí se má rozsvítit LED na desce Raspberry Pi Pico. Nejprve si ale můžeme vyzkoušet samotnou komunikaci, tj. např. odesílání znaků z mikrokontroléru a jejich příjem na straně PC.

Aplikace na Raspberry Pi

Třída machine.UART je určena pro vytvoření fyzického UART rozhraní pomocí dvojice RX/TX pinů. Pokud bychom ji chtěli využít, potřeboval bychom na druhé straně obdobné zařízení, např. jiné Pico. Pokud bychom chtěli takto komunikovat s PC, je potřeba konvertor UART-USB, např. s FTDI čipem (např. tento).

Lze však využít toho, že USB rozhraní Raspberry Pi funguje jako sériové rozhraní s rychlostí 115200 kb/s. A data z desky Pico lze tedy poslat příjemci na PC např. pomocí funkce print().

from machine import Pin, Timer
import sys
 
led = Pin('LED', Pin.OUT)
 
while True:
    # x = input()
    x = sys.stdin.read(1)
    if  x == 'a':
        led.toggle()
    print("UART: {}".format(x))

Aplikace na PC

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;

import serial
 
try:
    ser = serial.Serial('COM7', 115200)
    ser.flushInput()
 
    # while True:
    ser.write(b'a')
    data = ser.readline().decode()
    print(data)
 
except:
    print("unable to open COM port")

Zadání 3. miniprojektu

Rozšiřte aplikaci vytvořenou v rámci předchozích dvou miniprojektů o komunikaci s Raspberry Pi Pico prostřednictvím UART rozhraní.

Možné řešení

Následující systém je minimalistickou variantou aplikace, která umožňuje měnit stav LED na RPi Pico pomocí formuláře ve webovém rozhraní.

Následující aplikace slouží pro demonstraci principu komunikace a neřeší stavy, ke kterým může dojít.

Kód pro RPi Pico

from machine import Pin, Timer
import sys
 
led = Pin('LED', Pin.OUT)
 
while True:
    # x = input()
    x = sys.stdin.read(1)
    if x == 'a':
        led.toggle()
    print(led.value())

Kód pro Flask server

import serial
from flask import Flask, request, render_template
 
app = Flask(__name__)
 
@app.route('/', methods=['GET', 'POST'])
def main():
    x = request.form.get('zmena')
    ret = -1
    if x is not None:
        ser = serial.Serial('COM7', 115200)
        ser.flushInput()
        ser.write(b'a')
        ret = int(ser.readline().decode())
        ser.close()
    return render_template('index.html', data=ret)
 
if __name__ == '__main__':
    app.run(debug=True)

HTML šablona

<!DOCTYPE html>
<html lang="en">
    <head>
        <title>Pico LED</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <form method="post">
            <input type="submit" name="zmena" value="Nastav">
        </form>
        {% if data == -1 %}
        <p>LED: <span style="color: azure;">neznámý stav</p>
        {% elif data == 0 %}
        <p>LED: <span style="color: red;">nesvítí</p>
        {% elif data == 1 %}
        <p>LED: <span style="color: green;">svítí</p>
        {% endif %}
    </body>
</html>