定時偵測環境資訊儲存在SD記憶卡

功能:定時偵測光線將結果顯示在 LCD及序列埠視窗,利用按鈕開關切換寫入或不寫入 SD記憶卡

材料接線:

光敏電阻=A0,土壤濕度感測器=A1,按鈕模組=2,溫濕度感測器DHT11=3

時鐘模組DS1302腳位 RST=5,DAT=6,CLK=7

液晶顯示模組的SDA、SCL接擴展版同名稱位置(SDA=A4,SCL=A5)

記憶卡Micro SD模組 CS=10,MOSI=11,MISO=12,SCK(CLK)=13

 

程式:

//程式名稱:定時偵測環境資訊存記憶卡(未放記憶卡無法正常工作)
//功能說明:定時偵測光線將結果顯示在 LCD及序列埠視窗,利用按鈕開關切換寫入或不寫入 SD記憶卡
//光敏電阻=A0,土壤濕度感測器=A1,按鈕模組=2,溫濕度感測器DHT11=3
//時鐘模組DS1302腳位 RST=5,DAT=6,CLK=7
//液晶顯示模組的SDA、SCL接擴展版同名稱位置(SDA=A4,SCL=A5)
//記憶卡Micro SD模組 CS=10,MOSI=11,MISO=12,SCK(CLK)=13


#include "DHT.h"  // 溫濕度感測器DHT11
函式庫名稱:DHT sensor library,by Adafruit(要安裝:[工具]/[管理程式庫])
#define DHTPIN 3  // 定義溫濕度感測器DHT11的接腳
#define DHTTYPE DHT11  // 定義溫濕度感測器的型號為DHT11
DHT dht(DHTPIN, DHTTYPE);  //建立DHT物件名稱為dht

#include <ThreeWire.h>  //使用內建函式庫控制
I2C介面
#include <RtcDS1302.h> // 時鐘模組DS1302
函式庫名稱:Rtc by Makuna,by Michael C. Miller(要安裝:[工具]/[管理程式庫])
ThreeWire myWire(6, 7, 5); // 設定時鐘模組腳位(DAT, CLK, RST)
RtcDS1302<ThreeWire> Rtc(myWire);

#include <SPI.h>  // 使用內建的函式庫SPI.h,控制
ISPI介面
#include <SD.h>   // 記憶卡使用內建的函式庫SD.h,控制SD記憶卡模組

#define SD_CS_PIN SS  // CS腳位預設是SS腳位10,如果有多個SPI設備,要設定不同的腳位
File myFile;          // 建立檔案物件名稱為myFile

#include <LiquidCrystal_PCF8574.h>  // LCD液晶顯示模組
函式庫名稱:LiquidCrystal_PCF8574by Matthias Hertel(要安裝:[工具]/[管理程式庫])
LiquidCrystal_PCF8574 lcd(0x27);  // LCD液晶顯示模組的 i2c位址設定為0x27或0x3F
int BUTTON_PIN = 2;  //
設定按鈕開關腳位
boolean buttonState = false;  // 設定按鈕開關的現在狀態為布林值0(false)
int delayTime =5;  //設定
定時偵測要間隔幾秒,(請自行修改秒數)
int timeCount =0;  //配合間隔秒數使用計時迴圈,將迴圈次數的初始值設為0(不要修改)


void setup(){
  Serial.begin(9600);  // 設定與電腦序列埠監控視窗的速率相同
  Rtc.Begin();  //時鐘模組開始執行
  if (Rtc.GetDateTime() != RtcDateTime(__DATE__, __TIME__)){  //如果時鐘模組的時間與程式編譯當時的電腦時間不同,就更改時間模組的時間

    Serial.println("RTC is older than compile time!  (Updating DateTime)");
    Rtc.SetDateTime(RtcDateTime(__DATE__, __TIME__));
  }
  if (!SD.begin(SD_CS_PIN)) {  // 判斷SD模組初始化是否成功
    Serial.println("initialization failed!");  // 序列埠監控視窗顯示SD模組初始化失敗並結束離開
    return;  //結束直接跳離開此setup初始設定函式
  }
  Serial.println("Micro SD initialization done.");  // 序列埠監控視窗顯示初始化完成
  dht.begin();  // 溫濕度感測器DHT11開始執行

  lcd.begin(16, 2);         // LCD初始化,寬度16字,高度2行
  lcd.setBacklight(255);    // 設定背景亮度為255
  lcd.clear();              // 清除LCD顯示器的文字
  pinMode(A0,INPUT);        // 設定光敏電阻接在 A0(類比第0接腳)為輸入功能
}

