2016年8月31日 星期三

智慧型無人飛行器與 Memristor (憶阻器)

最近發現了 Pinterest 這個令我感到驚豔的創意點子網站, 我馬上註冊帳號進去瞧瞧, 發現它收集了各種創新設計, 有的是雛形, 有的是已經商品化, 點子之多令我堂目結舌. 其中介紹了一個智慧型無人機 :

https://www.pinterest.com/pin/381539399665087029/

點進去連線到泰國 Thaitch 網站的報導 :

Intelligent drones that ‘think’ will be airborne this year

原來有人正準備把人工智慧導入無人機, 目前的無人機大都需要人為操控, 而具有自動飛行功能者是依靠地圖設定之固定飛行路線航行, 如果航行途中遇到其他無人機, 或者地圖無法顯示的電塔, 旗桿或高壓電線, 就可能因意外碰撞而墜毀. 雖然可以配備超音波或小型雷達等偵測器避免空中撞擊, 但在高速飛行中可能無法偵測像高壓電線這麼細長的障礙物.

美國一家叫做 Bio Inspired Technologies 的公司模仿生物大腦的機制, 使用由電阻與短期記憶體構成的 Memrister 研發了人工大腦, 這些組件就像真實大腦的神經軸突那樣互相聯結起來, 讓嵌入型裝置能夠記憶與辨認雲朵, 飛鳥, 無線電塔, 或者是其他的無人機. 導入人工智慧技術後, 不僅可讓無人機具有自主飛行能力, 還能從空中辨認地面上奔跑的是一隻鹿或一隻狼, 並根據辨認結果規劃新的飛行路線. 該公司目前正在進行雛形測試, 預計今年底就能完成.

我對 Memrister 做了一番調查, 根據 Bio Inspired Technologies 公司關於 Memrister 的敘述 :

http://www.bioinspired.net/neuro-bit-memristors.html

"First theorized in 1971 by Leon Chua, a memristor, or "memory resistor", is an analog circuit element designed to exhibit varying resistance in order to "remember" the electrical signals it is experiencing, as well as the signals experienced in the past. This resistive memory allows circuits to emulate biological processes, such as the synapse, through artificial neural networking circuits."

原來 Memristor  (憶阻器) 的理論是 1971 年由 Leon Chua 發明的, 搜尋 Leon Chua 發現此人為祖籍福建的菲律賓華僑蔡少棠, 於菲律賓讀完大學後赴美深造, 取得麻省理工學院碩士與伊利諾大學博士, 現為加州柏克萊分校教授. 原來著名的虎媽蔡美兒 (耶魯大學法學教授, 虎媽的戰歌作者) 就是蔡少棠的長女. 蔡家四姊妹有三個是美國名校博士, 么妹雖為唐氏症, 但也拿了兩座殘奧金牌.

蔡博士認為被動電子元件除了電阻, 電容, 與電感外, 應該還存在一種憶阻器, 它與電阻不同之處在於電源關閉後它還能記住之前通過的電荷量, 其電阻值會隨累積的電量而改變, 可用來模擬生物神經元突觸的運作. 他以電阻, 電容, 電感, 以及憶阻器設計的蔡式電路, 是一個能夠表現出混沌系統行為的最簡單電路, 成為研究混沌理論之實用系統. 除了憶阻器外, 蔡博士還提出細胞式神經網路 (Cellular neural network, CNN), 被譽為非線性電路分析理論之父, 其個人網站參見 :

https://people.eecs.berkeley.edu/~chua/


2016年8月30日 星期二

X-series 鷹眼 M9059 四軸飛行器

最近公司福利社在賣這款價格 2880 元的小四軸, 將手機連接遙控器, 就成為  FPV 了, 看了讓我蠢蠢欲動 ... 因為都快年底了, 我今年福利點數 10000 點還分毫未動哩! 但理智告訴我別衝動, 先記下來比價. 其實我是想自己打造一台, 但是為了觀摩人家怎麼做, 也有可能會買一台來試試看, 因為模仿是我比較拿手的把戲 (天龍八部裡的慕容復是我的偶像) :


但是我到露天一查, 最便宜是 2100 :

# X-series 鷹眼 M9059 四軸飛行器 WIFI 手機連線錄影 拍照 2.4G圖傳,空拍機 $2100

一般也差不多是 2400, 我們福利社真的都比人家貴, 卡好, 根本是吃定我們的福利點數.


2016 年第 33 周記事

本周是小狐狸們的暑假尾聲, 姊姊要升高三, 二哥要升高一, 菁菁要升國二了. 暑假開始時姊姊就在準備學測了, 學科成績考得好繁星上的機會就比較大. 二哥幾乎暑假都在玩遊戲, C 語言沒學完, 他若沒主動問我的話, 我也沒時間教他, 就放手看看他會積極還是繼續玩遊戲 (當然是玩遊戲). 姊姊說他同學的哥哥也是整個暑假有空就在玩電腦遊戲, 我是覺得家裡的網路使用時間似乎要有管制, 但是我又要使用, 實在不好施行.

菁菁上學期結束時帶她去看了兩家補習班, 其中一家是單人指導學費很貴, 一科一個月要八千多, 教學她是可以接受, 但水某覺得太貴還是沒去; 另外一家她說老師很好, 但有些同學會在課堂講髒話, 她不喜歡. 最後決定乾脆自己教算了. 雖然之前姐姐哥哥都曾這麼做, 結果效果不彰最後還是上補習班, 探討原因其實是自己太忙無法持續. 菁菁是老么, 不要再丟給補習班了 (我以前不是最討厭的嗎?), 每天抽點時間幫她複習功課, 總比花了錢去補習班又沒有顯著效果好吧? 一個暑假下來, 數學只上到 2-1, 因為菁菁上了後面忘了前面, 只好反覆複習.

過去這兩周專心研究 Blynk 的用法, 所以鄉下庫房的整理工作停頓下來. 不過我這個人常常興之所至就會突然專攻某件事, 很難說得準甚麼時候又會瘋哪件事. 總之, 順著心情做事效率最高. 這一周終於把 Blynk 研究全部都搞定了, 算是專注期最長的一次了.

本期 (1501) 商周封面報導 TRF (目標可贖回遠期契約) 災情, 據說高達 3700 家中小企業因為錯押人民幣 TRF 導致面臨鉅額財務損失, 兩三年前人民幣正夯時銷售此種衍生性商品的 29 家銀行, 其中 13 家因銷售手法不當遭到金管會處分或糾正. 我感到震驚的是, 當周刊採訪金管會銀行局長時, 局長的反應是 : 風暴都要結束了, 你們還想報導? 然而周刊編輯說 : 他以為的結束, 其實正在開始. 哇! 這到底是哪裡有問題? 如果周刊的看法是對的, 而負責官員卻渾然不知, 那 ... 問題是不是大了? 我同事三年前也是看好人民幣定存, 結果現在帳面虧損哀哀叫, 解約就要面臨匯率損失, 只好走著瞧. 外匯這東西波動大風險高, 更何況是外匯衍生性商品牽涉的複雜數學!


傳說中的鬼島

今天看到這則新聞 :

# 台灣最宜居!10個榮登榜首的原因

原來在外國人眼裡台灣是這麼好, 居然被一些妖怪稱為鬼島.

在獲選的十大因素中, 安全應該是最重要的原因, 因為台灣被 FBI 評為全球僅次於日本的第二安全國家. 物價便宜也是重要因素, 去過日本就知道, 咱們這裡 150 塊台幣一碗的牛肉麵就算很棒了, 在日本吃一碗麵至少要 900 日幣, 折合台幣 300 元以上哩!

至於全民健保, 外國人來台旅遊應該享受不到, 雖然健保財務一直都是個問題, 但可不是每個國家都有呢! 連那些老是對台灣落井下石的人, 有病的時候還是不忘回國來享受一下, 從這裡就知道健保好在哪裡了. 參考 :

美國人其實很「羨慕」台灣健保的許多成就,為什麼?


2016年8月29日 星期一

開學了

漫長的暑假終於結束了, 今天是二哥的鳳中開學, 早上我載他去學校, 校車要明天才能坐. 而姊姊與菁菁則是明天開學, 菁菁今天只是返校而已. 為啥差一天呢? 因為鳳中是國立, 而前鎮與國中都是市立, 所以開學日期不同.

今天花了一整天整理周五的主管會議紀錄, 實在是太無聊了, 最討厭這種沒意義的 paper work, 但是輪到我也沒辦法.

2016年8月27日 星期六

★ Blynk 的其他元件 (Others)

 本周大部分閒暇時間都花在鑽研 Blynk 四大類元件的用法, 結果記錄在下面五篇文章 :

# Blynk 應用 (一) : 用手機控制 Arduino
# Blynk 應用 (二) : 在手機顯示即時溫溼度與光度
# Blynk 的控制元件 (Controllers)
Blynk 的顯示元件 (Displays)
# Blynk 的通知元件 (Notifications)

現在剩下其他類元件了, 補上這一塊後, 對於 Blynk 就會有整體的認識了.

以下測試程式是在我自己用洞洞板焊接的 Arduino Nano+ESP8266 IOT 模組上進行實驗, 電路圖參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇



Blynk 的其他 (Others) 類元件就是無法歸類在 Controls, Displays, Notifications 這三類的元件, 目前有如下五種元件 :


