Så blev min datalogger og styringsenhed færdig nok til en BETA-test (version 0.9)
Jeg fandt på at sætte en lille blæser i den så den kunne køle relæerne:

- En MEGET lille blæser
- 20140117_185819.jpg (353.72 KiB) Vist 14914 gange
Da den ikke kunne trækkes direkte fra en udgang på Arduinoen blev jeg nødt til at bruge en transistor der så styrer et direkte flow af 5V til blæseren. Blæseren er monteret ved relæerne og cirkulerer kun luften i kassen, den suger/puster ikke luften ud af kassen da loggeren også skal bruges udendørs og jeg ikke ønsker alt for meget gennemstrømning af evt. fugtig luft i kassen. Kassen er af alu og leder derfor varmen godt, men kan de ikke køles nok ned må jeg nok bore et par huller i kassen og lukke den varme luft ud.
Der er 2 temperaturfølere som skal logge temperaturer under gæringen, en der kan sættes ned i øllet og en der sidder på ledningen som blot måler temperaturen udenfor spanden. Den der sænkes ned i øllet har jeg købt færdiglavet i Kina, men har efterfølgende trukket noget silikoneslange over den da jeg ikke er helt sikker på at det krympflex og ledning den bliver leveret med er "foodgrade"
Den temperaturføler der sidder på ledningen er trukket over med noget (rødt) krympflex, og alle lodninger er overtrukket med krympflex for at undgå kortslutninger.

