Almost complete feature hog :P

Share you PDE file with our community
Post Reply
Deckoz2302
Posts: 149
Joined: Tue Nov 01, 2011 11:05 am

Almost complete feature hog :P

Post by Deckoz2302 »

Alright these are my PDE's utilizing all of the modules

here are the things included
Vortech mode displayed on main w/color of mode
All Measured Parameters
Vortech goto Nutrient transport for 30 min after feed
Vortech goto night when moonlights come on
Full use of the PWM module - with my Seasons() function for a true sunphase
Clouds, lightning
Moonphase displayed on main - waning, waxing, quarter, full

Things I still need to code
-SingleATO
-Use of Floatvalve for flood control in overflow(im running 350gph through a 200gph overflow haha scary)
-Custom Menu(Feed, Waterchange, Force Thunderstorm, Clear Overflow error, couple other things I havent thought of yet)
-Sending force thunderstorm to pwm
-couple other things I havent thought of yet :P

This is the RA pde

Code: Select all

#include <ReefAngel_Features.h>
#include <ReefAngel_Globals.h>
#include <ReefAngel_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <ReefAngel_EEPROM.h>
#include <ReefAngel_NokiaLCD.h>
#include <ReefAngel_ATO.h>
#include <ReefAngel_Joystick.h>
#include <ReefAngel_LED.h>
#include <ReefAngel_TempSensor.h>
#include <ReefAngel_Relay.h>
#include <ReefAngel_PWM.h>
#include <ReefAngel_Timer.h>
#include <ReefAngel_Memory.h>
#include <avr/pgmspace.h>
#include <ReefAngel_Colors.h>
#include <ReefAngel_CustomColors.h>
#include <ReefAngel_Salinity.h>
#include <ReefAngel_RF.h>
#include <ReefAngel.h>  


//*********************************************************************************************************************************
//Global Variables
byte vtechmode;
byte DayAge;
byte ThisPhase;
int JulianDate(int,int,int);
double MoonAge(int,int,int);
byte MoonState();
boolean bFeeding=false;
byte SeasonsVar[]={
  0,0,0,0,0,0,0,0};
//End Global Variables
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Start of PWM Expansion Code Header

// This is just how we are going to reference the PWM expansion ports within the code.
// You can change the labels if you would like, just as long as they are changed all throughout the code too.
#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5

// Initial values to all 6 channels at startup. They will always be 0.
byte PWMChannel[]={
  0,0,0,0,0,0};
//End of PWM Expansion Code Header
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Define Relay Ports
#define PowerCenter         1
#define Salinitypsu         2
#define Refugium            3
#define Reactors            4
#define LEDPSU              5
#define Return              6
#define Heater              7
#define Skimmer             8
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Custom Main, Graph & Menu
void DrawCustomMain()
{
  Wire.requestFrom(8,8);
  for (int a=0;a<8;a++)
  {
    if (Wire.available()) SeasonsVar[a]=Wire.receive();
  }

  byte x = 6;
  byte y = 2;
  byte t;
  char text[7];

  ReefAngel.LCD.DrawDate(6, 2);
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 132, 11);
  pingSerial();

  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 6, 90, "--------------------");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 6, 126, "--------------------");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 2, 93, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 2, 103, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 2, 113, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 2, 123, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 126, 93, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 126, 103, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 126, 113, "|");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 126, 123, "|");


  ReefAngel.LCD.DrawText(0,255,18,12,"EcoSmart Vortech");
  ReefAngel.LCD.Clear(255, 1, 19, 128, 29);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,35,21,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,42,21,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,25,21,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,22,21,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_PINK,255,25,21,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,8,21,"Nutrient Trnsp.");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,23,21,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,45,21,"Night");

  ReefAngel.LCD.DrawText(0,255,10,30,"Display");
  ConvertNumToString(text, ReefAngel.Params.Temp3, 10);
  ReefAngel.LCD.DrawLargeText(COLOR_INDIANRED, 255, 10, 40, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,100,30,"pH");
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(COLOR_PLUM, 255, 85, 40, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,43,48,"Salinity");
  ConvertNumToString(text, ReefAngel.Params.Salinity, 100);
  ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN, 255, 49, 58, text, Num8x8);
  pingSerial();

  pingSerial();
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 77, TempRelay);

  ReefAngel.LCD.DrawText(0,255,8,68,"Sump");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp1, COLOR_CORNFLOWERBLUE, 40, 68, 10);

  ReefAngel.LCD.DrawText(0,255,70,68,"Room");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp2, COLOR_CORNFLOWERBLUE, 99, 68, 10);

  if (ReefAngel.PWM.GetActinicValue() > 0)
  {
    ReefAngel.LCD.DrawText(0,255,8,97,"Moon");
    DayAge = MoonAge(day(), month(), year());
    MoonState(DayAge);
    char* ThisPhaseLabel[]={
      "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent"            };
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,35,97,ThisPhaseLabel[ThisPhase]);
  }
  else
  {
    ReefAngel.LCD.DrawText(0,255,8,97,"Cloud");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,40,97, SeasonsVar[5]); 
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,52,97, ":");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,57,97, SeasonsVar[6]); 
    ReefAngel.LCD.DrawText(0,255,80,97,"Length");    
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,110,97, SeasonsVar[7]); 
  }
  //  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetActinicValue(),COLOR_CORNFLOWERBLUE, 105, 109, 1);  

  ReefAngel.LCD.DrawText(0,255,8,109, "Sunrise");  
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,15,119, SeasonsVar[1]);
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,20,119, ":");
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,25,119, SeasonsVar[2]);

  ReefAngel.LCD.DrawText(0,255,75,109,"%");
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255, 60, 109, SeasonsVar[0]);

  ReefAngel.LCD.DrawText(0,255,88,109, "Sunset");  
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,90,119, SeasonsVar[3]);
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,102,119, ":");
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,107,119, SeasonsVar[4]); 
}
void DrawCustomGraph()
{
}

//End Custom
//*********************************************************************************************************************************

void setup()
{
  ReefAngel.Init(); 
  ReefAngel.PHMin=502;
  ReefAngel.PHMax=827;

  ReefAngel.FeedingModePorts = B10100000;
  ReefAngel.WaterChangePorts = B10101000;
  ReefAngel.OverheatShutoffPorts = B01000000;
  ReefAngel.LightsOnPorts = B00000000;

  // Ports that are always on
  ReefAngel.Relay.On(PowerCenter);
  ReefAngel.Relay.On(Return);
  ReefAngel.Relay.On(Reactors);
  ReefAngel.Relay.On(LEDPSU);
  ReefAngel.Relay.On(Refugium);
  ReefAngel.Relay.On(Salinitypsu);
}

void loop()
{

  //Heater on&off
  ReefAngel.StandardHeater(Heater,780,792);

  //Delay Skimmer
  ReefAngel.Relay.DelayedOn(Skimmer, 5);
  if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
  if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
  {
    bFeeding=false; 
    ReefAngel.RF.UseMemory=false;
    ReefAngel.RF.SetMode(Smart_NTM,155,5);
    ReefAngel.Timer[4].SetInterval(10); // Timer for 30min
    ReefAngel.Timer[4].Start();
    vtechmode = 5;
      }
  if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
  {
    ReefAngel.RF.UseMemory=true;
    vtechmode = InternalMemory.RFMode_read();
  }
  if (hour() <= 6 && hour() >= 19)
  {
      ReefAngel.PWM.SetActinic(MoonPhase());
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Night,15,0);
      vtechmode = 9;
  }
  else 
    {
      ReefAngel.RF.UseMemory=true;
      vtechmode = InternalMemory.RFMode_read();
      ReefAngel.PWM.SetActinic(0);
    }

  ReefAngel.ShowInterface();
}

