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/

2020-07-08 補充 :

Elsevier 出了一本介紹最近憶阻器發展的新書, 值得參考 :

# Memristive Devices for Brain-Inspired Computing




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 則可以傳遞給自己以外的人.