一. 橋接器元件 (Bridge) :  

先來測試 Blynk 最強的 Bridge 元件, 它是最能彰顯物聯網能力的元件, 因為它可以通過 Blynk Cloud 伺服器達成 Machine to machine 的控制. 官網說明文件參考 :

http://docs.blynk.cc/#widgets-other-bridge

簡言之, Bridge 元件是用來在設備與設備之間搭起互相通訊的橋樑, 通訊管道建立之後, 即使手機的 Blynk App 離線也不會影響到兩個設備之間的通訊, 這就是 M2M (Machine to machine) 通訊的一種典型例子. 設備A 只要擁有設備 B 的金鑰, 就能透過 Blynk Cloud 控制設備 B 的任何腳位狀態 (Digital/Analog/Virtual), App 的 Bridge 元件僅僅是在 Blynk Cloud 註冊此功能, 幫忙搭起溝通的橋樑而已, 讓設備 A 可以控制設備 B. 我們可以多個專案中使用 Bridge 元件來進行多重 M2M 控制.

首先在專案中新增 Bridge 元件 :


點一下元件進入設定畫面 :


M2M 的控制事實上是做在設備端的韌體程式裡, 我們須指定一個虛擬腳位做為與另一個設備端溝通的管道, 而 Blynk App 專案中建立 Bridge 元件只是告訴 Blynk Cloud 此專案有用到 Bridge 功能而已, 所以 App 裡此元件沒有需要設定的選項.

只要在專案中添加此 Bridge 元件後, 與此專案相連結的設備 (即擁有此專案金鑰之設備) 即擁有控制其他 Blynk 設備之能力. 接下來便是撰寫設備端韌體程式, 參考官網範例 :

https://github.com/blynkkk/blynk-library/blob/master/examples/Widgets/Bridge/Bridge.ino#L33

此範例有 A, B 兩設備, 設備 A 在 App 中註冊了 Bridge 元件, 用來控制另一個專案所連結的設備 B. 設備 A 首先利用虛擬腳位 V1 建立一個 WidgetBridge 物件 :

WidgetBridge bridge1(V1);

要控制另一個設備必須擁有該設備之金鑰, 並且在連線 Blynk Cloud 成功後呼叫 setAuthToken() 函數來獲得對方設備認可, 這是由 BLYNK_CONNECTED() 函數負責的 :

BLYNK_CONNECTED() {
  bridge1.setAuthToken("AuthTokenOfDeviceB");
  }

接著利用計時器每秒去 (High/Low) 交替更改設備 B 的 D9 與 V5 腳位狀態 (toggle).

在下面的測試中我使用兩塊 Arduino Nano+ESP8266 物聯網模組來當設備 A 與設備 B 以便測試 Bridge 功能. 我使用設備 A 的 V0 腳位當作 Bridge 的管道, 用來透過 Blynk Cloud 遠端控制另一塊模組設備 B. 為了方便起見, 我將 D9 改為 D13, 因為 D13 腳位在 UNO/Nano 板上有一個內建 LED 與其相接, 可以直接看到腳位狀態變化. 另外我把推送給設備 B 的 V5 腳位的數據改成設備 A 程式執行後所經過的秒數, 而不是 0/1 交替, 這樣在設備 B 的 Blynk App 上使用一個數值顯示器就能看到設備 A 程式已執行多少秒了.

為了在設備 B 上顯示設備 A 程式已執行的時間, 我先在 Blynk App 上建立了一個新專案叫做 Device B, 這樣會得到一個專案認證金鑰來代表設備 B :


按 E-mail 鈕將金鑰寄到信箱以便複製到程式裡面. 然後在 Device B 專案裡新增一個 Value Display M 元件來顯示設備 A 的程式執行時間, 此顯示元件綁定虛擬腳位 V4 :


這裡我們使用 Push 方式從設備 B 的虛擬腳位 V4 取得資料來顯示. 注意, 不能直接綁定到 Bridge 所使用的 V5. 原因後述. 設定好後按左上方的返回鍵回到專案面板如下 :


按最右邊的 Play 鍵開始執行設備 B 的 App.

接下來要撰寫設備 B 的韌體程式, 參考下面這篇關於數值顯示 Push 模式的範例 :

# Blynk 的顯示元件 (Displays)

不過這裡不需要使用 SimpleTimer 物件去週期性呼叫自訂函數將資料推送給 App, 而是必須準備一個 BLYNK_WRITE(vPin) 函數, 此函數會在虛擬腳位的值更新時被呼叫, 在此函數中我們須使用 param 的 asInt(), asFloat(), 或 asString() 等函數將字串轉成所要的資料型態, 然後呼叫 Blynk.virtualWrite(vPin) 將資料推送到 App 上顯示元件所綁定的另一個虛擬腳位.

在官網文件中關於 Bridge 的說明有這一段重要的話 :

"Keep in mind that bridge.virtualWrite doesn’t send any value to mobile app. You need to call Blynk.virtualWrite for that."

這就是為什麼我們在上面設定數值顯示器時, 不能直接將其綁定到 Bridge 的 V5 的原因了. 因為 App 上的 WidgetBridge 物件的 virtualWrite() 並不能更新遠端設備所連結 App 上的顯示元件, 必須用 Blynk 物件的 virtualWrite() 才行.

設備 B 的程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object

char ssid[]="H30-L02-webbot";
char pass[]="1234567890";
char auth[]="0155b16357f1432d876cfb3xxxxxxf33";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  }

void loop() {
  Blynk.run();
  }

BLYNK_WRITE(V5) { //called when V5 updated by Device A via Bridge
  int deviceA_uptime=param.asInt(); //store value that came via Bridge
  Blynk.virtualWrite(V4, deviceA_uptime); //display in App
  }

注意, 上面程式中我故意讓設備 B 透過我的手機行動網路基地台連上 Internet, 下面的設備 A 則仍然是透過家中社區網路上網,

我們會在設備 A 呼叫 WidgetBridge 物件的 virtualWrite() 將程式已執行時間推送到設備 B 的虛擬腳位 V5, 這會觸發設備 B 韌體程式的 BLYNK_WRITE() 函數, 我們再從 V5 取出推送過來的值, 用 Blynk.virtualWrite() 推送到 App.

上面搞定設備 B 後, 接下來撰寫設備 A 的程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";  //Device A's auth token

WidgetBridge deviceB(V0);  //use Device A's V0 to create a Bridge object for Device B

static bool value=true;

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(1000L, blynkDeviceB); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void blynkDeviceB() { //Here we will send HIGH or LOW once per second
  long uptime=millis()/1000L; //get seconds after running
  deviceB.virtualWrite(V5, uptime); //update deviceB's V5 with uptime  
  if (value) {deviceB.digitalWrite(13, HIGH);} //update deviceB's D13
  else {deviceB.digitalWrite(13, LOW);}  //update deviceB's D13
  value=!value; //toggle
  }

BLYNK_CONNECTED() { //called when deviceA connect to Blynk Server
  deviceB.setAuthToken("0155b16357f1432d876cfb3yyyyyyf33");
  }


我將 Device A (上), B (下) 兩塊 IOT 模組分別上傳程式後, 用兩個行動電源供電. 上電後約 6 秒兩塊都連上 Internet, 這時圖中下方的設備 B 之 Nano 板上 D13 LED 開始間隔一秒閃爍, 影片如下 :


打開手機執行 Device B 專案, 可見數值顯示器上的數字每一秒往上跳一下, 顯示設備 A 透過 Bridge 推送過來的程式已執行秒數 :


即使將設備 A 的專案停止執行也不會影響設備 B 上的 LED 閃爍, 也不會影響手機上 Device B 專案的設備 A 已執行時間顯示, 甚至關閉手機網路讓 Blynk App 與 Blynk 伺服器離線,  設備 B 的 LED 照常閃爍, 僅 Device B 專案上的秒數顯示暫停不動, 手機再連網時數字就會繼續跟上去. 可見 Bridge 的運作與手機 Blynk App 無關, 它是由 Blynk Cloud 伺服器在操控的.

上面這個簡單範例只是單向的 M2M 控制, 前面提過可以用多個 Bridge 元件達成多重 M2M 控制. 接下來要將上面範例擴展為雙向 M2M 控制, 亦即除了讓設備 A 透過 Bridge 控制設備 B 的 D13 LED 閃爍外, 也同時讓設備 B 控制設備 A 的 D13 LED 閃爍; 而且手機上的 Blynk App 都能看到對方設備的程式已執行時間.

為了讓設備 B 可以控制設備 A 的 LED 閃爍與推送設備 B 程式已執行時間, Device B 專案必須新增一個 Bridge 元件 :


這樣便在 Blynk Cloud 註冊設備 B 有 Bridge 功能了. 當然設備 B 的韌體程式也仿照上面範例的設備 A 程式進行修改, 讓它能控制設備 A 的 D13 LED 閃爍, 並將設備 B 程式已執行時間推送到設備 A 的 App. 設備 B 同樣利用自己的虛擬腳位 V0 與 Blynk Cloud 個 Bridge 功能溝通, 控制設備 A 的 D13 LED.

下面是修改後的設備 B 韌體程式 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="0155b16357f1432d876cfb3xxxxxxf33"; //Device B's auth token

WidgetBridge deviceA(V0);  //use Device B's V0 to create a Bridge object to control Device A

