昨天,我们讲了socket通信,当服务器和客户端建立起连接时,就可以互相通信了。在互联网应用大多使用WebSocket接口来传输数据。而在物联网的应用中,常常出现这种情况:海量的传感器,需要时刻保持在线,传输数据量非常低,有着大量用户使用。如果仍然使用socket作为通信,那么服务器的压力和通讯框架的设计随着数量的上升将变得异常复杂!
那么有无一个框架协议来解决这个问题呢,答案是有的。那就是 MQTT(消息队列遥测传输)。
实验讲解:
MQTT 是 IBM 于 1999 年提出的,和 HTTP 一样属于应用层,它工作在 TCP/IP协议族上,通常还会调用 socket 接口。是一个基于客户端 - 服务器的消息发布 / 订阅传输协议。其特点是协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M )通信和物联网(IoT )。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
从上图可以看到, MQTT 通信的角色有两个,分别是服务器和客户端。服务器只负责中转数据,不做存储;客户端可以是信息发送者或订阅者,也可以同时是两者。具体如下图:
确定了角色后是如何传输数据呢?下表示 MQTT 最基本的数据帧格式,例如温度传感器发布主题“Temperature ”编号 , 消息是“ 25 ”(表示温度)。那么所有订阅了这个主题编号的客户端(手机应用)就会收到相关信息,从而实现通信。如下表所示:
由于特殊的发布/ 订阅机制,服务器不需要存储数据(当然也可以在服务器的设备上建立一个客户端来订阅保存信息),因此非常适合海量设备的传输。
发布者(publish)代码:
'''
实验名称:MQTT通信
版本:v1.0
日期:2022.4
作者:01Studio
说明:编程实现MQTT通信,实现发布数据。
MQTT助手:http://www.tongxinmao.com/txm/webmqtt.php#collapseOne
'''
import network,time
from simple import MQTTClient #导入MQTT板块
from machine import SoftI2C,Pin,Timer
from ssd1306 import SSD1306_I2C#初始化相关模块
i2c = SoftI2C(sda=Pin(42), scl=Pin(40))#WIFI连接函数
def WIFI_Connect():WIFI_LED=Pin(46, Pin.OUT) #初始化WIFI指示灯wlan = network.WLAN(network.STA_IF) #STA模式wlan.active(True) #激活接口start_time=time.time() #记录时间做超时判断if not wlan.isconnected():print('connecting to network...')wlan.connect('FM-674614', '12345678') #输入WIFI账号密码while not wlan.isconnected():#LED闪烁提示WIFI_LED.value(1)time.sleep_ms(300)WIFI_LED.value(0)time.sleep_ms(300)#超时判断,15秒没连接成功判定为超时if time.time()-start_time > 15 :print('WIFI Connected Timeout!')breakif wlan.isconnected():#LED点亮WIFI_LED.value(1)#串口打印信息print('network information:', wlan.ifconfig())return Trueelse:return False#发布数据任务
def MQTT_Send(tim):client.publish(TOPIC, 'Hello 01Studio!')#执行WIFI连接函数并判断是否已经连接成功
if WIFI_Connect():SERVER = 'mq.tongxinmao.com'PORT = 18830CLIENT_ID = 'pyWiFi-ESP32-S2' # 客户端IDTOPIC = '/public/01Studio/1' # TOPIC名称client = MQTTClient(CLIENT_ID, SERVER, PORT)client.connect()#开启RTOS定时器,编号为-1,周期1000ms,执行socket通信接收任务tim = Timer(-1)tim.init(period=1000, mode=Timer.PERIODIC,callback=MQTT_Send) #1000ms也就是每1s就发送一次
订阅者( subscribe )代码:
'''
实验名称:MQTT通信
版本:v1.0
日期:2021.8
作者:01Studio
说明:编程实现MQTT通信,实现订阅(接收)数据。
'''
import network,time
from simple import MQTTClient #导入MQTT板块
from machine import SoftI2C,Pin,Timer
from ssd1306 import SSD1306_I2C#初始化相关模块
i2c = SoftI2C(sda=Pin(42), scl=Pin(40))#WIFI连接函数
def WIFI_Connect():WIFI_LED=Pin(46, Pin.OUT) #初始化WIFI指示灯wlan = network.WLAN(network.STA_IF) #STA模式wlan.active(True) #激活接口start_time=time.time() #记录时间做超时判断if not wlan.isconnected():print('connecting to network...')wlan.connect('FM-674614', '12345678') #输入WIFI账号密码while not wlan.isconnected():#LED闪烁提示WIFI_LED.value(1)time.sleep_ms(300)WIFI_LED.value(0)time.sleep_ms(300)#超时判断,15秒没连接成功判定为超时if time.time()-start_time > 15 :print('WIFI Connected Timeout!')breakif wlan.isconnected():#LED点亮WIFI_LED.value(1)#串口打印信息print('network information:', wlan.ifconfig())return Trueelse:return False#设置MQTT回调函数,有信息时候执行
def MQTT_callback(topic, msg):print('topic: {}'.format(topic))print('msg: {}'.format(msg))#接收数据任务
def MQTT_Rev(tim):client.check_msg()#执行WIFI连接函数并判断是否已经连接成功
if WIFI_Connect():SERVER = 'mq.tongxinmao.com'PORT = 18830CLIENT_ID = 'pyWiFi-ESP32-S2' # 客户端IDTOPIC = '/public/01Studio/1' # TOPIC名称client = MQTTClient(CLIENT_ID, SERVER, PORT) #建立客户端对象client.set_callback(MQTT_callback) #配置回调函数client.connect()client.subscribe(TOPIC) #订阅主题#开启RTOS定时器,编号为1,周期300ms,执行socket通信接收任务tim = Timer(1)tim.init(period=300, mode=Timer.PERIODIC,callback=MQTT_Rev)
从以上代码可以看到发布者和订阅者的编程方式相近,另外本实验需要一个MQTT 服务器( Broker ),这里使用的是跟我们将用到的 MQTT 在线网络助手同一个服务器和端口。
为了方便测试,我们可以使用 MQTT 网络助手进行调试。这里推荐一个在线
MQTT 网络调试助手: http://www.tongxinmao.com/txm/webmqtt.php#collapseOne
打开上面网址,即可看到 MQTT 在线调试助手。可以配置基本信息,这里完
全默认即可,点击连接。
我们先测试“发布者”代码。将“发布者”代码下载到开发板,然后在 MQTT
助手中订阅主题修改为: '/public/01Studio/1' (跟代码发布的主题一致), QOS 选
择 0 即可。然后点击订阅主题。
订阅后运行开发板 publish 发布程序,可以看到最下方接收框收到来自开发
板发布的信息。
“订阅者”代码测试方法跟“发布者”相反。将“订阅者”代码下载到开
发板,然后在电脑 MQTT 助手中发布主题修改为: '/public/01Studio/1' (跟代码发
布的主题一致。)在下方空白框输入“ Hello 01Studio! ”。
开发板运行“订阅者”代码,成功连接后回到 MQTT 助手点击【发布】按钮
发布信息,可以在开发板的 REPL 看到接收到的订阅信息“ Hello 01Studio! ”被打
印出来了(数据格式为字节数据)。