IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 快速部署一个简易的环温监测网络(BLE+MQTT+HTTP) -> 正文阅读

[网络协议]快速部署一个简易的环温监测网络(BLE+MQTT+HTTP)

??????

? ? ? ? ?本文不包含公司机密技术和信息。

? ? ? ? 来新公司几个月了,发现有一个困扰的点是,货架上的机器对环境温度还比较敏感,然后机器本身散热也挺严重,导致货架上部分机器受到高温干扰无法正常运行。

?????? 因为我在秒测时主要就是做的测温这一块儿,所以提了个主意,想要在货架上部署温度传感器,然后通过物联网网关集成数据,我们就可以随时监测当前的机器处在什么温度下。领导雷厉风行,觉得对研发有一定的帮助,直接拍板了。

?????? 想法有了,就去找了一下,发现了有很多专用的物联网服务器,比如mosquitto之类的。再加上蓝牙网关,初步想法基本上就形成了。

?????? 最终的技术框架:货架上根据需求密度,部署好BLE温度传感器,该传感器与具体的位置一一绑定,然后不断的广播该区域的温度数据。然后根据场地地形以及实际测试,部署BLE网关设备,网关会定期扫描周边的BLE广播数据,集成后上传到后端的物联网服务器。该服务器本身也提供一个简单的http服务,用于展示数据和提供查询服务。???

?????? 大概这么个结构(BLE + MQTT +HTTP):

479284eb708c42489188532eeaac5610.png

? ? ? ? 这个网络的一个好处是,无论哪一环,各种设备,实现方式等,都可以灵活替换。

????????传感器毫无疑问使用BLE无线温度计,超低功耗且传输距离适合室内部署。这里我直接选了老东家秒秒测的产品,秒测的产品做的很棒,然后因为自己参与打造的,很信任也很熟悉~。秒测几乎所有的带温度测量功能的产品都有BLE,所以理论上选哪个都行。这里综合使用场景考虑,无线冷链标签以及米家蓝牙温湿度计2。

? ? ? ? 然后BLE网关,我自己找了一个比较好的功能很强大的网关,但是秒测同事帮推荐了四月兄弟的产品,简陋一些但是便宜很多,出于信任便选了后者。收到网关后发现,四月兄弟提供了一个测试用的mqtt服务器,这下好了,前期不用自己部署服务器了。

? ? ? ? 收到网关的时候,恰逢疫情居家办公,就自己在家里做测试,我手头还保留了一些当时发的家用温湿度计,还有几个研发用的冷链标签(属于离职未归还的公司财产,罪过…)。按照说明配置好网关后,直接就可以在工具中看到网关扫描到的广播数据了,很棒。

? ? ? ? 然后开始研究如何部署自己的服务器,毕竟四月兄弟的测试服务器不受控制。网络上有很多企业级的,腾讯和阿里云都有提供此类服务,比较有名的,EMQX,功能非常强大。还有一个是轻量级的mosquitto,可以部署在低功耗的单板计算机上。

? ? ? ? 我这里的话,是在一个专用网络环境中,并发量不会太大。考虑到我可能得自己准备机器部署,刚好手头有一个正在吃灰的树莓派,就选择了mosquitto。

? ? ? ? 这里可以直接安装eclipse-mosquitto,也可以自己下载源码编译,两个我都尝试过了,都比较easy。配置好端口号启动服务后,修改网关的上报地址和topic等信息,就可以在mosquitto的log中看到有源源不断的数据上来了。

? ? ? ? 接下来需要提供一个简单的http服务,用来展示数据以及提供查询接口。

? ? ? ? 由于传感器要和货架一一对应,这里要预先准备好对应关系。某个传感器,其放置在哪个位置,对应该位置的机器的ip段是多少,这样后续查询的时候就可以通过绑定关系知道某个ip对应的货架温度是多少。

