Set16ChannelRaw Unexpected behavior

Related to the development libraries, released by Curt Binder
User avatar
Posts: 138
Joined: Thu May 23, 2013 2:52 pm
PostPosted: Sun Aug 23, 2015 9:39 pm
I have a 16 channel board attached, and code intended to separately ramp the different color channels.

Code: Select all
   
ReefAngel.PWM.Set16ChannelRaw(0,PWMSmoothRampHighRes(7,30,22,00,0,65,45,0));
ReefAngel.PWM.Set16ChannelRaw(1,PWMSmoothRampHighRes(7,25,22,05,0,65,45,0)); // 05 minute longer
ReefAngel.PWM.Set16ChannelRaw(2,PWMSmoothRampHighRes(7,30,22,00,0,80,45,0));
ReefAngel.PWM.Set16ChannelRaw(3,PWMSmoothRampHighRes(7,30,22,00,0,40,45,0));
ReefAngel.PWM.Set16ChannelRaw(4,PWMSmoothRampHighRes(7,30,22,00,0,20,45,0));
ReefAngel.PWM.Set16ChannelRaw(5,PWMSmoothRampHighRes(7,15,22,15,0,40,45,0)); // 15 minute longer
ReefAngel.PWM.Set16ChannelRaw(6,PWMSmoothRampHighRes(7,20,22,10,0,30,45,0)); // 10 minute longer
ReefAngel.PWM.Set16ChannelRaw(7,PWMSmoothRampHighRes(7,30,22,00,0,10,45,0));


When I had this code running with no difference in the start and stop times among the channels, everything behaved as expected--all the channels ramped smoothly (more or less, with occasional flickers) down to 0 together.

Trying to offset the times for some of the colors, however, resulted in an odd behavior -- in the above code, at 22:00, most channels drop to 0. At that time, the remaining channels (1, 5 and 6) jump WAY up in output -- to at least 50% or 60%, and stay there for the remaining five to fifteen minutes, rather than sloping down as expected.

From a review of the libraries (1.1.1, unmodified except for ORP calibration solution value), I can't see how there's any logic behind Set16ChannelRaw or PWMSmoothRampHighRes that would explain this behavior.

The above code is a simplified and cropped version of my full code, here below:

Code: Select all
#include <ReefAngel_Features.h>
#include <Globals.h>
#include <RA_Wifi.h>
#include <Wire.h>
#include <OneWire.h>
#include <Time.h>
#include <DS1307RTC.h>
#include <InternalEEPROM.h>
#include <RA_NokiaLCD.h>
#include <RA_ATO.h>
#include <RA_Joystick.h>
#include <LED.h>
#include <RA_TempSensor.h>
#include <Relay.h>
#include <RA_PWM.h>
#include <Timer.h>
#include <Memory.h>
#include <InternalEEPROM.h>
#include <RA_Colors.h>
#include <RA_CustomColors.h>
#include <Salinity.h>
#include <RF.h>
#include <IO.h>
#include <ORP.h>
#include <AI.h>
#include <PH.h>
#include <WaterLevel.h>
#include <Humidity.h>
#include <DCPump.h>
#include <PAR.h>
#include <ReefAngel.h>
#include <Moon.h>

////// Place global variable code below here
   
#define Heater_300W           Port1
#define Heater_100W           Port2
#define Skimmer               Port3
#define Neck_Wipe             Port4
#define Air_Pump              Port5
#define Filter_Pump           Port6
#define Refuge_Light          Port7
#define Ozone_Gen             Port8

#define FlowWolf              Box1_Port1
#define Closed_Loop           Box1_Port2
#define Kalk_Stirrer          Box1_Port3
#define ATO_Pump              Box1_Port4
#define Return_Pump_L         Box1_Port5
#define Return_Pump_R         Box1_Port6
#define Heater_200W           Box1_Port7
#define Refugium_Pump         Box1_Port8

#define Neutral_White_Center  0
#define Warm_White_Center     1
#define Royal_Blue_Center     2
#define Cool_Blue_Center      3
#define Cyan_Center           4
#define Red_Center            5
#define Indigo_Center         6
#define Violet_Center         7

int MoonBlueLevel=0;
int SunBlueLevel=0;

////// Place global variable code above here

