文章程式碼顯示

2018年1月20日 星期六

《進階※應用篇》寫程式Arduino教學 - 03:手機與 Arduino 的連結 BLYNK 應用簡述

本文受惠於 小狐狸事務所 ★ Blynk 應用 (一) : 用手機控制 Arduino ,實作後有些心得,撰寫成另一篇教學文。程式碼以及內文,部分引用了上方連結中的內容,若有冒犯請告知。

本文中若有不清楚的部分在上方連結的系列中應該都可以找到解答。

Arduino 學習到一個階段就會出現一些小瓶頸,總感覺少了一些什麼,例如無法很好的可視化數據資料、遠端遙控知易行難。在人人都有手機的年代如果我們可以讓 Arduino 結合手機 APP ,應該可以大幅增加趣味性,但這又出現了一個問題

「撰寫手機 APP 並不是那麼容易」

在早期學 Arduino 的人若想與手機進行連結,多半是使用 MIT App Inventor ,其可以幫助我們藉由圖塊的方式撰寫手機端的 APP ,實際使用過後我是覺得不太習慣這樣的方式,只能說程式碼寫久了要我去用圖塊的方式來寫程式不免覺得彆扭,所以 Inventor 就被我自己打入冷宮了

而如果我們要用程式碼來撰寫 APP ,又是一大工程,畢竟撰寫手機 APP 需要很深厚的程式語言功力,何況我們還要將 Arduino 與之連結,想到不只需要去撰寫手機介面,還要處理關於伺服器等等的東西就覺得很頭大,於是我對手機與 Arduino 連結的部分怯步了

約莫半年前我在小狐狸事務所看到有 BLYNK 這個 APP ,其又燃起了我對手機與 Arduino 連結之間的希望。

我們可以很簡單的理解,BLYNK 就是幫你處理好了手機 APP 端,並且這個 APP 支援很多不同種的 MCU (也就是說不單單是 Arduino 可以用而已)

建議大家可以先看看小狐狸的系列文章,我下面的敘述多半是加入自己的理解,敘述得更清楚些而已

正文開始 :

一開始須要在 BLYNK 申請帳號,並且得到一個 auth 。這個 auth 就像是一個 key 一樣,讓 Arduino 連接到 BLYNK 雲端後,知道你是哪個使用者(以及哪個專案),這部分請參考小狐狸的文章,我直接略過了。

使用手機直接對 Arduino 的 digital I/O進行控制

#define BLYNK_PRINT Serial

#include "ESP8266_Lib.h"
#include "BlynkSimpleShieldEsp8266.h"


char auth[] = "你的auth"; 
char ssid[] = "你的wifi名稱";
char pass[] = "你的wifi密碼";

// Hardware Serial on Mega, Leonardo, Micro...
//#define EspSerial Serial3 // For Mega ... 
// or Software Serial on Uno, Nano...
#include "SoftwareSerial.h"
SoftwareSerial EspSerial(2, 3); // 板子的 RX, TX
// Your ESP8266 baud rate:
#define ESP8266_BAUD 115200

ESP8266 wifi(&EspSerial);

void setup()
{
  Serial.begin(9600);
  delay(10);
  // Set ESP8266 baud rate
  EspSerial.begin(ESP8266_BAUD);
  delay(10);
  Blynk.begin(auth, wifi, ssid, pass);
}

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




這部分是直接使用 BLYNK APP 裡面的 Button 對 Arduino UNO 的 13 腳進行控制

若您使用的是如 Mega 板這種的,應該使用的是 12 行的程式碼並將 14、15 行註解掉

BLYNK_READ 函數 以及 Blynk.virtualWrite 函數


#define BLYNK_PRINT Serial 
#include "ESP8266_Lib.h"
#include "BlynkSimpleShieldEsp8266.h"
#include "DHT.h"
#define DHTPIN 6
#define DHTTYPE DHT11

char auth[] = “你的auth”;

char ssid[] = “你的wifi名稱";
char pass[] = "你的wifi密碼";

// 假設我們使用的是 Uno 板
#include "SoftwareSerial.h"
SoftwareSerial EspSerial(2, 3); // RX, TX

