Introduction
本文旨在使用 Arduino 建立一數據紀錄器。
使用場景為室外場域,網路取得不易,又需進行連續時間監測。因此,使用 Micro SD 作為儲存裝置;DS1302 模組作為時間記錄裝置,而本例使用一 DHT22 模組作為環境溫溼度紀錄數據擷取裝置。
安裝
材料
- Arduino
- Arduino Sensor Shield V5.0 (選用,直接從 Arduino 連出。惟當擴充感測器較為方便故本例使用此擴展模組)
- DS1302 Module (本例使用模組 MH-Real-Time Clock Modules-2)
- Micro SD Card Adapter Module (注意,SD 卡須先格式化為 FAT32 或 FAT16)
- DHT22 Module (使用此模組紀錄溫濕度作為範例)
- LED (選用,僅作為提示燈號用)
組裝

相關函式庫
DS1302 RTC 時鐘使用函式庫:
DHT22 溫溼度感測器使用函式庫:
SD 卡模組使用:
代碼
首先,要先將 DS1302 模組紀錄最新時間,可詳前文參考別人的代碼進行時間更新。
// DS1302 RTC 模組使用函式庫
#include <ThreeWire.h>
#include <RtcDS1302.h>
// 定義模組腳位(可自行定義)
ThreeWire myWire(4,5,2); // DAT, CLK, RST
RtcDS1302 <ThreeWire> Rtc(myWire);
// DHT 溫溼度感測模組使用函式庫
#include "DHT.h"
//DHT22 模組腳位定義
#define DHTPIN 6
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// SD 卡模組使用函式庫
#include <SPI.h>
#include <SD.h>
// SD 卡數據傳輸腳位定義(Arduino 預設腳位為 D10)
const int chipSelect = 10;
// LED 燈號腳位定義
const int led_Green = 8;
const int led_Red = 9;
// 紀錄參數定義
// 定義記錄檔檔案名稱為變數(定義第一個檔案在根目錄並命名為 D1.TXT)
String fileName = "D1.TXT";
// 設定文件大小限制(受限於 FAF32 SD 卡格式,確保檔案不會太大,單位:Bytes)
const unsigned long maxFileSize = 4000000000;
// 設定每筆資料間隔時間(單位:ms)
const int delayTime = 600000;
void setup ()
{
Serial.begin(9600); // 定義串列監視器鮑率(可自訂)
Rtc.Begin();
dht.begin();
pinMode(led_Green, OUTPUT);
pinMode(led_Red, OUTPUT);
// 啟動燈號預設為綠燈
digitalWrite(led_Green, HIGH);
// 初始化 SD 卡
if (!SD.begin(chipSelect)) {
// 若 SD 卡初始化失敗,則列印
Serial.println("卡片失效或不存在。");
digitalWrite(led_Green, LOW);
delay(10);
digitalWrite(led_Red, HIGH);
delay(10);
return;
}
// 如果 SD 卡初始化成功,則列印
Serial.println("卡片初始化完成。");
// SD 卡初始化後,檢查文件大小
checkFileSize();
// DHT 溫溼度感測模組檢查
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t) ) {
Serial.print(F("感測器讀取失敗。"));
digitalWrite(led_Green, LOW);
delay(10);
digitalWrite(led_Red, HIGH);
delay(10);
return;
}
else {Serial.println(F("感測器讀取成功。"));}
}
void loop ()
{
// 啟動燈號預設為綠燈
digitalWrite(led_Green, HIGH);
// 取得數據並檢查
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t) ) {
float h = -100; // 定義失效數據為 -100
float t = -100; // 定義失效數據為 -100
}
String dataString = String(getDateTime()) + "\t" + String(h) + "\t" + String(t);
// 存檔準備
checkFileSize(); // 檢查文件大小
File dataFile = SD.open(fileName.c_str(), FILE_WRITE);// 開啟 SD 卡文件,將數據寫入
// 當文件打開成功
if (dataFile) {
dataFile.println(dataString); // 將數據寫入文件
dataFile.close(); // 關閉文件
Serial.println(dataString); // 將數據打印到串列監視器
}
else {
// 如果文件打開失敗
Serial.print("Data print error.");
digitalWrite(led_Green, LOW);
delay(10);
digitalWrite(led_Red, HIGH);
delay(10);
}
// 延遲以進行下一循環
delay(delayTime);
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
char* getDateTime()
{
static char dateTimeChar[20];
snprintf_P(dateTimeChar,
countof(dateTimeChar),
PSTR("%04u/%02u/%02u %02u:%02u:%02u"),
Rtc.GetDateTime().Year(),
Rtc.GetDateTime().Month(),
Rtc.GetDateTime().Day(),
Rtc.GetDateTime().Hour(),
Rtc.GetDateTime().Minute(),
Rtc.GetDateTime().Second() );
if (Rtc.GetDateTime().IsValid())
{return dateTimeChar;}
else
{return "Invalid DateTime";}
return dateTimeChar;
}
void checkFileSize() {
// 如果文件大小超過限制(maxFileSize),則更改儲存的文件名。
File f = SD.open(fileName.c_str());
if (!f) {
// 如果文件打開失敗(例如,文件不存在或者其他問題),則嘗試更改文件名。
changeFileName();
} else if (f.size() >= maxFileSize) {
f.close();
changeFileName();
} else {
f.close();
}
}
void changeFileName() {
// 解析當前文件名中的文件號碼
int fileNumber = fileName.substring(7, fileName.length() - 4).toInt();
// 找出下一個可用的文件名(如 D2.TXT,D3.TXT...)。
while (true) {
fileNumber++;
String nextFileName = "D" + String(fileNumber) + ".TXT";
File nextFile = SD.open(nextFileName.c_str());
if (!nextFile) {
// 如果文件不存在,則使用此文件名。
fileName = nextFileName;
break;
}
nextFile.close();
}
}