void setup()
{
    ReefAngel.Init();
    ReefAngel.AddStandardMenu();
    ReefAngel.Use2014Screen();
    ReefAngel.AddSalinityExpansion();               
    ReefAngel.AddORPExpansion();                     
    ReefAngel.AddWaterLevelExpansion();
    ReefAngel.Add16ChPWM();                         

    ReefAngel.FeedingModePorts = 0;
    ReefAngel.FeedingModePortsE[0] = Port5Bit | Port6Bit;

    ReefAngel.WaterChangePorts = Port3Bit;                 // Skimmer
    ReefAngel.WaterChangePortsE[0] = Port4Bit | Port8Bit;  // ATO Pump and Refugium Circulation

    ReefAngel.LightsOnPorts = 0;
    ReefAngel.LightsOnPortsE[0] = 0;

    ReefAngel.OverheatShutoffPorts = Port1Bit | Port2Bit;   
    ReefAngel.OverheatShutoffPortsE[0] = Port7Bit;

    ReefAngel.TempProbe = T1_PROBE;
    ReefAngel.OverheatProbe = T1_PROBE;

    ReefAngel.DCPump.FeedingSpeed = 0;
    ReefAngel.DCPump.WaterChangeSpeed = 0;

    ReefAngel.Relay.On( Air_Pump );
    ReefAngel.Relay.On( Filter_Pump );
    ReefAngel.Relay.On( FlowWolf );
    ReefAngel.Relay.On( Closed_Loop );
    ReefAngel.Relay.On( Return_Pump_L );
    ReefAngel.Relay.On( Return_Pump_R );
    ReefAngel.Relay.On( Refugium_Pump );

}

void loop()
{
 
    if ( ReefAngel.Params.Temp[T1_PROBE] < 775 ) ReefAngel.Relay.On( Heater_300W );
    if ( ReefAngel.Params.Temp[T1_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_300W );
    if ( ReefAngel.Params.Temp[T2_PROBE] < 775 ) ReefAngel.Relay.On( Heater_100W );
    if ( ReefAngel.Params.Temp[T2_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_100W );
    if ( ReefAngel.Params.Temp[T3_PROBE] < 775 ) ReefAngel.Relay.On( Heater_200W );
    if ( ReefAngel.Params.Temp[T3_PROBE] > 780 ) ReefAngel.Relay.Off( Heater_200W );
 
    ReefAngel.DCPump.UseMemory = false;
    ReefAngel.DCPump.Threshold = 80;   
    ReefAngel.DCPump.SetMode( Sine, 100, 240 );
    ReefAngel.DCPump.DaylightChannel = Sync;
    ReefAngel.DCPump.ActinicChannel = AntiSync;
   
    moon_init(44,-116);   // approximate lat,lon for Boise, ID
    SunBlueLevel = PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 80, 45, 0 );
    MoonBlueLevel = PWMSmoothRampHighestRes( Moon.riseH, Moon.riseM, Moon.setH, Moon.setM, 0, (int)(MoonPhase()/19), 45, 0);


    ReefAngel.PWM.Set16ChannelRaw( Neutral_White_Center, PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 65, 45, 0 ) );
    ReefAngel.PWM.Set16ChannelRaw( Warm_White_Center,    PWMSmoothRampHighRes( 7, 25, 22, 05, 0, 65, 45, 0 ) ); // 05 minute longer
    ReefAngel.PWM.Set16ChannelRaw( Royal_Blue_Center,    max (SunBlueLevel, MoonBlueLevel) );
    ReefAngel.PWM.Set16ChannelRaw( Cool_Blue_Center,     PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 40, 45, 0 ) );
    ReefAngel.PWM.Set16ChannelRaw( Cyan_Center,          PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 20, 45, 0 ) );
    ReefAngel.PWM.Set16ChannelRaw( Red_Center,           PWMSmoothRampHighRes( 7, 15, 22, 15, 0, 40, 45, 0 ) ); // 15 minute longer
    ReefAngel.PWM.Set16ChannelRaw( Indigo_Center,        PWMSmoothRampHighRes( 7, 20, 22, 10, 0, 30, 45, 0 ) ); // 10 minute longer
    ReefAngel.PWM.Set16ChannelRaw( Violet_Center,        PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 10, 45, 0 ) );

    ReefAngel.Relay.Set( Refuge_Light, !((hour() >= 10) && (hour() < 19)));


    pinMode(lowATOPin,OUTPUT);
    if ( (hour() >= 8) && (hour() < 22) )
    {
      if ( now()%7200<10 ) analogWrite(lowATOPin,255);
      else analogWrite(lowATOPin,96);
    }
    else analogWrite(lowATOPin,96);

    ReefAngel.Relay.DelayedOn( Skimmer, 1 );
    if ( ReefAngel.WaterLevel.GetLevel() > 55 ) ReefAngel.Relay.Off( Skimmer );
    if ( ReefAngel.DisplayedMenu==FEEDING_MODE ) ReefAngel.Relay.Off( Skimmer );
    if ( !ReefAngel.HighATO.IsActive() ) ReefAngel.Relay.Override( Skimmer, 0 );

    ReefAngel.DosingPumpRepeat( Neck_Wipe, 0, 240, 30 );

    ReefAngel.DosingPumpRepeat( Kalk_Stirrer, 0, 240, 30 );

    if ( (hour() >= 8) && (hour() < 22) )
    {
      ReefAngel.WaterLevelATO( ATO_Pump, 1200, 38, 40 ); // On at 38% and Off at 40% with 1200 second (20 minute) timeout.
    }
    if ( (hour() >= 7) && (hour() < 8) )
    {
      ReefAngel.WaterLevelATO( ATO_Pump, 3600, 38, 40 ); // On at 38% and Off at 40% with 3600 second (60 minute) timeout.
    }

    if ( ReefAngel.Params.ORP>450 || ReefAngel.Params.ORP==0 ) ReefAngel.Relay.Off( Ozone_Gen );
    if ( ReefAngel.Params.ORP<400 && ReefAngel.Params.ORP>0 ) ReefAngel.Relay.On( Ozone_Gen );

    ReefAngel.Portal( "joshlawless" );
    ReefAngel.DDNS( "reef" );

    ReefAngel.ShowInterface();
}

