程序员如何实现财富自由系列之:学习并应用物联网技术

发布于:2023-09-27 ⋅ 阅读:(83) ⋅ 点赞:(0)

作者:禅与计算机程序设计艺术

1.简介

物联网(IoT)已经逐渐成为人们生活中的不可或缺的一部分,它不仅可以帮助我们收集、存储和传输数据,还能够让我们的生活变得更加智能化、便捷化。通过对现实世界中各种设备的信息采集及处理,可以帮助我们进行智慧城市建设、智慧农业、智慧工厂、智慧医疗等应用,甚至可能为我们的生活带来全新的体验。而作为一名程序员,要想掌握物联网技术,就需要从基础的硬件知识、网络协议、嵌入式系统、编程语言等方面都有所积累。本文将以较为广泛的范围来介绍一下如何学习并应用物联网技术,希望能帮助读者在实际工作中有所收获。

2.背景介绍

2.1 什么是物联网?

物联网(Internet of Things, IoT)是一个利用互联网技术与传感器相结合的新型网络,能够收集、整理和交换大量信息。它利用计算机网络、微控制器、传感器、集成电路、无线电波、通讯接口等物理装置以及电子设备实现数据的收集和处理。最早由英国剑桥分析公司发明,并于2011年正式启动了产业革命。随着IoT的普及,越来越多的人们开始关注和使用IoT技术来增强自身的能力、提升效率、改善生活品质。

2.2 为什么要学习物联网技术?

物联网技术的兴起促进了经济发展,尤其是在移动互联网(Mobile Internet)和云计算(Cloud Computing)的背景下。随着物联网技术的普及,越来越多的应用场景被激活,如智慧城市、智慧农业、智慧工厂、智慧医疗、智慧教育、智慧住房、智慧社会。要想真正发展自己的业务,就必须把握好“边缘计算”这一重要的分支领域。

物联网技术通过连接各类传感器设备、路由器、服务器、终端设备、基站等,能够实时地收集、汇总、处理、分析和呈现大量的数据。因此,掌握物联网技术对于个人开发者、企业管理者、创业公司和初创型企业都十分重要。

3.基本概念术语说明

3.1 概念:

(1)物理层 Physical Layer: 在物理层,物联网通信的双方都要具备一定的物理特性,比如双方之间的距离、布置位置、线缆颜色、接头方式等。此外,也会受到周围环境的影响,比如电磁干扰、雷击、恶劣天气等。因此,在物理层上需要考虑传输过程中产生的噪声、失真、衰减等问题。

(3)网络层 Network Layer: 网络层负责为数据包分配路径,选择转发节点,保证数据在传输过程中不丢失、不乱序和不重复。主要功能包括地址寻址、路由选择、拥塞控制等。

(4)传输层 Transport Layer: 传输层提供不同应用程序之间的数据传输服务,包括端到端的可靠性传输、差错纠正、流量控制、连接管理、多播、窗口大小、选择确认、优先级、会话等。

(5)会话层 Session Layer: 会话层用于建立、维护、取消和监视应用间的会话连接。主要功能包括会话控制、同步、回呼、撤销、确认和应答等。

(6)表示层 Presentation Layer: 表示层用于对信息的编码和解码,使通信双方都能理解彼此的消息。主要功能包括字符编码、加密解密、压缩和数据格式转换等。

(7)应用层 Application Layer: 应用层为用户提供了各种服务,包括文件传输、电子邮件、即时通信、远程访问等。应用层也为开发者提供了各种接口,可以通过该接口调用系统底层功能。

3.2 术语:

(1)协议 Protocol: 是定义两个或多个结点之间交换数据的规则、格式以及保障通信安全的一种手段。

(2)TCP/IP协议族 TCP/IP protocol suite: 由一系列协议组成的网络互连的标准协议集合,其中包括传输控制协议/ internet协议 (Transmission Control Protocol/Internet Protocol)。

