2

First post here. So I have an issue, which I have identified, but I'm not quite sure how to fix it. I'd really appreciate some input and suggestions. I realize that my "pwmDelta" is the problem, but I am not sure what to do to fix it.

I am trying to write a program that will increment the value of the pin 3 a set amount of time, in a somewhat linear fashion, starting at my low point, and finishing at my high point.

The issue is that my equation is incorrect and I can't output a partial bit value. Huge oversight on my part. If you read the data from the serial monitor with the code written as I have it, you'll see that it isn't coming even close to reaching my max value.

I need suggestions on how I can have the program figure out the delta in a way that I can send an integer value to the PWM output pin, yet still keep a somewhat linear increase over time. Would this even be possible?

int motor1 = 3;         //pin for PWM output to motor
float t = 0;            //Variable to keep Track of time
int tLim = 30;          //Time Limit for Ramp
float lowLim = 21;      //Low Limit, in Duty Cycle %
float highLim = 65;     //High Limit, in Duty Cycle %
float motorPWM = 53.55; //Starting Value of PWM, in Bits
float pwmDelta = ((255/100)*(highLim-lowLim))/(tLim); //Delta equation, to calculate how much to change PWM value each time it is incremented
float motorDelta;       //Variable to write value to Motor Output

void setup() {
  pinMode(motor1, OUTPUT);
  analogWrite(motor1, 53.55); //run motor at initial value for 20 seconds before beginnning ramp
  delay(20000);
  Serial.begin(9600);
}

void loop() {
  motorDelta = motorPWM + pwmDelta; //incrementing output value based on Delta Equation
  motorPWM = motorDelta; //Setting new incremented value equal to old value, so incrementing can continue

  if (t <= tLim) {  //Check if time is <= time limit
    analogWrite(motor1, (motorDelta)); //write incremented value to output pin
    delay(1000);  //wait 1 second, increment t, to keep track of time
    t++;
  }
  else {
    analogWrite(motor1, motorPWM = 165.75); //set pin to high value for 60 seconds, then set to 0
    delay(60000);
    analogWrite(motor1, motorPWM = 0);
  }
  Serial.print(motorDelta);
  Serial.println();
  Serial.print(t);
  Serial.println();
}
2
  • think about this .... what other parameter can you vary? – jsotola Mar 4 '20 at 23:25
  • @jsotola The 255/100 needs to be constant, as that gives me 2.55 bits per 1 Duty Cycle percent. Any of the other parameters in the pwmDelta can be varied, and it's designed to be as such. So when it's finished a user can input the Low/High Limit in Duty Cycle % and time, and the output will increase in a linear fashion over that time period. If you mean other parameters outside of that equation, I guess I am not following. – gfritz25 Mar 4 '20 at 23:37
4
float pwmDelta=((255/100)*(highLim-lowLim))/(tLim);

That line should be:

float pwmDelta=((255.0/100)*(highLim-lowLim))/(tLim);

Adding the .0 tells the compiler to do that math using floats and you get 2.55. Without it the math is done with int and the result is 1 which isn't what you want.

analogWrite(motor1, 53.55); 

Just write 54. analogWrite only accepts integer values.

analogWrite(motor1, motorPWM=165.75);

You should really do that as two lines, an assgnment and then an analogWrite. This way leads to confusing code. Also, analogWrite only accepts integer values so your decimals are useless there as well.

2
  • 1
    analogWrite(motor1, 53.55) outputs 53: float to int conversions do not round, they just truncate the fractional part. – Edgar Bonet Mar 5 '20 at 8:59
  • 1
    You're right. But I figured 54 would be closer to what he actually wanted because I rounded. I was suggesting that he use 54 not that analogRead would treat his float as 54. – Delta_G Mar 5 '20 at 23:35
2

The previous responses already showed the main issue with the program. Here I am providing a few suggestions that hopefully could still improve it:

  • Instead of using delay(), you could manage the time with millis(). This will make the ramp smoother, and you will be able to add code to the loop if needed that will not be blocked by the delays.
  • Storing the times in milliseconds and the PWM value in its natural unit (rather than percent) makes the code simpler.
  • Remove redundant variables, such as motorDelta, which is the same as motorPWM.
  • Consistent indentation and removal of superfluous parentheses help the readability.

Here is my attempt at implementing those suggestions:

const int motor1 = 3;  // pin for PWM output to motor
const unsigned long tLim = 30000;  // duration of the PWM ramp
const float PWM_unit = 255.0/100;  // convert percent to PWM scale
const float lowLim = PWM_unit * 21;
const float highLim = PWM_unit * 65;
const float PWM_rate = (highLim-lowLim) / tLim;

unsigned long start_time;  // start of the ramp
int previousPWM = 0;

void setup() {
    pinMode(motor1, OUTPUT);
    analogWrite(motor1, round(lowLim));
    delay(20000);
    Serial.begin(9600);
    Serial.println(" t   PWM");
    Serial.println("--------");
    start_time = millis();
}

void loop() {
    unsigned long t = millis() - start_time;

    if (t >= tLim) {  // we are done
        analogWrite(motor1, round(highLim));
        delay(60000);  // keep at highLim for 60 s
        analogWrite(motor1, 0);  // then stop
        exit(0);
    }

    int motorPWM = round(lowLim + t * PWM_rate);
    if (motorPWM != previousPWM) {
        analogWrite(motor1, motorPWM);
        Serial.print(t / 1000.0);
        Serial.print("  ");
        Serial.println(motorPWM);
        previousPWM = motorPWM;
    }
}
1
  • This helps tremendously. Thank you so much. I'll check it out later. It will be a part of a much larger program designed to replace a motor controller, with a much more functional one, so the delay would certainly cause issues with it. For the time being, I was just trying to see if I could make it work, and it wasn't working. This code was never intended to be long term, but I will definitely take your suggestions into consideration when it comes time to implement the code into the rest of the controller. – gfritz25 Mar 5 '20 at 23:14
1

You are trying to smoothly ramp up the PWM value from 21% to 65% over a period of 30 seconds at 1 second intervals, which does not work very well because the PWM value has to be a byte.

That means that the value written into the PWM register changes from 21*255/100 to 65*255/100, which is 53.55 to 165.75.

Use values of 53 and 166, which gives a difference of 113.

Your code is written to update the PWM value every 1000 ms, which produces 30 steps, most of which are not integers.

Instead, ramp up the PWM value in 113 steps. Each step will be an integer and each step will be 265486 μs in length.

0

What's happening is that you've written a fraction in a way that the compiler treats it as integer math. C/C++ truncate fractions, so 255/100 is just 2 (and if any of the rest of those are integers, they cause truncation too). What you want to do is make some number in that calculation a decimal--it doesn't matter which one--or cast an input to a float with (float) somevariable.

As long as that's all on one line, all of the calculations involved (excepting order of operations, so cast to float one of the variables being divided if you have them in separate parenthesized terms) will be in floating point. Do note that if you set the value of an integer with this, it will only truncate after doing the math accurately, too, so it's more correct.

float pwmDelta = ((255/100)*(highLim-lowLim))/(tLim);

Should be:

float pwmDelta = 2.55*(highLim-lowLim)/(float)tLim; (This also makes it all one term.)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.