2016年6月25日

arduino製作冰箱溫度記錄器

這是我家冰箱的溫度變化圖,收了三天的資料來的。紅色是門架底部的,藍色的是冷藏庫上層。

怎麼會想做這個呢?因為一直很好奇冰箱裡的溫度是怎麼變的,先前有跟小p一起買了sensordrone,是可以測溫然後紀錄的。我就把機器直接放在冰箱裡,人在冰箱外用藍牙連線,用手機收紀錄,但是冰箱溫度太低,放進去沒多久,儀器就昏死了。

於是我現在用arduino加上一些模組來測溫囉,只有把sensor牽線放在冰箱裡,主機則都是在冰箱外,這樣就避免了機器昏死的問題。
001



這張圖可以看到什麼現象呢?溫度起伏得好大喔,其實因為時間軸壓縮了,所以起伏看起來就很大,如果只看幾分鐘的範圍,不太會察覺溫度有起伏喔。

就像下面這張是用手機的app來收藍牙的資料,然後即時繪圖出來的。底部的藍線一個尖峰測的是光照,一個尖峰就代表照到很短暫的光,這光哪來的呢?其實就是用一個測光的模組放在冰箱裡,只要一開門,冰箱裡的燈就會亮,我就可以記錄下來。所以就等於用測光模組去紀錄開門時間點與時間長度。





Screenshot_2016-06-19-00-28-46


綠色的線段代表門架底部(通常拿來放牛奶)的地方,紅線則是冷藏庫上層。很明顯就看出,一開門,門架底部的溫度就上升了,但在一兩分鐘之內就會降回來。不過這前提是開門沒有開很久,最上面那張圖就可以告訴我們一些故事。開門開太久,連冷藏庫上層的溫度也會變化呢!



再來看看細部一點的圖,溫度的波動週期大約是一小時,起伏溫度大約是一兩度
002



有些情況,冷藏庫的溫度可能會降得更低。以致於提高了溫差。就以這兩處的溫差變化來看,小則0.5度,大則3度。看到這圖引發我更進一步想研究的興趣,我想知道壓縮機到底是在哪個時間點啟動的?這日後再作,應該可用壓電片貼在冰箱上來偵測壓縮機的振動。另外不同天的時候,溫度起伏也不太一樣,我推測跟冰箱內的食物量有關係。
003


這是手機和arduino連線的畫面
DSC05637



接出來的線就是這樣的,我相信對想做的人一點幫助也沒有,我只是想呈現一種高度亂線的美感。
DSC05638
DSC05639


除了arduino以外,用到的模組包含這些:
藍牙模組:用來將資料送出給手機接收,即時繪圖用
SD卡模組:紀錄各時間點的環境變化
RTC模組:輸出實際時間給記錄器,才知道收到的資料是什麼時間點
液晶螢幕模組:在機器上直接呈現資訊
測光模組:用來確認開門時間點和時間長
DS18B20:測溫,放冰箱冷藏庫上層和門架底部。




以下就是接線方式



溫度感測器DS18B20
函式庫
<OneWire.h>  http://playground.arduino.cc/Learning/OneWire
<DallasTemperature.h>   https://github.com/milesburton/Arduino-Temperature-Control-Library

接線方式
DS18B20--Arduino
Data-D2
VCC-5V
GND-GND
DATA和VCC之間再加一個4.7k的電阻



RTC時間模組ds1302
函式庫
https://github.com/msparks/arduino-ds1302

接線方式
ds1302--Arduino
VCC-5V
GND-GND
CLK(sclk)--D6
DAT(IO)--D7
RST(CE)-D8




藍牙
HC05、HC06--Arduino
VCC-5v
GND-GND
RXD-D1
TXD-D0

SD卡

SD Module--Arduino
GND--GND
3V3---不接
5V--5V
CS--D10
MOSI--D11
SCK--D13
MISO--D12
GND--不用接




光線感測
VCC-5V
GND-GND
Vout-A0


液晶螢幕
函式庫 https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library

接線方式
LCD--Arduino
GND--GND
VCC--5V
SDA--A4
SCL--A5



arduino的程式
---
// 匯入程式庫標頭檔
//from http://playground.arduino.cc/Learning/OneWire
#include <OneWire.h>