(3)TCP Connection: TCP连接是指建立在Internet Protocol (IP) 上的可靠的、基于字节流的、双向的通信信道,使用TCP协议。一个TCP连接通常是一条两端点之间的逻辑链路。

(4)TCP/IP 模型 TCP/IP model: 是网络通信模型的一种抽象,它将网络通信分为七个层次,每一层完成特定的功能,并且能完整实现计算机网络的通信功能。

(5)HTTP Hypertext Transfer Protocol: 是Web上使用的协议,使用TCP/IP模型,默认端口号为80。HTTP协议属于应用层协议,它规定客户端和服务器如何进行通信。

(6)HTTPS Hypertext Transfer Protocol Secure: 是为了防止网络上传输敏感信息而设计的协议。HTTPS与HTTP的区别在于,HTTPS经过SSL或TLS协议加密后才能正常访问。

(7)REST Representational State Transfer: REST是目前最流行的互联网软件架构风格,它定义了一组基于URL的标准,旨在提高互联网软件的可伸缩性、可用性、互用性。

(8)JSON JavaScript Object Notation: 是一种轻量级的数据交换格式,易于使用且解析速度快。

(9)API Application Programming Interface: 是提供应用程序与程序库或其他服务之间的接口。

(10)RESTful API Restful architecture: 是符合REST规范的API,RESTful架构要求每个URI代表一种资源,客户端和服务器之间,传递这种资源的状态变迹应该由HTTP动词(GET、POST、PUT、DELETE、PATCH等)来表述。

(11)MQTT Message Queuing Telemetry Transport: 是一种基于发布/订阅模式的“轻量级”“可发布/订阅”消息传输协议。

4.核心算法原理和具体操作步骤以及数学公式讲解

4.1 WiFi模块工作原理

WiFi模块(Wi-Fi module)主要由四部分组成:Transmitter, Receiver, Transceiver, Antenna。

  • Transmitter:负责发射无线信号,可以接收来自外部源的信号并通过天线发出。
  • Receiver:负责接收无线信号,将其转换为数字信号并存储到接收缓存中。
  • Transceiver:负责接收来自上位机的命令,并将它们翻译成信号。同时也可以将信号转换为数字信号。
  • Antenna:用于将发射出的无线信号转换为电磁能。

当主机想要发射数据时,首先创建一个数据帧并附加上一些额外的控制信息(比如MAC地址、QoS等)。然后,将该帧作为载荷附加到待发送的IEEE802.11数据包上。最后,将数据包放入待发送队列,等待被调制解调器处理。

4.2 MQTT协议

MQTT(Message Queue Telemetry Transport)协议是IBM公司与遵循MQTT规范的第三方供应商一起推出的物联网通信协议。

4.2.1 MQTT协议特点

  • MQTT支持QoS 0~2级别的发布/订阅消息机制,保证了数据可靠性。
  • 支持TCP和TLS加密协议,实现了通信安全性。
  • 支持消息发布、订阅、推送等功能,实现了物联网通信的功能特性。

4.2.2 MQTT协议消息格式

MQTT协议采用如下的消息格式:

PUBLISH | TOPIC | PAYLOAD
SUBSCRIBE | TOPIC | QOS
UNSUBSCRIBE | TOPIC
PINGREQ | 
PINGRESP | 
DISCONNECT | 

TOPIC:主题名称,订阅消息的时候使用
PAYLOAD:发布消息时的负载数据
QOS:Quality of Service,指消息服务质量,共三种取值:QoS=0,最多一次;QoS=1,至少一次;QoS=2,只有一次

MQTT协议的发布消息格式为:

0x30 | LENGTH | PACKET ID | PUBLISH MSG | [RETAIN] [DUP] [QoS] [DUP][PacketID] 
  • LENGTH:包含整个报文长度字段的两个字节,后面的内容全部按这个长度截取;
  • PACKET ID:给每条报文都分配的一个唯一标识符,用来保证报文顺序和完整性;
  • RETAIN:保留标志位,默认为0,代表不保留消息;如果设置成1则表示服务器必须保留消息。
  • DUP:复制标志位,默认为0,代表该消息不必存储多份副本。如果设置成1,代表消息需要存储多份副本。
  • QoS:指定发布消息的服务质量等级。取值为0、1或2,分别对应最多一次、至少一次、只有一次的服务质量。