int PWMSmoothRampHighestRes(byte startHour, byte startMinute, byte endHour, byte endMinute, int startPWMint, int endPWMint, byte slopeLength, int oldValue)
{
  LightsOverride=true;
  int current_hour = hour();
  long start = NumMins(startHour, startMinute)*60L;
  long end = NumMins(endHour, endMinute)*60L;
  long slopeLengthSecs = slopeLength*60L;

  if (start > end) // Start is greater than End so it is over midnight
  {
    if (current_hour < endHour) start -= 1440L*60L; // past midnight
    if (current_hour >= startHour) end += 1440L*60L; // before midnight
  }
  long current = NumMins(current_hour, minute())*60L + second();
  if (slopeLengthSecs > ((end-start)/2) ) slopeLengthSecs = (end-start)/2; // don't allow a slope length greater than half the total period
  if (current <= start || current >= end)
    return oldValue; // it's before the start or after the end, return the default
  else
  { // do the slope calculation
    int pwmDelta = endPWMint - startPWMint;
    float smoothPhase;
    if ((current > (start + slopeLengthSecs)) && (current < (end - slopeLengthSecs)))
      return endPWMint; // if it's in the middle of the slope, return the high level
    else if ((current - start) < slopeLengthSecs)
    {  // it's in the beginning slope up
      smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0) + 180.0;
      //smoothPhase = constrain(map(current, start, start+slopeLengthSecs, 180, 360), 180, 360);
    }
    else if ((end - current) < slopeLengthSecs)
    { // it's in the end slope down
      smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0);
      //smoothPhase = constrain(map(current, end-slopeLengthSecs, end, 0, 180), 0, 180);
    }
    return startPWMint + (int)(pwmDelta*((1.0+(cos(radians(smoothPhase))))/2.0));

  }
}
User avatar
Posts: 1426
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City
PostPosted: Mon Aug 24, 2015 3:17 am
What does the app say the values are when this happens?

--Colin
User avatar
Posts: 5342
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Aug 24, 2015 3:48 am
It sounds like your wrapping a variable or a time. Can be tricky with offsets if you wrap over the 24 hour mark. I had that happen a lot with the sunrise/sunset code. I can't look through this now (on vacation) but thats what I'd be looking for.
User avatar
Posts: 138
Joined: Thu May 23, 2013 2:52 pm
PostPosted: Mon Aug 24, 2015 8:45 am
cosmith71 wrote:What does the app say the values are when this happens?