int JulianDate(int d, int m, int y)
{ 
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{ 
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip); 
  //After several trials I've seen to add the following lines, 
  //which gave the result was not bad 
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1: 
    0, 29;
    ThisPhase = 0;
  case 2: 
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3: 
    7;
    ThisPhase = 2;
  case 4: 
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5: 
    14;
    ThisPhase = 4;
  case 6: 
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7: 
    22;
    ThisPhase = 6;
  case 8: 
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default: 
    return 0;
  }
}
This is the PWM module's PDE

Code: Select all

#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include "ReefAngel_Globals.h"
#include "ReefAngel_Features.h"
#include <ReefAngel_Colors.h>
#include <ReefAngel_CustomColors.h>
#include <Wire.h>
#include <avr/wdt.h>

#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5


//*********************************************************************************************************************************
//Globals integers
long calcSec(long,long);
long calcTime(long,long);
short Ndays;   //Returns the number of day in the year
//*********************************************************************************************************************************

byte cloudduration=0;
int cloudstart=0;
int rhour = 0, rmin = 0, shour = 0, smin = 0;

byte PWMports[] ={
  3,5,6,9,10,11};
byte ChannelValue[] = {
  0,0,0,0,0,0};

// PWM, sunrisehour, sunriseminute, sunsethour, sunsetminute, cloudstarthout, cloudstartminute, cloudduration
byte SeasonsVar[]={
  0,0,0,0,0,0,0,0};

byte cmdnum=255;
byte datanum=255;
void setup()
{
  Serial.begin(57600);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  randomSeed(analogRead(0));
  pinMode(3,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  //wdt_enable(WDTO_1S);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
  now();
}

void loop()
{
  wdt_reset();
  //Serial.println(ChannelValue[0],DEC);
  if (cmdnum!=255)
  {
    ProcessCMD(cmdnum,datanum);    
    cmdnum=255;
    datanum=255;
  }
  Seasons();
  CheckCloud();
  //Serial.println("Start");
  //Serial.println(ChannelValue[0],DEC);
  //Serial.println(rhour,DEC);
  //Serial.println(rmin,DEC);
  //Serial.println(shour,DEC);
  //Serial.println(smin,DEC);
  //Serial.println(cloudstart/60,DEC);
  //Serial.println(cloudstart%60,DEC);
  //Serial.println(cloudduration,DEC);
  for (int a=0;a<6;a++)
  {
    analogWrite(PWMports[a],ChannelValue[a]);
    
  }
}

void receiveEvent(int howMany) {
  wdt_reset();
  if (howMany==5)
  {
    byte cmd1, cmd2, cmd3, cmd4, cmd5;
    cmd1=Wire.receive();
    cmd2=Wire.receive();
    cmd3=Wire.receive();
    cmd4=Wire.receive();
    cmd5=Wire.receive();
    if (cmd1=='$' && cmd2=='$' && cmd3=='$')
    {
      cmdnum=cmd4;
      datanum=cmd5;
      //Serial.println(cmd4,DEC);
      //Serial.println(cmd5,DEC);
    }
  }
  else
  {
    for (int a=0;a<howMany;a++)
    {
      Wire.receive();
    }
  }  
}

void ProcessCMD(byte cmd, byte data)
{
  wdt_reset();
  // Individual Channel
  if (cmd>=0 && cmd<=5)
  {
    ChannelValue[cmd]=data;
    analogWrite(PWMports[cmd],data);
  }
}

void requestEvent() {
  SeasonsVar[0]=ChannelValue[0];
  SeasonsVar[1]=rhour;
  SeasonsVar[2]=rmin;
  SeasonsVar[3]=shour;
  SeasonsVar[4]=smin;
  SeasonsVar[5]=cloudstart/60;
  SeasonsVar[6]=cloudstart%60;
  SeasonsVar[7]=cloudduration;
  Wire.send(SeasonsVar,8);
}


//*********************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 1

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 100

  // Minimum number of minutes for cloud duration.  Don't use max duration of less than 6
#define Min_Cloud_Duration 6

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 20

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 2

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 5

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(11,00)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(17,00)

  // Percentage chance of a lightning happen for every cloud
  // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 100

  // Channels used by the actinic LEDs on the PWM Expansion module
  // These channels will not be dimmed when the cloud effect is triggered
  // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
#define Actinic_Channels B001110

  // Channels used by the daylight LEDs on the PWM Expansion module
  // These channels will be used for the spike when lightning effect is triggered
  // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
#define Daylight_Channels B000001

  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen 
  // results could happen.
  // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can
  //fit in that 510 minutes window.
  // It's a tight fit, but it did.

  //#define printdebug // Uncomment this for debug print on Serial Monitor window
#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process. 


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte numclouds=0;
  static byte lightningchance=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;
  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
  if (cloudchance==255)
#else
    if (hour()==0 && minute()==0 && second()==1 && cloudchance==255) 
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100); 
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds. 
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0; 
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day. 
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          ChannelValue[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ChannelValue[a],0,180);
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        for (int b=0;b<6;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            //delay(10);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
    }
    
    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
}
//End Cloud Function
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Reverse PWM slope from cloud function
byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
}
//End Reverse PWM function
//*********************************************************************************************************************************
//*********************************************************************************************************************************
//Calculate Sunrise/set by date & predefined season & rise/set times
void Seasons()
{
#define forceseasoncalculation
  static byte ssn , ssnp , ssnpt ;
  long stime, wstime, vstime, wrtime, rtime, vrtime;
  int wrhour,wrmin,wrsec,wshour,wsmin,wssec,rsec,ssec,vrhour,vrmin,vrsec,vshour,vsmin,vssec;
  int iDiffrise = 0;
  int iDiffset = 0;
  int risediffperday = 0;
  int setdiffperday = 0;
  int totalrise = 0;
  int totalset = 0;
  byte s=0;
  int DaysPerYear;        

  //rise and set times set by hour and minute. there are 4 seasons however there are 8 highs & lows in rise and set throughout the year
  //first spot is second half of winter starting jan 1st 
    int risehour[8]= {
    7,7,7,6,6,5,6,6      };
  int riseminute[8]={
    00,30,00,30,00,30,00,30      };
  int sethour[8] = {
    17,18,18,19,19,19,19,18      };
  int setminute[8] = {
    30,00,30,00,00,30,00,00      };

  //
  if (hour()==0 && minute()==0 && second()==0) ssnp=0;
#ifdef forceseasoncalculation
  if (ssnp==0)
#else
    if (hour()==0 && minute()==0 && second()==1 && ssnp==0)
#endif
    {
      //leapyear or not - Define days per year
      if (year()%4 == 0 && !(year()%100 == 0 && year()%400 != 0)) {
        DaysPerYear=366;
      }
      //every other year
      else {
        DaysPerYear = 365;
      }
      //Call Day Number Calc
      DayNumber(year(),month(),day());
      //define days between begin and middle season
      int seasons[9] ={
        0,45,96,135,187,238,283,328,DaysPerYear                  };
      //define season and array pulling variable
      for (s=0; seasons[s] < Ndays; s++) ssn = s+1, ssnpt = s+1, ssnp = s;
      //set loop on array time pulling variable to go back to beginning instead of increasing array size
      if (ssn >= 7) ssn = 0;

      //differece in seconds between season array times
      long rise1 = calcSec(risehour[ssn],riseminute[ssn]);
      long rise2 = calcSec(risehour[ssnp],riseminute[ssnp]);
      iDiffrise = calcTime(rise1, rise2);
      long set1 = calcSec(sethour[ssn],setminute[ssn]);
      long set2 = calcSec(sethour[ssnp],setminute[ssnp]);
      iDiffset = calcTime(set1,set2);             

      //calculate new sunrise/set difference from array value 
      //here we take the difference in day and get a difference in seconds per day. So if the time difference is 30 minutes from the last e
      //uation we divide that by the start day of season subtracted by the end day of season to get the number of days in the season
        //example: 30 minutes/(day 45 – day 0) = 30/45 = 0.66666
      //0.66666 is the difference of minutes each day throughout the season for sunrise
      risediffperday = iDiffrise/(seasons[ssnpt]-seasons[ssnp]);
      totalrise = risediffperday*(Ndays - seasons[ssnp]);
      setdiffperday = iDiffset/(seasons[ssnpt]-seasons[ssnp]);
      totalset = setdiffperday*(Ndays - seasons[ssnp]);

      //creating time in seconds
      rtime=calcSec(risehour[ssnp],riseminute[ssnp]);
      if (ssnp == 0 || ssnp == 2 || ssnp == 4 || ssnp == 6){ 
        rtime -= totalrise;
      }
      else {
        rtime += totalrise;
      }
      stime=calcSec(sethour[ssnp],setminute[ssnp]);
      if (ssnp == 1 || ssnp == 3 || ssnp == 5 || ssnp == 7){ 
        stime -= totalset;
      }
      else {
        stime += totalset;
      }
      wrtime = rtime + 1200;
      wstime = stime - 1200;
      vrtime = rtime - 1200;
      vstime = stime + 1200;
      //turning seconds back to h m s
      //here are three different times offset by half hour to have different colors rise and set at different times
      //standard times
      rhour=rtime/3600; 
      rtime=rtime%3600;
      rmin=rtime/60; 
      rtime=rtime%60; 
      rsec=rtime;
      if(rsec > 30) rmin++; 
      shour=stime/3600; 
      stime=stime%3600;
      smin=stime/60; 
      stime=stime%60; 
      ssec=stime;
      if(ssec > 30) smin++;
      //to change the offset change the number 1200 in seconds added or subtracted to rtime
      //half hour shorter
      wrhour = wrtime/3600;
      wrtime=wrtime%3600;
      wrmin=wrtime/60;
      wrtime=wrtime%60;
      wrsec=wrtime;
      if(wrsec>30) wrmin++;
      wshour = wstime/3600;
      wstime=wstime%3600;
      wsmin=wstime/60;
      wstime=wstime%60;
      wssec=wstime;
      if(wssec>30) wsmin++;
      //half hour longer
      vrhour = vrtime/3600;
      vrtime=vrtime%3600;
      vrmin=vrtime/60;
      vrtime=vrtime%60;
      vrsec=vrtime;
      if(vrsec>30) vrmin++;
      vshour = vstime/3600;
      vstime=vstime%3600;
      vsmin=vstime/60;
      vstime=vstime%60;
      vssec=vstime;
      if(vssec>30) vsmin++;



      //time for each active led channel to pull for sunrise
      int Sunrise[2]={
        rhour,rmin                  };
      int Sunset[2]={
        shour,smin                  };
      int whSunrise[2]={
        wrhour,wrmin                  };
      int whSunset [2]={
        wshour,wsmin                  };
      int vSunrise[2]={
        vrhour,vrmin                  };
      int vSunset [2]={
        vshour,vsmin                  };

      ChannelValue[LEDPWM0]=PWMSlope(whSunrise[1],whSunrise[2],whSunset[1],whSunset[2],0,100,180,ChannelValue[LEDPWM0]);
      ChannelValue[LEDPWM1]=PWMSlope(vSunrise[1],vSunrise[2],vSunset[1],vSunset[2],0,100,180,ChannelValue[LEDPWM1]);
      ChannelValue[LEDPWM2]=PWMSlope(Sunrise[1],Sunrise[2],Sunset[1],Sunset[2],0,100,180,ChannelValue[LEDPWM2]);
      ChannelValue[LEDPWM3]=PWMSlope(Sunrise[1],Sunrise[2],whSunset[1],whSunset[2],0,100,180,ChannelValue[LEDPWM3]);
      //return Sunrise, Sunset, whSunrise,whSunset,vSunrise,vSunset;
    }
}
//End Seasons Calculation
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Calculators for Seasons function
long calcSec(long hr, long minu)
{
    long totalseconds;
    totalseconds=(hr*3600)+(minu*60);
    return totalseconds;
}

