身邊朋友在裝修新房,順便來吐槽甲醛檢測麻煩,比如 有檢測無監(jiān)測(一次性),比如測試復雜(現(xiàn)場+人工)等等。剛好做為云產(chǎn)品經(jīng)理,經(jīng)常想的就是如何了解和驗證多云產(chǎn)品組合方案的可用性問題,索性結(jié)合一下,直接把這個需求上云,然后就有了這套系統(tǒng)。老規(guī)矩,先上結(jié)論。
系統(tǒng)概要
產(chǎn)品設(shè)計特性
- 原型驗證:驗證產(chǎn)品聯(lián)動效果與開發(fā)成本。
- 軟硬結(jié)合:擴展云端能力至線下物理環(huán)境。
- 能力擴展:serverless分離云端與終端,獨立擴展云監(jiān)控、消息隊列等其它產(chǎn)品。
- 通用架構(gòu):云端使用通用數(shù)據(jù)結(jié)構(gòu)及接口,免開發(fā)即對接各種監(jiān)測數(shù)據(jù)上報。
- 弱環(huán)境要求:可PoE供電,5V/0.5A低功耗可長期運行。
系統(tǒng)組成
終端:終端組件
- RaspberryPi 3B+(raspbian-stretch-lite/GPIO接口/python2.7)
- UART-CH2O傳感器(UART接口)
- 128X32 OLED屏 SSD1306芯片(I2C接口)
注:RaspberryPi后續(xù)簡寫為Rpi
云端:騰訊云產(chǎn)品
效果展示
騰訊云圖

終端組件

當前版本中,Rpi 使用wifi 連接互聯(lián)網(wǎng)(也可使用有線網(wǎng)),故此處上云有網(wǎng)絡(luò)依賴。
傳感器原理與功能定位
UART-CH2O傳感器
- 原理:電化學傳感器。通過與被測氣體發(fā)生反應(yīng),并產(chǎn)生電信號來工作。
- 優(yōu)點:簡單易操作。
- 缺點:非定量分析法,受溫濕度、其它氣體干擾準確度,且需要較準。
- 結(jié)論:以長期使用后的房間環(huán)境做為基準,進行0基準點參考。用于溫濕度差異不大的環(huán)境下,提供實時監(jiān)測(相對值),并附加長期趨勢分析。
設(shè)計與實現(xiàn)
產(chǎn)品設(shè)計與技術(shù)策略
設(shè)計過程中,也進行了產(chǎn)品生命周期的思考,嘗試進行了產(chǎn)品長短期設(shè)計的分析與定義(暫不展開,后續(xù)有時間寫一下)。
確認了形態(tài)目標后,遵循下面幾個基本原則,進行具體技術(shù)設(shè)計:
- 分級可用:避免單環(huán)故障,系統(tǒng)全面崩潰
- 讀寫分離:便于后續(xù)調(diào)試、優(yōu)化、更新版本
- 遠程維護:避免出現(xiàn)場處理異常
概要架構(gòu)圖

從架構(gòu)看來
- 云端部分:由于云產(chǎn)品的能力提供了各種便利,學習和搭建成本很低。
- 終端部分:需要多考慮免維護與自動恢復,各項工作內(nèi)容稍多一些。
硬件接線與打開系統(tǒng)接口
Rpi GPIO
GPIO
(General-purpose input/output)即通用IO接口,是一種常見的端口擴展器,樹莓派使用的是40針的GPIO接口。
RPI GPIO圖示

