讓智能電燈可以基于環(huán)境的明暗來(lái)自動(dòng)地打開(kāi)和關(guān)閉??梢苑譃閮蓚€(gè)階段,第一階段是打造傳感器設(shè)備來(lái)感知光照的強(qiáng)弱,判斷出環(huán)境的明暗狀態(tài),第二階段是創(chuàng)建一個(gè)場(chǎng)景聯(lián)動(dòng),根據(jù)傳感器的數(shù)值來(lái)控制智能電燈的狀態(tài)。
1.通信技術(shù)-選擇 BLE 低功耗藍(lán)牙技術(shù)
因?yàn)楣庹諅鞲衅髟O(shè)備的部署位置比較靈活,不太可能像智能電燈一樣連接房間里的電源線,所以我們要用一種比 Wi-Fi 功耗更低的通信技術(shù)。 不過(guò)在正式開(kāi)發(fā)之前,還得補(bǔ)充說(shuō)明一些 BLE 的相關(guān)知識(shí)。 BLE 設(shè)備可以在 4 種模式下工作: 1.廣播模式),這里特指單純的廣播模式。這種模式下設(shè)備不可以被連接,只能夠以一定的時(shí)間間隔把數(shù)據(jù)廣播出來(lái),供其他設(shè)備使用,比如手機(jī)掃描處理。藍(lán)牙 Beacon 設(shè)備就是工作在這種模式。 2.從機(jī)模式,這種模式下設(shè)備仍然可以廣播數(shù)據(jù),同時(shí)也可以被連接。建立連接后,雙方可以進(jìn)行雙向通信。比如你用手機(jī)連接一個(gè)具有藍(lán)牙功能的體溫計(jì),這時(shí)體溫計(jì)就是從機(jī)(Peripheral)。 3.主機(jī)模式,這種模式下設(shè)備不進(jìn)行廣播,但是可以掃描周圍的藍(lán)牙廣播包,發(fā)現(xiàn)其他設(shè)備,然后主動(dòng)對(duì)這些設(shè)備發(fā)起連接。還是剛才那個(gè)例子,主動(dòng)連接藍(lán)牙體溫計(jì)的手機(jī)就是主機(jī)(Central)角色。 4.觀察者模式,這種模式下設(shè)備像主機(jī)模式一樣,也不進(jìn)行廣播,而是掃描周圍的藍(lán)牙廣播包,但是不同的地方是,它不會(huì)與從機(jī)設(shè)備建立連接。一般收集藍(lán)牙設(shè)備廣播包的網(wǎng)關(guān)就是在這種模式下工作的,它會(huì)將收集的廣播數(shù)據(jù)通過(guò)網(wǎng)線、Wi-Fi 或者 4G 等蜂窩網(wǎng)絡(luò)上傳到云平臺(tái)。
這里我們用的藍(lán)牙可以定義設(shè)備在廣播模式下工作。
2.選擇開(kāi)發(fā)板- ESP32 芯片的 NodeMCU 開(kāi)發(fā)板
因?yàn)?NodeMCU 是基于 ESP8266 芯片的,這款芯片并不支持低功耗藍(lán)牙。在市場(chǎng)上還有一款基于 ESP32 芯片的 NodeMCU 開(kāi)發(fā)板。MicroPython 也支持 ESP32 芯片,這樣我們就可以繼續(xù)使用 Python 語(yǔ)言來(lái)開(kāi)發(fā)了。 最重要的是也不貴,某多上買一塊也就是二十來(lái)塊
準(zhǔn)備 MicroPython 環(huán)境
接下來(lái)就要在 NodeMCU(ESP32)上安裝 MicroPython 固件,準(zhǔn)備 Python 程序的運(yùn)行環(huán)境。 MicroPython 官網(wǎng)已經(jīng)為我們準(zhǔn)備了編譯好的固件文件,這省掉了我們?cè)陔娔X上進(jìn)行交叉編譯的工作。你可以從這個(gè)鏈接 鏈接: link. 中選擇“Firmware with ESP-IDF v3.x”下面的“GENERIC”類別,直接下載最新版本的固件文件到電腦中。
然后使用一根 USB 數(shù)據(jù)線,將 NodeMCU 開(kāi)發(fā)板和電腦連接起來(lái)。我們使用 esptool 工具把這個(gè)固件燒錄到 NodeMCU 開(kāi)發(fā)板上。先在電腦終端上輸入下面的命令,清空一下 NodeMCU 的 Flash 存儲(chǔ)芯片。
esptool.py --chip esp32 --port /dev/cu.usbserial-0001 erase_flash
可以從命令里看到,和之前智能電燈用的命令相比,這里增加了芯片信息“esp32”。另外,“–port”后面的串口設(shè)備名稱,需要你替換為自己電腦上對(duì)應(yīng)的名稱。 成功擦除 Flash 之后,就執(zhí)行下面的命令,將固件寫入 Flash 芯片。
esptool.py --chip esp32 --port /dev/cu.usbserial-0001 --baud 460800 write_flash -z 0x1000 esp32-idf3-20200902-v1.13.bin
這時(shí),我們使用電腦上的終端模擬器軟件,比如 SecureCRT,通過(guò)串口協(xié)議連接上開(kāi)發(fā)板,注意波特率(Baud rate)設(shè)置為 115200。 然后你應(yīng)該就能看到下圖所示的內(nèi)容,并且可以進(jìn)行交互。

