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