前言

  • 本文是“从物联到万联,Node.js与树莓派万维物联网构建实战”一书的读书笔记,该书翻译自“Building the Web of Things with examples in Node.js and Raspberry Pi (by Guinard &Trifa)”。

  • 书中相关源码可以通过@github访问,读者可以通过@webofthings来访问作者搭建好的web物联网应用。

  • 本文重点关注书中相关实例的实现及其依赖的一些关键概念,对一些基础知识和简单代码逻辑不做过多解释。主要总结实际操作碰到的问题和解决问题的思路。


第一个物联网聚合应用

1) 无法加载jquery.min.js

出现错误:”Failed to load resource: net::ERR_CONNECTION_RESET jquery.min.js:1“

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>

考虑国内无法访问ajax.googleapis.com,我们可以将jquery下载到本地再修改src指向本地路径,也可以搜索网络上可用的jquery。以下我们替换这一行代码为:

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>

重新打开html文件即可,注意进入浏览器可以在设置里面打开“开发人员模式”,切换到console选项卡查看打印的log。以下是该物联网聚合应用运行成功后的截图:


2)源码分析

//#A First, get the temperature in the user location from Yahoo

//#B Then get the temperature from the WoT Pi in London

//#C Prepare the text to publish and use it to update the content of the LCD screen

//#D POST the message to the LCD actuator

//#E Set a timer that will call the takePicture() function in N seconds (after the LCD content has been updated)

//#F Generates the text to display with the user name, location and Pi temperature

//#G Retrieve the current image from the Webcam in our office

//#H Update the HTML img tag with the image URL

以上英文还是比较好翻译的,如果不太确定,可以借助有道词典翻译下:

//#A首先,从雅虎获取用户位置的温度

//#B然后从伦敦的WoT Pi得到温度

//#C准备好要发布的文本并使用它来更新LCD屏幕的内容

//#D将信息发送到LCD驱动器

//#设置一个计时器,在N秒内调用takePicture()函数(LCD内容更新后)

//#F生成显示用户名、位置和Pi温度的文本

//#G从我们办公室的摄像头中获取当前的图像

//#H用图像URL更新HTML标签

这样我们就大概理清了这个web程序“ex-5-mashup.html”的大致逻辑就是:分别从雅虎和WOT Pi得到温度数据,经过处理之后把要发送的数据通过Web API发送给树莓派,等待一段时间,通过摄像头查看LCD数据的响应结果。


开始异步编程

关于IO模型的相关知识,可以参考“UNIX高级编程”的相关议题。

1)使用request库发起http请求

npm init

npm install request --save

node *.js

代码清单 3.7