4.搭建光照傳感器硬件電路
現(xiàn)在,我們開(kāi)始基于 NodeMCU 搭建光照傳感器的硬件電路。 首先,我們要準(zhǔn)備好實(shí)驗(yàn)的硬件: NodeMCU(ESP32)開(kāi)發(fā)板一個(gè)(二十多) 光照傳感器模塊一個(gè)(幾塊錢) 杜邦線 / 跳線若干個(gè)(幾塊錢一大把) 面包板一個(gè)(7塊錢,物聯(lián)網(wǎng)必備工具) 然后,你可以按照我畫的連線圖來(lái)搭建出自己的電路。 
這里說(shuō)明一下,在我的電路圖中,光照傳感器模塊從左到右,管腳分別是光強(qiáng)度模擬信號(hào)輸出管腳、電源地 GND 和電源正 VCC 管腳。你需要根據(jù)自己的傳感器模塊調(diào)整具體的連線。 課程選擇的是基于 PT550 環(huán)保型光敏二極管的光照傳感器元器件,它的靈敏度更高,測(cè)量范圍是 0Lux~6000Lux。 Lux(勒克斯)是光照強(qiáng)度的單位,它和另一個(gè)概念 Lumens(流明)是不同的。Lumens 是指一個(gè)光源(比如電燈、投影儀)發(fā)出的光能力的總量,而 Lux 是指空間內(nèi)一個(gè)位置接收到的光照的強(qiáng)度。 這個(gè)元器件通過(guò)信號(hào)管腳輸出模擬量,我們讀取 NodeMCU ESP32 的 ADC 模數(shù)轉(zhuǎn)換器(ADC0,對(duì)應(yīng) GPIO36)的數(shù)值,就可以得到光照強(qiáng)度。這個(gè)數(shù)值越大,表示光照強(qiáng)度越大。 因?yàn)?ADC 支持的最大位數(shù)是 12bit,所以這個(gè)數(shù)值范圍是 0~4095 之間。這里我們粗略地按照線性關(guān)系做一個(gè)轉(zhuǎn)換。具體計(jì)算過(guò)程,你可以參考下面的代碼:
from machine import ADC
from machine import Pin
class LightSensor():
def __init__(self, pin):
self.light = ADC(Pin(pin))
def value(self):
value = self.light.read()
print("Light ADC value:",value)
return int(value/4095*6000)
5.編寫藍(lán)牙程序
NodeMCU ESP32 的固件已經(jīng)集成了 BLE 的功能,我們可以直接在這個(gè)基礎(chǔ)上進(jìn)行軟件的開(kāi)發(fā)。這里我們需要給廣播包數(shù)據(jù)定義一定的格式,讓其他設(shè)備可以順利地解析使用掃描到的數(shù)據(jù)。 我們可以使用小米制定的MiBeacon藍(lán)牙協(xié)議。
MiBeacon 藍(lán)牙協(xié)議的廣播包格式是基于 BLE 的 GAP制定的。GAP 控制了藍(lán)牙的廣播和連接,也就是控制了設(shè)備如何被發(fā)現(xiàn),以及如何交互。 具體來(lái)說(shuō),GAP 定義了兩種方式來(lái)讓設(shè)備廣播數(shù)據(jù): 一個(gè)是廣播數(shù)據(jù),這個(gè)是必須的,數(shù)據(jù)長(zhǎng)度是 31 個(gè)字節(jié); 另一個(gè)是掃描回復(fù)數(shù)據(jù),它基于藍(lán)牙主機(jī)設(shè)備(比如手機(jī))發(fā)出的掃描請(qǐng)求來(lái)回復(fù)一些額外的信息。數(shù)據(jù)長(zhǎng)度和廣播數(shù)據(jù)一樣。
所以,只要含有以下指定信息的廣播報(bào)文,就可以認(rèn)為是符合 MiBeacon 藍(lán)牙協(xié)議的。 Advertising Data 中 Service Data (0x16) 含有 Mi Service UUID 的廣播包,UUID 是 0xFE95。 Scan Response 中 Manufacturer Specific Data (0xFF) 含有小米公司識(shí)別碼的廣播包,識(shí)別碼 ID 是 0x038F。 其中,無(wú)論是在 Advertising Data 中,還是 Scan Response 中,均采用統(tǒng)一格式定義。 具體的廣播報(bào)文格式定義,你可以參考下面的表格。 
因?yàn)槲覀円獮楣庹諅鞲衅髟黾訌V播光照強(qiáng)度數(shù)據(jù)的能力,所以主要關(guān)注Object 的定義。Object 分為屬性和事件兩種,具體定義了設(shè)備數(shù)據(jù)的含義,比如體溫計(jì)的溫度、土壤的濕度等,數(shù)據(jù)格式如下表所示:  按照 MiBeacon 的定義,光照傳感器的 Object ID 是 0x1007,數(shù)據(jù)長(zhǎng)度 3 個(gè)字節(jié),數(shù)值范圍是 0~120000 之間。 這是課程代碼提供參考。
#File:ble_lightsensor.py
import bluetooth
import struct
import time
from ble_advertising import advertising_payload
from micropython import const
_IRQ_CENTRAL_CONNECT = const(1)
_IRQ_CENTRAL_DISCONNECT = const(2)
_IRQ_GATTS_INDICATE_DONE = const(20)
_FLAG_READ = const(0x0002)
_FLAG_NOTIFY = const(0x0010)
_ADV_SERVICE_DATA_UUID = 0xFE95
_SERVICE_UUID_ENV_SENSE = 0x181A
_CHAR_UUID_AMBIENT_LIGHT = 'FEC66B35-937E-4938-9F8D-6E44BBD533EE’
# Service environmental sensing
_ENV_SENSE_UUID = bluetooth.UUID(_SERVICE_UUID_ENV_SENSE)
# Characteristic ambient light density
_AMBIENT_LIGHT_CHAR = (
bluetooth.UUID(_CHAR_UUID_AMBIENT_LIGHT),
_FLAG_READ | _FLAG_NOTIFY ,
)
_ENV_SENSE_SERVICE = (
_ENV_SENSE_UUID,
(_AMBIENT_LIGHT_CHAR,),
)
# https://specificationrefs./assigned-values/Appearance%20Values.pdf
_ADV_APPEARANCE_GENERIC_AMBIENT_LIGHT = const(1344)
class BLELightSensor:
def __init__(self, ble, name='Nodemcu’):
self._ble = ble
self._ble.active(True)
self._ble.irq(self._irq)
((self._handle,),) = self._ble.gatts_register_services((_ENV_SENSE_SERVICE,))
self._connections = set()
time.sleep_ms(500)
self._payload = advertising_payload(
name=name, services=[_ENV_SENSE_UUID], appearance=_ADV_APPEARANCE_GENERIC_AMBIENT_LIGHT
)
self._sd_adv = None
self._advertise()
def _irq(self, event, data):
# Track connections so we can send notifications.
if event == _IRQ_CENTRAL_CONNECT:
conn_handle, _, _ = data
self._connections.add(conn_handle)
elif event == _IRQ_CENTRAL_DISCONNECT:
conn_handle, _, _ = data
self._connections.remove(conn_handle)
# Start advertising again to allow a new connection.
self._advertise()
elif event == _IRQ_GATTS_INDICATE_DONE:
conn_handle, value_handle, status = data
def set_light(self, light_den, notify=False):
self._ble.gatts_write(self._handle, struct.pack(“!h”, int(light_den)))
self._sd_adv = self.build_mi_sdadv(light_den)
self._advertise()
if notify:
for conn_handle in self._connections:
if notify:
# Notify connected centrals.
self._ble.gatts_notify(conn_handle, self._handle)
def build_mi_sdadv(self, density):
uuid = 0xFE95
fc = 0x0010
pid = 0x0002
fcnt = 0x01
mac = self._ble.config('mac’)
objid = 0x1007
objlen = 0x03
objval = density
service_data = struct.pack(“<3HB”,uuid,fc,pid,fcnt)+mac+struct.pack(“<H2BH”,objid,objlen,0,objval)
print(“Service Data:”,service_data)
return advertising_payload(service_data=service_data)
def _advertise(self, interval_us=500000):
self._ble.gap_advertise(interval_us, adv_data=self._payload)
time.sleep_ms(100)
print(“sd_adv”,self._sd_adv)
if self._sd_adv is not None:
print(“sdddd_adv”,self._sd_adv)
self._ble.gap_advertise(interval_us, adv_data=self._sd_adv)
驗(yàn)證光照傳感器
我們可以通過(guò)手機(jī)上的藍(lán)牙調(diào)試軟件來(lái)掃描周圍藍(lán)牙設(shè)備,查看設(shè)備有沒(méi)有藍(lán)牙廣播包輸出,能不能跟手機(jī)正常交互。常用的軟件有 LightBlue、nRFConnect 和 BLEScanner,選擇其中一個(gè)就行了。 比如課程選擇的是 nRF Connect,打開(kāi)之后,它會(huì)自動(dòng)掃描周圍的藍(lán)牙廣播包,將發(fā)現(xiàn)的設(shè)備以列表的形式展示。 如果周圍藍(lán)牙設(shè)備很多的話,為了方便發(fā)現(xiàn)自己的開(kāi)發(fā)板,你可以點(diǎn)擊列表上方的“No Filter”,選擇將“Max.RSSI”打開(kāi)。拖動(dòng)其中的滑竿到合適的值,比如 -50dBm,就可以過(guò)濾掉藍(lán)牙信號(hào)強(qiáng)度比較弱(一般也是比較遠(yuǎn))的設(shè)備。  下面是手機(jī)掃描到的基于 NodeMCU 開(kāi)發(fā)板的藍(lán)牙設(shè)備。  其中名稱 Nodemcu 下面的就是廣播包的具體數(shù)據(jù)。 到這里,我們就完成了光照傳感器設(shè)備的開(kāi)發(fā)工作。
根據(jù)光照強(qiáng)度數(shù)據(jù)來(lái)聯(lián)動(dòng)控制智能電燈
對(duì)于藍(lán)牙設(shè)備,我們需要借助網(wǎng)關(guān)來(lái)實(shí)現(xiàn)聯(lián)網(wǎng)的目的。需要用到樹(shù)莓派打造藍(lán)牙網(wǎng)關(guān),最終實(shí)現(xiàn)光照傳感器和智能電燈的場(chǎng)景聯(lián)動(dòng)。
網(wǎng)關(guān)系統(tǒng)架構(gòu)
網(wǎng)關(guān)的主要功能是協(xié)議轉(zhuǎn)換,一方面它需要接收低功耗藍(lán)牙技術(shù)的光照傳感器的廣播數(shù)據(jù),另一方面,它需要把解析的數(shù)據(jù)上傳到云平臺(tái)。 具體的架構(gòu)圖如下所示。 
南向藍(lán)牙通信
在樹(shù)莓派上進(jìn)行藍(lán)牙開(kāi)發(fā),可以使用bluepy軟件包。它提供了一個(gè) Python 語(yǔ)言版本的低功耗藍(lán)牙 API 接口,而且對(duì)樹(shù)莓派的適配非常好。
通過(guò)終端登錄樹(shù)莓派
在學(xué)習(xí)第 15 講的時(shí)候,應(yīng)該已經(jīng)在樹(shù)莓派上部署好了包含 Gladys Assistant 系統(tǒng)的 Raspbian 操作系統(tǒng),現(xiàn)在你可以直接使用這個(gè)系統(tǒng)。安裝軟件包之前,我們?cè)陔娔X終端上輸入下面的命令,通過(guò) SSH 協(xié)議登錄到樹(shù)莓派系統(tǒng)中。
$ ssh pi@gladys.local
其中,pi 就是默認(rèn)的登錄用戶名,gladys.local 是樹(shù)莓派開(kāi)發(fā)板的本地域名。 當(dāng)提示輸入密碼時(shí),我們輸入默認(rèn)密碼 raspberry,然后回車,就登錄到了樹(shù)莓派系統(tǒng)中。

通過(guò)圖形化窗口軟件登錄樹(shù)莓派
當(dāng)然,你也可以使用提供圖形化窗口的軟件來(lái)登錄樹(shù)莓派,比如 SecureCRT,它除了支持串口協(xié)議,同時(shí)也支持 SSH 協(xié)議。你只需要新建一個(gè)連接會(huì)話,按照下圖所示的內(nèi)容填寫就行了:  第一次登錄時(shí),SecureCRT 會(huì)彈窗提示我們查看“Host Key”,這時(shí)點(diǎn)擊“Accept Once”即可。 
然后我們輸入密碼“raspberry”,同時(shí)勾選“Save password”,省去以后重復(fù)輸入密碼的麻煩。點(diǎn)擊“OK”后,就進(jìn)入樹(shù)莓派系統(tǒng)了。 
在樹(shù)莓派開(kāi)發(fā)藍(lán)牙程序
我們?cè)跇?shù)莓派的終端上輸入下面命令,就可以完成 bluepy 的安裝:
$ sudo apt-get install python3-pip libglib2.0-dev
$ sudo pip3 install bluepy
另外,我們還需要安裝 interruptingcow 軟件包。它主要是便于編寫定時(shí)任務(wù)。它的安裝命令是:
$ sudo pip3 install interruptingcow
具體代碼如下,供參考:
#File: blescan.py
import time
from threading import Thread
from interruptingcow import timeout
from bluepy.btle import DefaultDelegate, Peripheral, Scanner, UUID, capitaliseName, BTLEInternalError
from bluepy.btle import BTLEDisconnectError, BTLEManagementError, BTLEGattError
class LightScanner():
SCAN_TIMEOUT = 5
def __init__(self, name):
self._name = name
def status_update(self):
results = self._get_data()
# messages = [
# MqttMessage(
# topic=self.format_topic(“property/light”),
# payload=results.lightlevel,
# )
# ]
return results
def _get_data(self):
scan_processor = ScanProcessor(self._name)
scanner = Scanner().withDelegate(scan_processor)
scanner.scan(self.SCAN_TIMEOUT, passive=True)
with timeout(
self.SCAN_TIMEOUT,
exception=Exception(
“Retrieving data from {} device {} timed out after {} seconds”.format(
repr(self), self._name, self.SCAN_TIMEOUT
)
),
):
while not scan_processor.ready:
time.sleep(1)
return scan_processor.results
return scan_processor.results
class ScanProcessor:
ADV_TYPE_SERVICE_DATA = 0x16
def __init__(self, name):
self._ready = False
self._name = name
self._results = MiBeaconData()
def handleDiscovery(self, dev, isNewDev, _):
is_nodemcu = False
if isNewDev:
for (adtype, desc, value) in dev.getScanData():
#Service Data UUID == 0xFE95 according to MiBeacon
if adtype == self.ADV_TYPE_SERVICE_DATA and value.startswith(“95fe”):
print(“FOUND service Data:”,adtype, desc, value)
#Object ID == 0x1007 according to MiBeacon
if len(value) == 38 and value[26:30] == '0710’:
light_den = int((value[-2:] + value[-4:-2]), 16)
mac = value[14:26]
self._results.lightlevel = light_den
self._results.mac = mac
self.ready = True
@property
def mac(self):
return self._mac
@property
def ready(self):
return self._ready
@ready.setter
def ready(self, var):
self._ready = var
@property
def results(self):
return self._results
class MiBeaconData:
def __init__(self):
self._lightlevel = None
self._mac = None
@property
def lightlevel(self):
return self._lightlevel
@lightlevel.setter
def lightlevel(self, var):
self._lightlevel = var
@property
def mac(self):
return self._mac
@mac.setter
def mac(self, var):
self._mac = var
北向 MQTT 對(duì)接云平臺(tái)
接下來(lái)我們要實(shí)現(xiàn)網(wǎng)關(guān)和云平臺(tái)的對(duì)接。
1.MQTT 開(kāi)發(fā)環(huán)境準(zhǔn)備-安裝軟件包
藍(lán)牙網(wǎng)關(guān)與云平臺(tái)交互的通信協(xié)議也是使用 MQTT 協(xié)議,所以我們需要安裝 MQTT 的軟件包。這里我們選擇支持 Python 語(yǔ)言開(kāi)發(fā)的Eclipse Paho軟件包。我們?cè)跇?shù)莓派的終端上輸入下面的命令來(lái)安裝。
$ sudo pip3 install paho-mqtt
安裝成功后,我們可以寫一個(gè) demo 程序測(cè)試一下。下面是我測(cè)試的代碼,這段代碼仍然會(huì)連接到 test.mosquitto.org,并且訂閱“/geektime/iot”的主題消息。
#File: mqttdemo.py
import paho.mqtt.client as mqtt
def on_connect(client, userdata, flags, rc):
print(“Connected with result code “+str(rc))
client.subscribe(“/geektime/iot”)
def on_message(client, userdata, msg):
print(msg.topic+” “+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
#Still connect to mqtt.eclipse.org
client.connect(“test.mosquitto.org”, 1883, 60)
client.loop_forever()
2.部署文件到樹(shù)莓派
現(xiàn)在,我們把測(cè)試文件 mqttdemo.py 上傳到樹(shù)莓派上。 你可以在電腦終端上,運(yùn)行下面的命令。(注意,你需要先在樹(shù)莓派上創(chuàng)建 pi-gateway 這個(gè)目錄。
$ scp mqttdemo.py pi@gladys.local:/home/pi/pi-gateway/
其中這個(gè) scp 命令是基于 SSH 協(xié)議實(shí)現(xiàn)的安全文檔傳輸功能。 當(dāng)然,你也可能更習(xí)慣圖形化的軟件,所以我再介紹一個(gè)能實(shí)現(xiàn) scp 功能的軟件 FileZilla。它支持 MacOS、Windows 和 Linux 操作系統(tǒng),操作界面也非常直觀。 打開(kāi)“站點(diǎn)管理器”,創(chuàng)建“新站點(diǎn)”。你可以按照下圖設(shè)置具體配置參數(shù),然后點(diǎn)擊“連接”,登錄到樹(shù)莓派系統(tǒng)。為了方便之后的使用,你可以勾選“保存密碼”選項(xiàng)。
 在軟件界面的左半部分是你的電腦上的文件目錄,右半部分是樹(shù)莓派上的目錄。你只需要雙擊左邊的某個(gè)文件,就可以將文件傳輸?shù)綐?shù)莓派上。當(dāng)然你也可以雙擊右邊樹(shù)莓派上的文件,將它傳輸?shù)侥愕碾娔X。  把文件傳輸?shù)綐?shù)莓派之后,我們就可以在樹(shù)莓派的終端上輸入下面的命令,運(yùn)行上面的 demo 程序。