MQTT协议的订阅消息格式为:

0x82 | MSG ID MSB | MSG ID LSB | SUBSCRIBE MSG
  • MSG ID:订阅报文的标识符。
  • SUBSCRIBE MSG:指定订阅的主题列表和对应的服务质量。

MQTT协议的发布确认消息格式为:

0x40 | MSG ID MSB | MSG ID LSB | PUBACK MSG
  • MSG ID:发布报文的标识符。
  • PUBACK MSG:确认发布成功。

MQTT协议的取消订阅消息格式为:

0xA2 | MSG ID MSB | MSG ID LSB | UNSUBSCRIBE MSG
  • MSG ID:取消订阅报文的标识符。
  • UNSUBSCRIBE MSG:指定取消订阅的主题列表。

MQTT协议的PING请求消息格式为:

0xC0 | PINGREQ MSG
  • PINGREQ MSG:用于客户端与服务器之间的心跳检查。

MQTT协议的PING响应消息格式为:

0xD0 | PINGRESP MSG
  • PINGRESP MSG:响应服务器发送的PING请求。

MQTT协议的断开连接消息格式为:

0xE0 | DISCONNECT MSG
  • DISCONNECT MSG:通知服务器关闭当前连接。

4.3 NodeMCU架构

NodeMCU是基于ESP8266微控制器的开源物联网开发板,可编程入门级开发板,具有良好的可扩展性,适用于各种物联网项目。NodeMCU本身采用Lua语言编写,具有高灵活性、易上手、开发速度快、成本低、免费开源等优点。

4.3.1 ESP8266芯片

ESP8266(Espressif Systems 8266),是由乐鑫信息科技(中国)有限公司推出的非常便宜的单片机SOC(System on a Chip),其核心处理器是以太网控制器,可以实现TCP/IP协议栈,支持Wi-Fi、蓝牙、低功耗等物联网通信功能。目前市场上存在很多基于ESP8266的开发板,价格都在几块钱左右。

4.3.2 NodeMCU开发板原理图

NodeMCU的开发板布局主要分为以下几个部分:

  1. USB接口:用于给NodeMCU开发板供电;
  2. GPIO接口:用于连接主控板外部元器件;
  3. I2C接口:用于连接模拟传感器、IIC外设;
  4. SPI接口:用于连接SSD1306显示屏、DHT11温湿度传感器等;
  5. UART接口:用于连接串口调试器,传输数据;
  6. EN和RST按钮:用于切换开发板的使能状态、复位开发板;
  7. 电源LED:用于指示当前电源状态;
  8. LED矩阵:用于显示文字和图像;
  9. RGB LED:连接到GPIO接口上,可独立驱动;
  10. 天线接口:连接外部天线。

4.3.3 NodeMCU开发板软硬件连接方法

  • 电源接法:使用USB直连到开发板的5V/GND接口,并将EN和RST按下,使开发板工作状态下。
  • Wi-Fi接法:根据所选的版本,连接Wi-Fi热点,并获取AP的SSID密码信息。
  • 串口调试接法:将TXD连接到ESP8266的DIO13,RXD连接到ESP8266的DIO14接口,并接到UART接口的UART1。

4.4 ESP8266 AT指令集

AT指令集(Automated Terminal Communication Operations Codes,即自动通信操纵代码)是基于Telnet、串口等简单接口的命令集,用于AT命令交互式界面操作。对于需要频繁地发送指令进行配置的设备来说,AT指令集十分方便。

NodeMCU开发板支持的AT指令集如下表所示:

AT指令 描述
AT 测试AT指令是否可用
AT+CWMODE 设置设备的工作模式
AT+CWQAP 查询无线网络接入点的名称
AT+CWJAP 连接指定的无线网络
AT+CWLAPOPT 配置无线网络选项
AT+CIPMUX 设置多连接模式
AT+CIPSTATUS 查看当前的TCP连接状态
AT+CIPSTART 创建TCP连接
AT+CIPSEND 发送数据
AT+CIPOPEN 创建UDP连接
AT+CWDHCP 获取局域网IP地址
AT+CIFSR 获取IP地址
AT+CWAUTOCONN 开启AP自动连接
AT+CIPSHUT 关闭TCP或UDP连接
AT+CWHOSTNAME 修改主机名称
AT+CIPDNS 设置域名服务器地址
AT+CWLAP 查看Wi-Fi列表
AT+SYSRAM 设置系统RAM空间
AT+SYSMSG 打印系统日志
AT+RESET 重启设备
AT+UART 设置串口波特率
AT+SLEEP 进入睡眠模式
AT&W 保存设置并重启

5.具体代码实例和解释说明

5.1 Hello World!

创建并打开一个文本编辑器,输入如下的代码:

-- 连接wifi
dofile("connect_wifi.lc") -- 引用连接wifi的lua脚本

-- 初始化mqtt连接
mqttclient = nil
dofile("init_mqtt.lc") -- 引用初始化mqtt的lua脚本

-- 订阅topic
subscribeTopic()

-- 循环检测mqtt消息
tmr.alarm(0, 1000, tmr.ALARM_AUTO, checkMqttMsg)

以上代码包含两部分,第一部分是连接Wi-Fi和初始化MQTT连接的过程,第二部分是执行MQTT订阅、定时器检测MQTT消息的过程。

我们先来看第一个部分——连接Wi-Fi。该部分的作用是通过引用另一个lua脚本connect_wifi.lc,实现连接指定的Wi-Fi网络。

connect_wifi.lc中写入如下代码:

WIFI_SSID="Your Wifi Name"
WIFI_PWD="<PASSWORD>"
wifi.setmode(wifi.STATION)
station_cfg={}
station_cfg.ssid=WIFI_SSID
station_cfg.pwd=<PASSWORD>
station_cfg.auto=true
station_cfg.save=true
wifi.sta.config(station_cfg)
wifi.eventmon.register(wifi.EVENT_STA_CONNECTED, function(T) print("\n\tSTA Connected to:"..T.SSID.." IP:",T.IP) end)
wifi.eventmon.register(wifi.EVENT_STATION_GOT_IP, function(T) 
    dofile("init_mqtt.lc")
    subscribeTopic()
  end)
wifi.eventmon.register(wifi.EVENT_STA_DISCONNECTED, function(T) 
  if string.find(T.reason,"AP closed.") then
    wifi.sta.disconnect()
    tmr.delay(3000000) -- wait for 3 seconds before attempting to reconnect
    connectToWifi()
  else
    print("\n\tSTA Disconnected from ".. T.SSID.. "\nReason: ".. T.reason)
    node.restart()
  end
end)
print("Connecting to ".. WIFI_SSID.. "...")
wifi.sta.connect()
tmr.create():alarm(10000, tmr.ALARM_SINGLE, function() print("Connection timeout!") end)

这里设置了Wi-Fi的账号和密码,并设置了Wi-Fi模式为客户端模式。然后,通过wifi.sta.config()方法设置账号和密码信息,并将auto设置为true,这样在开机时可以自动连接指定的Wi-Fi。同时,设置save参数为true,这样当掉线后就可以自动连接上。

接着,注册三个回调函数,分别监听Wi-Fi的连接、获取IP地址、断开连接事件。当Wi-Fi连接上时,就会触发wifi.EVENT_STATION_GOT_IP事件,然后引用init_mqtt.lcsubscribeTopic()函数,实现初始化MQTT连接和订阅指定的topic。

