Wszelkiej maści sensory/sterowniki to nic innego jak kochane (prawie przez wszystkich) ESP8266 z biblioteką JSON i MQTT.
Na serwerze jest broker MQTT chyba najpopularniejszy mosqiuto. Serwer www nginx, parser PHP w wersji 7 oraz baza danych sqlite.
Dobór ie przypadkowy, ze względu na lekkość rozwiązań. Nginx zamiast tłustego apache, sqlite (szybki i lekki) zamiast słonia MySQL a teraz raczej MariaDB.
W szczegóły protokołu nie będę wchodził bo pewnie każdy wie co i jak, ja opiszę jak to połączyłem w swoim przypadku żeby zamiast klepać apki na dupodroida czy szajsfona (w moim gospodarstwie domowy używane są i jedne i drugie dlatego zdecydowałem się że aplikacja do sterowania będzie serwowane przez orangepi w formie aplikacji internetowej (HTML+PHP+SQL) przez co dostępna jest na każdym urządzeniu mającym przeglądarkę www.
Na temat budowy stron też nie będę pisał, ja mam swój framewrok, który napisałem kilka lat temu i cały czas go rozwijam, ale to nie ważne bo można sobie napisać w jednym pliku, pomięszać PHP z HTML i CSS itp. i też będie chodzić. U mnie jest rodzielenie logiki od widoku i podział na moduły.
Jak już wszytko zainstalowałem (apt-get install ...) pora to połączyć.
Do odbieranie danych po MQTT z czujników wykorzystałem klienta mosquito (mosquitto_sub - do subskrypcji, mosquitto_pub - można publikować).
Skrypt umieszczony w /etc/init.d pozwala na przekierowanie tego co przychodzi na interesujący nas topic do skryptu PHP który podejmie odpowiednie działania, co i jak wyjaśnię niżej.
Kod: Zaznacz cały
#!/bin/sh
### BEGIN INIT INFO
# Provides: mqttphp
# Required-Start: $all
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: MQTT service
# Description:
# Transport (MQTT) to PHP and sqlite.
### END INIT INFO
SERVERIP=192.168.5.9
DAEMON=/usr/bin/mosquitto_sub
MQTT_TOPIC=mqtt/\#
PHPSCRIPT=/var/www/iHome/mqtt/mosquitto.php
PID_FILE=/var/run/mqttphp.pid
test -x $DAEMON || exit 0
mqtt_start() {
echo "Start nasluchu ..."
rm $PID_FILE
touch $PID_FILE
echo $$ > $PID_FILE
$DAEMON -v -t $MQTT_TOPIC -h $SERVERIP |
while IFS= read -r line
do
/usr/bin/php7.0 $PHPSCRIPT $line
done
}
case "$1" in
start)
mqtt_start
;;
stop)
echo "Stop nasluchu ...\n\r"
for child in $(ps -o pid,ppid -ax | awk "{ if ( \$2 == $(cat $PID_FILE) ) { print \$1 }}")
do
kill $child
echo $child
done
;;
esac
exit 0
SERVERIP=192.168.5.9
DAEMON=/usr/bin/mosquitto_sub
MQTT_TOPIC=mqtt/\#
PHPSCRIPT=/var/www/iHome/mqtt/mosquitto.php
PID_FILE=/var/run/mqttphp.pid
czyli adres IP brokera MQTT w moim przypadku taki sam jak orange pi, dalej położenie binarki w systemi plików, MQTT_TOPIC to co chcemy subskybować, na końcu jest hasz czyli wszytko co przyjdzie na topic mqtt będzie przetwarzane, czyli np. mqtt/sensors albo mqtt/alram/lazienka itp. itd. PHPSCRIPT to ścieżka do skryptu PHP jaki to będzie przetwarzał, PID_FILE miejsce pliku w którym będzie ID procesu, potrzebne mi to było do testowania a potem uśmieracnia procesu jak zajdzie taka potrzeba.
Czarną robotę rodzi ten fragment kodu:
Kod: Zaznacz cały
$DAEMON -v -t $MQTT_TOPIC -h $SERVERIP |
while IFS= read -r line
do
/usr/bin/php7.0 $PHPSCRIPT $line
done
Teraz wkracza PHP.
Nie będę pokazywał całości bo to nie jest projekt opensource i jest wykorzystywany komercyjnie. Ale idea jest taka:
Jest klasa nadrzędna którą dziedziczą dzieci, kądy moduł to dziecko klasy nadrzędnej. W tej głównej klasie konfigurowane są wszystkie zasoby, jak baza danych, zmienne środkowiskowe, obsługa sesji itp. po czym na podstawie adresu podejmowana jest decyzja jaki moduł ma być uruchomiony.
Czyli to co przychodzi na do brokera w postaci JSON na topic nqtt/sensors/set/{idx:1,type:"ds18b20",id_value:"28XXYYZZ",value:2500}
Uruchamia moduł sensors, wywoływana jest akcja sety i przekazuje parametry.
Plik sensors jest bardzo prosty:
Kod: Zaznacz cały
<?php
include(PATH.'/mqtt/modules/mainclass.php');
class sensors extends MainClass
{
public function __construct() {
parent::__construct(get_class($this));
}
public function _default() {
}
public function set() {
$this->sql->query("INSERT INTO sensors (idx, type, id_value, value) VALUES(".$this->json['IDX'].", '".$this->json['type']."', '".$this->json['ID']."', ".$this->json['value'].")");
}
}
?>
tam wygląda to równie banalnie:
Kod: Zaznacz cały
abstract class MainClass {
....
protected $msg = '';
protected $json = null;
.....
// Konstruktor
public function __construct($className = '') {
$this->sql = sqlib::singleton();
$this->className = &$className;
}
public function msg($msg) {
$this->msg = &$msg;
$this->json = json_decode( $msg, true );
}
.....
}
Do łatwiejszego zarządzania bazą danych wykorzystany projekt https://www.phpliteadmin.org/ w sumie dwa pliki umieszczone na serwerze i przez przeglądarkę konfigurujemy co trzeba.
W drugą stronę czyli jak my chcemy coś zadać modułom czyli z poziomu PHP wysłać coś po MQTT do czujników wykorzysuję klasę phpMQTT (https://github.com/bluerhinos/phpMQTT).
Aplikacja do obsługi (warstwa widoku) wykorzystuje jQuery mobile (https://jquerymobile.com/) można łatwo i fajnie tworzyć "lekkie" interfejsy dla urządzeń mobilnych.
Tak mniej więcej to działa, jak ktoś ma jakieś pytania, sugestie itp. w miarę możliwości postaram się odpowiedzieć.
PS. zamieszczone kody maja charakter poglądowy, pochodzą z testowej maszyny (w tej chwili mam tylko do niej dostęp) i mogę się nico różnić od tych pracujących aktualnie.