Raspberry Pi
41) Dálková komunikace s RasPi přes webové
rozhraní nebo přes SMS
Účelem tohoto článku je zprovoznění dálkového ovládání RasPi
pomocí webového rozhraní, nebo pomocí SMS zpráv z mobilního
telefonu.
Typickým příkladem použití této funkce může být ostraha nějakého
objektu (když narušitel rozbije okno, odešle se SMS), nebo ovládání
domácnosti na dálku (odesláním SMS se například zapne topení).
Většina běžných uživatelů nemá k dispozici veřejnou IP adresu,
na kterou by připojili raspíčko. Kvůli tomu se nedá přistupovat
k RasPi vzdáleně přes internet.
I přesto je ale možné (alespoň omezeně) komunikovat s RasPi na dálku.
K tomu je potřeba zřídit veřejně přístupnou webovou stránku s
podporou PHP skriptů a FTP přenosem. Takovouto službu dnes nabízí
mnoho webhostingových společností a mnoho z nich je možné zřídit i
zdarma.
UPOZORNĚNÍ
Všechny příklady jsou psané
co nejjednodušším způsobem.
Vůbec jsem se v nich nezabýval síťovou bezpečností.
Kromě jednoduché ochrany heslem není v následujících příkladech
žádná další ochrana. Příklady nejsou odolné ani proti odposlechu.
Mějte na paměti, že veškerá komunikace mezi RasPi a internetem
probíhá po nezabezpečených linkách, takže případný
narušitel
může získat kontrolu nad ovládáním RasPi.
|
První fáze: Ovládání RasPi přes webové rozhraní
Umožňuje pomocí příkazů zadávaných do formuláře na veřejně přístupné
webové stránce, ovládat činnost RasPi.
Postup:
Na nějakém FreeHostingovém serveru (např. http://www.hostuju.cz,
nebo http://ic.cz ) si zaregistrujte svou stránku.
(já jsem ten příklad realizoval na vlastním webhostingu, takže webové
adresy v příkladech jsou trochu jiné).
Do takto vzniklého prostoru uložte stránku index.htm, která obsahuje tento zdrojový kód:
<HTML>
<HEAD>
<META http-equiv="cache-control" content="no-cache">
<META http-equiv="pragma" content="no-cache">
<META name="robots" content="noindex, nofollow, noarchive">
</HEAD>
<BODY>
<p align="center">Příklad dálkového řízení přes webové rozhraní</p>
<hr>
<form method="POST" action="odeslat.php">
<div align="center">
<center>
<table border="0" width="236">
<tr>
<td width="300">Příkaz :</td>
<td width="164"><input type="text" name="prikaz" size="20" value=""></td>
</tr>
<tr>
<td width="300">Heslo:</td>
<td width="164"><input type="password" name="heslo" size="20"></td>
</tr>
<tr>
<td width="300" colspan="2">
<p align="center"><input type="submit" value="Odeslat" name="B1"></td>
</tr>
</table>
</center>
</div>
</form>
</BODY>
</HTML>
|
Tento HTML kód vytvoří vstupní formulář:
Dále je třeba vytvořit PHP skript, který bude zpracovávat
zadaná data z formuláře.
Funkce je jednoduchá: V případě, že bude vloženo správné
heslo, uloží zadaný příkaz do souboru "kod.txt".
Jméno souboru s PHP skriptem bude "odeslat.php" a bude obsahovat následující
kód:
<HTML>
<HEAD>
<META http-equiv="cache-control" content="no-cache">
<META http-equiv="pragma" content="no-cache">
<META name="robots" content="noindex, nofollow, noarchive">
</HEAD>
<BODY>
<?php
// zjištění hodnoty předávaného parametru z formuláře na strance "index.htm"
$prikaz=$_POST['prikaz'];
$heslo=$_POST['heslo'];
if ($heslo != "-H-E-S-L-O-") // tady si zvolte vlastní heslo
{
echo "Spatne heslo";
}
else
{
if ($prikaz == "")
{
echo "Prikaz chybi, takze se nic nezapsalo";
}
else
{
// pokud je příkaz zadaný, uloží ho do souboru "kod.txt"
$soubor = "kod.txt";
$fh = fopen($soubor, 'w+');
fwrite($fh, $prikaz);
fclose($fh);
echo "Prikaz '" . $prikaz . "' byl zapsan do souboru 'kod.txt'";
}
}
?>
</BODY>
</HTML>
|
Protože kvůli přístupovým právům bývá problém s vytvořením nového souboru "kod.txt" pomocí PHP skriptu, vytvořte
na webu prázdný soubor s názvem "kod.txt" ručně a pak mu nastavte
přístupová práva na "777". Toto je třeba udělat pouze jednou. Když
už bude mít soubor neomezená práva, nebude problém s jeho automatickým
přepisováním.
Tím je hotová webová část projektu.
Dále je třeba v RasPi vytvořit skript, který bude v
pravidelných intervalech kontrolovat soubor "kod.txt" na
internetu a podle
jeho obsahu bude vykonávat požadované akce.
Pro jednoduchý příklad jsem v RasPi vytvořil skript, který vyhodnocuje
dva příkazy:
rozsvit24 ... rozsvítí LEDku na portu GPIO24
zhasni24 ... zhasne LEDku na portu GPIO24
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import time # nacteni systemovych podprogramu pro praci s casem
import RPi.GPIO as GPIO # nacteni systemovych podprogramu pro praci s konektorem GPIO
import urllib2 # cteni souboru umisteneho na internetu
GPIO.setwarnings(False) # zruseni vystraznych hlaseni (neuzavrene kanaly)
GPIO.setmode(GPIO.BCM) # styl cislovani podle signalu (nejsou to HW piny)
GPIO.setup(24, GPIO.OUT) # nastaveni GPIO24 (HW pin 18) na vystup (LED)
def rozsvit_LED(signal): # podprogram pro rozsveceni LED na prislusnem GPIO
print "ropzsvecuji LED"
GPIO.output(signal, GPIO.HIGH)
def zhasni_LED(signal): # podprogram pro zhasnuti LED na prislusnem GPIO
print "zhasinam LED"
GPIO.output(signal, GPIO.LOW)
stary_kod = ""
while True: # nekonecna smycka
try:
# adresa verejne pristupneho souboru "kod.txt" s ridicim prikazem
response = urllib2.urlopen('http://www.astromik.org/raspi/webdrive/kod.txt')
novy_kod = response.read()
except:
novy_kod= "chyba prenosu"
print "precteny kod z internetu: " , novy_kod
# podle kodu precteneho z internetu se vykonavaji prislusne akce:
if (stary_kod != novy_kod):
if (novy_kod == "rozsvit24"):
rozsvit_LED(24)
if (novy_kod == "zhasni24"):
zhasni_LED(24)
stary_kod = novy_kod
time.sleep(10)
|
Pak už jen stačí tento skript v RasPi spustit.
Když pak z libovolného místa otevřete na internetu stránku
s formulářem (index.htm), můžete zadávat
přikazy. Tyto příkazy si RasPi nejdéle po 10 sekundách přečte a
vyhodnotí.
Druhá fáze: Zobrazování dat z RasPi na webových stránkách
Pomocí této operace je možné například zobrazovat na
internetu teplotu naměřenou čidly připojenými k RasPi, zobrazovat
fotku z webkamery, nebo zobrazovat stav GPIO portů.
Princip spočívá v tom, že se naměřené hodnoty nejdříve
ukládají
do nějakého souboru v RasPi a tento soubor se pak přes FTP přenáší na veřejně
přístupný web.
Protože by při častém přepisování tohoto souboru
mohlo dojít k poškození SD karty, je třeba nejdříve zprovoznit
RAMdisk a soubor pak ukládat do takto vzniklého prostoru, kde přepisování
nevadí.
Návod na vytvoření RAMdisku je zde: RAMdisk
Pro příklad jsem do předchozího skriptu doplnil měření
osvětlení (nově přidané věci jsou doplněny červeně):
#!/usr/bin/python
# -*- encoding: utf-8 -*-
import time # nacteni systemovych podprogramu pro praci s casem
import RPi.GPIO as GPIO # nacteni systemovych podprogramu pro praci s konektorem GPIO
import urllib2 # cteni souboru umisteneho na internetu
from ftplib import FTP # podprogramy pro prenos souboru na internet pres FTP
import smbus # vyuzito jen pro priklad I2C komunikace s cidlem osvetleni
bus = smbus.SMBus(1) # novejsi varianta RasPi (512MB)
#bus = smbus.SMBus(0)) # starsi varianta RasPi (256MB)
addr = 0x23 # i2c adresa cidla osvetleni
GPIO.setwarnings(False) # zruseni vystraznych hlaseni (neuzavrene kanaly)
GPIO.setmode(GPIO.BCM) # styl cislovani podle signalu (nejsou to HW piny)
GPIO.setup(24, GPIO.OUT) # nastaveni GPIO24 (HW pin 18) na vystup (LED)
def rozsvit_LED(signal): # podprogram pro rozsveceni LED na prislusnem GPIO
print "ropzsvecuji LED"
GPIO.output(signal, GPIO.HIGH)
def zhasni_LED(signal): # podprogram pro zhasnuti LED na prislusnem GPIO
print "zhasinam LED"
GPIO.output(signal, GPIO.LOW)
def posli_na_web(html_text): # podprogram pro odeslani dat na webovy server
print "odesilam data na web"
# zacatek a konec webove stranky, mezi ktery se vlozi html_text
web_hlavicka="<html><head><meta http-equiv='refresh' content='5'></head><body>"
web_paticka ="</body></html>"
# vytvoreni mistniho souboru, ktery se ma odeslat na web
soubor=file("/home/pi/ramdisk/webdata.htm",'w')
soubor.write(web_hlavicka + html_text + web_paticka)
soubor.close()
# odeslani souboru na web s osetrenou moznou chybou komunikace
try:
ftp = FTP('ftp.astromik.org', 'raspiftp.astromik.org' , 'TAJNE-FTP-HESLO')
ftp.storbinary('STOR /www/raspi/webdrive/webdata.htm', open('ramdisk/webdata.htm', 'rb'))
except:
print "nepodarilo se navazat spojeni na server"
stary_kod = ""
while True: # nekonecna smycka
try:
# adresa verejne pristupneho souboru "kod.txt" s ridicim prikazem
response = urllib2.urlopen('http://www.astromik.org/raspi/webdrive/kod.txt')
novy_kod = response.read()
except:
novy_kod= "chyba prenosu"
print "precteny kod z internetu: " , novy_kod
# podle kodu precteneho z internetu se vykonavaji prislusne akce:
if (stary_kod != novy_kod):
if (novy_kod == "rozsvit24"):
rozsvit_LED(24)
if (novy_kod == "zhasni24"):
zhasni_LED(24)
# v pravidelnych intervalech se ctou nejaka data a ukladaji se na web:
# pro priklad je tu pouzito mereni osvetleni, ale stejne se muze zjistovat
# treba teplota, nebo stav GPIO portu
svetlo = bus.read_i2c_block_data(addr,0x11)
hodnota_svetla= (svetlo[1] + (256 * svetlo[0])) / 1.2
nejaky_data = "Aktualni osvetleni cidla na RasPi je: <b>" + str(int(hodnota_svetla)) + "</b> lx"
posli_na_web(nejaky_data)
stary_kod = novy_kod
time.sleep(10)
|
Pomocí tohoto kódu vznikne na webu soubor webdata.htm,
který obsahuje aktuální hodnotu osvětlení.
Tato hodnota se každých
10 sekund aktualizuje.
Ukázka činnosti je tady:
V horní části je spuštěný webový prohlížeč, pomocí kterého
je možné odkudkoli ovládat LEDku, která je připojená k RasPi.
Dole je pak terminál RasPi, ve kterém běží automatický skript
na vyhodnocování obsahu souboru "kod.txt".
Každých 10 sekund se na jiné webové stránce aktualizuje stav osvětlení
čidla.
Třetí fáze: Řízení RasPi přes SMS
Aby bylo možné ovládat RasPi přes SMS, je třeba se
zaregistrovat na nějaké SMS bráně.
Já jsem využil tuto webovou službu:
http://sms.sluzba.cz
(Tato služba je placená - jedno spuštění skriptu stojí
asi 1,80Kč. K tomu je třeba připočítat i cenu SMS podle operátora.)
V této službě se musí nastavit položka "SMS
Action", která umožňuje po přijetí SMS speciálním telefonním
číslem, spustit libovolný PHP
skript na serveru.
Já jsem provedl toto nastavení:
Jak je uvedeno v nastavovacím formuláři, při přijetí SMS se spouští
skript "http://www.astromik.org/raspi/webdrive/sms-write.php"
Zdroják toho skriptu "sms-write.php" vypadá takto:
<?php
// zjištění parametrů příkazové řádky po přijetí SMS
$odesilatel = $_GET['sender']; # číslo ze kterého byla SMS odeslana
$keyword = $_GET['keyword']; # první kód, který se definuje v registraci služby "SMS action"
$identifier = $_GET['identifier']; # druhý kód, který se definuje v registraci služby "SMS action"
$parametr = $_GET['text']; # text SMS (hlavní předávaný parametr, který obsahuje příkazy)
$smsid = $_GET['smsid']; # unikátní kód SMS
$cas = $_GET['time']; # čas přijetí SMS
if ($parametr == "osvetleni")
{
$content = join ('', file ('http://www.astromik.org/raspi/webdrive/webdata.htm'));
echo $content . "<br>";
}
// pokud je parametr zadaný, uloží ho do souboru "kod.txt"
if ($parametr == "")
{
echo "Parametr chybi, takze se nic nezapsalo<br>";
}
else
{
$soubor = "kod.txt";
$fh = fopen($soubor, 'w+');
fwrite($fh, $parametr);
fclose($fh);
echo "Prikaz '" . $parametr . "' byl zapsan do souboru 'kod.txt'<br>";
}
?>
|
Skript je hodně podobný prvnímu příkladu pro ovládání
RasPi přes webový formulář.
I tady jde o to, že parametr, který je odeslán jako text SMS, se
uloží do souboru "kod.txt".
POZOR:
V tomto příkladu už není ani ochrana heslem. Narušiteli,
který by znal tento zdrojový kód, by stačilo otevřít adresu :
http://www.astromik.org/raspi/webdrive/sms-write.php?text=HACKNUTO
... a tím by do souboru "kod.txt" uložil
text "HACKNUTO".
V horším případě by mohl jako text použít i nějaký kus škodlivého
kódu.
Důrazně proto doporučuji použít ještě nějaké další
techniky zabezpečení.
|
Odesílaná SMS vypadá takto:
CODE je klíčové slovo
31682 je identifikátor
tyto dvě hodnoty jsou předdefinovány v nastavení funkce "SMS
Action"
pak následuje vlastní příkaz (rozsvit24), který se zapisuje do
souboru "kod.txt".
Po úspěšném spuštění PHP skriptu se na mobil vrátí text s výstupem
toho PHP skriptu.
Takže se tímto způsobem může třeba zjistit aktuální stav osvětlení.
Video s příkladem ovládání LEDky připojené k RasPi přes SMS je tady:
Odezva na SMS nějakou dobu trvá, ale je vidět, že to funguje.
Detail přijaté SMS s hodnotou aktuálního osvětlení
(na videu je to nějak špatně čitelné)
Čtvrtá fáze: Odeslání SMS z RasPi
Tato funkce může být dobrá například k ostraze nějakého
objektu. Když narušitel sepne spínač (nebo třeba aktivuje infračidlo), RasPi to vyhodnotí a přes SMS
bránu odešle SMS na zadaný mobil.
Tady jsem také využil web http://sms.sluzba.cz.
I tato služba je placená (jedna odeslaná SMS stojí asi korunu).
Zprovoznění API pro odesílání SMS je popsáno na jejich webu a neměl
jsem s tím žádný problém, tak asi nemá cenu to sem znova všechno
opisovat.
Jen v krátkosti:
1) Uložit na web soubor "apipost30.php" (ke stažení je tady
).
2) V tom samém adresáři na webu vytvořit soubor "sms_posli.php" s tímto
obsahem (telefonní číslo upravit podle potřeby):
<HTML><head>
<META http-equiv="cache-control" content="no-cache">
<META http-equiv="pragma" content="no-cache">
</head>
<BODY>
<?php
$zprava =$_GET['zprava'];
$LOGIN =$_GET['jmeno'];
$PASSWORD =$_GET['heslo'];
require_once("apipost30.php");
$apipost = new ApiPost30($LOGIN, $PASSWORD);
$apipost->set_recipient("606123456"); // telefonni cislo prijemce
$apipost->set_text($zprava);
$apipost->send();
echo "Odeslano <br>" . $zprava
?>
</BODY>
</HTML>
|
3) Na RasPi vytvořit a spustit tento skript (jméno a heslo upravit
podle přihlašovacích údajů do sms.sluzba.cz):
#!/usr/local/bin/python
import RPi.GPIO as GPIO
import urllib2
import time
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(8, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.wait_for_edge(8, GPIO.FALLING)
print "bylo stisknuto tlacitko"
odeslano = False
while odeslano == False:
try:
adresa = 'http://www.astromik.org/raspi/webdrive/sms_posli.php'
response = urllib2.urlopen(adresa + '?jmeno=jméno&heslo=heslo&zprava=TLACITKO_STISKNUTO')
sms = response.read()
odeslano = True
except:
print "chyba prenosu"
time.sleep(5)
|
Skript čeká na stisk tlačítka a když k tomu stisku dojde, tak
pomocí API odešle SMS se zprávou "TLACITKO_STISKNUTO".
(Zprava nesmi obsahovat mezery. Pokud potřebujete odesílat text s
mezerami, nahraďte je podtržítkem, nebo kódem %20.)
Video:
Detailní fotka přijaté SMS:
Aktualizace 2.8.2016
Doplnění RAMdisku při odesílání dat na webserver.
|