static bool value=true;

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(1000L, blynkDeviceA); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void blynkDeviceA() { //Here we will send HIGH or LOW once per second
  long uptime=millis()/1000L; //get seconds after running
  deviceA.virtualWrite(V5, uptime); //update deviceA's V5 with uptime
  if (value) {deviceA.digitalWrite(13, HIGH);} //update deviceA's D13
  else {deviceA.digitalWrite(13, LOW);}  //update deviceA's D13
  value=!value; //toggle
  }

BLYNK_CONNECTED() { //called when deviceB connect to Blynk Server
  deviceA.setAuthToken("e80fe6aae0d442518820f61xxxxxx8f5"); //Device A's auth token
  }

BLYNK_WRITE(V5) { //called when V5 updated by Device A via Bridge
  int deviceA_uptime=param.asInt(); //store value that came via Bridge
  Blynk.virtualWrite(V4, deviceA_uptime); //display in App
  }

與上一個範例相比, 主要是多了 SimpleTimer 與 BridgetWidget, 因為它也要透過 Bridge 功能去控制設備 A 的 D13 LED, 所以多加了 BLYNK_CONNECTED() 函數, 利用 setAuthToken() 向 Blynk Cloud 送出想要控制的設備之金鑰; 同時它也要把自己的 uptime 推送給設備 A, 所以添加了自訂的 blynkDeviceA(), 讓計時器每秒去呼叫它, 將設備 B 自己的 uptime 寫入設備 A 的虛擬腳位 V5 上, 以及讓設備 A 的 D13 LED 閃爍.

回過頭來看設備 A, 因為我們也要在其 App 上顯示設備 B 推送過來的 uptime, 所以 Device A 專案裡除了原先的 Bridge 元件外還要新增一個 Value Display M 元件, 綁定設備 A 的虛擬腳位 V4 :



這樣設備 A 的 App 就設定好了. 此外, 設備 A 的韌體程式也需要修改, 因為要顯示來自設備 B 的 uptime 資料 (設備 B 已指定寫入設備 A 的 V5), 必須加上特定的 BLYNK_WRITE(V5) 函數, 每當設備 A 的 V5 被設備 B 透過 Bridge 寫入更新時, 這個 BLYNK_WRITE(V5) 函數就會被呼叫, 我們必須在此函數中把從 V5 收到資料推送到數值顯示器所綁定的 V4 腳位, 這樣 Device A 專案上的顯示器才會呈現設備 B 的 uptime.

設備 A 的韌體程式修改如下, 與上一範例相比, 僅加入 BLYNK_WRITE(V5) 函數來處理顯示設備 A 推送過來的 uptime 而已 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5"; //Device A's auth token

WidgetBridge deviceB(V0);  //use Device A's V0 to create a Bridge object to control Device B

static bool value=true;

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(1000L, blynkDeviceB); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void blynkDeviceB() { //Here we will send HIGH or LOW once per second
  long uptime=millis()/1000L; //get seconds after running
  deviceB.virtualWrite(V5, uptime); //update deviceB's V5 with uptime
  if (value) {deviceB.digitalWrite(13, HIGH);} //update deviceB's D13
  else {deviceB.digitalWrite(13, LOW);}  //update deviceB's D13
  value=!value; //toggle
  }

BLYNK_CONNECTED() { //called when deviceA connect to Blynk Server
  deviceB.setAuthToken("0155b16357f1432d876cfb3xxxxxxf33"); //Device B's auth token
  }

BLYNK_WRITE(V5) { //called when V5 updated by Device B via Bridge
  int deviceB_uptime=param.asInt(); //store value that came via Bridge
  Blynk.virtualWrite(V4, deviceB_uptime); //display in App
  }  

程式上傳後兩塊 IOT 模組同時上電後測試結果如預期, 現在兩塊板子的 D13 LED 都會閃爍  (上 : 設備 A, 下 : 設備 B) :


而且兩塊板子的 App 也都會顯示對方的 uptime :



上面範例的專案設計分享如下 :

Device A

Device B

從上面兩個簡單範例可知, 這個 Bridge 元件實在太好用了, 透過 Blynk Cloud 伺服器, 散佈在全球任何角落的兩個設備只要連上互聯網, 他們不僅可以一對一互相監控, 也可以一對多, 多對一, 或者多對多進行多重監控 (multiple control and monitor), 實際應用範例就要靠想像力去發揮了.例如水塔抽水自動控制, 馬達在樓下, 水塔在樓頂, 若使用一個 Arduino 控制的話, 感測器的電線要拉很遠, 要不然就要用藍芽或無線射頻模組. 不如使用兩塊 Arduino Nano+ESP8266 模組, 樓下的那塊負責控制抽水馬達, 樓上的負責偵測水位, 透過 wifi 與 Blynk 的 Bridge 做一對一互相監控, 還可以在全球任何地方作遠端監控. 也可以應用在車庫自動照明, 例如大門偵測到車子回家後, 便透過 Bridge 控制車庫燈打開, 而車庫控制器偵測到車子已停妥, 也透過 Bridge 控制客廳燈開啟等等.

在 Blynk 討論群組發現這篇文章 :

# Why nobody uses bridge?

這是去年 2015 Bridge 功能剛出來時的討論文章, 在最底下發現一個跟我的 IOT 模組很像的模組, 原來早就有人這麼做了. 更多範例參考 :

# Blynk Board Bridge Widget Demo



二. 時鐘元件 (RTC) :

此元件可讓我們取得 Blynk Cloud 伺服器的時間, 透過時區選擇, 伺服器傳回來的就是我們本地時間, 可用來同步硬體設備如 Arduino 的內部時鐘, 亦即不需要大費周章與 NTP 同步了, 透過 Blynk 就能輕而易舉辦到.

在專案中新增一個 RTC 元件如下 :


點一下元件進入設定畫面 :


RTC 元件只能綁定虛擬腳位, 這裡我選定 V2. 底下有一個 Time Zone 選項, 已經自動偵測是台灣時區, 預設即為 +8, 不須更改. 另外我們需要一個 Value Display M 元件以便在 App 上顯示時間, 綁定到虛擬腳位 V3, 使用 PUSH 模式 :


這樣 App 專案的設定就完成了, 裡面有兩個元件 :


韌體程式部分, 使用 RTC 元件需先匯入下列兩個函式庫 :

#include <TimeLib.h>
#include <WidgetRTC.h>

然後建立 WidgetRTC 物件 :

WidgetRTC rtc;

再呼叫特定之 BLYNK_ATTACH_WIDGET() 函數綁定虛擬腳位即可 :

BLYNK_ATTACH_WIDGET(rtc, vPin);

函式庫 TimeLib 位於 Blynk 函式庫安裝目錄 Time 子目錄下, 開啟 Time.cpp 可以找到它所提供的函式, 例如 hour(), minute() 等, 事實上跟以前測試 NTP 時所下載的 Time 函式庫是一樣的, 參考這篇 :

# 利用 NTP 伺服器來同步 Arduino 系統時鐘 (三)

其函式列表如下 :



 函式 說明
 hour() 傳回現在的時 (24 小時制)
 hourFormat12() 傳回現在的時 (12小時制)
 minute() 傳回現在的分
 second() 傳回現在的秒
 year() 傳回現在的年
 month() 傳回現在的月
 day() 傳回現在的日
 weekday() 傳回現在的星期 (星期日為 1)


傳回值均為整數. 實際範例參考官網教學文件:

https://github.com/blynkkk/blynk-library/blob/master/examples/Widgets/RTC/RTC.ino

我參考之前測 NTP 的程式將其改編如下 : 


#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>
#include <TimeLib.h>
#include <WidgetRTC.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="f4d4aa2dc86849adb0971d0zzzzzz371"; //Device A's auth token

WidgetRTC rtc;  //create an RTC object
BLYNK_ATTACH_WIDGET(rtc, V2); //binding RTC to virtual pin

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  rtc.begin();
  timer.setInterval(1000L, showTime); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void showTime() {
  String d=getDate();
  String t=getTime();
  String w=getWeek();
  Serial.println(d + F(" ") + t + F(" ") + w);
  Blynk.virtualWrite(V3, d + F(" ") + t + F(" ") + w); //Send time to App vPin
  }

String getDate() {
  String d=(String)year() + "-";
  byte M=month();
  if (M < 10) {d.concat('0');}
  d.concat(M);
  d.concat('-');
  byte D=day();
  if (D < 10) {d.concat('0');}
  d.concat(D);
  return d;
  }

String getTime() {
  String t="";
  byte h=hour();
  if (h < 10) {t.concat('0');}
  t.concat(h);
  t.concat(':');
  byte m=minute();
  if (m < 10) {t.concat('0');}
  t.concat(m);
  t.concat(':');
  byte s=second();
  if (s < 10) {t.concat('0');}
  t.concat(s);
  return t;
  }

