Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: Menu PID_v2 PinDetect QEI TextLCD Thermistor mbed
main.cpp
- Committer:
- walter76
- Date:
- 2015-09-30
- Revision:
- 0:b6fd8ca8bfbf
- Child:
- 1:219e882c32eb
File content as of revision 0:b6fd8ca8bfbf:
#include "mbed.h"
#include "TextLCD.h"
#include "PID.h"
#include "QEI.h"
#define TPIT_MIN 70
#define TPIT_MAX 250
#define TMEAT_MIN 50
#define TMEAT_MAX 120
#define TPIT_FAIL 300
#define TMEAT_FAIL 250
#define MAX_HOURS 24
/*
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
*/
uint8_t Hrs=0, Min=0, Sec=0, warning=0, set_mode=1, timeout=0;
float FanSpeed, Tpit, Tmeat, Tset, Talm;
float Vpot;
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 (D3); // D3=PB3 --> PMW output (FAN control)
AnalogIn POT1 (A0); // A0=PA0 <-- Pit setpoint potentiometer (debug only)
AnalogIn POT2 (A1); // A1=PA1 <-- Meat alarm potentiometer (debug only)
DigitalOut TC_CLK (D8); // D4=PB5 --> MAX6675 CLK (to both chips)
//DigitalOut TC_CS1 (D9); // D5=PB4 --> MAX6675 CS (chip 1)
DigitalOut TC_CS1 (A5); // A0=PC0 --> MAX6675 CS (chip 1)
DigitalOut TC_CS2 (D2); // D2=PA10 --> MAX6675 CS (chip 2)
DigitalIn TC_SDO (D4); // D8=PA9 <-- MAX6675 SDO (from both chips)
DigitalOut BUZZER (A2); // A2=PA4 --> self-oscillating buzzer
PwmOut ALM (D9);
InterruptIn Button (D7); // D7=PA8 <-- Hold/Set pushbutton
PID myPID(0.003, 60.0, 0.0, 0.5); // Specify Kc, Ti, Td & refresh rate
QEI Wheel(D5, D6, NC, 16); // quadrature encoder
TextLCD MyLCD(D15, D14, D13, D12, D11, D10, TextLCD::LCD20x4); //20x4 LCD connection
Ticker Sec_Beat; // timer ticker
Ticker LCD_Refresh; // LCD refresh ticker
// ------------------- Prototypes -----------------------
void Button_Pressed(void);
void Timer_tick(void);
void Update_LCD(void);
int MAX6675_GetTemp(int);
// -------------------- Routines ------------------------
void Button_Pressed() {
set_mode++;
if(set_mode>3) set_mode = 0;
Button.fall(NULL);
Wheel.reset();
}
// -------------------------------------------------------------
void Timer_tick()
{
if((Hrs==0)&&(Min==0)&&(Sec==0))
timeout = 1;
else
{
timeout = 0;
BUZZER = 0;
if ((!set_mode)&&(!timeout))
{
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--;
}
}
}
}
Tmeat = (float)(MAX6675_GetTemp(0)/4); // read meat & pit temperature
Tpit = (float)(MAX6675_GetTemp(1)/4);
if(timeout||warning) // toggle the buzzer
BUZZER=!BUZZER;
return;
}
// -------------------------------------------------------------
void Update_LCD(void) {
switch(set_mode)
{
case 0:
MyLCD.locate(19,0);
MyLCD.printf(" ");
MyLCD.locate(19,1);
MyLCD.printf(" ");
MyLCD.locate(19,2);
MyLCD.printf(" ");
break;
case 1: // set cooking time
MyLCD.locate(19,0);
MyLCD.printf("*");
Min = Sec = 0;
Hrs = int(Wheel.getPulses());
if((Hrs>MAX_HOURS))
{
Wheel.reset();
Hrs = MAX_HOURS;
}
break;
case 2: // set meat temperature alarm
MyLCD.locate(19,1);
MyLCD.printf("*");
Talm = TMEAT_MIN + int(Wheel.getPulses());
if((Talm<TMEAT_MIN)||(Talm>TMEAT_MAX))
{
Wheel.reset();
Talm=TMEAT_MIN;
}
break;
case 3: // set pit target temperature
MyLCD.locate(19,2);
MyLCD.printf("*");
Tset = TPIT_MIN + int(Wheel.getPulses());
if((Tset<TPIT_MIN)||(Tset>TPIT_MAX))
{
Wheel.reset();
Tset=TPIT_MIN;
}
break;
default:
set_mode = 0;
};
//Tmeat = (float)(MAX6675_GetTemp(0)/4);
//Tpit = (float)(MAX6675_GetTemp(1)/4);
//Tmeat = TMEAT_MIN + POT1*(TMEAT_MAX-TMEAT_MIN)-5; // debug only
//Tpit = 2+TPIT_MIN + POT2*(TPIT_MAX-TPIT_MIN); // debug only
//-------------------------------------------------
if(Tmeat>Talm) // Meat is ready!
warning = 1;
else
warning = 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
FAN.write(FanSpeed); // update fan speed
MyLCD.locate(0,3);
MyLCD.printf(" FAN SPEED: ");
MyLCD.locate(12,3);
MyLCD.printf("%2.0f%% ", FanSpeed*100);
}
//-------------------------------------------------
if(Tpit>300) // if Tpit is too high
{
MyLCD.locate(0,2);
MyLCD.printf(" CHECK PIT PROBE! ");
FAN.write(0); // stop the fan
warning=1;
}
else // otherwise show pit temperature and setpoint
{
warning=0;
MyLCD.locate(0,2);
MyLCD.printf(" TPIT= ");
MyLCD.locate(8,2);
MyLCD.printf("%2.0f ", Tpit);
MyLCD.locate(13,2);
MyLCD.printf("(%2.0f) ", Tset);
}
//-------------------------------------------------
if(Tmeat>200) // if Tmeat is too high
{
MyLCD.locate(0,1);
MyLCD.printf(" CHECK MEAT PROBE! ");
FAN.write(0); // stop the fan
warning=1;
}
else // otherwise show meat temperature and alarm threshold
{
warning=0;
MyLCD.locate(0,1);
MyLCD.printf(" TMEAT= ");
MyLCD.locate(8,1);
MyLCD.printf("%2.0f ", Tmeat);
MyLCD.locate(13,1);
MyLCD.printf("(%2.0f) ", Talm);
}
MyLCD.locate(0,0);
MyLCD.printf(" TIMER: %2d:%2d:%2d ", Hrs, Min, Sec);
Button.fall(&Button_Pressed); // enable button interrupt
}
// -------------------------------------------------------------
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(100); // set initial fan PWM period and duty-cycle
FAN.write(0.1);
ALM.period_us(2000); // set initial ALM period and duty-cycle
ALM.write(0.2);
TC_SDO.mode(PullDown); // enable pull-down on TC_SDO
Button.mode(PullUp); // enable pushbutton pull-up
Wheel.reset(); // reset quadrature encoder
MyLCD.cls(); // clear LCD
Sec_Beat.attach(&Timer_tick, 1); // configure tickers
LCD_Refresh.attach(&Update_LCD, 0.2);
myPID.setInputLimits(0, 300); // set PID in/out ranges
myPID.setOutputLimits(0, 1);
Button.fall(&Button_Pressed); // enable button interrupt
while(1) {
myPID.setSetPoint(Tset); // update pit setpoint (PID reference)
myPID.setProcessValue(Tpit); // update pit temperature (PID input)
FanSpeed = float(myPID.compute()); // calculate fan speed (PID output)
} //while
} //main