大家好,我是小智。
今天,我们结合前面的文章内容,进行一个简单的物联应用。
在物联网中最简单直接的应用就远程开关电灯,今天小智将为大家一一讲述。
智能灯控
本文用到的技术和工具都相对简单,在阅读本文时,建议没有关注小智往期文章的朋友回头去看看,对前面的一些知识有所了解。
物联网通讯之Swoole创建Websocket服务器,一切从简单开始
物联网之实时通讯,使用WorkerMan创建websocket服务器
物联网通讯之Swoole创建TCP服务器,并与调试工具进行通讯
回顾完全文后,正式进入主题。
一、设计一个简单实用的架构
系统架构
从上图可以看出,云端需要建立三个服务,分别是:tcp服务、mqtt服务、websocket服务。
线下需要准备串口服务器(tcp转485)、485硬件(继电器)。
控制端则可以是浏览或者App,当然小程序也可以,只要支持websocket的客户端都行。
二、服务器搭建
我们将用上一期学到的swoole建立websocket服务器应用到这里,做到真正的学以致用。然后在这个基础上,再建立一个tcp服务器。
代码相当简单,暂不考虑安全性,稳定性等方面(这些需要大家自己深入,小智只能把朋友们带进门)。
下面是详细代码:
tcpserver.php
<?php
use TrytoMqtt\Client;
require_once __DIR__ . '/vendor/autoload.php';
$device_maps = [];
//创建Server对象,监听 localhost:39001端口
$serv = new Swoole\Server("localhost", 39001);
$options = [
'clean_session' => false,
'client_id' => 'demo-tcpserver-123456',
'username' => '',
'password' => '',
];
$mqtt = new Client('localhost', 1883, $options);
$serv->on('WorkerStart', function ($serv, $worker_id) use ($mqtt) {
echo "tcp worker start\r\n";
$mqtt->onConnect = function ($mqtt) {
$mqtt->subscribe('/websocket485');
};
$mqtt->onMessage = function ($topic, $content) use ($serv) {
global $device_maps;
var_dump($device_maps);
echo date('Y-m-d H:i:s') . "Mqtt Receive=================================\r\n";
var_dump($topic, $content);
$json = json_decode($content, true);
var_dump($json);
if (isset($device_maps[$json['device']])) {
var_dump($json['msg']);
$data = json_decode($json['msg'], true);
echo "send instruction \n";
$serv->send($device_maps[$json['device']]['fd'], hex2bin(str_replace(' ', '', $data['instruction'])));
}
};
$mqtt->onError = function ($exception) use ($mqtt) {
echo "error\n";
};
$mqtt->onClose = function () {
echo "close\n";
};
$mqtt->connect();
});
//监听连接进入事件
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect.\n";
});
//监听数据接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $msg) use ($mqtt) {
global $device_maps;
var_dump($device_maps);
echo date('Y-m-d H:i:s') . "=================================\r\n";
$json = json_decode($msg, true);
var_dump($json);
//todo已经注册的TCP串口服务器,不能再注册
if (is_array($json) && isset($json['reg'])) {
if (!isset($device_maps[$json['reg']])) {
$device_maps[$json['reg']] = ['device' => $json['reg'], 'fd' => $fd];
echo "device register done";
} else {
echo "device has been registered";
}
} else {
if (!is_array($json)) {
foreach ($device_maps as $map) {
if ($map['fd'] == $fd) {
var_dump($map);
$msg = json_encode(['device' => $map['device'], 'msg' => bin2hex($msg)]);
break;
}
}
}
}
$mqtt->publish('/tcp485', $msg);
});
//监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
echo "Client: Close.\n";
global $device_maps;
foreach ($device_maps as $key => $map) {
if ($map['fd'] == $fd) {
unset($device_maps[$key]);
echo "remove " . $key;
break;
}
}
});
//启动服务器
$serv->start();
websocket.php
<?php
use TrytoMqtt\Client;
require_once __DIR__ . '/vendor/autoload.php';
$socket_maps = [];
$server = new swoole_websocket_server("localhost", 39002);
$options = [
'clean_session' => false,
'client_id' => 'demo-websocket-123456',
'username' => '',
'password' => '',
];
$mqtt = new Client('localhost', 1883, $options);
$server->on('WorkerStart', function ($serv, $worker_id) use ($mqtt) {
echo "websocket worker start\r\n";
$mqtt->onConnect = function ($mqtt) {
$mqtt->subscribe('/tcp485');
};
$mqtt->onMessage = function ($topic, $content) use ($serv) {
global $socket_maps;
var_dump($socket_maps);
echo "Mqtt=================================" . date('Y-m-d H:i:s') . "\r\n";
var_dump($topic, $content);
$json = json_decode($content, true);
var_dump($json);
if (isset($socket_maps[$json['device']])) {
var_dump($json['msg']);
echo "response \n";
$serv->push($socket_maps[$json['device']]['fd'], $json['msg']);
}
};
$mqtt->onError = function ($exception) use ($mqtt) {
echo "error\n";
};
$mqtt->onClose = function () {
echo "close\n";
};
$mqtt->connect();
});
$server->on('open', function ($server, $req) {
echo "connection open: {$req->fd}\n";
});
$server->on('message', function ($server, $frame) use ($mqtt) {
global $socket_maps;
echo "received tcp message: {$frame->data}" . date('Y-m-d H:i:s') . "\n";
$server->push($frame->fd, $frame->data);
var_dump($socket_maps);
$json = json_decode($frame->data, true);
var_dump($json);
//已经注册的客户端不能再注册
if (isset($json['reg'])) {
if (!isset($socket_maps[$json['reg']])) {
$socket_maps[$json['reg']] = ['device' => $json['reg'], 'fd' => $frame->fd];
echo "websocket client register done";
} else {
echo "websocket client has been registered";
}
$msg = $frame->data;
} else {
foreach ($socket_maps as $map) {
if ($map['fd'] == $frame->fd) {
var_dump($map);
$msg = json_encode(['device' => $map['device'], 'msg' => $frame->data]);
break;
}
}
}
$mqtt->publish('/websocket485', $msg);
});
$server->on('close', function ($server, $fd) {
global $socket_maps;
echo "connection close: {$fd}\n";
foreach ($socket_maps as $key => $map) {
if ($map['fd'] == $fd) {
unset($socket_maps[$key]);
echo "remove " . $key;
break;
}
}
});
$server->start();
三、调试
服务器搭建好后,接着是调试。调试只需要在服运器运行上述两个文件,具体操作请回看往期的文章,小智这里上一张动图,朋友们看看最终效果。
左边是Websocket客户端,右边是TCP客户端
从上图可以看出,Websocket客户端与TCP客户端已经能够正常通讯。
想获取源码的朋友们可以私信小智:先关注“智云说”,然后私信小智,小智收到后会尽快回复。