Witam wszystkich!!
W kilku zdaniach postaram się przedstawić ideę projektu i problemy na jakie natrafiłem.
Programowaniem zajmuje się od niedawna (jest to dla mnie ciekawe hobby), z wykształcenia elektronik, a że często elektronika idzie w parze z programowaniem postanowiłem że spróbuję zrealizować swój pomysł na tak zwany inteligętny dom którym będę mógł zarządzać z telefonu z dowolnego miejsca. Wiem ze w sieci jest sporo gotowców ale co to za frajda i przyjemność z zrobienia copy/paste !!.
Otóż cała idea wygląda tak w domu mam kilka mikrokontrolerów opartych na ESP8266(ESP12E) które zbierają dane temperatur,stanów przekaźników, różnych czujników i same mogą też coś załączyć, wszystkie sterowniki ESP komunikują (po WI-FI)się miedzy sobą i domowym serwerem po UDP (broadcast) na danym porcie.
Na serwerze(Ubuntu server 16.04) napisałem w php (wiem że zasadniczo php do tego nie służy, ale ten język mi się spodobał) na miarę swoich możliwości program który jest uruchomiony w tle i jego zadaniem jest nasłuchiwanie czy do gniazda TCP został podłączony klient, jeżeli tak to po odebraniu danych od klienta (w tym przypadku aplikacja na telefonie) przekazuje je dalej ale już po UDP i następnie w drugą stronę dane z UDP wysyła po TCP do podłączone klienta.
np1: dane od klienta TCP w postaci (string) "U=" jest wysyłane co 100milisekund, server po otrzymaniu "U=" wysyła do klienta dane które otrzymał ze sterowników ESP po UDP. (VP201=1/VP202=0/VP203=0/) aktualne stany logiczne przekaźników oznaczonych jako 201,202,203
np2: dane od klienta TCP w postaci (string) "?=" dane otrzymane od klienta zaczynające się od "?=" informują server że wszystko co jest po '?=" ma być dalej przesłane po UDP(broadcast) do wszystkich sterowników ESP jednoczesnie.(?=?VP201=1 otrzymane z TCP po przemieleniu przez server będą wysłane przez UDP w postaci ?VP201=1 ,to załączy przekaźnik oznaczony jako 201)
Ogólnie ta komunikacja jakoś działa i nawet szybko ale problem jest taki ze server ma losowe zawieszki, czasami działa bez problemu ponad tydzień a czasami kilka godzin. W serwerze nie ma przewidzianej obsługi wielu klientów i raczej nie będzie(nie przewiduję żeby jeszcze ktoś to obsługiwał)
Przedstawiam kod programu w komentarzach starałem się opisać do czego służy dana funkcja. Jeżeli przeoczyłem jakąś oczywistą informacje bądź coś jest niezrozumiałe proszę dać znać. Jestem otwarty na wszelkie sugestie co do zmian w programie lub ewentualnie napisanie od nowa.
Z góry dziękuję!
<?php
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$address = '0.0.0.0';
$port =5555; //nr portu nasłuchu TCP
//************UDP***************
$socket_UDP =socket_create(AF_INET,SOCK_DGRAM,SOL_UDP);
echo "Socket created \n";
socket_set_option($socket_UDP, SOL_SOCKET, SO_BROADCAST, 1);
socket_bind($socket_UDP,'0.0.0.0',1111) or die('Could not bind to address'); //otwarcie portu nasluchu UDP dla aktualnych danych nr portu 1111
//***********TCP******************
if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}
socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1);
if (socket_bind($sock, $address, $port) === false) {
echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
if (socket_listen($sock, 1) === false) {
echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
}
socket_set_nonblock($socket_UDP);
socket_set_nonblock($sock);
do {
usleep(10000); //zeby nie zamulać procka gdy brak podlaczonego klienta
// @socket_recvfrom($socket_UDP, $dane_UDP, 512,MSG_DONTWAIT, $remote_ip, $remote_port);
//if (strlen($dane_UDP)>1){
//echo $dane_UDP."\n";
$odczyt_UDP = socket_read($socket_UDP,512);
if (($msgsock = @socket_accept($sock)) === false) {
// echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n";
unset($msmsock);
//continue;
}else {
$send_info="Start";
socket_write($msgsock, $send_info, strlen($send_info));
do {
if (false === ($buf = @socket_read($msgsock, 2048, PHP_NORMAL_READ))) {
// echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n";
//echo "w false socket read \n";
if ($buf==False ||$buf==0){ //echo"kasowanie rozlaczonego klienta nr ".$msgsock;
unset($msgsock);
flush();
break;
}
break ;
}
$buf = trim($buf); //parsowane danych od klienta z TCP
if (!$buf = trim($buf)) {
continue;
}
/* //echo "3\n";
if ($buf == 'quit') {
break;
}
//echo "4\n";
if ($buf == 'shutdown') {
socket_close($msgsock);
break 2;
}
*/
if(strlen($buf)>1){
$rozkaz =substr($buf,0,strpos($buf,"=")); //wyłuskanie komendy rozkazu
if($rozkaz =="U"){ //update danych . odebrane dane z UDP wysyła do podłaczonego klienta z TCP
$odczyt_UDP = @socket_read($socket_UDP,512);
if(strlen($odczyt_UDP)>1){
socket_write($msgsock,$odczyt_UDP,strlen($odczyt_UDP));
$odczyt_UDP="";
}
$odczyt_UDP="";
}
elseif($rozkaz == "?"){ //wysyłanie danych otrzymanych od klienta z TCP na UDP broadcast (sterowniki na esp8266)
try {
$wartosc =substr($buf,strpos($buf,"=")+1);
$string_txt="?".$wartosc;
//socket_sendto($socket_UDP,$string_txt,strlen($string_txt),0,'192.168.1.255',2222);
send_udp($socket_UDP,$string_txt);
}
catch(Exception $e){
echo 'Wystąpił wyjątek nr '.$e->getCode().', jego komunikat to:'.$e->getMessage();
}
}
elseif($rozkaz == "R"){ //pobieranie danych z mysql do klienta TCP w celu rysowania wykresu (np R100 (indeks 100) to temperatura zewnetrzna)
$wartosc =substr($buf,strpos($buf,"=")+1);
$output= odczyt_mysql($wartosc);
socket_write($msgsock, $output, strlen($output)); //wysłanie logu do klienta
}
}
$odczyt_UDP="";
}while (true);
flush();
$odczyt_UDP="";
@socket_close($msgsock);
}
} while (true);
flush();
/*function read($sock){
while($buf = @socket_read($sock, 1024, PHP_NORMAL_READ))
if($buf = trim($buf))
break;
return $buf;
}*/
/*
function UDP_odczyt($UDP_socket){
$r = socket_recvfrom($UDP_socket, $dane_UDP, 512,MSG_DONTWAIT, $remote_ip, $remote_port);
return $dane_UDP;
}
function send_udp($socket,$text_to_send){
socket_sendto($socket,$text_to_send,strlen($text_to_send),0,'255.255.255.255',2222);
} */
function odczyt_mysql($wartosc){
$servername = "localhost";
$username = "xxxx";
$password = "xxxxxxxxx";
$dbname = "log_parametrow";
$output_msg="R$wartosc=";
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$sql = "SELECT VP$wartosc FROM (SELECT * FROM VP$wartosc ORDER BY Lp DESC LIMIT 2880) sub ORDER BY Lp ASC";// limit do 2880 pomiarow co daje wykres z ostatnich 9 dni
//$sql = "SELECT VP$wartosc FROM VP$wartosc";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$output_msg.= $row["VP$wartosc"] . ":";
}
//echo $output_msg;
} else {
// echo "0 results";
}
return $output_msg;
$conn->close();
}
?>