??????
? ? ? ? ?本文不包含公司机密技术和信息。
? ? ? ? 来新公司几个月了,发现有一个困扰的点是,货架上的机器对环境温度还比较敏感,然后机器本身散热也挺严重,导致货架上部分机器受到高温干扰无法正常运行。
?????? 因为我在秒测时主要就是做的测温这一块儿,所以提了个主意,想要在货架上部署温度传感器,然后通过物联网网关集成数据,我们就可以随时监测当前的机器处在什么温度下。领导雷厉风行,觉得对研发有一定的帮助,直接拍板了。
?????? 想法有了,就去找了一下,发现了有很多专用的物联网服务器,比如mosquitto之类的。再加上蓝牙网关,初步想法基本上就形成了。
?????? 最终的技术框架:货架上根据需求密度,部署好BLE温度传感器,该传感器与具体的位置一一绑定,然后不断的广播该区域的温度数据。然后根据场地地形以及实际测试,部署BLE网关设备,网关会定期扫描周边的BLE广播数据,集成后上传到后端的物联网服务器。该服务器本身也提供一个简单的http服务,用于展示数据和提供查询服务。???
?????? 大概这么个结构(BLE + MQTT +HTTP):
? ? ? ? 这个网络的一个好处是,无论哪一环,各种设备,实现方式等,都可以灵活替换。
????????传感器毫无疑问使用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自动转发的端口):
?????????假如在访问地址后添加一个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万组温度数据,对于后续拓展有一定的想象空间; ? ? 特点:防水塑封包装,适用于长期监测稳定运行,带塑封的情况下温度反应较缓慢,当前场景可拆掉塑封。? ?
|