UART-CH2O
接線方式(UART協(xié)議)
| 傳感器 | Rpi |
|---|---|
| 5V | Pin2(5V) |
| GND | Pin6(GND) |
| UART-TxD | Pin10(UART-RxD) |
注:本次使用傳感器,硬件接口是1.25mm端子,Rpi是2.5mm端子,使用了 7P1.25轉(zhuǎn)2.5杜邦線,進行連接
硬件參數(shù)
本次使用的傳感器是UART協(xié)議,從國外的DART傳感器到國內(nèi)產(chǎn)品,均支持此協(xié)議。
由于后續(xù)代碼與接口有關(guān),此處列一下我使用的傳感器的數(shù)據(jù)格式.默認每秒主動上報。
| Byte0 | Byte1 | Byte2 | Byte3 | Byte4 | Byte5 | Byte6 | Byte7 | Byte8 |
|---|---|---|---|---|---|---|---|---|
| 起始位 | 氣體名稱 | 單位 | 小數(shù)位數(shù) | 氣體濃度-高位 | 氣體濃度-低位 | 滿量程高位 | 滿量程低位 | 較驗值 |
| 0xFF | 0x17 | 0x04 | 0x00 | 0x00 | 0x25 | 0x13 | 0x88 | 0x25 |
OLED
接線方式(I2C協(xié)議)
| OLED | Rpi |
|---|---|
| VCC | Pin1(3.3V) |
| SDA | Pin3(SDA) |
| SCL | Pin5(SCL) |
| GND | Pin9(GND) |
打開I2C接口
raspi-config
按下圖打開I2C接口



測試執(zhí)行
i2cdetect -y 1

看到 3C 即識別硬件成功
終端開發(fā)與配置
本文暫僅放出關(guān)鍵代碼(硬件操作部分),便于大家擼硬件。
完整包(代碼+配置) 稍后放出,請關(guān)注 github/DemoOnTencentCloud。
環(huán)境配置
- 啟動對時:rc.local 增加 nptdate cn.ntp.org.cn。避免重啟后時間錯位,監(jiān)測錯位。
- 啟動拉起:getdata.py oled.py 需持續(xù)在線。
- 定時檢測:getdata.py oled.py cron每分鐘判斷活性,進程掛掉即拉起。
- 定時同步:sync.py cron每分鐘執(zhí)行
- 遠程維護:使用ssh tunnel 的 Remote Port Forwarding 模式,進行反向代理。
ssh tunnel
此處使用 autossh 進行連接,autossh可完成建立通道與監(jiān)控通道的工作,通道斷開后,可自主重連。遠端連接 騰訊云服務(wù)器,之后可以云服務(wù)器為跳板,反向代理訪問NAT環(huán)境Rpi設(shè)備。
autossh -M 監(jiān)控端口 -R 遠程通信端口:localhost:22 賬號名@遠程IP或域名 -p端口號 -i 賬號KEY -o serveraliveinterval=60 -N -f
連接時,在云服務(wù)器執(zhí)行
ssh -p 遠程通信端口 localhost
getdata.py
獲取傳感器讀數(shù)代碼(完整代碼待放出 github/DemoOnTencentCloud)
import serial
from time import sleep
ser=serial.Serial("/dev/serial0",9600)
while True :
r_data = ser.read()
sleep(0.3)
data_left = ser.inWaiting()
r_data += ser.read(data_left)
if 9 != len(r_data):
print 'error length: %d'%len(r_data)
continue
else:
n=ord(r_data[4])*256+ord(r_data[5])
updatedata(n/1000.0) # ppm = n/1000.0
flusholed.py
依賴庫安裝(基于 https://github.com/adafruit/Adafruit_Python_SSD1306)
sudo python -m pip install --upgrade pip setuptools wheel
sudo pip install Adafruit-SSD1306
下為功能偽代碼。(完整代碼待放出 github/DemoOnTencentCloud)
# 讀取 cachefile 緩存文件
# 刷新 OLED 顯示
sync.py
下為功能偽代碼。(完整代碼待放出 github/DemoOnTencentCloud)
# 訪問 APIGW,獲取最新記錄時間戳
# 讀本地sqlite庫,獲取增量數(shù)據(jù)
# 訪問 APIGW,提交更新數(shù)據(jù)
Sqlite結(jié)構(gòu)
表結(jié)構(gòu)
CREATE TABLE "sensordata" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"utime" INTEGER NOT NULL,
"utype" INTEGER DEFAULT 0 NOT NULL,
"udata" REAL NOT NULL,
"sdata" TEXT
);
| 字段 | 說明 |
|---|---|
| id | 自增主鍵 |
| stime | 監(jiān)測時間戳 |
| udata | 監(jiān)測數(shù)據(jù) |
| utype | (預(yù)留字段,擴展類別) |
| sdata | (預(yù)留字段,擴展數(shù)據(jù)類型) |
云端開發(fā)與配置
無服務(wù)器云函數(shù)
優(yōu)先配置 無服務(wù)器云函數(shù),參考 文檔 建立并保存“函數(shù)代碼”后,在管理頁面的“觸發(fā)方式”功能中,直接生成對應(yīng)API網(wǎng)關(guān)。

