2017年9月13日 星期三

*3*nRF24L01+*3* 初遇 Blynk - 建立從 nRF24L01+ 到 ESP8266 再到 Blynk 的 SHT31 單點無線溫溼度傳輸物聯網


 網頁最後修改時間:2017/09/13

經過前面幾篇關於 nRF24L01+  的網頁的介紹,相信讀者對於 nRF24L01+ 基本的資料傳送與接收有了一定程度的了解。再者,番外篇也特別以範例介紹了 Arduino 開發板和 ESP8266 無線網路模組利用 AT 指令連線的過程,最後以 Blynk 手機 app 的一個 Project 範例作為結束,展示了兩個無線裝置之間的遠端數據如何進行通訊。

我們最終的目的:就是要利用 nRF24L01+ 構建無線溫溼度節點群組 (sensor nodes),群組中的主節點負責接收其他節點的溫溼度數據,利用有線或無線網路 (ESP8266, WiFi Shield ... etc ) 的方式向伺服器 (例如 Blynk Server 、ThingSpeak ... etc ) 傳送並儲存數據,能夠在手機隨時監控與查詢各節點的溫溼度。

在這篇,將完成單點溫溼度無線傳輸與數據上傳 Blynk Server 的部分。與之前討論不同的是:發射端加入休眠功能,不傳送的時後,nRF24L01+ 與 Arduino Nano 進入 Power Down 休眠模式節省電力;接收端的整合型 LCD 增加一個可處理儲存於 Flash 字串的顯示函式,並且修改程式以解決 Blynk Arduino 函式庫記憶體需求的問題。
網頁裡面的程式碼以及使用的接線圖,會稍微不同此系列前面網頁所用的,但都是以其為基礎做增加和修改,建議先閱讀前面幾篇再來讀這篇。

Blynk 函式庫使用在 Arduino IDE 開發環境時,若使用 Arduino UNO / Nano (以 ATmega328P 晶片為核心) 的開發板配合 ESP8266,當不特別注意程式裡記憶體 (SRAM) 使用的情況時,就會遇到記憶體不足造成無法連線 Blynk Server 的問題 ( login timeout );即便已經連線上路由器取得 IP
Blynk server login timeout
會發現這問題,是因為融入了 DS3231 RTC 模組與整合型 LCD 的程式碼之後才連不上 Blynk Server;而在一開始未加入這兩個模組的程式時,是正常運行的!

所以,為了解決這個問題,以凸顯出 Arduino 開發板配合 ESP8266,Blynk 函式庫比想像中需要更多的記憶體 (SRAM),所以多了一組接收端接線圖用來解釋與盡量解決這問題。

因此程式撰寫時需要特別去處理變數字串的問題,一但處理不當,耗費太多資源,Blynk Server 就會怎麼連也連不上,很搞人!但這也是官網為什麼建議直接使用 ESP8266 來做的原因。不過相比於 Arduino,ESP8266 IO 和 ADC 數目少,這也是為什麼還是有人執著用 Arduino 開發板配合 ESP8266 來做;只是更換為有更多記憶體晶片型式的 ( 例如,Mega2560...etc)。

反正不管如何,解決了 SRAM 記憶體佔用的問題,就能大致解決 Blynk Server login timeout 的困境,所以就讓我們開始吧!

參考接線圖:
這裡的接線圖跟此系列前幾篇網頁有些微不同,所以要注意接下來的各小節的說明,若之前已經接過線了,就把缺少的部分再補上去!

/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
[ !!! 重要接線須知 !!! ]
  • 接線圖裡面所使用的 ESP8266 無線網路模組,採用的是 ESP-01;但 ESP-01S 也可直接照著接 (其中,<EN> 可選擇接或不接 3V3 皆可 )。
  • 請注意到,下面接線圖中所使用的 nRF24L0+ 無線模組,都是有加裝轉接板的!如果不接轉接板跟著接線會燒掉無線模組。
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 發射端:
發射端沿用*2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸的接線圖,並在 <A0> 新增加了一顆狀態 LED。當這顆 LED 燈會亮起,表示數據傳送失敗,或是 (當使用電池時) 沒電了。

接線圖代號:sch-transmitter-00
參考接線圖 - 無線溫溼度發射端
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 發射端接線材料:
發射端實際接線完成參考圖
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 接收端:
接收端則有兩組接線圖 (可點擊看原圖),在圖左上角有接線圖的代號。

