購買了一個帶有wifi的攝像頭, 沒有usb, 沒有以太網, 沒有藍牙......如何配置這個攝像頭接入家中的wifi?
一、代碼
1.SmartConfig
from socket import *
import time
import struct
from app.libs.crc8 import Crc8
def creat_content(ssid, password_, ip, bssid):
password = password_ + '1'
guide_code = [515, 514, 513, 512]
password_len = len(password)
ssid_len = len(ssid)
total_len = 5 + 4 + password_len + ssid_len
ssid_crc = get_crc(ssid, 1)
bssid_list = get_bssid_list(bssid)
bssid_crc = get_crc(bssid_list, 0)
datum_data = [total_len, password_len, ssid_crc, bssid_crc]
ip_address = get_ip_list(ip)
ap_password = [ord(str) for str in password]
ap_ssid = [ord(str) for str in ssid]
data = ip_address + ap_password + ap_ssid
total_data = datum_data + data
total_data_xor = 0
for num in total_data:
total_data_xor ^= num
datum_data.append(total_data_xor)
return guide_code, datum_data, data
def get_crc(list, str):
crc = Crc8()
list_num = list
if str:
list_str = list
list_num = [ord(str) for str in list_str]
[crc.update(num) for num in list_num]
return crc.value & 0xFF
def get_ip_list(ip):
return [int(num) for num in ip.split('.')]
def get_bssid_list(bssid):
return [int(num, 16) for num in bssid.split(':')]
def get_upper_lower(num):
high_ = num & 0xF0
high = high_ >> 4
low = num & 0x0F
word = [high, low]
return word[0], word[1]
def get_content(guide_code, datum_data, data):
messages = []
content_data = datum_data + data
length = len(content_data)
for k in range(20):
messages.extend(guide_code)
for j in range(25):
for k, num in enumerate(content_data):
crc = get_crc([num, k], 0)
crc_high, crc_low = get_upper_lower(crc)
data_high, data_low = get_upper_lower(num)
message_1 = (crc_high*16 + data_high + 40) & 0x1FF
message_2 = 296 + k % length
message_3 = (crc_low*16 + data_low + 40) & 0x1FF
message = [message_1, message_2, message_3]
messages.extend(message)
return messages
def add_contents(num_list):
all_msg = []
for num in num_list:
words = []
for i in range(num):
words.append('a')
one_msg = ''.join(words)
all_msg.append(one_msg)
return all_msg
MYGROUP = '224.1.1.1'
MYPORT = 7001
class Camera_UDP:
def __init__(self, port):
from app.libs.robot import Robot
robot = Robot()
self.ip = robot.ip
self.bssid = robot.bssid
self.addr = (robot.ip, port)
self.s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self.s.bind(self.addr)
ttl_bin = struct.pack('@i', 255)
self.s.setsockopt(IPPROTO_IP, IP_MULTICAST_TTL, ttl_bin)
self.s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, inet_aton(MYGROUP) + inet_aton(self.ip))
def send_message(self, ssid, passphrase):
guide_code, datum_data, data = creat_content(ssid, passphrase, self.ip, self.bssid)
messages = get_content(guide_code, datum_data, data)
k = 0
while True:
for i, msg in enumerate(add_contents(messages)):
if i % 5 == 0:
k += 1
MYGROUP = '224' + '.' + str(k % 100) + '.' + str(k % 100) + '.' + str(k % 100)
self.s.sendto(bytes(msg, 'utf-8'), (MYGROUP, MYPORT))
class Camera_TCP:
def __init__(self, port):
from app.libs.robot import Robot
robot = Robot()
self.ip = robot.ip
self.connected = False
self.addr = (robot.ip, port)
self.client_addr = ''
self.s = socket(AF_INET, SOCK_STREAM)
self.s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
self.s.bind(self.addr)
self.s.listen(2)
def receive_message(self):
data_temp = ['', '', '']
data_time = [0.0, 0.0, 0.0]
count = 0
while True:
connetion, client_address = self.s.accept()
try:
print("Connection from: ", client_address, 'count: ', count)
data = connetion.recv(1024)
data_temp[count] = data
data_time[count] = time.time()
if count > 0:
if data == data_temp[count-1] and time.time() - data_time[count-1] < 3:
count += 1
else:
count = 0
elif count == 0:
count += 1
if count == 2:
self.connected = True
count = 0
[connetion.send(bytes('OK', 'utf-8')) for i in range(10)]
print('Connected! IP: ', data)
finally:
connetion.close()
2.crc8
crc8採用標準多項式x8+x5+x4+1,依次對byte和seq做crc8校驗,低位先傳
class Crc8:
CRC_POLYNOM = 0x8c # 低位傳輸時標準多項式
CRC_INITIAL = 0x00
value = CRC_INITIAL # 全局變量
# 計算crcTable,即0~255對應的crc8值
dividend = 0
remainder = 0
crcTable = []
for i in range(1000):
crcTable.append(0)
while dividend < 256:
remainder = dividend
bit = 0
while bit < 8:
bit = bit + 1
# 與標準多項式異或操作
if (remainder & 0x01) != 0:
remainder = (remainder >> 1) ^ CRC_POLYNOM
# 如果最低位是0則向右移一位
else:
remainder = remainder >> 1
crcTable[dividend] = remainder
dividend = dividend + 1
# 用於計算一串數據的crc8,例如:DatumData中的ssid_crc8
# self.value是之前所有數據得到的crc8值
# tep是此次輸入的需要計算crc8的數字
def update(self, tep):
i = 0
value = self.value
while i < 1:
i = i + 1
# 當前數據與此次輸入的數據取異的值作爲本次用來計算crc8的數據:data
data = tep ^ value
# data與0xff和操作取到數據低8位的值,通過crcTable得到對應的crc8值:data_crc8
# 當前數據左移8位然後與data_crc8異或得到新的數據,低8位爲剛剛計算得到的data_crc8
value1 = (self.crcTable[data & 0xff] ^ (value << 8))
self.value = value1
二、SmartConfig編碼原理
1. 介紹
攝像頭一開始並未連接wifi網絡,於是電腦向當前接入的AP發送一系列UDP包,其中每一包的長度(Length字段)都按照SmartConfig通訊協議編碼,當前網絡的SSID和密碼就包含在Length字段中。此時攝像頭應工作在在混雜模式下,在混雜模式下,攝像頭將收到當前環境下所有wifi設備(AP/STATION)數據,通過一定的算法,識別數據包,解析出數據包發送方IP地址等信息,以及其中的SSID和密碼,並接入網絡,然後返回自己的IP地址。然後電腦和攝像頭即可建立TCP連接。
2.發送規則
1.GuideCode發送規則:
GuideCode由515/514/513/512組成,4包爲一組,需要連續發送10組以上。通過Wireshark抓包發現不同設備發送的前導碼數量並不完全一致。
2.DatumData和Data發送規則:
一組由3包組成,每組傳送1字節有效數據,每包Length爲9bit,每一組由如下格式數據組成:
① sequence header: 從某個數開始,每一組數據加1
② 每一包數據內的3個數據在組包完成後都須 + 40
3. 編碼內容
攝像頭髮送的數據由三部分組成:“GuideCode”、“DatumData”、“Data”
1) GuideCode:
GuideCode由515/514/513/512幾個數字組成
例如:
2) DatumData:
DatumData由5部分組成,分別爲:”totaldata_len” 、”password_len” 、”ssid_crc8” 、“bssid_crc8” 、“total_data_xor”
① totaldata_len(總數據長度;1字節)= 固定5字節(DatumData) + IP地址長度+ 密碼長度 + SSID長度
② password_len(密碼長度;1字節)
③ ssid_crc8(ssid的crc8結果;1字節)
④ bssid_crc8(bssid的crc8結果;1字節)
⑤ total_data xor(全部數據異或結果;1字節)
注意:SSID長度需要根據當前網絡是否隱藏判斷是否需要加入,如當前是隱藏網絡,totaldata_len須加ssid_len;,時Data區也須加ssid,否則不加此2項內容;但total_data_xor必須加入SSID計算xor。
例如:
解碼結果:
① totaldata_len: 0x1a ((5+4+10)+7)
② password_len: 0x0a
③ ssid_crc8: 0x84
④ bssid_crc8: 0xae
⑤ total_data_xor: 0xe8
3) Data:
Data由3部分組成,分別爲: “ip_address”、 “ap_password” 、“ap_ssid”
① ip_address(IP地址;4字節)
② ap_password
③ ap_ssid
例如:
解碼結果:
① ip_address: ”172.22.79.2”
② ap_password: ”1234567890”
三、crc8原理
CRC校驗算法,就是把需要校驗的數據與多項式進行循環異或(XOR),但進行XOR的方式與實際中數據傳輸時,是高位先傳、還是低位先傳有關。對於數據高位先傳的方式,XOR從數據的高位開始;對於數據低位先傳的方式,XOR從數據的低位開始。兩種不同的異或方式,即使對應相同的多項式,計算出來的結果也是不一樣的。
例如:數據高位先傳
使用多項式:x8+x5+x4+1(二進制爲:100110001)
計算一個字節:0x11(二進制爲:00010001)
計算步驟:
1)因爲採用順序異或,所以需要計算的數據左移8位,移位後數據爲:0001 0001 0000 000
2)先進行高9bit異或(多項式爲9bit),0001 0001 0000 0000,因爲高9bit的最高bit爲0,不需要進行異或,同理,接下來的兩bit也是0,也不需要進行進行異或。這樣處理後數據爲:1 0001 0000 0000
3)接下來最高位爲1,需要進行異或操作了
從上面的計算過程可以看到,多項式最高位爲1,遇到需要異或數據最高位爲1時,才進行異或計算,並且異或後,最高位就爲0了,最高位爲0,下次也不需要異或了,這樣需要採用代碼計算的方式,就可以把最高位去掉,不需要異或,最後結果也是一樣的。
四、參考
https://blog.csdn.net/flyingcys/article/details/54670688
http://depletionregion.blogspot.com/2013/10/cc3000-smart-config-transmitting-ssid.html
https://blog.csdn.net/zjli321/article/details/52998468