//from https://github.com/milesburton/Arduino-Temperature-Control-Library
#include <DallasTemperature.h>
#include <Wire.h>

//https://github.com/fdebrabander/Arduino-LiquidCrystal-I2C-library
#include <LiquidCrystal_I2C.h>

#include <SD.h>

#include <stdio.h>

//from https://github.com/msparks/arduino-ds1302
#include <DS1302.h>

const int kCePin   = 8;  //RST
const int kIoPin   = 7;  //DAT
const int kSclkPin = 6;  //CLK
DS1302 rtc(kCePin, kIoPin, kSclkPin);


String printTime() {//顯示日期時間的函式
  Time t = rtc.time();
  char buf[30];
  snprintf(buf, sizeof(buf), "%04d-%02d-%02d %02d:%02d:%02d",
           t.yr, t.mon, t.date,
           t.hr, t.min, t.sec);

  //Serial.print(buf);
  return buf;
}




LiquidCrystal_I2C lcd(0x27, 16, 2);

// Arduino數位腳位2接到1-Wire裝置
#define ONE_WIRE_BUS 2

// 運用程式庫建立物件
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);


unsigned long time;


void setup()
{

  Serial.begin(9600);
  pinMode(10, OUTPUT);

  rtc.writeProtect(false);// 是否防止寫入 (日期時間設定成功後即可改成true)
  rtc.halt(false);// 是否停止計時
  Time t(2016, 06, 18, 22, 32, 00, Time::kSaturday); //年 月 日 時 分 秒 星期幾 (日期時間設定成功後即可註解掉)
  //rtc.time(t);//設定日期時間 (日期時間設定成功後即可註解掉)


  if (!SD.begin(10)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");


  sensors.begin();
  //lcd.begin(16,2);
  lcd.begin();
  lcd.backlight();
  // 初始化




}

void loop(void)
{
  // 要求匯流排上的所有感測器進行溫度轉換
  time = millis();
  sensors.requestTemperatures();

  int light = analogRead(A0);
 
  // 參數0代表匯流排上第0個1-Wire裝置
  //Serial.print(printTime());
   //Serial.print(",");
  //Serial.print(time/1000);
  //Serial.print(",");
  Serial.print("E");
  Serial.print(sensors.getTempCByIndex(0));
  Serial.print(",");
  Serial.print(sensors.getTempCByIndex(1));
  Serial.print(",");
  int lightSmall = map(light,0,1023,-30,30);
  Serial.println(lightSmall);
 


  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(sensors.getTempCByIndex(0));
  lcd.print("C");
  lcd.setCursor(0, 1);
  //lcd.print("Sensor2:");
  lcd.print(sensors.getTempCByIndex(1));
  lcd.print("C");
  lcd.setCursor(7, 0);
  lcd.print(lightSmall);
  lcd.setCursor(7, 1);
  lcd.print(time/1000);


  File dataFile = SD.open("Data.TXT", FILE_WRITE);
  if (dataFile) {
    dataFile.print(printTime());
    dataFile.print(",");
    dataFile.print(time);
    dataFile.print(",");
    dataFile.print(lightSmall);
    dataFile.print(",");
    dataFile.print(sensors.getTempCByIndex(0));
    dataFile.print(",");
    dataFile.println(sensors.getTempCByIndex(1));

  }
  dataFile.close();

  delay(1000);
}

--

取出資料和繪圖時,用了文字列的awk和gnuplot

awk的目的只是為了將得到的文字檔,裡頭有個毫秒紀錄,把這個轉成比較容易理解的單位,就是小時,然後再輸出成新的文字檔。


awk 'BEGIN{FS=","};{print ($2-612823)/3600000,$3, $4 }' DATA.TXT >newData.txt

gnuPlot則是用來畫出圖形的,用了這些
set xtics ("Day0" 0,"" 12 1 ,"Day1" 24, "" 36 1 ,"Day2" 48, "" 60 1,"Day3" 72)
set xlabel "Time(Hour)"
set xrange[0:72]
set yrange[0:25]
plot "newData.txt" using 1:2 with lines lt rgb "blue" title "Top", "newData.txt" using 1:3 with lines lt rgb "red" title "Door"