--Colin


Neither the UApp nor the Android app display the values for the 16-channel expansion dimming levels, to my knowledge. I tried to write a few lines of code to output the current levels for the channels to the CustomVariables, but wasn't confident I had it coded correctly, and removed those lines of code.

While those lines were in place, they looked something like this (from memory):

Code: Select all
ReefAngel.PWM.Set16ChannelRaw(0,PWMSmoothRampHighRes(7,30,22,00,0,65,45,0));
ReefAngel.PWM.Set16ChannelRaw(1,PWMSmoothRampHighRes(7,25,22,05,0,65,45,0)); // 05 minute longer
ReefAngel.PWM.Set16ChannelRaw(2,PWMSmoothRampHighRes(7,30,22,00,0,80,45,0));
ReefAngel.PWM.Set16ChannelRaw(3,PWMSmoothRampHighRes(7,30,22,00,0,40,45,0));
ReefAngel.PWM.Set16ChannelRaw(4,PWMSmoothRampHighRes(7,30,22,00,0,20,45,0));
ReefAngel.PWM.Set16ChannelRaw(5,PWMSmoothRampHighRes(7,15,22,15,0,40,45,0)); // 15 minute longer
ReefAngel.PWM.Set16ChannelRaw(6,PWMSmoothRampHighRes(7,20,22,10,0,30,45,0)); // 10 minute longer
ReefAngel.PWM.Set16ChannelRaw(7,PWMSmoothRampHighRes(7,30,22,00,0,10,45,0));

for (int i=0; i<8; i++)
{
  ReefAngel.CustomVariable[i] = (int)(ReefAngel.PWM.Get16ChannelValueRaw(i)/40.95);
}


