3
0

Checks now status of temperature controller relais

output
This commit is contained in:
Markus Rosenstihl 2017-05-31 16:33:42 +01:00
parent 0e43ea9758
commit 6ea36736ed

View File

@ -1,210 +1,319 @@
#include <limits.h> #include <limits.h>
// Needed for LiquidCrystal_I2C.h #include <SPI.h>
#include <Wire.h> #include <TFT.h>
// from https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads/ //#include <SD.h>
#include <LiquidCrystal_I2C.h> // display setup
#define CS 18
#define DC 19
#define RESET 17
// as per http://funduino.de/nr-06-zwei-i%C2%B2c-displays-gleichzeitig
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
boolean setup_ok=true; // false will stop loop() // create an instance of the library
const int N=16; // number of sample averages TFT TFTscreen = TFT(CS, DC, RESET);
boolean setup_ok = true;
const int N = 16; // number of sample averages
volatile unsigned int deltas[N]; // array of time deltas between interrupt volatile unsigned int deltas[N]; // array of time deltas between interrupt
volatile unsigned int n = 0; // run variable volatile unsigned int n = 0; // run variable
unsigned long total; unsigned long total;
float flow; float flow;
int prescaler; struct rgb {
char buff[10]; // temp. buffer for string manipulation/formatting int red=0;
char serialbuff[16]; // temp. buffer for string manipulation/formatting int green=0;
int blue=0;
};
// begin user configuration parameters
/*
This is the prescaler setting for timer1:
The time base of the timer counter TCNT1
t_step or seconds/tick = prescaler/clock
Note: Maximum TCNT1 value is 65535, i.e.
Maximum time lengths (and thus timeouts) are:
8 0.0327675 s
64 0.26214 s
256 1.04856 s
1024 4.19424 s
*/
#define PRESCALER 256 // todo: make this dependent on timeout
/* /*
User configuration The flow meter can measure from 0.24 to 17 l/min at 4 pulses/s per l/min
================== max period about 1s, i.e. min flow of 0.24l/min
min period about 15 ms i.e. max flow of 17l/min
Flow meter
==========
The PFG flow meter can measure from 0.24 to 17 l/min at 4 pulses/s per l/min
max period about 1s, i.e. min flow of 0.24l/min
min period about 15 ms i.e. max flow of 17l/min
The time base of the timer counter TCNT1
t_step or seconds/tick = prescaler/clock
Note: Maximum TCNT1 value is 65535, i.e.
Maximum time lengths (and thus timeouts) per prescaler value are:
8 0.0327675 s
64 0.26214 s
256 1.04856 s
1024 4.19424 s
For accuracy the prescaler is set automatically fitting to the TIMEOUT.
*/ */
#define TIMEOUT 1.0 // measurement timeout in Hz (needs to be less than timer1 limits above) #define TIMEOUT 1.0 // measurement timeout in Hz (needs to be less than timer1 limits above)
#define DISPLAY_INTERVAL 1.0 // display update interval (and serial send interval) #define DISPLAY_INTERVAL 1.0 // display update interval (and serial send interval)
const float conversion = 4.0; // conversion factor, pulses/s per l/min const float conversion = 4.0; // conversion factor, pulses/s per l/min
const float flow_threshold = 1.0; // alarm threshold for flow const float flow_threshold = 0.5; // alarm threshold for flow
// end user configuration parameters
/* // begin hardware configuration / wiring setup
Hardware configuration / wiring setup // Pin 13 has an LED connected on most Arduino boards.
===================================== // alarm LED pin
#define ALARM_LED_PIN 4
// interlock pin
#define INTERLOCK_PIN 5
#define OVERTEMP_PIN 14
Pin 13 has an LED connected on most Arduino boards. // end hardware configuration / wiring setup
This is the alarm LED pin
*/
#define ALARM_LED_PIN 13
// interlock output pin
#define INTERLOCK_PIN 12
/* // temp. buffer for string manipulation/formatting
Setup routine char buff[10];
============= char old_flow_string[32];
char new_flow_string[32];
char old_status_string[16] = "yyyyyyyyyyyyyyy";
char new_status_string[16];
boolean status_change = true;
boolean status_safety = false;
Run once upon startup
*/
void setup() void setup()
{ {
lcd.begin(16,2); // start LCD (16x2 characters) SPI.setClockDivider(SPI_CLOCK_DIV2);
lcd.home (); // go home TFTscreen.begin();
lcd.print("PFG security"); // string first line // clear the screen with a black background
lcd.setCursor ( 0, 1 ); // go to the next line struct rgb bg;
lcd.print (""); // string second line bg.red = 0;
bg.green = 0;
bg.blue = 0;
TFTscreen.background(bg.red, bg.green, bg.blue);
// write the static text to the screen
// set the font color to white
TFTscreen.stroke(255, 255, 255);
// set the font size
TFTscreen.setTextSize(2);
// write the text to the top left corner of the screen
TFTscreen.text("PFG Safety", 0, 0);
Serial.begin(19200); // serial line speed Serial.begin(19200); // serial line speed
/* Interrupt pins /* Interrupt pins
UNO: UNO:
interrupt 1 is pin 3 interrupt 1 is pin 3
interrupt 0 is pin 2 interrupt 0 is pin 2
*/ */
attachInterrupt(0, get_time, FALLING); attachInterrupt(0, get_time, FALLING);
pinMode(ALARM_LED_PIN, OUTPUT); pinMode(ALARM_LED_PIN, OUTPUT);
pinMode(INTERLOCK_PIN, OUTPUT); pinMode(INTERLOCK_PIN, OUTPUT);
pinMode(OVERTEMP_PIN, INPUT);
// starting values for the deltas array
// otherwise the array values are maybe zero at the beginning and we get very high (wrong) flow values
for (int k = 0; k < N; k++) {
deltas[k] = UINT_MAX;
}
/* /*
Set sensible default starting values for the deltas array, timer1 setup
otherwise the array values are maybe zero at the beginning
and we get very high (wrong) flow values.
*/ */
for (int k=0; k<N; k++){
deltas[k]=UINT_MAX;
}
/*
timer1 setup
*/
// select prescaler according to TIMEOUT
if (TIMEOUT < 0.0327675)
prescaler = 8;
else if (TIMEOUT < 0.26214)
prescaler = 64;
else if (TIMEOUT < 1.04856)
prescaler = 256;
else if (TIMEOUT < 4.19424)
prescaler = 1024;
else {
lcd.setCursor (0,1); // char 0 on line 0 or 1
lcd.print ("ERR TIMEOUT BIG");
setup_ok=false;
return;
}
noInterrupts(); // disable all interrupts noInterrupts(); // disable all interrupts
// clear timer1 registers // clear timer1 registers
TCCR1A = 0; TCCR1A = 0;
TCCR1B = 0; TCCR1B = 0;
TCNT1 = 0; // timer1 counter, reset on every HW interrupt from the flow meter (see get_time function) TCNT1 = 0; // timer1 counter, reset on every HW interrupt pulse from the flow meter (see get_time function)
OCR1A = (int)(16e6/prescaler*TIMEOUT);// Clear timer on compare match value: 16MHz/PRESCALER*timeout(in s) OCR1A = (int)(16e6 / PRESCALER * TIMEOUT); // Clear timer on compare match 16MHz/PRESCALER*timeout(in s)
TCCR1B |= (1 << WGM12); // CTC mode (Clear Timer on Compare Match) TCCR1B |= (1 << WGM12); // CTC mode (Clear Timer on Compare Match)
/* /*
Prescaler settings x=0,1,2 (Timer0, Timer1, Timer2) Prescaler settings x=0,1,2 (Timer0, Timer1, Timer2)
CSx2 CSx1 CSx0 Beschreibung CSx2 CSx1 CSx0 Beschreibung
0 1 0 Clk/8 (1 << CS11) 0 1 0 Clk/8 (1 << CS11)
0 1 1 Clk/64 (1 << CS11)|(1 << CS10) 0 1 1 Clk/64 (1 << CS11)|(1 << CS10)
1 0 0 Clk/256 (1 << CS12) 1 0 0 Clk/256 (1 << CS12)
1 0 1 Clk/1024 (1 << CS12)|(1 << CS10) 1 0 1 Clk/1024 (1 << CS12)|(1 << CS10)
*/ */
if (prescaler == 8) if (PRESCALER == 8) {
TCCR1B |= (1 << CS11); TCCR1B |= (1 << CS11);
else if (prescaler == 64) if (TIMEOUT > 0.0327675) {
TCCR1B |= (1 << CS11)|(1 << CS10); interrupts();
else if (prescaler == 256) //lcd.setCursor (0,1); // char 0 on line 0 or 1
//lcd.print ("CFGERR TIMEOUT");
setup_ok = false;
return;
}
}
else if (PRESCALER == 64) {
if (TIMEOUT > 0.26214) {
interrupts();
//lcd.setCursor (0,1); // char 0 on line 0 or 1
//lcd.print ("CFGERR TIMEOUT");
setup_ok = false;
return;
}
TCCR1B |= (1 << CS11) | (1 << CS10);
}
else if (PRESCALER == 256) {
if (TIMEOUT > 1.04856) {
interrupts();
//lcd.setCursor (0,1); // char 0 on line 0 or 1
//lcd.print ("CFGERR TIMEOUT");
setup_ok = false;
return;
}
TCCR1B |= (1 << CS12); TCCR1B |= (1 << CS12);
else if (prescaler == 1024) }
TCCR1B |= (1 << CS12)|(1 << CS10); else if (PRESCALER == 1024) {
if (TIMEOUT > 4.19424) {
interrupts();
//lcd.setCursor (0,1); // char 0 on line 0 or 1
//lcd.print ("CFGERR TIMEOUT");
setup_ok = false;
return;
}
TCCR1B |= (1 << CS12) | (1 << CS10);
}
else {
interrupts();
// lcd.setCursor (0,1); // char 0 on line 0 or 1
// lcd.print ("CFGERR prescaler");
setup_ok = false;
return;
}
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts interrupts(); // enable all interrupts
delay(500); delay(100);
} }
// if after OCR1A seconds no pulse from flow meter occured an interrupt will force a very high delta value // if after OCR1A seconds no pulse from flow meter occured an interrupt will force a very high delta value
ISR(TIMER1_COMPA_vect) { // interupt service routine which will be called when an interrupt occurs at timer1 ISR(TIMER1_COMPA_vect) { // function which will be called when an interrupt occurs at timer1
deltas[n] = UINT_MAX; // add maximum value in the current array position deltas[n] = UINT_MAX; // add maximum value in the current array position
n++; n++;
if (n==N) n=0; // wrap around if (n == N) n = 0; // wrap around
} }
void loop() void loop()
{ {
if (!setup_ok) return; struct rgb bg;
delay((int) (DISPLAY_INTERVAL*1000)); if (!setup_ok) return;
total = 0;
// sum up all deltas in array
for (int k=0; k<N; k++){
// timeout occured, break out of loop
if (deltas[k] == UINT_MAX) {
total = ULONG_MAX; // and set total to highest possible value
break;
}
total += deltas[k];
}
// calculate average flow
flow = N/(float)total/conversion*16e6/prescaler;
// flow
//flow = total/N/conversion;
// format decimal value and save string in buff
dtostrf(flow,5,3,buff);
/* total = 0;
Security logic // sum up all deltas in array
============== for (int k = 0; k < N; k++) {
// timeout occured, break out of loop
If flow is too small,i.e. flow < flow_threshold: if (deltas[k] == UINT_MAX) {
total = ULONG_MAX; // and set total to highest possible value
break;
}
total += deltas[k];
}
// and calculate average flow
flow = N / (float)total / conversion * 16e6 / PRESCALER;
// flow
//flow = total/N/conversion;
// format decimal value and save string in buff
dtostrf(flow, 5, 3, buff);
/* security logic
if flow is too small,i.e. flow < flow_threshold:
a) alarm LED on a) alarm LED on
b) interlock LOW b) interlock LOW
*/ */
if (flow < flow_threshold) { strcpy(old_flow_string, new_flow_string);
digitalWrite(ALARM_LED_PIN, HIGH); strcpy(old_status_string, new_status_string);
digitalWrite(INTERLOCK_PIN, LOW);
snprintf(serialbuff, sizeof(serialbuff), "ERR %s l/min",buff ); snprintf(new_flow_string, sizeof(new_flow_string), "%s l/min", buff );
Serial.println(new_flow_string); // transmit flow
if ((flow < flow_threshold) ||digitalRead(OVERTEMP_PIN) ) { // flow too small, rise alarm
if (status_safety == true) {
status_change = true;
} }
else { else {
digitalWrite(ALARM_LED_PIN, LOW); status_change = false;
digitalWrite(INTERLOCK_PIN, HIGH);
snprintf(serialbuff, sizeof(serialbuff), "OK %s l/min",buff );
} }
Serial.println(serialbuff); // transmit flow status_safety = false;
digitalWrite(ALARM_LED_PIN, HIGH);
digitalWrite(INTERLOCK_PIN, LOW);
//if (status_change) {
bg.red=255;
bg.green=0;
bg.blue=0;
TFTscreen.background(bg.red, bg.green, bg.blue);
//}
if (digitalRead(OVERTEMP_PIN))
snprintf(new_status_string, sizeof(new_status_string), "Status: %s", "TEMP!" );
else
snprintf(new_status_string, sizeof(new_status_string), "Status: %s", "FLOW!" );
}
else {
if (status_safety == false) {
status_change = true;
}
else {
status_change = false;
}
status_safety = true;
digitalWrite(ALARM_LED_PIN, LOW);
digitalWrite(INTERLOCK_PIN, HIGH);
if (status_change) {
bg.red=0;
TFTscreen.background(bg.red, bg.green, bg.blue);
//TFTscreen.text("Status:", 0, 20);
//TFTscreen.text("l/min", 6 *((20+2)/2), 40);
}
snprintf(new_status_string, sizeof(new_status_string), "Status: %s", "OK" );
}
//Serial.begin(19200); // serial line speed
//Serial.println(new_status_string); // transmit flow
//Serial.end();
writeText(new_status_string, old_status_string, 2, 8 * ((20 + 2) / 2) * 0, 20, bg);
writeText(new_flow_string, old_flow_string, 2, 0, 40, bg);
delay((int) (DISPLAY_INTERVAL * 1000));
lcd.home();
lcd.print (serialbuff); // display flow
lcd.setCursor (0,1); // char 0 on line 0 or 1
//delay(DISPLAY_INTERVAL*1000);
dtostrf(flow_threshold,5,3,buff);
snprintf(serialbuff, sizeof(serialbuff), "THR %s l/min",buff );
lcd.print(serialbuff); // display threshold
} }
void writeText(char* newtext, char* oldtext, int textWidth, int x, int y, struct rgb bg) {
TFTscreen.setTextSize(textWidth);
int n = 13; //max(strlen(newtext),strlen(oldtext));
int charWidth = textWidth * 10 / 2 + 2;
int curPos = x;
for (int i = 0; i < n; i++) {
if ((oldtext[i] != newtext[i]) && (i < strlen(oldtext))) { // only write changed characters
// delete old char
TFTscreen.stroke(0, 0, 0);
TFTscreen.text(&oldtext[i], curPos, y);
}
// write new char
TFTscreen.stroke(255, 255, 255);
TFTscreen.text(&newtext[i], curPos, y);
// draw a rectangle to the end of the screen
if (i >= strlen(newtext)) {
TFTscreen.stroke(bg.red, bg.green, bg.blue);
TFTscreen.rect(curPos, y, TFTscreen.width() - curPos, textWidth * 10);
break;
}
// advance position
curPos += charWidth;
}
}
void get_time() { void get_time() {
deltas[n] = TCNT1; // save current timer1 count deltas[n] = TCNT1; // save current timer1 count
n++; n++;
if (n==N) n=0; // wrap around if (n == N) n = 0; // wrap around
TCNT1 = 0; // reset timer1 counter TCNT1 = 0; // reset timer1 counter
} }