Jump to content

Reef Angel tinkering


AlanM

Recommended Posts

So I've been tinkering with Reef Angel for the past few months.  Pretty cool stuff.  Among other things it gives you total control over your light and pump profiles.  You can run any of the EcoSmart modes or even the Else mode from the Jebaos on Tunzes or Jebaos with this controller or write your own.  I like the Sine mode myself which gradually goes up and down between max and 0.

 

Hardware and software are both open source, so it can be made to do whatever you want.  I've made some pretty large changes to the code after just jumping into it in the past few months, so it's not rocket science. 

 

So far I've:

 

- Converted one of the ATO input pins into a PWM pump controller

- Added some pump profiles for users to get Jebaos and Tunzes to do different things

- Added a light profile to let people increase and decrease intensity like the left and right side of a cosine function, similar to sunrise and sunset.  It lets the morning and evening have kind of a long dim tail.

- Added a threshold feature so that Tunze and Jebao owners can use all of the Vortech profiles (Nutrient Transport, Reef Crest, Tidal Swell, etc.) without hurting their pumps (It just skips over values from 0 to threshold while spitting the difference between the two).

- Increased the resolution of the PWM dimming functions to the max that the chipset can handle, which is 12-bit, 4096 levels of dimming.

- Added support for a custom expansion that Roberto makes upon request which has 16 PWM channels.

- Added network functions that let me query and set all of the new stuff from an Android app, iOS app, web browser, or the ReefAngel portal

- Customized the color display to show everything that I care about.

 

Here's the custom display I set up.  Everything updates in real time showing 4 pump speeds, their current mode, their threshold, 6 dimming channels, temp, pH, date/time, and a graph of the last day.  Still a few quirks to work out, but I am having fun.

 

IMAG1650_1_zpsrukny3rz.jpg

 

Are there any closet Reef Angel users out here in WAMAS?  I know lnevo, howaboutme, and Roscoe's Reefs are now using it.  Anyone else?

Link to comment
Share on other sites

I have it but I am pretty sure I am going to switch to either Apex or RK later this year. I seem to just have bad luck with making things work with the RA, whether it is my lights or ATO or probes, something always does not work. I think it is great for those who really like to tinker and adjust things and write codes, but that isn't me.

Link to comment
Share on other sites

I need to completely redo the water flow approach on my tank, and thinking of using the tunzes I already have plus some jebaos to create a back and forth gyre. If I can use a RA to control 12 of these in total, 6 on each side, then I'll probably start using one. You know in coming to you with questions first, Alan :)

Link to comment
Share on other sites

I need to completely redo the water flow approach on my tank, and thinking of using the tunzes I already have plus some jebaos to create a back and forth gyre. If I can use a RA to control 12 of these in total, 6 on each side, then I'll probably start using one. You know in coming to you with questions first, Alan :)

 

No prob to control them in whatever way you want and set the sync state on any or all of them.  There are two sync "channels", sync and anti-sync, though, so you are limited there unless you want to write a bunch of custom code, but that's totally doable as well. 

 

Also lnevo has been experimenting with RA dimming of his Evergrow-clones.  I know you have a few of those.

 

Reef Angel's multi-channel water sensor is really neat.  It's not an on-off type pressure switch.  It's actually more of a manometer which gives you a number saying where the water level is inside the sensor.  There are interesting things you can do with that kind of a continuous measurement in addition to having an ATO which has adjustable sensitivity.

 

Plus Roberto is adding ZigBee wireless to talk to the various components of the system so that you won't have to have USB cables strung around, which is also cool. 

Link to comment
Share on other sites

Tinkering... Lol :)

 

If you call major contributions from a "newbie" tinkering... :) Very exciting, Alan. Looking forward to seeing what else you can come up with.

 

By the way, did you integrate the Bessel and Airy functions?

Link to comment
Share on other sites

If you call major contributions from a "newbie" tinkering... :) Very exciting, Alan. Looking forward to seeing what else you can come up with.

 

By the way, did you integrate the Bessel and Airy functions?