#define ESP8266_BAUD 9600

ESP8266 wifi(&EspSerial);
DHT dht(DHTPIN, DHTTYPE); // Initialize DHT sensor

void setup() {
  Serial.begin(9600);
  delay(10);
  EspSerial.begin(ESP8266_BAUD);
  delay(10);
  Blynk.begin(auth, wifi, ssid, pass);
  }

void loop() {
  Blynk.run();
  }
  
BLYNK_READ(0) {
  float Temperature=dht.readTemperature(); //get temperature (C) from DHT11
  Blynk.virtualWrite(0, Temperature);
  BLYNK_LOG("Temperature %5.1f", Temperature);
  }


在手機端設定好顯示元件並且綁定到某個虛擬暫存器(上圖與程式碼是綁定到 V0 )後,我們就可以利用 Arduino 將值傳上去,並顯示到手機介面上,詳細的邏輯看上圖


注意這邊, BLYNK_READ 只有 "告知Arduino有更新虛擬暫存器值需求"我們還需要額外使用 Blynk.virtualWrite 才是真正把 Arduino 的數據傳上去雲端給手機顯示

假設我們在 Arduino 程式中定義了三個 BLYNK_READ() 函數,其參數為虛擬暫存器的編號BLYNK_READ(0)、 BLYNK_READ(1) 、BLYNK_READ(2) ,分別對應 V0~V2 三個虛擬暫存器所綁定的元件。每當手機 Blynk App 裡的元件 (Widget) 向 Arduino 提出資料要求時這些對應的函數就會被呼叫。

在手機 App 設定這三個元件時我們指定 Request rate ( 新版改為 READING RATE )為每 10 秒提出要求一次。即這三個函數每 10 秒就會被呼叫一次。 Arduino 中就會觸發BLYNK_READ(0)、 BLYNK_READ(1) 、BLYNK_READ(2) 函式,去讀取測量值。

也就是說當手機有讀取 V0 的需求時,會觸發類似中斷副程式的效果。如觸發 V0 則會觸發BLYNK_READ(0)

僅記, BLYNK_READ 是 APP 告訴 Arduino 這個虛擬暫存器的值有更新需求

Blynk.virtualWrite 才是真正把 Arduino 的數據傳上去雲端

前面我們藉由顯示元件設定 READING RATE 告知 Arduino 有虛擬暫存器的值要被更新的需求,觸發了"BLYNK_READ() 函數",但這僅僅告訴 Arduino 有值需要被更新而已

還沒有把值從 Arduino 傳上去雲端

Blynk.virtualWrite() 函數會把數據實際傳上雲端的虛擬暫存器,這樣手機 App 便能收到來自 Arduino 的測量值了

另外這裡的 BLYNK_LOG() 函數其實就是我們常用的 Serial.println(),即向指定序列埠輸出訊息。但 Blynk 定義的這個 BLYNK_LOG() 較有彈性,只要在程式開始時用定義

BLYNK_PRINT 常數為哪一個序列埠 (須在第一行),之後只要直接使用 BLYNK_LOG() 即可


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(2, 3); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object

char ssid[]="你的wifi名稱";
char pass[]="你的wifi密碼";
char auth[]="你的auth";

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 V0 updated by App
  Serial.print("X=");
  Serial.println(param.asInt());
  }


這部分在小狐狸的文章中是使用 Slider 來進行說明,在這我就不多說了

以下引用原文

當虛擬暫存器 V0 被 Blynk 伺服器更新時 (即 App 的 Slider 元件將數值寫入 Blynk Server, 伺服器就會更新虛擬暫存器 V0的值),此時 Arduino 裡面特定函數 BLYNK_WRITE(V0) 就會被觸發, 我們可以在此函數內透過 param 變數取得 V0 之值

上方的程式碼是針對 "單變數",也就是說在 APP 的控制元件中只會回傳一個値



上圖引用原文所述

如果是"搖桿"這種控制元件,則會回傳"雙變數" ,整體來說較為麻煩

忽略 BLYNK_READ() , 直接由 Arduino 主動更新虛擬暫存器的值