????????这里我使用一个json文件来描述这个关系,比如A22这个货架,对应的ip分段是 10.77.22,此货架上部署了两个传感器。

[
	{
		"position":"A22",
		"ip_start":"10.77.22.0",
		"ip_end":"10.77.22.254",		
		"sensor":["FE9738011038","FE9738011096"]		
	},
	{
		"position":"A41",
		"ip_start":"10.77.41.0",
		"ip_end":"10.77.41.254",		
		"sensor":["FE9738011025","A4C138F18B7F"]		
	}

]

代码如下,这里由于并发数量很低,所以写的代码也偷懒,需要什么数据,直接暴力遍历了(我下次一定从良23333)。然后这里两种传感器都做了解析,只要有广播上来会先识别是哪个设备,再按照对应的格式解析数据。然后实在不熟悉python,找了个web.py模块提供简单的http服务。

#! /usr/bin/env python3
# -- coding:utf-8--

import os
import sys

sys.path.append('/home/min.hu/.local/lib/python3.9')
sys.path.append('/home/min.hu/.local/lib/python3.9/site-packages')
sys.path.append(os.path.abspath(os.path.dirname(__file__) + '/' + '..'))
sys.path.append("..")

import time
import paho.mqtt.client as mqtt
import msgpack
import json
import web

#HOST= "mqtt.bconimg.com"
HOST= "xx.xx.xx.xx"
PORT=1884
TOPIC ="hm/test"
USER ="hm"
PWD="hm"

NIOT_NAME = "MOT-U202"      #冷链标签
MINI_NAME = "LYWSD03MMC"    #米家蓝牙温湿度计2

niot_list=set()
mini_list=set()
maps={}
lists=[]

#@json file:labs_more_2.json

def read_lists(folder):
    filelist =os.listdir(folder)
    for i in range(0,len(filelist)):
        path = os.path.join(folder,filelist[i])
        print("file:{}".format(filelist[i]))
        if os.path.isfile(path) and path.endswith('.json'):
            with open(path,'r') as jfile:
                mlist = json.load(jfile)  
                lists.extend(mlist)
                for k in range(0,len(mlist)):
                    print(mlist[k])
        
def get_mac_info_by_band_ip(band_ip):
    for key,value in maps.items():
        iplist=value['ip']
        for ip in iplist:
            if(band_ip == ip):
                return key              
    return None
def get_map_info_by_ip(band_ip):
    ip_section= band_ip.rsplit('.',1)  
    for item in lists:
        if item['ip_start'].startswith(ip_section[0]):
            #直接返回
            return item
    return None    
    
def get_map_info_by_mac(band_mac):
    for item in lists:        
        if band_mac in item['sensor']:
            return item
    return None    


def print_map_info(map):
    print(map) 

#niot    
# 02 01 06 03 02 09 18 17 16 CF CF FE 97 38 01 10 38 D4 0C 21 05 00 00 00 00  01  C4   66 0B    00 0A
#                                 |-------mac-------|-max-|-min-|--counter--|sta|batt|cur_temp|itv(0.5min)|
#mini
# 02 01 06 11 16 95 FE 30 58 5B 05 0C 7F 8B F1 38 C1 A4 28 01 00 05 38 01 F6 01 64 ...
#                                    |------mac--------|           |temp |-hum-|batt|      

      
def read_temp(mac,advdata,device):
    cur_temp =float(0)
    if device ==NIOT_NAME:
        cur_temp = float(advdata[27]|(advdata[28]<<8))/100
    elif device ==MINI_NAME:          
        cur_temp = float(advdata[22]|(advdata[23]<<8))/10    

    for item in lists:        
        if mac in item['sensor']:
            index =item['sensor'].index(mac)
            if not 'temp' in item:
                item['temp']=['' for i in range(len(item['sensor']))]
            item['temp'][index]=cur_temp
            print(item)
            break