long calcTime(long seconds1, long seconds2)
{
  long timediff=abs(seconds1-seconds2);
  return timediff;
}

void DayNumber(unsigned int y, unsigned int m, unsigned int d)
{
  int days[]={
    0,31,59,90,120,151,181,212,243,273,304,334      };    // Number of days at the beginning of the month in a not leap year.
  //Start to calculate the number of day
  if (m==1 || m==2){
    Ndays = days[(m-1)]+d;			   //for any type of year, it calculate the number of days for January or february
  }				// Now, try to calculate for the other months
  else if ((y % 4 == 0 && y % 100 != 0) ||  y % 400 == 0){  //those are the conditions to have a leap year
    Ndays = days[(m-1)]+d+1;     // if leap year, calculate in the same way but increasing one day
  }
  else {					  //if not a leap year, calculate in the normal way, such as January or February
    Ndays = days[(m-1)]+d;
  }
} 

//End calculators
//*********************************************************************************************************************************
If anyone has any questions of what does what or needs help, I will try to help the best I can
Last edited by Deckoz2302 on Mon Jan 02, 2012 9:57 am, edited 1 time in total.
StuGotz
Posts: 95
Joined: Sat Oct 15, 2011 9:17 am

Re: Almost complete feature hog :P

Post by StuGotz »

Sweet, I'm definitely adding the moon phase to my Custom Main! Where's your custom main code?
Deckoz2302
Posts: 149
Joined: Tue Nov 01, 2011 11:05 am

Re: Almost complete feature hog :P

Post by Deckoz2302 »

oops didnt realize I deleted it when I copied it - the moonphase is here

http://forum.reefangel.com/viewtopic.php?f=12&t=530

and my custom main is here

http://forum.reefangel.com/viewtopic.ph ... 9&start=30

I made a change to custom main which is on page 5 - during the day it shows clouds/duration and at night it shows moon phase
Deckoz2302
Posts: 149
Joined: Tue Nov 01, 2011 11:05 am

Re: Almost complete feature hog :P

Post by Deckoz2302 »

I realized I forgot some of the code when I posted here originally - Changes are

-Added ability to see pwm % for all channels
-During Day PWM %'s are shown on screen
-If Cloud, no pwm % is shown, Time of cloud and duration is shown, if lightning label of "cloud" changes to "storm"
-Night time, Moon cycle & pwm % is shown
-Still working on figuring out how to have more then one Vortech mode change in code & being able to define global variable to show current mode on main screen, changing modes Isn't the problem - its keeping the global defined
-Added the ability for a user to Define a single Rise and Set hour in PWM expansion pde, code does the rest - may be adding the ability for a left to right delayed rise for people that have set up there LED fixture to rise on one side first

hmm I think of stuff daily to add to this so updates are always coming

Code: Select all

#include <ReefAngel_Features.h>
#include <ReefAngel_Globals.h>
#include <ReefAngel_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <ReefAngel_EEPROM.h>
#include <ReefAngel_NokiaLCD.h>
#include <ReefAngel_ATO.h>
#include <ReefAngel_Joystick.h>
#include <ReefAngel_LED.h>
#include <ReefAngel_TempSensor.h>
#include <ReefAngel_Relay.h>
#include <ReefAngel_PWM.h>
#include <ReefAngel_Timer.h>
#include <ReefAngel_Memory.h>
#include <avr/pgmspace.h>
#include <ReefAngel_Colors.h>
#include <ReefAngel_CustomColors.h>
#include <ReefAngel_Salinity.h>
#include <ReefAngel_RF.h>
#include <ReefAngel.h>  


//*********************************************************************************************************************************
//Global Variables
byte vtechmode;
byte DayAge;
byte ThisPhase;
int JulianDate(int,int,int);
double MoonAge(int,int,int);
byte MoonState();
boolean bFeeding=false;
byte SeasonsVar[]={
  0,0,0,0,0,0,0,0,0,0,0,0};
//End Global Variables
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Start of PWM Expansion Code Header

// This is just how we are going to reference the PWM expansion ports within the code.
// You can change the labels if you would like, just as long as they are changed all throughout the code too.
#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5

// Initial values to all 6 channels at startup. They will always be 0.
byte PWMChannel[]={
  0,0,0,0,0,0};
//End of PWM Expansion Code Header
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Define Relay Ports
#define PowerCenter         1
#define Salinitypsu         2
#define Refugium            3
#define Reactors            4
#define LEDPSU              5
#define Return              6
#define Heater              7
#define Skimmer             8
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Custom Main, Graph & Menu
void DrawCustomMain()
{
  Wire.requestFrom(8,8);
  for (int a=0;a<12;a++)
  {
    if (Wire.available()) SeasonsVar[a]=Wire.receive();
  }

  byte x = 6;
  byte y = 2;
  byte t;
  char text[7];

  ReefAngel.LCD.DrawDate(6, 2);
  ReefAngel.LCD.Clear(COLOR_BLACK, 1, 11, 132, 11);
  pingSerial();

  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 6, 90, "--------------------");
  ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 6, 126, "--------------------");
  for (byte Left=93; Left < 133; Left+=10)
  {
    ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 2, Left, "|");
    ReefAngel.LCD.DrawText(COLOR_TOMATO,255, 126, Left, "|");
  }

  ReefAngel.LCD.DrawText(0,255,18,12,"EcoSmart Vortech");
  ReefAngel.LCD.Clear(255, 1, 19, 128, 29);
  if (vtechmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN,255,35,21,"Constant");
  else if(vtechmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,42,21,"Lagoon");
  else if (vtechmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_GOLD,255,25,21,"Reef Crest");
  else if (vtechmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_CORNFLOWERBLUE,255,22,21,"Short Pulse");
  else if (vtechmode == 4) ReefAngel.LCD.DrawLargeText(COLOR_PINK,255,25,21,"Long Pulse");
  else if (vtechmode == 5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,8,21,"Nutrient Trnsp.");
  else if (vtechmode == 6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,255,23,21,"Tidal Swell");
  else if (vtechmode == 9) ReefAngel.LCD.DrawLargeText(COLOR_WHITE,0,45,21,"Night");

  ReefAngel.LCD.DrawText(0,255,10,30,"Display");
  ConvertNumToString(text, ReefAngel.Params.Temp3, 10);
  ReefAngel.LCD.DrawLargeText(COLOR_INDIANRED, 255, 10, 40, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,100,30,"pH");
  ConvertNumToString(text, ReefAngel.Params.PH, 100);
  ReefAngel.LCD.DrawLargeText(COLOR_PLUM, 255, 85, 40, text, Num8x8);
  pingSerial();

  ReefAngel.LCD.DrawText(0,255,43,48,"Salinity");
  ConvertNumToString(text, ReefAngel.Params.Salinity, 10);
  ReefAngel.LCD.DrawLargeText(COLOR_LIMEGREEN, 255, 49, 58, text, Num8x8);
  pingSerial();

  pingSerial();
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 77, TempRelay);

  ReefAngel.LCD.DrawText(0,255,8,68,"Sump");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp1, COLOR_CORNFLOWERBLUE, 40, 68, 10);

  ReefAngel.LCD.DrawText(0,255,70,68,"Room");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp2, COLOR_CORNFLOWERBLUE, 99, 68, 10);

  if (ReefAngel.PWM.GetActinicValue() > 0)//if moon phase is active display state and pwm %
  {
    ReefAngel.LCD.DrawText(0,255,8,98,"Moon");
    DayAge = MoonAge(day(), month(), year());
    MoonState(DayAge);
    char* ThisPhaseLabel[]={
      "New","Waxing Crescent","First Quarter","Waxing Gibbous","Full","Waning Gibbous","Last Quarter","Waning Crescent"                        };
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,35,98,ThisPhaseLabel[ThisPhase]);
    ReefAngel.LCD.DrawText(0,255,75,109,"%");
    ReefAngel.LCD.DrawSingleMonitor(COLOR_CORNFLOWERBLUE,255, 60, 109, ReefAngel.PWM.GetActinicValue());
  }
  else if (SeasonsVar[5] > 0) //or if there is a cloud display clouds instead
  {
    if (SeasonsVar[8] > 0) ReefAngel.LCD.DrawText(0,255, 8, 98, "Storm"); //decide between dislaying storm or cloud based on lightningchance
    else ReefAngel.LCD.DrawText(0,255,8,98,"Cloud");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,42,98, SeasonsVar[5]); 
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,54,98, ":");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,59,98, SeasonsVar[6]); 
    ReefAngel.LCD.DrawText(0,255,75,98,"Length");  
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,115,98, SeasonsVar[7]); 
  }
  else //if no moon or clouds show LED PWM %'s
  {
    ReefAngel.LCD.DrawText(0,255,8,98,"W");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,15,98,SeasonsVar[0]);
    ReefAngel.LCD.DrawText(COLOR_TOMATO,255,33,98,"|");
    ReefAngel.LCD.DrawText(0,255,38,98,"R");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,45,98,SeasonsVar[9]);
    ReefAngel.LCD.DrawText(COLOR_TOMATO,255,63,98,"|");
    ReefAngel.LCD.DrawText(0,255,68,98,"B");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,75,98,SeasonsVar[10]);
    ReefAngel.LCD.DrawText(COLOR_TOMATO,255,93,98,"|");
    ReefAngel.LCD.DrawText(0,255,98,98,"V");
    ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,105,98,SeasonsVar[11]);
  } 
  
  ReefAngel.LCD.DrawText(0,255,8,109, "Sunrise");  
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,15,119, SeasonsVar[1]);
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,20,119, ":");
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,25,119, SeasonsVar[2]);

  ReefAngel.LCD.DrawText(0,255,88,109, "Sunset");  
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,90,119, SeasonsVar[3]);
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,102,119, ":");
  ReefAngel.LCD.DrawText(COLOR_CORNFLOWERBLUE,255,107,119, SeasonsVar[4]); 
}
void DrawCustomGraph()
{
}

//End Custom
//*********************************************************************************************************************************

void setup()
{
  ReefAngel.Init(); 
  ReefAngel.PHMin=502;
  ReefAngel.PHMax=827;

  ReefAngel.FeedingModePorts = B10100000;
  ReefAngel.WaterChangePorts = B10101000;
  ReefAngel.OverheatShutoffPorts = B01000000;
  ReefAngel.LightsOnPorts = B00000000;

  // Ports that are always on
  ReefAngel.Relay.On(PowerCenter);
  ReefAngel.Relay.On(Return);
  ReefAngel.Relay.On(Reactors);
  ReefAngel.Relay.On(LEDPSU);
  ReefAngel.Relay.On(Refugium);
  ReefAngel.Relay.On(Salinitypsu);
}

void loop()
{

  //Heater on&off
  ReefAngel.StandardHeater(Heater,780,792);

  //Delay Skimmer
  ReefAngel.Relay.DelayedOn(Skimmer, 5);
  if (hour() >=7 && hour() <= 18)
  {  
    if (ReefAngel.DisplayedMenu==FEEDING_MODE) bFeeding=true;
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && bFeeding )
    {
      bFeeding=false; 
      ReefAngel.RF.UseMemory=false;
      ReefAngel.RF.SetMode(Smart_NTM,155,5);
      ReefAngel.Timer[4].SetInterval(1800); // Timer for 30min
      ReefAngel.Timer[4].Start();
      vtechmode = 5;
    }
    if (ReefAngel.DisplayedMenu==DEFAULT_MENU && ReefAngel.Timer[4].IsTriggered())
    {
      ReefAngel.RF.UseMemory=true;
      vtechmode = InternalMemory.RFMode_read();
    }
  }
  if (hour() <= 6 || hour() >= 19)
  {
    ReefAngel.PWM.SetActinic(MoonPhase());
    ReefAngel.RF.UseMemory=false;
    ReefAngel.RF.SetMode(Night,15,0);
    vtechmode = 9;
  }
  else 
  {
    ReefAngel.RF.UseMemory=true;
    vtechmode = InternalMemory.RFMode_read();
    ReefAngel.PWM.SetActinic(0);
  }

  ReefAngel.ShowInterface();
}

int JulianDate(int d, int m, int y)
{ 
  int mm, yy;
  int k1, k2, k3;
  int j;

  yy = y - (int)((12 - m) / 10);
  mm = m + 9;
  if (mm >= 12)
  {
    mm = mm - 12;
  }
  k1 = (int)(365.25 * (yy + 4712));
  k2 = (int)(30.6001 * mm + 0.5);
  k3 = (int)((int)((yy / 100) + 49) * 0.75) - 38;
  // 'j' for dates in Julian calendar:
  j = k1 + k2 + d + 59;
  if (j > 2299160)
  {
    // For Gregorian calendar:
    j = j - k3; // 'j' is the Julian date at 12h UT (Universal Time)
  }
  return j;
}

double MoonAge(int d, int m, int y)
{ 
  int j = JulianDate(d, m, y);
  //Calculate the approximate phase of the moon
  int ip = (j + 4.867) / 29.53059;
  ip = ip - abs(ip); 
  //After several trials I've seen to add the following lines, 
  //which gave the result was not bad 
  if(ip < 0.5)
    int ag = ip * 29.53059 + 29.53059 / 2;
  else
    int ag = ip * 29.53059 - 29.53059 / 2;
  // Moon's age in days
  byte ag = abs(ag) + 1;
  return ag;
}

byte MoonState(byte D)
{
  switch(D){
  case 1: 
    0, 29;
    ThisPhase = 0;
  case 2: 
    1, 2, 3, 4, 5, 6;
    ThisPhase = 1;
  case 3: 
    7;
    ThisPhase = 2;
  case 4: 
    8, 9, 10, 11, 12, 13;
    ThisPhase = 3;
  case 5: 
    14;
    ThisPhase = 4;
  case 6: 
    15, 16, 17, 18, 19, 20, 21;
    ThisPhase = 5;
  case 7: 
    22;
    ThisPhase = 6;
  case 8: 
    23, 24, 25, 26, 27, 28;
    ThisPhase = 7;
  default: 
    return 0;
  }
}
PWM Module PDE

Code: Select all

#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include "ReefAngel_Globals.h"
#include "ReefAngel_Features.h"
#include <ReefAngel_Colors.h>
#include <ReefAngel_CustomColors.h>
#include <Wire.h>
#include <avr/wdt.h>

#define LEDPWM0 0
#define LEDPWM1 1
#define LEDPWM2 2
#define LEDPWM3 3
#define LEDPWM4 4
#define LEDPWM5 5


//*********************************************************************************************************************************
//Globals Vavriables
long calcSec(long,long);
long calcTime(long,long);
short Ndays;
static byte lightningchance=0;
byte cloudduration=0;
int cloudstart=0;
int rhour = 0, rmin = 0, shour = 0, smin = 0;
byte PWMports[] ={
  3,5,6,9,10,11};
byte ChannelValue[] = {
  0,0,0,0,0,0};
byte SeasonsVar[]={
  0,0,0,0,0,0,0,0,0,0,0,0}; // PWM0, sunrisehour, sunriseminute, sunsethour, sunsetminute, cloudstarthout, cloudstartminute, cloudduration,lightningchance, PWM1,PWM2,PWM3
byte cmdnum=255;
byte datanum=255;
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Setup
void setup()
{
  Serial.begin(57600);
  Wire.begin(8);
  Wire.onReceive(receiveEvent);
  Wire.onRequest(requestEvent);
  randomSeed(analogRead(0));
  pinMode(3,OUTPUT);
  pinMode(5,OUTPUT);
  pinMode(6,OUTPUT);
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  //wdt_enable(WDTO_1S);
  setSyncProvider(RTC.get);   // the function to get the time from the RTC
  setSyncInterval(SECS_PER_HOUR);  // Changed to sync every hour.
  now();
}
//End Setup
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Loop
void loop()
{
  wdt_reset();
  //Serial.println(ChannelValue[0],DEC);
  if (cmdnum!=255)
  {
    ProcessCMD(cmdnum,datanum);    
    cmdnum=255;
    datanum=255;
  }
  Seasons();
  CheckCloud();
  //Serial.println("Start");
  //Serial.println(ChannelValue[0],DEC);
  //Serial.println(rhour,DEC);
  //Serial.println(rmin,DEC);
  //Serial.println(shour,DEC);
  //Serial.println(smin,DEC);
  //Serial.println(cloudstart/60,DEC);
  //Serial.println(cloudstart%60,DEC);
  //Serial.println(cloudduration,DEC);
  for (int a=0;a<6;a++)
  {
    analogWrite(PWMports[a],ChannelValue[a]);
    
  }
}
//End Loop
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Standard PWM Functions Receive/Process
void receiveEvent(int howMany) {
  wdt_reset();
  if (howMany==5)
  {
    byte cmd1, cmd2, cmd3, cmd4, cmd5;
    cmd1=Wire.receive();
    cmd2=Wire.receive();
    cmd3=Wire.receive();
    cmd4=Wire.receive();
    cmd5=Wire.receive();
    if (cmd1=='$' && cmd2=='$' && cmd3=='$')
    {
      cmdnum=cmd4;
      datanum=cmd5;
      //Serial.println(cmd4,DEC);
      //Serial.println(cmd5,DEC);
    }
  }
  else
  {
    for (int a=0;a<howMany;a++)
    {
      Wire.receive();
    }
  }  
}