I forgot about the Bessel function promise, but I think I am going to do a new Sine type profile where everything less than zero is "anti-sync" with the "sync" channel totally off, and the reverse is true when the Sine is positive. Like a SmoothLongPulse type of function. Basically I dont really like any abrupt changes in flow. The standard Sine pattern has opposing pumps both running at the same time, just one ramping up and one down all the time.

Link to comment
Share on other sites

If anyone wants to see what programming for a ReefAngel looks like here is my code:

#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
unsigned long offtime = 0UL;
void DrawCustomMain() 
{
  // want to add dosing status, and 6 PWM channels of lights
  byte pumpmode = InternalMemory.DCPumpMode_read();
  byte threshold = InternalMemory.DCPumpThreshold_read();
  byte syncmode = ReefAngel.CustomVar[0];

  if      (pumpmode ==  0) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5," Constant  ");
  else if (pumpmode ==  1) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"  Lagoon   ");
  else if (pumpmode ==  2) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"Reef Crest ");
  else if (pumpmode ==  3) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"Short Pulse");
  else if (pumpmode ==  4) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"Long Pulse ");
  else if (pumpmode ==  5) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"Nutr Trans ");
  else if (pumpmode ==  6) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"Tidal Swell");
  else if (pumpmode == 12) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"   Else    ");
  else if (pumpmode == 13) ReefAngel.LCD.DrawLargeText(COLOR_MAGENTA,DefaultBGColor,15,5,"   Sine    ");
  
  if      (syncmode == 0) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,15,15,"Side2Side ");
  else if (syncmode == 1) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,15,15," AllSync  ");
  else if (syncmode == 2) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,15,15,"Front2Back");
  else if (syncmode == 3) ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,15,15,"   Gyre   ");
  else                    ReefAngel.LCD.DrawLargeText(COLOR_BLACK, DefaultBGColor,15,15," Unknown  ");

  //ReefAngel.LCD.DrawLargeText(COLOR_BLACK,DefaultBGColor,15,45,"Threshold:");
  char text[3];
  ConvertNumToString(text,threshold,1);
  ReefAngel.LCD.Clear(DefaultBGColor, 100,8, 126, 24);
  ReefAngel.LCD.DrawHugeText(COLOR_RED, DefaultBGColor, 100,8,text,Num12x16);
  DrawPumpBlock(15,25);
  DrawLEDBlock(9,45);

  DrawCustomMonitor(15,97);  
  byte TempRelay = ReefAngel.Relay.RelayData;
  TempRelay &= ReefAngel.Relay.RelayMaskOff;
  TempRelay |= ReefAngel.Relay.RelayMaskOn;
  ReefAngel.LCD.DrawOutletBox(12, 107, TempRelay);
  ReefAngel.LCD.DrawDate(6,122);
}

void DrawLEDBlock(byte x, byte y) {
  ReefAngel.LCD.Clear(COLOR_PURPLE, x, y, x+19, y+9);
  ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_PURPLE,x+1,y+1,ReefAngel.PWM.Get16ChannelValue(2));
  ReefAngel.LCD.Clear(COLOR_NAVY, x+19, y, x+38, y+9);
  ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_NAVY,x+20,y+1,ReefAngel.PWM.Get16ChannelValue(3));
  ReefAngel.LCD.Clear(COLOR_ANTIQUEWHITE, x+38, y, x+57, y+9);
  ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_ANTIQUEWHITE,x+39,y+1,ReefAngel.PWM.Get16ChannelValue(4));
  ReefAngel.LCD.Clear(COLOR_BLUE, x+57, y, x+76, y+9);
  ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_BLUE,x+58,y+1,ReefAngel.PWM.Get16ChannelValue(5));
  ReefAngel.LCD.Clear(COLOR_CRIMSON, x+76, y, x+95, y+9);
  ReefAngel.LCD.DrawText(COLOR_WHITE,COLOR_CRIMSON,x+77,y+1,ReefAngel.PWM.Get16ChannelValue(6));
  ReefAngel.LCD.Clear(COLOR_DARKTURQUOISE, x+95, y, x+117, y+9);
  ReefAngel.LCD.DrawText(COLOR_BLACK,COLOR_DARKTURQUOISE,x+96,y+1,ReefAngel.PWM.Get16ChannelValue(7));
}

