斐讯M1的报文
家里有個斐訊M1的空氣檢測儀,原來可以發送數據到服務器,再從手機app查看的。后來廠家倒閉了,服務器也關了,,,因為聯系不到服務器,wifi信號燈就會一直一閃一閃的。這幾天嘗試模擬搭建服務器,獲得成功,記錄一下。
一、首先原服務器的域名是aircat.phicomm.com ,這個是改不了的,但是可以通過在路由器的dns上做設置,把它解析為自己局域網內的服務器IP,修改完ping一下域名,能成功的解析為本地地址就對了。
二、服務器通信的端口是tcp 9000 。
最簡單是在模擬的服務器上執行
netcat -l -p 9000 ,
防火墻也要開放9000端口
sudo ufw allow 9000
此時M1能訪問到aircat.phicomm.com:9000 , 信號燈就不會閃了。而且可以看到netcat不斷收到M1發送的數據。
三、如果要進一步,拿到發送的數據,就需要再做些開發了。嘗試用python做了個簡單實現
# coding: utf-8 import select import socket import queue from time import sleepresponseMsg = b'\x00\x18\x00\x00\x02{"type":5,"status":1}\xff#END#'# Create a TCP/IP server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setblocking(False)# Bind the socket to the port server_address = ('192.168.88.17', 9000) print ('starting up on %s port %s' % server_address) server.bind(server_address)# Listen for incoming connections server.listen(5)# Sockets from which we expect to read inputs = [server]# Sockets to which we expect to write # 處理要發送的消息 outputs = []# Outgoing message queues (socket: Queue) message_queues = {}while inputs:# Wait for at least one of the sockets to be ready for processingprint ('waiting for the next event')# 開始select 監聽, 對input_list 中的服務器端server 進行監聽# 一旦調用socket的send, recv函數,將會再次調用此模塊readable, writable, exceptional = select.select(inputs, outputs, inputs)# Handle inputs# 循環判斷是否有客戶端連接進來, 當有客戶端連接進來時select 將觸發for s in readable:# 判斷當前觸發的是不是服務端對象, 當觸發的對象是服務端對象時,說明有新客戶端連接進來了# 表示有新用戶來連接if s is server:# A "readable" socket is ready to accept a connectionconnection, client_address = s.accept()print ('connection from', client_address)# this is connection not serverconnection.setblocking(0)# 將客戶端對象也加入到監聽的列表中, 當客戶端發送消息時 select 將觸發inputs.append(connection)# Give the connection a queue for data we want to send# 為連接的客戶端單獨創建一個消息隊列,用來保存客戶端發送的消息message_queues[connection] = queue.Queue()else:# 有老用戶發消息, 處理接受# 由于客戶端連接進來時服務端接收客戶端連接請求,將客戶端加入到了監聽列表中(input_list), 客戶端發送消息將觸發# 所以判斷是否是客戶端對象觸發data = s.recv(1024)# 客戶端未斷開if len(data)>0:# A readable client socket has dataprint ('received [%s %s] from %s' % (data[:23].hex(), data[23:], s.getpeername()))# 將收到的消息放入到相對應的socket客戶端的消息隊列中responsedata=data[:23] + responseMsgmessage_queues[s].put(responsedata)# Add output channel for response# 將需要進行回復操作socket放到output 列表中, 讓select監聽if s not in outputs:outputs.append(s)else:# 客戶端斷開了連接, 將客戶端的監聽從input列表中移除# Interpret empty result as closed connectionprint ('closing', client_address)# Stop listening for input on the connectionif s in outputs:outputs.remove(s)inputs.remove(s)s.close()# Remove message queue# 移除對應socket客戶端對象的消息隊列del message_queues[s]# Handle outputs# 如果現在沒有客戶端請求, 也沒有客戶端發送消息時, 開始對發送消息列表進行處理, 是否需要發送消息# 存儲哪個客戶端發送過消息for s in writable:try:# 如果消息隊列中有消息,從消息隊列中獲取要發送的消息message_queue = message_queues.get(s)send_data = ''if message_queue:send_data = message_queue.get_nowait()else:# 客戶端連接斷開了print ("has closed")except queue.Empty:# 客戶端連接斷開了errinfo=s.getpeername()print ( errinfo )outputs.remove(s)else:if message_queue :if len(send_data)>0:print ("send %s " % send_data)s.send(send_data)else:print ("has closed ")# writable.remove(s)# print "Client %s disconnected" % (client_address)# # Handle "exceptional conditions"# 處理異常的情況for s in exceptional:print ('exception condition on', s.getpeername())# Stop listening for input on the connectioninputs.remove(s)if s in outputs:outputs.remove(s)s.close()# Remove message queuedel message_queues[s]sleep(1)#start #[aa0f01350742398f0b0000000000000000b0f89324705300 b'\x00\x00\x04{ "humidity": "47.29", "temperature": "23.66", "value": "1", "hcho": "10" }\xff#END#'] from ('192.168.130.40', 23741)#[aa0f01350742398f0b0000000000000000b0f89324705300 b'\x00\x00\x01\xff#END#'] #[aa0f01350742398f0b0000000000000000b0f8932470 b'\x00\x03\x00\x00\x01\xff#END#'代碼tcp通信的框架基本復制自網上,原作可能是在python2開發的,自己微調了一下,在python3可以跑。這里面關鍵是通信的報文解析,經過觀察是這樣的:
?1、M1往服務器送的報文
aa 0f 01 35 07 42 39 8f 0b 00 00 00 00 00 00 00 00 b0 f8 93 24 70 53?b'\x00\x00\x00\x00\x04{ "humidity": "47.29", "temperature": "23.66", "value": "1", "hcho": "10" }\xff#END#
(1)前23個字節(紅色部分,十六進制表示)一般是固定的,把它分為四組
| aa 0f 01 | 35 07 42 39 8f 0b | 00 00 00 00 00 00 00 00 | b0 f8 93 24 70 53 | |
| 第一組 | 第二組 | 第三組 | 第四組 |
第一組3個字節aa 0f 01是固定的,可以看為是報文標志符,可以用它來幫助判斷是否M1的報文;
第三組固定是8個全00字節;第四組是mac地址 ;
第二組經過觀察,其實是第四組(mac地址)的鏡像,可能是為了校驗或者迷惑?
(2)隨后是5個字節?+ Json數據?
5個字節定義不明,但目前只看到b'\x00\x00\x00\x00\x04?' ,這個不影響數據解析
Json數據就是發送的溫度、濕度、pm2.5等數據,稍作解析就可以利用。
(3)最后是結束標志:? ?b'\xff#END#?'? ,
跟開頭的報文標志聯合起來可以應該可以用正則提取報文了。
2、服務器往M1發的報文
服務器往M1發的報文有兩種
(1)心跳報文
aa 0f 01 35 07 42 39 8f 0b 00 00 00 00 00 00 00 00 b0 f8 93 24 70 53?b'\x00\x18\x00\x00\x02{"type":5,"status":1}\xff#END#'
這個報文紅色部分跟M1發過來的前23個字節是一樣的,可以從M1送來的報文截取后原封不動發回去。(調試時試過如果不一樣,會導致M1主動關閉連接,估計是m1判斷送來的報文mac地址跟自己mac地址不一樣,認為不是自己的數據。)后面的定義不明,不過也是固定的。
這個報文的作用好像是會激發M1上報數據,如果不發的話,M1在連接成功最初,會密集報送一定量的數據,之后如果沒有服務器反饋會明顯減慢發送頻率。在代碼里看見有人定義它為“心跳報文”。
(2)設置亮度
aa 0f 01 35 07 42 39 8f 0b 00 00 00 00 00 00 00 00 b0 f8 93 24 70 53?b'\x00\x18\x00\x00\x02{"brightness":"50.0","type":2}\xff#END#'
這個報文是設置屏幕亮度,brightness后面的數字一般是0、 25、 50、 75、 100,亮度逐漸增強
( 觀察發現發送了設置屏幕亮度的報文后,M1會暫停上報數據了,轉而發送
[aa0f01350742398f0b0000000000000000b0f893247053 b'\x00\x03\x00\x00\x01\xff#END#']
可能是對于設置亮度成功的一種應答。
此時需要服務器發一條心跳報文給M1,就會開始繼續上報數據)
總結
- 上一篇: aden -接球游戏3.0
- 下一篇: 记录一次微信小程序+阿里云oss的配置步