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 階段尚未完備, 官網教學文件也沒有資料, 等正式推出後再來補齊.


3 則留言:

  1. 你好,想請教您一個問題。
    我目前使用Arduino Mega配合ESP8266-01實現使用BLYNK遠端控制繼電器的實驗
    實驗已經完成並且可以work

    考量到穩定性的問題,我讓Arduino Mega板定時重啟(約1小時),且在重啟前使用digitalWrite功能將ESP8266的CH-PD腳位拉低,待Arduino Mega重啟後再setup裡面再將CH-PD腳位拉高,實現ESP8266與Mega板同時重啟的作用。重啟的成功率大概有九成以上

    就這樣讓實驗平台work了兩三天,都正常工作。但今天整個平台當機,察看了之後發現使用Blynk.begin(auth, wifi, ssid, pass); 函式後,回傳fail to connect ... 並且整個程式就卡在這裡,沒有後續的動作。

    按了Mega版的reset鈕後也沒有用(版子會reset,但依舊fail to connect),直到我將ESP8266的獨立電源手動重啟,並且再次將Mega版重啟後,實驗平台才重新上線且正常工作。

    目前我的想法是,若偵測到串列埠回傳"fail to connect"後,將esp8266的獨立電源用繼電器重啟,以模擬我的解決辦法。

    但我發現當回傳"fail to connect"後,Arduino內的程式也不會繼續往下執行(因我在Blynk.begin函式後方用一個Serial.println試圖顯示一個字串,結果此字串並沒有被執行),就像程式死當在這裡一樣

    想請教我該如何解決這個問題

    回覆刪除
  2. 吳兄, 您這問題我也曾經想過, 因為任何板子都有穩定性問題, 溫度或電磁干擾導致異常當機是常有的事, 這時不管是 Arduino 或 ESP8266 都要去 reset. 您可以利用 Arduino 的看門狗功能來解決, 參考 :

    http://makerpro.cc/2014/12/introduce-arduino-watch-dog-timer/

    回覆刪除
  3. Hi
    事實上我在程式裡面已經有用看門狗的功能
    但看門狗的enable必須要在Blynk.begin後才能打開
    否則會還沒連線上就被看門狗重啟

    然而若Blynk.begin連線失敗的話(如回傳fail to connect),會導致程式無法繼續執行
    也就無法藉由看門狗enable進而重啟的方式再次連線

    回覆刪除