一組是沿用初遇 Blynk - {*2_1*nRF24L01+*2_1*}裡的接線圖。<A1> 增加了一個開關 (選擇是否輸出計算之後的 SHT31 溫溼度值)、 <D4> 增加一顆 LED 燈 (不能再使用板載的 <D13> LED 燈,會跟電路衝突) 和加入 nRF24L01+ 無線模組的線路。

另一組則是上面的線路再加上*2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸的接線圖,但移除了不需要的開關,直接在程式裡面選擇輸出的方式,以節省記憶體。

接線圖代號:sch-receiver-01
參考接線圖 - 無線溫溼度接收端 (無外接 RTC 和 LCD 版本)
接線圖代號:sch-receiver-02
參考接線圖 - 無線溫溼度接收端 (外接 RTC 與 LCD 版本)
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 接收端接線材料:
Blynk 安裝:
Blynk 的安裝分為 Arduino IDE 和手機 APP 兩個部分,並且需要在 Blynk App 中載入此篇網頁要用的 Blynk Project。怎麼做,請上初遇 Blynk - {*2_1*nRF24L01+*2_1*}網頁照步驟安裝,這裡不再贅述!

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* Blynk Porject 按鈕修改:
手機裡面的 Blynk Project 載入之後,修改最下面的那顆按鈕所連結的 PIN "D13" 改為 "D4",因為 Arduino Nano 的 <D13> 是 SPI 介面的 SCK 腳位,已經被 nRF24L01+ 所用,所以不可以去控制它,否則無線通訊會失靈!

Arduino 程式碼:
在接下來網頁裡面所使用的 Arduino 程式碼,會大肆修改來自*2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸網頁中的 nRF24one2oneSHT31Transmitter.ino nRF24one2oneSHT31Receiver.ino 這兩個程式,使用者可先至該網頁裡面下載。

由於 Blynk Server 的條件限制,對於要儲存的數據,每分鐘只會儲存一筆,但是依然可接收每分鐘多筆數據的輸入,而這些數據將會平均之後更新為此分鐘所代表的數值,所以對於發射端與接收端的程式會進行如下的修改與設計:

* 發射端:
  • 限制發射端每 15 秒傳送一次溫溼度數據 (raw data),其餘的時間 Arduino Nano 和 nRF24L01+ 進入睡眠狀態 (POWER DOWN MODE) 節省電量,非常適合用於電池供電的場合。
  • 在通電的同時,可利用 <A1> 接腳選擇是否要進一步將數據計算,將數據計算成實際的溫溼度值。一般在經過測試階段之後,這功能就可以不再需要。
  • 接腳 <A0> 接了一顆 LED 指示燈。只有當數據成功傳送出去時這一顆 LED 燈才會是熄滅的;亮起,表示通訊參數匹配出問題,無法正常傳送數據,或是接收端不存在。
* 接收端:
  • 限制接收端每秒執行四次數據接收與上傳
  • 取消外部接腳輸出選擇功能
  • 如果開啟整合型 LCD 顯示,日期與時間每秒鐘更新一次,溫度數據一經接收後計算就馬上顯示
  • 如果開啟 DEBUG 模式,只輸出計算之後的溫溼度值
  • 減少 Arduino Nano SRAM 的使用量
    • 移除 BLYNK_LOG 字串輸出方式
    • 整合型 LCD 支援處理儲存於 Flash 字串的顯示函式
    • 重新整理程式碼
  • 三種輸出模式選擇
    • Blynk debug 與自訂訊息輸出
    • DS3231 RTC 模組與整合型 LCD 訊息輸出
    • 取消上面兩個輸出,只留下預設上傳到 Blynk Server 的溫溼度值
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 發射端程式碼:
在不要求電量限制的環境下,(使用原網頁的接線圖) nRF24one2oneSHT31Transmitter.ino 程式碼只需要修改 loop() 裡用到的 interval 就可以
/** time */
unsigned long previousMills = 0;
const unsigned long interval = 16000L;         // milliseconds
//----------------------------------------- end of time

但!發射端與接收端不同的是,它通常都是放置在遙遠的另一端,而另一端剛好有插頭的情況也都不多,所以當在使用電池供應的場合中,只要微控制器不工作的時候,就要考慮讓微控制器進入休眠的狀態以延長電池使用的壽命。

