網頁最後修改時間:2017/09/13
![]() |
我們最終的目的:就是要利用 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 |
所以,為了解決這個問題,以凸顯出 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
![]() |
參考接線圖 - 無線溫溼度發射端 |
* 發射端接線材料:
- Arduino Nano / Uno
- 一些杜邦線
- LED 燈
- R2 電阻 ( 假設 LED Vf = 2VDC, R 值取 150Ω 以上即可)
- 麵包板或Arduino Nano 擴充底板
- SHT31-D 溫溼度感測器
- NRF24L01+ (功率加強版本) 無線模組 (含轉接板)
- NRF24L01+ + PA+LNA 遠距離無線模組(含轉接板與 SMA 天線)
![]() |
發射端實際接線完成參考圖 |
* 接收端:
接收端則有兩組接線圖 (可點擊看原圖),在圖左上角有接線圖的代號。
一組是沿用初遇 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 版本) |
* 接收端接線材料:
- Arduino Nano / Uno
- 一些杜邦線
- LED 燈
- R2 (和 R6) 電阻 ( 假設 LED Vf = 2VDC, R 值取 150Ω 以上即可)
- R4 和 R5 (R7 和 R8) 可採用 四通道雙向邏輯電壓準位轉換模組 或使用分壓電路 ( 1K 和 2 K 電阻 )
- 麵包板或Arduino Nano 擴充底板
- V2.1 版 { 萬物皆聯網-ESP8266 IoT(Internet of Things)入門學習套件 }
- 裡面有 USB 轉 TTL模組、麵包板電源、杜邦線和 ESP8266, ESP-01S ( Ai-Mod, AT v1.2.0.0 based on SDK v1.5.4.1 )
- 高精度即時時鐘 DS3231 + 32KByte EEPROM二合一模組
- {5V}整合型 {4/8BIT, IIC, 4SPI}1602英文字型藍底白字 LCD 螢幕
- NRF24L01+ (功率加強版本) 無線模組 (含轉接板)
- NRF24L01+ + PA+LNA 遠距離無線模組(含轉接板與 SMA 天線)
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 和休眠的功能之後,修改之後的程式碼如下
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Wire.h> | |
#include <SPI.h> | |
#include <RF24.h> | |
#include <printf.h> | |
// sleep | |
#include <avr/sleep.h> | |
#include <avr/power.h> | |
/** SHT31-D */ | |
const int SHT31_IIC_ADDRESS = 0x44; | |
unsigned char rawdata[6]; | |
//----------------------------------------- end of SHT31-D | |
/** nRF24L01+ */ | |
#define CEPIN 7 | |
#define CSNPIN 8 | |
// RF 頻道 | |
const uint8_t RF_CHANNEL = 100; | |
RF24 rf24( CEPIN, CSNPIN ); | |
// 通訊位址 | |
const uint8_t receiverAddress[] = "Node0"; | |
// status LED | |
#define STATUSLED A0 | |
//----------------------------------------- end of eRF24L01+ | |
/** debug */ | |
#define DEBUGOUTPUT A1 | |
bool isdebug; | |
//----------------------------------------- end of SHT31-D | |
/** sleep */ | |
volatile uint8_t sleep_cycles_remaining; | |
//----------------------------------------- end of sleep | |
void setup() { | |
Serial.begin( 115200 ); | |
printf_begin(); | |
pinMode( DEBUGOUTPUT, INPUT_PULLUP ); | |
pinMode( STATUSLED, OUTPUT ); | |
isdebug = !digitalRead( DEBUGOUTPUT ); | |
/** SHT31-D */ | |
Wire.begin(); | |
delay(10); | |
if( !checki2cdevice( SHT31_IIC_ADDRESS ) ) { | |
if( isdebug) Serial.println( F("SHT31-D not found!") ); | |
while(1) delay(1); | |
} | |
softResetSHT31(); | |
/** nRF24L01+ */ | |
rf24.begin(); | |
if( !rf24.isChipConnected() ) { | |
if( isdebug ) Serial.println( F("nRF24L01+ not found!") ); | |
while(1) delay(1); | |
} | |
// RF 相關參數設定 | |
rf24.setPayloadSize( sizeof(rawdata) ); | |
rf24.setChannel( RF_CHANNEL ); | |
rf24.setPALevel( RF24_PA_HIGH ); | |
rf24.setDataRate( RF24_250KBPS ); | |
rf24.openWritingPipe( receiverAddress ); | |
// 除錯模式下,輸出無線模組的設定 | |
if( isdebug ) rf24.printDetails(); | |
/** setup watchdog */ | |
setSleepInterval(); // 16 sec | |
delay(1000); | |
} | |
void loop() { | |
/** 取得溫濕度的 raw data */ | |
memset( &rawdata, 0, sizeof(rawdata) ); | |
if( readRawDataSHT31() ) { | |
if( !rf24.write( &rawdata, sizeof(rawdata) ) && isdebug ) { | |
// transmit failed | |
digitalWrite( STATUSLED, HIGH ); // this toggles the status LED at pin A0 to show trandmit failed | |
Serial.println( F(" Tx failed") ); | |
} else { | |
digitalWrite( STATUSLED, LOW ); // transmit was successful so make sure status LED is off | |
} | |
if( isdebug ) { | |
// 計算溫溼度值,不確認 CRC 正確性 | |
int temp = (rawdata[0] * 256) + rawdata[1]; | |
float cTemp = -45.0 + (175.0 * temp / 65535.0); | |
float fTemp = (cTemp * 1.8) + 32.0; | |
float humidity = (100.0 * ((rawdata[3] * 256.0) + rawdata[4])) / 65535.0; | |
Serial.print( F("Temp: ") ); | |
Serial.print( cTemp ); | |
Serial.print( " C ("); | |
Serial.print( fTemp ); | |
Serial.print( F(" F), Humi: ")); | |
Serial.print( humidity ); | |
Serial.println( "%RH"); | |
//** 如果沒有下面這一行,則不會完全輸出完 Serial.print(ln) 的所有字元 | |
//** 就直接進入到 sleep 的處理函式中了。 | |
Serial.flush(); | |
} | |
} | |
//** prepare to sleep | |
rf24.powerDown(); | |
gotoSleep(); | |
rf24.powerUp(); | |
} | |
/** | |
* [checki2cdevice 檢查 i2c 裝置是否存在或接線是否良好] | |
* | |
* @param i2caddr [i2c 裝置地址] | |
* | |
* @return [true/false] | |
*/ | |
bool checki2cdevice( uint8_t i2caddr ) { | |
// 呼叫此函示之前, Wire 必須先初始化 | |
Wire.beginTransmission( i2caddr ); | |
if( Wire.endTransmission() == 0 ) return true; | |
return false; | |
} | |
/**************************************************************************************** | |
* SHT31-D | |
* **************************************************************************************/ | |
/** | |
* [readRawDataSHT31 讀取 SHT31-D 未經計算的原始資料] | |
* | |
* @return [取得 6-byte 的資料] | |
*/ | |
bool readRawDataSHT31() { | |
Wire.beginTransmission( SHT31_IIC_ADDRESS ); | |
Wire.write( 0x2C ); | |
Wire.write( 0x06 ); | |
Wire.endTransmission(); | |
delay(300); | |
Wire.beginTransmission( SHT31_IIC_ADDRESS ); | |
Wire.endTransmission(); | |
Wire.requestFrom( SHT31_IIC_ADDRESS, 6 ); | |
if( Wire.available() == 6 ) { | |
rawdata[0] = Wire.read(); rawdata[1] = Wire.read(); rawdata[2] = Wire.read(); | |
rawdata[3] = Wire.read(); rawdata[4] = Wire.read(); rawdata[5] = Wire.read(); | |
return true; | |
} | |
return false; | |
} | |
void softResetSHT31() { | |
Wire.beginTransmission( SHT31_IIC_ADDRESS ); | |
Wire.write( 0x30 ); | |
Wire.write( 0xA2 ); | |
Wire.endTransmission(); | |
delay(10); | |
} | |
//------------------------------------------------------------------------------------- | |
/**************************************************************************************** | |
* 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); | |
} |
其中就新增休眠的函式部分做大概說明,因為這需要對照 ATMega328 的資料手冊來看,否則看不懂,所以我只針對其中比較重要的變數做說明。只要掌握這幾個變數的設定方式,休眠時間就可以自行修改;但其他的,就請自行消化了。
prescalar:位於 setSleepInterval() 函式中,用來設定使用的基本休眠時間間隔
0:16ms、 1:32ms、2:64ms、3:0.125 ssleep_cycles_remaining:位於 gotoSleep() 函式裡,用來設定總休眠時間
4:0.25 s、5:0.5 s、 6:1.0 s
7:2.0 s、 8:4.0 s、 9:8.0 s
以下面的程式碼做例子,基本休眠時間為 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 溫溼度模組的程式碼。接下來,讓我們把這些缺少的東西補進去
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Wire.h> | |
#include <SPI.h> | |
#include <RF24.h> | |
/** Blynk */ | |
#include <SoftwareSerial.h> | |
SoftwareSerial debugSerial(2, 3); // RX, TX | |
/* Comment this out to disable prints and save space */ | |
#define BLYNK_PRINT debugSerial | |
#include <ESP8266_Lib.h> | |
#include <BlynkSimpleShieldEsp8266.h> | |
// You should get Auth Token in the Blynk App. | |
// Go to the Project Settings (nut icon). | |
char auth[] = "YourAuthToken"; | |
// Your ESP8266 baud rate: | |
#define ESP8266_BAUD 115200 | |
ESP8266 wifi(&Serial); | |
// Please note that a single BlynkTimer object | |
// allows to schedule up to 16 timers. | |
BlynkTimer timer; | |
//----------------------------------------- end of Blynk | |
/** ESP8266 */ | |
// Your WiFi credentials. | |
// Set password to "" for open networks. | |
char ssid[] = "YourNetworkName"; | |
char pass[] = "YourPassword"; | |
//----------------------------------------- end of ESP8266 | |
/** nRF24L01+ */ | |
#define CEPIN 7 | |
#define CSNPIN 8 | |
// RF 頻道 | |
const uint8_t RF_CHANNEL = 100; | |
RF24 rf24( CEPIN, CSNPIN ); | |
// 通訊位址 | |
const uint8_t thisNodeAddress[] = "Node0"; | |
unsigned char rawdata[6]; // 無線接收到的 SHT31 原始數據 | |
//----------------------------------------- end of nRF24L01+ | |
/** debug */ | |
const uint8_t DEBUGOUTPUT = A1; | |
bool isdebug; | |
//----------------------------------------- end of debug | |
/** | |
* [updateTRH 上傳 SHT31 溫溼度數據] | |
* | |
*/ | |
void updateTRH() { | |
if( rf24.available() ) { | |
memset( &rawdata, 0, sizeof(rawdata) ); | |
rf24.read( &rawdata, sizeof( rawdata ) ); | |
/** 計算 SHT31-D 溫溼度數據 */ | |
int temp = (rawdata[0] * 256) + rawdata[1]; | |
float cTemp = -45.0 + (175.0 * temp / 65535.0); | |
float fTemp = (cTemp * 1.8) + 32.0; | |
float humidity = (100.0 * ((rawdata[3] * 256.0) + rawdata[4])) / 65535.0; | |
/** push data to Blynk Server */ | |
Blynk.virtualWrite( V1, cTemp ); | |
Blynk.virtualWrite( V2, humidity ); | |
/** UART 溫溼度資料輸出 */ | |
if( isdebug ) { | |
// 計算溫溼度值,不確認 CRC 正確性 | |
debugSerial.print( F("Temp: ") ); | |
debugSerial.print( cTemp ); | |
debugSerial.print( " C ("); | |
debugSerial.print( fTemp ); | |
debugSerial.print( F(" F), Humi: ")); | |
debugSerial.print( humidity ); | |
debugSerial.println( "%RH"); | |
debugSerial.flush(); | |
} | |
} | |
} | |
void setup() { | |
// Set ESP8266 baud rate | |
Serial.begin( ESP8266_BAUD ); | |
delay(10); | |
debugSerial.begin( ESP8266_BAUD ); | |
delay(10); | |
/** Blynk + ESP8266AT */ | |
Blynk.begin( auth, wifi, ssid, pass ); | |
timer.setInterval( 100L, updateTRH ); | |
//----------------------------------- | |
Wire.begin(); | |
pinMode( DEBUGOUTPUT, INPUT_PULLUP ); | |
isdebug = !digitalRead( DEBUGOUTPUT ); | |
/** nRF24L01+ */ | |
rf24.begin(); | |
if( !rf24.isChipConnected() ) { | |
if( isdebug ) debugSerial.println( F("nRF24L01+ not found!") ); | |
while(1) delay(1); | |
} | |
// RF 相關參數設定 | |
rf24.setPayloadSize( sizeof(rawdata) ); | |
rf24.setChannel( RF_CHANNEL ); | |
rf24.setPALevel( RF24_PA_HIGH ); | |
rf24.setDataRate( RF24_250KBPS ); | |
rf24.openReadingPipe( 1, thisNodeAddress ); | |
rf24.startListening(); | |
} | |
void loop() { | |
Blynk.run(); | |
timer.run(); | |
} |
上面的程式碼,加入了 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 編譯輸出 |
![]() |
simpleBlynknRF24one2oneSHT31Receiver.ino DEBUG 模式下 debugSerial 的輸出 |
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
[ 編譯大小 ]
由於程式裡面 auth, ssid, pass 這三個變數大家都不一樣,因此編譯出來的大小也會不同 (但是不會差太多),特此說明!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
** sch-receiver-02 接線搭配*2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸程式碼修改 **
-------------------------------------------------------------------------------------------------------------------
接下來的程式也可以由 simpleBlynknRF24one2oneSHT31Receiver.ino 來改,但反正我都給完整程式,所以其實也沒差!但如果真的想學習的話,這部分應該要想一下,如果是你(妳),你(妳)會怎麼改 ?
下面給出第一個版本的程式,直接用 *2*nRF24L01+*2* 的 nRF24one2oneSHT31Receiver.ino 加入 Blynk 使用 ESP8266 的程式碼,未作任何其他的處理
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Wire.h> | |
#include <SPI.h> | |
#include <RF24.h> | |
#include <RTClib.h> | |
/** Blynk */ | |
#include <SoftwareSerial.h> | |
SoftwareSerial debugSerial(2, 3); // RX, TX | |
/* Comment this out to disable prints and save space */ | |
#define BLYNK_PRINT debugSerial | |
#include <ESP8266_Lib.h> | |
#include <BlynkSimpleShieldEsp8266.h> | |
// You should get Auth Token in the Blynk App. | |
// Go to the Project Settings (nut icon). | |
char auth[] = "YourAuthToken"; | |
// Your ESP8266 baud rate: | |
#define ESP8266_BAUD 115200 | |
ESP8266 wifi(&Serial); | |
// Please note that a single BlynkTimer object | |
// allows to schedule up to 16 timers. | |
BlynkTimer timer; | |
//----------------------------------------- end of Blynk | |
/** ESP8266 */ | |
// Your WiFi credentials. | |
// Set password to "" for open networks. | |
char ssid[] = "YourNetworkName"; | |
char pass[] = "YourPassword"; | |
//----------------------------------------- end of ESP8266 | |
/** 整合型 LCD */ | |
#define LCD_IIC_ADDRESS 0x3C | |
char lcdbuf[17]; | |
//----------------------------------------- end of 整合型 LCD | |
/** DS3231 */ | |
RTC_DS3231 rtc; | |
//----------------------------------------- end of DS3231 | |
/** nRF24L01+ */ | |
#define CEPIN 7 | |
#define CSNPIN 8 | |
// RF 頻道 | |
const uint8_t RF_CHANNEL = 100; | |
RF24 rf24( CEPIN, CSNPIN ); | |
// 通訊位址 | |
const uint8_t thisNodeAddress[] = "Node0"; | |
unsigned char rawdata[6]; // 無線接收到的 SHT31 原始數據 | |
//----------------------------------------- end of nRF24L01+ | |
/** debug */ | |
const uint8_t DEBUGOUTPUT = A1; | |
const uint8_t IICLCDOUTPUT = A2; | |
bool isdebug, isiiclcd; | |
//----------------------------------------- end of debug | |
/** callback functions for BlynkTimer */ | |
/** | |
* [rtcUpdate RTC 模組的時間更新 Callback 函式] | |
* | |
*/ | |
void updateRTC() { | |
// 更新時間顯示 | |
static uint8_t idx = 0; | |
DateTime now = rtc.now(); | |
switch( ++idx ) { | |
case 1: | |
sprintf( lcdbuf, "*-*%4d/%02d/%02d*-*", now.year(), now.month(), now.day() ); | |
displayCharOnLCD( 1, 1, lcdbuf, 16 ); | |
break; | |
case 3: | |
case 4: | |
case 5: | |
sprintf( lcdbuf, "*--*%02d:%02d:%02d*--*", now.hour(), now.minute(), now.second() ); | |
displayCharOnLCD( 1, 1, lcdbuf, 16 ); | |
if( idx == 5 ) idx = 0; | |
default: | |
; | |
} | |
} | |
/** | |
* [updateTRH 上傳 SHT31 溫溼度數據] | |
* | |
*/ | |
void updateTRH() { | |
if( rf24.available() ) { | |
memset( &rawdata, 0, sizeof(rawdata) ); | |
rf24.read( &rawdata, sizeof( rawdata ) ); | |
/** 計算 SHT31-D 溫溼度數據 */ | |
int temp = (rawdata[0] * 256) + rawdata[1]; | |
float cTemp = -45.0 + (175.0 * temp / 65535.0); | |
float fTemp = (cTemp * 1.8) + 32.0; | |
float humidity = (100.0 * ((rawdata[3] * 256.0) + rawdata[4])) / 65535.0; | |
/** push data to Blynk Server */ | |
Blynk.virtualWrite( V1, cTemp ); | |
Blynk.virtualWrite( V2, humidity ); | |
/** UART 溫溼度資料輸出 */ | |
if( isdebug ) { | |
// 計算溫溼度值,不確認 CRC 正確性 | |
debugSerial.print( F("Temp: ") ); | |
debugSerial.print( cTemp ); | |
debugSerial.print( " C ("); | |
debugSerial.print( fTemp ); | |
debugSerial.print( F(" F), Humi: ")); | |
debugSerial.print( humidity ); | |
debugSerial.println( "%RH"); | |
debugSerial.flush(); | |
} | |
} | |
} | |
//----------------------------------------- end of callback ... | |
void setup() { | |
// Set ESP8266 baud rate | |
Serial.begin( ESP8266_BAUD ); | |
delay(10); | |
debugSerial.begin( ESP8266_BAUD ); | |
delay(10); | |
Wire.begin(); | |
pinMode( DEBUGOUTPUT, INPUT_PULLUP ); | |
pinMode( IICLCDOUTPUT, INPUT_PULLUP ); | |
isdebug = !digitalRead( DEBUGOUTPUT ); | |
isiiclcd = !digitalRead( IICLCDOUTPUT ); | |
/** RTC DS3231 */ | |
// 須先完成時間的調整 | |
if( !checki2cdevice( DS3231_ADDRESS ) ) { | |
if( isdebug ) debugSerial.println( F("DS3231 RTC Module not found!") ); | |
while(1) delay(1); | |
} | |
rtc.begin(); | |
/** 整合型 LCD (IIC 模式) */ | |
if( isiiclcd ) { | |
if( !checki2cdevice( LCD_IIC_ADDRESS ) ) { | |
if( isdebug ) debugSerial.println( F("LCD not found!") ); | |
while(1) delay(1); | |
} | |
// 整合型 LCD 初始化 @ I2C 模式 | |
initLCD(); | |
delay(100); | |
clearLCD(); | |
delay(100); | |
displayCharOnLCD( 1, 1, "**LCD/RTC ready*", 16 ); | |
delay(1000); | |
} | |
/** nRF24L01+ */ | |
rf24.begin(); | |
if( !rf24.isChipConnected() ) { | |
if( isdebug ) debugSerial.println( F("nRF24L01+ not found!") ); | |
while(1) delay(1); | |
} | |
// RF 相關參數設定 | |
rf24.setPayloadSize( sizeof(rawdata) ); | |
rf24.setChannel( RF_CHANNEL ); | |
rf24.setPALevel( RF24_PA_HIGH ); | |
rf24.setDataRate( RF24_250KBPS ); | |
rf24.openReadingPipe( 1, thisNodeAddress ); | |
rf24.startListening(); | |
if( isiiclcd ) { | |
displayCharOnLCD( 2, 1, "*nRF24L01+ ready", 16 ); | |
delay(800); | |
clearLCD(); | |
delay(100); | |
// 每一秒更新螢幕上 RTC 日期與時間 | |
timer.setInterval( 1000L, updateRTC ); | |
} | |
/** Blynk + ESP8266AT */ | |
displayCharOnLCD( 1, 1, "**Blynk Server**", 16 ); | |
displayCharOnLCD( 2, 1, "connecting... ", 16 ); | |
Blynk.begin( auth, wifi, ssid, pass ); | |
if( Blynk.connected() ) { | |
displayCharOnLCD( 2, 1, "connected ", 16 ); | |
delay(900); | |
clearLCD(); | |
delay(100); | |
} | |
// 每 100 ms 檢查一次是否有無線數據傳送進來 | |
timer.setInterval( 100L, updateTRH ); | |
//----------------------------------- | |
} | |
void loop() { | |
Blynk.run(); | |
timer.run(); | |
} | |
bool checki2cdevice( uint8_t i2caddr ) { | |
Wire.beginTransmission( i2caddr ); | |
if( Wire.endTransmission() == 0 ) return true; | |
return false; | |
} | |
/**************************************************************************************** | |
* LCD | |
****************************************************************************************/ | |
void initLCD() { | |
Wire.beginTransmission( LCD_IIC_ADDRESS ); | |
Wire.write( 0x00 ); | |
Wire.write( 0x38 ); | |
Wire.write( 0x0C ); | |
Wire.write( 0x01 ); | |
Wire.write( 0x06 ); | |
Wire.endTransmission(); | |
} | |
void clearLCD() { | |
Wire.beginTransmission( LCD_IIC_ADDRESS ); | |
Wire.write( 0x80 ); | |
Wire.write( 0x01 ); | |
Wire.endTransmission(); | |
} | |
void displayCharOnLCD( int line, int column, const char *dp, unsigned char len ) { | |
unsigned char i; | |
Wire.beginTransmission( LCD_IIC_ADDRESS ); | |
Wire.write( 0x80 ); | |
Wire.write( 0x80 + (line - 1 ) * 0x40 + ( column - 1 ) ); | |
Wire.write( 0x40 ); | |
for( i = 0; i < len; i++) { | |
Wire.write( *dp++ ); | |
} | |
Wire.endTransmission(); | |
} |
編譯程式與上傳後,接著看一下編譯之後的結果
![]() |
BlynkSingleNodeWirelessReceiver00.ino 編譯結果 |
![]() |
BlynkSingleNodeWirelessReceiver00.ino DEBUG 模式下 debugSerial 的輸出 |
讀者可以看到,ESP8266 確實是連上指定的 SSID 並且取得 IP,但是就是一直連不上 Blynk Server,很奇怪吧!
對,我也是這樣覺得!
不過,上了 Blynk 找了一些討論後發現,最有可能的是記憶體不足造成的
我的直覺認為,應該是與 Blynk 函式庫在連線時會需要大量的 SRAM 導致,剩餘 25% 的空間還是不足夠的,還要更多!
其實沒錯,嘗試過幾種方式的修改,即便 SRAM 已經降低到 68% 做測試,依然無法成功連線
![]() |
BlynkSingleNodeWirelessReceiver02.ino 編譯結果 |
修改到這個地步,接下來就是修改輸出選擇的方式;不再使用接腳選擇輸出方式,而改用前置處理器讓程式編譯時選擇需要的部分,以這樣的方式減少 Flash 和 SRAM 記憶體的使用量。即使這樣只能二選一,但是當所有需要的訊息都可以在整合型 LCD 或是 debugSerial 各別輸出顯示時,為什麼還需要兩個一起做同樣的動作呢 ?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <Wire.h> | |
#include <SPI.h> | |
#include <RF24.h> | |
#include <RTClib.h> | |
/** | |
* 下面兩個只能選一個開啟 | |
*/ | |
//#define DEBUG | |
#define USEIICLCD | |
#ifdef DEBUG | |
#undef USEIICLCD | |
//** Blynk | |
#include <SoftwareSerial.h> | |
SoftwareSerial debugSerial(2, 3); // RX, TX | |
/* Comment this out to disable prints and save space */ | |
#define BLYNK_PRINT debugSerial | |
#endif | |
#ifdef USEIICLCD | |
#undef DEBUG | |
//** 整合型 LCD | |
#define LCD_IIC_ADDRESS 0x3C | |
char lcdbuf[17]; | |
//------------------------------------- end of 整合型 LCD | |
//** DS3231 | |
RTC_DS3231 rtc; | |
//------------------------------------- end of DS3231 | |
#endif | |
/** Blynk */ | |
#include <ESP8266_Lib.h> | |
#include <BlynkSimpleShieldEsp8266.h> | |
// Your ESP8266 baud rate: | |
#define ESP8266_BAUD 115200 | |
ESP8266 wifi(&Serial); | |
// Please note that a single BlynkTimer object | |
// allows to schedule up to 16 timers. | |
BlynkTimer timer; | |
//----------------------------------------- end of Blynk | |
/** nRF24L01+ */ | |
#define CEPIN 7 | |
#define CSNPIN 8 | |
// RF 頻道 | |
const uint8_t RF_CHANNEL = 100; | |
RF24 rf24( CEPIN, CSNPIN ); | |
// 通訊位址 | |
const uint8_t thisNodeAddress[] = "Node0"; | |
unsigned char rawdata[6]; // 無線接收到的 SHT31 原始數據 | |
//----------------------------------------- end of nRF24L01+ | |
/** callback functions for BlynkTimer */ | |
void update() { | |
#ifdef USEIICLCD | |
static uint8_t idx_250ms = 0; | |
static uint8_t idx_1second = 0; | |
uint16_t number; | |
uint8_t fractional; | |
#endif // endif USEIICLCD | |
// every 250ms | |
if( rf24.available() ) { | |
memset( &rawdata, 0, sizeof(rawdata) ); | |
rf24.read( &rawdata, sizeof( rawdata ) ); | |
/** 計算 SHT31-D 溫溼度數據 */ | |
int temp = (rawdata[0] * 256) + rawdata[1]; | |
float cTemp = -45.0 + (175.0 * temp / 65535.0); | |
#ifdef DEBUG | |
float fTemp = (cTemp * 1.8) + 32.0; | |
#endif | |
float humidity = (100.0 * ((rawdata[3] * 256.0) + rawdata[4])) / 65535.0; | |
/** push data to Blynk Server */ | |
Blynk.virtualWrite( V1, cTemp ); | |
Blynk.virtualWrite( V2, humidity ); | |
/** UART 溫溼度資料輸出 */ | |
#ifdef DEBUG | |
// 計算溫溼度值,不確認 CRC 正確性 | |
debugSerial.print( F("Temp: ") ); | |
debugSerial.print( cTemp ); | |
debugSerial.print( F(" C (") ); | |
debugSerial.print( fTemp ); | |
debugSerial.print( F(" F), Humi: ")); | |
debugSerial.print( humidity ); | |
debugSerial.println( F("%RH") ); | |
debugSerial.flush(); | |
#elif defined(USEIICLCD) | |
number = (int)cTemp; | |
fractional = ( cTemp - (float)number ) * 100; | |
sprintf(lcdbuf, "%4d.%02dC ", number, fractional ); | |
displayCharOnLCD( 2, 1, lcdbuf, 9); | |
number = (int)humidity; | |
fractional = ( humidity - (float)number ) * 100; | |
sprintf(lcdbuf, "%3d.%02d%c", number, fractional, 0x25 ); | |
displayCharOnLCD( 2, 10, lcdbuf, 7); | |
#endif // endif DEBUG | |
} | |
#ifdef USEIICLCD | |
// 更新時間顯示 | |
if( ++idx_250ms == 4 ) { | |
idx_250ms = 0; | |
DateTime now = rtc.now(); | |
switch( ++idx_1second ) { | |
case 1: | |
sprintf( lcdbuf, "*-*%4d/%02d/%02d*-*", now.year(), now.month(), now.day() ); | |
displayCharOnLCD( 1, 1, lcdbuf, 16 ); | |
break; | |
case 3: | |
case 4: | |
case 5: | |
sprintf( lcdbuf, "*--*%02d:%02d:%02d*--*", now.hour(), now.minute(), now.second() ); | |
displayCharOnLCD( 1, 1, lcdbuf, 16 ); | |
if( idx_1second == 5) idx_1second = 0; | |
default: | |
; | |
} | |
} | |
#endif // endif USEIICLCD | |
} | |
//----------------------------------------- end of callback ... | |
void setup() { | |
// You should get Auth Token in the Blynk App. | |
// Go to the Project Settings (nut icon). | |
String auth = F("69b3820bc03642ccb662eaf3351d706c"); | |
// Your WiFi credentials. | |
// Set password to "" for open networks. | |
String ssid = F("Buffalo-WiFi"); | |
String pass = F("sakuyaki55168"); | |
// Set ESP8266 baud rate | |
Serial.begin( ESP8266_BAUD ); | |
delay(10); | |
#ifdef DEBUG | |
debugSerial.begin( ESP8266_BAUD ); | |
delay(10); | |
#endif // endif DEUG | |
#ifdef USEIICLCD | |
Wire.begin(); | |
/** 整合型 LCD (IIC 模式) */ | |
if( !checki2cdevice( LCD_IIC_ADDRESS ) ) { | |
// 如果出問題,根本沒地方可以輸出,直接停止程式運行 ! | |
while(1) delay(1); | |
} | |
// 整合型 LCD 初始化 @ I2C 模式 | |
initLCD(); | |
delay(100); // delay 100 ms | |
clearLCD(); | |
/** RTC DS3231 */ | |
// 須先完成時間的調整 | |
if( !checki2cdevice( DS3231_ADDRESS ) ) { | |
// 到這裡若 RTC 出問題,直接可用 LCD 輸出 | |
displayCharOnLCD( 1, 1, F("DS3231 not found"), 16 ); | |
while(1) delay(1); | |
} | |
rtc.begin(); | |
displayCharOnLCD( 1, 1, F("**LCD/RTC ready*"), 16 ); | |
delay(1000); | |
#endif // endif USEIICLCD | |
/** nRF24L01+ */ | |
rf24.begin(); | |
if( !rf24.isChipConnected() ) { | |
#ifdef DEBUG | |
debugSerial.print( F("nRF24L01+ not found!") ); | |
#elif defined(USEIICLCD) | |
displayCharOnLCD( 1, 2, F("nRF24 not found!"), 16 ); | |
#endif // endif | |
while(1) delay(1); | |
} | |
// RF 相關參數設定 | |
rf24.setPayloadSize( sizeof(rawdata) ); | |
rf24.setChannel( RF_CHANNEL ); | |
rf24.setPALevel( RF24_PA_HIGH ); | |
rf24.setDataRate( RF24_250KBPS ); | |
rf24.openReadingPipe( 1, thisNodeAddress ); | |
rf24.startListening(); | |
#ifdef USEIICLCD | |
displayCharOnLCD( 2, 1, F("*nRF24L01+ ready"), 16 ); | |
delay(1000); | |
/** Blynk + ESP8266AT */ | |
displayCharOnLCD( 1, 1, F("**Blynk Server**"), 16 ); | |
displayCharOnLCD( 2, 1, F("connecting... "), 16 ); | |
#endif // endif USEIICLCD | |
Blynk.begin( auth.c_str(), wifi, ssid.c_str(), pass.c_str() ); | |
//Blynk.begin( auth, wifi, ssid, pass ); | |
#ifdef USEIICLCD | |
if( Blynk.connected() ) { | |
displayCharOnLCD( 2, 1, F("connected "), 16 ); | |
delay(900); | |
clearLCD(); | |
delay(100); | |
} | |
#endif // endif USEIICLCD | |
// 每 250 ms 檢查一次是否有無線數據傳送進來 | |
// 每 1000ms 更新一次 LCD 上面的 RTC 時間 | |
timer.setInterval( 250L, update ); | |
//----------------------------------- | |
} | |
void loop() { | |
Blynk.run(); | |
timer.run(); | |
} | |
/** | |
* 下面函式只有使用到整合型 LCD 時需要用到 | |
*/ | |
#ifdef USEIICLCD | |
bool checki2cdevice( uint8_t i2caddr ) { | |
Wire.beginTransmission( i2caddr ); | |
if( Wire.endTransmission() == 0 ) return true; | |
return false; | |
} | |
/**************************************************************************************** | |
* LCD | |
****************************************************************************************/ | |
void initLCD() { | |
Wire.beginTransmission( LCD_IIC_ADDRESS ); | |
Wire.write( 0x00 ); | |
Wire.write( 0x38 ); | |
Wire.write( 0x0C ); | |
Wire.write( 0x01 ); | |
Wire.write( 0x06 ); | |
Wire.endTransmission(); | |
} | |
void clearLCD() { | |
Wire.beginTransmission( LCD_IIC_ADDRESS ); | |
Wire.write( 0x80 ); | |
Wire.write( 0x01 ); | |
Wire.endTransmission(); | |
} | |
void displayCharOnLCD( int line, int column, const char *dp, unsigned char len ) { | |
unsigned char i; | |
Wire.beginTransmission( LCD_IIC_ADDRESS ); | |
Wire.write( 0x80 ); | |
Wire.write( 0x80 + (line - 1 ) * 0x40 + ( column - 1 ) ); | |
Wire.write( 0x40 ); | |
for( i = 0; i < len; i++) { | |
Wire.write( *dp++ ); | |
} | |
Wire.endTransmission(); | |
} | |
void displayCharOnLCD( int line, int column, const __FlashStringHelper* pData, unsigned char len ) { | |
char buffer[ len + 1 ]; | |
memcpy_P( buffer, pData, len ); | |
buffer[ len ] = '\0'; | |
displayCharOnLCD( line, column, buffer, len ); | |
} | |
//------------------------------------------------------------------------------------- | |
#endif // endif USEIICLCD |
切換上面程式碼註解 line 9 或 line 10 (只能二選一) 其中一行,然後進行編譯可得到下面結果
![]() |
BlynkSingleNodeWirelessReceiver03.ino 選擇開啟 DEBUG 模式選項 |
![]() |
BlynkSingleNodeWirelessReceiver03.ino 選擇開啟 USEIICLCD 模式選項 |
Blynk 單點無線溫溼度測試:
接下來的測試需要發射端與接收端兩組裝置,依照接收端選擇的模式分別來做
* 發射端
- 接線圖:sch-transmitter-00
- 程式碼:nRF24one2oneSHT31TransmitterWithSleep.ino
* 接收端
- 接線圖:sch-receiver-01 (DEBUG 模式), sch-receiver-02 (USEIICLCD 模式)
- 程式碼:BlynkSingleNodeWirelessReceiver03.ino
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,而且又是多點通訊,所以要去買料與準備料件了,就先到這!
<< 部落格相關網頁 >>
- Google Spreadsheet(試算表)之 ESP8266 溫濕度紀錄與趨勢圖
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 07 } - 結合 Arduino + ESP8266 實現 MQTT 主題訂閱與接收
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 06 } - 了解 MQTT 協議,學習如何訂閱 MQTT 主題與接收 MQTT 發佈消息
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 05 } - 結合 ESP8266 發佈 MQTT 消息
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 04 } - 了解 MQTT 協議,學習如何發佈 MQTT 消息
- 當 ESP8266 遇上中華電信 IoT 智慧聯網大平台 { 入門 - 03 } - 使用 Arduino + ESP8266 上傳 SHT31 溫溼度數據
- *0*nRF24L01+*0* Arduino 二點四GHz 訊號掃描器
- *1*RF24L01+*1* 如何提高 nRF24L01+ 板載天線無線模組的傳輸距離與穿牆效果 ? 加碼:不同天線形式的穿牆測試
- *2*nRF24L01+*2* SHT31 單點無線溫溼度傳輸
- 初遇 Blynk ( 物聯網手機 APP ) - 如何使用 Arduino 和 AT 韌體 ( Ai-Mod, AT v1.2.0.0 based on SDK v1.5.4.1 ) 的 ESP8266 (ESP-01, ESP-01S) 連上 Blynk 伺服器和儲存數據 {*2_1*nRF24L01+*2_1*}
- *3*nRF24L01+*3* 初遇 Blynk - 建立從 nRF24L01+ 到 ESP8266 再到 Blynk 的 SHT31 單點無線溫溼度傳輸物聯網
沒有留言:
張貼留言
留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!
發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?
不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !