Code: Select all
ReefAngel.PWM.Set16ChannelRaw(0,PWMSmoothRampHighRes(7,30,22,00,0,65,45,0));
ReefAngel.PWM.Set16ChannelRaw(1,PWMSmoothRampHighRes(7,25,22,05,0,65,45,0)); // 05 minute longer
ReefAngel.PWM.Set16ChannelRaw(2,PWMSmoothRampHighRes(7,30,22,00,0,80,45,0));
ReefAngel.PWM.Set16ChannelRaw(3,PWMSmoothRampHighRes(7,30,22,00,0,40,45,0));
ReefAngel.PWM.Set16ChannelRaw(4,PWMSmoothRampHighRes(7,30,22,00,0,20,45,0));
ReefAngel.PWM.Set16ChannelRaw(5,PWMSmoothRampHighRes(7,15,22,15,0,40,45,0)); // 15 minute longer
ReefAngel.PWM.Set16ChannelRaw(6,PWMSmoothRampHighRes(7,20,22,10,0,30,45,0)); // 10 minute longer
ReefAngel.PWM.Set16ChannelRaw(7,PWMSmoothRampHighRes(7,30,22,00,0,10,45,0));
Trying to offset the times for some of the colors, however, resulted in an odd behavior -- in the above code, at 22:00, most channels drop to 0. At that time, the remaining channels (1, 5 and 6) jump WAY up in output -- to at least 50% or 60%, and stay there for the remaining five to fifteen minutes, rather than sloping down as expected.
From a review of the libraries (1.1.1, unmodified except for ORP calibration solution value), I can't see how there's any logic behind Set16ChannelRaw or PWMSmoothRampHighRes that would explain this behavior.
The above code is a simplified and cropped version of my full code, here below:
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 <PAR.h>
#include <ReefAngel.h>
#include <Moon.h>
////// Place global variable code below here
#define Heater_300W Port1
#define Heater_100W Port2
#define Skimmer Port3
#define Neck_Wipe Port4
#define Air_Pump Port5
#define Filter_Pump Port6
#define Refuge_Light Port7
#define Ozone_Gen Port8
#define FlowWolf Box1_Port1
#define Closed_Loop Box1_Port2
#define Kalk_Stirrer Box1_Port3
#define ATO_Pump Box1_Port4
#define Return_Pump_L Box1_Port5
#define Return_Pump_R Box1_Port6
#define Heater_200W Box1_Port7
#define Refugium_Pump Box1_Port8
#define Neutral_White_Center 0
#define Warm_White_Center 1
#define Royal_Blue_Center 2
#define Cool_Blue_Center 3
#define Cyan_Center 4
#define Red_Center 5
#define Indigo_Center 6
#define Violet_Center 7
int MoonBlueLevel=0;
int SunBlueLevel=0;
////// Place global variable code above here
void setup()
{
ReefAngel.Init();
ReefAngel.AddStandardMenu();
ReefAngel.Use2014Screen();
ReefAngel.AddSalinityExpansion();
ReefAngel.AddORPExpansion();
ReefAngel.AddWaterLevelExpansion();
ReefAngel.Add16ChPWM();
ReefAngel.FeedingModePorts = 0;
ReefAngel.FeedingModePortsE[0] = Port5Bit | Port6Bit;
ReefAngel.WaterChangePorts = Port3Bit; // Skimmer
ReefAngel.WaterChangePortsE[0] = Port4Bit | Port8Bit; // ATO Pump and Refugium Circulation
ReefAngel.LightsOnPorts = 0;
ReefAngel.LightsOnPortsE[0] = 0;
ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;
ReefAngel.OverheatShutoffPortsE[0] = Port7Bit;
ReefAngel.TempProbe = T1_PROBE;
ReefAngel.OverheatProbe = T1_PROBE;
ReefAngel.DCPump.FeedingSpeed = 0;
ReefAngel.DCPump.WaterChangeSpeed = 0;
ReefAngel.Relay.On( Air_Pump );
ReefAngel.Relay.On( Filter_Pump );
ReefAngel.Relay.On( FlowWolf );
ReefAngel.Relay.On( Closed_Loop );
ReefAngel.Relay.On( Return_Pump_L );
ReefAngel.Relay.On( Return_Pump_R );
ReefAngel.Relay.On( Refugium_Pump );
}
void loop()
{
if ( ReefAngel.Params.Temp[T1_PROBE] < 775 ) ReefAngel.Relay.On( Heater_300W );
if ( ReefAngel.Params.Temp[T1_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_300W );
if ( ReefAngel.Params.Temp[T2_PROBE] < 775 ) ReefAngel.Relay.On( Heater_100W );
if ( ReefAngel.Params.Temp[T2_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_100W );
if ( ReefAngel.Params.Temp[T3_PROBE] < 775 ) ReefAngel.Relay.On( Heater_200W );
if ( ReefAngel.Params.Temp[T3_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_200W );
ReefAngel.DCPump.UseMemory = false;
ReefAngel.DCPump.Threshold = 80;
ReefAngel.DCPump.SetMode( Sine, 100, 240 );
ReefAngel.DCPump.DaylightChannel = Sync;
ReefAngel.DCPump.ActinicChannel = AntiSync;
moon_init(44,-116); // approximate lat,lon for Boise, ID
SunBlueLevel = PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 80, 45, 0 );
MoonBlueLevel = PWMSmoothRampHighestRes( Moon.riseH, Moon.riseM, Moon.setH, Moon.setM, 0, (int)(MoonPhase()/19), 45, 0);
ReefAngel.PWM.Set16ChannelRaw( Neutral_White_Center, PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 65, 45, 0 ) );
ReefAngel.PWM.Set16ChannelRaw( Warm_White_Center, PWMSmoothRampHighRes( 7, 25, 22, 05, 0, 65, 45, 0 ) ); // 05 minute longer
ReefAngel.PWM.Set16ChannelRaw( Royal_Blue_Center, max (SunBlueLevel, MoonBlueLevel) );
ReefAngel.PWM.Set16ChannelRaw( Cool_Blue_Center, PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 40, 45, 0 ) );
ReefAngel.PWM.Set16ChannelRaw( Cyan_Center, PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 20, 45, 0 ) );
ReefAngel.PWM.Set16ChannelRaw( Red_Center, PWMSmoothRampHighRes( 7, 15, 22, 15, 0, 40, 45, 0 ) ); // 15 minute longer
ReefAngel.PWM.Set16ChannelRaw( Indigo_Center, PWMSmoothRampHighRes( 7, 20, 22, 10, 0, 30, 45, 0 ) ); // 10 minute longer
ReefAngel.PWM.Set16ChannelRaw( Violet_Center, PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 10, 45, 0 ) );
ReefAngel.Relay.Set( Refuge_Light, !((hour() >= 10) && (hour() < 19)));
pinMode(lowATOPin,OUTPUT);
if ( (hour() >= 8) && (hour() < 22) )
{
if ( now()%7200<10 ) analogWrite(lowATOPin,255);
else analogWrite(lowATOPin,96);
}
else analogWrite(lowATOPin,96);
ReefAngel.Relay.DelayedOn( Skimmer, 1 );
if ( ReefAngel.WaterLevel.GetLevel() > 55 ) ReefAngel.Relay.Off( Skimmer );
if ( ReefAngel.DisplayedMenu==FEEDING_MODE ) ReefAngel.Relay.Off( Skimmer );
if ( !ReefAngel.HighATO.IsActive() ) ReefAngel.Relay.Override( Skimmer, 0 );
ReefAngel.DosingPumpRepeat( Neck_Wipe, 0, 240, 30 );
ReefAngel.DosingPumpRepeat( Kalk_Stirrer, 0, 240, 30 );
if ( (hour() >= 8) && (hour() < 22) )
{
ReefAngel.WaterLevelATO( ATO_Pump, 1200, 38, 40 ); // On at 38% and Off at 40% with 1200 second (20 minute) timeout.
}
if ( (hour() >= 7) && (hour() < 8) )
{
ReefAngel.WaterLevelATO( ATO_Pump, 3600, 38, 40 ); // On at 38% and Off at 40% with 3600 second (60 minute) timeout.
}
if ( ReefAngel.Params.ORP>450 || ReefAngel.Params.ORP==0 ) ReefAngel.Relay.Off( Ozone_Gen );
if ( ReefAngel.Params.ORP<400 && ReefAngel.Params.ORP>0 ) ReefAngel.Relay.On( Ozone_Gen );
ReefAngel.Portal( "joshlawless" );
ReefAngel.DDNS( "reef" );
ReefAngel.ShowInterface();
}
int PWMSmoothRampHighestRes(byte startHour, byte startMinute, byte endHour, byte endMinute, int startPWMint, int endPWMint, byte slopeLength, int oldValue)
{
LightsOverride=true;
int current_hour = hour();
long start = NumMins(startHour, startMinute)*60L;
long end = NumMins(endHour, endMinute)*60L;
long slopeLengthSecs = slopeLength*60L;
if (start > end) // Start is greater than End so it is over midnight
{
if (current_hour < endHour) start -= 1440L*60L; // past midnight
if (current_hour >= startHour) end += 1440L*60L; // before midnight
}
long current = NumMins(current_hour, minute())*60L + second();
if (slopeLengthSecs > ((end-start)/2) ) slopeLengthSecs = (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 = endPWMint - startPWMint;
float smoothPhase;
if ((current > (start + slopeLengthSecs)) && (current < (end - slopeLengthSecs)))
return endPWMint; // if it's in the middle of the slope, return the high level
else if ((current - start) < slopeLengthSecs)
{ // it's in the beginning slope up
smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0) + 180.0;
//smoothPhase = constrain(map(current, start, start+slopeLengthSecs, 180, 360), 180, 360);
}
else if ((end - current) < slopeLengthSecs)
{ // it's in the end slope down
smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0);
//smoothPhase = constrain(map(current, end-slopeLengthSecs, end, 0, 180), 0, 180);
}
return startPWMint + (int)(pwmDelta*((1.0+(cos(radians(smoothPhase))))/2.0));
}
}