Brew heater controller. Programmed to mash grain and to boil wort
Dependencies: mbed QEI UniGraphic
Diff: main.cpp
- Revision:
- 0:c673d397e9dc
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Fri Jan 07 12:08:12 2022 +0000
@@ -0,0 +1,771 @@
+/**
+
+ 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
+ }
+}