作者:阿海
搭給厚!哇希肉多多A阿海(台語)。最近常常有 LASS 的社群夥伴問我:「我可以在自己的PM2.5偵測裝置上,加上LCD ,或者控制空氣清淨機嗎?」相信各位專業的 Maker 一定可以馬上異口同聲『有何不可!』,沒錯!有何不可呢?
只是情境上,LASS 的PM2.5偵測裝置的設置,應該是在屋頂或者是窗外,在窗外的裝置裝上一個螢幕顯示,並且還有智慧插座,要觀看數值還要跑到戶外,這樣的情境,阿海怎麼都想不通啊!阿海心中,怎麼都想放一個獨立的抬頭顯示器在室內,並且讓它默默的工作才對啊!
大多數的物聯網開發版範例,很多都是將自己的資料,上傳到一個資料平台來閱覽,或者只是透過手機操作平台,來遠端操作裝置,而這樣和 MQTT 當初提供點對點相連的概念,可能稍微有點差距。如果我們能夠在屋頂裝設感測器,在屋內放一個能夠顯示、又可以連動控制的面板,不就能解決這個問題了嗎?
剛好轟動 Maker林、驚動 Arduino板海市場的 LinkIt Smart 7688 Duo 剛上市不久,不如阿海就繼續透過這片好棒棒的開發板,結合128 x 64的 OLED,來實做一個全新的 LASS M2M節點裝置吧!以下,阿海詳述了設計和製作的過程,分享如何一步一步的完成所需要的功能。
必備物品
- Mediatek LinkItSmart 7688 Duo x1
- SSD1306相容的128 x 64 I2C OLED模組 x1
選配物品(控制空氣清淨機)
- 5V Relay 模組 x1
- 電力配線用一連明盒附插座 x1
- 有插頭的多芯電線 x1
設計步驟一:分析系統架構
想要了解LASS的架構,必定要先了解MQTT,如果要把MQTT用一個簡單的說法來解釋,就是把它想成無線電台,想要跟對方互相通訊,就要轉到正確的頻道,當大家的頻道都相同時,別人廣播什麼你就聽得見了(就像收音機一般)。
而在MQTT裡面,頻道的概念就叫做「Topic」,所以想要接收來自另外一端的消息,我們就得先去訂閱你想知道的Topic,目前所有的PM2.5公開測試,都定義在「LASS/TEST/PM2.5」這一個Topic上,我得知原先哈爸是有規劃Private Topic的欄位的,但是因為陰錯陽差在Field Try釋出的版本並沒能帶上,因此想要找到自身站台的訊息,就勢必要從所有站點的訊息海裡面「撈」出來了。
設計步驟二:取得,並測試線上正在運作的各種資料
要開發和測試 MQTT,Chrome 上有一套很簡單的 App 叫做 MQTTLens,透過這個工具你可以輕鬆的在 MQTT上的頻道訂閱/發佈 訊息,來看看如何安裝並且啟動它吧!
接下來就要連上 LASS 的了,在啟動畫面後左上角 Connection 右邊,有一個「+」號,點擊後會帶出新增連線的畫面。
LASS的MQTT伺服器位址是 「gpssensor.ddns.net」,服務Port一樣是 「1883」,填寫完後點擊右下角的「Create Connection」。
填寫範例如下:
填寫完畢來訂閱主題,測試看看通訊會不會成功吧!請在對話框內輸入訂閱 Topic「LASS/Test/+」 ,然後按下「Subscribe」,如果連線成功就會如下圖這樣,畫面會顯示站台上的訊息,陸陸續續湧進。
觀察此頻道的固定格式如下:
|ver_format=3|fmt_opt=0|app=PM25|ver_app=0.7.13|device_id=FT1_004|tick=260989326|date=2016-01-21|time=06:27:23|device=LinkItONE|s_0=67852.00|s_1=100.00|s_2=1.00|s_3=0.00|s_4=662.00|s_d0=66.00|s_t0=21.40|s_h0=72.50|s_d1=81.00|gps_lat=23.284105|gps_lon=120.275816|gps_fix=1|gps_num=16|gps_alt=5
其中 ,「Device_ID 是站台編號」、「s_d0 就是PM2.5的資料欄位」、「s_d1是PM10的資料」「s_t0 為溫度的資料」、「s_h0 則是溼度的資料」,以及日期和時間,這是驗證裝置上線情形和觀察原始資料最直接的辦法了!
為了開發時方便,等等先不要訂閱主頻道,因為這樣會面對海量的訊息,所以我們來創建一個新的頻道叫「DeveloperTest」,仿照上面的格式來測試發佈訊息,並且按下「Subscribe」訂閱這個頻道後按下「Publish」發佈一段訊息,確認伺服器收送正常工作。
設計步驟三:分析開發版的資源並規劃實作方式
一說到 Linkti Smart 7688 Duo,就來複習一下它的架構:它是由兩個主要單元組成,上半部能網路連線的通訊端為 OpenWRT 系統,於500Mhz 的 MT7628 MPU 上運行,第二是 Arduino 相容的 ATMEGA32U4 在 8Mhz 的速度運行。
直觀上我們會選擇在 OpenWRT 端訂閱 MQTT,並且在海量的訊息中,找到想要特定站台傳來的資料處理好,再透過 UART 傳送到 Arduino 端,Arduino 收到 UART(Serial1) 的資料後,對這些資料進行處理,並進行相對應的控制,最後顯示在 OLED 上。
設計步驟四:於Linux端取得MQTT資料分封傳送
LASS的Github上,已經有陳伶志博士提供:MQTT資料轉送到Thingspeak的程式,只要將它進行稍微的修改就可以達到本次需要的目標了,開源專案的最大好處就是如此,站在巨人的肩膀上,輕輕一跳大家就可以到更高的地方!
為了要使用 Serial Library 和 MQTT 這兩個 library,在設備上要安裝 pyserial 和 paho-mqtt 這兩個套件,我們連線進 7688 Duo 下以下兩個指令:你可以使用 PuTTy 連線 mylinkit.local 這個位址,連線成功之後,操作安裝指令。
pip install pyserial paho-mqtt
接下來,將 lass-pm25-friend.py 這隻程式放到 7688 Duo 上,Windows上可以用SCP這套軟體來傳輸。
在Linux-like系統上,直接在Terminal 終端機程式。
scp lass-pm25-friend.py root@mylinkit.local:/root/
阿海將這隻程式修改,讓收到訂閱的MQTT Message程式部分,再加幾行代碼,使收到的資料「原封不動」,只加分行符號「’\n’」轉送下去。也許有人會疑惑,為什麼要這樣設計呢?
明明都已經正確的解析出資料了,卻不傳送已經解析過的資料,是的,這是因為:Arduino 上有許多板子可以直接支援MQTT的(訂閱/傳送),為了未來移植到其他版子方便,阿海就將字串解析的工作交給 MCU 了。(詳細的程式碼請見 Github 上的原始碼會比較清楚)
python 程式內部有一些相關的參數可以調整,說明如下:
################################################ # Please configure the following settings for your environment MQTT_SERVER = "gpssensor.ddns.net" #伺服器位址 MQTT_PORT = 1883 #伺服器連接阜 MQTT_ALIVE = 90 #連接Timeout的時間 MQTT_TOPIC = "LASS/Test/#" #訂閱的主題開頭 '#'代表全部的任意字串 SERIALPORT="/dev/ttyS0" #所使用的Serial port名稱 BUADRATE=57600 #LinkitSmart 7688 Duo Serial1 端的 buadrate ###############################################
接下來要執行這隻程式,指令如下:
python lass-pm25-friend.py [你的站號] [你的ThingspeakAPI key] [模式]
其中模式 0 是只使用thingspeak(原本的功能)、1是只使用Serial、2是兩種功能都使用。
執行後的畫面如下:
Trying To Connect:gpssensor.ddns.net
MQTT Connected with result code 0
Got your MQTT channel
LASS/Test/PM25|ver_format=3|fmt_opt=0|app=PM25|ver_app=0.7.13|device_id=FT1_004|tick=378198422|date=2016-01-22|time=15:01:45|device=LinkItONE|s_0=69813.00|s_1=100.00|s_2=1.00|s_3=0.00|s_4=692.00|s_d0=27.00|s_t0=20.10|s_h0=75.30|s_d1=28.00|gps_lat=23.284026|gps_lon=120.275832|gps_fix=1|gps_num=16|gps_alt=6
為了讓每次通電開機時,都能夠自動啟動這一隻程式,先將啟動語法複製好設定在 /etc/rc.local/ 之下,你必須先輸入指令:
chmod 777 /etc/rc.local
vim /etc/rc.local
並在VIM中編輯如下:
python /root/lass_pm25_friend.py [FT1_ID] [APIKEY] 2 >lasslog.out &
設計步驟五:於 Arduino 端接收 MQTT 資料字串,並取得想要的數值
完成上一個步驟之後,接下來要對 Serial 1進行測試,確保資料在傳送的過程中不會因為電路的誤差而「糟精」(台語:偏差)的疑慮,這種時候,阿海都會直接打開 Arduino IDE 中的範例 「MultiSrerialMEGA」,這個好用的工具範例,修改兩端到要使用的 Buadrate,隨即測試接收資料看看。
當確認傳來的資料是相同的之後,就開始著手撰寫通訊程式,Arduino中,剛好能夠利用String 物件輕鬆的操作字串,而MQTT訊息串並不是太大,於是就將所有的資料流存,放到字串裡面。
得到字串之後,就來設計解析字串程式,這邊要取得的有「日期」、「時間」、「溫度」、「溼度」、「PM2.5」、「PM10」這六項資料。其中前兩項並不是數字,所以撰寫一段額外的程序,來分別放置在字串變數,和浮點數陣列之中。而解析時只要找到tag的前置名稱加上「=」號的開頭位置,以及結尾的「|」符號,就能夠取出這段資料。
首先宣告想要截取的特徵前導字元:
#include <avr/pgmspace.h> const char data_datep[] PROGMEM = "date"; const char data_timep[] PROGMEM = "time"; const char interestValue_1[] PROGMEM = "s_d0"; const char interestValue_2[] PROGMEM = "s_t0"; const char interestValue_3[] PROGMEM = "s_h0"; const char interestValue_4[] PROGMEM = "s_d1"; const char* const interestValue[] PROGMEM = {data_datep, data_timep , interestValue_1, interestValue_2, interestValue_3, interestValue_4, };
然後尋找特徵碼的開頭,並使用 String.indexOf 定位,將所需要的部分轉換成數值,存放在浮點數陣列之中。
float sensorValue[4]; char buffer[8]; boolean onMessage(){ Serial.println("OnMessage"); for(int i=0;i<=data_num-1;i++){ #ifdef BOARD_AVR strcpy_P(buffer, (char*)pgm_read_word(&(interestValue[i]))); #else strcpy(buffer,interestValue[i]); #endif byte startpos=msg.indexOf(buffer); String temp =msg.substring(msg.indexOf(assigner,startpos)+1,msg.indexOf(seperator,startpos)); //From tagname after'=' to seperator'|' if(temp==""){ return 0; //if failed to match tags.... } if(i==0){ data_date = temp; } else if(i==1){ data_time = temp; } else { sensorValue[i-2]=temp.toFloat(); } Serial.println(temp); } return 1; // I count 2440 message/second with static process , so maybe perfomence is about 100~1000 message/s. }
將這些資料印在Serial上,確認解析字串成功之後,就要準備來顯示在 128 x 64 的繪圖型 OLED 上了。
設計步驟六:打造Arduino端 OLED顯示單元
在寫文章的第一天,阿海就把 Adafruit 買來的1.3吋 OLED 打破了,於是阿海只能屈就一片 Library 相容的0.96寸模組了,這片 SSD1306 OLED 模組在 Library 內有兩種顯示模式:一種是引用內建的字型,直接給字串顯示他的做法類似 Print,而另外一種是直接讀取一段單色的byte array bm p圖檔,會一直變動的數字,必須使用字型來顯示比較省事。
而中文的部分,因為我預定只顯示幾個特定的字,因此不如直接把「字」畫成「圖」吧,有一種大家的電腦都有內建的:神人級繪圖修圖軟體「小畫家」(PTT Mobile01的人這樣稱呼的),是產生單色點陣圖最方便的工具了!在 SSD1306 裡面,每一段 Byte Array 圖檔都有寬度8的倍數的限制,一般顯示中文的點陣字體是「16 x 15」,因此要顯示「溫度」這兩個字,我們大概需要32 x 16像素的畫框。
在小畫家裡面設定好畫框,輸入中文字:
接下來我們要將圖檔轉成ByteArray,網路上有一個免費的軟體叫「LCD Assistant」,是Adafruit推薦使用的,既然有人推,阿海就用用看,將剛剛畫好的圖檔導入,就可以產生一個內含Byte Array的檔案了,我將這個檔案另外放置在 Bitmap.c 裡面,放在相同的專案資料夾,讓IDE能夠識別,其他的文字和圖案,都是這樣如法炮製來產生圖檔,你就能在OLED上顯示這些字了,不過遺憾的是,繪圖的定位和文字模式會有一點差別,這時候只能多嘗試幾次,很快就可以對齊了。
使用Adafruit Library 顯示一段文字的程式(Arduino)
在程式燒錄進去之前,先將OLED組裝上去,他是使用I2C介面的,請將模組上SDA、SCL分別接到 7688 Duo上的 D2、D3兩腳,VCC接上3V3,GND接上GND。如使用Relay模組,GND再將線路T接出來,Relay版上的VCC再接上5V。
設計步驟七:打造Arduino端 空氣清淨機智慧插座控制單元
最後來撰寫一段簡單的控制程式,實現在空氣污染太高的時候,能夠自動啟動的空氣清淨機,一般在工程現場,最常見的控制器都是「窗型控制」的,所謂窗型控制,就是數值高到某一個界線,就會啟動機器,而要一直到啟動後產生效用,到達另一個比啟動值較低的界限,才會關閉,這樣的控制方式就稱為窗型控制,例如這邊寫入當PM2.5大於50ug/m^3 ,就讓Relay通電來啟動空氣清淨機,而啟動後要到40ug/m^3以後才會停止運作。
此外,為了預防網路連結失效時,機器運轉不停,阿海也讓程式十分鐘沒有數字更新,就得強迫關閉空氣清淨機,同時也為了避免感測器數值跳動幅度過大,也新增一個變數,來設定最短的啟動時間,才不會讓機器頻繁啟動而損壞。
使用Relay來控制的程式碼如下:
if(logichandle){ //Run your logic here;example give you standard windows control if(sensorValue[0] > ONvalue){ digitalWrite(PINOUT,HIGH); //Turn ON lastOnTime =nowtime; } if(sensorValue[0] <OFFvalue && (nowtime - lastOnTime) > LEASTONTIME){ digitalWrite(PINOUT,LOW); //Turn OFF } heartbeat =nowtime; //after done place logic handle 0; logichandle=0; } if((nowtime - heartbeat) >MOSTONTIMME){ digitalWrite(PINOUT,LOW); //Turn OFF Because no connection; }
然後依照大部分智慧插座的教學,進行Relay的連接和插座的組裝。
由於阿海寫完稿,又把插座拿去做別的東西了,我只能放一張它的「紀念照」了:
設計步驟八-執行結果
以上,是本專案所有的設計的思考及實作過程,希望留下這樣的紀錄,對於未來想要開發LASS應用裝置的人,會有所幫助,最後來看看完整的執行結果吧!
(本文原發表於MakerPRO,原文連結)
相關資訊
本文所有程式公開放置在:
https://github.com/LinkItONEDevGroup/LASS/tree/master/Companion-module
加入討論請到LASS社群:
https://www.facebook.com/groups/1607718702812067/
阿海特別感謝兩位社群朋友,於撰寫本文時提供協助:哈爸、陳伶志 博士
作者介紹:關於肉多多
肉多多-多肉植物科技農場為三個在物聯網公司退役的人組成,平時在雲嘉南搞工程拼經濟,下班就種多肉怡情,偶爾參展交朋友,如果你喜歡肉多多分享的文章,就到粉絲專頁點個讚吧,畢竟點讚一點都不會花你錢,還有可能讓阿海多寫幾篇文章跟大家分享喔!