String getWeek() {
  String w[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  return w[weekday()-1];
  }

此程式中我們利用計時器每秒觸發執行自訂的 showTime() 函數, 於其內呼叫 Blynk.virtualWrite() 函數將日期時間字串推送到虛擬腳位 V3 所綁定的顯示器上. 而取得日期, 時間, 以及星期的功能分別由 getDate(), getTime(), 以及 getWeek() 負責, 這樣比較有彈性. 由於 Time 函式庫的 getWeek() 傳回值星期天是 1, 星期六是 7, 故利用陣列轉成英文縮寫字串傳回.


比起以前用 NTP 伺服器, 親自操控緩衝器累得半死, 用 Blynk 的 RTC 元件實在是太輕鬆啦! 當然我們也可以將這個日期時間資訊輸出到實體的 1602 LCD 去, 參考下面這篇的測試 6-1 :


利用 NTP 伺服器來同步 Arduino 系統時鐘 (三)

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>
#include <TimeLib.h>
#include <WidgetRTC.h>
#include <LiquidCrystal.h>
#define RS 2
#define E 3
#define D4 10
#define D5 11
#define D6 12
#define D7 13

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
LiquidCrystal lcd(RS,E,D4,D5,D6,D7);  //create LCD object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="f4d4aa2dc86849adb0971d0zzzzzz371"; //Device A's auth token

WidgetRTC rtc;  //create an RTC object
BLYNK_ATTACH_WIDGET(rtc, V2); //binding RTC to virtual pin

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  rtc.begin();
  timer.setInterval(1000L, showTime); //Don't send more that 10 values per second
  lcd.begin(16,2); //define 2*16 LCD
  lcd.clear(); //clear screen    
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void showTime() {
  String d=getDate();
  String t=getTime();
  String w=getWeek();
  Serial.println(d + F(" ") + t + F(" ") + w);
  lcd.setCursor(0,0); //move to (x,y)
  lcd.print(d); //print date
  lcd.setCursor(11,0); //move to (x,y)
  lcd.print(w); //print date 
  lcd.setCursor(0,1); //move to (x,y)
  lcd.print(t); //print date
  Blynk.virtualWrite(V3, d + F(" ") + t + F(" ") + w); //Send time to the App
  }

String getDate() {
  String d=(String)year() + "-";
  byte M=month();
  if (M < 10) {d.concat('0');}
  d.concat(M);
  d.concat('-');
  byte D=day();
  if (D < 10) {d.concat('0');}
  d.concat(D);
  return d;
  }

String getTime() {
  String t="";
  byte h=hour();
  if (h < 10) {t.concat('0');}
  t.concat(h);
  t.concat(':');
  byte m=minute();
  if (m < 10) {t.concat('0');}
  t.concat(m);
  t.concat(':');
  byte s=second();
  if (s < 10) {t.concat('0');}
  t.concat(s);
  return t;
  }

String getWeek() {
  String w[]={"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
  return w[weekday()-1];

  }

藍色部分就是加進去跟 LCD 液晶模組有關者. 執行結果如下 :



Blynk 的出現實在是幫了開發者一個大忙, 當然也不能說以前的努力是白忙了, 像 NTP 伺服器對時就搞了好久才成功, 但至少經過這些過程總算知道底層的細節實作要怎樣做啦! 此專案分享如下 :



三. 選單元件 (Menu) : 

選單元件就是下拉式選單, 依寬度不同分成兩個 :
  1. Menu M : 寬度為手機螢幕一半
  2. Menu L : 寬度為手機螢幕寬
新增一個 Menu L 元件如下 :


點一下元件進入設定畫面, 不管是 M 還是 L, 其設定畫面都一樣 :


選單元件只能綁定虛擬腳位, 因為其選項值必須經過程式處理. 預設有兩個選項, 每按底下的 +Add item 一次會增加一個, 選項數目可以任意新增, 選項的標題預設是 Item 1, 2, ... 可加以編輯修改, 但其前面中括號內的數字 [1], [2], ... 是固定的選項值無法編輯, 亦即第一項固定傳回 1, 第二項固定傳回 2, ... 例如下面的閃燈選擇選單裡, 我們綁定 V0 腳位, 新增 item3 :


當我們選擇 Menu 元件上的選項時, 其值便透過所綁定的虛擬腳位 V0 傳送給 Blynk 伺服器, 在設備端要接收此選項必須於韌體程式中定義此虛擬腳位的特定 BLYNK_WRITE(vPin) 函數, 這樣的話每當此虛擬腳位被更新時, BLYNK_WRITE() 函數就會被呼叫, 我們就可以在此函數內利用特定之 param 物件處理選項值, 參考官網教學文件範例 :

https://github.com/blynkkk/blynk-library/blob/master/examples/Widgets/Menu/Menu.ino

我將其改編為 ESP8266 版本如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object

char ssid[]="EDIMAX-meinung";
char pass[]="1234567890";
char auth[]="f4d4aa2dc86849adb0971d0zzzzzz371";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  }

void loop() {
  Blynk.run();
  }

BLYNK_WRITE(V0) { //called when vPin updated by server
  switch (param.asInt()) {
    case 1: { //Item 1
      Serial.println("Item 1 selected");
      blinkD13(1);
      break;
      }
    case 2: { //Item 2
      Serial.println("Item 2 selected");
      blinkD13(2);
      break;
      }
    case 3: { //Item 3
      Serial.println("Item 3 selected");
      blinkD13(3);
      break;
      }
    default: {
      Serial.println("Unknown item selected");
      }
    }
  }

void blinkD13(int count) { //blink D13 LED
  for (int i=0; i<count; i++) {
    digitalWrite(13, HIGH);
    delay(500);
    digitalWrite(13, LOW);
    delay(500);  
    }
  }

上面程式中我新增了一個自訂函數 blinkD13(), 傳入欲閃爍的次數, 則 Arduino 板子上的 D13 LED 便會閃爍指定之次數. 當設備的的虛擬腳位 V0 被伺服器寫入時 (即選項被改變時), BLYNK_WRITE() 函數便會被呼叫, 寫入的值 (即選項值) 會放在 param 物件中傳給 Arduino, 利用 asInt() 將字串轉成整數即可得到選項值, 關於 BLYNK_READ()BLYNK_WRITE() 參考 :

# Blynk 的控制元件 (Controllers)

程式上傳後執行結果如預期, D13 LED 會依選項值閃爍指定次數. 從元件功能來看, 我覺得其實此元件應該放在控制元件類才對. 另外, 使用按鈕元件一個要耗掉 200 單位能量, Menu 元件才耗費 400 單位能量, 所以如果要用到很多 Button 元件的話 (超過兩個), 不如改用 Menu, 因為裡面的選項似乎無限制, 每一個選項相當於一個按鈕. 此專案分享如下 :



四. 頁籤元件 (Tabs) : 

此元件是一個頁面排版容器, 只是用來擴增專案的版面空間而已, 亦即若整個專案用了很多元件, 一個螢幕版面可能擺不下, 必須上下滑動很麻煩, 可以依據功能或操作順序分類, 將這些元件分別擺放到不同的頁籤裡, 切換頁籤即可看到不同的元件, 只要每一個頁籤內的全部元件不會超過螢幕高度, 就不用上下滑動螢幕了.

新增一個 Tabs 元件如下圖, 預設已有兩個頁籤 :


點一下右邊兩個圈圈的鏍母按鈕進入設定畫面 :


可以修改預設的頁籤標題 Tab1, Tab2 為自訂標題, 按 +Add Tab 可以新增頁籤, 此元件最多可容納 4 個頁籤. 回到專案面板點選要加入元件的頁籤, 則所新增的元件就會放在該頁籤內 :


由於 Tabs 只是個容器而已, 所以耗用能量為 0 單位, 不用白不用 (有需要才用啦!).

其他類元件還有一個藍芽元件 (BLE), 目前還在 Beta 階段尚未完備, 官網教學文件也沒有資料, 等正式推出後再來補齊.


好書 : 我死過所以知道怎麼活 : 與死神相遇的 11 分鐘

這本書我在河堤預約了好久才拿到, 作者鍾灼輝是一位香港心理學家, 其人多才多藝, 能開船開飛機, 也是滑雪與潛水教練, 還曾在新加坡國際射擊賽中奪金. 不獨擅長茶藝, 書法, 音樂, 花藝, 也是高級品酒師. 專業上是專攻犯罪心理學的博士, 於 1997 年加入香港警隊, 曾任香港警務處高級督察, 在從事警務的 15 年間處理過上百件意外與自殺死亡事件.


但是這樣精采的人生卻在 2004 年於紐西蘭的一次滑翔機飛行訓練中差點終結, 由於拉動飛機起飛的繩索無法正常脫鉤以及強側風衝擊的影響, 飛機從 110 公尺的高空失速墜落地面, 造成嚴重骨折與全身多處挫傷割傷而失去意識, 在急救員到達之前經歷了11分鐘瀕死的奇異經驗. 所幸腦部與內臟未受到嚴重創傷, 送醫急救後奇蹟生還, 歷經六年輪椅生涯重獲新生. 作者經此大難, 對於人生有了不同以往的見解, 藉著回顧瀕死經驗, 闡述人生該怎麼活.

據云, 當其失去意識的剎那, 首先看到一道明亮的金色光芒, 像是日出時的晨光乍現, 內心感到十分安詳寧靜. 而且身體也同樣發出金色光芒, 雖然殘留著原來的型態, 但卻沒有了身體疆界的感覺. 整個人漂浮在半空中, 彷彿地心引力消失了一樣. 往下看, 地上躺著一架破爛的滑翔機殘骸, 殘破的機艙內是一動也不動的自己, 鮮血從身上大大小小的傷口中湧出, 狀甚恐怖. 這時他明白自己已經死了, 原來死亡的那一刻, 靈魂會完全脫離肉體, 以光的型態存在, 變成兩個同時存在的我. 但奇怪的是, 那脫離肉體感官的靈魂, 同樣看得到, 聽得到, 感受得到外在世界, 也能了解自己內在的思維. 與傳說中的死後世界不同的是, 沒有看到天堂仙境, 也沒有見到駭人的地獄, 只有出奇的寧靜祥和. 然後看到一幕自己還是胚胎的景象, 被溫暖的羊水包圍住在母親子宮中. 原來死亡與出生的感覺很像, 彷彿生命的終點跟起點是重疊連接在一起似的.

這時從光海的深處有一個聲音 (死神?) 問他是要離開還是留下, 並說在還沒決定之前會讓他進入平行時空的時間之流, 進去之後是一個立體的水晶絲網, 這些絲線是時間與因果之流, 將人生不同的經歷交織連接在一起. 沿著絲線遊走會看到人生不同階段的景象 (時光回溯?), 原來人是活在千絲萬縷的因果關係裡. 在這個時間之流裡, 他看到了自己短暫三十年的人生脈絡. 他以為這是終極審判, 但死神的回答是 : "死後的世界並沒有最終審判, 也不存在對錯善惡, 那些是人類的判別心製造出來的, 只存在於二元對立的世界裡. 這裡是萬物合一的大同世界, 萬物只以本質存在著, 無善也無惡, 是對也是錯". 死神說他可以選擇去或留, 他正在疑惑時, 死神說看來你還沒準備回答人生最後的問題, 等準備好再來吧!時間之流驟然消失, 接下來他就感覺到劇烈疼痛, 他奇蹟似地生還了! 怪哉!人在瀕死之際還可以選擇去留喔?

在本書中,作者以其認知心理學與香港警隊死亡事件調查員的專業背景剖析自己的瀕死經驗,並從各種角度來探討生命的目的,靈魂的存在,以及死亡的意義,此書具有相當可看性.

以下摘要整理書中一些值得參考的隻字片語:
  1. 靈魂如同人的潛意識,是一種較高層次的精神活動,其存在價值是為了尋找生命的意義.
  2. 死亡可能不只是代表肉體敗壞的過程,或許也是一股看不見的力量,可以把靈魂與身體完整的切分開來,讓他們出現各自獨立的現象.人並非隨著身體的死亡而徹底消失,靈魂的部分將會完好地存在於另一個世界.
  3. 人不需要執著於善惡對立的二元思維,因為不論物質名利或行善功德,生前的一切都是萬般帶不走的.因此我學會不再以滿足別人的期望要求為目標,也不貪圖有來生福報的幻想,尋回屬於自己的價值標準,選擇做自己認為對的事,為自己的生命負責.
  4. 金錢物質只是人類虛構出來的東西, 根本沒有內涵價值可言, 時間才是衡量生命價值的無可取代單位. 當我們改用時間去衡量事物, 事物就再也沒有貴賤之分, 只有值得與否. 時間才是生命唯一的資源.
  5. 人生雖然短暫, 但絕對足夠一個人好好享受利用. 我們要學會用時間換取快樂而非金錢, 用時間換取自由而非貪婪. 
  6. 凡事只可追求不可強求, 要先懂得捨棄才可真正擁有.
  7. 錯過比做錯更讓人懊悔可惜; 愧對自己比愧對別人更難受. 
  8. 你怎麼過你的今天, 就等於怎麼過你的未來, 你今日的生活亦將是你十年後的生活寫照. 
  9. 某些藥物可能誘發類似瀕死經歷的精神現象, 例如氯胺酮, 環己酮等會產生精神從身體抽離的感覺與視覺幻影; 而麥角酸二乙胺或安非他命則會影響神經傳導物質而產生類似瀕死經驗的幻覺. 
  10. 美國麥克唐蓋爾博士曾在醫院以光束天平做過靈魂度量實驗, 發現病人在過世瞬間, 體重霎那間減少 10.6~42.5 克不等的重量. 

2016年8月26日 星期五

關於 Vue.js

今天在公司的訓練課程列表裡面看到 Vue.js 這課程, 一時覺得好奇, 這是新的 JS Framework 嗎? 甚麼時候蹦出來的? 畢竟快一年沒碰網頁前後端技術, 連 jQuery 功夫都快廢了, 看到新的技術不斷出來卻沒跟上, 開始感到焦慮的說 ... .

找到下面這篇簡介, 稍微了解 Vue.js 的底細, 原來是跟 Angular JS 類似的前端網頁技術 (哈哈, 這個我當初判定不學的東東, 原來還出了差距頗大的第二版), 但是它比 Angular 要輕量且快速, 同樣可以實現雙向繫結與前後端完全分離 :

# 一些平鋪直敘技術相關文

Vue.js 官網教學文件參考 :

https://vuejs.org/guide/

看起來相當不錯, 感覺它跟 Angular JS 的關係就好像是 Java 之於 C++ (不知類比得對不對). 不過對於新技術是否值得投入, 我除了貨比三家憑感覺外, 還要看發展趨勢, 過了初升段後再投入比較保險, 就像當初 jQuery 還沒大紅大紫之前也是百家爭鳴, 花了大把時間學其他技術, 最後還是乖乖投向 jQuery 的人, 最大的收穫就是有資格寫技術演進史吧, 但最大的損失應該是學了一堆後來都不再使用的技術.  


2016年8月25日 星期四

Blogger 留言消失問題

最近學習 ESP8266 與 Arduino 的人越來越多了, 來問我問題的同好也日漸增加, 但是有人反映在網誌的留言莫名其妙消失了! 我也很納悶, 明明手機收信時有看到留言, 為何進 blogger 就不見了, 還以為是留言者自己刪除了哩.

今天研究了這個問題, 我發現可能是因為 blogger 將某些留言認為是垃圾留言而自動刪除之故, 參考 Google 這篇說明 :

https://support.google.com/blogger/answer/187141?hl=zh-Hant

如果留言中含有下列五種以外的 HTML 標記, 就會被認定為垃圾留言而自動移除 :

我們也透過下列方式防範垃圾內容:
  1. 只接受 5 種 HTML 標記:
    • <a> (建立連結之用)
    • <strong> 和 <b> (將文字轉成粗體)
    • <em> 和 <i> (將文字轉為斜體)
  2. 所有連結都會套用 rel="nofollow" 標記
難怪, 連我的回應也被移除了, 就是因為含有這五個以外的 HTML 標記之故. 我看解決之道就是常去垃圾留言區把全部被刪除的留言丟回來, 我以前都不知道還有這個垃圾留言區的存在, 我猜有可能被 Google 判定過是垃圾留言後, 以後的留言都會被當成是垃圾, 就像郵件信箱自動掃除垃圾信的規則那樣被掃掉.


2016年8月24日 星期三

★ Blynk 的通知元件 (Notifications)

測試完 Blynk 的顯示元件後, 對 Blynk 的了解又更深一層了. 接下來要繼續測試通知元件 (Notification), Blynk 有三個通知元件 :
  1. Twitter : 傳送訊息到個人的推特
  2. Email : 傳送訊息到指定郵件信箱
  3. Push : 推送訊息到手機 
以下測試程式是在我自己用洞洞板焊接的 Arduino Nano+ESP8266 IOT 模組上進行實驗, 電路圖參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇



一. 推特元件 (Twitter) :

首先在專案中新增一個 Twitter 元件 :


點一下面板上的 Twitter 元件進入設定畫面 :


這裡有提示使用 Blynk 函式庫傳送推文的方法是呼叫 Blynk.tweet() 函式. 按底下的 "Connect Twitter" 鈕, 要求授權 Blynk 使用我目前在手機上已登入的 Twitter 帳戶, 若要改換帳號, 請按 "點選以切換帳戶" 超連結 :


按 "允許" 鈕 Blynk 即開始連結此 Twitter 帳戶, 成功後會在帳號後面顯示 connected, 同時底下會出現 "Disconnect" 鈕. 若要更換連結帳號, 請先按此鈕切斷連線回到上一步重新連結.


App 部分這樣就 OK 了, 接下來是撰寫設備端韌體程式, 但使用 Blynk.tweet() 函式前須先了解 Twitter 有兩個限制 :
  1. 連續兩個推文內容不可雷同
  2. 連續兩個推文必須至少間隔 60 秒
以下測試程式係參考官網說明文件範例修改而來, 原程式使用一個按鈕來產生中斷以送出推文, 原始範例參考 :

https://github.com/blynkkk/blynk-library/blob/master/examples/Widgets/Twitter/Twitter.ino#L26

為了簡化程式, 我改為固定每 10 分鐘送出一個推文, 其內容為程式執行後所經歷的秒數, 這樣就滿足 Twitter 的要求了. 程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(10L * 60000L, tweetUptime); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void tweetUptime() {
  long uptime=millis() / 60000L; //minutes elapsed
  Serial.println("Tweeting every 10 minutes ;)");
  Blynk.tweet(String("Running for ") + uptime + " minutes.");
  }

此程式使用 SimpleTimer 計時器每 10 分鐘觸發執行自訂函數 tweetUptime() 一次, 送出程式執行到目前所歷經的分鐘數. 結果如下 :


可見確實每十分鐘就送出一次推文.

二. Email 元件 :

此元件可讓我們從硬體設備端送出郵件到任何 Email 信箱位址, 只要呼叫 Blynk 函式庫的 email() 函數即可 :

Blynk.email("my_email@example.com", "Subject", "Your message goes here");

但是此元件與 Twitter 一樣有兩個限制 :
  1. 郵件長度包含標題與內容預設最多不可超過 120 個字元.
  2. 每分鐘最多只能送出一封信件. 
其中第一個限制可以透過修改 BLYNK_MAX_SENDBYTES 常數的預設值加以放寬, 例如 :

#define BLYNK_MAX_SENDBYTES 1200

此指令將傳送總字元數放寬至 1200 字元. 注意, 因為 Blynk 函式庫以 Unicode 處理所傳送之字元, 而 Unicode 所佔 bytes 數至少是 ASCII 的兩倍, 因此若此常數太小, 收到的訊息內容可能有截尾現象 (truncated). 

首先新增 Email 元件 :


點一下 Email 元件進入設定畫面 :


這邊只要輸入要傳送的目的地信箱即可, 事實上這裡不填也沒關係, 因為韌體程式中會指定要傳送到哪一個信箱.

接下來便是韌體程式部分, 參考官網文件範例 :

https://github.com/blynkkk/blynk-library/blob/master/examples/Widgets/Email/Email.ino#L26

同樣地這裡我將其修改為更簡單的範例, 但為了挑戰 Blynk 對 Email 的限制, 改成每分鐘傳送一封 Email 到指定信箱, 看看結果如何. 傳送 Email 只要呼叫 Blynk.email() 函數即可 :

Blynk.email("郵件位址", "主旨", "郵件內容");

Blynk.email() 一次只能傳一個郵件位址, 若要傳給多個信箱, 要分次呼叫.

程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(1L * 60000L, emailUptime); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void emailUptime() {
  long uptime=millis() / 6000L;
  Serial.println("Email every 1 minutes :)");
  Blynk.email("abc@gmail.net", "Running time", String("Running for ") + uptime + " minutes.");
  }

開啟信箱檢查所收到的信件, 發現實際上是每兩分鐘才收到一封信 :


比較起來, Email 是通知時效上較差的方式, 雖然手機也可以收 Email, 但這時代會三不五時看信箱的越來越少了.

三. 推送通知 (Push Notification) :

此元件可讓遠端設備推送通知訊息到手機 App. 新增 Push 元件如下 :


點一下進入設定畫面 :


這裡有兩個選項, 其中 "Notify when hardware goes offline" 預設為 OFF, 若改為 ON, 則當遠端設備離線 (斷電或網路異常), Blynk Cloud 就會發出通知到手機. 而 "Priority" 選項預設為 NORMAL, 若設為 HIGH, 則訊息會比較即時, 但這樣將使手機更耗電, 因為必須開啟一個固定網路連線以避免訊息延遲傳遞. 參考 Google 關於 Android 即時訊息的說明 :

#  Google Cloud Messaging : Setting the priority of a message

跟 Email 的限制一樣, 推送訊息也有兩個限制 :
  1. 訊息內容最多不可超過 120 個字元.
  2. 每分鐘最多只能送出一則訊息. 
跟 Email 一樣, 第一個限制若要放寬可以透過修改 BLYNK_MAX_SENDBYTES 常數的預設值加以放寬 (兩個是一樣的常數), 例如 :

#define BLYNK_MAX_SENDBYTES 256

設備端韌體程式可呼叫 Blynk.notify() 函數向手機 App 推送訊息 :

Blynk.notify("訊息");

我將上面 Email 的範例改為推送訊息, 同樣每分鐘送出一則, 測試看看是否變成兩分鐘, 官網元範例程式參考 :

PushNotification_Button.ino


#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(1L * 60000L, notifyUptime); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void notifyUptime() {
  long uptime=millis() / 6000L;
  Serial.println("Notify every 1 minutes :)");
  Blynk.notify(String("Running for ") + uptime + " minutes.");
  }

