大家好,我是機靈鶴。
最近研究開發了一個物聯網
IoT
小項目——久坐提醒 / 喝水提醒小助手。
1. 項目介紹
本項目實作了一個久坐提醒和喝水提醒的小助手,在檢測到連續工作較長時間之後,會列印紙條,提醒我們起身活動一下;在設定的喝水時間到了以後,也會列印紙條,提醒我們去喝水。
廢話不多說,先上圖。

項目用到的硬體裝置有:
-
- 樹莓派4B(作為網關裝置來接入塗鴉 IoT 生态)
- 咕咕機G2(迷你熱敏列印機)
- 人體紅外傳感器
- 無線智能 Zigbee 網關
事情是這樣的,過幾天就是跟女朋友的戀愛紀念日了,作為一名程式員,送什麼禮物才能既有創意又有誠意,既實用又能讓女朋友感受到我滿滿的愛呢?
我突然想到,前段時間女朋友公司年度體檢,檢查結果出來有好幾項飄黃,醫生給的建議是,多喝水!多運動!女朋友雖然不是程式員,但也是那種長期坐辦公室的工作,有時候忙起來連水都會忘記喝。
于是我想,何不利用我的專業技能,開發一個 ”久坐提醒“ 和 ”喝水提醒“ 小助手送給女朋友呢。
2. 方案設計
為了完成這個項目,我翻箱倒櫃找出來幾個,若幹年前買的壓箱底的電子産品。
-
- 咕咕機G2:一台迷你的熱敏列印機,之前做手帳時候買來列印圖案的,後來發現時間長了會褪色,于是就用來列印便條來背單詞,後來四六級過了以後,咕咕機就閑置了。
- 樹莓派4B:之前買了用來搭個人網站伺服器的,結果發現沒有公網 IP ,外網通路賊麻煩,而且有時候家裡停電斷網什麼的,伺服器動不動就失聯了。後來買了雲伺服器以後,樹莓派就漸漸閑置了。
- 人體紅外傳感器:上學時候偷偷放教室門口,檢測老師有沒有來的,畢業以後不需要跟老師鬥智鬥勇了,就慢慢閑置了(PS:由于之前那個傳感器閑置太久壞了,本項目開發時重新采購的塗鴉智能的傳感器)。
我的設想是:
-
- 喝水提醒:建立幾個喝水鬧鐘,喝水時間一到,咕咕機便會自動列印便條,提醒女朋友該去喝水了。
- 久坐提醒:将人體紅外傳感器放置在桌上正對座椅,當檢測到女朋友坐着持續時間超過半小時,便觸發久坐提醒,通知咕咕機列印便條,提醒女朋友該起來活動活動了。
硬體都備的差不多了,在開發時我遇到了一個比較棘手的問題,就是咕咕機跟人體傳感器它也不是同一家的裝置啊!
雖然兩家都有各自的控制 APP,但是它們畢竟是兩套系統,兩個平台,沒法兒關聯啊!
這時候我發現一個很牛的東西,塗鴉的 Link SDK ,号稱
适用于塗鴉現有産品方案外的裝置接入
。
按它的說法,所有塗鴉 IoT 生态以外的智能裝置,隻要是支援二次開發,理論上都可以通過這套
Link SDK
接入到塗鴉的生态中。那這就厲害了!這就意味着,我可以通過
Link SDK
把咕咕機和傳感器接入到同一套系統中,實作關聯了。
經過一番研究,本項目的方案設計示意圖如下:
在樹莓派中運作塗鴉的 Link SDK,一方面,樹莓派作為一個網關裝置接入到塗鴉雲中,與塗鴉雲中的其他裝置進行通信;另一方面,樹莓派通過調用咕咕機的
memobird API
,與咕咕機進行通信。
3. 開發流程
開發過程大概分為 4 個階段:
-
- 建立産品:在塗鴉雲平台建立産品,包括配置功能點,開發操作面闆,下載下傳 SDK 及擷取授權碼,線上調試等環節。
- 硬體開發:下載下傳 Link SDK,編寫代碼并運作在樹莓派中,将樹莓派作為一個 IoT 裝置接入到塗鴉雲上。
- 咕咕機開發:根據咕咕機開發文檔,開發并封裝好相關接口,在樹莓派上可以調用接口實作咕咕機裝置綁定和紙條列印等功能。
- 智能關聯:通過設定智能場景和關聯條件,完成咕咕機與人體紅外傳感器之間的智能關聯。
接下來正式進入開發階段。
3.1 建立産品
3.1.1 注冊開發者賬号
首先,需要在塗鴉智能平台(https://t.tuya.com/AY1D3R8rbM)注冊開發者賬号。
注冊完成後,進入 IoT 平台首頁
3.1.2 建立智能産品
點選 IoT 平台首頁的
建立産品
按鈕,開始建立我們的智能産品。
第一步,選擇我們要建立的産品類型,由于我們要接入的硬體裝置是樹莓派+咕咕機,不在塗鴉現有産品品類之中,是以點選
找不到品類?
按鈕。
第二步,填寫産品資訊,根據自己項目的實際情況填寫産品資訊,填寫完成後,點選建立産品。
3.1.3 功能定義
建立好産品之後,我們需要為産品添加
功能定義
。
什麼意思呢?說白了就是為雲平台和裝置之間制定通信協定的,約定好哪條協定對應哪個功能,資料格式如何如何之類的。
标準功能是為了塗鴉生态内的裝置開發時提供的快捷模闆,我們這裡用不到,需要使用自定義功能。
點選建立自定義功能,建立我們的喝水提醒功能。
-
- DP_ID 時功能的編号,前 100 的編号被系統保留,我們的可以使用 101 往後的編号。
- 資料類型有六種,具體的差別參考文檔:https://developer.tuya.com/cn/docs/iot/custom-functions?id=K937y38137c64
- 資料傳輸類型,上報是指裝置向雲平台發送消息,下發是指雲平台發送指令。
3.1.4 面闆開發
設定好功能定義後,進入裝置面闆界面,開發 APP 互動的面闆。
編輯器左側提供了很多元件,可以通過拖拽的方式來編輯面闆布局,并在右側屬性頁簽中,通過添加互動,來為目前選中的元件添加互動動作。
詳細使用方法參考:https://developer.tuya.com/cn/docs/iot/panel?id=K9hzyyk6g4p3m
如圖,我們添加一個按鈕,在按鈕屬性中,添加互動,觸發
喝水提醒
功能 。這樣我們點選按鈕時,就會向裝置發送一條
喝水提醒
的消息。
編輯好以後,點選右上角
預覽
按鈕,然後用
塗鴉智能
APP 掃描二維碼,可以在手機上實時預覽(預覽模式下,編輯器中做的修改,手機APP中會同步變化)。
APP 可以在各大應用商店下載下傳到,搜
塗鴉智能
即可。
預覽測試沒問題的話,可以點選釋出。
經過一段時間的打包後,打包成功,我們直接點選
去釋出
,然後選擇
釋出并應用
即可。
3.1.5 硬體開發
在硬體開發頁面,雲端接入方式選擇
Link SDK
,雲端接入硬體選擇
通用CPU
。
我們将自己的裝置接入塗鴉雲的話,第一需要 Link SDK , 第二需要購買
授權碼
。
Link SDK
在開發資料中可以下載下傳,授權碼需要購買(10元/個)。
不過平台給每個開發者提供了兩個調試用的雲端授權碼,可以免費領取(領取按鈕在
立即購買
按鈕下方,我因為領取過了是以截圖裡沒有顯示)。
領取成功以後,點選
下載下傳授權碼清單
按鈕即可下載下傳。授權碼清單中包含了 uuid 和 key,如下所示:
uuid | key |
tuyaf5753924f3d94306 | 5TbgqpGXzbgrr8Plb8L93rBy38Liu7ok |
3.1.6 産品配置
産品配置中,可以為裝置添加一些如多語言,消息推送,配網引導的功能,因為本項目中暫時用不到,可以跳過。
3.1.7 裝置調試
然後是裝置調試,由于我們的樹莓派 Link SDK 還沒有開發到位,是以先添加一個虛拟調試裝置,來進行調試。
手機應用商店裡,下載下傳安裝
塗鴉智能
APP,然後用 APP 掃描上面的二維碼,可以添加一個虛拟裝置,用于調試。
添加好虛拟裝置後,點選調試按鈕,進入調試界面。然後可以使用這裡的虛拟裝置跟手機 APP 端進行通信。
通過這個,我們可以看到上報資料和下發資料是否正确(主要用來驗證
3.1.4 節
開發的面闆功能是否正确)。
消息協定調試無誤後,開始開發我們的裝置
3.2 硬體開發
産品建立成功,且 APP 端調試無誤後,我們正式請出我們的主角,樹莓派,來進行基于 Link SDK 的硬體開發。
3.2.1 運作環境
樹莓派上燒錄好
Raspberry Pi OS
系統,并且安裝好
Python3.6+
環境。
燒錄系統的過程略去不講,大家沒有樹莓派的話,也可以用自己電腦,或者虛拟機來替代。
3.2.2 安裝源碼
首先下載下傳 Link SDK ,平台上預設提供的是 c 語言版本。
下載下傳位址:https://github.com/tuya/tuya-iot-link-sdk-embedded-c
我比較常用 Python,是以找的是 Python 版本(支援 Python 3.6+)。
下載下傳位址:https://github.com/tuya/tuyaos-link-sdk-python
安裝方法:
- 通過
安裝Pypi
python3 -m pip install tuyalinksdk
- 通過源碼安裝
git clone https://github.com/tuya/tuyaos-link-sdk-python.git
python3 -m pip install ./tuyaos-link-sdk-python
3.2.3 代碼編寫
在項目路徑中,建立一個
demo.py
腳本,腳本内容如下:
#!/usr/bin/env python
import time
from tuyalinksdk.client import TuyaClient
from tuyalinksdk.console_qrcode import qrcode_generate
# 這裡更換成自己的ID
PID = "jj9hhmjxsf8u2b94"
UUID = "tuyaf5753924f3d94306"
AUTHKEY = "5TbgqpGXzbgrr8Plb8L93rBy38Liu7ok"
client = TuyaClient(productid = PID, uuid = UUID, authkey = AUTHKEY)
def on_connected():
print('Connected.')
def on_qrcode(url):
qrcode_generate(url)
def on_reset(data):
print('Reset:', data)
def on_dps(dps):
print('DataPoints:', dps)
client.push_dps(dps)
client.on_connected = on_connected
client.on_qrcode = on_qrcode
client.on_reset = on_reset
client.on_dps = on_dps
client.connect()
client.loop_start()
while True:
time.sleep(1)
代碼中第 7-9 行的 3 個參數,
PID
,
UUID
,
AUTHKEY
需要替換成你自己的産品的值。
PID
,即
productid
産品 ID, 可以在
我的産品
清單裡檢視
産品ID
,如圖所示。
UUID
和
AUTHKEY
為裝置的授權碼,在前面
3.1.5 硬體開發
部分,下載下傳的免費授權碼的檔案中可以檢視,分别對應
UUID
和
KEY
兩列。
3.2.4 真機運作
代碼寫好以後,在樹莓派中使用終端,執行
python3 demo.py
指令啟動程式。
程式運作後,會彈出一個二維碼,用
塗鴉智能 APP
掃碼以後,即可添加裝置。
在代碼中,
on_dps
函數負責接收和處理 APP 端下發的指令,我們隻需要根據指令的值,觸發不同的操作即可。
如,接收到
{"101": "True"}
時,執行
喝水提醒
的行為,示例代碼如下:
DRINK_DP_ID = "101"
def on_dps(dps):
print('DataPoints:', dps)
client.push_dps(dps)
for key in dps:
if key == DRINK_DP_ID and dps[key] == True:
# TODO: 喝水提醒
pass
3.3 咕咕機接入
咕咕機是一款迷你的熱敏列印機,可以友善的列印文字和圖檔,而且官網的開發者平台上開放了它的 API ,可玩性非常高。
3.3.1 注冊開發者賬号
首先需要在
Memobird
開放平台新增賬號,并申請成為咕咕機開發者。
Memobird 開放平台線上位址:https://open.memobird.cn/
注冊好賬号,填寫好自己的
咕咕号
,
聯系方式
,
用途
等資訊之後,送出稽核就可以了。
一段時間,稽核通過後,會向新增賬號的郵箱發送一封郵件,包含申請到的
Access_Key
。
開發文檔:https://open.memobird.cn/upload/webapi.pdf
3.3.2 綁定裝置
示例代碼:
import requests
import datetime
# 綁定裝置的API
BIND_DEVICE_API = "http://open.memobird.cn/home/setuserbind"
# 開發者 Access_Key
Access_Key = "261ba***************9274a"
# 咕咕機裝置編号,輕按兩下咕咕機吐出來的裝置編号
MEMOBIRD_ID = "301*********fe69"
# 使用者自定義字元串,與咕咕平台進行關聯的使用者唯一辨別符
USER_IDENTIFYING = "smartcrane"
# 擷取時間戳
def getTimeStamp():
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return timestamp
# 綁定裝置,擷取使用者ID
def getUserID():
params = {
"ak" : Access_Key,
"timestamp" : getTimeStamp(),
"memobirdID" : MEMOBIRD_ID,
"useridentifying" : USER_IDENTIFYING
}
try:
r = requests.get(BIND_DEVICE_API, params=params)
# 傳回結果形如:
# {"showapi_res_code":1,"showapi_res_error":"ok","showapi_userid":8}
return r.json()["showapi_userid"]
except Exception as e:
print(e)
return ""
getUserID()
3.3.3 列印文字
示例代碼:
import requests
import datetime
# 列印紙條的API
PRINT_PAPER_API = "http://open.memobird.cn/home/printpaper"
def printText(text, userId):
# 要列印的内容 T:文字的base64編碼(gbk)
printContent = "T:" + base64.b64encode(text.encode("gbk")).decode()
params = {
"ak" : Access_Key,
"timestamp" : getTimeStamp(),
"memobirdID" : MEMOBIRD_ID,
"printcontent" : printContent,
"userID" : userId,
}
r = requests.post(PRINT_PAPER_API, data=params)
print(r.text)
userId = getUserID()
printText("Hello World", userId)
運作後,咕咕機成功列印。
3.3.4 列印圖檔
示例代碼:
import requests
import datetime
# 列印紙條的API
PRINT_PAPER_API = "http://open.memobird.cn/home/printpaper"
# JPG/PNG圖檔轉單色點陣圖base64編碼的API
BASE64_PIC_API = "http://open.memobird.cn/home/getSignalBase64Pic"
# 擷取圖檔轉換後的base64編碼
def getPictureBase64(path):
with open(path,"rb") as f:#轉為二進制格式
base64_data = base64.b64encode(f.read())#使用base64進行加密
params = {
"ak" : Access_Key,
"imgBase64String" : base64_data,
}
r = requests.post(BASE64_PIC_API, data=params)
return r.json()["result"]
def printPicture(path, userId):
# 要列印的内容 P:單色點陣圖的base64編碼
printContent = "P:" + getPictureBase64(path)
params = {
"ak" : Access_Key,
"timestamp" : getTimeStamp(),
"memobirdID" : MEMOBIRD_ID,
"printcontent" : printContent,
"userID" : userId,
}
r = requests.post(PRINT_PAPER_API, data=params)
print(r.text)
userId = getUserID()
printPicture("drink_0.jpeg", userId)
運作代碼以後,咕咕機成功列印出來圖檔。
更多的功能,如擷取列印狀态,列印網頁,列印HTML等,這裡用不到,就不做示例了
大家感興趣的話,可以去參考官網開發者文檔。
3.3.5 咕咕機接入塗鴉雲
接下來,我們通過樹莓派來操控咕咕機,将咕咕機接入塗鴉生态。
首先我們将代碼整理一下,儲存為
memobird.py
腳本,如下:
import requests
import datetime
import random
import base64
class Memobird:
def __init__(self):
self.BIND_DEVICE_API = "http://open.memobird.cn/home/setuserbind"
self.PRINT_PAPER_API = "http://open.memobird.cn/home/printpaper"
self.AK = "填入你申請的開發者AK"
self.MEMOBIRD_ID = "填入你咕咕機的裝置ID"
self.USER_IDENTIFYING = "smartcrane" #随便填一個字元串都可以
self.DRINK_REMINDER_TEXT = [
"機靈鶴,你好\n我是“喝水提醒小助手”\n希望此時此刻看到消息的你 \n快去喝一杯水\n成為一個一天八杯水的男人吧!"
]
self.userID = self.getUserID()
def getTimeStamp(self):
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
return timestamp
def getUserID(self):
params = {
"ak" : self.AK,
"timestamp" : self.getTimeStamp(),
"memobirdID" : self.MEMOBIRD_ID,
"useridentifying" : self.USER_IDENTIFYING
}
try:
r = requests.get(self.BIND_DEVICE_API, params=params)
return r.json()["showapi_userid"]
except Exception as e:
print(e)
return ""
def printText(self, text, userId):
printContent = "T:" + base64.b64encode(text.encode("gbk")).decode()
params = {
"ak" : self.AK,
"timestamp" : self.getTimeStamp(),
"memobirdID" : self.MEMOBIRD_ID,
"printcontent" : printContent,
"userID" : self.userID,
}
r = requests.post(self.PRINT_PAPER_API, data=params)
print(r.text)
def drink_reminder(self):
printText = self.getTimeStamp() + "\n" + random.choice(self.DRINK_REMINDER_TEXT)
self.printText(printText, self.userID)
這裡封裝了一個
Memobird
類,提供了一個
drink_reminder
函數,用來列印
喝水提醒
的文字。
我們隻需要在樹莓派中,接收到 APP 下發的
喝水提醒
指令時,調用
drink_reminder
函數即可。
相應的,我們将
demo.py
改造一下:
#!/usr/bin/env python
import time
from tuyalinksdk.client import TuyaClient
from tuyalinksdk.console_qrcode import qrcode_generate
from memobird import Memobird
# 這裡更換成自己的ID
PID = "jj9hhmjxsf8u2b94"
UUID = "tuyaf5753924f3d94306"
AUTHKEY = "5TbgqpGXzbgrr8Plb8L93rBy38Liu7ok"
DRINK_DP_ID = "101"
memobird = Memobird()
client = TuyaClient(productid = PID, uuid = UUID, authkey = AUTHKEY)
def on_connected():
print('Connected.')
def on_qrcode(url):
qrcode_generate(url)
def on_reset(data):
print('Reset:', data)
def on_dps(dps):
print('DataPoints:', dps)
client.push_dps(dps)
for key in dps:
if key == DRINK_DP_ID and dps[key] == True:
# TODO: 喝水提醒
memobird.drink_reminder()
client.on_connected = on_connected
client.on_qrcode = on_qrcode
client.on_reset = on_reset
client.on_dps = on_dps
client.connect()
client.loop_start()
while True:
time.sleep(1)
重新啟動代碼,就到了最激動人心的時候了。
點選
喝水提醒
按鈕,咕咕機成功列印出了提醒喝水的文字!!!
至此,通過 Tuya 的 Link SDK,我們借助樹莓派作為網關,将咕咕機成功接入了塗鴉生态之中。
3.4 智能關聯
接下來,我們隻需要在塗鴉智能 APP 中建立智能場景,設定執行條件和執行任務,便可以實作簡單的自動化,以及裝置關聯啦。
3.4.1 喝水提醒小助手
根據我們自己的作息時間,我們可以設定定時喝水提醒。
如:每天定時下午 2 點 10 分提醒喝水。
設定好以後,儲存并啟用,系統将會在每天下午 2 點 10 分列印紙條,提醒我們喝水。
3.4.2 久坐提醒小助手
如果說,喝水提醒小助手,隻是相當于設定了一個鬧鐘,感覺用處沒那麼明顯的話。那麼我們接入其他各種傳感器裝置,進行裝置間的關聯,可以做的事情就很厲害了。
接下來,我們将與人體傳感器關聯,在
喝水提醒小助手
的基礎上,實作一個
久坐提醒小助手
。
首先我們需要在塗鴉 IoT 平台,繼續添加一條自定義功能
久坐提醒
,如圖所示
然後,在
memobird.py
中設定好
久坐提醒
的文案,并提供一個
久坐提醒
的函數接口。
self.SEDENTARY_REMINDER_TEXT = [
"機靈鶴,你好\n我是“久坐提醒小助手”\n檢測到您已經連續工作很久了\n久坐對身體危害很大\n快起來活動一下吧"
]
def sedentary_reminder(self):
printText(self.getTimeStamp() + "\n" + random.choice(self.SEDENTARY_REMINDER_TEXT), self.userID)
最後,在
demo.py
中添加一條
久坐提醒
的消息定義,并完善該消息對應的處理函數。
DRINK_DP_ID = "101"
SEDENTARY_DP_ID = "102"
def on_dps(dps):
print('DataPoints:', dps)
client.push_dps(dps)
for key in dps:
if key == DRINK_DP_ID and dps[key] == True:
# TODO: 喝水提醒
memobird.drink_reminder()
if key == WELCOME_DP_ID and dps[key] == True:
# TODO: 久坐提醒
memobird.welcome_reminder()
儲存代碼,重新運作啟動程式。
手機 APP 端,連接配接無線網關及人體傳感器。
在傳感器頁面中,設定智能關聯:檢測到有人移動,持續 30 分鐘時,觸發久坐提醒。
然後将傳感器固定到桌子旁邊,正對座位的位置即可。
在連續坐着工作半個小時後,咕咕機成功列印出久坐提醒的紙條。
最後,隻需要将久坐提醒和喝水提醒的文案修改一下,就可以送給女朋友啦!
我想女朋友收到禮物一定會感動哭了吧!
4. 寫在後面的話
整個項目從構思到開發完成曆時 9 天,期間踩了很多坑,繞了很多彎,不過好在最後成功完成,也收獲了很多。同時借着這個機會,也算是入了 物聯網/智能家居 的門。
在項目開發初期,我對物聯網的這套邏輯其實是比較模糊的,我不清楚文檔裡每一個步驟的作用,不清楚裝置之間通信的原理,我甚至搞不清每一個硬體裝置在這套系統裡需要扮演的角色。比如我會常常試圖将樹莓派作為系統的控制核心,用它來監聽傳感器狀态,用它來控制咕咕機,以及用它來處理智能關聯的邏輯。然而事實上,樹莓派隻需要完成一個任務——監聽和處理塗鴉 APP 下發的指令就可以了,這也正是塗鴉 Link SDK 的核心能力。其他部分如監聽傳感器狀态,裝置間智能關聯等等,都可以在 APP 中完成。
在逐漸捋清楚整套邏輯之後,開發過程逐漸順暢。我發現,在解決了裝置接入和裝置關聯問題之後,物聯網比拼的其實就是誰腦洞開的更大了,看誰能用一些奇怪的裝置組合,通過一些莫名其妙的規則發生關聯,最後産生奇妙的效果,給人們的生活帶來出乎意料的友善。