void loop(){
  RtcDateTime now = Rtc.GetDateTime();  // 建立時鐘物件名稱為now
  String t1="/", t2=":", t3="  ";  // 設定時間的數字要合併顯示時中間的文字 "/",":"," " ,例如:年/月/日  時:分:秒
  String timeNow=now.Year()+t1+now.Month()+t1+now.Day()+t3+now.Hour()+t2+now.Minute()+t2+now.Second();  //設定現在的時間變數為 timeNow存放時鐘模組的時間
  Serial.println(timeNow);  //將時間顯示在序列埠視窗
  int h = dht.readHumidity();   //取得溫濕度感測器DHT11的濕度
  int t = dht.readTemperature();  //取得溫濕度感測器DHT11的溫度C
  int moisture = analogRead(A1);  //取得土壤濕度感測器數值
  int light = analogRead(A0);  //取得光敏電阻的亮度數值
  String str1 = "DATA=";  //設定字串變數 str1放文字 "DATA="
  String str2 = ",";  //設定字串變數 str2放文字 ","

  String str3 = "     ";  //設定字串變數 str3放5個空格
  Serial.println(str1 + light + str2+h+str2+t+str2+moisture+str3);  //序列埠視窗顯示偵測到的數值前後合併字串
  lcd.setCursor(0, 0);  //LCD定位在第0個字、第0列位置
  lcd.print(str1 + light + str2+h+str2+t+str2+moisture);  //LCD將偵測到的數值前後合併字串顯示出來

  if (buttonState){  // 如果按鈕
被按下狀態為 true 就開始將偵測結果寫入記憶卡 SD
    myFile = SD.open("test.txt", FILE_WRITE);   //  開啟檔案 test.txt 設定為寫入模式,如果檔案不存在會自動建立(不可用中文檔名)

    if (myFile){  // 如果開啟檔案成功狀態為 true 就進行以下的寫入SD程式

      myFile.println(timeNow);  // 將現在時間變數存到 test.txt 檔案中
      myFile.println(str1 + light + str2+h+str2+t+str2+moisture);  // 將光線
light濕度 h溫度 t土壤濕度 moisture存到 test.txt 檔案中
      myFile.close();  // 關閉檔案
      Serial.println("Writing to test.txt...");  // 序列埠監控視窗顯示文字
      lcd.setCursor(0, 1);  // LCD 定位到第0個字、第1列位置
      lcd.print("Write to SD      ");  // LCD 顯示文字

    }
    else {  // 如果無法開啟檔案,就在序列埠及LCD顯示錯誤訊息
      Serial.println("error opening test.txt");
      lcd.setCursor(0, 1);
      lcd.print("Write error     ");
    }
  }
  else {  // 如果按鈕沒有被按下狀態為
False ,序列埠及LCD顯示停止寫入的訊息
    Serial.println("Write stop      ");
    lcd.setCursor(0, 1);  //定位在 LCD 的第0個字、第1列位置
    lcd.print("Write stop      ");  
  }
  timeCount = 0;   //將計時迴圈次數的初始值設為0(不要修改)

  while(timeCount < delayTime * 10){ // 計時迴圈變數 timeCount每跑一圈為0.1秒,當未達到間隔秒數時繼續偵測按鈕開關是否被按下
    if (!digitalRead(BUTTON_PIN)){  // 偵測按鈕被按下時讓按鈕狀態buttonState設定為相反
      buttonState = !buttonState;  // 按鈕狀態設定為相反
      delay(500);  // 等待 0.5秒預防按鈕多次反彈的不正常狀況
      break;  // 離開while迴圈
    }
    timeCount++;  // 計時迴圈變數加 1
    delay(100);  // 等待 0.1秒

  }
}