程式上傳執行結果跟 Email 一樣, 變成每兩分鐘才收到通知, 手機會發出聲響, 第一次收到時會詢問是否允許推送到手機面板. 為了擷取這些通知的列表, 我故意在 "設定/通知管理員" 的規則中, 將 Blynk 從 "允許" 改為 "限制" (取得資料後再改回通知或允許), 如下所示 :


這樣之後收到的通知就會被攔截在 "紀錄" 裡了 :


從最後的時間欄可知, 雖然程式每分鐘送出一則通知, 但受到 Google Cloud Messaging 的限制, 偶數分鐘的那則被丟掉了, 結果兩分鐘才會收到一則通知. 然後我拔掉 Arduino 電源, 讓設備離線, 結果真的有收到離線通知, 即上圖最上面 13:58 那一則通知.

上面三種通知元件我覺得以第三種推送通知最實用, 因為只要有上網, 手機收到推送的通知就會發出聲響+震動, 不需要開啟 Email 或 Twitter 去看. 可以在 "設定/音效" 中更改預設的通知鈴聲 :


最好選一個比較響亮的, 這樣才能起到通知的作用. 不過這三種通知方式用途不同, Twitter 與 Push 只能將訊息傳遞給自己; 而 Email 則可以傳遞給自己以外的人.


