PWM & map

Hello all, here is full code, crude, simplistic, crappy, and rough - but it compiles and loads to Arduino nicely. Temperature and setpoint work and display on LCD. The last section in my problem.

// variables for input pin and control LED
#include <LiquidCrystal.h>
#include <math.h>
int SetPnt_pin = 5;
int temp_pin = 1;
int heat_pin = 9;

int rawsetpnt = 0;
int SetPntF = 0;
int tempraw = 0;
float tempV = 0;
float tempC = 0;
int tempF = 0;
int ctrl_F;
int tempF1;
int tempF2;

LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // (RS, Enable, data lines)

void setup()
{
  // declaration of pin modes
  pinMode(heat_pin, OUTPUT);

  // setup to send to LCD with 16 columns and 2 lines.
  lcd.begin(16,2);
  Serial.begin(9600);
}

void loop(){

  tempraw = 0;
  ctrl_F = 0;
  {
    for (int i = 0; i < 20; i++)  // a smoothing function
      tempraw = tempraw+analogRead(temp_pin);
    delay(50);
    tempraw = tempraw / 20;
    tempV = ((tempraw * 5.0) / 1023.0);
    tempC = ((tempV-1.375) / .0225);
    tempF = ((tempC*1.8)+32);

    lcd.setCursor(0,0);
    lcd.print("HeatTemp = ");
    lcd.print(tempF);
    lcd.print("F");
    lcd.print("   ");
  }
  delay(50);
  //*************************start setPoint section
  {
    rawsetpnt = analogRead(SetPnt_pin);
    rawsetpnt = (rawsetpnt/3.09); 
    SetPntF = rawsetpnt;

    lcd.setCursor(0,1);
    lcd.print("SetPoint = " );
    lcd.print(SetPntF);
    lcd.print("F");
    lcd.print("   ");
  }

  delay(50);
/*everything works just fine down to this section. The setpoint displays and
 changes with pot. Temperature displays a reasonable temperature and goes 
up with heat applied to sensor.  Here is where my problem comes in - trying 
to map with PWM to vary output to heater.*/
  //tempF1 = (tempF - 20);
  //tempF2 = tempF;
  //ctrl_F = map (tempF, tempF1, tempF2, 255, 0);//emaking all these different.  
  //ctrl_F = map (tempF, 75, 95, 255, 0); //this works
  ctrl_F = map (tempF, (tempF - 20), tempF, 255, 0);   
//will this work? using (setpt - 20) & setpt rather than an actual number

  if (tempF < (SetPntF - 20)) 
  {
    analogWrite (heat_pin, 255); //until temperature gets within 20 points 
//of setpoint, heat on max heat
  }
  else // else if tempF is NOT < (SetPntF-20) then start tapering heat off 
//from 255 to zero
  {
    analogWrite(heat_pin, ctrl_F); //start tapering off the heat, from 255 to zero.
  }
  Serial.print("ctrl_F =");
  Serial.println(ctrl_F);
}

If I set the analogWrite(heat_pin, SetPntF): the PWM varys nicely as it should. The problem seems to be that ctrl_F always is “0” (zero) so it doesn’t work.

What I do get is the LED (for heater) is ON 100% until 20 before setpoint, then the PWM goes to zero, which is what it should do since ctrl_F is always zero.

Why does it not vary with temperature as it should? Is it because the map function only works with a real number, not… what do you call a word that is assigned a value? The example only uses numbers, but didn’t say if “words” work or not.

Thanks for any and all help.

Ken H>

I'm not sure that I understand your question, but I think you are asking if the map function can be called with variables as arguments, rather than constants. The answer is yes.

But look at the call you are making:

ctrl_F = map (tempF, (tempF - 20), tempF, 255, 0);

The input value and the end of the from range are the same value. The output, then, will always be the end of the to range. That's 0 in this case.

It's not clear to me, what you are trying to do here, so I can't suggest how to fix this.