- Sensorerne til temperatur og bobler
- 20140117_185937.jpg (364.63 KiB) Vist 14914 gange
Jeg besluttede mig for at købe et færdigt printkort til montering af sensoren der registrerer bobler, da jeg fandt dem til kun 7,- i en dansk butik (Porto kostede mere end de stumper jeg bestilte)
Den monteres på en S-gærlås (vist tidligere:
http://haandbrygforum.dk/viewtopic.php? ... =15#p53678" onclick="window.open(this.href);return false;) og registrerer når der sker ændringer i lysstrålen.
Den logfører temperaturen på alle 4 følere så jeg kan følge med i hvor varme relæerne bliver, jeg har 2 SSR med heatsinks som jeg kan bruge i stedet hvis de små relæer bliver for varme.
Som den er sat op nu skriver den data hvert minut, men jeg tror det er for mange målepunkter, så den bliver nok sat op til at logføre data hver time. Det er skrevet ind i koden men den del er kommenteret ud lige nu. Udover de 4 temperaturer tæller den også bobler pr. time og dette tal skrives til loggen sammen med temperaturerne.
Den bruger 2 filer en til temperaturer/bobler og en til tænd/sluk-tidspunkter for varme og kulde.
'Der er 5 parametre man kan justere:
- Hvor længe en kilde minimum skal være tændt.
- Hvor længe der går fra en kilde slukkes til en kilde må tændes.
- Hvor varme relæerne må blive.
- Max. temperatur på øllet under gæringen.
- Min. temperatur på øllet under gæringen.
Jeg har en lille krølle på koden som jeg lige skal have løst, idet den ikke kommer ud af "Alarm" løkken når et relæ bliver for varmt. Det er som om den ikke læser temperaturen på relæet og reagerer på den. Men det forventer jeg ikke bliver noget stort problem at fikse.
Her er min kode som den ser ud lige nu:
(EDIT 18/01/14: Ny kode postet der ikke har den omtalte fejl, og samtidig bruger mindre plads i Arduinoen)
Kode: Vælg alt
//biblioteker
#include <DS1307RTC.h>
#include <Time.h>
#include <OneWire.h>
#include <Wire.h>
#include <DallasTemperature.h>
#include <SD.h>
#include <LiquidCrystal_I2C.h>
//Opstart af boards mm
// Dallas
// Data wire is plugged into port 5 Pause the Arduino
#define ONE_WIRE_BUS 5
#define TEMPERATURE_PRECISION 9
//SD kort
//CS on Pin 4
const int chipSelect = 4;
// LCD 1602
// set the LCD address to 0x27 for a 20 chars and 4 line display
LiquidCrystal_I2C lcd(0x27,20,4);
//Definer variabler
tmElements_t tm;
boolean SourceOn, OnAllowed, OffAllowed, HeatOn, CoolOn, HeatAlarm, CoolAlarm, RelayAlarm, BlowerOn;
boolean ControlState = false;
float OldHour, OldMin, BlopCount, LastTemp;
int i, Pause, SOT;
// Blæser pin
const int BlowerPin = 1;
// the pin that the photogate is attached to
const int PhotoPin = A0;
// counter for the number of Blops
int BlopCounter = 0;
// current state of the buttPause
int PhotoState = 0;
// previous state of the buttPause
int lastPhotoState = 0;
// Setup a OneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our OneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
//datafil instans
File dataFile; // Systemet benytter 2 filer: Datalog.txt (temperatur og bobler) og Timelog.txt (Kilder tænd/sluk tidspunkter)
// Relætemperatur variabler
float CoolRelayTemp, HeatRelayTemp;
//Parametre
//Her skrives de forskellige parametre styringen skal bruge, grader angives i hele grader C resten i minutter.
const float TempMax=35; //Øvre grænse for temperatur i gærtank. Angives i grader C.
const float TempMin=27; //Nedre grænse for temperatur i gærtank. Angives i grader C.
const float SourceOnTimeLimit=3; //Styres af SOT og OffAllowed, angiver hvor lang tid en kilde minimum skal være tændt før den må slukkes. Angives i minutter.
const float VentTempSkift=10; //Styres af Pause og OnAllowed, angiver hvor lang tid der minimum skal gå fra en kilde slukkes til der må tændes for en kilde. Angives i minutter.
const float RelayAlarmTemp=40; //Maksimal temperatur et relæ må opnå før alarmen udløses og BEGGE relæer slukkes.
// Relæ og LED udgange.
int Cool = 7; //Blå diode
int Heat = 6; //Rød diode
int Beat = 8; //Grøn diode
int Styr = 3; //diode på kPausetakt til relæstyring
int Alarm = 2; //diode på lyskPausetakt til display
char Control = A2; //bruges til kPausetakt der angiver om relæ styring skal bruges.
void setup (void)
{
tmElements_t TimeTest;
//Reset relæer, og nulstil flag
SourceOn = false;
OnAllowed = true;
OffAllowed = false;
CoolOn = false;
HeatOn = false;
BlowerOn = false;
CoolAlarm = false; // Sæt alarm status til false for kølingrelæ
HeatAlarm = false; // Sæt alarm status til false for varmerelæ
RelayAlarm = false;
pinMode(Cool, OUTPUT);
pinMode(Heat, OUTPUT);
pinMode(Beat, OUTPUT);
pinMode(Styr, OUTPUT);
pinMode(Alarm, OUTPUT);
pinMode(BlowerPin, OUTPUT);
pinMode(Control, INPUT);
digitalWrite(Cool, LOW);
digitalWrite(Heat, LOW);
digitalWrite(Beat, LOW);
digitalWrite(Styr, LOW);
digitalWrite(Alarm, LOW);
digitalWrite(BlowerPin, HIGH); // Blæser er slukket når PIN er HIGH
// initialize the lcd
lcd.init();
lcd.backlight();
// initialize the Photogate pin as a input:
pinMode(PhotoPin, INPUT);
//
if (RTC.read(TimeTest))
{
lcd.setCursor(0,0);
lcd.print("RTC OK");
}
else
{
if (RTC.chipPresent())
{
lcd.clear();
lcd.print("RTC stoppet");
delay(2000);
}
else
{
lcd.clear();
lcd.print("RTC read error!");
delay(2000);
}
}
lcd.setCursor(7,0);
// Start up the Dallas library
sensors.begin();
lcd.print("Sensor OK");
lcd.setCursor(0,1);
// Initialize SD-card
pinMode(4, OUTPUT); // 53 Pause a mega, 4 Pause a UNO
if (!SD.begin(chipSelect)) {
lcd.print("SD-kort fejl!");
delay(2000);
// dPause't do anything more:
return;
}
lcd.print("Card OK");
delay(2000);
// Print parametre til lcd
lcd.clear();
lcd.setCursor(0,0);
lcd.print(TempMin, 0);
lcd.print(">T<");
lcd.print(TempMax, 0);
lcd.setCursor(8,0);
lcd.print("Alarm:");
lcd.print(RelayAlarmTemp);
lcd.setCursor(0,1);
lcd.print("SOT:");
lcd.print(SourceOnTimeLimit, 0);
lcd.setCursor(8,1);
lcd.print("Vent:");
lcd.print(VentTempSkift, 0);
delay(5000);
lcd.clear();
SkrivDisplay();
CoolRelayTemp = sensors.getTempCByIndex(1); // Henter temp på Cool relæ
HeatRelayTemp = sensors.getTempCByIndex(3); // Henter temp på Heat relæ
}
//funktiPauseer
boolean RelayTemp() // Overvåger temperaturen på relæerne
{
CoolRelayTemp = sensors.getTempCByIndex(1); // Henter temp på Cool relæ
HeatRelayTemp = sensors.getTempCByIndex(3); // Henter temp på Heat relæ
if (CoolRelayTemp >= RelayAlarmTemp || HeatRelayTemp >= RelayAlarmTemp) // Hvis der er en alarm
{
RelayAlarm = true;
SkrivTid("Relay Alarm;");
digitalWrite(Cool, LOW);//sluk for køling
digitalWrite(Heat, LOW);//Sluk for varme
digitalWrite(Alarm, HIGH); // Tænd alarm diode
// Sæt globale variabler
SourceOn = false;
OffAllowed = true;
OnAllowed = false;
Pause = 0;
SOT = 0;
if (CoolRelayTemp >= RelayAlarmTemp) // Køle relæ er overophedet
{
CoolAlarm = true; // Sæt global alarm
}
if (HeatRelayTemp >= RelayAlarmTemp) // Varme relæ er overophedet
{
HeatAlarm = true; // Sæt global alarm
}
return false;
}
else // Hvis der ikke er en alarm
{
CoolAlarm = false; // Sæt alarm status til false for kølingrelæ
HeatAlarm = false; // Sæt alarm status til false for varmerelæ
RelayAlarm = false;
digitalWrite(Alarm, LOW); // Sluk alarmdiode
SkrivTid("Relay Alarm OK;");
return true;
}
}
boolean TempStyring() // tænder og slukker for varme og kulde
{
float CurrTemp;
if (RTC.read(tm)) // hvis ikke RTC kan læses returneres false
{
CurrTemp = sensors.getTempCByIndex(0); // Hent temperatur fra gærspand, 0 er den første sensor på bussen - skal sættes til korrekt index!
if (CurrTemp >= TempMax && !SourceOn && OnAllowed) //temp over max og intet er tændt: Tænd kulde
{
digitalWrite(Cool, HIGH);
digitalWrite(Heat, LOW);
SourceOn = true;
OffAllowed = false;
SOT = 0;
Pause = 0;
SkrivTid("Cold Pause;");
CoolOn = true;
}
if (CurrTemp <= TempMin && !SourceOn && OnAllowed) // temp under min og intet er tændt: Tænd varme
{
digitalWrite(Heat, HIGH);
digitalWrite(Cool, LOW);
SourceOn = true;
OffAllowed = false;
SOT = 0;
Pause = 0;
SkrivTid("Heat Pause;");
HeatOn = true;
}
if (CurrTemp < TempMax && CurrTemp > TempMin && OffAllowed) // temp er over min og under max: Sluk alt!
{
digitalWrite(Heat, LOW);
digitalWrite(Cool, LOW);
SourceOn = false;
OnAllowed = false;
Pause = 0;
SOT = 0;
SkrivTid("Source SOT;");
CoolOn = false;
HeatOn = false;
}
}
else
{
return false;
}
}
boolean TimeSkift () //undersøger om timetal er ændret
{
//Set new time variables
if (RTC.read(tm))
{
if ( OldHour != tm.Hour)
{ // hvis Time ændres
OldHour= tm.Hour;
// Returner TRUE
return true;
}
else
{ // Hvis time ikke ændres
//Returner FALSE
return false;
}
}
}
boolean MinutSkift () //undersøger om Minuttal er ændret
{
//Set new time variables
if (RTC.read(tm))
{
if ( OldMin != tm.Minute)
{ // hvis Minut ændres
OldMin= tm.Minute;
// Returner TRUE
return true;
}
else
{ // Hvis dato ikke ændres
//Returner FALSE
return false;
}
}
}
boolean SkrivTid(String Tekst)
{
dataFile = SD.open("tidlog.txt", FILE_WRITE);
if (dataFile && RTC.read(tm)) {
dataFile.print(Tekst);
dataFile.print(';');
// skriv dato og klokkeslæt til fil i formatet yyyy-mm-dd;hh:mm, passer med excel datoformat
dataFile.print(tmYearToCalendar(tm.Year));
dataFile.print('-');
dataFile.print(tm.Month);
dataFile.print('-');
dataFile.print(tm.Day);
dataFile.print(";");
if (tm.Hour >= 0 && tm.Hour < 10) //skriv klokkeslæt i fil
{
dataFile.print("0");
dataFile.print(tm.Hour);
}
else
{
dataFile.print(tm.Hour);
}
dataFile.print(":");
if (tm.Minute >= 0 && tm.Minute < 10)
{
dataFile.print("0");
dataFile.println(tm.Minute);
}
else
{
dataFile.println(tm.Minute);
}
dataFile.close();
}
// if the file isn't open, pop up an error:
else
{
lcd.clear();
lcd.print("error tidlog.txt");
delay(2000);
}
}
boolean SkrivLog ()
{
tmElements_t tm;
RTC.read(tm);// hent nyeste tid
sensors.requestTemperatures(); // Hent temperaturer fra alle DS18B20
LastTemp = sensors.getTempCByIndex(0);
float FilData[5]; //array der indeholder de data der skal skrives til datafilen, værdi er antal sensorer plus en ekstra til bobler-værdien.
sensors.requestTemperatures(); // Send the command to get temperatures
for (i=0; i<=3; i++) //gennemløb array med data, og læg temperaturer i det. værdi er antal sensorer minus en.
{
FilData[i]= sensors.getTempCByIndex(i);
// Data skrives i denne rækkefølge: Internal, CoolRelay, External, HeatRelay
}
//læg blob count i float array FilData på plads 4, skal rettes hvis der er flere/færre end 4 DS18B20
FilData[4] = BlopCounter; //brug den sidste plads i array til bobler værdien.
dataFile = SD.open("datalog.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile && RTC.read(tm)) {
for (i=0; i<=4; i++) //gennemløb array med data, og skriv data i filen. Værdi er antal DS18B20, sidste værdi der skrives er BlopCounter.
{
dataFile.print(FilData[i]);
dataFile.print(";");
}
// skriv dato og klokkeslæt til fil i formatet yyyy-mm-dd;hh:mm, passer med excel datoformat
dataFile.print(tmYearToCalendar(tm.Year));
dataFile.print('-');
dataFile.print(tm.Month);
dataFile.print('-');
dataFile.print(tm.Day);
dataFile.print(";");
if (tm.Hour >= 0 && tm.Hour < 10) //skriv klokkeslæt i fil
{
dataFile.print("0");
dataFile.print(tm.Hour);
}
else
{
dataFile.print(tm.Hour);
}
dataFile.print(":");
if (tm.Minute >= 0 && tm.Minute < 10)
{
dataFile.print("0");
dataFile.println(tm.Minute);
}
else
{
dataFile.println(tm.Minute);
}
dataFile.close();
}
// if the file isn't open, pop up an error:
else
{
lcd.clear();
lcd.print("error datalog.txt");
}
BlopCounter= 0; //Nulstil "Blop" tælleren
}
boolean SkrivDisplay()
{
lcd.clear();
lcd.print("B:");
lcd.print(BlopCounter);
if (BlowerOn)
{
lcd.setCursor(15,0);
lcd.print("*");
}
if (ControlState) //skriver kun hvis TempStyring er aktiveret.
{
if (HeatAlarm) // Hvis er er Heatalarm
{
lcd.setCursor(0,1);
lcd.print("Heat-relay ALARM");
}
if (CoolAlarm) // Hvis der er en Coolalarm
{
lcd.setCursor(0,1);
lcd.print("Cool-relay ALARM");
}
if (!RelayAlarm) // Hvis der ikke er en alarm
{
lcd.setCursor(7,1);
lcd.print("P:");
lcd.print(Pause);
lcd.setCursor(12,1);
lcd.print("A:");
lcd.print(SOT);
lcd.setCursor(0,1);
lcd.print("T:");
lcd.print(LastTemp, 1);
}
if (CoolOn || CoolAlarm)
{
lcd.setCursor(7,0);
lcd.print("CR:");
lcd.print(CoolRelayTemp, 1);
}
if (HeatOn || HeatAlarm)
{
lcd.setCursor(7,0);
lcd.print("HR:");
lcd.print(HeatRelayTemp, 1);
}
}
else // Hvis styring er deaktiveret skrives temperaturen
{
lcd.setCursor(0,1);
lcd.print("T:");
lcd.print(LastTemp, 1);
}
}
//Hovedløkke
void loop()
{
digitalWrite(Beat, HIGH); //tænder grøn diode på frPauset der angiver programmet kører.
if (analogRead(Control) >=1000)
{
ControlState = true; // TempStyring er aktiv.
digitalWrite(Styr, HIGH);
}
else
{
ControlState = false; //TempStyrng er inaktiv, sluk for evt. kilder der er tændt.
digitalWrite(Heat, LOW);
digitalWrite(Cool, LOW);
digitalWrite(Styr, LOW);
CoolOn = false;
HeatOn = false;
SourceOn = false;
Pause = VentTempSkift;
}
if (MinutSkift()) // Hvis Minuttal er skiftet skal time undersøges, og temp skal undersøges.
{
if (!RelayAlarm) // Hvis der ikke er en alarm
{
if (SourceOn) // Hvis der er en kilde tændt
{
SOT++; // SourceOnTime - tæller tiden en kilde har været tændt 1 op.
Pause = 0; // Pause nulstilles da der ikke er nogen kilder tændt
}
else // Hvis ikke der er en kilde tændt
{
Pause++; // Pause - Tæller pausen mellem tændt og slukket 1 op.
SOT = 0; // SourceOnTimeLimit må ikke tælle op så længe der ikke er tændt for noget.
}
}
if (ControlState) // hvis styring er aktiv
{
if (!RelayAlarm) // hvis ikke der er en alarm på relæ temperaturer
{
TempStyring();
}
}
if (RelayAlarm || SourceOn && ControlState) //Hvis der er en alarm eller der er tændt for en kilde skal blæser tændes
{
digitalWrite(BlowerPin, LOW); // Blæser er tændt når PIN er LOW
BlowerOn = true;
}
else
{
digitalWrite(BlowerPin, HIGH); // Blæser er slukket når PIN er HIGH
BlowerOn = false;
}
SkrivLog(); // står dette kald her skriver den log hvert minut
if (TimeSkift()) // hvis timetal skifter skal temp logføres sammen med antal bobler
{
//SkrivLog(); // står dette kald her skriver den log hvert time.
}
SkrivDisplay(); //opdater display
}
// undersøg om kilder må slukkes eller tændes hvis der ikke er en alarm
if (!RelayAlarm)
{
if (SOT >=SourceOnTimeLimit && SourceOn) // Hvis det er længere end SourceOnTimeLimit siden der sidst blev tændt for en kilde er det tilladt at slukke en kilde
{
OffAllowed = true;
}
else
{
OffAllowed = false;
}
if (Pause >=VentTempSkift && !SourceOn) // Hvis det er længere end VentTempSkift siden der sidst blev slukket for en kilde er det tilladt at tænde en kilde
{
OnAllowed = true;
}
else
{
OnAllowed = false;
}
}
else // Hvis der er en alarm
{
OffAllowed = true;
digitalWrite(Cool, LOW);//sluk for køling
digitalWrite(Heat, LOW);//Sluk for varme
digitalWrite(Alarm, HIGH); // Tænd alarm diode
}
// Tæl Bobler
// read the Photogate input pin:
PhotoState = digitalRead(PhotoPin);
// compare the Photogate State to its previous state
if (PhotoState != lastPhotoState)
{
// if the state has changed, increment the counter
if (PhotoState == HIGH)
{
// if the current state is HIGH then the buttPause went from SOT to Pause:
BlopCounter++;
SkrivDisplay();
}
}
// save the current state as the last state, for next time through the loop
lastPhotoState = PhotoState;
digitalWrite(Beat, LOW); //slukker grøn diode på frPauseten der angiver at program kører
delay(10);
}
Liste over stumper kan laves hvis nogen ønsker det, jeg burde have alle priser osv. men som det ser ud lige nu ligger den omkring 300,- for det hele.