Nu har jag nått en milstolpe i programmerandet. Jag har nu alla funktioner på plats utom möjligheten att välja effektläge via en rotationsencoder.
Alla "viktiga" larm såsom, fallschaktslarmet, pannans överhettningsskydd och brännarens överhettningsskydd går nu via en timer interrupt med 10Hz. Notera att pannans överhettningsskydd bryter strömmen till fläkten, skruven och tändelementet alltså den bryter helt oberoende av min hemmabyggda styrning. Det känns tryggt och säkert att använda den säkerhetsfunktionen som används i det befintliga systemet. Enda skillnaden är att i nuvarande system bryts strömmen även till styrboxen men det kommer den inte att göra i det nya systemet (finns egentligen ingen anledning). Övriga säkerhetsanordningar är också intakta.
Så snart jag fått till effektväljaren så kommer jag att gå över i betatestning (koppla in styrningen på pannan och köra den med personlig övervakning).
En annan detalj som blir mer som bling-bling är trådlös överföring via radio till en display uppe i själva bodelen av huset.
Kod: Markera allt
// #include <MemoryFree.h>
#include <TimerOne.h>
#include <Time.h> // For the clock
#include <DS1307RTC.h> // For the RTC
#include <Wire.h> // For serial communication
#include <LiquidCrystal_I2C.h> // For running thel i2c LCD
// Declare variables
// Set pin numbers
byte activityPin = 13; // Used for indicate various activity (blinks led)
byte skruvPin = 12; // Set pin for feeder
byte boilerOverheatPin = 11; // Set pin for overheated boiler
byte heaterPin = 10; // Set pin for heater
byte burnerOverheatPin = 9; // Set pin for overheated burner
byte resetPin = 8; // Reset program (Not Arduino)
byte callingForHeat = 7; // Set pin for listening on boiler calling for heat
byte speedPin1 = 6; // Set pin for fan speed control
byte speedPin2 = 5; // Set pin for fan speed control
byte speedPin3 = 4; // Set pin for fan speed control
byte speedPin4 = 3; // Set pin for fan speed control
byte chutePin = 2; // Set pin for chute sensor
int flameInput = A0; // Analog pin for flame
// Set other variables
int flameVisible; // Is there a flame?
int calling; // Does boiler call for heat?
boolean FOn; // Is there a flame
int k; // What is the boiler status?
time_t t = now();
time_t f = now(); // Sets the variable 't' to now()
long timePlusFiveSec; // Container for now + 5 seconds
long timePlusOneSec; // Container for now + 1 second
long timePlusThirtySec; // Container for now + 30 seconds
long timePlusTenSec; // Container for now + 10 seconds
long timePlusTwentySec; // Container for now + 20 seconds
long timePlusThreehundredSec; // Container for now + 300 seconds (5 minutes)
long timePlusThirteenSec; // Container for now + 13 seconds
long timePlusNineSec; // Container for now + 9 seconds
int filledPellets; // Is pellets filled (start feed)
int flameThreshold = 100; // Contains threshold for when flame is detected
volatile int choosenEffect = 7; // Volatile container makes it possible to change effect during run
int boilOH; // Container for the boiler overheat state
int burnOH; // Container for the burner overheat state
time_t chuteTimerStart;
long chuteTimerNow;
int chuteTime;
byte startCounting;
byte chuteMode;
byte chuteLarm = 0;
// Other settings
LiquidCrystal_I2C lcd(0x27,20,4);
void setup()
{
CreateSwedishChars();
// Initiate some hardware
Timer1.initialize(100000); // Set timer interrupt to 100 000 uS (10 Hz)
Timer1.attachInterrupt(CheckChuteStatus); // Attachthe service routine
lcd.init();
lcd.backlight();
Wire.begin();
Serial.begin(9600);
pinMode(skruvPin, OUTPUT);
pinMode(callingForHeat, INPUT);
pinMode(heaterPin, OUTPUT);
pinMode(boilerOverheatPin, INPUT);
pinMode(burnerOverheatPin, INPUT);
pinMode(resetPin, INPUT);
pinMode(speedPin1, OUTPUT);
pinMode(speedPin2, OUTPUT);
pinMode(speedPin3, OUTPUT);
pinMode(speedPin4, OUTPUT);
pinMode(chutePin, INPUT);
pinMode(activityPin, OUTPUT);
setSyncProvider(RTC.get);
// Decide if boiler is on at startup (ie after short power out).
}
void loop()
// Things that starts right away
// Check current boiler status
{
if (chuteLarm == 1)
{
larmStop(4);
}
if (boilOH == 0)
{
larmStop(3);
}
if (burnOH == 1)
{
larmStop(2);
}
FOn=getFlameStatus(); // Get flame status bool (on or off)/(True or False)
k=CheckBoilerStatus(); // Get the status from boiler int 0-3
/*
lcd.clear();
lcd.setCursor(0,1); // Debug
lcd.print(F("Pannstatus: "));
lcd.print(k); // Debug
*/
switch (k) { // Act on boiler status
case 0: // 0 Boiler is idling, nothing to do but sending log data
// lcd.clear();
lcd.setCursor(0,0);
lcd.print(F("Pannan v"));
lcd.write(char(4));
lcd.print(F("ntar"));
lcd.setCursor(0,1);
lcd.print(F("K"));
lcd.write(char(1));
lcd.print(F("r dataunderh."));
break;
case 1: // 1 Boiler should run with set effect mode
runBoiler(choosenEffect);
break;
case 2:
blowOut(); // 2 Blow out flame, boiler is done
break;
case 3:
fillPellets(); // 3 Start by feed and ignite pellets
break;
}
}
int CheckBoilerStatus() // Checking boiler status and set it as an int 0-3
/*
Controls the status of the boiler
0 = Boiler is hot enough, no need to start, flame is out
1 = Boiler has flame and wants more heat, run pellet feed and fan
2 = Boiler has flame but is hot enough, run fan until flame is out
3 = Boiler has no flame and is not hot enough, start from beginning with feeding pellet and start heating
*/
{
FOn = getFlameStatus(); // Get flame status
calling = digitalRead(callingForHeat); // Does boiler ask for heat?
if ((FOn == 1) && (calling == 1)) // Set proper status according to info
{
return 1;
}
else if ((FOn == 1) && (calling == 0))
{
return 2;
}
else if ((FOn == 0) && (calling == 1))
{
return 3;
}
else
{
return 0;
}
/*
lcd.setCursor(0,0);
lcd.print(F("Pannstatus: "));
lcd.print(FOn);
lcd.setCursor(0,1);
lcd.print(F(" Kallar: "));
lcd.print(calling);
*/
}
int getFlameStatus() { // Determents the flame status
int flameValue; // Int to hold the avg flame value
int avgFlame; // Int variable to hold the sums of flame level
for (int i = 1; i < 51; i++) { // Take 50 samples of the flame value
avgFlame = analogRead(flameInput)+avgFlame;
}
flameValue = avgFlame / 50; // Divide the result by 50
if (flameValue < flameThreshold) // Determent if flame is on or off
{
return false;
}
else if (flameValue >= flameThreshold)
{
return true;
}
}
void fillPellets() { // Routine to fill pellets ans start ignition
lcd.clear();
if (filledPellets == false) // Check that pellets has not all ready been filled
{
t=now();
timePlusThirtySec = t + 2; // Calculate the 30 second feeding time
while (t < timePlusThirtySec){ // Fill pellets for 30 seconds
t = now();
lcd.setCursor(0,0);
lcd.print(timePlusThirtySec - t);
lcd.print(F(" "));
digitalWrite(skruvPin, HIGH); // Sets the skruvPin high
lcd.print(F("Matar ... "));
}
filledPellets = 1; // Pellets has been filled
digitalWrite(skruvPin, LOW); // Turn off skruvPin (set to low)
lcd.setCursor(0,0);
lcd.print(F("Matning, klar!"));
ignitePellets(); // Run the ignition routine
}
else{
}
}
void ignitePellets() { // Routine to ignite pellets
if (getFlameStatus() == 0)
{
lcd.clear();
lcd.setCursor(0,3);
lcd.print(getFlameStatus());
lcd.setCursor(0,0);
lcd.print(F("T"));
lcd.write(char(4));
lcd.print(F("nder pellets "));
for (int c = 1; c < 11; c++) { // Try 10 times to ignite
lcd.setCursor(0,1);
lcd.print(F("T"));
lcd.write(char(4));
lcd.print(F("nder, nr: "));
lcd.print(c);
digitalWrite(heaterPin, HIGH);
t = now();
timePlusTenSec = t + 1;
while (t < timePlusTenSec) {
t = now();
lcd.setCursor(0,2);
lcd.print(F("Fl"));
lcd.write(char(4));
lcd.print(F("kt h"));
lcd.write(char(0));
lcd.print(F("g "));
//Run fan at high speed
}
t = now();
timePlusTenSec = t + 1;
while (t < timePlusTenSec) {
t = now();
lcd.setCursor(0,2);
lcd.print(F("Fl"));
lcd.write(char(4));
lcd.print(F("kt l"));
lcd.write(char(2));
lcd.print(F("g "));
//Run fan at low speed
}
k = CheckBoilerStatus();
if (k == 1)
{
return;
}
}
digitalWrite(heaterPin, LOW);
if (k == 3)
{
larmStop(1);
}
}
else if (getFlameStatus() == true)
{
}
else
{
}
}
void runBoiler(int effect) {
lcd.clear();
digitalWrite(heaterPin, LOW);
time_t q = now();
int effectValue;
switch (effect)
{
case 1:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusTwentySec = q + 20;
while (q < timePlusTwentySec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusTwentySec - q);
lcd.write(char(1));
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
break;
case 2:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusThirteenSec = q + 13;
while (q < timePlusThirteenSec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusThirteenSec - q);
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
break;
case 3:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusNineSec = q + 9;
while (q < timePlusNineSec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusNineSec - q);
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
break;
case 4:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusNineSec = q + 7;
while (q < timePlusNineSec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusNineSec - q);
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
// pause 7 sec
effectValue = 4;
break;
case 5:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusNineSec = q + 5;
while (q < timePlusNineSec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusNineSec - q);
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
// pause 4.5 sec
effectValue = 5;
break;
case 6:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusNineSec = q + 3;
while (q < timePlusNineSec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusNineSec - q);
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
// pause 3 sec
effectValue = 6;
break;
case 7:
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
timePlusNineSec = q + 2;
while (q < timePlusNineSec)
{
lcd.setCursor(0,1);
lcd.print(F("Matar om: "));
lcd.print(timePlusNineSec - q);
lcd.print(F(" "));
q = now();
};
digitalWrite(skruvPin, HIGH);
delay(1000);
digitalWrite(skruvPin, LOW);
if (getFlameStatus() == 0)
{
larmStop(5);
}
CheckBoilerStatus();
// pause 2 sec
effectValue = 7;
break;
case 0:
while (choosenEffect == 0)
{
effectValue = 0; // Do nothing, boiler is off
lcd.setCursor(0,0);
lcd.print(F("Effektl"));
lcd.write(char(4));
lcd.print(F("ge: "));
lcd.print(effect);
lcd.setCursor(0,1);
lcd.print(F("Pannan av"));
CheckBoilerStatus();
break;
}
}
}
void blowOut () {
// Set fan high if status is 2, for 5 minutes (300 seconds)
lcd.clear();
time_t r = now();
timePlusThreehundredSec = r + 10;
while ((r < timePlusThreehundredSec) && (!k == 0))
{
lcd.setCursor(0,0);
lcd.print(F("Bl"));
lcd.write(char(2));
lcd.print(F("ser ut flamman"));
lcd.setCursor(0,1);
lcd.print(F("Kvar: "));
lcd.print(timePlusThreehundredSec - r);
lcd.print(F(" av 300 sek "));
r = now();
k = CheckBoilerStatus();
lcd.setCursor(0,2);
lcd.print(k);
// Fan speed high
}
k = CheckBoilerStatus();
while (k == 0);
{
k = CheckBoilerStatus();
lcd.setCursor(0,2);
lcd.print(k);
}
// CheckBoilerStatus();
}
void larmStop(int larmMode) {
lcd.clear();
digitalWrite(skruvPin, LOW);
digitalWrite(heaterPin, LOW);
switch (larmMode)
{
case 0:
// larmMode 0 = no alarm fired
break;
case 1:
// Set fan to High for five minutes
lcd.clear();
lcd.setCursor(0,0);
lcd.print(F("LARM!!! Nr: "));
lcd.print(larmMode);
lcd.setCursor(0,1);
lcd.print(F("Misslyckad start!"));
lcd.setCursor(0,2);
lcd.print(F("Starta om manuellt"));
shutDown();
break;
case 2:
// Burner fired overheat protection
lcd.clear();
lcd.setCursor(0,0);
lcd.print(F("LARM!!! Nr: "));
lcd.print(larmMode);
lcd.setCursor(0,1);
lcd.write(char(1));
lcd.print(F("verhettad br"));
lcd.write(char(4));
lcd.print(F("nnare"));
lcd.setCursor(0,2);
lcd.print(F("V"));
lcd.write(char(4));
lcd.print(F("nta p"));
lcd.write(char(2));
lcd.print(F(" aut. "));
lcd.write(char(2));
lcd.print(F("terst."));
shutDown();
break;
case 3:
/*
Boiler fired overheat protection (Mains power for fan and screw is off)
Manual reset of boiler required
*/
lcd.clear();
lcd.setCursor(0,0);
lcd.print(F("LARM!!! Nr: "));
lcd.print(larmMode);
lcd.setCursor(0,1);
lcd.write(char(1));
lcd.print(F("verhettad panna"));
lcd.setCursor(0,2);
lcd.write(char(3));
lcd.print(F("terst. manuellt"));
shutDown();
break;
case 4:
/*
Chute detector triggers a alarm. Chute is full of pellets or smoke is coming
up the chute. Clean glass and clear chute.
*/
lcd.clear();
lcd.setCursor(0,0);
lcd.print(F("LARM!!! Nr: "));
lcd.print(larmMode);
lcd.setCursor(0,1);
lcd.print(F("Fallschakt"));
lcd.setCursor(0,2);
lcd.print(F("Kontr. fallschakt"));
shutDown();
break;
case 5:
/*
Flame is lost during run. Probable cause is lack of pellets
*/
lcd.clear();
lcd.setCursor(0,0);
lcd.print(F("LARM!!! Nr: "));
lcd.print(larmMode);
lcd.setCursor(0,1);
lcd.print(F("Pannan slocknat"));
lcd.setCursor(0,2);
lcd.print(F("Pellets slut?"));
lcd.setCursor(0,3);
lcd.print(F("Fyll & starta om"));
shutDown();
break;
}
// Blink LCD background to get attention
int s = 1;
while (s < 10) {
lcd.backlight();
delay(200);
lcd.noBacklight();
delay(200);
}
}
int CheckOverheat()
{
boilOH = digitalRead(boilerOverheatPin);
burnOH = digitalRead(burnerOverheatPin);
if (burnOH == 1)
{
return 3;
}
else
{
}
if (boilOH == 0)
{
return 2;
}
else
{
}
}
void CheckChuteStatus()
{
if ((digitalRead(chutePin) == 1) && (startCounting == 0))
{
chuteMode=1;
digitalWrite(activityPin, HIGH);
}
else if ((digitalRead(chutePin) == 1) && (startCounting == 1))
{
chuteMode=2;
}
else
{
chuteMode=3;
}
switch (chuteMode)
{
case 1:
{
chuteTimerStart = now() + 5;
startCounting = 1;
}
break;
case 2:
{
f = now();
chuteTime = chuteTimerStart - f;
Serial.println(chuteTime);
switch(chuteTime)
{
case 1:
{
chuteLarm = 1;
break;
}
default:
{
break;
}
}
break;
case 3:
{
startCounting = 0;
}
break;
}
boilOH = digitalRead(boilerOverheatPin);
burnOH = digitalRead(burnerOverheatPin);
}
}
void shutDown()
{
digitalWrite(skruvPin, LOW);
digitalWrite(heaterPin, LOW);
digitalWrite(speedPin1, LOW);
digitalWrite(speedPin2, LOW);
digitalWrite(speedPin3, LOW);
digitalWrite(speedPin4, LOW);
}
void CreateSwedishChars()
{
byte versalOdots[8] = { // Ö
B01010,
B01110,
B10001,
B10001,
B10001,
B10001,
B01110,
};
byte gemenOdots[8] = { // ö
B01010,
B00000,
B01110,
B10001,
B10001,
B10001,
B01110,
};
byte gemenAring[8] = { // å
B00100,
B00000,
B01110,
B00001,
B01111,
B10001,
B01111,
};
byte versalAring[8] = { // Å
B00100,
B01110,
B10001,
B10001,
B11111,
B10001,
B10001,
};
byte gemenAdots[8] = { // ä
B01010,
B00000,
B01110,
B00001,
B01111,
B10001,
B01111,
};
byte versalAdots[8] = { // Ä
B01010,
B01110,
B10001,
B10001,
B11111,
B10001,
B10001,
};
lcd.createChar(0, gemenOdots); // ö
lcd.createChar(1, versalOdots); // Ö
lcd.createChar(2, gemenAring); // å
lcd.createChar(3, versalAring); // Å
lcd.createChar(4, gemenAdots); // ä
lcd.createChar(5, versalAdots); // Ä
}