AlanM August 9, 2014 August 9, 2014 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. 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?
Squishie89 August 9, 2014 August 9, 2014 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.
ctenophore August 9, 2014 August 9, 2014 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
AlanM August 9, 2014 Author August 9, 2014 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.
howaboutme August 10, 2014 August 10, 2014 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?
AlanM August 10, 2014 Author August 10, 2014 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.
AlanM August 10, 2014 Author August 10, 2014 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:
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now