I wouldn't consider this final code. I'd like to clean it up and test it a little more, but it seems to be working fine. If you have wifi, search for ping_Serial and uncomment it out. I don't have my wifi set up yet. That's next on the list.
Code: Select all
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <ReefAngel.h>
#include <avr/pgmspace.h>
////// Place global variable code below here
// Create the menu entries
// menu text <= 20 chars
prog_char menu1_label[] PROGMEM = "Feeding";
prog_char menu2_label[] PROGMEM = "Water Change";
prog_char menu3_label[] PROGMEM = "ATO Clear";
prog_char menu4_label[] PROGMEM = "Overheat Clear";
prog_char menu5_label[] PROGMEM = "PH Calibration";
prog_char menu6_label[] PROGMEM = "Date / Time";
prog_char menu7_label[] PROGMEM = "Version";
prog_char menu8_label[] PROGMEM = "Variables";
prog_char menu9_label[] PROGMEM = "Reserved";
// Group the menu entries together
PROGMEM const char *menu_items[] = {
menu1_label, menu2_label, menu3_label,
menu4_label, menu5_label, menu6_label,
menu7_label, menu8_label, menu9_label
};
#define DOSER3_PORT Port1
#define DOSER4_PORT Port2
#define DOSER5_PORT Port3
#define IM_DOSER3AMOUNT 120 // int16
#define IM_DOSER3RATE 122 // int16
#define IM_DOSER4AMOUNT 124 // int16
#define IM_DOSER4RATE 126 // int16
#define IM_DOSER5AMOUNT 128 // int16
#define IM_DOSER5RATE 130 // int16
// Number of variables to edit, The PROGMEM arrays below must match
#define IMEDIT_COUNT 6
// comment this out to turn off the ok/cancel button for variable editing
#define IMEDIT_OKCANCEL
// all IMEDIT strings must be <= 20 chars
prog_char imedit_name_0[] PROGMEM = "Doser 3 Amount";
prog_char imedit_name_1[] PROGMEM = "Doser 3 Rate";
prog_char imedit_name_2[] PROGMEM = "Doser 4 Amount";
prog_char imedit_name_3[] PROGMEM = "Doser 4 Rate";
prog_char imedit_name_4[] PROGMEM = "Doser 5 Amount";
prog_char imedit_name_5[] PROGMEM = "Doser 5 Rate";
PROGMEM const char *imedit_name[] = {
imedit_name_0, imedit_name_1, imedit_name_2, imedit_name_3, imedit_name_4, imedit_name_5
};
prog_char imedit_aux1_0[] PROGMEM = "mL/day";
prog_char imedit_aux1_1[] PROGMEM = "mL/min";
PROGMEM const char *imedit_aux1[] = {
imedit_aux1_0, imedit_aux1_1, imedit_aux1_0, imedit_aux1_1, imedit_aux1_0, imedit_aux1_1 // reuse the doser unit strings for 3 dosers
};
PROGMEM prog_int16_t imedit_mempos[] = {
IM_DOSER3AMOUNT, IM_DOSER3RATE, IM_DOSER4AMOUNT, IM_DOSER4RATE, IM_DOSER5AMOUNT, IM_DOSER5RATE
};
PROGMEM prog_char imedit_isbyte[] = {
0, 0, 0, 0, 0, 0
};
PROGMEM prog_int16_t imedit_min[] = {
0, 0, 0, 0, 0, 0
};
PROGMEM prog_int16_t imedit_max[] = {
32767, 32767, 32767, 32767, 32767, 32767
};
PROGMEM prog_int16_t imedit_format[] = {
1, 10, 1, 10, 1, 10
};
void RequiredForInputLoops() {
wdt_reset(); // watch dog timer reset, just leave this here
//**todo** enable ping_Serial() when wifi hooked up
//ping_Serial(); // handle wifi events
}
boolean IMEDIT_get_isbyte(int pos) {
if (pgm_read_byte(&(imedit_isbyte[pos]))!=0) return true;
return false;
}
int IMEDIT_get_format(int pos) {
return pgm_read_word(&(imedit_format[pos]));
}
int IMEDIT_get_mempos(int pos) {
return pgm_read_word(&(imedit_mempos[pos]));
}
int IMEDIT_get_min(int pos) {
return pgm_read_word(&(imedit_min[pos]));
}
int IMEDIT_get_max(int pos) {
return pgm_read_word(&(imedit_max[pos]));
}
void IMEdit_get_name(char *dest, int pos) {
int ptr;
ptr = pgm_read_word(&(imedit_name[pos]));
strcpy_P(dest, (char*)ptr);
}
void IMEdit_get_aux1(char *dest, int pos) {
int ptr;
ptr = pgm_read_word(&(imedit_aux1[pos]));
strcpy_P(dest, (char*)ptr);
}
void IMEdit_EditorByIMPos(int pos) {
char name[21];
char aux1[21];
IMEdit_get_name(name,pos);
IMEdit_get_aux1(aux1,pos);
IMEdit_Editor(name,
aux1,"","",
IMEDIT_get_min(pos),
IMEDIT_get_max(pos),
IMEDIT_get_mempos(pos),
IMEDIT_get_isbyte(pos),
IMEDIT_get_format(pos));
}
void IMEdit_Picker() {
int pos=0;
boolean done=false;
boolean redraw=true;
int i,j,y;
char str[21];
while (!done) {
RequiredForInputLoops();
// Draw the list with the item at pos at the center of the screen
// 11 items on screen
if (redraw) {
redraw=false;
y=10;
for (i=pos-5; i<=pos+5; i++) {
if ((i<0)||(i>IMEDIT_COUNT)) {
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, y, " ");
} else if (i==IMEDIT_COUNT) {
if (i==pos)
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 10, y, "Exit ");
else
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, y, "Exit ");
} else {
IMEdit_get_name(str,i);
int len=strlen(str);
for (j=0; j<20-len; j++) {
strcat(str," ");
}
if (i==pos)
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 10, y, str);
else
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, y, str);
}
y+=10;
}
}
if (ReefAngel.Joystick.IsUp()) {
if (pos > 0) {
pos--;
redraw=true;
} else if (pos==0) {
pos=IMEDIT_COUNT;
redraw=true;
}
}
if (ReefAngel.Joystick.IsLeft()) {
pos-=5;
if (pos<0) pos=0;
redraw=true;
}
if (ReefAngel.Joystick.IsDown()) {
if (pos < IMEDIT_COUNT) {
pos++;
redraw=true;
} else if (pos==IMEDIT_COUNT) {
pos=0;
redraw=true;
}
}
if (ReefAngel.Joystick.IsRight()) {
pos+=5;
if (pos > IMEDIT_COUNT) pos=IMEDIT_COUNT;
redraw=true;
}
if (ReefAngel.Joystick.IsButtonPressed()) {
if (pos==IMEDIT_COUNT) {
// Exit selected
done=true;
} else {
ReefAngel.LCD.Clear(COLOR_WHITE,1,1,132,132);
IMEdit_EditorByIMPos(pos);
ReefAngel.LCD.Clear(COLOR_WHITE,1,1,132,132);
redraw=true;
}
}
}
}
void IMEdit_Editor(char *Name, char *auxline1, char *auxline2, char *auxline3, int minval, int maxval, int memorypos, boolean is_byte, int format) {
int val;
char str[10];
int x;
boolean redraw=true;
boolean done=false;
// read the variable from memory
if (!is_byte) {
val=InternalMemory.read_int(memorypos);
} else {
val=InternalMemory.read(memorypos);
}
// constrain it based on min/max
if (val<minval) val=minval;
if (val>maxval) val=maxval;
// set up the stepping
int step=1;
int maxstep=10000;
while (maxstep > maxval) {
maxstep /= 10;
}
#define IMEDIT_VAR_Y 70
#define IMEDIT_STEP_Y 105
// draw the header and footer
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 6, 10, " ");
x=66-3*strlen(Name); // center text
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, x, 10, Name);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 20, auxline1);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 30, auxline2);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 10, 40, auxline3);
//x=66-3*strlen("Press when done"); // center text
//x=66-3*15=21
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 21, 120,"Press when done");
// input loop
while (!done) {
RequiredForInputLoops();
if (redraw) {
redraw=false;
// draw variable
ConvertNumToString(str, val, format);
x=66-3*strlen(str); // center text
ReefAngel.LCD.Clear(COLOR_BLUE,33,IMEDIT_VAR_Y-16,98,IMEDIT_VAR_Y+22);
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 39, IMEDIT_VAR_Y-8, " ^ ");
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 39, IMEDIT_VAR_Y+8, " ` ");
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, x, IMEDIT_VAR_Y, str);
// draw step
ConvertNumToString(str, step, format);
x=strlen(str);
x=66-3*x;
ReefAngel.LCD.Clear(COLOR_WHITE,40,IMEDIT_STEP_Y-1,92,IMEDIT_STEP_Y+10);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 57, IMEDIT_STEP_Y-8, "+/-");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, x, IMEDIT_STEP_Y, str);
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 33, IMEDIT_STEP_Y, "<");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 93, IMEDIT_STEP_Y, ">");
}
if (ReefAngel.Joystick.IsUp()) {
// increase variable
if (maxval - step < val)
val=maxval;
else
val+=step;
redraw=true;
}
if (ReefAngel.Joystick.IsDown()) {
// decrease variable
if (minval + step > val)
val=minval;
else
val-=step;
redraw=true;
}
if (ReefAngel.Joystick.IsRight()) {
// increase step
if (step==maxstep) step=1;
else step*=10;
redraw=true;
}
if (ReefAngel.Joystick.IsLeft()) {
// decrease step
if (step==1) step=maxstep;
else step/=10;
redraw=true;
}
if (ReefAngel.Joystick.IsButtonPressed()) {
#ifdef IMEDIT_OKCANCEL
// switch to ok/cancel mode
while (ReefAngel.Joystick.IsButtonPressed()); // don't start until joystick is lifted
boolean isok=true;
ReefAngel.LCD.Clear(COLOR_WHITE,1,IMEDIT_STEP_Y-9,132,132);
while (!done) {
RequiredForInputLoops();
if (isok) {
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 12, 110," Ok ");
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 72, 110," Cancel ");
} else {
ReefAngel.LCD.DrawText(COLOR_BLACK, COLOR_WHITE, 12, 110," Ok ");
ReefAngel.LCD.DrawText(COLOR_WHITE, COLOR_BLUE, 72, 110," Cancel ");
}
if (ReefAngel.Joystick.IsRight()||ReefAngel.Joystick.IsLeft()||ReefAngel.Joystick.IsUp()||ReefAngel.Joystick.IsDown()) {
isok=!isok;
}
if (ReefAngel.Joystick.IsButtonPressed()) {
done=true; // this ends 2 while loops, taking us outside the outer loop
}
} // end while
#else
isok=true;
done=true;
#endif
if (isok) {
// just set the variable, no ok/cancel
if (is_byte)
InternalMemory.write(memorypos,byte(val));
else
InternalMemory.write_int(memorypos,val);
}
} //end if button pressed
} // end while
}
void MenuEntry1()
{
ReefAngel.FeedingModeStart();
}
void MenuEntry2()
{
ReefAngel.WaterChangeModeStart();
}
void MenuEntry3()
{
ReefAngel.ATOClear();
ReefAngel.DisplayMenuEntry("Clear ATO Timeout");
}
void MenuEntry4()
{
ReefAngel.OverheatClear();
ReefAngel.DisplayMenuEntry("Clear Overheat");
}
void MenuEntry5()
{
ReefAngel.SetupCalibratePH();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry6()
{
ReefAngel.SetupDateTime();
ReefAngel.DisplayedMenu = ALT_SCREEN_MODE;
}
void MenuEntry7()
{
ReefAngel.DisplayVersion();
}
void MenuEntry8()
{
IMEdit_Picker();
ReefAngel.DisplayedMenu=ALT_SCREEN_MODE;
}
void MenuEntry9()
{
// Not yet implemented
ReefAngel.DisplayedMenu=RETURN_MAIN_MODE;
}
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.InitMenu(pgm_read_word(&(menu_items[0])),SIZE(menu_items));
//ReefAngel.AddStandardMenu(); // Add Standard Menu
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = 0;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = 0;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = 0;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = 0;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Set the Overheat temperature setting
InternalMemory.OverheatTemp_write( 840 );
// Ports that are always on
////// Place additional initialization code below here
////// Place additional initialization code above here
}
void RunDosers345() {
double dose; // applied every 15 minutes, 96 times per day
double rate;
int tmp;
tmp=InternalMemory.read_int(IM_DOSER3AMOUNT);
if (tmp > 0) {
dose=double(tmp);
rate=double(InternalMemory.read_int(IM_DOSER3RATE))/10.0;
dose=(60.0*dose)/(96.0*rate); // now it is in seconds per 15 minute dose
dose=dose + 0.5; // round to int properly
tmp=int(dose);
ReefAngel.DosingPumpRepeat(DOSER3_PORT,0,15,tmp);
}
tmp=InternalMemory.read_int(IM_DOSER4AMOUNT);
if (tmp > 0) {
dose=double(tmp);
rate=double(InternalMemory.read_int(IM_DOSER4RATE))/10.0;
dose=(60.0*dose)/(96.0*rate); // now it is in seconds per 15 minute dose
dose=dose + 0.5; // round to int properly
tmp=int(dose);
ReefAngel.DosingPumpRepeat(DOSER4_PORT,5,15,tmp);
}
tmp=InternalMemory.read_int(IM_DOSER5AMOUNT);
if (tmp > 0) {
dose=double(tmp);
rate=double(InternalMemory.read_int(IM_DOSER5RATE))/10.0;
dose=(60.0*dose)/(96.0*rate); // now it is in seconds per 15 minute dose
dose=dose + 0.5; // round to int properly
tmp=int(dose);
ReefAngel.DosingPumpRepeat(DOSER5_PORT,10,15,tmp);
}
}
void loop()
{
RunDosers345();
////// Place your custom code above here
// This should always be the last line
ReefAngel.ShowInterface();
}