Brew heater controller. Programmed to mash grain and to boil wort
Dependencies: mbed QEI UniGraphic
main.cpp
00001 /** 00002 00003 Spice modal 00004 .SUBCKT NTCLE203E3103SB0 RN Rp PARAMS: TOLR=0 TOLB=0 00005 X64 Rn Rp NTC_BASE Params: 00006 + w=-14.6571976 00007 + x=4798.842 00008 + y=-115334 00009 + z=-3730535 00010 + gth=0.0032 gth1 = 0.0000167 00011 + cth=0.032 00012 + a=-14.65719769 00013 + r25=10000 00014 + b=4798.842 00015 + c=-115334 00016 + d=-3730535 00017 + T0=273.15 00018 + TR={1+TOLR/100} 00019 + TB={1+TOLB/100} 00020 .ENDS 00021 00022 */ 00023 00024 00025 /* Includes ------------------------------------------------------------------*/ 00026 00027 /* mbed specific header files. */ 00028 /* 00029 GPIO_InitTypeDef GPIO_InitStruct; 00030 //Configure GPIO pin : PB14 00031 GPIO_InitStruct.Pin = GPIO_PIN_14; 00032 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // digital Output 00033 GPIO_InitStruct.Speed = GPIO_SPEED_LOW; 00034 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); 00035 */ 00036 #include "mbed.h" 00037 #include "SSD1306.h" 00038 #include "string" 00039 00040 #include "MashProfile.h" 00041 #include "MashProfileFull.h" 00042 #include "Boil1.h" 00043 #include "Boil2.h" 00044 #include "Boil3.h" 00045 #ifndef PROFILES 00046 #define PROFILES 00047 #include "Profiles.h" 00048 #endif //Profiles 00049 00050 #define Height 32 00051 #define Width 132 00052 00053 00054 00055 #include "QEI.h" 00056 00057 //Firing delay in microseconds 00058 #define SyncToZeroCrossingDelayRise 115 00059 #define SyncToZeroCrossingDelayFall 115 00060 00061 #define TriacPulseLength 250 00062 // Blinking rate in milliseconds 00063 #define BLINKING_RATE_MS 500 00064 #define Bias_Current 0.261 // Thermistor current in mA 00065 /* 00066 00067 FIR filter designed with 00068 http://t-filter.appspot.com 00069 00070 sampling frequency: 2000 Hz 00071 00072 * 0 Hz - 100 Hz 00073 gain = 1 00074 desired ripple = 5 dB 00075 actual ripple = 1.0675530446538501 dB 00076 00077 * 600 Hz - 1000 Hz 00078 gain = 0 00079 desired attenuation = -60 dB 00080 actual attenuation = -71.5928467318133 dB 00081 00082 */ 00083 00084 #define NumberOfTaps 9 00085 00086 static double filter_taps[NumberOfTaps] = { 00087 0.009246906411528302, 00088 0.0484176527692072, 00089 0.12764201806827455, 00090 0.21665898750162413, 00091 0.25663867508629506, 00092 0.21665898750162413, 00093 0.12764201806827455, 00094 0.0484176527692072, 00095 0.009246906411528302 00096 }; 00097 //float PotBuf[NumberOfTaps]; 00098 float TempBuf[NumberOfTaps]; 00099 int CycleCount=0,StartT=0; 00100 bool FireTriac,Boiling,DisplayFlag; 00101 /*------- inputs ---------*/ 00102 InterruptIn FallingEdge(D12); 00103 InterruptIn RisingEdge(D13); 00104 //AnalogIn Potentiometer(A0); 00105 // Thermistor (ntc) with a constant current flowing through it 00106 AnalogIn Thermistor(A1); 00107 //Rotary Encoder for entering values and navigation 00108 QEI wheel (D9, D8, NC, 20); 00109 DigitalIn Button(PC_9); 00110 InterruptIn ButtonPressed(PC_9); 00111 /*------- inputs -end-----*/ 00112 00113 /*------- outputs ---------*/ 00114 //DigitalOut led(LED1); 00115 // Output to switch on the LED in the opto to fire the triac 00116 DigitalOut Triac(D10),OledReset(D6); 00117 // Test output to see timings and states on a scope 00118 DigitalOut Test(D15); 00119 // The screen 00120 SSD1306 MyOled(SPI_8, 10000000, D4, D5, D3, D2, D6, D7,"MyOled", Width, Height); // Spi 8bit, 10MHz, mosi, miso, sclk, cs, reset, dc 00121 // fixme clock may need to be 5MHz 00122 // 5000000 gives a clock of 3.125MHz (genius) 00123 // 10000000 gives 6.25 00124 // 12800000 gives 12.8Mhz still works 00125 // 20000000 gives 12.5Mhz 00126 // the nearest i can find to 8MHz is 10000000 00127 00128 /*------- outputs -end-----*/ 00129 00130 00131 char Buff[3][20]; 00132 Timeout SyncDelay,TriacPulseTimeout,ReadTemperature; 00133 00134 //float Pot; 00135 int FireCount; 00136 int Demand; 00137 float valT,LastTemp=0,LastError=0,Integrated=0; 00138 bool Rise,MainsOn; 00139 time_t now; 00140 Timer ElapsedTime; 00141 uint64_t LastTime,TempTime,DebounceTime,StartTime,DemandTime,CheckTime,KnobTime,CycleTime; 00142 struct Controls { 00143 float Temperature; 00144 int8_t PercentPower; 00145 int16_t TimeSeconds; 00146 int8_t ProfileNumber; 00147 int8_t ElementNumber; 00148 uint8_t MessageNo; 00149 goal Goal; 00150 char Message[20]; 00151 00152 }; 00153 struct Controls MyControls= {25.0,50,0}; 00154 struct Profile *ProfileArray[5]= { &MashProfile,&MashProfileFull,&Boil1,&Boil2,&Boil3}; 00155 enum VariableType { Index, TempC, PwrW, TimeS }; 00156 struct MenuType { 00157 int8_t IndexVal; //The current thing pointed to by the cursor 00158 int8_t Number; 00159 int8_t MaxIndex; 00160 int8_t Offset; 00161 VariableType CurrentType; 00162 void EncoderHandler( bool Increment, VariableType ChangeType); 00163 struct Controls *Cntrl; // points to the main loop controls 00164 char *MenuText[8]; //8 lines of menus to start with 00165 void (*NextMenu[8])(); 00166 bool Switch; 00167 }; 00168 00169 Serial Mypc(USBTX, USBRX, 115200); //for debugging 00170 int64_t DebugValue; 00171 void FiringCounter(int Power); 00172 void PulseTriac(); 00173 void SyncFall(); 00174 void SyncRise(); 00175 //char *Menu[2]={"Manual","Programmed"}; 00176 00177 int string_length(char* given_string) 00178 { 00179 // variable to store the 00180 // length of the string 00181 int length = 0; 00182 while (*given_string != '\0') { 00183 length++; 00184 given_string++; 00185 } 00186 00187 return length; 00188 } 00189 void MenuType::EncoderHandler( bool Increment, VariableType ChangeType) 00190 { 00191 int Multiplier=1,t; 00192 t=ElapsedTime.read_ms()-KnobTime; 00193 if (t<50) Multiplier=10; 00194 else if (t<100) Multiplier=5; 00195 else if (t<150) Multiplier=2; 00196 KnobTime=ElapsedTime.read_ms(); 00197 switch (ChangeType) { 00198 case Index: 00199 if (Increment) IndexVal++; 00200 else IndexVal--; 00201 if (IndexVal>MaxIndex) IndexVal=0; 00202 if (IndexVal<0) IndexVal=MaxIndex; 00203 if ((IndexVal-Offset)>2) Offset=IndexVal-2; 00204 if ((IndexVal-Offset)<0) Offset=IndexVal; 00205 break; 00206 case TempC: 00207 if (Increment) Cntrl->Temperature=Cntrl->Temperature+(float)0.1*(float)Multiplier; 00208 else Cntrl->Temperature=Cntrl->Temperature-(float)0.1*(float)Multiplier; 00209 if (Cntrl->Temperature>120)Cntrl->Temperature=120; 00210 if (Cntrl->Temperature<25)Cntrl->Temperature=25; 00211 break; 00212 case PwrW: 00213 if (Increment) Cntrl->PercentPower+=Multiplier; 00214 else Cntrl->PercentPower-=Multiplier; 00215 if (Cntrl->PercentPower>100)Cntrl->PercentPower=100; 00216 if (Cntrl->PercentPower<=0)Cntrl->PercentPower=1; 00217 break; 00218 case TimeS: 00219 Multiplier=Multiplier*Multiplier; 00220 if (Increment) Cntrl->TimeSeconds+=Multiplier; 00221 else Cntrl->TimeSeconds-=Multiplier; 00222 if (Cntrl->TimeSeconds>10800)Cntrl->TimeSeconds=10800; 00223 if (Cntrl->TimeSeconds<0)Cntrl->TimeSeconds=0; 00224 break; 00225 } 00226 } 00227 00228 struct MenuType MainMenu; 00229 void SetTopLevelMenu(); 00230 void SetProgrammedMenu(); 00231 void SetManualMenu(); 00232 void ShowProgramMenu(); 00233 void StartProfileMenu(); 00234 void DoNothing(); 00235 void AlterDataMenu(); 00236 void LoadProfile(int Prof,int Ele); 00237 void SetProfile(); 00238 void ProfileMenu(); 00239 void GetTemperature(); 00240 void DisplayNow(); 00241 void DisplayMenu(); 00242 void SetOutputSpeed(); 00243 void PulseTriac() 00244 { 00245 if (Triac==1) { 00246 Triac=0; 00247 Test=0; //fixme 00248 } else { 00249 Triac=1;// Fire the triac 00250 Test=1; 00251 TriacPulseTimeout.attach_us(&PulseTriac,TriacPulseLength); 00252 } 00253 } 00254 void MenuSetup() 00255 { 00256 MainMenu.Offset=0; 00257 MainMenu.IndexVal=0; 00258 MainMenu.Cntrl=&MyControls; 00259 SetTopLevelMenu(); 00260 } 00261 void SyncFall() 00262 { 00263 int64_t t; 00264 /* After many attempts to get this right t should be a uint64_t BUT 00265 whan you do a comparison if(uint64_t> 99) it all goes to crap. 00266 I think you end up with an overflow in the math used to evaluate it. 00267 So only do comparicons with signed numbers int or float. 00268 */ 00269 if (!Rise) return; 00270 Rise=false; 00271 t=ElapsedTime.read_us()-CycleTime; 00272 DebugValue=t; 00273 CycleTime=ElapsedTime.read_us(); 00274 if (DisplayFlag) return; 00275 if ((t>20400)||(t<19600)) { 00276 CycleCount=0; 00277 MainsOn=false; 00278 } 00279 if (CycleCount<10) { 00280 CycleCount++; 00281 return; 00282 } 00283 MainsOn=true; 00284 if (FireTriac) SyncDelay.attach_us(&PulseTriac,SyncToZeroCrossingDelayFall); 00285 FiringCounter(Demand); 00286 ReadTemperature.attach_us(&GetTemperature,9000); //read temp if mains on synced 00287 // the noise generated on triac firing upsets the temperature measurment 00288 } 00289 void SyncRise() 00290 { 00291 int64_t t; 00292 Test=1; 00293 if (Rise) return; 00294 Rise=true; 00295 t=ElapsedTime.read_us()-CycleTime; 00296 if (DisplayFlag) return; 00297 if ((t>10200)||(t<9800)) { 00298 CycleCount=0; 00299 MainsOn=false; 00300 } 00301 if (CycleCount<10) { 00302 return; 00303 } 00304 Test=0; 00305 if (FireTriac) SyncDelay.attach_us(&PulseTriac,SyncToZeroCrossingDelayRise); 00306 } 00307 00308 float ReadResistance() // Read voltage across thermistor 00309 { 00310 00311 int i,j; 00312 float tmp=0; 00313 j=StartT; 00314 TempBuf[StartT]=Thermistor; 00315 for (i=0; i<NumberOfTaps; i++) { 00316 if (j<0) j+=NumberOfTaps; 00317 tmp+=TempBuf[j]*filter_taps[i]; 00318 j--; 00319 if (j<0) j=NumberOfTaps-1; 00320 } 00321 StartT--; 00322 if (StartT<0) StartT=NumberOfTaps-1; 00323 /* tmp is the fraction 3.3V across thermistor. 00324 Resistance = V/I 00325 Resistance = (tmp*3.3V)/Bias_Current */ 00326 tmp=(tmp*(float)3.3)/(float)Bias_Current; // Answer is in Kohm because current is in mA 00327 return tmp; 00328 } 00329 float ResistanceToTemp(float Res) 00330 { 00331 /* T0 = 25C = 298K 00332 beta =3977 00333 R0=10000 ohm */ 00334 Res=Res+(float)0.0001; //Res can't be zero 00335 float temp; // temp here is both temperary and temperature 00336 temp=log(Res/(float)10)/(float)3977.0; //beta from datasheet 00337 temp=(float)1/(temp+(float)0.003354); // 1/298.15 00338 //temp=temp-(float)273.15 ;//kelvin to c conversion 00339 /* ok so the temp is miles off due to things like the sensor being pressed 00340 against the outside of the barrel. Loosing heat to ambient etc. 00341 So lets compensate for that with an offset and a scale factor. */ 00342 temp=(temp*(float)1.09)-(float)22; 00343 temp=temp-(float)273.15 ;//kelvin to c conversion 00344 return temp; 00345 } 00346 void GetTemperature() 00347 { 00348 if (DisplayFlag) return; // The noise generated by the comms to the display causes 0.5C error 00349 valT=ResistanceToTemp(ReadResistance()); 00350 } 00351 void Pressed() 00352 { 00353 int t; 00354 t=ElapsedTime.read_ms()-DebounceTime; 00355 DebounceTime=ElapsedTime.read_ms(); 00356 if (t<250) return; 00357 //Mypc.printf("Index %d \n",MainMenu.IndexVal); 00358 MainMenu.Number=MainMenu.IndexVal; 00359 MainMenu.NextMenu[MainMenu.IndexVal](); 00360 } 00361 void setup() 00362 { 00363 set_time(1256729737); // Set RTC time to Wed, 28 Oct 2009 11:35:37 00364 now=time(NULL); 00365 // Mypc.printf("Setup"); 00366 Triac=0; // Low to fire 00367 FallingEdge.fall(&SyncFall); //falling mains voltage 00368 RisingEdge.fall(&SyncRise); //rising mains voltage 00369 FireCount=0; 00370 ButtonPressed.fall(&Pressed); 00371 //SetOutputSpeed(); 00372 MyOled.BusEnable(true);//Chip select 00373 MyOled.FastWindow(true); 00374 MyOled.set_orientation(3); 00375 MyOled.locate(0,0); 00376 MyOled.set_font((unsigned char*) Terminal6x8,32,127,false); 00377 MyOled.cls(); 00378 MyOled.BusEnable(false);//Chip select 00379 ButtonPressed.fall(&Pressed); 00380 ElapsedTime.start(); 00381 LastTime=ElapsedTime.read_ms(); 00382 DebounceTime=LastTime; 00383 CycleTime=ElapsedTime.read_us(); 00384 TempTime=LastTime; 00385 CheckTime=LastTime; 00386 DemandTime=LastTime; 00387 KnobTime=LastTime; 00388 MenuSetup(); 00389 } 00390 void SetOutputSpeed() 00391 { 00392 GPIO_InitTypeDef GPIO_InitStruct; 00393 //Configure GPIO pin : PB_3 D3 00394 GPIO_InitStruct.Pin = GPIO_PIN_3; 00395 GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // digital Output 00396 GPIO_InitStruct.Speed = GPIO_SPEED_LOW; 00397 HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); 00398 } 00399 00400 void FiringCounter(int Power) 00401 { 00402 int x; 00403 x=Power; 00404 if (x>100) x=100; 00405 if (x<0) x=0; 00406 if (FireCount<0) { 00407 FireCount+=100-x; 00408 FireTriac=true; 00409 } else { 00410 FireCount-=x; 00411 FireTriac=false; 00412 } 00413 00414 } 00415 void SetTopLevelMenu() 00416 { 00417 00418 MainMenu.IndexVal=0; 00419 MainMenu.MaxIndex=2; 00420 MainMenu.Offset=0; 00421 MainMenu.MenuText[0]=TopLevelMenu[0]; 00422 MainMenu.MenuText[1]=TopLevelMenu[1]; 00423 MainMenu.MenuText[2]=TopLevelMenu[2]; 00424 MainMenu.CurrentType=Index; 00425 MainMenu.NextMenu[0]=&SetTopLevelMenu; 00426 MainMenu.NextMenu[1]=&SetManualMenu; 00427 MainMenu.NextMenu[2]=&SetProgrammedMenu; 00428 MainMenu.NextMenu[3]=&SetTopLevelMenu;//Set it to something that is valid 00429 MainMenu.NextMenu[4]=&SetTopLevelMenu; 00430 MainMenu.NextMenu[5]=&SetTopLevelMenu; 00431 MyControls.Temperature= 25.0; 00432 MyControls.PercentPower=1; 00433 MyControls.TimeSeconds=0; 00434 MainMenu.Switch=true; 00435 } 00436 void SetProgrammedMenu() 00437 { 00438 MainMenu.Offset=0; 00439 MainMenu.IndexVal=0; 00440 MainMenu.MaxIndex=5;//12345678901234567890 00441 MainMenu.MenuText[0]="Previous Menu "; 00442 MainMenu.MenuText[1]=MashProfile.Name; 00443 MainMenu.MenuText[2]=MashProfileFull.Name; 00444 MainMenu.MenuText[3]=Boil1.Name; 00445 MainMenu.MenuText[4]=Boil2.Name; 00446 MainMenu.MenuText[5]=Boil3.Name; 00447 00448 MainMenu.NextMenu[0]=&SetTopLevelMenu; 00449 MainMenu.NextMenu[1]=&SetProfile; 00450 MainMenu.NextMenu[2]=&SetProfile; 00451 MainMenu.NextMenu[3]=&SetProfile; 00452 MainMenu.NextMenu[4]=&SetProfile; 00453 MainMenu.NextMenu[5]=&SetProfile; 00454 00455 MainMenu.CurrentType=Index; 00456 MainMenu.Switch=true; 00457 } 00458 void SetManualMenu() 00459 { 00460 MainMenu.IndexVal=0; 00461 MainMenu.MaxIndex=3; 00462 MainMenu.Offset=0; // 1234567890123456789 00463 MainMenu.MenuText[0]="Manual Menu "; 00464 MainMenu.MenuText[1]="Temperature"; 00465 MainMenu.MenuText[2]="Power Level"; 00466 MainMenu.MenuText[3]="Time "; 00467 MainMenu.NextMenu[0]=&SetTopLevelMenu; 00468 MainMenu.NextMenu[1]=&AlterDataMenu; 00469 MainMenu.NextMenu[2]=&AlterDataMenu; 00470 MainMenu.NextMenu[3]=&AlterDataMenu; 00471 MainMenu.NextMenu[4]=&DoNothing; 00472 MainMenu.Switch=false; 00473 } 00474 void ShowProgramMenu() 00475 { 00476 MainMenu.Offset=0; 00477 MainMenu.IndexVal=0; 00478 MainMenu.MaxIndex=2; 00479 MainMenu.MenuText[0]="Previous Menu "; 00480 MainMenu.MenuText[1]=ProfileArray[MyControls.ProfileNumber]->Name; 00481 MainMenu.MenuText[2]="Start "; 00482 00483 if (MyControls.Goal==Null) { 00484 MainMenu.MenuText[3]=""; 00485 MainMenu.NextMenu[3]=&SetProgrammedMenu; 00486 } else {//profile loaded 00487 MainMenu.MenuText[3]="Current Profile "; 00488 // 1234567890123456789 00489 MainMenu.NextMenu[3]=&ProfileMenu; 00490 MainMenu.MaxIndex=3; 00491 } 00492 MainMenu.MenuText[4]=""; 00493 MainMenu.MenuText[5]=""; 00494 MainMenu.NextMenu[0]=&SetProgrammedMenu; 00495 MainMenu.NextMenu[1]=&DoNothing; 00496 MainMenu.NextMenu[2]=&StartProfileMenu; 00497 00498 MainMenu.NextMenu[4]=&SetProgrammedMenu; 00499 MainMenu.NextMenu[5]=&SetProgrammedMenu; 00500 MainMenu.CurrentType=Index; 00501 MainMenu.Switch=true; 00502 00503 } 00504 void StartProfileMenu() 00505 { 00506 LoadProfile(MyControls.ProfileNumber,0); //load the first element of the selected profile 00507 ProfileMenu(); 00508 } 00509 void ProfileMenu() 00510 { 00511 MainMenu.IndexVal=0; 00512 MainMenu.MaxIndex=3; 00513 MainMenu.Offset=0; // 00514 MainMenu.MenuText[0]=MyControls.Message; 00515 MainMenu.MenuText[1]="Temperature"; 00516 MainMenu.MenuText[2]="Power Level"; 00517 MainMenu.MenuText[3]="Time "; 00518 MainMenu.NextMenu[0]=&ShowProgramMenu; 00519 MainMenu.NextMenu[1]=&AlterDataMenu; 00520 MainMenu.NextMenu[2]=&AlterDataMenu; 00521 MainMenu.NextMenu[3]=&AlterDataMenu; 00522 MainMenu.NextMenu[4]=&DoNothing; 00523 MainMenu.Switch=false; 00524 } 00525 void AlterDataMenu() 00526 { 00527 if (MainMenu.CurrentType==Index) switch (MainMenu.IndexVal) { 00528 case 1: 00529 MainMenu.CurrentType=TempC; 00530 break; 00531 case 2: 00532 MainMenu.CurrentType=PwrW; 00533 break; 00534 case 3: 00535 MainMenu.CurrentType=TimeS; 00536 break; 00537 default: 00538 MainMenu.CurrentType=Index; 00539 } else MainMenu.CurrentType=Index; 00540 } 00541 void DoNothing() //The purpose of this function is assign a function pointer to it rather than something wrong or a crash 00542 { 00543 } 00544 void SetProfile() 00545 { 00546 int i=MainMenu.Number-1; 00547 if (i<0)i=0; 00548 if (i>4)i=4;//sanitize 00549 MyControls.ProfileNumber=i; 00550 strncpy(MyControls.Message,ProfileArray[i]->Name,20); 00551 MyControls.Goal=Null; 00552 ShowProgramMenu(); 00553 00554 00555 } 00556 void LoadProfile(int Prof,int Ele) 00557 { 00558 MyControls.Temperature= ProfileArray[Prof]->Element[Ele].Temp; 00559 MyControls.PercentPower=ProfileArray[Prof]->Element[Ele].Power; 00560 MyControls.TimeSeconds=ProfileArray[Prof]->Element[Ele].Seconds; 00561 MyControls.Goal=ProfileArray[Prof]->Element[Ele].GoalType; 00562 MyControls.ProfileNumber=Prof; 00563 MyControls.ElementNumber=Ele; 00564 MyControls.MessageNo=ProfileArray[Prof]->Element[Ele].MessageNo; 00565 StartTime=ElapsedTime.read(); 00566 /*Mypc.printf("T %.1f P %d Sec %d Prof %d Ele %d\n", 00567 MyControls.Temperature, 00568 MyControls.PercentPower, 00569 MyControls.TimeSeconds, 00570 MyControls.ProfileNumber, 00571 MyControls.ElementNumber); 00572 */ 00573 } 00574 00575 void DisplayMenu() 00576 { 00577 DisplayFlag=true; //This flag is used to stop generating new interupts 00578 wait_us(SyncToZeroCrossingDelayRise+TriacPulseLength+25); //wait for interupts to finish 00579 MyOled.BusEnable(true);//Chip select 00580 bool Highlight=false; 00581 //We have a 32 pixecl height and 9 pixcel font. 3 lines 10 spacing 00582 if (MainMenu.Switch) { 00583 for (int i=0; i<3 ; i++) { 00584 MyOled.locate(6,(i*10)+1); 00585 if (MainMenu.IndexVal==(i+MainMenu.Offset)) { 00586 MyOled.background(White); 00587 MyOled.foreground(Black); 00588 } else { 00589 MyOled.background(Black); 00590 MyOled.foreground(White); 00591 } 00592 MyOled.printf(MainMenu.MenuText[i+MainMenu.Offset]); 00593 MyOled.background(Black); 00594 MyOled.foreground(White); 00595 } 00596 } else { 00597 time_t t; 00598 if (MainMenu.CurrentType==TempC)sprintf(Buff[0]," %6.1f ",MyControls.Temperature); 00599 else sprintf(Buff[0]," %6.1f ",valT); 00600 if (MainMenu.CurrentType==PwrW)sprintf(Buff[1]," %5d%c ",MyControls.PercentPower,38); 00601 else sprintf(Buff[1]," %5d%c ",Demand,38); 00602 if (MainMenu.CurrentType==TimeS) { 00603 t=MyControls.TimeSeconds; 00604 strftime(Buff[2],10,"%T ",localtime(&t)); 00605 } else { 00606 t=(int)ElapsedTime.read()-StartTime; 00607 strftime(Buff[2],10,"%T ",localtime(&t)); 00608 } 00609 for (int i=0; i<3 ; i++) { 00610 MyOled.locate(6,(i*10)+1); 00611 if (MainMenu.IndexVal==(i+MainMenu.Offset)) { 00612 Highlight=true; 00613 } else { 00614 Highlight=false; 00615 } 00616 if (Highlight&&MainMenu.CurrentType==Index) { 00617 MyOled.background(White); 00618 MyOled.foreground(Black); 00619 MyOled.printf(MainMenu.MenuText[i+MainMenu.Offset]); 00620 } else { 00621 MyOled.background(Black); 00622 MyOled.foreground(White); 00623 MyOled.printf(MainMenu.MenuText[i+MainMenu.Offset]); 00624 } 00625 if (Highlight&&MainMenu.CurrentType!=Index) { 00626 MyOled.background(White); 00627 MyOled.foreground(Black); 00628 if ((i+MainMenu.Offset>=1)&&(i+MainMenu.Offset<=3))MyOled.printf(Buff[i+MainMenu.Offset-1]); 00629 } else { 00630 MyOled.background(Black); 00631 MyOled.foreground(White); 00632 if ((i+MainMenu.Offset>=1)&&(i+MainMenu.Offset<=3))MyOled.printf(Buff[i+MainMenu.Offset-1]); 00633 } 00634 } 00635 } 00636 MyOled.BusEnable(false);//Chip select 00637 DisplayFlag=false; 00638 } 00639 int CalcDemand() //return percent power 00640 { 00641 float ErrorSignal, Differential; 00642 int power; 00643 ErrorSignal=MyControls.Temperature-valT; 00644 if (abs(ErrorSignal)<1)Integrated+=ErrorSignal; 00645 else Integrated=0; 00646 if (Integrated>20)Integrated=(float)20; 00647 if (Integrated<-1)Integrated=(float)-1; 00648 Differential=ErrorSignal-LastError; 00649 LastError=ErrorSignal; 00650 power=(int)(((float)70*ErrorSignal)+((float)0.6*Integrated)+(45*Differential)); 00651 // Mypc.printf("Error %.2f Int %.2f Dif %.2f\n\r", ErrorSignal,Integrated,Differential); 00652 MainsOn ? Mypc.printf("Mains On\n\r") : Mypc.printf("Mains Off\n\r"); 00653 Mypc.printf("DebugValue %d\n\r",DebugValue); 00654 Mypc.printf("Elapsed time %d\n\r",ElapsedTime.read_us()); 00655 00656 if (power<0)power=0; 00657 if (power>MyControls.PercentPower)power=MyControls.PercentPower; 00658 return power; 00659 } 00660 void DisplayMessage() 00661 { 00662 int x,y,z; 00663 z=MyControls.MessageNo; //convert to int before compare 00664 if (z>7)MyControls.MessageNo=0; 00665 if (z==0) { 00666 strncpy(MyControls.Message,ProfileArray[MyControls.ProfileNumber]->Name,20); 00667 } else { 00668 y=string_length(Message[MyControls.MessageNo]) ; 00669 for (x=0; x<18; x++) { 00670 if (x<y)MainMenu.MenuText[0][x]=Message[MyControls.MessageNo][x]; 00671 else MainMenu.MenuText[0][x]=' '; 00672 } 00673 MainMenu.MenuText[0][19]='\0'; 00674 } 00675 } 00676 int main() 00677 { 00678 Mypc.printf("Starting Main\n\r"); 00679 setup(); 00680 int Position=wheel.getPulses(); 00681 int UpdateTime=750; 00682 uint64_t TempTime=0; 00683 int64_t t;// time is uint64_t but int64_t used for comparison in if statement 00684 while (true) { //main loop 00685 if (Position!=wheel.getPulses()) { 00686 MainMenu.EncoderHandler( Position<wheel.getPulses(), MainMenu.CurrentType); 00687 Position=wheel.getPulses(); 00688 UpdateTime=250; //Quick display refresh when knob moved 00689 //Mypc.printf("Pos %d \n",Position); 00690 //DisplayMenu(); 00691 } 00692 /*Had a problem where mains detection was dropping out here. 00693 The sum ElapsedTime.read_ms()-CycleTime was sometimes =0. 00694 But 0>75 is true. eg if ((ElapsedTime.read_ms()-CycleTime)>75) would be true 00695 if the sum result was 0. Doing the sum first was ok. Had the same problem with Debounce. 00696 */ 00697 t=ElapsedTime.read_us()-CycleTime; 00698 if (t>75000) { //mains not proven. Read temp. When mains is on this is done synced to mains 00699 CycleCount=0; 00700 MainsOn=false; 00701 GetTemperature(); 00702 } 00703 t=ElapsedTime.read_ms()-LastTime; 00704 if (t>UpdateTime) { 00705 LastTime=ElapsedTime.read_ms(); 00706 UpdateTime=750; 00707 DisplayMenu(); 00708 } 00709 t=ElapsedTime.read_ms()-TempTime; 00710 if (t>15000) { //upped to 15s which will give a smaller temp rise by averaging over a longer period 00711 if (valT>99.0f) { 00712 if (valT-LastTemp<0.15f)Boiling=true; 00713 else Boiling=false; // still rising 00714 } else Boiling=false; //too cold 00715 LastTemp=valT; 00716 TempTime=ElapsedTime.read_ms(); 00717 } 00718 t=ElapsedTime.read_ms()-DemandTime; 00719 if (t>3000) { 00720 Demand=CalcDemand(); 00721 DemandTime=ElapsedTime.read_ms(); 00722 } 00723 t=ElapsedTime.read_ms()-CheckTime; 00724 if (t>3000) { 00725 CheckTime=ElapsedTime.read_ms(); 00726 switch (MyControls.Goal) { 00727 //enum goal { Null,Boil, Temp, Time}; 00728 case Boil: 00729 if (Boiling ) { 00730 DisplayMessage(); 00731 if (MyControls.ElementNumber<5) { 00732 MyControls.ElementNumber++; 00733 LoadProfile(MyControls.ProfileNumber,MyControls.ElementNumber); 00734 } else { 00735 MyControls.Temperature=25.0; 00736 MyControls.PercentPower=0; 00737 MyControls.TimeSeconds=0; 00738 } 00739 } 00740 break; 00741 case Temp: 00742 if (abs(valT-MyControls.Temperature)<(float)0.25) { 00743 DisplayMessage(); 00744 if (MyControls.ElementNumber<5) { 00745 MyControls.ElementNumber++; 00746 LoadProfile(MyControls.ProfileNumber,MyControls.ElementNumber); 00747 } else { 00748 MyControls.Temperature=25.0; 00749 MyControls.PercentPower=0; 00750 MyControls.TimeSeconds=0; 00751 } 00752 } 00753 break; 00754 case Time: 00755 t=ElapsedTime.read()-StartTime; 00756 if (t>MyControls.TimeSeconds) { 00757 DisplayMessage(); 00758 if(MyControls.ElementNumber<5) { 00759 MyControls.ElementNumber++; 00760 LoadProfile(MyControls.ProfileNumber,MyControls.ElementNumber); 00761 } else { 00762 MyControls.Temperature=25.0; 00763 MyControls.PercentPower=0; 00764 MyControls.TimeSeconds=0; 00765 } 00766 } 00767 break; 00768 } 00769 }//goal test 00770 } 00771 }
Generated on Sat Jul 23 2022 05:42:19 by 1.7.2