def check_name_from_ltv_data(mac,advdata):
    i=0
    while(i < len(advdata)):
        length =advdata[i]
        sec_type=advdata[i+1]        
        if sec_type ==9:#通过设备名称筛选
            name = ''.join(chr(advdata[i+2+x]) for x in range(length-1))
            if(MINI_NAME == name):
                mini_list.add(mac)
            elif(NIOT_NAME == name):
                niot_list.add(mac)
            else:
                None        
        i+=(length+1)

def check_temp_from_lvt_data(mac,advdata):
    if mac in niot_list:
        read_temp(mac,advdata,NIOT_NAME)    
    elif mac in mini_list:
        read_temp(mac,advdata,MINI_NAME) 

def print_hex(bytes):
    # l=[hex(int(i)) for i in bytes]
    l=["%02X"%(int(i)) for i in bytes] 
    print(" ".join(l))

def on_subscribe(mosq, obj, mid, granted_qos):
    print("Subscribed: " + str(mid))

def on_message(mosq, obj, msg):
    for d in msgpack.unpackb(msg.payload, raw = True)[b'devices']:
        #parse iBeacon data        
        advData = d[8:]
        mac = "{:02X}{:02X}{:02X}{:02X}{:02X}{:02X}".format((d[1]), (d[2]), (d[3]), (d[4]), (d[5]), (d[6]))
        # print("mac:%s"%mac)
        map=get_map_info_by_mac(mac)
        if map!=None:
            if(d[0]==4):            
                check_name_from_ltv_data(mac,advData)
            else:    
                check_temp_from_lvt_data(mac,advData)             


def on_connect(mosq, obj,flags, rc):
    mqttTopic = TOPIC
    print("Connected with result code "+str(rc))
    mqttc.subscribe(mqttTopic, 0)
    print("Connected")

read_lists("./")
mqttHost    = HOST
mqttPort    = PORT
mqttc = mqtt.Client()
mqttc.on_connect = on_connect
mqttc.on_subscribe = on_subscribe
mqttc.on_message = on_message
mqttc.username_pw_set(USER,PWD)
mqttc.connect(mqttHost, mqttPort, 60)
# mqttc.loop_forever()
mqttc.loop_start()

#query page.

class sensor:
    def GET(self,ip):
        if not ip:            
            return temp_page(lists)  
        else:
            map =get_map_info_by_ip(ip)
            if(map):
                print(map)  
                return map

    
urls = ('/(.*)', 'sensor')
app = web.application(urls, globals())
temp_page =web.template.frender('templates/temp.html')

if __name__ == "__main__":
    print("app.run...")
    app.run()
    

? ? ? ? 发现可以使用模板文件:

$def with(lists)

<!DOCTYPE html>
<html>
    <head>        
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>温度详情</title>           

    </head>
    <body>
        <h1>设备温度列表:</h1>
<table border ='1' >        
        
$for item in lists:
    $if 'temp' in item:
        <tr>
            <td>货架:</td>
            <td>$item['position']</td>
            <td>Mac:</td>
            <td>$item['sensor']</td>
            <td>温度:</td>
            <td>$item['temp']</td>
        </tr>        
</table> 
</body>
</html>

? ? ? ? 效果(这里是在服务器上跑,然后本地访问的,localhost是vscode自动转发的端口):

11f14c02272643379e3109491efb7d0d.png

?????????假如在访问地址后添加一个ip参数,则会直接返回这样一组数据:

{'position': 'A22', 'ip_start': '10.77.22.0', 'ip_end': '10.77.22.254', 'sensor': ['FE9738011038', 'FE9738011096'], 'temp': [31.27, 30.02]}

? ? ? ? 更进一步的扩展能力,比如需要查看某个货架的历史温度,有两种实现方式。一种是,服务器端集成数据仓库服务,持久化接收到的温度数据,物联网环境推荐使用同样小巧轻便的sqlite3;另外传感器本身是有存储历史数据的,可以通过直接读取某个传感器的数据来展示,这中方式需要网关支持,然后BLE的数据吞吐量限制,传输数据需要一定的时间。