void DrawPumpBlock(byte x, byte y) {
  //left pumps
  ReefAngel.LCD.DrawText(DPColor,DefaultBGColor,x,y,"BL:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetActinicValue(), DPColor, x+18, y,1);
  ReefAngel.LCD.DrawText(APColor,DefaultBGColor,x,y+10,"FL:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.Get16ChannelValue(0), APColor, x+18, y+10,1);
  // right pumps
  ReefAngel.LCD.DrawText(DPColor,DefaultBGColor,x+60,y,"BR:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.GetDaylightValue(), DPColor, x+78, y,1);
  ReefAngel.LCD.DrawText(APColor,DefaultBGColor,x+60,y+10,"FR:");
  ReefAngel.LCD.DrawSingleMonitor(ReefAngel.PWM.Get16ChannelValue(1), APColor, x+78, y+10,1);
}

void DrawCustomMonitor(byte x, byte y) {

    // left column
    ReefAngel.LCD.DrawText(T1TempColor,DefaultBGColor,x,y,"T1:");
    ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.Temp[T1_PROBE], T1TempColor, x+18, y,10);
    // right column 
    ReefAngel.LCD.DrawText(PHColor,DefaultBGColor,x+60,y,"PH:");
    ReefAngel.LCD.DrawSingleMonitor(ReefAngel.Params.PH, PHColor, x+78, y,100);
}

void DrawCustomGraph()
{
  ReefAngel.LCD.DrawGraph(5,55);
}
////// 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;
  // Add support for 16 channel PWM board
  ReefAngel.Add16ChPWM();
  // 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]="Unused"; 
  ReefAngel.CustomLabels[3]="Unused"; 
  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 = Unused
 4 = Unused
 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 ); 
  ReefAngel.Relay.DelayedOn( Port2 );
  //ReefAngel.StandardLights( Port3,7,0,19,0 ); // unused
  //ReefAngel.StandardLights( Port4,15,0,10,0 ); // unused
  if (ReefAngel.Params.PH < 850) 
  {
    ReefAngel.DosingPumpRepeat1( Port7 ); // alk doser
  }
  else
  {
    ReefAngel.Relay.Off(Port7);  // turn it off if the pH is too high
  }
  ReefAngel.DosingPumpRepeat2( Port8 ); // cal doser
  ReefAngel.DCPump.UseMemory = true;
  //ReefAngel.DCPump.DaylightChannel = Sync; // Tunze right
  //ReefAngel.DCPump.ActinicChannel = AntiSync; // Tunze left
  ////// Place your custom code below here
  //ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = AntiSync; // Jebao Left
  //ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = Sync; // Jebao Right

  setPowerheadModes(ReefAngel.CustomVar[0]);
  // lights
  if ((hour() >= 7)&& (hour() < 9)) { // dim slope in the early morning for 2 hours up to 10 percent
    ReefAngel.PWM.Set16Channel(2,PWMSlopeHighRes(7,0,21,0, 0,10,120,ReefAngel.PWM.SIXTEENChExpansionChannel[2])); // Violet channel, startpwm, endpwm, slope length
    ReefAngel.PWM.Set16Channel(3,PWMSlopeHighRes(7,0,21,0, 0,10,120,ReefAngel.PWM.SIXTEENChExpansionChannel[3])); // Royal Blue channel, startpwm, endpwm, slope length
    ReefAngel.PWM.Set16Channel(4,PWMSlopeHighRes(7,0,21,0, 0,10,120,ReefAngel.PWM.SIXTEENChExpansionChannel[4])); // White channel, startpwm, endpwm, slope length
  } else if ((hour() >= 9) && (hour() < 14)) { // regular slope up
    ReefAngel.PWM.Set16Channel(2,PWMSmoothRampHighRes(9,0,21,0,10,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[2])); // Violet channel, startpwm, endpwm, slope length
    ReefAngel.PWM.Set16Channel(3,PWMSmoothRampHighRes(9,0,21,0,10,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[3])); // Royal Blue channel, startpwm, endpwm, slope length
    ReefAngel.PWM.Set16Channel(4,PWMSmoothRampHighRes(9,0,21,0,10,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[4])); // White channel, startpwm, endpwm, slope length
  } else if (hour() >= 14) { // regular slope down
    ReefAngel.PWM.Set16Channel(2,PWMSmoothRampHighRes(9,0,21,0,0,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[2])); // Violet channel, startpwm, endpwm, slope length
    ReefAngel.PWM.Set16Channel(3,PWMSmoothRampHighRes(9,0,21,0,0,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[3])); // Royal Blue channel, startpwm, endpwm, slope length
    ReefAngel.PWM.Set16Channel(4,PWMSmoothRampHighRes(9,0,21,0,0,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[4])); // White channel, startpwm, endpwm, slope length
  }
  // fans and OCW colors up and down
  ReefAngel.PWM.Set16Channel(5,PWMSmoothRampHighRes(9,0,21,0, 0, 60,240,ReefAngel.PWM.SIXTEENChExpansionChannel[5])); // Cool Blue channel, startpwm, endpwm, slope length
  ReefAngel.PWM.Set16Channel(6,PWMSmoothRampHighRes(9,0,21,0, 0, 40,240,ReefAngel.PWM.SIXTEENChExpansionChannel[6])); // Deep Red channel, startpwm, endpwm, slope length
  ReefAngel.PWM.Set16Channel(7,PWMSmoothRampHighRes(9,0,21,0, 0, 40,240,ReefAngel.PWM.SIXTEENChExpansionChannel[7])); // Cyan channel, startpwm, endpwm, slope length
  ReefAngel.PWM.Set16Channel(8,PWMSmoothRampHighRes(9,0,21,0,10,100,240,ReefAngel.PWM.SIXTEENChExpansionChannel[8])); // Fan channel, startpwm, endpwm, slope length

  // Turn off Skimmer if Locker is full
  if(ReefAngel.HighATO.IsActive())
  {
    ReefAngel.Relay.Off(Port2);
  }
  else
  {
    ReefAngel.Relay.On(Port2);
  }
  
  // Turn off things if the power goes out
  if(ReefAngel.LowATO.IsActive())
  { // then power is on
    if (offtime > 0) { // then power was off 
      ReefAngel.Relay.On(Port2);
      ReefAngel.Relay.On(Port5);
    }
    offtime = 0UL;
  } 
  else
  { // then power is off
    // turn off lights, skimmer, return pump after two minutes, to conserve power for powerheads
    if (offtime == 0) offtime = now();
    if ((now() - offtime) > 120) { // it has been off for two minutes
      ReefAngel.Relay.Off(Port2); // turn off skimmer
      ReefAngel.Relay.Off(Port5); // turn off return pump
    }
  }

  ////// Place your custom code above here

  // This should always be the last line
  ReefAngel.Portal( "AlanM" );
  ReefAngel.ShowInterface();
}

