New
Dependencies: Menu PID_v2 PinDetect QEI TextLCD Thermistor mbed
main.cpp
- Committer:
- albertobianco
- Date:
- 2016-06-09
- Revision:
- 4:800e1df45650
- Parent:
- 3:884bfa90a09d
File content as of revision 4:800e1df45650:
#include "mbed.h" #include "TextLCD.h" #include "PID.h" #include "QEI.h" #include "Menu.h" #include "PinDetect.h" #include "Thermistor.h" #define TPIT_MIN 0.0 #define TPIT_MAX 250.0 #define TCOAL_MIN 0.0 #define TCOAL_MAX 1000.0 #define TMEAT_MIN 50 #define TMEAT_MAX 120 #define MAX_HOURS 24 #define PULLUP_MEAT 10000.0 /* This FW implements a PID temperature controller specifically designed for Ugly Drum Smokers (UDS) and uses a K-type termocouple as pit temperature sensors and a PWM-controlled fan to regulate the airflow at the intake. A second K-type termocouple is used to monitor the meat temperature, while a countdown timer takes care of the cooking time. The required HW consists of: - 1x STM32 F411RE Nucleo board - 1x 20x4 Text LCD - 1x Quadrature rotary encoder - 1x Pushbutton (if not embedded in the encoder) - 2x MAX6675 modules - 2x K-type termocouples (pit & meat) - 1x Buzzer - 1x 12V fan - 1x N-MOS capable of driving the fan - 1x fly-wheeling diode */ int Hrs=0, Min=0, Sec=0, warning=0, timeout=0; int TimerRunning = 0; float FanSpeed, TcoalPID, Tcoal, Tpit, Tmeat, Tset; float Talm = 200.0; float TcoalSet, DfanSet; float Vpot; uint8_t req_LCD_update; uint8_t displayLine; int pitPidMode = 0; int coalPidMode = 0; char stringa[20]; void UpdatePidMode(void); void SetCoalTemp(void); void SetFanDuty(void); void SetPIDK(void); void ResetSeconds(void); void SetPitTemp(void); float coalKc = 0.1, coalTauI = 6.0; float pitKc = 0.1, pitTauI = 60.0; MenuNode MNhead(NULL,"Exit"); MenuNode MNpid(&MNhead,"PID settings"); //MenuNode MNpitap(&MNpid,"Pit Auto PID"); MenuNode MNpitPidTarget(&MNhead,"Pit Target temp",(void *) &Tset,'f',&SetPitTemp,1.0,TPIT_MIN,TPIT_MAX); MenuNode MNpitPidMode(&MNpid,"Pit PID mode",(void *) &pitPidMode,'i',&UpdatePidMode,1.0,0.0,1.0,(char *[]){"Manual","Auto"}); MenuNode MNcoalPidMode(&MNpid,"Coal PID mode",(void *) &coalPidMode,'i',&UpdatePidMode,1.0,0.0,1.0,(char *[]){"Manual","Auto"}); MenuNode MNpitPidOutput(&MNpid,"Coal Temp (man)",(void *) &TcoalSet,'f',&SetCoalTemp,5.0,TCOAL_MIN,TCOAL_MAX); MenuNode MNcoalPidOutput(&MNpid,"Fan Speed (man)",(void *) &DfanSet,'f',&SetFanDuty,0.05,0.0,1.0); MenuNode MNcoalKc(&MNpid,"Coal Kc",(void *) &coalKc,'e',&SetPIDK,1.25,0.0001,100.0); MenuNode MNcoalTauI(&MNpid,"Coal TauI",(void *) &coalTauI,'e',&SetPIDK,1.25,1.0,1000.0); MenuNode MNpitKc(&MNpid,"Pit Kc",(void *) &pitKc,'e',&SetPIDK,1.25,0.0001,100.0); MenuNode MNpitTauI(&MNpid,"Pit TauI",(void *) &pitTauI,'e',&SetPIDK,1.25,1.0,10000.0); MenuNode MNtmr(&MNhead,"Timer settings"); MenuNode MNtmrRun(&MNtmr,"Timer mode",(void *) &TimerRunning,'i',NULL,1.0,0.0,2.0,(char *[]){"Stop","Countdown", "Timer"}); MenuNode MNtmrHrs(&MNtmr,"Hours",(void *) &Hrs,'i',&ResetSeconds,1.0,0.0,24.0); MenuNode MNtmrMin(&MNtmr,"Minutes",(void *) &Min,'i',&ResetSeconds,5.0,0.0,55.0); MenuNode MNtalm(&MNhead,"Meat Tgt temp", &Talm,'f', NULL, 1.0, 30.0, 150.0); Menu MN(&MNhead); Thermistor MeatThermistor(25.0, 100000.0, 3950.0); //DigitalOut RLED (PC_5); // Pin 2 of CN10 (MORPHO connector, debug only) //DigitalOut YLED (PC_6); // Pin 4 of CN10 (MORPHO connector, debug only) //DigitalOut GLED (PC_8); // Pin 6 of CN10 (MORPHO connector, debug only) PwmOut FAN (D5); // D3=PB3 --> PMW output (FAN control) DigitalOut TC_CLK (D4); // D4=PB5 --> MAX6675 CLK (to both chips) DigitalOut TC_CS1 (A5); // A0=PC0 --> MAX6675 CS (chip 1) DigitalOut TC_CS2 (D2); // D2=PA10 --> MAX6675 CS (chip 2) DigitalIn TC_SDO (D8); // D8=PA9 <-- MAX6675 SDO (from both chips) //DigitalOut BUZZER (A2); // A2=PA4 --> self-oscillating buzzer PwmOut ALM (D9); PinDetect Button (D7); // D7=PA8 <-- Hold/Set pushbutton AnalogIn MeatIn (A0); PID pitPID(0.1, 60.0, 0.0, 1.0); // Specify Kc, Ti, Td & refresh rate PID coalPID(0.1, 6.0, 0.0, 1.0); // Specify Kc, Ti, Td & refresh rate QEI Wheel(D3, D6, NC, 16, QEI::X4_ENCODING); // quadrature encoder TextLCD MyLCD(D15, D14, D13, D12, D11, D10, TextLCD::LCD16x2); //20x4 LCD connection Ticker Sec_Beat; // timer ticker //Ticker decSecBeat; // 1/10th sec beat // ------------------- Prototypes ----------------------- void Button_Pressed(void); void Timer_tick(void); void Update_LCD(void); int MAX6675_GetTemp(int); void ResetSeconds(void) { Sec = 0; } // -------------------- Routines ------------------------ void SetPIDK(void) { pitPID.setTunings(pitKc, pitTauI, 0.0); coalPID.setTunings(coalKc, coalTauI, 0.0); } void SetCoalTemp(void) { if(!pitPID.isAuto()) pitPID.setOutput(TcoalSet); } void SetPitTemp(void) { pitPID.setSetPoint( Tset ); } void SetFanDuty(void) { if(!coalPID.isAuto()) coalPID.setOutput(DfanSet); } void Button_Pressed() { if(!MN.isLeaf()) MN.descend(); else { if(MN.isSelect()) MN.setEdit(); else if(MN.isEdit()) { MN.resetEdit(); MN.execFun(); } } req_LCD_update = 1; Wheel.reset(); } void UpdatePidMode(void) { coalPID.setMode(coalPidMode); pitPID.setMode(pitPidMode); } // ------------------------------------------------------------- void Timer_tick() { float Rmeat; if((Hrs==0)&&(Min==0)&&(Sec==0) && (TimerRunning == 1)) timeout = 1; else { timeout = 0; if (TimerRunning == 1) { Sec--; if((Hrs==0)&&(Min==0)&&(Sec==0)) timeout = 1; else if((Sec<=0)||(Sec>60)) { Sec=59; if(Min>0) Min--; else if (Hrs>0) { Min=59; Hrs--; } } } else if ((TimerRunning == 2) ) { Sec++; if((Sec>=60)) { Sec=0; Min++; if(Min>=60) { Min = 0; Hrs++; } } } } Tcoal = (float)(MAX6675_GetTemp(0)/2); // read meat & pit temperature Tpit = (float)(MAX6675_GetTemp(1)/2); pitPID.setProcessValue(Tpit); TcoalPID = pitPID.compute(); coalPID.setSetPoint( TcoalPID ); coalPID.setProcessValue(Tcoal); FanSpeed = coalPID.compute(); FAN.write(FanSpeed); if(MN.isHead()) req_LCD_update = 1; Rmeat = MeatIn.read(); Rmeat = (Rmeat * PULLUP_MEAT) / (1 - Rmeat); Tmeat = MeatThermistor.trans_R2T(Rmeat); if(Tmeat>Talm || timeout) // Meat is ready! warning = 1; else warning = 0; return; } // ------------------------------------------------------------- int saturate(int data, int min, int max) { if(data > max) return max; else if (data < min) return min; else return data; } char * idleString(int n, char *dst) { int tcoal_l, tpit_l, tmeat_l; switch(n) { case 0: sprintf(dst,"%02d:%02d:%02d Tgt %02dC",Hrs,Min,Sec,int(Talm)); break; case 1: tcoal_l = saturate(int(Tcoal),0,999); tpit_l = saturate(int(Tpit),0,999); tmeat_l = saturate(int(Tmeat),0,999); sprintf(dst,"C%3dC P%3dC M%2dC",tcoal_l,tpit_l,tmeat_l); break; case 2: sprintf(dst,"d%3d CP%4dC ",int(FanSpeed * 100), int(TcoalPID)); break; case 3: sprintf(dst,"Ciao Mamma"); break; } return dst; } void Update_LCD(void) { if(MN.isHead()) { MyLCD.locate(0,0); MyLCD.printf(idleString(displayLine,stringa)); MyLCD.locate(0,1); MyLCD.printf(idleString((displayLine+1)&0x03,stringa)); } else { MyLCD.locate(0,0); MyLCD.printf("%-16s",MN.getText()); if(MN.isSelect()) { MyLCD.locate(0,1); MyLCD.printf("%-16s",MN.getNextText()); } else { MyLCD.locate(0,1); MyLCD.printf("%-16s",MN.getDataText(stringa)); } } //Button.fall(&Button_Pressed); #if 0 if(warning||timeout) // in case of a warning or timeout { MyLCD.locate(0,3); MyLCD.printf(" ---- WARNING! ---- "); //FAN.write(0); // stop the fan (optional) } else // otherwise { BUZZER = 0; // buzzer off } #endif } // ------------------------------------------------------------- int MAX6675_GetTemp(int chip) { int Temp=0, mask=32768; int i; if(chip==0) TC_CS1 = 0; // select chip #1 else TC_CS2 = 0; // select chip #2 wait_ms(1); for(i=0; i<16; i++) { TC_CLK = 1; // CLK high if(TC_SDO == 1 ) Temp = Temp | mask; wait_ms(1); TC_CLK = 0; // CLK low wait_ms(1); mask = mask/2; } wait_ms(1); TC_CS1 = 1; // Both CS high TC_CS2 = 1; Temp=((Temp>>3)&0x0FFF); return(Temp); } // ------------------------------------------------------------- // ------------------------------------------------------------- // ------------------------------------------------------------- int main() { FAN.period_us(500); // set initial fan PWM period and duty-cycle FAN.write(0.1); pitPID.setInputLimits(TPIT_MIN, TPIT_MAX); pitPID.setOutputLimits(TCOAL_MIN, TCOAL_MAX); coalPID.setInputLimits(TCOAL_MIN, TCOAL_MAX); coalPID.setOutputLimits(0.0, 1.0); ALM.period_us(500); // set initial ALM period and duty-cycle ALM.write(0.01); TC_SDO.mode(PullDown); // enable pull-down on TC_SDO Button.mode(PullUp); // enable pushbutton pull-up Button.setSampleFrequency( 20000 ); //Wheel.reset(); // reset quadrature encoder Hrs = 2; timeout = 0; MyLCD.cls(); // clear LCD MyLCD.locate(0,0); MyLCD.printf(__TIME__, Hrs, Min, Sec); MyLCD.locate(1,1); MyLCD.printf(__DATE__, Hrs, Min, Sec); Sec_Beat.attach(&Timer_tick, 1); // configure tickers //decSecBeat.attach(&Fast_tick, 1); // configure tickers FanSpeed = 0; //Button.fall(&Button_Pressed); // enable button interrupt Button.attach_deasserted( &Button_Pressed ); while(1) { if(req_LCD_update) { Update_LCD(); req_LCD_update = 0; } if(warning) ALM.write(0.01); else ALM.write(0.0); if(MN.isSelect()) { if(Wheel.getPulses() > 0) { MN.next(); Wheel.reset(); req_LCD_update = 1; } else if(Wheel.getPulses() < 0) { MN.prev(); Wheel.reset(); req_LCD_update = 1; } } else if(MN.isEdit()) { if(Wheel.getPulses()) { MN.edit(Wheel.getPulses()); Wheel.reset(); req_LCD_update = 1; } } else if(MN.isIdle()) { if(Wheel.getPulses() > 0) { displayLine++; displayLine &= 0x3; Wheel.reset(); req_LCD_update = 1; } else if(Wheel.getPulses() < 0) { displayLine--; displayLine &= 0x3; Wheel.reset(); req_LCD_update = 1; } } } } //main