網頁最後修改時間:2020/12/21
![]() |
參考接線圖:
下面是無線溫溼度傳輸的發射端與接收端的參考接線圖。接線的時候盡量選擇手邊可用的相同材料即可,不限定一定要跟網頁一樣,因為這篇網頁的重點在無線數據的傳輸上,而不是取得 SHT31 的數據。
微控制器可選擇不一樣的 Arduino 開發板。若沒有相同的溫溼度感測器,使用其他的也可以 (改一下函式庫和相關程式碼),甚至手動建立數據也可以。發射端與接收端接在 <A1> 和 <A2> 接腳上面的開關圖示,代表是否選擇 UART 除錯輸出、使用整合型 LCD 顯示日期時間和溫溼度數據;這裡可以直接使用跳線替代,不接則不會輸出任何數據。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 發射端參考接線圖:
![]() |
發射端參考接線圖 |
* 發射端接線材料:
- Arduino Nano / Uno
- 一些杜邦線
- 麵包板或Arduino Nano 擴充底板
- SHT31-D 溫溼度感測器
- NRF24L01+ (功率加強版本) 無線模組 (含轉接板)
- NRF24L01+ + PA+LNA 遠距離無線模組(含轉接板與 SMA 天線)
![]() |
發射端實際接線完成參考圖 |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 接收端參考接線圖:
![]() |
接收端參考接線圖 |
* 接收端接線材料:
- Arduino Nano / Uno
- 一些杜邦線
- Arduino Nano 擴充底板
- 高精度即時時鐘 DS3231 + 32KByte EEPROM二合一模組
- {5V}整合型 {4/8BIT, IIC, 4SPI}1602英文字型藍底白字 LCD 螢幕
- NRF24L01+ (功率加強版本) 無線模組 (含轉接板)
- NRF24L01+ + PA+LNA 遠距離無線模組(含轉接板與 SMA 天線)
![]() |
接收端實際接線完成參考圖 |
下面兩個 Arduino 函式庫,在編譯前必須先行安裝的,請至函式庫下方的連結網頁中下載;其餘的直接在程式碼中實現。
- DS3231 RTC Module 函式庫 (與下面網頁使用相同的函式庫)
*0*RTC(即時時鐘)模組*0* 如何更新 DS3231 RTC 模組的時間與大型數字時鐘製作 - nRF24L01+ 函式庫 (與下面網頁使用相同的函式庫)
*1*nRF24L01+*1* 如何提高 nRF24L01+ 無線模組的傳輸距離與穿牆效果? 加碼:不同天線形式的穿牆測試
這篇網頁使用 Arduino IDE v1.8.4 編譯上傳程式碼!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
Arduino 程式碼:
下面兩個程式碼不做逐行解釋,在這裡只做整體說明,需要詳細說明的話,請自行再閱讀零件資料手冊與函式庫的說明。
在這兩個程式中,不管是發射端與接收端都使用 nRF24L01+ 做無線通訊,所以兩者的的無線頻率與基本組態設定都必須一樣,否則無法互相通訊!預設的情況下,nRF24L01+ 六個 data pipe 的自動確認 (Auto Acknowledgment) 功能都是開啟的,所以在發射端只要負責將數據準備好傳送 (write) 出去,接收端則是等待 (available())數據過來 (read) 後進行計算,其他的不需要去管,也不需要再做處理,nRF24L01+ 自己會搞定!至於附加的週邊,只是為了顯示處理後的數據而已,用與不用取決於實際應用的場合上,可自行再做選擇!
nRF24L01+ 可接收來自 6 個同頻率不同位址的無線數據,由於 data pipe 0 額外也用於傳送,所以在接收端的設定是使用 data pipe 1 來做數據接收,保留 data pipe 0 作為之後程式用。當 nRF24L01+ 無線模組有用到傳送與接收的功能時,如果又選擇 data pipe 0 作為接收,那麼每一次在傳送或是接收數據時,都必須重新設定傳送或是接收的位址,否則就會導致通訊失敗。這樣設定有點煩,簡單與程式碼容易了解起見,發射端與接收端分別使用不同的 data pipe。
接著,就直接進入到程式碼的部分。
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 發射端程式碼:
在發射端的程式碼主要是取得 SHT31-D 的溫溼度 raw data (原始數據,未經過計算),然後直接使用 nRF24L01+ 傳送出去。此時,若接收端不存在的話,開啟 UART 除錯就會出現 Tx failed 的文字訊息出現;從這裡也可以知道,自動確認是由 nRF24L01+ 自己完成。
在不開啟 UART 除錯功能的情況下,發射端除了訊息不輸出之外,也不再進一步的計算 SHT31-D 的溫溼度,這樣可減少 MCU 處理的時間增加空閒時間,讓 MCU 進入到睡眠的狀態節省電力 (在此先保留此功能)。由於是測試,所以每次通訊的時間設得比較短,只有 1000 ms。實際應用上,每次通訊可以設幾十秒或是幾分鐘一次,數據傳送出去之後,裝置就進入到睡眠狀態已節省電力,這樣的方式就很適合用在電池供應的裝置上。
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> | |
/** SHT31-D */ | |
const int SHT31_IIC_ADDRESS = 0x44; | |
unsigned char rawdata[6]; | |
/** nRF24L01+ */ | |
#define CEPIN 7 | |
#define CSNPIN 8 | |
// RF 頻道 | |
const uint8_t RF_CHANNEL = 100; | |
RF24 rf24( CEPIN, CSNPIN ); | |
// 通訊位址 | |
const uint8_t receiverAddress[] = "Node0"; | |
//----------------------------------------- end of eRF24L01+ | |
/** debug */ | |
#define DEBUGOUTPUT A1 | |
bool isdebug; | |
//----------------------------------------- end of SHT31-D | |
/** time */ | |
unsigned long previousMills = 0; | |
const unsigned long interval = 1000; // milliseconds | |
//----------------------------------------- end of time | |
void setup() { | |
Serial.begin( 115200 ); | |
printf_begin(); | |
pinMode( DEBUGOUTPUT, INPUT_PULLUP ); | |
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+ */ | |
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(); | |
} | |
void loop() { | |
// 每隔 intercal ms 傳送一次資料 | |
unsigned long currentMillis = millis(); | |
if( currentMillis - previousMills >= interval ){ | |
previousMills = currentMillis; | |
/** 取得溫濕度的 raw data */ | |
memset( &rawdata, 0, sizeof(rawdata) ); | |
if( readRawDataSHT31() ) { | |
if( !rf24.write( &rawdata, sizeof(rawdata) ) && isdebug ) { | |
Serial.println( F(" Tx failed") ); | |
} | |
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"); | |
} | |
} | |
} | |
} | |
bool checki2cdevice( uint8_t i2caddr ) { | |
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); | |
} | |
//------------------------------------------------------------------------------------- |
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 接收端程式碼:
相對於發射端,接收端負責的東西就比較多,除了接收數據並進行運算之外,由於不使用網路上傳數據,所以除了選擇由 UART 輸出計算後的溫溼度之外,也提供整合型 LCD 顯示的方式,都是由外部接腳做控制。
開啟 UART 輸出的情況下,幾乎所有的訊息都會輸出,但是 DS3231 所提供的日期和時間不會;開啟使用整合型 LCD,則螢幕第一行分時顯示現在日期和時間,第二則顯示最近一次接收並計算之後的溫溼度值。
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> | |
#include <RTClib.h> | |
/** 整合型 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"; | |
//----------------------------------------- end of nRF24L01+ | |
/** debug */ | |
const uint8_t DEBUGOUTPUT = A1; | |
const uint8_t IICLCDOUTPUT = A2; | |
bool isdebug, isiiclcd; | |
//----------------------------------------- end of debug | |
/** time update */ | |
unsigned long previousMills = 0; | |
const long interval = 1000; // milliseconds | |
//----------------------------------------- end of time update | |
unsigned char rawdata[6]; | |
void setup() { | |
Serial.begin( 115200 ); | |
Wire.begin(); | |
printf_begin(); | |
pinMode( DEBUGOUTPUT, INPUT_PULLUP ); | |
pinMode( IICLCDOUTPUT, INPUT_PULLUP ); | |
isdebug = !digitalRead( DEBUGOUTPUT ); | |
isiiclcd = !digitalRead( IICLCDOUTPUT ); | |
/** RTC DS3231 */ | |
// 須先完成時間的調整 | |
if( !checki2cdevice( DS3231_ADDRESS ) ) { | |
if( isdebug ) Serial.println( F("DS3231 RTC Module not found!") ); | |
while(1) delay(1); | |
} | |
rtc.begin(); | |
/** 整合型 LCD (IIC 模式) */ | |
if( isiiclcd ) { | |
if( !checki2cdevice( LCD_IIC_ADDRESS ) ) { | |
if( isdebug ) Serial.println( F("LCD not found!") ); | |
while(1) delay(1); | |
} | |
// 整合型 LCD 初始化 @ I2C 模式 | |
initLCD(); | |
delay(100); // delay 100 ms | |
clearLCD(); | |
delay(100); // delay 100 ms | |
displayCharOnLCD( 1, 1, "**LCD/RTC ready*", 16 ); | |
delay(1000); | |
} | |
/** 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.openReadingPipe( 1, thisNodeAddress ); | |
rf24.startListening(); | |
if( isiiclcd ) { | |
displayCharOnLCD( 2, 1, "*nRF24L01+ ready", 16 ); | |
delay(800); | |
clearLCD(); | |
delay(100); | |
} | |
// 除錯模式下,輸出無線模組的設定 | |
if( isdebug ) rf24.printDetails(); | |
} | |
void loop() { | |
unsigned long currentMillis = millis(); | |
if( currentMillis - previousMills >= interval ) { | |
// 更新時間顯示 | |
static uint8_t idx = 0; | |
previousMills = currentMillis; | |
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: | |
; | |
} | |
} | |
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; | |
/** UART 溫溼度資料輸出 */ | |
if( isdebug ) { | |
// 計算溫溼度值,不確認 CRC 正確性 | |
Serial.print( F("Temp: ") ); | |
Serial.print( cTemp ); | |
Serial.print( " C ("); | |
Serial.print( fTemp ); | |
Serial.print( F(" F), Humi: ")); | |
Serial.print( humidity ); | |
Serial.println( "%RH"); | |
} | |
/** 整合型 LCD 溫濕度資料輸出 */ | |
// line 2, format:100.00C 100.0%RH | |
if( isiiclcd ) { | |
uint8_t Tfractional, RHfractional; | |
uint16_t Tnumber, RHnumber; | |
Tnumber = (int)cTemp; | |
RHnumber = (int)humidity; | |
Tfractional = (cTemp - (float)Tnumber ) * 100; // 小數點兩位 | |
RHfractional = (humidity - (float)RHnumber) * 10; // 小數點一位 | |
sprintf( lcdbuf, "%3d.%02dC %3d.%1d%cRH", | |
Tnumber, Tfractional, | |
RHnumber, RHfractional, 0x25 ); | |
displayCharOnLCD( 2, 1, lcdbuf, 16 ); | |
} | |
} | |
} | |
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(); | |
} | |
//------------------------------------------------------------------------------------- |
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
程式碼與資料都在網頁提供的連結或是網頁中,請自行下載使用與測試!
/*-/--*-*/*/*/*/***//-*-*-**-*/*-*-/*/*/*-*-/-////--/**/**--**/--///--//**----**//--**//**----***//*-**//*
/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* 測試影片:
下面是測試的影片,兩個線路中的 <S1>、<S2>和<S3> 全部接地,UART 輸出與硬體線路一起動作,動作與測試過程影片中有文字描述
結論:
為什麼要花這麼多的篇幅來寫 nRF24L01+ 這個已經存在很久的無線模組,其實是為了 LoRa (Long Range) 鋪路,兩者都是無線模組 (前者是 2.4GHz,後者是 433 MHz / 866 MHz / 915 MHz ),特別適用於網路不普及也不方便的區域處數據的取得,一但這些數據連接到網路時,不就是 "物聯網" 了嗎 ?
所以呢 ? ESP8266 出現了!
不過寫到這裡之前我已經在想,到底下一篇是要 Arduino + ESP8266 (AT 指令),還是直接使用 ESP8266 來延續這個網頁裡面的東西 ? 記得有人問過我 "Arduino + ESP8266 (AT 指令) 和 Blynk 怎麼結合來用,並將資料顯示在手機上",要不下一篇就這個主題吧!
<< 部落格相關網頁 >>
- *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"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!
發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?
不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !