Checks now status of temperature controller relais
output
This commit is contained in:
		
							
								
								
									
										327
									
								
								pfg_security.ino
									
									
									
									
									
								
							
							
						
						
									
										327
									
								
								pfg_security.ino
									
									
									
									
									
								
							| @@ -1,78 +1,101 @@ | |||||||
| #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: | ||||||
| @@ -82,42 +105,25 @@ void setup() | |||||||
|   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 | ||||||
|   Set sensible default starting values for the deltas array, |   // otherwise the array values are maybe zero at the beginning and we get very high (wrong) flow values | ||||||
|   otherwise the array values are maybe zero at the beginning  |   for (int k = 0; k < N; k++) { | ||||||
|   and we get very high (wrong) flow values. |     deltas[k] = UINT_MAX; | ||||||
|   */ |  | ||||||
|   for (int k=0; k<N; k++){ |  | ||||||
|     deltas[k]=UINT_MAX; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   /* |   /* | ||||||
|     timer1 setup |     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) | ||||||
| @@ -127,34 +133,73 @@ void setup() | |||||||
|     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() | ||||||
| { | { | ||||||
|  |   struct rgb bg; | ||||||
|   if (!setup_ok) return; |   if (!setup_ok) return; | ||||||
|     delay((int) (DISPLAY_INTERVAL*1000)); |  | ||||||
|   total = 0; |   total = 0; | ||||||
|   // sum up all deltas in array |   // sum up all deltas in array | ||||||
|     for (int k=0; k<N; k++){ |   for (int k = 0; k < N; k++) { | ||||||
|     // timeout occured, break out of loop |     // timeout occured, break out of loop | ||||||
|     if (deltas[k] == UINT_MAX) { |     if (deltas[k] == UINT_MAX) { | ||||||
|       total = ULONG_MAX;  // and set total to highest possible value |       total = ULONG_MAX;  // and set total to highest possible value | ||||||
| @@ -162,49 +207,113 @@ void loop() | |||||||
|     } |     } | ||||||
|     total += deltas[k]; |     total += deltas[k]; | ||||||
|   } |   } | ||||||
|     // calculate average flow |   // and calculate average flow | ||||||
|     flow = N/(float)total/conversion*16e6/prescaler; |   flow = N / (float)total / conversion * 16e6 / PRESCALER; | ||||||
|  |  | ||||||
|   // flow |   // flow | ||||||
|   //flow = total/N/conversion; |   //flow = total/N/conversion; | ||||||
|   // format decimal value and save string in buff |   // format decimal value and save string in buff | ||||||
|     dtostrf(flow,5,3,buff); |   dtostrf(flow, 5, 3, buff); | ||||||
|  |  | ||||||
|     /*  |   /* security logic | ||||||
|     Security logic |  | ||||||
|     ============== |  | ||||||
|  |  | ||||||
|     If flow is too small,i.e. flow < flow_threshold: |     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 { | ||||||
|  |       status_change = false; | ||||||
|  |     } | ||||||
|  |     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(ALARM_LED_PIN, LOW); | ||||||
|     digitalWrite(INTERLOCK_PIN, HIGH); |     digitalWrite(INTERLOCK_PIN, HIGH); | ||||||
|       snprintf(serialbuff, sizeof(serialbuff), "OK  %s l/min",buff ); |     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); | ||||||
|     } |     } | ||||||
|     Serial.println(serialbuff);        // transmit flow |     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 | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user