Thank you for comments Paul - and you are up early.

OK, let me try to word this better (clearly defining the problem is half the solution sometimes). If I assign a value as shown below it works.
Example:
tempF = 200. then (tempF - 20) = 180.

ctrl_F = map (tempF, (tempF - 20), tempF, 255, 0);
would be:
ctrl_F=map(tempF, 180, 200, 255, 0); // with numbers there the map function works.

OR if tempF = 300

ctrl_F=map(tempF, 280, 300, 255, 0); // that also works.

As shown below it does not work - cntrl_F stays = zero.

if (tempF < (SetPntF - 20)) //as long as tempF is less than 20 below setpoint, heat max
  {
    analogWrite (heat_pin, 255);
  }
  else //  20 below setpoint, start tapering heat off from 255 to zero
  {
    analogWrite(heat_pin, ctrl_F); //as tempF approaches setpoint, heat approaches zero

I should be using a PID controller, but that is more than my abilities - I think if I can get this to work it will be ok.

Should I be working with SetPntF? Something on the order of:

ctrl_F=map(tempF, (SetPnt-20), SetPntF, 255, 0):

Is that the problem - trying to use tempF in all the places?

Thanks for any and all help,

Ken H>

ctrl_F = map ([glow]tempF[/glow], (tempF - 20), [glow]tempF[/glow], 255, 0);

The argument that defines the value to map is tempF. The range to map FROM ends at tempF. The range to map TO ends at 0.

When the value to map is at the upper limit of the FROM range, it maps to the upper end of the TO range (or 0, in your case).

The way you are calling the map function, the value to be mapped is ALWAYS at the upper limit of the range.

ctrl_F = map (tempF, (tempF - 20), tempF, 255, 0); would be: ctrl_F=map(tempF, 180, 200, 255, 0); // with numbers there the map function works.

In the last line, change tempF to 200, and work out what that should map to.

Is that the problem - trying to use tempF in all the places?

Yes. But, whether the solution is to use SetPntF in it's place is something only you can decide.

Paul, forgive me, but I am especially dense this morning (and I’m NOT a programmer, I’m more hardware).

ctrl_F=map(tempF, 180, 200, 255, 0);

In the last line, change tempF to 200, and work out what that should map to.

I think that is the line you are referring to? Well, that is where tempF does =200.

200-20=180 for fromLow
200 = tempF for fromHigh.

The map function is: map(value, fromLow, fromHigh, toLow, toHigh)

What I’m trying to map is, when
tempF = 180, then ctrl_F should be 255,
when tempF = 200, then ctrl_F should be zero.

This should allow the heater to heat max until 180 is reached, then start tapering off to zero heat as 200 is reached. This “180” and “200” would change as tempF changes. i.e. if tempF = 250, then the numbers would be 230 & 250; tempF=300, 280 & 300.

Are you saying that “value” can not equal “fromHigh” because that will give zero?

What if I have “value = tempF”; (tempF-20 = fromLow), (tempF-1=fromHigh), 255, 1)

i.e. if tempF = 200 it would then be:
ctrl_F=map(tempF, 180, 199, 255, 1); there is not a zero anywhere.

I just wish the heater to start tapering off as the tempF approaches setpoint and heater to go off when setpoint is reached. To do this my thinking is to have ctrl_F mapped to a range of values from 255 to 0 (standard PWM range?).

Paul, there is something niggling at my mind that says “something ain’t right” - you seem to see if, but darn if I do.

Thank you for your help - I’ve been trying to write this for the last couple of hours - write a bit, think some, write some more, delete some, write.

more thinking:

if tempF < SetPntF - 20);
{
analogWrite(heat_pin, 255);
}
else
{
analogWrite(heat_pin,ctrl_F);
}

“IF” ctrl_F was set to 255 tapering to zero, would that work? If so, how to set ctrl_F to 255 to 0?

Ken H>