然后几个主要的问题点。

????????树莓派的优点是,轻巧的很,部署也很方便,哪里需要就往哪里搬。如果没有专用服务器的话,一个树莓派几百元就能搞定。使用树莓派存在的一个问题是,只能在局域网中访问。如果需要在外部网络访问,需要花生壳之类的内网穿透软件。一套下来,成本可能比直接部署在云上高…

????????由于公司本身有研发专用的服务器,我想了想就省点钱,把mosquitto转移到研发服务器上。本身是个linux小白,又没有root权限,折腾起来有点麻烦,最后是在docker上安装的。

????????网上有一大堆如何在docker上部署mosquitto的文章,基本上都说明白了。有一个细节的地方是,mosquitto.conf文件的配置,各种路径要对应docker中的环境而不是本机。mosquitto.conf文件配置(docker):

persistence true
persistence_location /mosquitto/data
allow_anonymous false
password_file /mosquitto/config/pwfile.conf
log_dest file /mosquitto/log/mqtt.log
log_type error
log_type warning
log_type debug
listener 1884
#注意在docker中生成的文件不会同步到当前主机中,比如pwfile.conf文件,这里是手动复制过来的

Docker启动脚本:

docker run -itd \
 --name=mosquitto\
 --privileged=true \
 -p 1884:1884 -p 9001:9001 \
 -v /home/min.hu/mqtt/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \
 -v /home/min.hu/mqtt/config/pwfile.conf:/mosquitto/config/pwfile.conf \
 -v /home/min.hu/mqtt/data:/mosquitto/data \
 -v /home/min.hu/mqtt/log:/mosquitto/log \
  eclipse-mosquitto 

另一个比较麻烦的问题是,对于一些专用的vpn网络,部署起来相对麻烦。我们这边,要找运维开端口开防火墙,然后坑爹的网络环境跑起来还各种丢数据,ssh都登录不上,要反复的测试。

实际部署

????????一般基于BLE5.0以上的网关,其传输距离在50米左右,也就是说,一台网关可以覆盖方圆100米的范围。然后实际部署的时候,要考虑到墙壁以及各种物体阻碍信号传输,具体要部署多少个网关可以实际测试下。

????????然后传感器的部署数量,这里可以分为中高低三种精度。中等精度,每个货架放两台,一边一个;对于一个高精度的监测网络而言,可以每个货架每层放两台,或者更高精度每台机器旁边都放置一个传感器;低精度的话,每个货架一个就可以。

????????MQTT服务器的架构本身决定了,可以有多个sub client 和pub client。所以扩充起来非常方便,适合一步一步将整个网络从小到大逐渐扩展。

????????然后部署成本上,不计算其他任务的情况下,整体实际研发和部署时间:1-2天;

附,成本清单:

????????四月兄弟IOT网关(不带电源):265元/台 +电源12元/台;

? ? ? ??米家蓝牙温湿度计2:29元/台。寿命一年,可换电池。
?? ?优点:带液晶显示屏,带湿度数据,本机可存储三个月数据;
?? ?缺点:0℃以下可能无法正常工作;此产品为米家定制,未绑定米家账号的时候,不广播温湿度数据,可行性需要与秒测验证;
? ? ? ? 秒秒测独角兽:69元/台:

?? ?寿命:一年半以上,若调整广播频率和数据记录间隔,估计最低可达两年以上。
?? ?优点:超长寿命可免于维护,设备本身可连续记录10万组温度数据,对于后续拓展有一定的想象空间;
? ? 特点:防水塑封包装,适用于长期监测稳定运行,带塑封的情况下温度反应较缓慢,当前场景可拆掉塑封。? ?

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-06-26 17:06:02  更:2022-06-26 17:06:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年3日历 -2024/3/28 18:46:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码