2016年8月23日 星期二

2016 年第 32 周記事

過了中元節後的節氣就是今天 8/23 的處暑了, 處者止也, 即暑氣至此消散, 秋意漸濃也. 今年南部的暑熱雖然創新高, 但在午後常見的雷陣雨澆淋下, 似乎沒有很長久. 我喜歡秋冬, 不喜炎夏, 因為再冷總是可以多加件棉襖解決, 但酷熱總不能扒層皮解決吧.

本周菁菁常拉肚子, 周六時問她要不要留在高雄休息, 她說這樣就兩周沒回鄉下了耶! 所以就一起回去了. 上週末我也微恙, 上顎吞口水時覺得有阻礙感, 後轉微痛, 週三趕緊去楊宜璋那裏看, 原來扁桃腺發炎, 吃了兩趟藥才好. 現在盡量不再熬夜研究東研究西, 晚上 11 點半前睡覺, 早上起來做完甩手功後去河堤快走, 走一趟約 4 公里, 約有 5000 步, 回來沖個澡再去上班, 整個人有精神多了. 今天則是改為去程慢跑, 回程走路, 跑完大汗淋漓真過癮, 有流汗才算是運動啊.

過去這一周主要在研究如何使用 Blynk App, 這是一款非常精美的手機軟體, 也是物聯網應用的絕佳工具, 透過 Blynk 雲端讓手機可以即時監控遠端的物聯網設備, 有任何狀況也會發出通知警告我們, 實在是 100 分的工具軟體. 俗話說工欲善其事必先利其器, 熟悉此工具可讓物聯網專案應用更快完成. 

暑假快結束了, 二哥的 C 語言學習卻還是只進到陣列而已. 雖然我屢次提醒, 但顯然遊戲比枯燥的 C 語言要有吸引力多了. 我希望他能減少玩遊戲的時間, 那些時間若是給我來用的話, 不要說 學會 C 語言, 連 Python, Lua 我都學會了. 年少總是不知道時間的可貴, 等真正了解時間的價值之後, 頭髮已泛白矣.


關於網誌名稱

有人問我為什麼叫小狐狸事務所, 其實這原先只是記錄我家三隻小狐狸小時候生活點滴的網誌 (原本在無名小站), 隨著她們漸漸長大, 能記錄的事情越來越少, 就漸漸變成我的學習紀錄簿了. 名稱來自於姊姊小時候的暱稱, 可能是受到繪本裡可愛的小狐狸的影響, 她非常喜歡狐狸, 所以就被我叫做小狐狸, 弟弟妹妹出生後也都理所當然成為了小狐狸. 在陪伴他們長大的時空裡, 我就成了小狐狸事務所的所長了. 往事如煙, 腦海中能牢記的並不多, 必須靠自己孜孜不倦地記錄下來. 人生是甚麼? 就是所經歷的故事呀! 當青春漸漸遠去, 翻閱這些前塵往事時, 或許能幫助自己勉力找回往日的浮光掠影.


2016年8月20日 星期六

★ Blynk 的顯示元件 (Displays)

整理完 Blynk 控制器元件後, 繼續來看看顯示元件 (Displays). 顯示元件主要用來呈現從硬體設備端取得的資料並加以圖形化, 對 App 而言是從遠端設備讀取數據 (Read), 這可以用兩種方式達成, 一個方式是由 Blink App 的顯示元件主動向硬體設備發出週期性的要求 (Request); 另一個方式是由硬體設備向 Blink App 顯示元件定期推送資料 (Push).


目前有十種顯示元件如下 :


其中 Graph (曲線圖或長條圖) 與 Gauge (儀表) 我已經在上週利用 Blynk 顯示 Arduino 溫溼度與照度對 Blynk 初步測試時使用過了, 參考 :

# Blynk 應用 (二) : 在手機顯示即時溫溼度與光度

以下測試程式是在我自己用洞洞板焊接的 Arduino Nano+ESP8266 IOT 模組上進行實驗, 電路圖參考 :

# 製作 Arduino Nano + ESP8266 物聯網模組 (四) : 完結篇



若懶得焊接板子, 可以照上面電路圖自行用麵包板接線, 只不過 AMS1117 不是 DIP 封裝, 沒辦法插入麵包板, 比較麻煩而已.