var request = require('request');request('http://webofthings.org', function (error, response, body) { //#Aif (!error && response.statusCode === 200) {console.log(body); //#B}});//#A This is an anonymous callback that will be invoked when the request library did fetch the webofthings homepage from the Web//#B This will display the HTML code of the page

以上看出,request库的第二个参数是一个匿名回调函数,当request库从第一个参数指定的URL获取到web网络数据后,该匿名函数被调用;这里的function有三个参数,当这个异步操作过程出现任何错误时,会将错误内容返回给error,response返回响应头,body返回响应正文。匿名函数体我们直接通过console.log将body内容打印处理。


2018.09.22更新

第7章 实现Web智能产品

本章涵盖的内容如下:

  • 探索Web智能产品的三种可能模式

  • 通过Web协议访问传感器和执行器

  • 使用Node.js和Express 在树莓派上面构建REST和WebSocket API

  • 构建COPA设备并连接到Web

  • 在树莓派上面使用MQTT来连接EVERYTHNG API

前言

本章围绕物联网设备接入Web的三种集成模式展开,它们分别是:

  • 直接连接模式——在设备上面实现REST

  • 网关集成模式——COAP

  • 云端集成模式——EVRYTHNG的MQTT

7.1 连接设备到Web

设计智能web产品的过程:

  1. 集成策略——选择一个模型将智能产品集成到Web。本章将讲解这些模型。

  2. 资源设计——确定智能产品的功能或服务,然后组织它们的层次结构。

  3. 表述设计——决定每个资源服务采用哪一种表述。

  4. 接口设计——决定每个服务可能接收哪些命令,以及处理哪些异常。

  5. 资源链接设计——决定不同资源之间如何相互链接。

7.2 直接集成模式——在设备上实现REST

  • 要想实现一套成熟的REST API,有许多的Node.js框架可以选择。这里列举一些最流行的Node.js的Web/REST框架:<1>

  • EXPRESS: A NODE.JS WEB FRAMEWORK

    虽然Express 可以在树莓派和大部分linux设备上面平稳运行,但是我们也要明白,Express并不是实现IOT设备Web API的最轻量方式。<2>

7.2.2 资源设计

我们要将硬件资源映射为REST接口,那么一个最基本的问题就是,如何来描述这些资源呢?答案就是通过JSON来保存我们的资源树。

步骤1:创建资源模型

新建resources/resources.json,代码如下:

{"pi": {"name": "WoT Pi","description": "A simple WoT-connected Raspberry PI for the WoT book.","port": 8484,"sensors": {"temperature": {"name": "Temperature Sensor","description": "An ambient temperature sensor.","unit": "celsius","value": 0,"gpio": 12},"humidity": {"name": "Humidity Sensor","description": "An ambient humidity sensor.","unit": "%","value": 0,"gpio": 12},"pir": {"name": "Passive Infrared","description": "A passive infrared sensor. When 'true' someone is present.","value": true,"gpio": 17}},"actuators": {"leds": {"1": {"name": "LED 1","value": false,"gpio": 4},"2": {"name": "LED 2","value": false,"gpio": 9}}}}}

新建resources/model.js,代码如下:

var resources = require('./resources.json');

module.exports = resources;

这个文件加载resources.json文件,然后exports将object转变为node模块,这样就可以在应用里面使用它了。

步骤2:创建Express路由

在Express和其他许多Web框架中,资源的URL通过路由来定义

var express = require('express'),

router = express.Router(),

resources = require('./../resources/model');

router.route('/').get(function (req, res, next) {

req.result = resources.pi.actuators;

next();

});

....

module.exports = router;

创建完路由后,export router,这样其他模块就可以require它了。

步骤3:创建Express应用

通过Express framwork封装一个HTTP server,在servers/http.js文件实现,在这个文件中加载在我们之前创建的路由。

// Final version

var express = require('express'),

actuatorsRoutes = require('./../routes/actuators'),

sensorRoutes = require('./../routes/sensors'),

thingsRoutes = require('./../routes/things'),

resources = require('./../resources/model'),

converter = require('./../middleware/converter'),

cors = require('cors'),

bodyParser = require('body-parser');

...

var app = express();

...

//绑定路由到Express应用

app.use('/pi/actuators', actuatorsRoutes);

app.use('/pi/sensors', sensorRoutes);

app.use('/things', thingsRoutes);

app.get('/pi', function (req, res) {

res.send('This is the WoT-Pi!')

});

...

module.exports = app;

在可以测试功能之前,还需要wot-server.js文件,这是WOT服务器的入口,负责以正确的配置启动服务。

// Final version

var httpServer = require('./servers/http'),

wsServer = require('./servers/websockets'),

resources = require('./resources/model');

...

// HTTP Server

var server = httpServer.listen(resources.pi.port, function () {

console.log('HTTP server started...');

// Websockets server

wsServer.listen(server);

console.info('Your WoT Pi is up and running on port %s', resources.pi.port);

});

步骤4:将传感器绑定到服务器上

来到这里就比较有趣了,因为涉及到和硬件的交互,直接看代码:

var model = resources.pi.sensors.pir;

var resources = require('./../../resources/model');

var model = resources.pi.sensors.pir;

...

function connectHardware() { //#B

var Gpio = require('onoff').Gpio;

sensor = new Gpio(model.gpio, 'in', 'both'); //#C

sensor.watch(function (err, value) { //#D

if (err) exit(err);

model.value = !!value;

showValue();

});

console.info('Hardware %s sensor started!', pluginName);

};

model.gpio获取硬件的GPIO配置信息,这个信息保存在我们前面提到的resources.json文件中。最终通过watch来监听GPIO事件,触发事件时回调函数会被执行。

有了插件后,在wot-server.js当然要把它用起来啦:

// Internal Plugins

var ledsPlugin = require('./plugins/internal/ledsPlugin'), //#A

pirPlugin = require('./plugins/internal/pirPlugin'), //#A

dhtPlugin = require('./plugins/internal/DHT22SensorPlugin'); //#A

// Internal Plugins for sensors/actuators connected to the PI GPIOs

// If you test this with real sensors do not forget to set simulate to 'false'

pirPlugin.start({'simulate': true, 'frequency': 2000}); //#B

ledsPlugin.start({'simulate': true, 'frequency': 10000}); //#B

dhtPlugin.start({'simulate': true, 'frequency': 10000}); //#B

在树莓派上用真实硬件测试

在树莓派上面运行我们前面创建好的项目

  • npm install –save 添加依赖

  • 修改启动服务的参数为{‘simulate’:false}

  • node wot-server.js

7.2.3 表述设计

实现一个表述转换中间件

/middleware/converter.js : 实现html、json、msgpack 表述转换

7.2.4 接口设计

添加一个BODY PARSER

body-parser模块:用来接收客户端的JSON数据,在http.js中require它,在中间链开头添加app.use(bodyParser.json()),因为必须在其他中间件处理之前先处理HTTP消息的body部分。

支持其他的HTTP动作

为routes/actuators.js中的LED添加PUT支持

router.route('/leds/:id').get(function (req, res, next) { //#A

req.result = resources.pi.actuators.leds[req.params.id];

next();

}).put(function(req, res, next) { //#B

var selectedLed = resources.pi.actuators.leds[req.params.id];

selectedLed.value = req.body.value; //#C

req.result = selectedLed;

next();

});

将执行器绑定到服务器

/pligins/internal/ledsPlugin.js : 用于执行用户修改更新硬件状态。

7.2.5 通过WebSocket实现pub/sub接口

启动WebSocket服务用于监听HTTP服务器上面升级为WebSocket协议的请求

// HTTP Server

var server = httpServer.listen(resources.pi.port, function () {

console.log('HTTP server started...');

// Websockets server

wsServer.listen(server);

console.info('Your WoT Pi is up and running on port %s', resources.pi.port);

});

7.2.6 小结——直接集成模式

这小节我们直接在真实的设备上面构建WOT服务器,并且实现一些高级的功能如内容协商和WebSocket推送功能,也有大部分设备无法运行Node原生环境,后面会介绍另一种模式来支持非HTTP/WebSocket设备,即网关集成模式。

7.3 网关集成模式——CoAP

7.3.1 运行一个CoAP服务器

npm install coap

var coap = require('coap')

coap.createServer

github 上面的一个tiny coap server <3>

7.3.2 通过网关代理CoAP

我们知道,CoAP基于REST,但是由于它不是使用HTTP而是使用UDP代替TCP的,因此需要一个网关将CoAP消息转换成HTTP,它是理想的设备到设备低功耗无线电通信方式。我们需要为CoAP设备创建WOT网关,才能通过浏览器运行javascript和CoAP设备通信。

COAP技术门户网站<4>

为CoAP设备构建通用的HTTP代理<5>

7.3.3 小结——网关集成模式

对于一些设备来说,直接支持HTTP或者WebSocket是不太现实的,需要依靠更强大的网关来连接到万维物联网。除了Express外,还有其他开源的可选网关,比如OpenHab或The Thing System。

OpenHab<5>

The Thing System<6>


7.4 云端集成模式——EVRYTHNG的MQTT

云服务不仅能将智能产品的API通过HTTP和WebSocket暴露出来,还能提供很多额外的特性,如无限制的数据存储、用户管理界面、数据可视化、流处理、支持多种并发请求等。

云服务提供商

Xively<1>

ThingWorx<2>

ThingSpeak<3>

Carriots<4>

thethings.io<5>

7.4.1设置EVRYTHNG账号

步骤1——创建项目和应用程序

步骤2——创建第一个产品和智能物件

步骤3——创建设备API KEY

步骤4——改变属性

7.4.2 创建MQTT客户端应用程序

代码路径:chapter7/part3-clound

cp config-sample.json config.json

change "thngId":"U5ngAdV6HfQQmpwaw2ywcfWt"

"thngApiKey":"6iVK8UUzwOlvdtQYfqwLjYmAVWkEbhhusbGEFIPwFN2fzfzDjASkpBbUHF9q1iG6ulNsqkdM7Wn4ENUN"

npm install

node simple-plug.js

成功运行MQTT客户端打印如下,该客户端与云引擎建立一个持久的连接,这个连接基于MQTT,每隔5秒更新一次属性值。

同时登陆EVEYTHNG平台,以下Thngs相关的Properties会同步刷新。

思考:MQTT推送数据改变云引擎模型中的数据,那么这个云引擎是如何告知浏览器javascript的呢?websocket吗?回想前面我们学习的,WebSocket:用来推送消息的实质的Web协议。

7.4.3 使用action来控制智能插座

上面成功运行MQTT客户端打印其实在一个回调函数中打印的,这个客户端的回调函数将会在EVRYTHNG云端属性发生变化时被调用。代码逻辑如下:

client.on('message', function (topic, message) { // #G

console.log(message.toString());

});

我们可以修改这里的逻辑,在云端属性变化的时候去触发动作,但这并不是最好的选择,因为这样必须分清那些属性是设备设置的,那些属性是应用程序里面改变的。这里我们使用action来发送更复杂的命令,可以带多个输入的参数。

首先我们要创建一个叫做_setStatus的action类型,猜测应该是管理平台的如下:

同样我们也可以使用curl来创建

curl -X POST "$SERVER/actions?project=$PROJECT_ID" \

> -H "Authorization: $EVRYTHNG_API_KEY" \

> -H "Content-Type: application/json" \

> -d '{"name":"_setStatus","description":"Changes the status of the Thng","tags":["Wot","device"]}'

果然,以上命令执行成功后,多出如下一项,正是我们在body里面填的信息:

7.4.4 创建一个简单的Web控制应用

使用云服务创建简单的Web应用

需求:创建一个简单的web应用,使用户能够通过云端智能物件与设备交互。

  • 使用WebSocket订阅它的属性,并在设备属性改变的时候及时显示出来;

  • 这个应用也能通过云平台支持的REST API将命令推送到设备;

使用二维码来标识智能产品

  • GitHub pages 可以用来部署自己的网页或者搭建自己的网站

这里我们使用EVRYTHNG为我们提供的二维码访问地址:

https://webofthings.github.io/wot-book/plug.html?key=(your API key)&thngId=(your Thng ID)

这是我的设备二维码:

https://webofthings.github.io/wot-book/plug.html?key=6iVK8UUzwOlvdtQYfqwLjYmAVWkEbhhusbGEFIPwFN2fzfzDjASkpBbUHF9q1iG6ulNsqkdM7Wn4ENUN&thngId=U5ngAdV6HfQQmpwaw2ywcfWt

注意:前面章节是自己手敲curl命令来实现的,这些命令统一包含在part3-cloud下面的setup.sh脚本中

7.4.5 小结——云端集成模式

实时WOT产品最佳的方式是提供直接访问和云端访问两种模式。

7.5 总结

本章讲解了物联网设备连接到万维物联网的三种模式,它们分别是:直连模式,网关模式和云服务模式。并详细介绍这三种模式的应用场景和使用的技术:Express 路由,COAP服务,MQTT客户端,Websocket订阅,action post等。在第八章我们将探索资源-链接设计步骤,实现资源的可探索与可发现。


第八章 发现层:描述和发现Web智能产品

8.1 可发现性问题

8.2 发现智能设备

8.2.1 网络发现(Network discovery)

  • 常见的网络发现协议有:组播DNS(mDNS)、DLNA以及UPnP等。例如,大部分网络电视和多媒体播放器能够使用DLNA来发现局域网中的网络连接存储设备(NAS),并且从中读取媒体文件。

8.2.2 Web上的资源发现

爬取Web智能产品的API

HATEOAS和Web链接

8.3 描述Web智能产品

8.3.1 Web智能产品模型简介

8.3.2 元数据

8.3.3 属性

8.3.4 行为

8.3.5 智能产品

8.3.6 在树莓派上面实现Web智能产品模型

8.3.7 小结——Web智能产品模型

[读书笔记]从物联到万联——Node.js与树莓派万维物联网构建实战相关推荐

  1. 【个人笔记】《知了堂》node.js简介及创建应用

    Node.js 究竟是什么? Node 是一个服务器端 JavaScript 解释器,它将改变服务器应该如何工作的概念.它的目标是帮助程序员构建高度可伸缩的应用程序,编写能够处理数万条同时连接到一个( ...

  2. 前端点滴(Node.js)(五)---- 构建 Web 应用(二)数据上传

    Node.js 2. 数据上传 在实际的业务中,我们往往需要接收一些数据,比如表单数据.文件提交.Json上传.XML上传等等. Node的http模块只对HTTP报文头部进行了解析,然后触发requ ...

  3. 日志库 winston 的学习笔记 - 创建一个使用 winston 的 Node.js 应用

    winston 被设计为一个简单且通用的日志库,支持多种传输. 传输本质上是日志的存储设备. 每个 winston 记录器都可以在不同级别配置多个存储渠道.例如,人们可能希望将错误日志存储在持久的远程 ...

  4. 读书笔记 1.数据包分析技术与网络基础 Wireshark数据包分析实战 第3版

    1.数据包分析技术与网络基础 1.2.1 协议 发起连接 :是由客户端还是服务器发起连接?在真正通信之前必须要交换哪些信息? 协商连接参数 :通信需要进行协议加密吗?加密密钥如何在通信双方进行传输? ...

  5. Node.js + Express 4.x + MongoDB 构建登录注册-简易用户管理(四)

    登录和注册的功能算实现了,下面封装DBHelp和增加一个简单的用户管理,这样增删查改就集齐了. 在routes文件夹下面新建DBHelp.js,代码如下: const MongoClient=requ ...

  6. Node.js学习笔记8

    Node.js学习笔记8 HTTP服务器与客户端 Node.js的http模块,封装了一个高效的HTTP服务器和一个简易的HTTP客户端 http.server是一个基于事件的HTTP服务器,核心由N ...

  7. 千锋Node.js学习笔记

    千锋Node.js学习笔记 文章目录 千锋Node.js学习笔记 写在前面 1. 认识Node.js 2. NVM 3. NPM 4. NRM 5. NPX 6. 模块/包与CommonJS 7. 常 ...

  8. Node.js学习笔记9-将Node应用部署到Docker

    将Node应用部署到Docker 阅读这篇blog大约需要10分钟 Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源. Docker 可以让开发者打包他们 ...

  9. 6分钟看懂 Node.js 武功精髓

    本篇来自[北妈的小北鼻]写手联盟 小北鼻作者:闹闹 阅读大约需要6分钟 1 为什么要学习Node.js? Node.js是目前非常火热的技术,可是作为一个前端开发人员,为什么要学Node.js? 说N ...

最新文章

  1. 10年以后,Google Labs再次回归!VR部门负责人任新leader
  2. 物品回收平台java代码_java垃圾回收
  3. Android复习13【广播:思维导图、音乐播放器】
  4. 使用 Apache Lucene 搜索文本——轻松为应用程序构建搜索和索引功能
  5. c++ 两个多边形区域重叠_2018 年英国中级数学挑战赛中的多边形问题
  6. stm32超声波测距代码_超声波模块另类用法,悬浮,你也能做到
  7. STM32之CAN总线原理
  8. View绑定式监听器实现原理
  9. pku1274 The Perfect Stall
  10. 人工智能 一种现代方法 第3章 用搜索树对问题求解
  11. c语言符号字符集包括,c语言基本符号
  12. 计算机论文题目_基于java的毕业设计题目_50例
  13. 计算机组装模拟系统吗,怎么在线模拟组装电脑
  14. 北邮iptv用WindowsMediaplayer打不开的解决办法
  15. 三月写给沉默王二的一封道歉信
  16. OpenCV实践小项目(一): 信用卡数字识别
  17. 移动后端即服务(BaaS)市场现状及未来发展趋势
  18. ORB SLAM2源码解读(三):Frame类
  19. 嵌入式Linux——音频设备驱动(1):uda1341音频设备驱动框架分析
  20. 未来人工智能将带来对社会的经济变革

热门文章

  1. 安徽工业大学计算机学院在东校区还是西校区?,安工大东校区附近学校
  2. 【视频讲解】【全民编程】《软件编程-讲课视频》【零基础入门到实战应用】
  3. 腾讯云-物联网开发平台测试(联合mqtt.fx 1.7.1)
  4. 2021年php面试题
  5. 最火热的东南亚跨境电商平台Shopee、Lazada,为什么必须要做补单
  6. 大学计算机专业绩点在3.5算好,大学平均绩点3.5算差吗 平均绩点多少算高
  7. git回滚 reset 之后如何恢复到之前状态
  8. 利用正则表达式判断身份证号
  9. 测量平差之附有限制条件的条件平差(概括平差模型)
  10. 企业社交网络和在线社区市场现状研究分析-