V tomto projektu představím, jak lze pomocí potenciometru ovládat hlasitost Raspberry Pi a jak lze pomocí internetu nastavovat hlasitost počítači s Windows přes internetové protokoly. Projekt je programován v Pythonu a VB.NET, využívá analogově digitální převodník a pro komunikaci přes internet využívá protokol TCP.
První část projektu ukáže, jak ovládat hlasitost Raspberry Pi a druhá část (IoT varianta) ukáže, jak s pomocí potenciometru ovládat hlasitost vzdáleného počítače s Windows. Osobně využívám druhou variantu, protože Raspberry Pi nepoužívám jako primární PC, ale jako menší server pro různé (zejména mnou naprogramované) služby, nicméně i tak si myslím, že první varianta může být pro někoho užitečná.
Zapojení
Zapojení je jednoduché, je třeba propojit sběrnici SPI s ADC. Zvolil jsem MCP3208, lze zvolit obdoby. Zapojení MCP, ADC a jeho popis je v mém jiném projektu „Ohmmetr“, pokud Vás to zajímá, mrkněte tam. Potenciometr se zapojí „bočními“ vývody na zem a 3.3V, prostřední vývod na nultý kanál ADC.
Část 1 – nastavování hlasitosti Raspberry Pi
Program v pythonu inicializuje knihovny, ADC převodník, a uloží si pomocnou hodnotu s procentuální úrovní potenciometru. V nekonečném cyklu pak budeme sledovat úroveň a pokud se změnila, spustíme příkaz amixer s vhodnými parametry, který nastaví hlasitost Raspberry Pi. Příkaz se volá s parametrem -M, který zajistí relativně lineární a přirozený posun následovaný parametrem set 'PCM' hlasitost%. Místo PCM může být i něco jiného. PCM je pro zvuk z HDMI. Možnosti závisí na konfiguraci Raspberry Pi a lze je získat pomocí příkazu amixer (bez parametrů), který vypíše název (text v uvozovkách).
Například příkaz amixer -M set 'PCM' 75% nastaví hlasitost na 75%. Kód programu vypadá následovně.
from gpiozero import MCP3208 from time import sleep import subprocess # ADC převodník mcp = MCP3208(channel=0) # aktuální úroveň hlasitosti. Výchozí hodnota musí být přepočítaná, takže tu musí být hodnota menší než -3 nebo větší než 103 current = -10 while True: # měření hodnoty value = round(100 * mcp.value) # jednotka jsou procenta #print(str(value) + " current=" + str(current)) # logování, pro zapnutí odkomentujte sleep(0.1) # šetření procesoru. V tomto čase ho náš program nebude vytěžovat. if value != current: # uložení hodnoty pro budoucí porovnávání current = value # sestavení příkazu, který akci provede command = ["amixer", "-M", "set", "'PCM'", str(current) + "%"] # spuštění procesu pro nastavení hlasitosti proc = subprocess.Popen(command, stdout=subprocess.PIPE) # zachycení výstupu (out, err) = proc.communicate()
Část 2 – nastavování hlasitosti vzdálenému PC s Windows
Následující program musí mít 2 části. Server, což je PC s Windows a klient, což je Raspberry Pi. Na Windows je server napsaný v VB.NET a vyžaduje stažení knihovny AudioSwitcher.AudioApi.CoreAudio, kterou lze stáhnout s pomocí instalátoru závislostí NuGet. Ve Visual Studiu klikněte pravým na projekt, zvolte Manage NuGet Packages…, pak se přepněte na kartu Browse, vyhledejte tuto knihovnu a nechte ji nainstalovat.
Úvod do pokročilého IoT – Práce se sockety
Pokud jste již něco dělali s IoT dost možná to bylo nějaké http API nebo jste něco stahovali z internetu nebo jste posílali nějaký GET či POST požadavek či něco obdobného. Většina tutoriálu totiž učí IoT jen a pouze přes http, protože je to prostě nejjednodušší. Ničemně http jakožto univerzální protokol řeší hromady věcí okolo, které většina vývojářů ani zdaleka nepotřebuje/nevyužije/nezná a přenášejí se tak úplně zbytečně. Navíc tento protokol ani zdaleka nebyl pro IoT navržen. Přestože by se mohlo zdát, že těch pár bajtů nikoho nezabije, spočítejte si kolik dat se však zbytečně přenese na http hlavičkách a věcech okolo za určitou dobu, pokud to Vaše aplikace dělá nějak pravidelně nebo tyto informace přenáší více zařízení. Zjistíte že z malých čísel (desítky až stovky) se stanou tísíce až statisíce a to pouze v omezeném čase. Představte si, že Vaše aplikace poběží někde několik let a nebude data posílat jedno zařízení, ale několik stovek. Fungovat to bude, ale zbytečně vytížíte kapacity síťových linek, které mohly být využity efektivněji. Abychom si ukázali, jak se to dělá nízkoúrovňově, návrhem si svůj jednoduchý protokol, tak jako návrháři http navrhovali ho. Stejně jako http postavíme ho nad TCP. Existuje ještě UDP a pár dalších. UDP je jednoduší a šetří ještě více dat, ale ten negarantuje doručení dat ani doručení dat ve správném pořadí ani hromadu dalších věcí, které TCP řeší a nám se budou hodit. Protokol bude jednoduchý, klient v něm bude posílat byty při změně úrovně hlasitosti (to již máme vyřešené v první části) a server bude posílat pravidelně 0 aby klient věděl, že server žije, případně kvůli NATům, které neaktivní připojení občas zavírají při neaktivitě.
Komunikace vyšší 5,6,7 síťové vrstvy (pro pochopení síťových vrstev si vyhledejte na wikipedii OSI model) se 4 síťovou vrstvou (TCP), kterou již zajišťuje operační systém se využívá takzvaných socketů. Sockety mohou být několika typů, nám bude stačit sockety typu STREAM a fakt že funguje nad internetovým protokolem IP. Sockety jsou však využívaný i u úplně odlišných protokolů, které s internetem nikdy nebyli spjaty, příkladem je Bluetoth. Po vytvoření socketu Ať už v VB.NET nebo pythonu (budeme je dělat na obou platformách) je podstatných 5 metod - Bind, Listen a Connect, Accept, Send.
Bind použijeme na serveru a říká operačnímu systému že určitý TCP nebo UDP (v našem případě TCP) port si chceme vyhradit pro sebe. Krom portu se ještě udává IP adresa síťového zařízení, na kterém chceme poslouchat. Lze použít univerzální adresu 0.0.0.0, která zajistí poslech na všech síťových kartách.
Listen použijeme na serveru říká operačnímu systému, že chceme dedikovat nějakou frontu a čekat klienty v této frontě. Velikost fronty se udává jako parametr této metody.
Connect se používá na klientovi a slouží k sestavení spojení s vzdáleným serverem. Adresa a vzdálený port se obvykle předává jako parametr. Základní kód serveru a klienta bude vypadat následovně.
Metoda Accept slouží na serveru k přijmutí klienta.
Send odešle pole bytů na druhou stranu připojení.
Server - VB.NET
Dim sock As Sockets.Socket = New Socket(SocketType.Stream, ProtocolType.IP) sock.Bind(New IPEndPoint(IPAddress.Parse("0.0.0.0"), 1234)) sock.Listen(1) While True Dim client = sock.Accept() ' čekání a spracovávání dat přijatých od klienta ... End While
Klient – Python
clientsocket = socket.socket() clientsocket.settimeout(10) clientsocket.connect(("192.168.0.130", 1234)) while True: # měření hodnoty ... if value != current: # změna hdonoty clientsocket.send(bytes([value]))
A teď už zbývá toto jenom napasovat do měření hodnoty a nastavování hlasitosti na serveru. Nastavování hlasitosti ve Windows zajistí tyto dva řádky. Ukázka nastaví hlasitost na 50%.
Dim speaker = (New CoreAudioController()).DefaultPlaybackDevice speaker.Volume = 50
Při práci se sítí je třeba ještě ošetřit zejména nečekané stavy a odpojení klienta. Vše by mělo již být pochopitelné z kódu.
Kompletní kód serveru (VB.NET)
Imports System.Net Imports System.Net.Sockets Imports System.Threading Imports AudioSwitcher.AudioApi.CoreAudio Module Module1 Sub Main() ' ovladač hlasitosti Dim speaker = (New CoreAudioController()).DefaultPlaybackDevice ' TCP server Dim sock As Sockets.Socket = New Socket(SocketType.Stream, ProtocolType.IP) sock.Bind(New IPEndPoint(IPAddress.Parse("0.0.0.0"), 1234)) sock.Listen(1) ' čekání a obsluhování klientů While True Dim client = sock.Accept() Console.WriteLine("Klient připojen. Linka obsazena.") ' vyjimka může nastat při pádu nebo odpojení klienta. V obou dvou případech se vrátíme čekat na dalšího klienta. Try Dim buffer(100) As Byte ' čekání a spracovávání dat přijatých od klienta While True If client.Available > 0 Then ' čtení poslední přijaté hodnoty Dim received = client.Receive(buffer) If received > 0 Then Dim val = buffer(received - 1) If val >= 0 And val <= 100 Then ' nastavování hlasitosti Console.WriteLine("Volume is set " & val) speaker.Volume = val End If End If Else ' data nepřišla. Kontrola, že klient žije. If client.Poll(50, Sockets.SelectMode.SelectRead) And client.Available = 0 Then client.Close() Throw New Exception() End If client.Send(New Byte() {0}) ' je čas jít spát Thread.Sleep(10) End If End While Catch ex As Exception Console.WriteLine("Klient odpojen. Linka je volná.") End Try End While End Sub End Module
Kompletní kód klienta (Python)
from gpiozero import MCP3208 from time import sleep import subprocess import socket import sys clientsocket = socket.socket() clientsocket.settimeout(10) clientsocket.connect(("192.168.0.130", 1234)) # ADC převodník mcp = MCP3208(channel=0) # aktuální úroveň hlasitosti. Výchozí hodnota musí být přepočítaná, takže tu musí být hodnota menší než -3 nebo větší než 103 current = -10 while True: # měření hodnoty value = round(100 * mcp.value) # jednotka jsou procenta #print(str(value) + " current=" + str(current)) # logování, pro zapnutí odkomentujte sleep(0.1) # šetření procesoru. V tomto čase ho náš program nebude vytěžovat. if value != current: # +-3 je tolerance kolísajících hodnot. # uložení hodnoty pro budoucí porovnávání current = value # zaslání dat clientsocket.send(bytes([current]))
Běh na pozadí a po spuštění počítače
Server v pythonu
Běh serveru v pythonu po spuštění Raspberry Pi lze jednoduše zajistit přidáním řádku příkazu následovaného ampersandem do souboru /etc/rc.local. Řádek musíte přidat před příkaz exit a měl by vypadat cca následovně.
python3 /cesta/ke/skriptu.py &
Na ampersand na konci nesmíte zapomenout, pak by se Vám zaseklo bootování Raspberry Pi.
Klient v VB.NET
Základem je aby klient neměl viditelné konzolové okno. To lze zajistit přepnutí projektu na formulářovou aplikaci. Lze to udělat ve vlastnostech projektu (Nabídka Project > Properties) a na kartě Applications zvolit v nabídce Application Type jako typ aplikace Windows Forms Application. Zajistit aby se aplikace spustila po spuštění PC lze několika způsoby. Jeden z nejjednodušších je plánovač úloh. Otevřete je vyhledáním v nabídce start v českých Windows pod pojmem Plánovač úloh. V pravém panelu zvolíte Vytvořit úlohu, vyberete že chcete úlohu konfigurovat pro Windows 10 (pokud máte jinou verzi Windows, tak vyberte tu co máte), na kartě Aktivační události přidáte přihlášení uživatele a na kartě Akce přidáte akci spustit program a nastavíte tam cestu k vašemu programu.
Závěr
Viděli jste aplikaci, která komunikuje přes síť, neposílá zbytečná data, viděli jste sestavení socketu a komunikaci přes síť. Výsledkem je potenciometr, pro ovládání hlasitosti vzdáleného počítače řízený pomoci Raspberry Pi.
Zdrojové kódy můžete stáhnout zde.
Použité součástky
- Raspberry Pi (zkoušeno na 3, mělo by fungovat i na ostatních verzích) - https://dratek.cz/arduino/1385-raspberry-pi-3-model-b-quad-core-1-2-ghz-64bit-cpu-1gb-ram-wifi-bluetooth-4-1-1473147122.html
- MCP3208 (nebo podobný, stačí s méně kanály): http://www.microchip.com/wwwproducts/en/en010534
- Nepájivé kontaktní pole: https://dratek.cz/arduino/1433-nepajive-kontaktni-pole-zy-103-1475273073.html
- Vodiče F-M: https://dratek.cz/arduino-kabelaz-propoje-rozsireni/1214-arduino-vodice-samice-samice-40-kusu-1457705007.html
- Vodiče M-M: https://dratek.cz/arduino-kabelaz-propoje-rozsireni/1063-arduino-vodice-samec-samec-40-kusu-1500635966.html
- Potenciometr (libovolný, doporučuji v rozmezí 1K až 10K): https://dratek.cz/arduino/1176-potenciometr-1kohm-linearni-1451772754.html