hah, I will do that tonight. Here's my latest code, for my future reference.
I have nearly everything I am doing on my current controller in plus extras.
1) 12-bit PWM rise and dim over 3 hours for my 6 light channels and PWM fans using the CustomExpansion module
5) Runs my 4 powerheads in permanent nutrient transport mode.
Number 5 is the one I would like to work on next with the setPHModes function. to be able to set the sync state and the wave mode state of the 4 powerheads from some kind of time commands in the loop and from the GUI.
Is the NutrientTransportMode function smart enough to know that DC pumps are not supposed to be run between 0 and 30, so if I put a min speed of 0 it will skip up to 30 percent when it needs to exceed 0?
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 <Humidity.h>
#include <DCPump.h>
#include <ReefAngel.h>
////// Place global variable code below here
// define default argument of isPercent in function prototype instead of definition, maybe an Arduino bug?
void CustomExpansion(byte id, byte channel, int percentage, boolean isPercent=true);
#define antiSyncMode 0; // pumps on alternate sides alternating in speed
#define syncMode 1; // pumps on alternate sides on at same time and flow meeting in middle
#define gyreMode 2; // pumps on opposite corners on at same time so that flow turns in a circle. Opposite corners on at different times
////// Place global variable code above here
void setup()
{
// This must be the first line
ReefAngel.Init(); //Initialize controller
ReefAngel.Use2014Screen(); // Let's use 2014 Screen
// Ports toggled in Feeding Mode
ReefAngel.FeedingModePorts = Port6Bit;
// Ports toggled in Water Change Mode
ReefAngel.WaterChangePorts = 0;
// Ports toggled when Lights On / Off menu entry selected
ReefAngel.LightsOnPorts = Port3Bit;
// Ports turned off when Overheat temperature exceeded
ReefAngel.OverheatShutoffPorts = Port1Bit;
// Use T1 probe as temperature and overheat functions
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
// Set the Overheat temperature setting
InternalMemory.OverheatTemp_write( 830 );
// Feeeding and Water Change mode speed
ReefAngel.DCPump.FeedingSpeed=0;
ReefAngel.DCPump.WaterChangeSpeed=0;
// Ports that are always on
ReefAngel.Relay.On( Port5 );
ReefAngel.Relay.On( Port6 );
////// Place additional initialization code below here
//Custom Port Labels
ReefAngel.CustomLabels[0]="Heater";
ReefAngel.CustomLabels[1]="Skimmer";
ReefAngel.CustomLabels[2]="LEDPower";
ReefAngel.CustomLabels[3]="Fuge Light";
ReefAngel.CustomLabels[4]="Return";
ReefAngel.CustomLabels[5]="PowerHeads";
ReefAngel.CustomLabels[6]="Alkdoser";
ReefAngel.CustomLabels[7]="Caldoser";
////// Place additional initialization code above here
}
/*
Want to use one float valve port for skimmate locker
Relay Ports:
1 = Heater
2 = Skimmer
3 = LEDPower
4 = Fuge Light
5 = Reeflo Return
6 = Powerheads, 2xTunze 2xJebao
7 = Alkdoser
8 = CalDoser
DC Pumps:
Actinic: Tunze Left
Daylight: Tunze Right
CustomDimmer: // Want lights with sinusoidal ramp up and down with center at 1pm starting and ending at 7am/7pm
// want Jebaos to operate in conjunction with Tunzes some of the time and opposite some of the time, so sometimes it will be
// pushing water at each other, sometimes it will be turning a gyre back and forth.
0 = Jebao Left
1 = Jebao Right
2 = Violet
3 = Royal Blue
4 = White
5 = Cool Blue
6 = Deep Red
7 = Cyan
8 = PWM fans
*/
void loop()
{
ReefAngel.StandardHeater( Port1,785,790 );
ReefAngel.Relay.DelayedOn( Port2,1 );
ReefAngel.StandardLights( Port3,7,0,19,0 ); // led power
ReefAngel.StandardLights( Port4,15,0,10,0 ); // fuge light
if (ReefAngel.Params.PH < 840)
{
ReefAngel.DosingPumpRepeat( Port7,0,30,50 ); // alk doser
}
else
{
ReefAngel.Relay.Off(Port7); // turn it off if the pH is too high
}
ReefAngel.DosingPumpRepeat( Port8,15,30,50 ); // cal doser
ReefAngel.DCPump.UseMemory = false;
ReefAngel.DCPump.SetMode( NutrientTransport,75,10 );
ReefAngel.DCPump.DaylightChannel = Sync; // Tunze right
ReefAngel.DCPump.ActinicChannel = AntiSync; // Tunze left
////// Place your custom code below here
CustomExpansion(0x41,0,NutrientTransportMode(0,75,500,false),true); // Jebao Left
CustomExpansion(0x41,1,NutrientTransportMode(0,75,500,true),true); // Jebao Right
// lights
CustomExpansion(0x41,2,PWMSmoothRamp(13,00,22,00,0,(int)(75*40.95),180,0),false); // V, feeding value, not percent from smoothramp
CustomExpansion(0x41,3,PWMSmoothRamp(13,30,21,30,0,(int)(75*40.95),180,0),false); // RB, feeding value, not percent from smoothramp
CustomExpansion(0x41,4,PWMSmoothRamp(13,30,21,30,0,(int)(75*40.95),180,0),false); // W, feeding value, not percent from smoothramp
CustomExpansion(0x41,5,PWMSmoothRamp(13,30,21,30,0,(int)(50*40.95),180,0),false); // CB, feeding value, not percent from smoothramp
CustomExpansion(0x41,6,PWMSmoothRamp(13,30,21,30,0,(int)(50*40.95),180,0),false); // DR, feeding value, not percent from smoothramp
CustomExpansion(0x41,7,PWMSmoothRamp(13,30,21,30,0,(int)(50*40.95),180,0),false); // Cy, feeding value, not percent from smoothramp
// fans
CustomExpansion(0x41,8,PWMSmoothRamp(13,00,22,00,25,75,180,0),true); // Cy, feeding value, percent from smoothramp
// Turn off Skimmer if Locker is full
if(ReefAngel.HighATO.IsActive())
{
ReefAngel.Relay.Off(Port2);
}
else
{
ReefAngel.Relay.On(Port2);
}
////// Place your custom code above here
// This should always be the last line
ReefAngel.Portal( "AlanM" );
ReefAngel.ShowInterface();
}
void setPHModes(byte syncModeIn, byte waveModeIn)
{
switch (syncModeIn) {
case 0:
ReefAngel.DCPump.DaylightChannel = Sync;
ReefAngel.DCPump.ActinicChannel = AntiSync;
// want to add code here to also set the daylight and actinic ones to the wave function/syncmode I set here
// custom port 0 left should be AntiSync, custom port 1 on right should be Sync and set to same wave function as DC pumps
break;
case 1:
ReefAngel.DCPump.DaylightChannel = Sync;
ReefAngel.DCPump.ActinicChannel - Sync;
// want to add code here to also set the daylight and actinic ones to the wave function/syncmode I set here
// custom port 0 on left should be Sync, custom port 1 on right should be Sync and set to same wave function as DC pumps
break;
case 2:
ReefAngel.DCPump.DaylightChannel = Sync;
ReefAngel.DCPump.ActinicChannel - AntiSync;
// want to add code here to also set the daylight and actinic ones to the wave function/syncmode I set here
// custom port 0 on left should be Sync, custom port 1 on right should be AntiSync and set to same wave function as DC pumps
break;
}
}
int PWMSmoothRamp(byte startHour, byte startMinute, byte endHour, byte endMinute, int startPWM, int endPWM, byte slopeLength, int oldValue)
{
LightsOverride=true;
int current_hour = hour();
int start = NumMins(startHour, startMinute);
int end = NumMins(endHour, endMinute);
if (start > end) // Start is greater than End so it is over midnight
{
if (current_hour < endHour) start -= 1440; // past midnight
if (current_hour >= startHour) end += 1440; // before midnight
}
int current = NumMins(current_hour, minute());
if (slopeLength > ((end-start)/2) ) slopeLength = (end-start)/2; // don't allow a slope length greater than half the total period
if (current <= start || current >= end)
return oldValue; // it's before the start or after the end, return the default
else
{ // do the slope calculation
int pwmDelta = endPWM - startPWM;
int smoothPhase;
if ((current > (start + slopeLength)) && (current < (end - slopeLength)))
return endPWM; // if it's in the middle of the slope, return the high level
else if ((current - start) < slopeLength)
{ // it's in the beginning slope up
smoothPhase = constrain(map(current, start, start+slopeLength, 180, 360), 180, 360);
}
else if ((end - current) < slopeLength)
{ // it's in the end slope down
smoothPhase = constrain(map(current, end-slopeLength, end, 0, 180), 0, 180);
}
return startPWM + (pwmDelta*(1.0+(cos(radians(smoothPhase)))/2.0));
}
}
void CustomExpansion(byte id, byte channel, int percentage, boolean isPercent)
{
Wire.beginTransmission(id);
Wire.write(0);
Wire.write(0xa1);
Wire.endTransmission();
int newdata = 0;
if (isPercent)
{
newdata=(int)(percentage*40.95);
}
else
{
newdata=percentage;
}
Wire.beginTransmission(id);
Wire.write(0x8+(4*channel));
Wire.write(newdata&0xff);
Wire.write(newdata>>8);
Wire.endTransmission();
}