二、IoTFW.js 1

在本章和第 3 章IoTFW.js - II 中,我们将为构建各种物联网解决方案开发参考架构。参考架构或物联网框架将作为我们未来物联网解决方案的基础,我们将在本书中研究这些解决方案。我们将把这个参考架构或框架称为 IoTFW.js。我们将致力于以下主题,让 IoTFW.js 变得生动起来:

  • 设计一个 IoTFW.js 架构
  • 开发基于节点的服务器端层
  • 开发基于 Angular 4 的网络应用
  • 开发基于 ion 3 的移动应用
  • 开发一个 Angular 4 和 Electron.js 桌面应用
  • 在树莓 Pi 3 上设置和安装所需的依赖项
  • 整合所有部分

我们将讨论本章前面的一些主题以及第 3 章、 IoTFW.js - II 中的一些主题。

设计参考架构

正如我们在第 1 章物联网世界中所看到的,我们将要研究的所有示例都有一个共同的设置。这将是硬件、固件(运行在硬件上的软件)、代理、应用编程接口引擎和用户应用。

当我们遇到框架的相关部分时,我们将对其进行扩展。

当我们需要的时候,我们将在硬件、移动应用或应用编程接口引擎上进行扩展。

有了这个参考架构,我们将在现实世界中的设备和虚拟世界中的云之间建立一个管道。换句话说,物联网是设备和互联网之间的最后一公里解决方案。

体系结构

一个简单的参考架构,将树莓派、无线网关、云引擎和用户界面应用拼接在一起,如下图所示:

在非常高的水平上,我们在左手边有智能设备,在右手边有用户设备。他们之间的所有通信都是通过云进行的。

以下是对之前架构中每个关键实体的描述。我们将从左侧开始,向右侧移动。

智能设备

智能设备是硬件实体,包括传感器、执行器或两者,任何微控制器或微处理器,在我们的例子中,是树莓 pi 3。

传感器是一种电子元件,可以感知或测量物理属性,并将其传递回微控制器或微处理器。中继回来的数据可以是周期性的或事件驱动的;事件驱动的,如仅当数据发生变化时。诸如 LM35 或 DHT11 的温度传感器是传感器的例子。

执行器也是一种机电元件,可以在现实世界中触发动作。通常,执行器不会自行动作。微控制器、微处理器或电子逻辑向致动器发送信号。执行器的一个例子是机械继电器。

我们提到的微处理器将是这本书的覆盆子 Pi 3。

树莓 Pi 3 是一台单板计算机,由树莓 Pi 基金会设计开发。树莓 Pi 3 是第三代树莓 Pi。

在这本书里,我们将使用树莓 Pi 3 模型 B 的所有例子。树莓 Pi 3 型的一些规格如下:

| 功能 | 规格 | | 产生 | three | | 发布日期 | 2016 年 2 月 | | 体系结构 | ARMv8-A (64/32 位) | | 片上系统 | Broadcom BCM2837 | | 中央处理器 | 1.2 GHz 64 位四核 ARM Cortex-A53 | | 存储器 | 1 GB(与图形处理器共享) | | USB 2.0 端口 | 4(通过车载 5 端口 USB 集线器) | | 车载网络 | 10/100 兆位/秒以太网、802.11n 无线、蓝牙 4.1 | | 低级外设 | 17× GPIO 加上同样的特定功能,还有 HAT ID 总线 | | 额定功率 | 空闲时平均 300 毫安(1.5 瓦),压力下最大 1.34 安(6.7 瓦)(连接显示器、键盘、鼠标和无线网络) | | 电源 | 通过 MicroUSB 或 GPIO 头 5 伏 |

For more information on the specifications, please refer to the specifications of Raspberry Pi: https://en.wikipedia.org/wiki/Raspberry_Pi#Specifications.

我们架构的下一部分是无线路由器。一个普通的家用无线路由器将作为我们的网关。正如我们在第 1 章物联网世界中所看到的那样,在集群设备与独立设备部分,我们遵循独立设备的方法,其中每个设备都是自给自足的,并且有自己的无线电与外部世界通信。我们将要建造的所有项目都包括一个树莓 Pi 3,它有一个微处理器和无线电与传感器接口,执行器与互联网接口。

mqtt 经纪人

我们参考框架中的下一个重要部分是设备和云之间的安全通信通道。我们将使用 MQTT 作为我们的沟通渠道。MQTT 的描述引用自http://mqtt.org/faq:

MQTT stands for MQ Telemetry Transport. It is a publish/subscribe, extremely simple and lightweight messaging protocol, designed for constrained devices and low-bandwidth, high-latency or unreliable networks. The design principles are to minimise network bandwidth and device resource requirements whilst also attempting to ensure reliability and some degree of assurance of delivery.

