畢業設計--20200302--domoticz-ESP8266+micropython+MQTT 發送數據到 虛擬傳感器

記錄一下成果 主要是 domoticz  接收我們自己定義的傳感器的數據。  我準備用 Stm32 進行數據採集 然後讓 ESP8266 發送出去

目錄

MQTT的一些認識

microPython程序的實現 main.py

domoticz 服務器的一些設置

      ​


  1. 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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章