Set16ChannelRaw Unexpected behavior

Related to the development libraries, released by Curt Binder
Post Reply
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Set16ChannelRaw Unexpected behavior

Post by joshlawless »

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
cosmith71
Posts: 1437
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City

Re: Set16ChannelRaw Unexpected behavior

Post by cosmith71 »

What does the app say the values are when this happens?

--Colin
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Set16ChannelRaw Unexpected behavior

Post by lnevo »

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
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

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
cosmith71
Posts: 1437
Joined: Fri Mar 29, 2013 3:51 pm
Location: Oklahoma City

Re: Set16ChannelRaw Unexpected behavior

Post by cosmith71 »

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
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

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
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Set16ChannelRaw Unexpected behavior

Post by lnevo »

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
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

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
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

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)).
AlanM
Posts: 263
Joined: Wed Jan 01, 2014 7:26 am

Re: Set16ChannelRaw Unexpected behavior

Post by AlanM »

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:

http://forum.reefangel.com/status/chart ... er=scpwme2
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

It looks like it should be

Code: Select all

else if ((end - current) < slopeLengthSecs)
{ // it's in the end slope down
smoothPhase = (((float)(end-current)/(float)slopeLengthSecs)*180.0) + 180.0; 
This way, smoothPhase varies from 2pi to pi over the final slope,
the cosine of smoothPhase varies from 1 to -1 over the final slope,
half the sum of smoothPhase and 1.0 varies from 1 to 0 over the final slope,
and the return value varies from endPWMint to startPWMint over the final slope
Last edited by joshlawless on Fri Aug 28, 2015 3:19 pm, edited 1 time in total.
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

Just a follow-up to state that after making the change specified in the post above to my copy of the libraries, I can confirm the behavior is now as I would expect.

Chart

One thing I noticed when parsing the function -- it calculates the value based on the current time in seconds. With a 12-bit slope, though, you would need at least a 70 minute (linear) ramp to take full advantage of all 4095 levels available. I wonder if it isn't possible to increase the resolution to tenths (or even hundredths) of a second, e.g., by multiplying the start, end and current values by 10, and adding (current_millis()%1000 - current_millis()%100)/100 to the current value, as in this function (which also takes PWM start and end values as 12-bit values instead of percentages):

Code: Select all

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 current_millis = millis()
  long start = NumMins(startHour, startMinute)*600L;
  long end = NumMins(endHour, endMinute)*600L;
  long slopeLengthTenthSecs = slopeLength*600L;

  if (start > end) // Start is greater than End so it is over midnight
  {
    if (current_hour < endHour) start -= 1440L*600L; // past midnight
    if (current_hour >= startHour) end += 1440L*600L; // before midnight
  }
  long current = NumMins(current_hour, minute())*600L + second()*10L + (current_millis()%1000 - current_millis()%100)/100L;
  if (slopeLengthTenthSecs > ((end-start)/2) ) slopeLengthTenthSecs = (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 + slopeLengthTenthSecs)) && (current < (end - slopeLengthTenthSecs))) 
      return endPWMint; // if it's in the middle of the slope, return the high level
    else if ((current - start) < slopeLengthTenthSecs) 
    {  // it's in the beginning slope up
      smoothPhase = (((float)(current-start)/(float)slopeLengthTenthSecs)*180.0) + 180.0;
    }
    else if ((end - current) < slopeLengthTenthSecs)
    { // it's in the end slope down
      smoothPhase = (((float)(end-current)/(float)slopeLengthTenthSecs)*180.0) + 180.0;
    }
    return startPWMint + (int)(pwmDelta*((1.0+(cos(radians(smoothPhase))))/2.0));
  }
}
User avatar
lnevo
Posts: 5430
Joined: Fri Jul 20, 2012 9:42 am

Re: Set16ChannelRaw Unexpected behavior

Post by lnevo »

Looks awesome. I'm not sure how much sense it would make to change it to tenths but I'm intrigued by your approach. I've been thinking of that for a bit with my dosing pump code. Because I'm dosing by volume at the end of the day I'm dosing a little bit under maybe by a full ml because of the resolution there. Interested in helping to take a stab there? :)
User avatar
joshlawless
Posts: 138
Joined: Thu May 23, 2013 2:52 pm

Re: Set16ChannelRaw Unexpected behavior

Post by joshlawless »

lnevo wrote:Looks awesome. I'm not sure how much sense it would make to change it to tenths but I'm intrigued by your approach. I've been thinking of that for a bit with my dosing pump code. Because I'm dosing by volume at the end of the day I'm dosing a little bit under maybe by a full ml because of the resolution there. Interested in helping to take a stab there? :)
Sure thing.

For posterity, here's an updated/corrected version of the function above -- the one above had some typos (missing ";" and accidentally included "()" after current_mills.

Code: Select all

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 current_millis = millis();
  long start = NumMins(startHour, startMinute)*600L;
  long end = NumMins(endHour, endMinute)*600L;
  long slopeLengthTenthSecs = slopeLength*600L;

  if (start > end) // Start is greater than End so it is over midnight
  {
    if (current_hour < endHour) start -= 1440L*600L; // past midnight
    if (current_hour >= startHour) end += 1440L*600L; // before midnight
  }
  long current = NumMins(current_hour, minute())*600L + second()*10L + (current_millis%1000 - current_millis%100)/100L;
  if (slopeLengthTenthSecs > ((end-start)/2) ) slopeLengthTenthSecs = (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 + slopeLengthTenthSecs)) && (current < (end - slopeLengthTenthSecs))) 
      return endPWMint; // if it's in the middle of the slope, return the high level
    else if ((current - start) < slopeLengthTenthSecs) 
    {  // it's in the beginning slope up
      smoothPhase = (((float)(current-start)/(float)slopeLengthTenthSecs)*180.0) + 180.0;
    }
    else if ((end - current) < slopeLengthTenthSecs)
    { // it's in the end slope down
      smoothPhase = (((float)(end-current)/(float)slopeLengthTenthSecs)*180.0) + 180.0;
    }
    return startPWMint + (int)(pwmDelta*((1.0+(cos(radians(smoothPhase))))/2.0));
  }
}
Post Reply