void ProcessCMD(byte cmd, byte data)
{
  wdt_reset();
  // Individual Channel
  if (cmd>=0 && cmd<=5)
  {
    ChannelValue[cmd]=data;
    analogWrite(PWMports[cmd],data);
  }
}
//End Standard Functions
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Send Data Function
void requestEvent() {
  SeasonsVar[0]=ChannelValue[0];
  SeasonsVar[1]=rhour;
  SeasonsVar[2]=rmin;
  SeasonsVar[3]=shour;
  SeasonsVar[4]=smin;
  SeasonsVar[5]=cloudstart/60;
  SeasonsVar[6]=cloudstart%60;
  SeasonsVar[7]=cloudduration;
  SeasonsVar[8]=lightningchance;
  SeasonsVar[9]=ChannelValue[1];
  SeasonsVar[10]=ChannelValue[2];
  SeasonsVar[11]=ChannelValue[3];
  Wire.send(SeasonsVar,12);
}
//End Send Data
//*********************************************************************************************************************************

//*********************************************************************************************************************************
// Random Cloud/Thunderstorm effects function
void CheckCloud()
{

  // ------------------------------------------------------------
  // Change the values below to customize your cloud/storm effect

  // Frequency in days based on the day of the month - number 2 means every 2 days, for example (day 2,4,6 etc)
  // For testing purposes, you can use 1 and cause the cloud to occur everyday
#define Clouds_Every_X_Days 3

  // Percentage chance of a cloud happening today
  // For testing purposes, you can use 100 and cause the cloud to have 100% chance of happening
#define Cloud_Chance_per_Day 30

  // Minimum number of minutes for cloud duration.  Don't use max duration of less than 6
#define Min_Cloud_Duration 6

  // Maximum number of minutes for the cloud duration. Don't use max duration of more than 255
#define Max_Cloud_Duration 30

  // Minimum number of clouds that can happen per day
#define Min_Clouds_per_Day 2

  // Maximum number of clouds that can happen per day
#define Max_Clouds_per_Day 5

  // Only start the cloud effect after this setting
  // In this example, start could after 11:30am
#define Start_Cloud_After NumMins(11,00)

  // Always end the cloud effect before this setting
  // In this example, end could before 8:00pm
#define End_Cloud_Before NumMins(15,00)

  // Percentage chance of a lightning happen for every cloud
  // For testing purposes, you can use 100 and cause the lightning to have 100% chance of happening
#define Lightning_Change_per_Cloud 40

  // Channels used by the actinic LEDs on the PWM Expansion module
  // These channels will not be dimmed when the cloud effect is triggered
  // Number is a binary form. B001100 means channel 2 and 3 are used for actinics
#define Actinic_Channels B011101

  // Channels used by the daylight LEDs on the PWM Expansion module
  // These channels will be used for the spike when lightning effect is triggered
  // Number is a binary form. B000011 means channel 0 and 1 are used for daylights
#define Daylight_Channels B000010

  // Note: Make sure to choose correct values that will work within your PWMSLope settings.
  // For example, in our case, we could have a max of 5 clouds per day and they could last for 50 minutes.
  // Which could mean 250 minutes of clouds. We need to make sure the PWMSlope can accomodate 250 minutes of effects or unforseen 
  // results could happen.
  // Also, make sure that you can fit double those minutes between Start_Cloud_After and End_Cloud_Before.
  // In our example, we have 510 minutes between Start_Cloud_After and End_Cloud_Before, so double the 250 minutes (or 500 minutes) can
  //fit in that 510 minutes window.
  // It's a tight fit, but it did.

  //#define printdebug // Uncomment this for debug print on Serial Monitor window
#define forcecloudcalculation // Uncomment this to force the cloud calculation to happen in the boot process. 


  // Change the values above to customize your cloud/storm effect
  // ------------------------------------------------------------
  // Do not change anything below here

  static byte cloudchance=255;
  static byte numclouds=0;
  static byte cloudindex=0;
  static byte lightningstatus=0;
  static int LastNumMins=0;
  // Every day at midnight, we check for chance of cloud happening today
  if (hour()==0 && minute()==0 && second()==0) cloudchance=255;

#ifdef forcecloudcalculation
  if (cloudchance==255)
#else
    if (hour()==0 && minute()==0 && second()==1 && cloudchance==255) 
#endif
    {
      //Pick a random number between 0 and 99
      cloudchance=random(100); 
      // if picked number is greater than Cloud_Chance_per_Day, we will not have clouds today
      if (cloudchance>Cloud_Chance_per_Day) cloudchance=0;
      // Check if today is day for clouds. 
      if ((day()%Clouds_Every_X_Days)!=0) cloudchance=0; 
      // If we have cloud today
      if (cloudchance)
      {
        // pick a random number for number of clouds between Min_Clouds_per_Day and Max_Clouds_per_Day
        numclouds=random(Min_Clouds_per_Day,Max_Clouds_per_Day);
        // pick the time that the first cloud will start
        // the range is calculated between Start_Cloud_After and the even distribuition of clouds on this day. 
        cloudstart=random(Start_Cloud_After,Start_Cloud_After+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));

        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  // Now that we have all the parameters for the cloud, let's create the effect

  if (cloudchance)
  {
    //is it time for cloud yet?
    if (NumMins(hour(),minute())>=cloudstart && NumMins(hour(),minute())<(cloudstart+cloudduration))
    {
      // let's go through all channels to pick which ones will be dimmed
      for (int a=0;a<6;a++)
      {
        if (bitRead(Actinic_Channels,a)==0)
        {
          // this will slope down the channel from the current PWM to 0 within 3minutes.
          // then it will stay at 0 for the duration of the cycle
          // and finally slope up from 0 to PWM value within 3 minutes
          // it is basically an inversed slope
          ChannelValue[a]=ReversePWMSlope(cloudstart,cloudstart+cloudduration,ChannelValue[a],0,180);
        }
      }
      if (lightningchance && (NumMins(hour(),minute())==(cloudstart+(cloudduration/2))) && second()<5) 
      {
        for (int b=0;b<6;b++)
        {
          if (bitRead(Daylight_Channels,b)==1)
          {
            if (random(100)<20) lightningstatus=1; 
            else lightningstatus=0;
            if (lightningstatus) ChannelValue[b]=100; 
            else ChannelValue[b]=0;
            //delay(10);
          }
          else
          {
            ChannelValue[b]=20;
          }
        }
      }
    }
    
    if (NumMins(hour(),minute())>(cloudstart+cloudduration))
    {
      cloudindex++;
      if (cloudindex < numclouds)
      {
        cloudstart=random(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2),(Start_Cloud_After+(((End_Cloud_Before-Start_Cloud_After)/(numclouds*2))*cloudindex*2))+((End_Cloud_Before-Start_Cloud_After)/(numclouds*2)));
        // pick a random number for the cloud duration of first cloud.
        cloudduration=random(Min_Cloud_Duration,Max_Cloud_Duration);
        //Pick a random number between 0 and 99
        lightningchance=random(100);
        // if picked number is greater than Lightning_Change_per_Cloud, we will not have lightning today
        if (lightningchance>Lightning_Change_per_Cloud) lightningchance=0;
      }
    }
  }
}
//End Cloud Function
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Reverse PWM slope from cloud function
byte ReversePWMSlope(long cstart,long cend,byte PWMStart,byte PWMEnd, byte clength)
{
  long n=elapsedSecsToday(now());
  cstart*=60;
  cend*=60;
  if (n<cstart) return PWMStart;
  if (n>=cstart && n<=(cstart+clength)) return map(n,cstart,cstart+clength,PWMStart,PWMEnd);
  if (n>(cstart+clength) && n<(cend-clength)) return PWMEnd;
  if (n>=(cend-clength) && n<=cend) return map(n,cend-clength,cend,PWMEnd,PWMStart);
  if (n>cend) return PWMStart;
}
//End Reverse PWM function
//*********************************************************************************************************************************
//*********************************************************************************************************************************
//Calculate Sunrise/set by date & predefined season & rise/set times
void Seasons()
{
  //Set the hour you want the calculations of rise an set to be based on
  int UserRiseHour = 6;
  int UserSetHour =  19;
  
  #define forceseasoncalculation
  static byte ssn , ssnp = 0 , ssnpt ;
  long stime, wstime, vstime, wrtime, rtime, vrtime;
  int wrhour,wrmin,wrsec,wshour,wsmin,wssec,rsec,ssec,vrhour,vrmin,vrsec,vshour,vsmin,vssec;
  int iDiffrise = 0;
  int iDiffset = 0;
  int risediffperday = 0;
  int setdiffperday = 0;
  int totalrise = 0;
  int totalset = 0;
  byte s=0;
  int DaysPerYear;        

  //rise and set times set by hour and minute. there are 4 seasons however there are 8 highs & lows in rise and set throughout the year
  //first spot is second half of winter starting jan 1st - DO NOT CHANGE
    int risehour[8]= {UserRiseHour+1,UserRiseHour+1,UserRiseHour+1,UserRiseHour,UserRiseHour,UserRiseHour-1,UserRiseHour,UserRiseHour};/*{
    7,7,7,6,6,5,6,6      };*/
  int riseminute[8]={
    00,30,00,30,00,30,00,30      };
  int sethour[8] = {UserSetHour-2,UserSetHour-1,UserSetHour-1,UserSetHour,UserSetHour,UserSetHour,UserSetHour,UserSetHour-1};/*{
    17,18,18,19,19,19,19,18      };*/
  int setminute[8] = {
    30,00,30,00,00,30,00,00      };

  //
  if (hour()==0 && minute()==0 && second()==0) ssnp=0;
#ifdef forceseasoncalculation
  if (ssnp==0)
#else
    if (hour()==0 && minute()==0 && second()==1 && ssnp==0)
#endif
    {
      //leapyear or not to define DaysPerYear - DO NOT CHANGE
      if (year()%4 == 0 && !(year()%100 == 0 && year()%400 != 0)) {
        DaysPerYear=366;
      }
      else {
        DaysPerYear = 365;
      }
      //Call Day Number Calc to determin day ie december 31st on a non leap year is day 365 - DO NOT CHANGE
      DayNumber(year(),month(),day());
      //define days between beginning, middle and end of seasons high peaks -  DO NOT CHANGE
      int seasons[9] ={
        0,45,96,135,187,238,283,328,DaysPerYear                  };
      //define season and array pulling variable - DO NOT CHANGE
      for (s=0; seasons[s] < Ndays; s++) ssn = s+1, ssnpt = s+1, ssnp = s;
      //set loop on array time pulling variable to go back to beginning instead of increasing array size - DO NOT CHANGE
      if (ssn >= 7) ssn = 0;

      //differece in seconds between two rise/set array times pulled - DO NOT CHANGE
      long rise1 = calcSec(risehour[ssn],riseminute[ssn]);
      long rise2 = calcSec(risehour[ssnp],riseminute[ssnp]);
      iDiffrise = calcTime(rise1, rise2);
      long set1 = calcSec(sethour[ssn],setminute[ssn]);
      long set2 = calcSec(sethour[ssnp],setminute[ssnp]);
      iDiffset = calcTime(set1,set2);             

      //calculate new sunrise/set difference from array value & last group of code - DO NOT CHANGE
      risediffperday = iDiffrise/(seasons[ssnpt]-seasons[ssnp]);
      totalrise = risediffperday*(Ndays - seasons[ssnp]);
      setdiffperday = iDiffset/(seasons[ssnpt]-seasons[ssnp]);
      totalset = setdiffperday*(Ndays - seasons[ssnp]);

      //creating time in seconds for main sun rise/set number - DO NOT CHANGE
      rtime=calcSec(risehour[ssnp],riseminute[ssnp]);
      if (ssnp == 0 || ssnp == 2 || ssnp == 4 || ssnp == 6){ 
        rtime -= totalrise;
      }
      else {
        rtime += totalrise;
      }
      stime=calcSec(sethour[ssnp],setminute[ssnp]);
      if (ssnp == 1 || ssnp == 3 || ssnp == 5 || ssnp == 7){ 
        stime -= totalset;
      }
      else {
        stime += totalset;
      }
      
      //These are the off set times, standard rtime and stime are for Royal Blues & Blues
      // DO NOT CHANGE the operators in these equations ie +- 
      // The number is in seconds (1200) change this number to change the offset for each color
      wrtime = rtime + 1200;//w r/stime is for Whites - shorter time span then blues
      wstime = stime - 1200;
      vrtime = rtime - 1200;//v r/stime is for Violets - Longer time then blues
      vstime = stime + 1200;
      
      //turning seconds back to Hours:Minutes:Seconds
      //Blues
      rhour=rtime/3600; 
      rtime=rtime%3600;
      rmin=rtime/60; 
      rtime=rtime%60; 
      rsec=rtime;
      if(rsec > 30) rmin++; 
      shour=stime/3600; 
      stime=stime%3600;
      smin=stime/60; 
      stime=stime%60; 
      ssec=stime;
      if(ssec > 30) smin++;
      //White
      wrhour = wrtime/3600;
      wrtime=wrtime%3600;
      wrmin=wrtime/60;
      wrtime=wrtime%60;
      wrsec=wrtime;
      if(wrsec>30) wrmin++;
      wshour = wstime/3600;
      wstime=wstime%3600;
      wsmin=wstime/60;
      wstime=wstime%60;
      wssec=wstime;
      if(wssec>30) wsmin++;
      //Violet
      vrhour = vrtime/3600;
      vrtime=vrtime%3600;
      vrmin=vrtime/60;
      vrtime=vrtime%60;
      vrsec=vrtime;
      if(vrsec>30) vrmin++;
      vshour = vstime/3600;
      vstime=vstime%3600;
      vsmin=vstime/60;
      vstime=vstime%60;
      vssec=vstime;
      if(vssec>30) vsmin++;



      //time for each active led channel to pull for sunrise - DO NOT CHANGE
      int Sunrise[2]={
        rhour,rmin                  };
      int Sunset[2]={
        shour,smin                  };
      int whSunrise[2]={
        wrhour,wrmin                  };
      int whSunset [2]={
        wshour,wsmin                  };
      int vSunrise[2]={
        vrhour,vrmin                  };
      int vSunset [2]={
        vshour,vsmin                  };
        
      //This is the PWM Slope for each channel, each channel pulls an array value from above(hour,minute) to use, how you set them is up to you. 
      //Just always use a Rise hour in a Rise spot, always a set hour in a set spot ect ect
      ChannelValue[LEDPWM0]=PWMSlope(vSunrise[1],vSunrise[2],vSunset[1],vSunset[2],0,100,1,ChannelValue[LEDPWM0]);
      ChannelValue[LEDPWM1]=PWMSlope(whSunrise[1],whSunrise[2],whSunset[1],whSunset[2],0,100,1,ChannelValue[LEDPWM1]);
      ChannelValue[LEDPWM2]=PWMSlope(Sunrise[1],Sunrise[2],Sunset[1],Sunset[2],0,100,1,ChannelValue[LEDPWM2]);
      ChannelValue[LEDPWM3]=PWMSlope(Sunrise[1],Sunrise[2],whSunset[1],whSunset[2],0,100,1,ChannelValue[LEDPWM3]);

      //ChannelValue[LEDPWM4]=PWMSlope(Sunrise[1],Sunrise[2],whSunset[1],whSunset[2],0,100,180,ChannelValue[LEDPWM3]);
      //ChannelValue[LEDPWM5]=PWMSlope(Sunrise[1],Sunrise[2],whSunset[1],whSunset[2],0,100,180,ChannelValue[LEDPWM3]);
    }
}
//End Seasons Calculation
//*********************************************************************************************************************************