$ sudo python3 mqttdemo.py
云平臺(tái)創(chuàng)建光照傳感器設(shè)備
現(xiàn)在,我們已經(jīng)做好了對(duì)接云平臺(tái)的準(zhǔn)備工作。在樹(shù)莓派上開(kāi)發(fā)與云平臺(tái)的通信代碼之前,我們還需要在騰訊云平臺(tái)上創(chuàng)建對(duì)應(yīng)的光照傳感器設(shè)備。 在“新建產(chǎn)品”中,產(chǎn)品類別選擇“智慧生活”—>“安防報(bào)警”—>“光照度傳感器”。數(shù)據(jù)協(xié)議仍然選擇“數(shù)據(jù)模板”,其他的保持默認(rèn)值即可。

創(chuàng)建成功后,我們點(diǎn)擊進(jìn)入數(shù)據(jù)模板的設(shè)置界面。為了盡量簡(jiǎn)單,課程只定義了一個(gè)屬性“光照度”,而且是只讀類型。你可以直接導(dǎo)入下面的 JSON 文件完成數(shù)據(jù)模板的設(shè)置。
{
“version”: “1.0”,
“profile”: {
“ProductId”: “你的ProductID”,
“CategoryId”: “112”
},
“properties”: [
{
“id”: “Illuminance”,
“name”: “光照度”,
“desc”: “光照度檢測(cè)”,
“mode”: “r”,
“define”: {
“type”: “float”,
“min”: “0”,
“max”: “6000”,
“start”: “0”,
“step”: “1”,
“unit”: “Lux”
}
}
],
“events”: [],
“actions”: []
}
 在“交互開(kāi)發(fā)”標(biāo)簽頁(yè)中,和智能電燈一樣,我們?nèi)匀槐3帧笆褂霉俜叫〕绦蚩刂飘a(chǎn)品”選項(xiàng)是打開(kāi)狀態(tài)。另外,還有一個(gè)配置項(xiàng)需要關(guān)注,那就是“智能聯(lián)動(dòng)配置”,因?yàn)楹竺嫖覀円獮楣庹諅鞲衅髟O(shè)置聯(lián)動(dòng)場(chǎng)景。 