If you want to map temperatures, then pinning the FROM range to the input temperature is not the way to do it.

You should have some point at which the heater should come on, and some point at which it should go off. I think I understand that SetPntF is the temp at which it should go off, and that SetPntF-20 is when it should go on.

These values, independent of tempF, should be used in the map's FROM range. The 255 and 0 for the TO range are good. Then, map tempF.

When tempF is below SetPntF-180, what do you want to have happen? If you pass a value to map that is outside the FROM range, the return value will be outside the TO range.

If SetPntF is 200, and tempF is 160, map(tempF, SetPntF-20, SetPntF, 255, 0); will return 510.

Thank you Paul for taking time to work with me on this.

There are 3 basic ranges of heater control:

  1. from room temperature to 20 degrees below setpoint, heat to be ON for max heat.

  2. from 20 degrees below setpoint to setpoint, heat should start tapering from 255 (max) to 0 (OFF).

  3. above setpoint heater should be OFF.

Then as temperature falls below setpoint, the heater would be controlled by the value of ctrl_F depending how far below setpoint the tempF is.

Paul, you say

pinning the FROM range to the input temperature is not the way to do it.

How should I do it? I think if I could get ctrl_F to have the value from 255 to 0, it might work with the following code:

if tempF < SetPntF - 20); 
{
analogWrite(heat_pin, 255); //if tempF is less than 20 below SetPntF, heat max.
}
else  //otherwise have heat_pin heat per ctrl_F (someway ctrl_F needs to be tied to 
//tempF so it knows go to 0 when SetPntF = tempF...
{
analogWrite(heat_pin,ctrl_F); //
}

Would the above code work? I don’t think so, but darn if I can see how to do it.
Thank you again for taking time to help me understand.

Ken H>

The if part is right. If the temperature is more than 20 degrees below the set point, run the heater wide open.

The else part should have a call to map(tempF, (SetPntF - 20), SetPntF, 255, 0); to map ramp down the heater.

The else part should be an else if, actually:

else if(tempF > SetPntF - 20 && tempF < SetPntF)

There should be an else clause, to turn the heater off of tempF is above SetPntF. The map function would return a negative value, which should cause analogWrite to do nothing, but I prefer to assure that the argument to analogWrite is between 0 and 255, inclusive.

But, that’s just me.

THANK YOU!! Paul, That is starting to click a bit more now. Darn it, a buddy send the && part last night… remember I kept saying there was something? I’ve used that Boolean before but couldn’t think of it last night and a buddy sent to me just as I was going to bed at midnight! I forgot!! this morning. Good thing my head is screwed on.

I’ll be playing with this some more tonight - maybe… for sure tomorrow and over the weekend.

Yes, I see you have SetPntF for the From map rather than the tempF I had.

[edit]Paul, to set heater to OFF when tempF > SetPntF, should I use analogWrite(heat_pin, 0)? OR should I use digitalWrite(heat_pin, LOW)? I don’t know if it is OK to mix analogWrite with digitalWrite on the same pin.[/edit]

Thank you again,

Ken H>

to set heater to OFF when tempF > SetPntF, should I use analogWrite(heat_pin, 0)? OR should I use digitalWrite(heat_pin, LOW)?

Both accomplish the same thing. analogWrite with a duty cycle of 0 causes the pin to never come on. The digitalWrite with a value of LOW does the same thing.

Thank you Paul - I suspected they would do the same, but didn't know if one was preferred over the other or not. Sounds like either one is good.

Thank you - I hope to be working on the code some more later tonight. Dinner time now.

Ken H>

PaulS - THANK YOU!! for the comments and guidance on this project. I finally got a chance to work with the code tonight and it works GREAT!! Just as I wanted.

with temperature more than 20 below setpoint, output is HIGH, as the temperature hits 20 below, the output changes to the PWM and tracks the increase in temp nicely until at setpoint, output goes to LOW.

Thank you for your help.

Ken H>