//*********************************************************************************************************************************
//Calculators for Seasons function
long calcSec(long hr, long minu)
{
    long totalseconds;
    totalseconds=(hr*3600)+(minu*60);
    return totalseconds;
}

long calcTime(long seconds1, long seconds2)
{
  long timediff=abs(seconds1-seconds2);
  return timediff;
}

void DayNumber(unsigned int y, unsigned int m, unsigned int d)
{
  int days[]={
    0,31,59,90,120,151,181,212,243,273,304,334      };    // Number of days at the beginning of the month in a not leap year.
  //Start to calculate the number of day
  if (m==1 || m==2){
    Ndays = days[(m-1)]+d;			   //for any type of year, it calculate the number of days for January or february
  }				// Now, try to calculate for the other months
  else if ((y % 4 == 0 && y % 100 != 0) ||  y % 400 == 0){  //those are the conditions to have a leap year
    Ndays = days[(m-1)]+d+1;     // if leap year, calculate in the same way but increasing one day
  }
  else {					  //if not a leap year, calculate in the normal way, such as January or February
    Ndays = days[(m-1)]+d;
  }
} 

//End calculators
//*********************************************************************************************************************************
sebimme
Posts: 26
Joined: Sat Jan 07, 2012 12:56 am

Re: Almost complete feature hog :P