一. 數值顯示器 (Value Display) :

此種顯示元件有四個, 可以綁定硬體設備的 Digital/Analog/Virtual 輸出腳, 主要差別是元件外觀的大小與是否具有將輸出資料格式化的功能 :
  1. Value Display S : 數值顯示器 (短)
  2. Value Display M : 數值顯示器 (中)
  3. Labeled Value M : 格式化的數值顯示器 (中)
  4. Labeled Value L : 格式化的數值顯示器 (長)
前兩種的設定畫面如下 :


"VALUE" 框填入此顯示器標題, 例如溫度, 溼度等. PIN 可以選 Digital/Analog/Virtual, 右邊是其數值上下限值. 而 Reading Frequncy 為讀取頻率, 預設 1 秒, 這表示 Blynk App 會週期性向硬體設備發出讀取要求, 如果此顯示器綁定的是實體腳位 (Digital/Analog), 則設備端不需要寫傳送資料的程式碼, Blynk App 會自動取得資料.

但是, 如果綁定虛擬腳位 (Virtual Pin), 則設備端韌體程式必須將資料寫入該虛擬腳位, App 才會收到所傳遞的數據. 以 Arduino 來說, 必須定義一個 BLYNK_READ(vPin) 函數, 然後呼叫 Blynk.virtualWrite() 將資料傳送給 App.


例如下面這個範例是顯示程式執行以來經過之秒數 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  }

void loop() {
  Blynk.run();
  }

BLYNK_READ(V0) {
  Serial.println(millis()/1000);
  Blynk.virtualWrite(V0, millis()/1000);
  }


使用虛擬腳位時, Reading Frequncy 也可以選擇 PUSH 模式, 從硬體設備推送資料到 Blynk App, 而非由 App 提出要求, 如下圖所示 :


這必須配合計時器 (Timer) 週期性地呼叫 Blynk.virtualWrite() 向 Blynk App 推送資料, 才能讓顯示器元件呈現即時數據, 例如 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  timer.setInterval(1000L, pushUptime); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void pushUptime() { //push Arduino uptime to virtual pin V0
  Serial.println(millis()/1000);
  Blynk.virtualWrite(V0, millis()/1000);
  }

在上面的程式中, 為了使用計時器, 我們匯入了 SimpleTimer 函式庫並建立了 SimpleTimer 物件, 在 setup() 函式中呼叫計時器物件的 setInterval() 方法並指定每秒觸發 pushUptime() 函式一次, 呼叫 Blynk.virtualWrite() 將程式執行以來所經過的秒數寫入虛擬腳位 V0, 這樣 App 上就會即時顯示程式執行已過了幾秒了 :


注意, 千萬不可以把 Blynk.VirtualWrite() 放在 loop() 迴圈函數中, 這樣會傳送太頻繁的資料給 Blynk Cloud 伺服器, 造成所謂 "Flood Error", 參考 :

http://docs.blynk.cc/#troubleshooting-flood-error

務必如上例所示使用 SimpleTimer 計時器函數來控制數據傳輸頻率. 當資料傳送頻率超過每秒 20 筆時, Blynk Cloud 會自切斷連線, 手機 Blynk App 會顯示 "Your hardware is offline".

接下來看看格式化的顯示器元件, 此元件提供四種格式化字串, 可設備端傳來的數據機以期望的格式顯示 :

 格式 作用 (原始數據例如 12.6789)
 /pin/ 不格式化, 顯示原始數據 12.6789
 /pin./ 顯示四捨五入後之整數部分 13
 /pin.#/ 顯示四捨五入後至小數後一位 12.7
 /pin.##/ 顯示四捨五入後至小數後兩位 12.68

其設定畫面與 Value Display 相同, 只是多了一個 LABEL 欄位而已, 可將上面的格式化字串插入任何字串, 例如 "溫度 /pin.##/ 度" 就會顯示至小數後兩位 :


下面範例是從 DHT11 取得溫濕度後, 利用 PUSH 方式推送至格式化數值顯示器, 但不格式化而是顯示原始數據, 溫度使用虛擬腳位 V0, 濕度使用虛擬腳位 V1, 手機 App 設定如下 :



設備端程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>
#include "DHT.h"
#define DHTPIN 6
#define DHTTYPE DHT11

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  timer.setInterval(1000L, pushWeather); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void pushWeather() { //push Arduino uptime to virtual pin V0
  float Temperature=dht.readTemperature(); //get temperature (C) from DHT11
  float Humidity=dht.readHumidity(); //get humidity from DHT11
  Serial.print("Temperature=" + (String)Temperature + " ");
  Serial.println("Humidity=" + (String)Humidity);
  Blynk.virtualWrite(V0, Temperature);
  Blynk.virtualWrite(V1, Humidity);
  }

結果如下 :


由於 DHT11 精確度只到整數, 因此推送給 App 的原始數據小數部分是 .000 的浮點數. 可見格式化顯示元件的好處是可以隨意控制想要呈現的輸出格式, 除了收到的原始數據可格式化外, 還能添加其他文字. 此專案分享如下 :



二. LED 顯示器 :

此元件用來在手機上以燈光明亮度顯示遠端數據, 其值介於 0~255 之間, 0 會讓 LED 燈滅, 而 255 讓 LED 最亮. LED 只能綁定到虛擬腳位, 不能綁定 Digital 或 Analog 腳. 其設定畫面如下 :


在設備端的韌體程式裡, 必須先建立對應此虛擬腳位的 WidgetLED 物件, 呼叫此物件的 setValue() 方法可以設定 LED 的值 (0~255), 呼叫 on() 可將 LED 設為最亮, 相當於呼叫 setValue(255); 而呼叫 off() 方法則熄滅 LED, 相當於呼叫 setValue(0). 呼叫 getValue() 則可以取得目前 LED 的亮度值 (0~255).

下面範例是由設備端透過虛擬腳位 V0 控制 Blynk App 的 LED 燈每秒閃爍 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
WidgetLED led0(V0);

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  timer.setInterval(1000L, blinkLED); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void blinkLED() { //push Arduino uptime to virtual pin V0
  if (led0.getValue()) { //LED not off
    led0.off();
    Serial.println("LED V0: off");
    }
  else { //LED off
    led0.on();
    Serial.println("LED V0: on");
    }
  }

執行結果 LED 燈確實會每秒閃爍一次 :




下面範例則是使用 setValue() 讓 LED 越來越亮, 到達 255 後又越來越暗, 如此周而復始 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
WidgetLED led0(V0);
int i;
boolean brighter=true;

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  timer.setInterval(1000L, brighter_darker_toggle); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void brighter_darker_toggle() { //push Arduino uptime to virtual pin V0
  if (brighter) {
    if (i < 255) {i += 5;}  //brighter
    else {brighter=false;}  //darker
    }
  else { //darker
    if (i > 0) {i -= 5;}  //darker
    else {brighter=true;}  //brighter
    }
  Serial.println(i);
  led0.setValue(i);
  Blynk.virtualWrite(V1, i);  
  }

在此範例中, 我另外添加了一個 Value Display 來顯示亮度值, 並將其綁定到虛擬腳位 V1 :



三. LCD 液晶顯示器 :

此元件模擬 1602 液晶顯示器, 其設定畫面如下 :


它有 Simple 與 Advanced 兩種模式可選, 所謂的 Advanced 模式是把 LCD 顯示元件當成真正的 1602 一樣, 必須先建立一個 WidgetLCD 物件, 然後呼叫 print() 函數輸出到手機 App 上指定的虛擬腳位 (Advanced 模式只能使用 Virtual Pin), 下列範例是在 LCD 上顯示 "Hello World", 綁定到 App 的虛擬腳位 V0, 設定如下 :


Colors 欄位可設定 LCD 背景與文字顏色.

程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
WidgetLCD lcd(V0);

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  lcd.clear(); //clear LCD screen
  lcd.print(4, 0, "Hello"); //(X: 0-15, Y: 0-1, "message")
  lcd.print(4, 1, "World"); //(X: 0-15, Y: 0-1, "message")
  }

void loop() {
  Blynk.run();
  }


上面程式中, 輸出到 LCD 的 print() 是放在 setup() 裡, 這樣設備開機後 LCD 螢幕顯示完 Hello World 便不動了. 如果要顯示變動資料, 同樣地, 為了避免要求太頻繁造成 Flood Error, 不可以把 print() 指令直接放在 loop 裡, 而是要將其放在一個函數中, 利用 SimpleTimer 物件的 setInterval() 去設定週期性觸發此函數.

例如下面的範例是將上面的程式已執行時間改在 LCD 元件顯示 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
WidgetLCD lcd(V0);

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  lcd.clear(); //clear LCD screen
  timer.setInterval(1000L, pushUptime); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void pushUptime() { //push Arduino uptime to virtual pin V0
  lcd.print(0, 0, "Uptime"); //(X: 0-15, Y: 0-1, "message")    
  lcd.print(0, 1, millis()/1000); //(X: 0-15, Y: 0-1, "message")  
  }