所以原來的 nRF24one2oneSHT31Transmitter.ino 的程式配合 sch-transmitter-00 接線方式的程式碼,加入了狀態 LED 和休眠的功能之後,修改之後的程式碼如下

發射端程式碼下載

其中就新增休眠的函式部分做大概說明,因為這需要對照 ATMega328 的資料手冊來看,否則看不懂,所以我只針對其中比較重要的變數做說明。只要掌握這幾個變數的設定方式,休眠時間就可以自行修改;但其他的,就請自行消化了。

prescalar:位於 setSleepInterval() 函式中,用來設定使用的基本休眠時間間隔
0:16ms、 1:32ms、2:64ms、3:0.125 s
4:0.25 s、5:0.5 s、 6:1.0 s
7:2.0 s、  8:4.0 s、 9:8.0 s
sleep_cycles_remaining:位於 gotoSleep() 函式裡,用來設定總休眠時間

以下面的程式碼做例子,基本休眠時間為 8 秒,循環兩次 (16秒) 後叫醒 Arduino Nano 工作。利用這個方式,要讓微控制器睡多久再工作一次,都只是這兩個變數設定的問題了。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*******************************************
* sleep
*******************************************/
ISR(WDT_vect) {
    --sleep_cycles_remaining;
}

void setSleepInterval() {

    uint8_t prescalar = 9;      

    uint8_t wdtcsr = prescalar & 7;
    if ( prescalar & 8 )
        wdtcsr |= _BV(WDP3);
    MCUSR &= ~_BV(WDRF);
    WDTCSR = _BV(WDCE) | _BV(WDE);
    WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);
}

void gotoSleep() {

    sleep_cycles_remaining = 2;
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    sleep_enable();
    WDTCSR |= _BV(WDIE);
    while( sleep_cycles_remaining ) {
        sleep_mode();
    }
    sleep_disable();
    WDTCSR &= ~_BV(WDIE);
}

另外,千萬別忘了!nRF24L01+ 也可以 powerDown() 節省電力,在休眠之前可以關掉。

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 接收端程式碼:
接收端的程式可以是初遇 Blynk - {*2_1*nRF24L01+*2_1*}網頁裡修改後的那個程式再加入 nRF24L01+ 硬體的程式修改而成,這是最簡單可完成的部分!但是除非與電腦連接,否則得不到任何除錯訊息或相關輸出。

另一個則是使用*2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸網頁的 nRF24one2oneSHT31Receiver.ino 程式來做修改;也就是加入 ESP8266 與 Blynk 函式庫的支援。這部分會因為 Arduino Nano SRAM 記憶體限制的關係,必須重新考慮程式碼的撰寫方式,以增加 SRAM 的容量;這部分會在後面特別討論我所使用的方法。

** sch-receiver-01 接線搭配 初遇 Blynk - {*2_1*nRF24L01+*2_1*} 程式碼修改 **
----------------------------------------------------------------------------------------------------------
原網頁的程式,模擬 SHT31 的溫溼度值並傳送給 Blynk Server,所以少了 nRF24L01+ 接收和 SHT31 溫溼度模組的程式碼。接下來,讓我們把這些缺少的東西補進去

接收端程式碼下載

上面的程式碼,加入了 nRF24L01+ 無線模組接收的部分,以及 SHT31-D 溫濕度模組取值的函式,更名 insertTRH() 這個 Callback 函式的名稱為 updateTRH() 與裡面的程式碼。

由於我們使用了 Blynk 函式庫,因此程式撰寫上有一些限制。也就是說,setup() 函式處理了所有硬體零件初始化與設定的動作,任何需要週期性確認的東西,必須使用 BlynkTimer 來處理,不要直接寫進到 loop() 函式裡面。所以在裡面,我們設定一個每 100 ms 執行一次的函式 updateTRH(),負責檢查 nRF24L01+ 接收的狀態和計算出溫溼度值,溫溼度值可以經由外部接腳選擇是否要由 debugSerial 輸出。一但開機的同時,接腳 <A1> 接地就會進入 Blynk 除錯模式,執行時後就會將 Blynk 訊息輸出到 debugSerial 定義的通訊埠。