When those lines were in the code, I would see the Custom Variable values for the warm white, red and violet channels (1, 5, and 6) pop up into the 30s right after 22:00 and curiously increase (e.g., warm white was at 31, refreshed the page and it was at 32, then eventually 33). That behavior made me think I had something coded incorrectly, so I removed the for loop that was writing values to the custom variables (which didn't appear to fix the issue, as after 22:05, the red and violet channels were on at what appeared to be nearly 100% intensity).
User avatar
Posts: 1426
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City
PostPosted: Mon Aug 24, 2015 8:50 am
You might want to change the '05' to just '5'. Arduino treats anything prefaced with a zero as an octal number. Don't think that's causing your problem, though.

Are you sure those channels are dimming at all? Could they be running full blast all the time, with it only noticeable when the other channels dim?
User avatar
Posts: 138
Joined: Thu May 23, 2013 2:52 pm
PostPosted: Mon Aug 24, 2015 8:53 am
lnevo wrote:It sounds like your wrapping a variable or a time. Can be tricky with offsets if you wrap over the 24 hour mark. I had that happen a lot with the sunrise/sunset code. I can't look through this now (on vacation) but thats what I'd be looking for.


I do have one slope that wraps over midnight, but it's on a channel that wasn't implicated in the behavior (the royal blue channel I use for moonlight). It also isn't passed to the Set16ChannelRaw function unless it's calculated as the larger of the two slopes (daylight ramping based on schedule on/off or moonlight ramping based on moonrise/moonset):

Code: Select all
moon_init(44,-116);   // approximate lat,lon for Boise, ID
SunBlueLevel = PWMSmoothRampHighRes( 7, 30, 22, 00, 0, 80, 45, 0 );
MoonBlueLevel = PWMSmoothRampHighestRes(Moon.riseH,Moon.riseM,Moon.setH,Moon.setM,0, (int)(MoonPhase()/19),45,0);

ReefAngel.PWM.Set16ChannelRaw(0,PWMSmoothRampHighRes(7,30,22,00,0,65,45,0));
ReefAngel.PWM.Set16ChannelRaw(1,PWMSmoothRampHighRes(7,25,22,05,0,65,45,0)); // 05 minute longer
ReefAngel.PWM.Set16ChannelRaw(2,max(SunBlueLevel, MoonBlueLevel));
ReefAngel.PWM.Set16ChannelRaw(3,PWMSmoothRampHighRes(7,30,22,00,0,40,45,0));
ReefAngel.PWM.Set16ChannelRaw(4,PWMSmoothRampHighRes(7,30,22,00,0,20,45,0));
ReefAngel.PWM.Set16ChannelRaw(5,PWMSmoothRampHighRes(7,15,22,15,0,40,45,0)); // 15 minute longer
ReefAngel.PWM.Set16ChannelRaw(6,PWMSmoothRampHighRes(7,20,22,10,0,30,45,0)); // 10 minute longer
ReefAngel.PWM.Set16ChannelRaw(7,PWMSmoothRampHighRes(7,30,22,00,0,10,45,0));


The PWMSmoothRampHighestRes function is one I define myself below the loop() to take 12-bit values as start and end values (to limit the moonlight intensity below what could be accomplished with the PWMSmoothRampHighRes function, which takes start and and values as percentages, and then multiples by 40.95).
User avatar
Posts: 5342
Joined: Fri Jul 20, 2012 9:42 am
PostPosted: Mon Aug 24, 2015 12:39 pm
SmoothRamp might be the culprit. I had issues with it at one point in testing. I think the length of the duration is key with that. Try the slope or parabola and see if you have the same issues.


Sent from my iPad using Tapatalk
User avatar
Posts: 138
Joined: Thu May 23, 2013 2:52 pm
PostPosted: Mon Aug 24, 2015 5:03 pm
cosmith71 wrote:You might want to change the '05' to just '5'. Arduino treats anything prefaced with a zero as an octal number. Don't think that's causing your problem, though.

Are you sure those channels are dimming at all? Could they be running full blast all the time, with it only noticeable when the other channels dim?


Appreciate the tip -- didn't realize that leading zeros did that.

I can say with certainty that these channels are dimmer before the other lights reach 0. The total light output of the tank at 9:59 is quite low, and at 10:00, is blindingly bright (the indigo channel is hard to look at, especially if your eyes are nice and used to dim nearly 0% light levels)
User avatar
Posts: 138
Joined: Thu May 23, 2013 2:52 pm
PostPosted: Mon Aug 24, 2015 5:09 pm
lnevo wrote:SmoothRamp might be the culprit. I had issues with it at one point in testing. I think the length of the duration is key with that. Try the slope or parabola and see if you have the same issues.


Sent from my iPad using Tapatalk


I thought it odd that the colors looked fine this morning -- which made me wonder if it were specific to the slope down.

I wonder if the calculation for the down slope (the second "else if" below) should be using "end" instead of "start"? (I can't bring myself to step through it, but it strikes me as a possible error:

Code: Select all
else if ((current - start) < slopeLengthSecs)
{ // it's in the beginning slope up
smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0) + 180.0;
//smoothPhase = constrain(map(current, start, start+slopeLengthSecs, 180, 360), 180, 360);
}

else if ((end - current) < slopeLengthSecs)
{ // it's in the end slope down
smoothPhase = (((float)(current-start)/(float)slopeLengthSecs)*180.0);
/////////shouldn't this ^^^^^^^^^^^^^  be (end-current)?
//smoothPhase = constrain(map(current, end-slopeLengthSecs, end, 0, 180), 0, 180);
}


Calculating the smoothPhase based on the distance between the "current" and "start" variables, when doing the down slope at the end of the curve, means that the smoothPhase could be quite giant (e.g., if start was at 7:00 am, and current is 10:05 pm, then the distance between them (in minutes) is 785, so the value returned

Code: Select all
    return startPWMint + (int)(pwmDelta*((1.0+(cos(radians(smoothPhase))))/2.0));


is going to be way off, and even increasing, since the distance is getting larger (rather than smaller, as would be the case if smoothPhase was calculated by (end-current)).

Posts: 263
Joined: Wed Jan 01, 2014 7:26 am
PostPosted: Wed Aug 26, 2015 10:12 am
Josh,

I'll take a look at it. It does look like an error. I use the Sigmoid one so I didn't notice it. Sigmoid is a bit flatter on the top tham SmoothRamp.

You can see my curve here:

status/chart.aspx?id=AlanM&filter=scpwme2
Next

Return to Development Libraries

Who is online

Users browsing this forum: No registered users and 1 guest

cron