我们将使用超越 SSL 或 MQTT 的 MQTT。在我们的架构中,我们将使用 Mosca(http://www.mosca.io/)作为我们的 MQTTS 代理。Mosca 是一个 Node.js MQTT 代理。当我们开始使用它时,我们将更多地谈论 Mosca。

API 引擎

API 引擎是一个 web 服务器应用,写在 Node.js,Express 上,持久层为 MongoDB。该引擎负责作为 MQTT 客户端与 Mosca 通信,将数据持久化到 MongoDB 中,并使用 Express 公开 API。然后这些应用接口被应用使用来显示数据。

我们还将为用户界面实现基于套接字的应用编程接口,以便从应用和服务器之间的设备获得实时通知。

MongoDB

我们将使用 MongoDB 作为我们的数据持久层。MongoDB 是一个 NoSQL 文档数据库,它允许我们将不同模式的文档保存在一个集合中。这种数据库非常适合处理来自各种设备的传感器数据,因为数据结构或参数因解决方案而异。要了解更多关于 MongoDB 的信息,请参考https://www.mongodb.com/

Web 应用

web app 是一个简单的 web/移动 web 界面,它将实现 API 引擎公开的 API。这些 API 将包括身份验证、访问特定智能设备、从智能设备获取最新数据,以及通过 API 将数据发送回智能设备。我们将使用 Angular 4(https://angular.io/)和 Twitter Bootstrap 3(http://getbootstrap.com/)技术来构建网络应用。

移动应用

我们将遵循移动混合方法来构建我们的移动应用。移动应用实现由应用接口引擎公开的应用接口。这些 API 将包括身份验证、访问特定智能设备、从智能设备获取最新数据以及通过 API 将数据发送回智能设备。我们将使用由 Angular 4 提供动力的 ion 3(http://ionicframework.com/)来构建移动应用。

桌面应用

我们将遵循桌面混合方法来构建桌面应用。桌面应用将实现由应用接口引擎公开的应用接口。这些 API 将包括身份验证、访问特定智能设备、从智能设备获取最新数据,以及通过 API 将数据发送回智能设备。我们将使用电子(https://electron.atom.io/)作为构建桌面应用的外壳。我们将使用 Angular 4 和 Twitter Bootstrap 3(http://getbootstrap.com/)技术来构建桌面应用。我们尝试在网络和桌面应用之间尽可能多地重用代码。

数据流

既然我们已经了解了体系结构的各个部分,现在我们将看看组件之间的数据流。我们将讨论从智能设备到应用的数据流,反之亦然。

应用的智能设备

从传感器到用户设备的简单数据流如下:

从上图可以看出,数据来源于传感器;这些数据由树莓 Pi 3 读取,并通过 Wi-Fi 路由器发布给 MQTTS 经纪人(Mosca)。一旦代理接收到数据,它会将数据发送给应用编程接口引擎,应用编程接口引擎会将数据保存到数据库中。一旦数据成功保存,应用编程接口引擎会将新数据发送到我们的应用,以实时显示数据。

这里需要注意的一点是,应用编程接口引擎将充当 MQTT 客户端,并订阅设备发布数据的主题。当我们检查实现时,我们将查看这些主题。

通常,该流程中的数据是典型的传感器传输数据。

智能设备的应用

下图显示了数据如何从应用流向智能设备:

从上图中我们可以看到,如果应用希望向智能设备发送指令,它会将该消息发送给 API 引擎。然后,应用编程接口引擎将该数据保存到数据库中,并将其发布到 MQTTS 代理,以传递给设备。然后,设备对执行器上的数据做出反应。

请注意,在这两个流程中,MQTTS 代理管理设备,应用编程接口引擎管理应用。

构建参考体系结构

在这一节中,我们将开始把所有的部分放在一起,并缝合所需的设置。我们将从 Node.js 安装开始,然后是数据库,然后是其他部分。

在服务器上安装 Node.js

在我们继续开发之前,我们需要服务器上的 Node.js。这里的服务器可以是您自己的台式机、笔记本电脑、AWS 机器或数字海洋实例,它们可能有也可能没有公共 IP(https://www.iplocation.net/public-vs-private-ip-address)。

要安装 Node.js,导航到https://nodejs.org/en/并为您的机器下载合适的版本。安装完成后,您可以通过从命令提示符/终端运行来测试安装:

node -v
# v6.10.1

npm -v
# 3.10.10  

您可能拥有比之前显示的版本更高的版本。

既然我们有了所需的软件,我们将继续。

安装 nodemon

现在我们已经安装了 Node.js,我们将安装 nodemon。这将负责自动重启我们的节点应用。运行:

npm install -g nodemon

MongoDB

您可以按照下面列出的两种方法之一来设置数据库。

本地安装

我们可以在服务器上将 MongoDB 设置为独立安装。这样,数据库就在服务器上运行,数据就保存在那里。

根据您的操作系统,您可以按照https://docs.mongodb.com/manual/installation/提供的说明设置数据库。

一旦安装了数据库,为了测试一切是否正常,您可以打开一个新的终端,并通过运行以下命令来启动 Mongo 守护程序:

mongod  

您应该会看到类似以下内容的内容:

我正在默认端口27017上运行数据库。

现在我们将使用 mongo shell 与数据库进行交互。打开新的命令提示符/终端并运行以下命令:

mongo  

这将把我们带到mongo外壳,使用它我们可以与 MongoDB 接口。以下是一些方便的命令:

| 描述 | 命令 | | 显示所有数据库 | show dbs | | 使用特定的数据库 | use local | | 创建数据库 | use testdb | | 检查正在使用的数据库 | db | | 创建收藏 | db.createCollection("user"); | | 显示数据库中的所有集合 | show collections | | (创建)在集合中插入文档 | db.user.insert({"name":"arvind"}); | | (读取)查询集合 | db.user.find({}); | | (更新)修改集合中的文档 | db.user.update({"name": "arvind"}, {"name" : "arvind2"}, {"upsert":true}); | | (删除)删除文档 | db.user.remove({"name": "arvind2"}); |

使用前面的命令,您可以熟悉 Mongo shell。在我们的 API 引擎中,我们将使用 mongose ODM(http://mongoosejs.com/)从 Node.js/Express-API 引擎进行管理。

使用 mLab

如果您不想经历在本地设置数据库的麻烦,您可以为此使用 MongoDB 作为服务,如 MLab(https://mlab.com/)。在本书中,我将遵循这种方法。我将使用 mLab 的实例,而不是本地数据库。

要设置 mLab MongoDB 实例,首先导航到https://mlab.com/login/并登录。如果您没有帐户,您可以通过导航至https://mlab.com/signup/创建一个帐户。

mLab 有一个免费层,我们将利用它来构建我们的参考体系结构。免费轮胎非常适合像我们这样的开发和原型项目。一旦我们完成了实际开发,并准备好了生产级应用,我们就可以查看一些更可靠的计划。你可以在 https://mlab.com/plans/pricing/了解价格

登录后,单击新建按钮创建新的数据库。现在,在云提供商下选择亚马逊网络服务,然后选择免费的计划类型,如下图所示:

最后,将数据库命名为iotfwjs并点击 CREATE。几秒钟后,应该会为我们创建一个新的 MongoDB 实例。

数据库创建完成后,打开iotfwjs数据库。我们应该会看到两个警告:一个是声明这个沙盒数据库不应该用于生产,这是我们知道的,第二个是没有数据库用户在场。

所以,让我们开始创建一个。点击用户选项卡,点击添加数据库用户按钮,用用户名admin和密码admin123填写表格,如下所示:

您可以选择自己的用户名和密码,并在本书的剩余部分进行相应的更新。

现在要测试与我们数据库的连接,使用页面顶部的部分使用mongo shell 进行连接。我的情况如下:

打开新的命令提示符并运行以下命令(相应地更新 mLab 网址和凭据后):

mongo ds241055.mlab.com:41055/iotfwjs -u admin -p admin123  

我们应该能够登录到 shell,并且可以从这里运行查询,如下所示:

这就完成了我们对 MongoDB 的设置。

mqtts 经纪人-莫斯科

在本节中,我们将把 MQTTS 代理放在一起。我们将使用 Mosca(http://www.mosca.io/)作为独立服务(https://github . com/mcollina/Mosca/wiki/Mosca 作为独立服务)。

创建一个名为chapter2的新文件夹。在chapter2文件夹内,创建一个名为broker的新文件夹,并在文件夹内打开一个新的命令提示符/终端。然后运行以下命令:

npm install mosca pino -g  

这将在全球范围内安装 Mosca 和 Pino。Pino(https://github.com/pinojs/pino)是一个 Node.js 日志记录器,它将所有消息记录到 Mosca 抛出的控制台。

现在,Mosca 的默认版本实现了 MQTT。但是我们希望保护智能设备和云之间的通信,以避免中间人攻击。

因此,为了设置 MQTTS,我们需要一个 SSL 密钥和 SSL 证书。要在本地创建 SSL 密钥和证书,我们将使用openssl

要检查您的机器上是否有openssl,运行openssl version -a,您应该会看到关于您的本地安装openssl的信息。

如果没有openssl,可以从https://www.openssl.org/source/下载。

现在,在broker文件夹中,创建另一个名为certscd的文件夹。运行以下命令生成所需的密钥和证书文件:

openssl req -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem  

这会提示一些问题,您可以按照以下思路填写:

这将在名为key.pemcertificate.pemcerts文件夹中创建两个新文件。我们将在我们的 Mosca 设置中使用这些。

接下来,在broker文件夹的根目录下,创建一个名为index.js的新文件,并按如下方式进行更新:

let SSL_KEY = __dirname + '/certs/key.pem';
let SSL_CERT = __dirname + '/certs/certificate.pem';
let MONGOURL = 'mongodb://admin:admin123@ds241055.mlab.com:41055/iotfwjs';

module.exports = {
    id: 'broker',
    stats: false,
    port: 8443,
    logger: {
        name: 'iotfwjs',
        level: 'debug'
    },
    secure: {
        keyPath: SSL_KEY,
        certPath: SSL_CERT,
    },
    backend: {
        type: 'mongodb',
        url: MONGOURL
    },
    persistence: {
        factory: 'mongo',
        url: MONGOURL
    }
};

前面的代码是我们将要用来启动 Mosca 的配置。这里的配置加载 SSL 证书和密钥,并将 Mongo 设置为我们的持久层。

保存index.js并返回终端/提示符和cd到我们有index.js文件的位置。接下来,运行以下命令:

mosca -c index.js -v | pino  

我们应该看到以下内容:

从前面可以看到,我们连接到iotfwjs数据库,代理将监听端口8883的连接。

这就结束了我们使用 Mosca 对 MQTTS 代理的设置。

在下一步中,我们将实现 API 引擎,此时,我们将测试 MQTTS 代理与 API 引擎的集成。

应用编程接口引擎-节点和快速

在本节中,我们将构建应用编程接口引擎。该引擎与我们的应用接口,并将信息从智能设备级联到智能设备,作为 MQTT 客户端与代理连接。

首先,我们将使用名为generator-node-express-mongo(https://www.npmjs.com/package/generator-node-express-mongo)的约曼(http://yeoman.io/)生成器克隆一个我们创建的存储库。我们采用了generator-node-express-mongo搭建的代码,并根据我们的需要做了一些修改。

在机器的某个地方,使用以下命令下载本书的完整代码库:

git clone https://github.com/PacktPublishing/Practical-Internet-of-Things-with-JavaScript.git

或者,您也可以从https://github . com/packt publishing/实用性-带 JavaScript 的物联网下载 zip 文件。

储存库下载完成后,cd进入base文件夹,将api-engine-base文件夹复制到chapter2文件夹。

这将下载api-engine样板代码。一旦repo被克隆,cd进入文件夹并运行以下命令:

npm install  

这将安装所需的依赖项。

如果打开cloned文件夹,应该会看到如下内容:

.
├── package.json
└── server
    ├── api
     └── user
     ├── index.js
     ├── user.controller.js
     ├── user.model.js
    ├── app.js
    ├── auth
     ├── auth.service.js
     ├── index.js
     └── local
     ├── index.js
     └── passport.js
    ├── config
     ├── environment
      ├── development.js
      ├── index.js
      ├── production.js
      └── test.js
     ├── express.js
     └── socketio.js
    ├── mqtt
     └── index.js
    └── routes.js

这个文件夹包含了我们开始使用 API 引擎所需的所有基本信息。

从前面的结构可以看出,我们在文件夹的根处有一个package.json。该文件包含所有需要的依赖项。我们还在这里定义了我们的启动脚本。

我们所有的应用文件都在server文件夹中。一切从api-engine/server/app.js开始。我们初始化mongooseexpresssocketioconfigroutesmqtt。最后,我们启动服务器,借助server.listen()收听localhost上的9000端口。

api-engine/server/config/express.js具有初始化 Express 中间件所需的设置。api-engine/server/config/socketio.js由管理 web 套接字所需的逻辑组成。

我们将使用api-engine/server/config/environment来配置环境变量。对于本书的大部分内容,我们将使用开发环境。如果打开api-engine/server/config/environment/development.js,应该会看到mongomqtt的配置。更新如下:

// MongoDB connection options
    mongo: {
        uri: 'mongodb://admin:admin123@ds241055.mlab.com:41055/iotfwjs'
    },

    mqtt: {
        host: process.env.EMQTT_HOST || '127.0.0.1',
        clientId: 'API_Server_Dev',
        port: 8883
    }
};

Update the mongo URL as per your setup (mLab or local). Since we are going to connect to the Mosca broker running on our local machine, we are using 127.0.0.1 as the host.

批准

接下来,我们将看看现成的身份验证。我们将使用 JSON 网络令牌 ( JWTs )来验证将与我们的应用编程接口引擎通信的客户端。我们将使用护照(http://passportjs.org/)进行认证。

打开api-engine/server/auth/index.js,我们应该会看到使用require('./local/passport').setup(User, config);的 Passport 设置,我们正在创建一个新的认证路径。

路线在api-engine/server/routes.js中配置。如果打开api-engine/server/routes.js,应该会看到app.use('/auth', require('./auth'));。这将创建一个名为/auth的新端点,在the api-engine/server/auth/index.js内部,我们现在已经添加了router.use('/local', require('./local'));,如果我们想要访问api-engine/server/auth/local/index.js内部的POST方法,我们将向/auth/local发出一个 HTTP POST请求。

api-engine中,我们使用 passport 本地身份验证策略(https://github.com/jaredhanson/passport-local)来使用 MongoDB 对用户进行身份验证,以实现持久性。

为了创建一个新用户,我们将使用用户应用编程接口。如果我们打开api-engine/server/routes.js,我们应该会看到一个定义的路径来访问用户集合app.use('/api/v1/users', require('./api/user'));。我们用/api/v1/users作为前缀,所以我们可以在以后版本我们的应用编程接口层。

如果我们打开api-engine/server/api/user/index.js,我们应该看到定义了以下六条路线:

  • router.get('/', auth.hasRole('admin'), controller.index);
  • router.delete('/:id', auth.hasRole('admin'), controller.destroy);
  • router.get('/me', auth.isAuthenticated(), controller.me);
  • router.put('/:id/password', auth.isAuthenticated(), controller.changePassword);
  • router.get('/:id', auth.isAuthenticated(), controller.show);
  • router.post('/', controller.create);

第一条路线是获取数据库中的所有用户,使用api-engine/server/auth/auth.service.js中定义的auth.hasRole中间件,我们将检查用户是否经过身份验证并具有管理员角色。

下一个路由是删除一个有 ID 的用户;之后,我们有一个基于令牌获取用户信息的路径。我们有PUT路线更新用户信息;一条GET路线,根据用户 ID 获取用户信息;最后,创建用户的POST路线。请注意POST路由没有任何身份验证或授权中间件,因为访问该端点的用户将首次使用我们的应用(或试图向我们注册)。

使用POST路线,我们将创建一个新用户;这就是我们注册用户的方式:api-engine/server/api/user/user.model.js由用户的 Mongoose 模式组成,api-engine/server/api/user/user.controller.js由我们定义的路由逻辑组成。

MQTT 客户端

最后,我们将看看 MQTT 客户端与我们的api-engine的集成。如果打开api-engine/server/mqtt/index.js,应该会看到 MQTTS 客户端的默认设置。

我们使用以下配置通过 MQTTS 连接到 Mosca 代理:

var client = mqtt.connect({
    port: config.mqtt.port,
    protocol: 'mqtts',
    host: config.mqtt.host,
    clientId: config.mqtt.clientId,
    reconnectPeriod: 1000,
    username: config.mqtt.clientId,
    password: config.mqtt.clientId,
    keepalive: 300,
    rejectUnauthorized: false
});

我们订阅了两个事件:一个是当连接建立时,另一个是当我们收到消息时。在connect事件中,我们订阅了一个名为greet的主题,并在下一行向该主题发布了一条简单的消息。在message事件中,我们正在监听来自经纪人的任何消息,并打印主题和消息。

有了这个,我们知道了使用api-engine所需的大部分代码片段。要启动api-enginecd进入chapter2/api-engine文件夹并运行以下内容:

npm start  

这将在端口9000上启动一个新的快速服务器应用。

美国石油学会发动机测试

为了快速检查我们创建的应用编程接口,我们将使用名为 Postman 的 Chrome 扩展。你可以从这里设置 Chrome 扩展:https://Chrome . Google . com/web store/detail/postman/fhbjgbiflingbdggehcdbncdddomop?hl=en

一旦 Postman 被设置好,我们将测试两个 API 调用来验证注册和登录方法。

打开邮差,输入请求的网址http://localhost:9000/api/v1/users。接下来,选择方法类型为POST。一旦完成,我们将设置标题。添加一个新的标题,键为content-type,值为application/json

现在我们将构造请求体/有效载荷。单击标题旁边的正文选项卡,然后选择原始请求。并用以下内容更新它:

{ 
   "email" : "arvind@myapp.com", 
   "password" : "123456", 
   "name" : "Arvind" 
} 

您可以根据需要更新数据。然后点击发送。这会向 API 引擎发出请求,API 引擎会将数据保存到数据库中,并使用新的用户对象以及身份验证令牌进行响应。

我们的输出应该如下:

现在,如果我们用相同的数据再次点击发送按钮,我们应该会看到一个验证错误,类似于以下内容:

现在,为了验证新注册的用户,我们将只使用电子邮件和密码向http://localhost:9000/auth/local发出请求。我们应该看到和下面一样的东西:

这验证了我们创建的应用编程接口。

至此,我们完成了 API 引擎的演练。在下一节中,我们将把api-engine与代理集成在一起,并测试它们之间的连通性。

代理和应用编程接口引擎之间的通信

既然我们已经完成了云上的两个软件,我们将把它们连接起来。在api-engine/server/config/environment/development.js中,我们已经定义了api-engine需要连接的代理 IP 和端口。

稍后,如果我们在不同的机器上部署这两个部分,这是我们更新 IP 和端口的地方,所以api-engine指的是代理。

现在,要测试通信,请将cd放入chapter2/broker文件夹并运行以下内容:

mosca -c index.js -v | pino  

我们应该看到以下内容:

接下来,打开一个新的命令提示符/终端,cd进入chapter2/api-engine文件夹,运行如下:

npm start 

我们应该看到以下内容:

API 引擎连接到 mLab MongoDB 实例,发布它启动了一个新的 Express 服务器,最后,它连接到 Mosca 代理,并发布了一条消息到问候主题。

现在,如果我们看看 Mosca 终端,我们应该会看到以下内容:

经纪人记录了到目前为止发生的活动。客户端连接用户名API_Server_Dev,订阅名为服务质量 ( 服务质量)的话题0

这样,我们在代理和 API 引擎之间的集成就完成了。

接下来,我们将转向树莓 Pi 3,并开始在 MQTTS 客户端上工作。

If you are new to MQTT protocol, you can refer to MQTT Essentials: Part 1 - Introducing MQTT (http://www.hivemq.com/blog/mqtt-essentials-part-1-introducing-mqtt) and the subsequent parts. To know more about QoS, refer to MQTT Essentials Part 6: Quality of Service 0, 1 & 2 (https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels).

树莓 Pi 软件

在这一节中,我们将构建所需的软件,使树莓派成为我们的 Mosca 代理通过无线路由器的客户端。

我们已经在数据流图中看到树莓 Pi 是如何站在传感器和 Mosca 代理之间的。现在我们将设置所需的代码和软件。

树莓派的制作

在本节中,我们将了解如何在树莓 Pi 上安装所需的软件。

安装了 Raspbian OS(https://www.raspberrypi.org/downloads/raspbian/)的树莓 Pi 是一个先决条件。在我们继续之前,无线网络应该已经设置并连接好了。

If you are new to setting up a Raspberry Pi 3, refer to the Beginner's Guide to Installing Node.js on a Raspberry Pi (http://thisdavej.com/beginners-guide-to-installing-node-js-on-a-raspberry-pi/). We will, however, cover the Node.js part, you can refer until you bring up the Pi and configure the Wi-Fi.

安装操作系统后,启动树莓 Pi 并登录。此时,它将通过您自己的接入点连接到互联网,您应该能够毫无问题地浏览互联网。

I am accessing my Raspberry Pi 3 from my Apple MacBook Pro using VNC Viewer. This way, I am not always connected to the Raspberry Pi 3.

我们将从下载 Node.js 开始。打开一个新的终端并运行以下命令:

$ sudo apt update
$ sudo apt full-upgrade 

这将升级所有需要升级的软件包。接下来,我们将安装最新版本的 Node.js。在撰写本文时,Node 7.x 是最新的:

$ curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash -
$ sudo apt install nodejs  

这将需要一些时间来安装,一旦安装完成,您应该能够运行以下命令来查看 Node.js 和npm的版本:

node -v
npm -v  

这样,我们就完成了在树莓 Pi 3 上运行我们的 MQTTS 客户端所需的软件的设置。

树莓派 MQTTS 客户端

现在我们将使用 Node.js 的 MQTTS 客户端

在树莓 Pi 3 的桌面上,创建一个名为pi-client的文件夹。打开一个终端cd进入pi-client文件夹。

我们要做的第一件事是创建一个package.json文件。从pi-client文件夹中,运行以下命令:

$ npm init

然后回答适用的问题。完成后,接下来我们将在树莓 Pi 3 上安装 mqtt . js(https://www.npmjs.com/package/mqtt)。运行以下命令:

$ npm install mqtt -save  

一旦安装完成,最终的package.json将与此相同:

{
    "name": "pi-client",
    "version": "0.1.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "start": "node index.js"
    },
    "keywords": ["pi", "mqtts"],
    "author": "Arvind Ravulavaru",
    "private": true,
    "license": "ISC",
    "dependencies": {
        "mqtt": "^2.7.1"
    }
}

请注意,我们已经添加了一个启动脚本来启动我们的index.js文件。我们稍后将创建index.js文件。

接下来,在pi-client文件夹的根目录下,创建一个名为config.js的文件。更新config.js如下:

module.exports = { 
    mqtt: { 
        host: '10.2.192.141', 
        clientId: 'rPI_3', 
        port: 8883 
    } 
}; 

Do notice the host property. This is set to the IP address of my MacBook and my MacBook is where I am going to run the Mosca broker API engine. Make sure all three (Mosca broker, API engine, and Raspberry Pi 3) of them are on the same Wi-Fi network.

接下来,我们将编写所需的 MQTT 客户端代码。在pi-client文件夹的根目录下创建一个名为index.js的文件,并按如下方式进行更新:

var config = require('./config.js'); 
var mqtt = require('mqtt') 
var client = mqtt.connect({ 
    port: config.mqtt.port, 
    protocol: 'mqtts', 
    host: config.mqtt.host, 
    clientId: config.mqtt.clientId, 
    reconnectPeriod: 1000, 
    username: config.mqtt.clientId, 
    password: config.mqtt.clientId, 
    keepalive: 300, 
    rejectUnauthorized: false 
}); 

client.on('connect', function() { 
    client.subscribe('greet') 
    client.publish('greet', 'Hello, IoTjs!') 
}); 

client.on('message', function(topic, message) { 
    // message is Buffer 
    console.log('Topic >> ', topic); 
    console.log('Message >> ', message.toString()) 
}); 

这与我们在 API 引擎上编写的测试连通性的测试代码相同。保存所有文件,向你的 Mosca 经纪人前进。

经纪人与树莓派的沟通

在本节中,我们将通过 MQTTS 在代理和树莓 Pi 之间进行通信。

导航至broker文件夹并运行以下内容:

mosca -c index.js -v | pino  

接下来,前往树莓派,cd进入pi-client文件夹,运行以下内容:

$ npm start  

我们应该在树莓派上看到以下信息:

当我们查看 Mosca 的控制台时,我们应该会看到以下内容:

这就结束了我们在树莓 Pi 3 和 Mosca 代理之间的连接测试。

解决纷争

如果您看不到之前的消息,请检查以下内容:

  • 检查树莓 Pi 和运行代理的机器是否在同一个无线网络上
  • 交叉检查运行代理的机器的 IP 地址

树莓 Pi、代理和 API 引擎之间的通信

现在我们将集成树莓 PI、代理和 API 引擎,并将数据从 Pi 传递到 API 引擎。

我们要实现这一点的方法是,我们要创建一个名为api-engine的主题和另一个名为rpi的主题。

要将数据从树莓 PI 发送到 API 引擎,我们将使用api-engine主题,当我们需要将数据从 API 引擎发送到树莓 Pi 时,我们将使用rpi主题。

在这个例子中,我们将获得树莓派的媒体访问控制地址,并将其发送给应用编程接口引擎。应用编程接口引擎将通过向树莓接口发回相同的媒体访问控制地址来确认这一点。API 引擎和树莓 PI 之间的通信将发生在前面提到的两个主题上。

所以首先我们将api-engine/server/mqtt/index.js更新如下:

var mqtt = require('mqtt'); 
var config = require('../config/environment'); 

var client = mqtt.connect({ 
    port: config.mqtt.port, 
    protocol: 'mqtts', 
    host: config.mqtt.host, 
    clientId: config.mqtt.clientId, 
    reconnectPeriod: 1000, 
    username: config.mqtt.clientId, 
    password: config.mqtt.clientId, 
    keepalive: 300, 
    rejectUnauthorized: false 
}); 

client.on('connect', function() { 
    client.subscribe('api-engine'); 
}); 

client.on('message', function(topic, message) { 
    // message is Buffer 
    // console.log('Topic >> ', topic); 
    // console.log('Message >> ', message.toString()); 
    if (topic === 'api-engine') { 
        var macAddress = message.toString(); 
        console.log('Mac Address >> ', macAddress); 
        client.publish('rpi', 'Got Mac Address: ' + macAddress); 
    } else { 
        console.log('Unknown topic', topic); 
    } 
}); 

这里,一旦 MQTT 连接建立,我们就订阅api-engine主题。当我们收到来自api-engine主题的任何数据时,我们会将同样的数据发送回rpi主题。

broker文件夹中,运行以下命令:

mosca -c index.js -v | pino  

接下来,从api-engine文件夹中运行以下命令:

npm start  

接下来,回到树莓派。我们将安装getmac模块(https://www.npmjs.com/package/getmac,它将帮助我们获取设备的 MAC 地址。

pi-client文件夹中,运行以下命令:

$ npm install getmac --save  

一旦完成,更新/home/pi/Desktop/pi-client/index.js如下:

var config = require('./config.js'); 
var mqtt = require('mqtt'); 
var GetMac = require('getmac'); 

var client = mqtt.connect({ 
    port: config.mqtt.port, 
    protocol: 'mqtts', 
    host: config.mqtt.host, 
    clientId: config.mqtt.clientId, 
    reconnectPeriod: 1000, 
    username: config.mqtt.clientId, 
    password: config.mqtt.clientId, 
    keepalive: 300, 
    rejectUnauthorized: false 
}); 

client.on('connect', function() { 
    client.subscribe('rpi'); 
    GetMac.getMac(function(err, macAddress) { 
        if (err) throw err; 
        client.publish('api-engine', macAddress); 
    }); 
}); 

client.on('message', function(topic, message) { 
    // message is Buffer 
    // console.log('Topic >> ', topic); 
    // console.log('Message >> ', message.toString()); 
    if (topic === 'rpi') { 
        console.log('API Engine Response >> ', message.toString()); 
    } else { 
        console.log('Unknown topic', topic); 
    } 
}); 

在前面的代码中,我们已经等待树莓 Pi 和代理之间建立连接。一旦完成,我们就订阅了rpi主题。接下来,我们使用GetMac.getMac()获取树莓派的媒体访问控制地址,并将其发布到api-engine主题。

message事件回调中,我们正在收听rpi话题。如果我们从服务器收到任何数据,它将打印在这里。

保存文件并从pi-client文件夹中运行以下程序:

$ npm start  

现在,如果我们查看 broker 终端/提示符,我们应该会看到以下内容:

这两个设备都连接并订阅了感兴趣的主题。

接下来,如果我们看一下api-engine终端/提示,应该会看到如下内容:

最后,树莓码头应该看起来像这样:

至此,我们结束了树莓 Pi 与代理和 API 引擎的集成。

在下一节中,我们将实现一个 web 应用,它可以通过代理和 API 引擎发送和接收来自树莓 Pi 的数据。

Web 应用

在本节中,我们将构建一个与我们的应用编程接口引擎接口的网络应用。网络应用是我们与智能设备交互的主要界面。

我们将使用 Angular (4)和 Twitter Bootstrap (3)构建网络应用。没有规定接口应该用 Angular 和 Bootstrap 构建;它也可以使用 jQuery 或 React.js 来构建。我们要做的就是使用浏览器中的 JavaScript 与 API 引擎的 API 进行交互。我们使用 Angular 的唯一原因是为了保持我们所有应用的框架一致。因为我们将使用同样遵循 Angular 方法的 ion 框架,所以我们将很容易管理和重用。

为了开始使用网络应用,我们将安装 Angular CLI(https://github.com/angular/angular-cli)。

在运行我们的代理和应用编程接口引擎的机器上,我们也将设置网络应用。

设置应用

chapter2文件夹中,打开一个新的命令提示符/终端,并运行以下命令:

npm install -g @angular/cli  

这将安装角度命令行界面生成器。如果安装完成后运行ng -v,应该会看到大于等于 1.0.2 的版本号。

If you are facing any issues while setting up and running the IoTFW.js, feel free to drop your comment here: https://github.com/PacktPublishing/Practical-Internet-of-Things-with-JavaScript/issues/1

对于 web 应用,我们已经使用 Angular CLI 创建了一个基础项目,并添加了与 API 引擎集成的基本部分。我们将按原样克隆该项目,然后在此基础上开始工作。

首先,我们需要网络应用基础。如果您还没有克隆图书的代码存储库,您可以使用以下命令行(在您的机器上的任何地方)进行克隆:

git clone git@github.com:PacktPublishing/Practical-Internet-of-Things-with-JavaScript.git

或者你也可以从https://github . com/PacktPublishing/实用-带 JavaScript 的物联网下载 zip 文件。

储存库下载完成后,cd进入base文件夹,将web-app-base文件夹复制到chapter2文件夹。

一旦基础已被复制,cd进入web-app文件夹,并运行以下命令:

npm install  

这将安装所需的依赖项。

项目结构

如果打开cloned文件夹,应该会看到如下内容:

.
├── README.md
├── e2e
 ├── app.e2e-spec.ts
 ├── app.po.ts
 └── tsconfig.e2e.json
├── karma.conf.js
├── package.json
├── protractor.conf.js
├── src
 ├── app
  ├── add-device
   ├── add-device.component.css
   ├── add-device.component.html
   ├── add-device.component.spec.ts
   └── add-device.component.ts
  ├── app.component.css
  ├── app.component.html
  ├── app.component.spec.ts
  ├── app.component.ts
  ├── app.global.ts
  ├── app.module.ts
  ├── device
   ├── device.component.css
   ├── device.component.html
   ├── device.component.spec.ts
   └── device.component.ts
  ├── device-template
   ├── device-template.component.css
   ├── device-template.component.html
   ├── device-template.component.spec.ts
   └── device-template.component.ts
  ├── guard
   ├── auth.guard.spec.ts
   └── auth.guard.ts
  ├── home
   ├── home.component.css
   ├── home.component.html
   ├── home.component.spec.ts
   └── home.component.ts
  ├── login
   ├── login.component.css
   ├── login.component.html
   ├── login.component.spec.ts
   └── login.component.ts
  ├── nav-bar
   ├── nav-bar.component.css
   ├── nav-bar.component.html
   ├── nav-bar.component.spec.ts
   └── nav-bar.component.ts
  ├── register
   ├── register.component.css
   ├── register.component.html
   ├── register.component.spec.ts
   └── register.component.ts
  └── services
  ├── auth.service.spec.ts
  ├── auth.service.ts
  ├── data.service.spec.ts
  ├── data.service.ts
  ├── devices.service.spec.ts
  ├── devices.service.ts
  ├── http-interceptor.service.spec.ts
  ├── http-interceptor.service.ts
  ├── loader.service.spec.ts
  ├── loader.service.ts
  ├── socket.service.spec.ts
  └── socket.service.ts
 ├── assets
 ├── environments
  ├── environment.prod.ts
  └── environment.ts
 ├── favicon.ico
 ├── index.html
 ├── main.ts
 ├── polyfills.ts
 ├── styles.css
 ├── test.ts
 ├── tsconfig.app.json
 ├── tsconfig.spec.json
 └── typings.d.ts
├── tsconfig.json
└── tslint.json

现在,对于项目结构和代码设置的演练。

在高层次上,我们有一个src文件夹,在那里我们将拥有所有的源代码和单元测试代码,还有一个e2e文件夹,由端到端测试组成。

我们将在src/app文件夹中度过大部分时间。在我们进入这个文件夹之前,打开web-app/src/main.ts,这就是一切开始的地方。接下来,我们在这里添加了 Twitter Bootstrap Cosmos 主题(https://bootswatch.com/cosmo/)并定义了一些布局样式。

现在,app/src文件夹:这里,我们已经定义了根组件、根模块以及所需的组件和服务。

App 模块

打开web-app/src/app/app.module.ts。这个文件由@NgModule声明组成,它定义了我们将要使用的所有组件和服务。

我们创建了以下组件:

  • AppComponent:保存路由器出口的应用根组件
  • NavBarComponent:这是出现在所有页面上的导航栏组件。该组件自动检测身份验证状态,并相应地显示菜单栏
  • LoginComponent:这涉及到登录功能
  • RegisterComponent:使用 API 引擎进行注册
  • HomeComponent:该组件显示当前登录用户连接的所有设备
  • DeviceComponent:该组件显示一台设备的信息
  • AddDeviceComponent:这个组件让我们在设备列表中添加一个新组件
  • DeviceTemplateComponent:在我们的应用中用于表示设备的通用模板

除了前面的模块,我们还在导入中添加了所需的模块:

对于服务,我们有以下内容:

  • AuthService:管理 API 引擎公开的认证 API
  • DevicesService:管理 API 引擎公开的设备 API
  • DataService:管理 API 引擎公开的数据 API
  • SocketService:管理从 API 引擎实时发送数据的 web 套接字
  • AuthGuard:保护需要认证的路由的角度守卫。阅读在 Angular 中使用防护装置保护路线(https://blog . thoughtram . io/Angular/2016/07/18/Angular-2 . html中有关防护装置的更多信息
  • LoaderService:活动进行时显示和隐藏加载器栏
  • Http:我们用来发出 HTTP 请求的 HTTP 服务。这里,我们没有按原样使用 HTTP 服务,而是扩展了类,并在两者之间添加了我们的逻辑,以便使用加载器服务更好地管理 HTTP 请求体验

请注意,此时,应用编程接口引擎没有设备和数据的应用编程接口,也没有为数据设置套接字。一旦我们完成了网络应用,我们将在应用编程接口引擎中实现。

在这个 web 应用中,我们将有以下路线:

  • login:让用户登录应用
  • register:注册我们的应用
  • home:显示用户账户中所有设备的页面
  • add-device:向用户设备列表添加新设备的页面
  • view-device/:id:查看一台设备的页面,由 URL 中的 id 参数标识
  • **:默认路由设置为登录
  • '':如果没有匹配的路由,我们将用户重定向到登录页面

Web 应用服务

现在,我们在较高的层次上理解了这个 web 应用中存在的所有内容,我们将浏览这些服务和组件。

打开web-app/src/app/services/http-interceptor.service.ts;在这个类中,我们扩展了Http类,实现了类方法。我们增加了两个自己的方法requestInterceptor()responseInterceptor(),分别拦截请求和响应。

当请求即将发送时,我们调用requestInterceptor()显示一个加载器,指示 HTTP 活动,一旦响应到达,我们使用responseInterceptor()隐藏加载器。这样,用户清楚地知道是否有任何后台活动正在进行。

接下来是LoaderService班;打开web-app/src/app/services/loader.service.ts,从这里可以看到,我们增加了一个名为BehaviorSubject<boolean>类型的 status 的类属性(要了解更多Behaviour主题,请参考https://github . com/Reactive-Extensions/RxJS/blob/master/doc/API/subjects/behavior . MD)。我们有一个方法,如果 HTTP 服务或任何其他组件想要显示或隐藏加载器栏,然后将该值设置为真或假,它们将调用该方法。

加载器服务所需的 HTML 显示在web-app/src/app/app.component.html中,所需的样式显示在web-app/src/app/app.component.css中。

我们将使用网络套接字在网络应用和应用编程接口引擎之间实时传输数据。打开web-app/src/app/services/socket.service.ts,我们应该看到构造函数和getData()方法。我们正在使用socket.io-client(https://github.com/socketio/socket.io-client)来管理我们的网络应用中的网络套接字。

在构造函数中,我们创建了一个到 API 引擎的新套接字连接,并将 auth token 作为查询参数传递。我们也将通过 web 套接字验证传入的连接。只有当令牌有效时,我们才会允许连接,否则我们会关闭 web 套接字。

getData()里面,我们订阅了一个设备的data:save主题。这就是当设备有新数据可用时,我们从应用编程接口引擎得到通知的方式。

现在,我们将了解三种 API 服务,通过它们我们可以验证用户身份、获取用户的设备以及获取设备的数据:

  • AuthService:打开web-app/src/app/services/auth.service.ts。在这里,我们定义了register()login()logout(),它们负责管理认证状态,我们定义了isAuthenticated(),它返回认证的当前状态,如用户是登录还是注销。
  • DevicesService:打开web-app/src/app/services/devices.service.ts。在这里,我们实现了三个方法:一个创建,一个读取,一个删除。这样,我们就可以为用户管理设备。
  • DataService:打开web-app/src/app/services/data.service.ts,管理设备的数据。这里我们只有两种方法:一种是创建新的数据记录,另一种是获取设备的最后 30 条记录。

请注意,我们正在使用web-app/src/app/app.global.ts来保存所有不变的全局变量。

既然我们已经完成了所需的服务,我们将遍历组件。

Web 应用组件

我们将从应用组件开始。应用组件是根组件,它保存路由器出口、加载器服务 HTML 和通知服务 HTML。你可以在这里找到相同的:web-app/src/app/app.component.html。在web-app/src/app/app.component.ts中,我们定义了showLoader来决定是否显示装载机。我们还定义了通知选项,用于存储通知服务配置。

在构造函数内部,我们正在监听路由器上的路由更改事件,因此我们可以在页面更改时显示一个加载栏。我们还在听装载机服务状态变量。如果这种情况发生变化,我们会显示或隐藏加载程序。

用户登陆的第一个页面是登录页面。登录页面/组件web-app/src/app/login/login.component.ts只有一种方法,从web-app/src/app/login/login.component.html获取用户的电子邮件和密码,并对用户进行身份验证。

使用主页上的注册按钮,用户可以自己注册。在RegisterComponentweb-app/src/app/register/register.component.ts中,我们定义了register(),它获取用户信息,并使用AuthService注册用户。

一旦用户被成功认证,我们将用户重定向到LoginComponent。在HomeComponentweb-app/src/app/home/home.component.ts中,我们获取与用户相关联的所有设备,并在加载时显示它们。该页面还有一个使用AddDeviceComponent添加新设备的按钮。

要查看一台设备,我们使用DeviceComponent查看一台设备。

到目前为止,我们没有任何可用于处理设备和数据的 API。我们将在下一节完成 API 引擎更新后重新访问这个页面。

启动应用

要运行该应用,请打开web-app文件夹内的终端/提示,并运行以下内容:

ng serve

Make sure the API engine and Mosca are running before you run the previous command.

一旦网络包编译成功,导航到http://localhost:4200/login,我们应该会看到登录页面,这是第一页:

我们可以使用我们在测试应用编程接口引擎时创建的帐户,使用邮递员,或者我们可以通过单击注册网络应用创建一个新帐户,如下所示:

如果注册成功,我们将被重定向到如下主页:

如果我们打开开发人员工具,我们应该会看到前面的消息。应用编程接口引擎没有实现设备的应用编程接口,因此出现了之前的404s。我们将在第三章IoTFW.js - II 中解决这个问题。

一旦我们完成了应用编程接口引擎的更新,我们还将在第 3 章中浏览网络应用的剩余部分。

摘要

在这一章中,我们已经完成了设置框架以使用物联网解决方案的过程。我们只用 JavaScript 作为编程语言构建了大部分框架。

我们首先了解了从树莓 Pi 到最终用户设备(如网络应用、桌面应用或移动应用)的架构和数据流。然后,在建立了 MongoDB 之后,我们开始使用 Mosca 开发代理。接下来,我们设计并开发了 API 引擎,并完成了基本的树莓 PI 设置。

我们已经开发了网络应用,并设置了必要的模板来处理应用的剩余部分。在第三章IoTFW.js - II 中,我们将完成整个框架,还将集成一个 DHT11(温湿度)传感器和一个 LED,以端到端验证双向数据流。