// AkvarieComputer - menu.c
#include <pic18.h>
#include <stdio.h>
#include <string.h>
#include "lcd.h"
#include "delay.h"
#include "akvarie.h"
#include "menu.h"
#include "xeeprom.h"
const char AutomationSelectType[] = "Type <%s> ";
const char *AutomationSelectTexts[] = {AutomationSelectType};
const char AutomationSelectTypeTypes[] = {TYPE_AUTOMATION};
const void *AutomationSelectTypeValues[] = {&CA.Type};
extern const char *Automationtypes_values[];
const char DeleteAutomation[] = "<Delete Automation>";
const char Light_1[] = "From (hour) <%d> ";
const char Light_2[] = "To (hour) <%d> ";
const char Light_3[] = "Power <%d> ";
const char *LightTexts[] = {Light_1,Light_2,Light_3,DeleteAutomation,NULL};
const void *LightData[] = {&CA.Data.Light.StartTime,&CA.Data.Light.StopTime,&CA.Data.Light.PowerNo};
const char LightTypes[] = {TYPE_INTEGER,TYPE_INTEGER,TYPE_INTEGER};
// just Light_3 // const char Osmolator_1[] = "Power <%d> ";
const char Osmolator_2[] = "Input 1 <%s> ";
const char Osmolator_3[] = "Input 2 <%s> ";
const char *OsmolatorTexts[] = {Light_3,Osmolator_2,Osmolator_3,DeleteAutomation,NULL};
const void *OsmolatorData[] = {&CA.Data.Osmolator.PowerNo,&CA.Data.Osmolator.Input[0],&CA.Data.Osmolator.Input[1]};
const char OsmolatorTypes[] = {TYPE_INTEGER,TYPE_POWER,TYPE_POWER};
const char Ozonizer_1[] = "Max mV Level <%d> ";
const char *OzonizerTexts[] = {Ozonizer_1,Light_3,DeleteAutomation,NULL};
const void *OzonizerData[] = {&CA.Data.Ozonizer.RedoxMaxLevel,&CA.Data.Ozonizer.PowerNo};
const char OzonizerTypes[] = {TYPE_INTEGER,TYPE_INTEGER};
const char CalciumReactor_1[]= "Cut-off pH <%.2f> ";
const char *CalciumReactorTexts[] = {CalciumReactor_1,Light_3,Light_1,Light_2,DeleteAutomation,NULL};
const void *CalciumReactorData[]= {&CA.Data.CalciumReactor.CutOffpH,
&CA.Data.CalciumReactor.PowerNo,
&CA.Data.CalciumReactor.StartTime,
&CA.Data.CalciumReactor.StopTime};
const char CalciumReactorTypes[] = {TYPE_FLOAT2,TYPE_INTEGER,TYPE_INTEGER,TYPE_INTEGER};
const char DosingPump_1[] = "Every <%d> hour ";
const char DosingPump_2[] = "Every <%d> minute ";
const char DosingPump_3[] = "Run for <%d> sec. ";
const char *DosingPumpTexts[] = {DosingPump_1,DosingPump_2,DosingPump_3,Light_3,DeleteAutomation,NULL};
const char *DosingPumpData[] = {&CA.Data.DosingPump.Hour,
&CA.Data.DosingPump.Minute,
&CA.Data.DosingPump.Seconds,
&CA.Data.DosingPump.PowerNo};
const char DosingPumpTypes[] = {TYPE_INTEGER,TYPE_INTEGER,TYPE_INTEGER,TYPE_INTEGER};
char ReadAutomation(int No)
{
char Error;
if (No > 255)
ErrorMsg("Outofrange",155);
Error = XEEReadArray(0xa0,No * 100,&CA,sizeof(Automation));
if (Error == XEE_SUCCESS)
return 1;
else
{
ErrorMsg("EEPROM Error",200+Error);
return 0;
}
}
char WriteAutomation(int No)
{
char i;
char Error;
char *Pointer = &CA;
XEEBeginWrite(0xa0,No * 100);
for (i = 0; i < sizeof(Automation); i++)
{
Error = XEEWrite(*Pointer++);
if (Error != XEE_SUCCESS)
ErrorMsg("EEPROM Write",210+Error);
}
XEEEndWrite();
}
char GetAutomationType()
{
char i = 0;
while(Automationtypes_values[i])
{
if (strcmp(Automationtypes_values[i],CA.Type) == 0)
return i;
i++;
}
return -1;
}
void InitAutomation(int No)
{
memset(&CA,0,sizeof(Automation));
CA.MagicChar = MAGICCHAR;
CA.No = No;
strcpy(CA.Type,"Off");
}
char DeleteAutomationFunc(int No)
{
char i;
char Last;
char Text[30];
lcd_clear();
sprintf(Text,"Delete %s ?",CA.Type);
lcdtext(1,1,Text);
while(1)
{
if (KeyEnter)
{
while(KeyEnter);
i = No + 1;
while(1)
{
DelayMs(10);
ReadAutomation(i);
if (CA.MagicChar == MAGICCHAR)
{
CA.No = CA.No - 1;
DelayMs(10);
WriteAutomation(CA.No);
i++;
}
else
{
// Delete Last
CA.MagicChar = 0;
CA.No = i - 1;
DelayMs(10);
WriteAutomation(CA.No);
sprintf(Text,"Deleted no. %d",CA.No);
return 1;
}
}
}
if (KeyLeft)
{
while(KeyLeft);
return 0;
}
}
}
void EditAutomation(int No)
{
char OneUp;
ReadAutomation(No);
if (CA.MagicChar == MAGICCHAR)
{
switch(GetAutomationType())
{
case 0: // Off
break;
case 1: // Light
OneUp = 0;
while(!OneUp)
{
switch(OpenMenu(LightTexts,LightData,LightTypes))
{
case 0: // Start Time
case 1: // Stop Time
case 2: // Power
if (EditOption(LightTexts,LightData,LightTypes,-1,0))
WriteAutomation(CA.No);
break;
case 3: // Delete automation
DeleteAutomationFunc(CA.No);
OneUp = 1;
break;
case -1:
OneUp = 1;
break;
}
}
break;
case 2: // Osmolator
OneUp = 0;
while(!OneUp)
{
switch(OpenMenu(OsmolatorTexts,OsmolatorData,OsmolatorTypes))
{
case 0: // Start Time
case 1: // Stop Time
case 2: // Power
if (EditOption(OsmolatorTexts,OsmolatorData,OsmolatorTypes,-1,0))
WriteAutomation(CA.No);
break;
case 3: // Delete automation
DeleteAutomationFunc(CA.No);
OneUp = 1;
break;
case -1:
OneUp = 1;
break;
}
}
break;
case 3: // Ozonizer
OneUp = 0;
while(!OneUp)
{
switch(OpenMenu(OzonizerTexts,OzonizerData,OzonizerTypes))
{
case 0: // Max Redox Level
case 1: // Power
if (EditOption(OzonizerTexts,OzonizerData,OzonizerTypes,-1,0))
WriteAutomation(CA.No);
break;
case 2: // Delete automation
DeleteAutomationFunc(CA.No);
OneUp = 1;
break;
case -1:
OneUp = 1;
break;
}
}
break;
case 4: // Calcium Reactor
OneUp = 0;
while(!OneUp)
{
switch(OpenMenu(CalciumReactorTexts,CalciumReactorData,CalciumReactorTypes))
{
case 0: // Cut off pH
case 1: // Power
case 2: // Starttime
case 3: // stoptime
if (EditOption(CalciumReactorTexts,CalciumReactorData,CalciumReactorTypes,-1,0))
WriteAutomation(CA.No);
break;
case 4:
DeleteAutomationFunc(CA.No);
OneUp = 1;
break;
case -1:
OneUp = 1;
break;
}
}
break;
case 5: // Dosing Pump
OneUp = 0;
while(!OneUp)
{
switch(OpenMenu(DosingPumpTexts,DosingPumpData,DosingPumpTypes))
{
case 0:
case 1:
case 2:
case 3:
if (EditOption(DosingPumpTexts,DosingPumpData,DosingPumpTypes,-1,0))
WriteAutomation(CA.No);
break;
case 4:
DeleteAutomationFunc(CA.No);
OneUp = 1;
break;
case -1:
OneUp = 1;
break;
}
}
break;
default: // Unknown automation - delete it !
DeleteAutomationFunc(CA.No);
break;
}
}
}
void NewAutomation()
{
char Text[30];
int No = 1;
if (ReadAutomation(No))
{
while (CA.MagicChar == MAGICCHAR && No < 255)
{
No++;
ReadAutomation(No);
}
}
if (No == 255)
{
ErrorMsg("Out of memory",22);
}
lcd_clear();
sprintf(Text,"New Automation(%d)",No);
lcdtext(1,1,Text);
InitAutomation(No);
if (EditOption(AutomationSelectTexts,AutomationSelectTypeValues,(char*)AutomationSelectTypeTypes,2,0))
{
// Init values
switch(GetAutomationType())
{
case 0:
lcd_clear();
return;
break;
case 1: // Light
CA.Data.Light.StartTime = 0;
CA.Data.Light.StopTime = 23;
CA.Data.Light.PowerNo = 0;
break;
case 2: // Osmolator
CA.Data.Osmolator.PowerNo = 0;
break;
case 3: // Ozonizer
CA.Data.Ozonizer.RedoxMaxLevel = 300;
CA.Data.Ozonizer.PowerNo = 0;
break;
case 4: // Calcium Reactor
CA.Data.CalciumReactor.CutOffpH = 6.5f;
CA.Data.CalciumReactor.PowerNo = 0;
CA.Data.CalciumReactor.StartTime = 12;
CA.Data.CalciumReactor.StopTime = 23;
break;
case 5: // Dosing Pump
CA.Data.DosingPump.Hour = 0;
CA.Data.DosingPump.Minute = 0;
CA.Data.DosingPump.Seconds = 0;
CA.Data.DosingPump.PowerNo = 0;
break;
}
WriteAutomation(No);
lcd_clear();
sprintf(Text,"Created as no. %d",No);
lcdtext(1,1,Text);
lcdtext(1,2,"Press Enter..");
while(!KeyEnter);
while(KeyEnter);
}
}
void PerformAutomation()
{
char Text[30];
char No;
char d;
// Loop Through Automation
for (No = 1; No < 255; No++)
{
if (ReadAutomation(No))
{
if (CA.MagicChar == MAGICCHAR)
{
for (d = 0; d < 50; d++)
{
DelayMs(5);
if (KeyEnter)
return;
}
switch(GetAutomationType())
{
case -1:
lcdtext(1,4,"Unknown automation");
DelayMs(250);
break;
case 0: // Off
break;
case 1: // Light
sprintf(Text,"Auto: %d %s ",CA.No,CA.Type);
lcdtext(1,4,Text);
if (CA.Data.Light.StartTime > CA.Data.Light.StopTime)
{
// Over midnat
if (AI.Clock[2] >= CA.Data.Light.StartTime ||
AI.Clock[2] < CA.Data.Light.StopTime)
{
if (CA.Data.Light.PowerNo >=1 && CA.Data.Light.PowerNo <= 4)
strcpy(AI.Power[CA.Data.Light.PowerNo - 1],"On");
}
else
if (CA.Data.Light.PowerNo >=1 && CA.Data.Light.PowerNo <= 4)
strcpy(AI.Power[CA.Data.Light.PowerNo - 1],"Off");
}
else
if (AI.Clock[2] >= CA.Data.Light.StartTime &&
AI.Clock[2] < CA.Data.Light.StopTime)
{
if (CA.Data.Light.PowerNo >=1 && CA.Data.Light.PowerNo <= 4)
strcpy(AI.Power[CA.Data.Light.PowerNo - 1],"On");
}
else
if (CA.Data.Light.PowerNo >=1 && CA.Data.Light.PowerNo <= 4)
strcpy(AI.Power[CA.Data.Light.PowerNo - 1],"Off");
break;
case 2: // Osmolator
{
char Result[2] = {0,0};
char i;
sprintf(Text,"Auto: %d %s ",CA.No,CA.Type);
lcdtext(1,4,Text);
for (i = 0; i < 2; i++)
{
if (strcmp(CA.Data.Osmolator.Input[i],"Void\0") != 0)
{
if (strcmp(CA.Data.Osmolator.Input[i],"On\0") == 0)
{
if (i == 0)
{
if (RA4 == 1)
Result[i] = 1;
else
Result[i] = 0;
}
if (i == 1)
{
if (RA5 == 1)
Result[i] = 1;
else
Result[i] = 0;
}
}
if (strcmp(CA.Data.Osmolator.Input[i],"Off\0") == 0)
{
if (i == 0)
{
if (RA4 == 0)
Result[i] = 1;
else
Result[i] = 0;
}
if (i == 1)
{
if (RA5 == 0)
Result[i] = 1;
else
Result[i] = 0;
}
}
}
else
Result[i] = 1;
}
if (Result[0] && Result[1])
strcpy(AI.Power[CA.Data.Osmolator.PowerNo - 1],"On");
else
strcpy(AI.Power[CA.Data.Osmolator.PowerNo - 1],"Off");
}
break;
case 3: // Ozonizer
{
char i;
sprintf(Text,"Auto: %d %s ",CA.No,CA.Type);
lcdtext(1,4,Text);
for (i = 0; i < 4; i++)
{
if (strcmp(AI.ProbeSetup[i],"mV\0") == 0)
{
if (AI.Probe[i] < CA.Data.Ozonizer.RedoxMaxLevel)
strcpy(AI.Power[CA.Data.Ozonizer.PowerNo - 1],"On");
else
strcpy(AI.Power[CA.Data.Ozonizer.PowerNo - 1],"Off");
}
}
}
break;
case 4: // Calcium Reactor
{
char i;
sprintf(Text,"Auto: %d %s ",CA.No,CA.Type);
lcdtext(1,4,Text);
for (i = 0; i < 4; i++)
{
if (strcmp(AI.ProbeSetup[i],"pH\0") == 0)
{
if (strcmp(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"On\0") == 0)
{
if (AI.Probe[i] < CA.Data.CalciumReactor.CutOffpH)
strcpy(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"Off");
else
strcpy(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"On");
}
else
{
if (AI.Probe[i] < (CA.Data.CalciumReactor.CutOffpH + 0.2))
strcpy(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"Off");
else
strcpy(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"On");
}
}
}
if (CA.Data.CalciumReactor.StartTime > CA.Data.CalciumReactor.StopTime)
{
// Over midnat
if (AI.Clock[2] >= CA.Data.CalciumReactor.StartTime ||
AI.Clock[2] < CA.Data.CalciumReactor.StopTime)
{
// Nothing !
}
else
if (CA.Data.CalciumReactor.PowerNo >=1 && CA.Data.CalciumReactor.PowerNo <= 4)
strcpy(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"Off");
}
else
if (AI.Clock[2] >= CA.Data.CalciumReactor.StartTime &&
AI.Clock[2] < CA.Data.CalciumReactor.StopTime)
{
// Nothing !
}
else
if (CA.Data.CalciumReactor.PowerNo >=1 && CA.Data.CalciumReactor.PowerNo <= 4)
strcpy(AI.Power[CA.Data.CalciumReactor.PowerNo - 1],"Off");
}
break;
case 5: // Dosing Pump
{
if (AI.Clock[2] % CA.Data.DosingPump.Hour == 0)
{
// Hour OK
if (AI.Clock[1] % CA.Data.DosingPump.Minute == 0)
{
// Minute OK
if (AI.Clock[0] < CA.Data.DosingPump.Seconds)
strcpy(AI.Power[CA.Data.DosingPump.PowerNo - 1],"On");
else
strcpy(AI.Power[CA.Data.DosingPump.PowerNo - 1],"Off");
}
}
}
break;
}
}
else
{
UpdatePowers();
for (d = 0; d < 50; d++)
{
DelayMs(5);
if (KeyEnter)
return;
}
lcdtext(1,4," ");
return;
}
}
}
}