(完整代碼待放出 github/DemoOnTencentCloud)。
當前主要強調(diào)幾個注意事項:
- 自動提交:連接數(shù)據(jù)庫必須使用“autocommit = True,”參數(shù),否則由于事務(wù)隔離,DB鏈接重新連接前,查詢結(jié)果不變。(查不到新增記錄ID)
- 返回頭:API網(wǎng)關(guān)開啟“響應(yīng)集成”時,云函數(shù)返回值需結(jié)合返回信息,指定"Content-Type",否則出現(xiàn) "transfer closed with outstanding read data remaining" 錯誤。
- 驗證連接:云函數(shù)實例可長期存在,但一定時間未操作mysql鏈接時,mysql將釋放鏈接,所以代碼中需要進行驗證鏈路可用性。
API網(wǎng)關(guān)
配置服務(wù)
參考 文檔 ,以上文“觸發(fā)方式”中建立的API網(wǎng)關(guān)服務(wù),由API網(wǎng)關(guān)的 服務(wù) 頁面,點擊相應(yīng)服務(wù)名,選擇“API管理”分頁,點擊“編輯”,然后配置“請求方法-POST”、“鑒權(quán)類型-密鑰對”、“使用響應(yīng)集成”,其它余配置按默認即可。
下載與使用SDK
API網(wǎng)關(guān) 控制臺 -> 點擊 服務(wù)名 -> 點擊 API文檔/SDK -> 點擊 下載SDK
(完整配置待放出 github/DemoOnTencentCloud)
云數(shù)據(jù)庫 Mysql
表結(jié)構(gòu)
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET AUTOCOMMIT = 0;
START TRANSACTION;
SET time_zone = "+00:00";
CREATE TABLE `sensordata` (
`id` int(11) NOT NULL,
`stime` timestamp NULL DEFAULT NULL,
`utype` int(11) NOT NULL DEFAULT '0',
`udata` float NOT NULL,
`sdata` varchar(256) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `sensordata`
ADD PRIMARY KEY (`id`);
ALTER TABLE `sensordata`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
| 字段 | 說明 |
|---|---|
| id | 自增主鍵 |
| stime | 同sqlite utime |
| udata | 同sqlite udata |
| utype | 同sqlite utype |
| sdata | 同sqlite sdata |
云圖配置
簡要使用說明
拖選組件 -> 點擊數(shù)據(jù)欄 -> 選擇數(shù)據(jù)庫 -> 填寫SQL -> 開啟自動更新 -> 預(yù)覽 -> 發(fā)布
操作示例圖

組件配置信息
- 最新同步時間 - 通用文本
select concat('最新同步時間 ',stime) as value from sensordata order by id desc limit 1
- 國標系數(shù)比 - 水位圖
select round((udata)/0.08*100, 2) as value from sensordata order by id desc limit 1
- 實時讀數(shù) - 基本條形圖
select round(udata, 3) as x, '' as y from sensordata order by id desc limit 1
- 10分鐘數(shù)據(jù) - 基本折線圖
select * from (select id, round(udata, 3) as y, date_format(stime, '%H:%i:%S') as x, utype as s from sensordata order by id desc limit 360) as t1 order by id asc
- 7天數(shù)據(jù) - 基本折線圖
select distinct (dt), round(AVG(udata),3) as y, dt as x, '0' as s from (select id, date_format(stime, '%Y-%m-%d %H') as dt, udata from sensordata order by id desc limit 604800) as t1 group by dt order by dt ASC
寫在最后
關(guān)于丟數(shù)據(jù)(非守護進程)、臟數(shù)據(jù)(未較驗數(shù)據(jù)唯一性)、缺乏系統(tǒng)監(jiān)控告警(未接入云監(jiān)控)等等待優(yōu)化點,由于時間關(guān)系暫未展開,后面可以再行探討。