最後,需要注意一點的就是:Blynk.begin() 這一個函式在執行的時候,沒有連上線的時候是絕不會返回的!也就是說,這一行之後的程式碼都不會先執行,整個程式會在這裡停下來,一直等待 ESP8266 連上 Blynk Server,所以不要以為接下來的程式碼會先執行;因為在撰寫其他 ESP8266 standalone 程式時,連線指令發出之後下面的程式碼會繼續下去不會阻斷 (除非程式裡面自己阻斷),所以這點要注意,避免程式執行時,不曉得是停在無線連線是還是硬體初始化上。

講得有點多了,現在編譯與上傳這個程式到你的 Arduino Nano 去試試吧!

這個程式在執行上沒有任何的問題,但不知道眼尖的你(妳),有沒特別注意到編譯成功之後所輸出的記憶體使用的情況?

有沒看到,這個簡單的程式,Sketch 使用了 76% 的儲存空間 (Flash) 和 65% 的 SRAM 記憶體 。這個兩個數值請先記在心中,當接下來加入整合型 LCD 和 DS3231 RTC 模組的程式之後,可以想像會增加多少,而這又會對程式執行時造成什麼影響?
simpleBlynknRF24one2oneSHT31Receiver.ino 編譯輸出
打開 UART 軟體,可以看到 DEBUG 模式下 debugSerial 的輸出與接收計算後的溫溼度值
simpleBlynknRF24one2oneSHT31Receiver.ino DEBUG 模式下 debugSerial 的輸出
上面輸出有用 [中括號] 開頭的就是 Blynk 輸出的訊息,其他的是程式裡另外加上去的。

/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
[ 編譯大小 ]
由於程式裡面 auth, ssid, pass 這三個變數大家都不一樣,因此編譯出來的大小也會不同 (但是不會差太多),特此說明!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*

** sch-receiver-02 接線搭配*2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸程式碼修改 **
-------------------------------------------------------------------------------------------------------------------
接下來的程式也可以由 simpleBlynknRF24one2oneSHT31Receiver.ino 來改,但反正我都給完整程式,所以其實也沒差!但如果真的想學習的話,這部分應該要想一下,如果是你(妳),你(妳)會怎麼改 ?

下面給出第一個版本的程式,直接用 *2*nRF24L01+*2* 的 nRF24one2oneSHT31Receiver.ino 加入 Blynk 使用 ESP8266 的程式碼,未作任何其他的處理

接收端程式碼下載

編譯程式與上傳後,接著看一下編譯之後的結果
BlynkSingleNodeWirelessReceiver00.ino 編譯結果
可以清楚看到,Sketch 使用了 86% 的儲存空間和 75% 的 SRAM 記憶體,兩者都大幅上升 了10%,但是還是距離 100% 還有一段距離,執行應該是還不至於會產生問題??? 那麼實際執行之後的結果如何呢???
BlynkSingleNodeWirelessReceiver00.ino DEBUG 模式下 debugSerial 的輸出
正常的輸出到 Ready 之後,一但無法與 Blynk Server 連線,大概 10 ... 15 秒之間就會出現 Login timeout 的訊息。一但這訊息出現,就開始麻煩了!

讀者可以看到,ESP8266 確實是連上指定的 SSID 並且取得 IP,但是就是一直連不上 Blynk Server,很奇怪吧!

對,我也是這樣覺得!

不過,上了 Blynk 找了一些討論後發現,最有可能的是記憶體不足造成的
我的直覺認為,應該是與 Blynk 函式庫在連線時會需要大量的 SRAM 導致,剩餘 25% 的空間還是不足夠的,還要更多!

其實沒錯,嘗試過幾種方式的修改,即便 SRAM 已經降低到 68% 做測試,依然無法成功連線
BlynkSingleNodeWirelessReceiver02.ino 編譯結果
看到這結果真的令人很傷心,還是連不上!

修改到這個地步,接下來就是修改輸出選擇的方式;不再使用接腳選擇輸出方式,而改用前置處理器讓程式編譯時選擇需要的部分,以這樣的方式減少 Flash 和 SRAM 記憶體的使用量。即使這樣只能二選一,但是當所有需要的訊息都可以在整合型 LCD 或是 debugSerial 各別輸出顯示時,為什麼還需要兩個一起做同樣的動作呢 ?

接收端程式碼下載

切換上面程式碼註解 line 9 或 line 10 (只能二選一) 其中一行,然後進行編譯可得到下面結果
BlynkSingleNodeWirelessReceiver03.ino 選擇開啟 DEBUG 模式選項
BlynkSingleNodeWirelessReceiver03.ino 選擇開啟 USEIICLCD 模式選項
分別切換選項編譯之後,其結果已經達到之前可順利動作的 65% SRAM 使用量以下。接著下來上傳程式到 Arduino 開發板,來看看實際運作的動作!

