#include #include #include //#include // display setup #define CS 18 #define DC 19 #define RESET 17 // create an instance of the library 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 n = 0; // run variable unsigned long total; float flow; struct rgb { int red=0; 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 /* 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 */ #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) const float conversion = 4.0; // conversion factor, pulses/s per l/min const float flow_threshold = 0.5; // alarm threshold for flow // end user configuration parameters // begin 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 // end hardware configuration / wiring setup // temp. buffer for string manipulation/formatting 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; void setup() { SPI.setClockDivider(SPI_CLOCK_DIV2); TFTscreen.begin(); // clear the screen with a black background struct rgb bg; 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 1.0", 0, 0); Serial.begin(19200); // serial line speed /* Interrupt pins UNO: interrupt 1 is pin 3 interrupt 0 is pin 2 */ attachInterrupt(0, get_time, FALLING); pinMode(ALARM_LED_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; } /* timer1 setup */ noInterrupts(); // disable all interrupts // clear timer1 registers TCCR1A = 0; TCCR1B = 0; 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 16MHz/PRESCALER*timeout(in s) TCCR1B |= (1 << WGM12); // CTC mode (Clear Timer on Compare Match) /* Prescaler settings x=0,1,2 (Timer0, Timer1, Timer2) CSx2 CSx1 CSx0 Beschreibung 0 1 0 Clk/8 (1 << CS11) 0 1 1 Clk/64 (1 << CS11)|(1 << CS10) 1 0 0 Clk/256 (1 << CS12) 1 0 1 Clk/1024 (1 << CS12)|(1 << CS10) */ if (PRESCALER == 8) { TCCR1B |= (1 << CS11); if (TIMEOUT > 0.0327675) { interrupts(); //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); } 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; } //int prescaler = PRESCALER; //snprintf(new_flow_string, sizeof(new_flow_string), "PRESCALER %i", prescaler); //TFTscreen.text("PRESCALER",0,30); TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt interrupts(); // enable all interrupts delay(1000); } // if after OCR1A seconds no pulse from flow meter occured an interrupt will force a very high delta value 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 n++; if (n == N) n = 0; // wrap around } void loop() { struct rgb bg; 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]; } // 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 b) interlock LOW */ strcpy(old_flow_string, new_flow_string); strcpy(old_status_string, new_status_string); 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 { 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(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)); } 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() { deltas[n] = TCNT1; // save current timer1 count n++; if (n == N) n = 0; // wrap around TCNT1 = 0; // reset timer1 counter }