void setPowerheadModes(byte syncModeIn)
{
  switch (syncModeIn) {
  case 0:  // side to side anti-sync
    ReefAngel.DCPump.ActinicChannel = AntiSync; // Tunze Left
    ReefAngel.DCPump.DaylightChannel = Sync;    // Tunze Right
    ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = AntiSync; // Jebao Left
    ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = Sync;     // Jebao Right
    break;
  case 1: // all on sync
    ReefAngel.DCPump.ActinicChannel = Sync;   // Tunze Left
    ReefAngel.DCPump.DaylightChannel = Sync;  // Tunze Right
    ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = Sync; // Jebao Left
    ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = Sync; // Jebao Right
    break;
  case 2: // front/back sync
    ReefAngel.DCPump.ActinicChannel = Sync;   // Tunze Left
    ReefAngel.DCPump.DaylightChannel = Sync;  // Tunze Right
    ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = AntiSync; // Jebao Left
    ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = AntiSync; // Jebao Right
    break;
  default: // gyre flow, diagonal sync
    ReefAngel.DCPump.ActinicChannel = AntiSync; // Tunze Left
    ReefAngel.DCPump.DaylightChannel = Sync;    // Tunze Right
    ReefAngel.DCPump.SIXTEENChExpansionChannel[0] = Sync;     // Jebao Left
    ReefAngel.DCPump.SIXTEENChExpansionChannel[1] = AntiSync; // Jebao Right
    break;
  }
}

And here's what the display looks like:

 

IMAG1656_1_zpshf0aijmg.jpg

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...