Blynk 單點無線溫溼度測試:
接下來的測試需要發射端與接收端兩組裝置,依照接收端選擇的模式分別來做

* 發射端
  • 接線圖:sch-transmitter-00
  • 程式碼:nRF24one2oneSHT31TransmitterWithSleep.ino
* 接收端
  • 接線圖:sch-receiver-01 (DEBUG 模式), sch-receiver-02 (USEIICLCD 模式)
  • 程式碼:BlynkSingleNodeWirelessReceiver03.ino
Note:拍攝的時候,忘記在軟體選擇輸出解析度為 1080P 了,所以影片只有預設值 720P,這點先說聲抱歉!但該呈現的部分應該都在影片上可以看到。

Blynk 手機專案檔的效果跟 初遇 Blynk - {*2_1*nRF24L01+*2_1*} 網頁中展示的沒什麼不同,所以就沒有特別放在影片中了;一但接收端連線成功到 Blynk Server,Blynk 手機端就可以開始接收到資料。

兩個影片我都加上了簡單的字幕來做說明,因此就不再下面贅述相同的東西。測試時要注意的事項我列在下面:
  • 發射端可以使用 PC 的 USB 供電
  • 接收端若是使用 PC 端的 USB 供電,當發射端通電開始通訊的時候,有可能會造成接收端重置;因此影片中才在上傳程式之後使用外接電源供電。
  • ESP8266 與 Arduino Nano 在影片中是使用兩個電源,因此供電有時間差。若是兩個裝置都使用同一個外接電源時,由於同時供電的關係,有可能會造成 Arduino Nano 太快傳送 AT 指令給 ESP8266,而 ESP8266 還沒準備好接收指令 ... 等類的連線失敗的問題;這時就要考慮在程式中適當的地方加入 delay(),讓裝置完全 ready 後再作後續控制的動作。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 接收端 (DEBUG 模式):

/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
接收端 (USEIICLCD 模式)
影片中整合型 LCD 的可變電阻接線不是太牢固,為了讓影片清楚一點,需要不時做調整,不好意思!

結論:

經過幾天的網頁的撰寫與程式測試下來,發現 Blynk 雖然好用,但是以 ATMega328  晶片作為核心的 Arduino 開發板,配合 ESP8266 來做開發會面臨 SRAM 短缺的問題;因為 ATMega328 只有 2KBytes 的 SRAM。

經過多次的測試之後發現,類似的 Arduino 開發板在編譯之後,至少還需要 SRAM 有 34% 的空間,否則就容易遇到 Login timeout 的問題;因此,以網頁中的開發方式,在撰寫程式時,應該盡量將字串儲存到 Flash,並且減少變數的宣告與使用,才能節省 SRAM 記憶體的使用。

Blynk 函式庫裡面有提供一個字串輸出函式:BLYNK_LOG#(),但是在上面的程式裡面都沒有看到,WHY ??? 原因是,測試過的結果顯示,將 debugSerial.print() 或是 debugSerial.println() 全部改成 BLYNK_LOG#() 函式來做輸出,上面其中一個程式的 SRAM 記憶體增加了 3% 的使用量。這也就是上面的程式完全看不到 BLYNK_LOG#() 的原因!

雖然這樣測試下來,可以知道官網為什麼建議要使用 ESP8266 standalone 來開發 Blynk 程式,而不是網頁中所使用的方式。但是,上面有提到過,個人需求和應用層面不同,有利就有弊,不是這樣做就不好,而是需要考量的東西會比較多,反而會比較複雜,考驗能力與功力!

單節點無線溫溼度傳輸的部分已經完成,多節點的部分就不會再使用 Arduino Nano 配合 ESP8266 來做了,因為程式空間與記憶體都已明顯不夠用了。接續的,應該會考慮 ESP8266 standalone 的方式,像是相容於 NodeMCU 開發板類型的 ESP8266 開發板來做,這種比較小巧,類似 Arduino Nano 開發板,接腳是排針不是插孔,做實驗比較方便。不過,因為電壓變成 3.3V,而且又是多點通訊,所以要去買料與準備料件了,就先到這!



<< 部落格相關網頁 >>

沒有留言:

張貼留言