回到 Simple mode, 此模式將 1602 的兩排文字當成是腳位, 可以綁定到 Analog 與 Virtual 兩種腳位, 這樣就與上面的 Value Display 類似, 可以選擇 Reading 或 Push 模式來取得設備端的資料, Push 模式是在設備端韌體程式中, 使用計時器自行將資料推送到 App 中, 必須用 Blynk.VirtualWrite() 將資料輸出到虛擬腳位 (因此只能綁定虛擬腳位); 而 Reading 模式則是在 Blynk App 中的 Reading Frequency 欄位中指定固定周期向設備端要求資料, 可以綁定 Analog 或 Virtual 腳位, 若綁定虛擬腳位, 則設備端韌體程式必須準備 BLYNK_READ(vPin) 函式才行, 若綁定 Analog 腳位就不用.

下面範例程式是使用 Push 模式向 App 的 V0 推送程式執行以來的秒數, 向 V1 推送毫秒數 :


程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  timer.setInterval(1000L, sendSeconds); //Don't send more that 10 values per second
  timer.setInterval(1000L, sendMillis); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void sendSeconds() {
  Blynk.virtualWrite(V0, millis() / 1000);
  }

void sendMillis() {
  Blynk.virtualWrite(V1, millis());
  }


如果採用 Reading 模式的話, 設備端韌體程式就不需要費心使用計時器推送了 (不需要匯入 SimpleTimer 函式庫), 但若綁定虛擬腳位, 必須準備 BLYNK_READ() 函數對應, 設定如下 :


程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  }

void loop() {
  Blynk.run();
  }

BLYNK_READ(V0) {
  Blynk.virtualWrite(V0, millis() / 1000);
  }

BLYNK_READ(V1) {
  Blynk.virtualWrite(V1, millis());
  }

所以 Reading 模式在設備端韌體部分比較簡單, 但結果是一樣的. 注意, LCD 在 Simple mode 下兩列 message 都是可以格式化的, 用法跟上面 Formated Value Display 元件完全一樣.

四. 終端機元件 (Terminal) :

此元件除了用來顯示設備端訊息外, 也可以從 App 對設備下達指令, 模擬一般 Command Line 通訊軟體如 Putty 的基本雙向通訊功能. 此元件只能綁定虛擬腳位, 作為設備與 App 間的溝通介面, 其設定畫面如下 :


其中 INPUT LINE 欄位設定是否要開啟輸入框, 若設為 ON, 則終端機視窗底下會有一個輸入框, 使用者若輸入文字再按 Enter, 就會透過所綁定的虛擬腳位 (INPUT 選項) 傳送給設備, 這樣 BLYNK_WRITE(vPin) 函數就會被呼叫, 我們可在此函數中處理回應, 利用呼叫終端機物件的 println() 函數來將回應訊息傳給 App (當然是透過所綁定之虛擬腳位). 如果只是要像 LCD 那樣顯示設備端資訊, 不需要向設備下指令, 就可以設為 OFF. AUTOSCROLL 欄位設定輸出訊息是否要自動向下捲動.

設備端韌體程式必須建立一個 WidgetTermal 物件, 它類似序列埠物件, 透過呼叫 print(), println(), write() 以及 flush() 等函數向虛擬腳位輸出訊息, 傳送到 App 的 Terminal 元件上. 終端機回應處理部分使用的是 Reading (Request) 模式, 亦即必須為虛擬腳位準備 BLYNK_WRITE() 函數來處理回應訊息.

下面範例改寫自官網說明文件 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
WidgetTerminal terminal(V0);

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  terminal.println(F("Blynk v" BLYNK_VERSION ": Device started"));
  terminal.println(F("-------------"));
  terminal.println(F("Type 'Hello' and get a reply, or type"));
  terminal.println(F("anything else and get it printed back."));
  terminal.flush();  //Ensure everything is sent
  }

void loop() {
  Blynk.run();
  }

BLYNK_WRITE(V0) { //called when V0 updated (user send data to V0)
  if (String("Hello") == param.asStr()) { //if receives "Hello"
    terminal.println(F("You said: 'Hello'")); //show user command "Hello" in Widget
    terminal.println(F("I said: 'World'")); //show hardware response "World"
    }
  else { //otherwise send it back
    terminal.print(F("You said:"));
    terminal.write(param.getBuffer(), param.getLength());  //echo
    terminal.println();  //new line
    }
  terminal.flush(); //Ensure everything is sent
  }

此程式的 BLYNK_WRITE() 函數就是處理 App 端虛擬腳位 V0 傳來的 Reading Request, 其要求參數 param 攜帶了使用者在 Terminal 元件所下指令, 經字串比對若指令為 "Hello", 就回應 "World", 否則就將指令 echo 回去, 這時會呼叫 getBuffer() 函數取得緩衝器內的 raw data, 再用 write() 函數寫回去. 結果如下 :


這很像 Linux 的 shell 對吧! 注意, 上面的程式中使用了 Blynk 函式庫定義的版本常數 BLYNK_VERSION, 放在 F() 函數內直接與常數字串相接即可, 不可用 "+" 串接.

五. 歷史圖形元件 (History Graph) :

之前曾經使用 Graph 元件來顯示氣候資料, 可以顯示量測值隨時間變化的圖形 (折線圖或長條圖), 參考 :

# Blynk 應用 (二) : 在手機顯示即時溫溼度與光度

然而 Graph 元件的時間尺度是固定無法調整的, 沒辦法看到之前的數據變化情形. 而此 History Graph 元件提供三種格局的尺度, 可以讓我們放大縮小時間軸, 以便觀察之前傳送到 Blynk 雲端伺服器的資料. 此元件可以綁定 Digital/Analog/Virtual 三種腳位, 但一個元件只能同時觀察四個量測變數, 設定畫面如下 :


History Graph 是顯示過去曾傳送到伺服器的所有資料, 因為我之前使用 V0, V1, V2 做過別的實驗, 這裡為了演示, 避免之前的數據讓圖形很奇怪 (例如 V0 之前用來傳送程式執行秒數, 其數值高達數萬, 現在拿來顯示溫度又變成 20~30, 其歷史圖形就很突兀), 所以我改用 V4~V7 當參數. 雖然目前我手邊沒有氣壓計模組, 我還是補上去, 免得四個通道有一個是空的. 底下 "Show Legend" 欄位預設 OFF, 設為 ON 會在上方顯示四個參數的圖例 (曲線顏色說明).

注意, 上面設定項目裡面沒有 Reading Frequency 可選, 這是因為此元件是用來從雲端資料庫中取出所指定之腳位了歷史資料來顯示, 所以並不需要為此元件特別定義資料驅動方式, 這些腳位由其他程式碼驅動, History Graph 只是從資料庫取出歷史資料來顯示而已. 在下面程式碼中為了演示之故, History Graph 元件特別使用 Push 方式以計時器推送資料給 V4~V7, 正常應用情況下可能是用其他顯示元件如 Gauge 以 Reading 模式取得資料. 程式如下 :

#define BLYNK_PRINT Serial  //Comment this out to disable prints and save space
#include <SoftwareSerial.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <SimpleTimer.h>
#include "DHT.h"
#define DHTPIN 6
#define DHTTYPE DHT11

SoftwareSerial esp8266(7, 8); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
DHT dht(DHTPIN, DHTTYPE); //Initialize DHT sensor

char ssid[]="EDIMAX-tony";
char pass[]="1234567890";
char auth[]="e80fe6aae0d442518820f61xxxxxx8f5";

void setup() {
  Serial.begin(9600); //Set console baud rate
  esp8266.begin(9600); //Set ESP8266 baud rate
  Blynk.begin(auth, wifi, ssid, pass);
  while (Blynk.connect() == false) {} //wait until connected
  timer.setInterval(1000L, pushWeather); //Don't send more that 10 values per second
  }

void loop() {
  Blynk.run();
  timer.run();
  }

void pushWeather() {
  float Temperature=dht.readTemperature(); //get temperature (C) from DHT11
  float Humidity=dht.readHumidity(); //get humidity from DHT11
  int cds=analogRead(A0); //get cds voltage (0~5V to 0~1023 dark)
  int Luminance=100-map(cds,0,1023,0,100); //map 0~1023(dark) to 0(dark)~100%
  Blynk.virtualWrite(V4, Temperature);
  Blynk.virtualWrite(V5, Humidity);
  Blynk.virtualWrite(V6, Luminance);
  }

這裡利用計時器每秒將氣候資料透過虛擬腳位推送到雲端, 然後顯示在手機 App 上. 下面是一小時尺規內 (1h) 的歷史圖形 :


可見此元件提供六種時間尺規來顯示圖形 :

  1. 1h : 一小時
  2. 6h : 六小時
  3. 1d : 一天
  4. 1w : 一周
  5. 1m : 一個月
  6. 3m : 三個月

亦即, History Graph 最長也只能顯示過去 3 個月的歷史圖形. 點 6h 即切換到六小時尺規內的歷史圖形如下 :


在 Play 狀態下, 於歷史圖形上向左邊滑動, 會出現如下畫面 :


按 "Export to CSV" 會將此元件所綁定腳位的歷史資料以 CSV 壓縮檔寄送到郵件信箱 :


因上面程式實際上只有 V4~V6 三支虛擬腳位有資料, 所以只有三個壓縮檔 :


解壓縮後的 CSV 檔內容如下 (此為溫度 V4) :


按下面的 "Erase Data" 則會刪除此元件所綁定的所有腳位之歷史資料.


刪除後資料庫便無資料, 顯示 "No data" :


OK, 以上便是顯示元件的用法測試.

參考 :

Blynk HTTP RESTful API