Brew heater controller. Programmed to mash grain and to boil wort
Dependencies: mbed QEI UniGraphic
main.cpp
- Committer:
- dswood
- Date:
- 2022-01-07
- Revision:
- 0:c673d397e9dc
File content as of revision 0:c673d397e9dc:
/** Spice modal .SUBCKT NTCLE203E3103SB0 RN Rp PARAMS: TOLR=0 TOLB=0 X64 Rn Rp NTC_BASE Params: + w=-14.6571976 + x=4798.842 + y=-115334 + z=-3730535 + gth=0.0032 gth1 = 0.0000167 + cth=0.032 + a=-14.65719769 + r25=10000 + b=4798.842 + c=-115334 + d=-3730535 + T0=273.15 + TR={1+TOLR/100} + TB={1+TOLB/100} .ENDS */ /* Includes ------------------------------------------------------------------*/ /* mbed specific header files. */ /* GPIO_InitTypeDef GPIO_InitStruct; //Configure GPIO pin : PB14 GPIO_InitStruct.Pin = GPIO_PIN_14; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // digital Output GPIO_InitStruct.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); */ #include "mbed.h" #include "SSD1306.h" #include "string" #include "MashProfile.h" #include "MashProfileFull.h" #include "Boil1.h" #include "Boil2.h" #include "Boil3.h" #ifndef PROFILES #define PROFILES #include "Profiles.h" #endif //Profiles #define Height 32 #define Width 132 #include "QEI.h" //Firing delay in microseconds #define SyncToZeroCrossingDelayRise 115 #define SyncToZeroCrossingDelayFall 115 #define TriacPulseLength 250 // Blinking rate in milliseconds #define BLINKING_RATE_MS 500 #define Bias_Current 0.261 // Thermistor current in mA /* FIR filter designed with http://t-filter.appspot.com sampling frequency: 2000 Hz * 0 Hz - 100 Hz gain = 1 desired ripple = 5 dB actual ripple = 1.0675530446538501 dB * 600 Hz - 1000 Hz gain = 0 desired attenuation = -60 dB actual attenuation = -71.5928467318133 dB */ #define NumberOfTaps 9 static double filter_taps[NumberOfTaps] = { 0.009246906411528302, 0.0484176527692072, 0.12764201806827455, 0.21665898750162413, 0.25663867508629506, 0.21665898750162413, 0.12764201806827455, 0.0484176527692072, 0.009246906411528302 }; //float PotBuf[NumberOfTaps]; float TempBuf[NumberOfTaps]; int CycleCount=0,StartT=0; bool FireTriac,Boiling,DisplayFlag; /*------- inputs ---------*/ InterruptIn FallingEdge(D12); InterruptIn RisingEdge(D13); //AnalogIn Potentiometer(A0); // Thermistor (ntc) with a constant current flowing through it AnalogIn Thermistor(A1); //Rotary Encoder for entering values and navigation QEI wheel (D9, D8, NC, 20); DigitalIn Button(PC_9); InterruptIn ButtonPressed(PC_9); /*------- inputs -end-----*/ /*------- outputs ---------*/ //DigitalOut led(LED1); // Output to switch on the LED in the opto to fire the triac DigitalOut Triac(D10),OledReset(D6); // Test output to see timings and states on a scope DigitalOut Test(D15); // The screen SSD1306 MyOled(SPI_8, 10000000, D4, D5, D3, D2, D6, D7,"MyOled", Width, Height); // Spi 8bit, 10MHz, mosi, miso, sclk, cs, reset, dc // fixme clock may need to be 5MHz // 5000000 gives a clock of 3.125MHz (genius) // 10000000 gives 6.25 // 12800000 gives 12.8Mhz still works // 20000000 gives 12.5Mhz // the nearest i can find to 8MHz is 10000000 /*------- outputs -end-----*/ char Buff[3][20]; Timeout SyncDelay,TriacPulseTimeout,ReadTemperature; //float Pot; int FireCount; int Demand; float valT,LastTemp=0,LastError=0,Integrated=0; bool Rise,MainsOn; time_t now; Timer ElapsedTime; uint64_t LastTime,TempTime,DebounceTime,StartTime,DemandTime,CheckTime,KnobTime,CycleTime; struct Controls { float Temperature; int8_t PercentPower; int16_t TimeSeconds; int8_t ProfileNumber; int8_t ElementNumber; uint8_t MessageNo; goal Goal; char Message[20]; }; struct Controls MyControls= {25.0,50,0}; struct Profile *ProfileArray[5]= { &MashProfile,&MashProfileFull,&Boil1,&Boil2,&Boil3}; enum VariableType { Index, TempC, PwrW, TimeS }; struct MenuType { int8_t IndexVal; //The current thing pointed to by the cursor int8_t Number; int8_t MaxIndex; int8_t Offset; VariableType CurrentType; void EncoderHandler( bool Increment, VariableType ChangeType); struct Controls *Cntrl; // points to the main loop controls char *MenuText[8]; //8 lines of menus to start with void (*NextMenu[8])(); bool Switch; }; Serial Mypc(USBTX, USBRX, 115200); //for debugging int64_t DebugValue; void FiringCounter(int Power); void PulseTriac(); void SyncFall(); void SyncRise(); //char *Menu[2]={"Manual","Programmed"}; int string_length(char* given_string) { // variable to store the // length of the string int length = 0; while (*given_string != '\0') { length++; given_string++; } return length; } void MenuType::EncoderHandler( bool Increment, VariableType ChangeType) { int Multiplier=1,t; t=ElapsedTime.read_ms()-KnobTime; if (t<50) Multiplier=10; else if (t<100) Multiplier=5; else if (t<150) Multiplier=2; KnobTime=ElapsedTime.read_ms(); switch (ChangeType) { case Index: if (Increment) IndexVal++; else IndexVal--; if (IndexVal>MaxIndex) IndexVal=0; if (IndexVal<0) IndexVal=MaxIndex; if ((IndexVal-Offset)>2) Offset=IndexVal-2; if ((IndexVal-Offset)<0) Offset=IndexVal; break; case TempC: if (Increment) Cntrl->Temperature=Cntrl->Temperature+(float)0.1*(float)Multiplier; else Cntrl->Temperature=Cntrl->Temperature-(float)0.1*(float)Multiplier; if (Cntrl->Temperature>120)Cntrl->Temperature=120; if (Cntrl->Temperature<25)Cntrl->Temperature=25; break; case PwrW: if (Increment) Cntrl->PercentPower+=Multiplier; else Cntrl->PercentPower-=Multiplier; if (Cntrl->PercentPower>100)Cntrl->PercentPower=100; if (Cntrl->PercentPower<=0)Cntrl->PercentPower=1; break; case TimeS: Multiplier=Multiplier*Multiplier; if (Increment) Cntrl->TimeSeconds+=Multiplier; else Cntrl->TimeSeconds-=Multiplier; if (Cntrl->TimeSeconds>10800)Cntrl->TimeSeconds=10800; if (Cntrl->TimeSeconds<0)Cntrl->TimeSeconds=0; break; } } struct MenuType MainMenu; void SetTopLevelMenu(); void SetProgrammedMenu(); void SetManualMenu(); void ShowProgramMenu(); void StartProfileMenu(); void DoNothing(); void AlterDataMenu(); void LoadProfile(int Prof,int Ele); void SetProfile(); void ProfileMenu(); void GetTemperature(); void DisplayNow(); void DisplayMenu(); void SetOutputSpeed(); void PulseTriac() { if (Triac==1) { Triac=0; Test=0; //fixme } else { Triac=1;// Fire the triac Test=1; TriacPulseTimeout.attach_us(&PulseTriac,TriacPulseLength); } } void MenuSetup() { MainMenu.Offset=0; MainMenu.IndexVal=0; MainMenu.Cntrl=&MyControls; SetTopLevelMenu(); } void SyncFall() { int64_t t; /* After many attempts to get this right t should be a uint64_t BUT whan you do a comparison if(uint64_t> 99) it all goes to crap. I think you end up with an overflow in the math used to evaluate it. So only do comparicons with signed numbers int or float. */ if (!Rise) return; Rise=false; t=ElapsedTime.read_us()-CycleTime; DebugValue=t; CycleTime=ElapsedTime.read_us(); if (DisplayFlag) return; if ((t>20400)||(t<19600)) { CycleCount=0; MainsOn=false; } if (CycleCount<10) { CycleCount++; return; } MainsOn=true; if (FireTriac) SyncDelay.attach_us(&PulseTriac,SyncToZeroCrossingDelayFall); FiringCounter(Demand); ReadTemperature.attach_us(&GetTemperature,9000); //read temp if mains on synced // the noise generated on triac firing upsets the temperature measurment } void SyncRise() { int64_t t; Test=1; if (Rise) return; Rise=true; t=ElapsedTime.read_us()-CycleTime; if (DisplayFlag) return; if ((t>10200)||(t<9800)) { CycleCount=0; MainsOn=false; } if (CycleCount<10) { return; } Test=0; if (FireTriac) SyncDelay.attach_us(&PulseTriac,SyncToZeroCrossingDelayRise); } float ReadResistance() // Read voltage across thermistor { int i,j; float tmp=0; j=StartT; TempBuf[StartT]=Thermistor; for (i=0; i<NumberOfTaps; i++) { if (j<0) j+=NumberOfTaps; tmp+=TempBuf[j]*filter_taps[i]; j--; if (j<0) j=NumberOfTaps-1; } StartT--; if (StartT<0) StartT=NumberOfTaps-1; /* tmp is the fraction 3.3V across thermistor. Resistance = V/I Resistance = (tmp*3.3V)/Bias_Current */ tmp=(tmp*(float)3.3)/(float)Bias_Current; // Answer is in Kohm because current is in mA return tmp; } float ResistanceToTemp(float Res) { /* T0 = 25C = 298K beta =3977 R0=10000 ohm */ Res=Res+(float)0.0001; //Res can't be zero float temp; // temp here is both temperary and temperature temp=log(Res/(float)10)/(float)3977.0; //beta from datasheet temp=(float)1/(temp+(float)0.003354); // 1/298.15 //temp=temp-(float)273.15 ;//kelvin to c conversion /* ok so the temp is miles off due to things like the sensor being pressed against the outside of the barrel. Loosing heat to ambient etc. So lets compensate for that with an offset and a scale factor. */ temp=(temp*(float)1.09)-(float)22; temp=temp-(float)273.15 ;//kelvin to c conversion return temp; } void GetTemperature() { if (DisplayFlag) return; // The noise generated by the comms to the display causes 0.5C error valT=ResistanceToTemp(ReadResistance()); } void Pressed() { int t; t=ElapsedTime.read_ms()-DebounceTime; DebounceTime=ElapsedTime.read_ms(); if (t<250) return; //Mypc.printf("Index %d \n",MainMenu.IndexVal); MainMenu.Number=MainMenu.IndexVal; MainMenu.NextMenu[MainMenu.IndexVal](); } void setup() { set_time(1256729737); // Set RTC time to Wed, 28 Oct 2009 11:35:37 now=time(NULL); // Mypc.printf("Setup"); Triac=0; // Low to fire FallingEdge.fall(&SyncFall); //falling mains voltage RisingEdge.fall(&SyncRise); //rising mains voltage FireCount=0; ButtonPressed.fall(&Pressed); //SetOutputSpeed(); MyOled.BusEnable(true);//Chip select MyOled.FastWindow(true); MyOled.set_orientation(3); MyOled.locate(0,0); MyOled.set_font((unsigned char*) Terminal6x8,32,127,false); MyOled.cls(); MyOled.BusEnable(false);//Chip select ButtonPressed.fall(&Pressed); ElapsedTime.start(); LastTime=ElapsedTime.read_ms(); DebounceTime=LastTime; CycleTime=ElapsedTime.read_us(); TempTime=LastTime; CheckTime=LastTime; DemandTime=LastTime; KnobTime=LastTime; MenuSetup(); } void SetOutputSpeed() { GPIO_InitTypeDef GPIO_InitStruct; //Configure GPIO pin : PB_3 D3 GPIO_InitStruct.Pin = GPIO_PIN_3; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // digital Output GPIO_InitStruct.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); } void FiringCounter(int Power) { int x; x=Power; if (x>100) x=100; if (x<0) x=0; if (FireCount<0) { FireCount+=100-x; FireTriac=true; } else { FireCount-=x; FireTriac=false; } } void SetTopLevelMenu() { MainMenu.IndexVal=0; MainMenu.MaxIndex=2; MainMenu.Offset=0; MainMenu.MenuText[0]=TopLevelMenu[0]; MainMenu.MenuText[1]=TopLevelMenu[1]; MainMenu.MenuText[2]=TopLevelMenu[2]; MainMenu.CurrentType=Index; MainMenu.NextMenu[0]=&SetTopLevelMenu; MainMenu.NextMenu[1]=&SetManualMenu; MainMenu.NextMenu[2]=&SetProgrammedMenu; MainMenu.NextMenu[3]=&SetTopLevelMenu;//Set it to something that is valid MainMenu.NextMenu[4]=&SetTopLevelMenu; MainMenu.NextMenu[5]=&SetTopLevelMenu; MyControls.Temperature= 25.0; MyControls.PercentPower=1; MyControls.TimeSeconds=0; MainMenu.Switch=true; } void SetProgrammedMenu() { MainMenu.Offset=0; MainMenu.IndexVal=0; MainMenu.MaxIndex=5;//12345678901234567890 MainMenu.MenuText[0]="Previous Menu "; MainMenu.MenuText[1]=MashProfile.Name; MainMenu.MenuText[2]=MashProfileFull.Name; MainMenu.MenuText[3]=Boil1.Name; MainMenu.MenuText[4]=Boil2.Name; MainMenu.MenuText[5]=Boil3.Name; MainMenu.NextMenu[0]=&SetTopLevelMenu; MainMenu.NextMenu[1]=&SetProfile; MainMenu.NextMenu[2]=&SetProfile; MainMenu.NextMenu[3]=&SetProfile; MainMenu.NextMenu[4]=&SetProfile; MainMenu.NextMenu[5]=&SetProfile; MainMenu.CurrentType=Index; MainMenu.Switch=true; } void SetManualMenu() { MainMenu.IndexVal=0; MainMenu.MaxIndex=3; MainMenu.Offset=0; // 1234567890123456789 MainMenu.MenuText[0]="Manual Menu "; MainMenu.MenuText[1]="Temperature"; MainMenu.MenuText[2]="Power Level"; MainMenu.MenuText[3]="Time "; MainMenu.NextMenu[0]=&SetTopLevelMenu; MainMenu.NextMenu[1]=&AlterDataMenu; MainMenu.NextMenu[2]=&AlterDataMenu; MainMenu.NextMenu[3]=&AlterDataMenu; MainMenu.NextMenu[4]=&DoNothing; MainMenu.Switch=false; } void ShowProgramMenu() { MainMenu.Offset=0; MainMenu.IndexVal=0; MainMenu.MaxIndex=2; MainMenu.MenuText[0]="Previous Menu "; MainMenu.MenuText[1]=ProfileArray[MyControls.ProfileNumber]->Name; MainMenu.MenuText[2]="Start "; if (MyControls.Goal==Null) { MainMenu.MenuText[3]=""; MainMenu.NextMenu[3]=&SetProgrammedMenu; } else {//profile loaded MainMenu.MenuText[3]="Current Profile "; // 1234567890123456789 MainMenu.NextMenu[3]=&ProfileMenu; MainMenu.MaxIndex=3; } MainMenu.MenuText[4]=""; MainMenu.MenuText[5]=""; MainMenu.NextMenu[0]=&SetProgrammedMenu; MainMenu.NextMenu[1]=&DoNothing; MainMenu.NextMenu[2]=&StartProfileMenu; MainMenu.NextMenu[4]=&SetProgrammedMenu; MainMenu.NextMenu[5]=&SetProgrammedMenu; MainMenu.CurrentType=Index; MainMenu.Switch=true; } void StartProfileMenu() { LoadProfile(MyControls.ProfileNumber,0); //load the first element of the selected profile ProfileMenu(); } void ProfileMenu() { MainMenu.IndexVal=0; MainMenu.MaxIndex=3; MainMenu.Offset=0; // MainMenu.MenuText[0]=MyControls.Message; MainMenu.MenuText[1]="Temperature"; MainMenu.MenuText[2]="Power Level"; MainMenu.MenuText[3]="Time "; MainMenu.NextMenu[0]=&ShowProgramMenu; MainMenu.NextMenu[1]=&AlterDataMenu; MainMenu.NextMenu[2]=&AlterDataMenu; MainMenu.NextMenu[3]=&AlterDataMenu; MainMenu.NextMenu[4]=&DoNothing; MainMenu.Switch=false; } void AlterDataMenu() { if (MainMenu.CurrentType==Index) switch (MainMenu.IndexVal) { case 1: MainMenu.CurrentType=TempC; break; case 2: MainMenu.CurrentType=PwrW; break; case 3: MainMenu.CurrentType=TimeS; break; default: MainMenu.CurrentType=Index; } else MainMenu.CurrentType=Index; } void DoNothing() //The purpose of this function is assign a function pointer to it rather than something wrong or a crash { } void SetProfile() { int i=MainMenu.Number-1; if (i<0)i=0; if (i>4)i=4;//sanitize MyControls.ProfileNumber=i; strncpy(MyControls.Message,ProfileArray[i]->Name,20); MyControls.Goal=Null; ShowProgramMenu(); } void LoadProfile(int Prof,int Ele) { MyControls.Temperature= ProfileArray[Prof]->Element[Ele].Temp; MyControls.PercentPower=ProfileArray[Prof]->Element[Ele].Power; MyControls.TimeSeconds=ProfileArray[Prof]->Element[Ele].Seconds; MyControls.Goal=ProfileArray[Prof]->Element[Ele].GoalType; MyControls.ProfileNumber=Prof; MyControls.ElementNumber=Ele; MyControls.MessageNo=ProfileArray[Prof]->Element[Ele].MessageNo; StartTime=ElapsedTime.read(); /*Mypc.printf("T %.1f P %d Sec %d Prof %d Ele %d\n", MyControls.Temperature, MyControls.PercentPower, MyControls.TimeSeconds, MyControls.ProfileNumber, MyControls.ElementNumber); */ } void DisplayMenu() { DisplayFlag=true; //This flag is used to stop generating new interupts wait_us(SyncToZeroCrossingDelayRise+TriacPulseLength+25); //wait for interupts to finish MyOled.BusEnable(true);//Chip select bool Highlight=false; //We have a 32 pixecl height and 9 pixcel font. 3 lines 10 spacing if (MainMenu.Switch) { for (int i=0; i<3 ; i++) { MyOled.locate(6,(i*10)+1); if (MainMenu.IndexVal==(i+MainMenu.Offset)) { MyOled.background(White); MyOled.foreground(Black); } else { MyOled.background(Black); MyOled.foreground(White); } MyOled.printf(MainMenu.MenuText[i+MainMenu.Offset]); MyOled.background(Black); MyOled.foreground(White); } } else { time_t t; if (MainMenu.CurrentType==TempC)sprintf(Buff[0]," %6.1f ",MyControls.Temperature); else sprintf(Buff[0]," %6.1f ",valT); if (MainMenu.CurrentType==PwrW)sprintf(Buff[1]," %5d%c ",MyControls.PercentPower,38); else sprintf(Buff[1]," %5d%c ",Demand,38); if (MainMenu.CurrentType==TimeS) { t=MyControls.TimeSeconds; strftime(Buff[2],10,"%T ",localtime(&t)); } else { t=(int)ElapsedTime.read()-StartTime; strftime(Buff[2],10,"%T ",localtime(&t)); } for (int i=0; i<3 ; i++) { MyOled.locate(6,(i*10)+1); if (MainMenu.IndexVal==(i+MainMenu.Offset)) { Highlight=true; } else { Highlight=false; } if (Highlight&&MainMenu.CurrentType==Index) { MyOled.background(White); MyOled.foreground(Black); MyOled.printf(MainMenu.MenuText[i+MainMenu.Offset]); } else { MyOled.background(Black); MyOled.foreground(White); MyOled.printf(MainMenu.MenuText[i+MainMenu.Offset]); } if (Highlight&&MainMenu.CurrentType!=Index) { MyOled.background(White); MyOled.foreground(Black); if ((i+MainMenu.Offset>=1)&&(i+MainMenu.Offset<=3))MyOled.printf(Buff[i+MainMenu.Offset-1]); } else { MyOled.background(Black); MyOled.foreground(White); if ((i+MainMenu.Offset>=1)&&(i+MainMenu.Offset<=3))MyOled.printf(Buff[i+MainMenu.Offset-1]); } } } MyOled.BusEnable(false);//Chip select DisplayFlag=false; } int CalcDemand() //return percent power { float ErrorSignal, Differential; int power; ErrorSignal=MyControls.Temperature-valT; if (abs(ErrorSignal)<1)Integrated+=ErrorSignal; else Integrated=0; if (Integrated>20)Integrated=(float)20; if (Integrated<-1)Integrated=(float)-1; Differential=ErrorSignal-LastError; LastError=ErrorSignal; power=(int)(((float)70*ErrorSignal)+((float)0.6*Integrated)+(45*Differential)); // Mypc.printf("Error %.2f Int %.2f Dif %.2f\n\r", ErrorSignal,Integrated,Differential); MainsOn ? Mypc.printf("Mains On\n\r") : Mypc.printf("Mains Off\n\r"); Mypc.printf("DebugValue %d\n\r",DebugValue); Mypc.printf("Elapsed time %d\n\r",ElapsedTime.read_us()); if (power<0)power=0; if (power>MyControls.PercentPower)power=MyControls.PercentPower; return power; } void DisplayMessage() { int x,y,z; z=MyControls.MessageNo; //convert to int before compare if (z>7)MyControls.MessageNo=0; if (z==0) { strncpy(MyControls.Message,ProfileArray[MyControls.ProfileNumber]->Name,20); } else { y=string_length(Message[MyControls.MessageNo]) ; for (x=0; x<18; x++) { if (x<y)MainMenu.MenuText[0][x]=Message[MyControls.MessageNo][x]; else MainMenu.MenuText[0][x]=' '; } MainMenu.MenuText[0][19]='\0'; } } int main() { Mypc.printf("Starting Main\n\r"); setup(); int Position=wheel.getPulses(); int UpdateTime=750; uint64_t TempTime=0; int64_t t;// time is uint64_t but int64_t used for comparison in if statement while (true) { //main loop if (Position!=wheel.getPulses()) { MainMenu.EncoderHandler( Position<wheel.getPulses(), MainMenu.CurrentType); Position=wheel.getPulses(); UpdateTime=250; //Quick display refresh when knob moved //Mypc.printf("Pos %d \n",Position); //DisplayMenu(); } /*Had a problem where mains detection was dropping out here. The sum ElapsedTime.read_ms()-CycleTime was sometimes =0. But 0>75 is true. eg if ((ElapsedTime.read_ms()-CycleTime)>75) would be true if the sum result was 0. Doing the sum first was ok. Had the same problem with Debounce. */ t=ElapsedTime.read_us()-CycleTime; if (t>75000) { //mains not proven. Read temp. When mains is on this is done synced to mains CycleCount=0; MainsOn=false; GetTemperature(); } t=ElapsedTime.read_ms()-LastTime; if (t>UpdateTime) { LastTime=ElapsedTime.read_ms(); UpdateTime=750; DisplayMenu(); } t=ElapsedTime.read_ms()-TempTime; if (t>15000) { //upped to 15s which will give a smaller temp rise by averaging over a longer period if (valT>99.0f) { if (valT-LastTemp<0.15f)Boiling=true; else Boiling=false; // still rising } else Boiling=false; //too cold LastTemp=valT; TempTime=ElapsedTime.read_ms(); } t=ElapsedTime.read_ms()-DemandTime; if (t>3000) { Demand=CalcDemand(); DemandTime=ElapsedTime.read_ms(); } t=ElapsedTime.read_ms()-CheckTime; if (t>3000) { CheckTime=ElapsedTime.read_ms(); switch (MyControls.Goal) { //enum goal { Null,Boil, Temp, Time}; case Boil: if (Boiling ) { DisplayMessage(); if (MyControls.ElementNumber<5) { MyControls.ElementNumber++; LoadProfile(MyControls.ProfileNumber,MyControls.ElementNumber); } else { MyControls.Temperature=25.0; MyControls.PercentPower=0; MyControls.TimeSeconds=0; } } break; case Temp: if (abs(valT-MyControls.Temperature)<(float)0.25) { DisplayMessage(); if (MyControls.ElementNumber<5) { MyControls.ElementNumber++; LoadProfile(MyControls.ProfileNumber,MyControls.ElementNumber); } else { MyControls.Temperature=25.0; MyControls.PercentPower=0; MyControls.TimeSeconds=0; } } break; case Time: t=ElapsedTime.read()-StartTime; if (t>MyControls.TimeSeconds) { DisplayMessage(); if(MyControls.ElementNumber<5) { MyControls.ElementNumber++; LoadProfile(MyControls.ProfileNumber,MyControls.ElementNumber); } else { MyControls.Temperature=25.0; MyControls.PercentPower=0; MyControls.TimeSeconds=0; } } break; } }//goal test } }