#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[]="你的wifi名稱";
char pass[]="你的wifi密碼";
char auth[]="你的auth";

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);
  }



前面有提到在 App 裡面可以設定 READING RATE (原文中是READING FREQUENCY,BLYNK 改版後名稱改為 READING RATE) 來決定 Arduino 中的 BLYNK_READ() 要多久被觸發一次,但其較不彈性。我們可以換個方法

讓 Arduino 主動更新虛擬暫存器的值

記住!在手機 App 端只要發現雲端的虛擬暫存器值有被更新,就會主動更新顯示幕上面的數值。

此法在 App 內的顯示元件其 READING RATE 要設定為”PUSH”

我們在 Arduino 內用 "SimpleTimer.h" 函式庫週期性地呼叫 Blynk.virtualWrite() 來將數據傳送到雲端

在此要注意,千萬不可以把 Blynk.VirtualWrite() 放在 loop() 迴圈函數中,這樣會傳送太頻繁的資料給 Blynk Cloud 伺服器,當資料傳送頻率超過每秒 20 筆時,Blynk Cloud 會自切斷連線,手機 Blynk App 會顯示 "Your hardware is offline"。

顯示元件的格式化


blynk內的LED

#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(2, 3); //(RX, TX)
ESP8266 wifi(&esp8266); //create wifi object
SimpleTimer timer; //create a Timer object
WidgetLED led0(V0);

char ssid[]="你的wifi名稱";
char pass[]="你的wifi密碼";
char auth[]="你的auth";

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");
    }
  }



BLYNK 上的 LED 元件用來在手機上以燈光明亮度顯示數據,其值介於 0~255 之間。

0 會讓 LED 燈滅;255 讓 LED 最亮。LED 只能綁定到虛擬腳位,不能綁定 Digital 或 Analog 腳。在 Arduino 的程式裡必須先建立對應此虛擬腳位的 WidgetLED 物件(第9行),並透過呼叫此物件的 setValue() 來設定 LED 的值 (0~255)。直接呼叫 on() 可將 LED 設為最亮,相當於呼叫 setValue(255);而呼叫 off() 則熄滅 LED,相當於呼叫 setValue(0)

呼叫 getValue() 可以取得目前 LED 的亮度值 (0~255)

BLYNK_CONNECTED() 函數

#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
int blinkRate;

char ssid[]="你的wifi帳號";
char pass[]="你的wifi密碼";
char auth[]="你的auth";

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();
  blinkD13Led(blinkRate);
  }

BLYNK_WRITE(V0) { //called when V0 updated by App slider
  blinkRate=param.asInt(); //store value from App slider
  Serial.println(blinkRate);
  }

BLYNK_CONNECTED() { //get data stored in virtual pin V0 from server
  Blynk.syncVirtual(V0);
  }

void blinkD13Led(int t) {
  digitalWrite(13, HIGH);
  delay(t);         
  digitalWrite(13, LOW);
  delay(t);         
  }

此部分在解決虛擬暫存器的值會被重置的問題

上方程式碼原本將 blinkRate 設為 100 拿來當做 LED 的閃爍頻率,然後將 APP 中的 Slider 綁定到虛擬暫存器 V0 ,後面就是用這個 V0 來做可變閃爍頻率的功能。

但是當 Arduino 重開機後,blickRate 會重置為 100 。

我們現在想要它重開機後自動去抓取 BLYNK 上 V0 虛擬暫存器的值,這樣就可以保留上次關機前最後的閃爍頻率了。實際作法簡而言知就是當 Arduino 重新連結上 BLYNK 時,會馬上去抓取目前存放在雲端上的值(V0) ,這樣就可以保證閃爍頻率是上次關機前的了,我們在程式碼中也可以看到,有用 BLYNK_CONNECTED()函數後我們的 blinkRate 就不需要給初始值。

注意,假如我們在  BLYNK_CONNECTED()函數裡面對虛擬暫存器 V0 執行 Blynk.syncVirtual(V0) 。 它的做法是會觸發 Arduino 內的 BLYNK_WRITE(V0) 函數

↓↓↓ 連結到部落格方針與索引 ↓↓↓

Blog 使用方針與索引