Post by sebimme »

Hello Deckoz2302, I try to compile your RA PDE on my arduino Board But i get This error message:
Attachments
error2.jpg
error2.jpg (155.26 KiB) Viewed 14193 times
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: Almost complete feature hog :P

Post by binder »

Inside your Features file, you must enable the large fonts. You need this line to make it work properly:

Code: Select all

#define FONT_8X8
That will enable the use of DrawLargeText.

Check out this topic for more help: http://forum.reefangel.com/viewtopic.php?f=14&t=109
sebimme
Posts: 26
Joined: Sat Jan 07, 2012 12:56 am

Re: Almost complete feature hog :P

Post by sebimme »

Now I have another error: " class ReefAngelClass' has no member named 'RF' "
I think I'm missing something in the file features, There is a file features 100% complete
binder
Posts: 2871
Joined: Fri Mar 18, 2011 6:20 pm
Location: Illinois
Contact:

Re: Almost complete feature hog :P

Post by binder »

This might be simpler for you.

https://github.com/curtbinder/ReefAngel ... Features.h

That is the "official" fully commented and documented features file. This file gets updated with every generate with RAGen. Most of the features (and descriptions) are inside RAGen, but some of the newer ones are not (RF, Salinity, etc).

Read through that file and if you still have problems, just post more questions. :)
Deckoz2302
Posts: 149
Joined: Tue Nov 01, 2011 11:05 am

Re: Almost complete feature hog :P

Post by Deckoz2302 »

Hey sorry for the late response - in my features file I have

Custom main
Custom menu
Salinityexpansion
Rfexpansion
Pwmexpansion
Font 8x8
Numbers 8x8
Wifi
Date time setup
Ato single

These are defined in my features file - sorry for the confusion
Ill post my features file tonight
Also - updated pde will come tonight when I get home, I've fix a couple errorness loops in the pde preventing things from. displaying properly
sebimme
Posts: 26
Joined: Sat Jan 07, 2012 12:56 am

Re: Almost complete feature hog :P

Post by sebimme »

can you convert all for 0.9 compatibility?
Deckoz2302
Posts: 149
Joined: Tue Nov 01, 2011 11:05 am

Re: Almost complete feature hog :P

Post by Deckoz2302 »

I can and will soon - tank has been on cruise control for a few months lol. Now I need to update libraries and all the other fun stuff that's been going on
User avatar
JNieuwenhuizen
Posts: 96
Joined: Thu Feb 16, 2012 12:39 am
Location: South Africa

Re: Almost complete feature hog :P

Post by JNieuwenhuizen »

Hi Deckoz2302.

I have 5 Led channels , Channel 1 , 2 , 3 , 4 being white and blue, and channel 5 being colours.

I am using your pwm code - how would I set it up to switch on channel 1 , then 2 then 3, then 4&5 with 30 min delay on each? that would give a sunrise effect, and the same with sunset, channel 1, then delay channel 2 with 30 min, then channel 3 with 30 min then channel 4&5 with 30 minutes.
abhi_123
Posts: 217
Joined: Tue Mar 20, 2012 8:34 am

Re: Almost complete feature hog :P

Post by abhi_123 »

Hi,

i am planning to use your pwm code for 2 led channels only.

what modification should i do to make it work for.
Image
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Almost complete feature hog :P

Post by lnevo »

Deckoz, looks like you've got the code I need in here for calculating Sunrise and Sunset... I'm not going to be using dimming at the moment, so just wondering if you have any pointers on what to pull / ignore, etc. from your PDE. I'll be analyzing it later when I have some more time, but this looks like the place to start...
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Almost complete feature hog :P

Post by lnevo »

FYI...I just got bitten by this code since Timer[4] is used for the portal timer...

Code: Select all

    ReefAngel.Timer[4].SetInterval(10); // Timer for 30min
    ReefAngel.Timer[4].Start();
Might want to modify your example so other people don't get bitten too :)
User avatar
Rodasphoto
Posts: 187
Joined: Wed Apr 10, 2013 2:48 pm
Location: Athens, Ga
Contact:

Re: Almost complete feature hog :P

Post by Rodasphoto »

Tagging along so I can do this in the future.
Image
Post Reply