記錄一下成果 主要是 domoticz 接收我們自己定義的傳感器的數據。 我準備用 Stm32 進行數據採集 然後讓 ESP8266 發送出去
目錄
-
MQTT的一些認識
在學習這部分的時候我主要參考了 這篇文章 http://www.1zlab.com/wiki/micropython-esp32/mqtt/
以下的部分圖片來自這篇文章的截圖 感謝作者的分享
這是 MQTT的模型對於我們的智能家居的系統而言 服務端自然就是安裝了 domoticz的 樹莓派或者類似的 平臺
剩下的傳感器的角色就是 發佈者(publisher) 而 我們的智能受控設備就是訂閱者。
在domoticz的 MQTT 服務器上面有一些的topic 你可以理解成是 報紙 有財經報紙 時政報紙 還有娛樂報紙
然後不同的受控設備 就去訂閱 不同的 topic 根據topic 裏面的提及自己的指令 去執行指定的動作(開燈)
publisher的角色類似於 作者, 他們把讀者( MQTT 服務器 domoticz)感興趣的信息 以json 的數據包 上傳到MQTT的 端口下的指定的 topic 。
這裏面 每一個輸出的 topic 裏面的 消息是羣發的,只要你訂閱了這個topic 就可以在有新的數據到來的時候收到 信息(不管這條信息 是不是給你的),然後 設備根據 消息 來判斷這個信息是不是自己的。
當我們設備少的時候 我們只需要 設置一個 輸出 topic (domoticz/out) 所有受控的智能設備 都會受到信息。 當我們的設備多的時候肯定不能這樣幹 我們就在設置一些其他的topic ,以此來減 那些硬件的負擔。
我們的傳感器(上傳數據的)會將自己收集到的信息上傳到指定的topic 然後我們的domoticz 會去分析這些數據,分類顯示 或者是留着做觸發等等
microPython程序的實現 main.py
import json
import simple
import time
import network
import os
sta_if = network.WLAN(network.STA_IF)
ap = network.WLAN(network.AP_IF)
ap.active(0)
sta_if.active(True)
sta_if.connect('Jeason_test', '1a2b3c4d')
sta_if.isconnected()
print(sta_if.ifconfig())
data = {
"idx" : 13,
"nvalue" : 15,
"svalue" : str(12.3)#"19.0"
}
json_data = json.dumps(data)
print(json_data)
time.sleep_ms(1000)
from machine import Pin
p2 = Pin(2, Pin.OUT)
def ctrl_cb(topic, msg):
data_dict = json.loads(msg)
if data_dict['idx'] == 1:
print("it's me")
print('state: ' + str(data_dict['nvalue']))
p2.value(bool(1- data_dict['nvalue'] ))
#print(data_dict)
c = simple.MQTTClient('esp8266', '192.168.8.4', 1883)
c.set_callback(ctrl_cb)
c.connect()
#c.subscribe('domoticz/out')
while True:
# c.check_msg()
c.publish('domoticz/in',json_data,1)
p2.value(0)
time.sleep_ms(100)
p2.value(1)
time.sleep_ms(100)
import usocket as socket
import ustruct as struct
#from ubinascii import hexlify
class MQTTException(Exception):
pass
class MQTTClient:
def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0,ssl=False, ssl_params={}):
if port == 0:
port = 8883 if ssl else 1883
self.client_id = client_id
self.sock = None
self.addr = socket.getaddrinfo(server, port)[0][-1]
self.ssl = ssl
self.ssl_params = ssl_params
self.pid = 0
self.cb = None
self.user = user
self.pswd = password
self.keepalive = keepalive
self.lw_topic = None
self.lw_msg = None
self.lw_qos = 0
self.lw_retain = False
def _send_str(self, s):
self.sock.write(struct.pack("!H", len(s)))
self.sock.write(s)
def _recv_len(self):
n = 0
sh = 0
while 1:
b = self.sock.read(1)[0]
n |= (b & 0x7f) << sh
if not b & 0x80:
return n
sh += 7
def set_callback(self, f):
self.cb = f
def set_last_will(self, topic, msg, retain=False, qos=0):
assert 0 <= qos <= 2
assert topic
self.lw_topic = topic
self.lw_msg = msg
self.lw_qos = qos
self.lw_retain = retain
def connect(self, clean_session=True):
self.sock = socket.socket()
self.sock.connect(self.addr)
if self.ssl:
import ussl
self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
msg = bytearray(b"\x10\0\0\x04MQTT\x04\x02\0\0")
msg[1] = 10 + 2 + len(self.client_id)
msg[9] = clean_session << 1
if self.user is not None:
msg[1] += 2 + len(self.user) + 2 + len(self.pswd)
msg[9] |= 0xC0
if self.keepalive:
assert self.keepalive < 65536
msg[10] |= self.keepalive >> 8
msg[11] |= self.keepalive & 0x00FF
if self.lw_topic:
msg[1] += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
msg[9] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
msg[9] |= self.lw_retain << 5
self.sock.write(msg)
#print(hex(len(msg)), hexlify(msg, ":"))
self._send_str(self.client_id)
if self.lw_topic:
self._send_str(self.lw_topic)
self._send_str(self.lw_msg)
if self.user is not None:
self._send_str(self.user)
self._send_str(self.pswd)
resp = self.sock.read(4)
assert resp[0] == 0x20 and resp[1] == 0x02
if resp[3] != 0:
raise MQTTException(resp[3])
return resp[2] & 1
def disconnect(self):
self.sock.write(b"\xe0\0")
self.sock.close()
def ping(self):
self.sock.write(b"\xc0\0")
def publish(self, topic, msg, retain=False, qos=0):
pkt = bytearray(b"\x30\0\0\0")
pkt[0] |= qos << 1 | retain
sz = 2 + len(topic) + len(msg)
if qos > 0:
sz += 2
assert sz < 2097152
i = 1
while sz > 0x7f:
pkt[i] = (sz & 0x7f) | 0x80
sz >>= 7
i += 1
pkt[i] = sz
#print(hex(len(pkt)), hexlify(pkt, ":"))
self.sock.write(pkt, i + 1)
self._send_str(topic)
if qos > 0:
self.pid += 1
pid = self.pid
struct.pack_into("!H", pkt, 0, pid)
self.sock.write(pkt, 2)
self.sock.write(msg)
if qos == 1:
while 1:
op = self.wait_msg()
if op == 0x40:
sz = self.sock.read(1)
assert sz == b"\x02"
rcv_pid = self.sock.read(2)
rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
if pid == rcv_pid:
return
elif qos == 2:
assert 0
def subscribe(self, topic, qos=0):
assert self.cb is not None, "Subscribe callback is not set"
pkt = bytearray(b"\x82\0\0\0")
self.pid += 1
struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
#print(hex(len(pkt)), hexlify(pkt, ":"))
self.sock.write(pkt)
self._send_str(topic)
self.sock.write(qos.to_bytes(1, "little"))
while 1:
op = self.wait_msg()
if op == 0x90:
resp = self.sock.read(4)
#print(resp)
assert resp[1] == pkt[2] and resp[2] == pkt[3]
if resp[3] == 0x80:
raise MQTTException(resp[3])
return
# Wait for a single incoming MQTT message and process it.
# Subscribed messages are delivered to a callback previously
# set by .set_callback() method. Other (internal) MQTT
# messages processed internally.
def wait_msg(self):
res = self.sock.read(1)
self.sock.setblocking(True)
if res is None:
return None
if res == b"":
raise OSError(-1)
if res == b"\xd0": # PINGRESP
sz = self.sock.read(1)[0]
assert sz == 0
return None
op = res[0]
if op & 0xf0 != 0x30:
return op
sz = self._recv_len()
topic_len = self.sock.read(2)
topic_len = (topic_len[0] << 8) | topic_len[1]
topic = self.sock.read(topic_len)
sz -= topic_len + 2
if op & 6:
pid = self.sock.read(2)
pid = pid[0] << 8 | pid[1]
sz -= 2
msg = self.sock.read(sz)
self.cb(topic, msg)
if op & 6 == 2:
pkt = bytearray(b"\x40\x02\0\0")
struct.pack_into("!H", pkt, 2, pid)
self.sock.write(pkt)
elif op & 6 == 4:
assert 0
# Checks whether a pending message from server is available.
# If not, returns immediately with None. Otherwise, does
# the same processing as wait_msg.
def check_msg(self):
self.sock.setblocking(False)
return self.wait_msg()
分析一下代碼。 我們的代碼 總共倆文件 第一個是main.py 另一個是 simple.py 第二個simple 主要是 mqtt 相關的函數 不是我寫的 我也不明白。main.py 中其實就是實現了一個 定時發送 數據的功能。
這個是我們的測試用的字典, 這裏面有我們的 domoticz 感興趣的一些關鍵的鍵值對, 我們的數據就是通過這個上傳的
這一句的作用是將 我們的字典轉換成一個 json 的格式 可以用來上傳
最後就是循環發送了
domoticz 服務器的一些設置
這個名字可以隨便起無關緊要 ,傳感器類型不同 對應的 數據包的格式也不太一樣 我測試用的是溫度 關於其他的傳感器的數據格式 我們需要去參考 官方的 API
我們做這個domoticz的 測試很重要的一個 參考信息就是調試信息 我們可以打開設置 去找
這裏有我們發送的一些信息 以及一些其他的指令
這裏是一些我用到的網網址以及參考的部分 希望對您有一定的幫助
domoticz中文API https://www.domoticz.cn/wiki/Domoticz_API%E5%8F%8AJSON%E7%BD%91%E5%9D%80
https://blog.csdn.net/xxmonstor/article/details/80479851
http://www.1zlab.com/wiki/micropython-esp32/mqtt/
Python 字典 https://www.runoob.com/python3/python3-dictionary.html
micropython wiki http://docs.micropython.org/en/latest/genrst/modules.html#json
https://blog.csdn.net/why19940926/article/details/88541295