我們點(diǎn)擊“配置”,在設(shè)置頁(yè)面中,就可以看到“光照度”這個(gè)屬性,因?yàn)樗侵蛔x屬性,所以只能作為聯(lián)動(dòng)的觸發(fā)條件。我們勾選“作為條件”的選項(xiàng),完成配置。
 下一步,在“設(shè)備調(diào)試”界面中,我們創(chuàng)建一個(gè)測(cè)試設(shè)備。點(diǎn)擊“新建設(shè)備”,輸入設(shè)備名稱“Lightsensor_1”。  創(chuàng)建成功后,在測(cè)試設(shè)備列表中,點(diǎn)擊“Lightsensor_1”,進(jìn)入設(shè)備的詳情頁(yè)面,我們可以看到設(shè)備三元組的信息。你需要將這些信息記錄下來(lái),因?yàn)楹竺娴拈_(kāi)發(fā)中需要使用。 在測(cè)試設(shè)備列表中,我們點(diǎn)擊“二維碼”操作,獲取測(cè)試設(shè)備的二維碼,以便在小程序“騰訊連連”中添加這個(gè)設(shè)備。 到這里,騰訊云平臺(tái)上的產(chǎn)品創(chuàng)建工作就完成了。
產(chǎn)品聯(lián)網(wǎng)開(kāi)發(fā)
在騰訊云平臺(tái)準(zhǔn)備好產(chǎn)品的配置工作之后,我們繼續(xù)在樹(shù)莓派上完成北向的通信交互的開(kāi)發(fā)工作。 之前在第二部分已經(jīng)了解了 MQTT 通信的主題 Topic ,以及 Broker 服務(wù)器地址、端口號(hào)、設(shè)備 ID、用戶名和密碼等連接參數(shù)的知識(shí)。 我們還是可以使用 sign.html 這個(gè)網(wǎng)頁(yè)工具生產(chǎn)用戶名和密碼,然后就能得到所有的參數(shù)。這時(shí),把這些參數(shù)替換到下面這段代碼的對(duì)應(yīng)位置就可以了。
#File: gateway.py
from blescan import LightScanner, MiBeaconData
import time
import asyncio
import json
import uuid
import paho.mqtt.client as MQTTClient
“””
QCloud Device Info
“””
DEVICE_NAME = “Lightsensor_1”
PRODUCT_ID = “MAO3SVUCFO”
DEVICE_KEY = “TYjuKNc2GpDykXUv4MWBOA==“
“””
MQTT topic
“””
MQTT_CONTROL_TOPIC = “$thing/down/property/“+PRODUCT_ID+”/“+DEVICE_NAME
MQTT_CONTROL_REPLY_TOPIC = “$thing/up/property/“+PRODUCT_ID+”/“+DEVICE_NAME
def mqtt_callback(client, userdata, msg):
# Callback
print(f”Received `{msg.payload.decode()}` from `{msg.topic}` topic”)
async def mqtt_connect():
#connect callback
def on_connect(client, userdata, flags, rc):
if rc == 0:
print(“Connected to MQTT Broker!”)
else:
print(“Failed to connect, return code %d\n”, rc)
mqtt_client = None
MQTT_SERVER = PRODUCT_ID + “.iotcloud.tencentdevices.com”
MQTT_PORT = 1883
MQTT_CLIENT_ID = PRODUCT_ID+DEVICE_NAME
MQTT_USER_NAME = “MAO3SVUCFOLightsensor_1;12010126;2OYA5;1609057368”
MQTTT_PASSWORD = “8f79b7f1b0bef9cde7fd9652383b6ff8bfeb8003cc994c64f3c8e069c11fd4c7;hmacsha256”
mqtt_client = MQTTClient.Client(MQTT_CLIENT_ID)
mqtt_client.username_pw_set(MQTT_USER_NAME, MQTTT_PASSWORD)
mqtt_client.on_connect = on_connect
mqtt_client.connect(MQTT_SERVER, MQTT_PORT, 60)
return mqtt_client
def mqtt_report(client, light_level):
client_token = “clientToken-“ + str(uuid.uuid4())
msg = {
“method”: “report”,
“clientToken”: client_token,
“params”: {
“Illuminance”: light_level
}
}
client.publish(MQTT_CONTROL_REPLY_TOPIC, json.dumps(msg))
async def light_loop(mclient):
bles = LightScanner('Nodemcu’)
mclient.subscribe(MQTT_CONTROL_TOPIC)
mclient.on_message = mqtt_callback
mclient.loop_start()
while True:
try:
data = bles.status_update()
except Exception as e:
print(“BLE SCAN error:”, e)
continue
print(“Light Level:”, data.lightlevel)
mqtt_report(mclient, data.lightlevel)
time.sleep(0.1)
async def main():
mqtt_client = None
# MQTT connection
try:
mqtt_client = await asyncio.wait_for(mqtt_connect(), 20)
except asyncio.TimeoutError:
print(“mqtt connected timeout!”)
if mqtt_client is not None:
await asyncio.gather(light_loop(mqtt_client))
asyncio.run(main())
在樹(shù)莓派上部署軟件
接下來(lái),我們把代碼文件 gateway.py 和 blescan.py 兩個(gè)文件也上傳到樹(shù)莓派的 /home/pi/pi-gateway 目錄中。 同時(shí),為了讓程序作為后臺(tái)服務(wù)運(yùn)行,并且能夠開(kāi)機(jī)自啟動(dòng),我們來(lái)做一個(gè) Pi Gateway Service。 首先,你需要新建一個(gè) service.sh 腳本文件,內(nèi)容如下:
#!/bin/sh
set -e
SCRIPT_DIR=$( cd “$( dirname “$0” )” >/dev/null 2>&1 && pwd )
cd “$SCRIPT_DIR”
sudo python3 ./gateway.py “$@“
然后,創(chuàng)建我們 service 的配置文件,內(nèi)容如下:
[Unit]
Description=Pi Gateway
Documentation=https://time./column/intro/100063601
After=network.target
[Service]
Type=simple
WorkingDirectory=/home/pi/pi-gateway
ExecStart=/home/pi/pi-gateway/service.sh
Restart=always
[Install]
WantedBy=multi-user.target
接著,把這兩個(gè)文件上傳到樹(shù)莓派系統(tǒng)的 /home/pi/pi-gateway 目錄中,并且運(yùn)行下面命令,修改文件的屬性。
$ sudo chmod a+x service.sh
$ sudo chmod a+x pi-gateway.service
最后,執(zhí)行下面的幾條命令,為樹(shù)莓派系統(tǒng)增添上 Pi Gateway 這個(gè)服務(wù)。
```javascript
$ sudo cp /home/pi/pi-gateway/pi-gateway.service /etc/systemd/system/
$ sudo systemctl daemon-reload
$ sudo systemctl start pi-gateway
$ sudo systemctl status pi-gateway
$ sudo systemctl enable pi-gateway
到這里,網(wǎng)關(guān)程序已經(jīng)在樹(shù)莓派上運(yùn)行起來(lái)。我們?cè)隍v訊云物聯(lián)網(wǎng)平臺(tái)上可以看到,光照傳感器變?yōu)椤霸诰€”狀態(tài)。
設(shè)置場(chǎng)景聯(lián)動(dòng)
現(xiàn)在可以為它們?cè)O(shè)置場(chǎng)景聯(lián)動(dòng)了。
場(chǎng)景聯(lián)動(dòng)任務(wù)分解
我們希望實(shí)現(xiàn)的聯(lián)動(dòng)場(chǎng)景是,基于環(huán)境的光照強(qiáng)度自動(dòng)控制電燈的開(kāi)和關(guān)。具體來(lái)說(shuō),這個(gè)目標(biāo)可以拆解為 3 個(gè)自動(dòng)觸發(fā)任務(wù): 當(dāng)光照強(qiáng)度大于 1024Lux 時(shí),關(guān)閉電燈。 當(dāng)光照強(qiáng)度小于 1024Lux 時(shí),打開(kāi)電燈。 至于光照強(qiáng)度等于 1024Lux 時(shí),也打開(kāi)電燈。 注意,這里的 1024Lux 是我自己選擇的一個(gè)值,你可以根據(jù)房屋情況自己調(diào)整。
聯(lián)動(dòng)設(shè)備準(zhǔn)備
如果你還沒(méi)有在小程序中添加光照傳感器設(shè)備,這時(shí)可以打開(kāi)微信中的騰訊連連小程序,掃描上面云平臺(tái)“設(shè)備調(diào)試”中保存的那個(gè)二維碼,添加光照傳感器測(cè)試設(shè)備“Lightsensor_1”。 現(xiàn)在你的小程序里面已經(jīng)有了兩個(gè)設(shè)備,如下圖所示。 
剛才我們已經(jīng)在騰訊云物聯(lián)網(wǎng)平臺(tái)上,為光照傳感器設(shè)置了“智能聯(lián)動(dòng)配置”?,F(xiàn)在,我們來(lái)為智能電燈配置智能聯(lián)動(dòng)能力。 我們進(jìn)入智能電燈的“交互開(kāi)發(fā)”頁(yè)面,打開(kāi)下面的“智能聯(lián)動(dòng)配置”頁(yè)面,然后,像下圖顯示的那樣,把“電燈開(kāi)關(guān)”的“作為任務(wù)”條件勾選上。 
聯(lián)動(dòng)任務(wù)創(chuàng)建
然后,我們進(jìn)入騰訊連連小程序,點(diǎn)擊下面的“+”,選擇“添加智能”,開(kāi)始配置工作。 
我們從彈框里選擇“自動(dòng)智能”,可以看到下圖的配置界面: 
首先,我們添加條件,選擇光照傳感器設(shè)備,然后就會(huì)看到光照度屬性。我們先設(shè)置大于 1024Lux 的條件。  然后,我們添加任務(wù),選擇智能電燈設(shè)備后,可以看到電燈開(kāi)關(guān)的屬性,選擇“關(guān)”,點(diǎn)擊保存。 
這時(shí),我們可以看到這個(gè)智能聯(lián)動(dòng)的條件和任務(wù)已經(jīng)配置完成。騰訊連連小程序還支持配置“生效時(shí)間段”,可以限定智能聯(lián)動(dòng)在選定的時(shí)間段內(nèi)運(yùn)行。

接下來(lái),我們還可以設(shè)置一個(gè)主題圖片和名稱,這個(gè)根據(jù)喜好來(lái)就行了。  按照相同的方法,我們可以設(shè)置其他兩個(gè)條件,如下圖所示:  最終的智能聯(lián)動(dòng),包括了剛才提到的 3 個(gè)不同的觸發(fā)條件。  現(xiàn)在,可以通過(guò)控制光照傳感器的光照明暗(比如用手遮擋光敏元器件然后再把手拿開(kāi)),來(lái)觀察智能電燈的打開(kāi)和關(guān)閉,檢驗(yàn)功能是否正常。
學(xué)習(xí)筆記總結(jié)自'物聯(lián)網(wǎng)開(kāi)發(fā)實(shí)戰(zhàn)’–郭朝斌 –筆記只用于學(xué)習(xí)交流,請(qǐng)不要用于商業(yè)用途。
|