当Wi-Fi连接失败或者意外断开连接时,会触发相应的回调函数。当出现意外断开连接原因为“AP已关闭”,就会重新尝试连接,否则会重启整个设备。

连接超时的判断和超时后的重连,是为了避免因为网络问题导致的连接不稳定。

第二部分——初始化MQTT连接和订阅Topic,同样也是引用另一个lua脚本init_mqtt.lc

init_mqtt.lc中写入如下代码:

-- mqtt config
local mqttHost = "your broker ip address or domain name"
local mqttPort = your port number
local mqttId = "your client id"
local mqttUser = ""
local mqttPwd = ""

-- create mqtt client and configure it
mqttclient = mqtt.Client(mqttId, 120)
mqttclient:lwt("/lwt", "offline", 0, 0)
mqttclient:on("message", onMqttMsgReceived)
mqttclient:on("offline", onMqttDisconnected)
mqttclient:username_pw_set(mqttUser, mqttPwd)
mqttclient:connect(mqttHost, mqttPort, 0, 1, function(client)
  print("\nMQTT connected")
  client:subscribe("/your topic/#", 0)
end)

这里设置了MQTT的相关配置,包括broker的IP地址或域名,端口号,客户端ID,用户名和密码。然后,创建了一个MQTT客户端对象,并注册了两个回调函数。其中,on("message")用于处理收到的MQTT消息;on("offline")用于处理MQTT连接断开。

最后,调用connect()方法连接到MQTT Broker,并订阅指定的topic。

第三部分——订阅Topic。

这里订阅的是一个通配符topic,以"/your topic/#"的方式订阅所有的以"/your topic/"开头的topic。

第四部分——定时器检测MQTT消息。

为了实现定时检测MQTT消息,我们设置了一个定时器,周期为1秒,使用tmr.ALARM_AUTO模式,并传入checkMqttMsg函数作为回调函数。该函数的内容比较简单,就是读取MQTT Client对象的消息队列,并逐一处理。

下面再详细说一下MQTT消息处理的流程:

  1. 当订阅的topic收到新的消息时,MQTT Client会发送消息到指定的callback函数,该函数的第一个参数为topic;
  2. 根据topic前缀过滤,将收到的消息转发给对应的handler,例如如果收到的topic为/your topic/data,则会转发给handler /your topic/data
  3. handler负责处理收到的消息,包括解析、校验、处理等;
  4. 如果handler处理完毕后没有返回任何数据,则默认返回nil

5.2 订阅和处理消息

订阅和处理消息主要依赖于MQTT协议的订阅和发布机制。由于MQTT协议的QoS级别,只能确保消息至少被消费一次。如果要确保消息被消费完全,可以使用RabbitMQ等中间件,实现队列的概念。

这里以NodeMCU为例,编写一个handler。handler的名字为"/your topic/data",对应的topic为"/your topic/data"

在handler中编写如下代码:

function handleData(msg)
  local payloadStr = msg:getPayload()
  local dataArr = cjson.decode(payloadStr)

  -- process received data...
end

return handleData

该handler接收一个MQTT消息对象,并获得消息的负载数据。这里通过cjson.decode()方法将负载数据转换成Lua table数组。

之后,可以处理table数组中的数据,做一些逻辑处理或运算。这里只是举例,并没有真正处理数据。

最后,在main.lua中引入该handler,并订阅它:

dofile("handle_data.lc") -- 引用handle_data.lc

mqttclient:subscribe("/your topic/data", 0, function(client)
   print("Subscribed to /your topic/data")
   client:on("message", "/your topic/data", handleData)
end)

这里调用subscribe()方法订阅指定topic,并注册一个回调函数,用于处理收到的消息。在回调函数中,将收到的消息传入handleData()函数处理。

这样,NodeMCU只需要订阅一个topic,就可以收到它的所有消息,并根据消息的topic选择相应的handler进行处理。


网站公告

今日签到

点亮在社区的每一天
去签到