Pages: [1]   Go Down
Author Topic: A millis() problem within a function, within another function. (functception)  (Read 1004 times)
0 Members and 1 Guest are viewing this topic.
British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey guys

I am currently prototyping a dual zone hvac controller, and having issues when I need to pause the program while the damper opens for which ever floor needs heat/cool/fan.



I am having troubles with the millis() function. The blink without delay works fine while in the loop and checks analog inputs at various times, however I need to implement a delay function (without delay of course) that waits until the damper is open then carries on with the code. I have been trying different variations with the millis() and having no luck

As for a general source code for what I am attempting:

Code:

void loop()

every (x) seconds
{
   heat();    // need heat?
}

void heat()
{
  if (temp too cold)
  {
    digitalWrite(damperOne, HIGH);
    damperdelay();
   }
}

damperDelay()
{
    if ((damperTime - prevTime) > damperDelay)
  {
    prevTtime = damperTime;
   damperState == true;                // boolean which controls the heat relay
  }
}


I think it is because millis(); starts when the system is first turned on and by the time there is a call for heat, the millis > damper delay. I'm not entirely sure but I've tried all sorts of things and nothing is working.

Thanks!
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 644
Posts: 50500
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I think it is because millis(); starts when the system is first turned on
It does.

Quote
and by the time there is a call for heat, the millis > damper delay.
You are supposed to record when there is a call for heat, and use the difference between then and now to compare to damper delay.

Quote
I'm not entirely sure but I've tried all sorts of things and nothing is working.
That code you posted won't compile, so we can't really help you, can we?

Logged

British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Paul,
I know it wont compile, I generalized my code so it was quick to write down. I was just looking for a general answer. so start a millis function when there is a call for heat and how do i compare it to the damper delay>?

you can type it using my uncompilable code  smiley-lol
Logged

Seattle, WA USA
Online Online
Brattain Member
*****
Karma: 644
Posts: 50500
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
unsigned long needForHeatTime;

void heat()
{
  if (temp too cold)
  {
    digitalWrite(damperOne, HIGH);
    needForHeatTime = millis();
    damperdelay();
   }
}

void damperdelay()
{
  if ((millis() - needForHeatTime) > damperDelay)
  {
   damperState = true;                // ONE equal; it's an assignment statement
  }
}
Using the pseudo-verify, this "compiles".
Logged

British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey thanks for the help, but for some reason it is not working (most likely the operator). Damper relay turns on, damper function is opening (verified with a serial.print msg) but it will not go past the   

 if ((damperTime - prevTime) > damperDelay)
  {



Here is some actual code

Code:
unsigned long needForHeatTime;


void heat()
{
  double t = Thermister(analogRead(0));

  if (t <= setTempOne - 0.5) bheatOn = true;
  else if (t >= setTempOne + 0.25) bheatOn = false;

  if ((bheatOn  == true) && (bheatState != true))
  {
    bheatState = true;
    digitalWrite(DAMPER_ONE_RELAY, HIGH);
    Serial.println("                                        FIRST FLOOR DAMPER OPENING...");
     needForHeatTime = millis();
    damperWait();
  }

void damperWait()
{
  Serial.println(" waiting for damper");

  if ((millis() - needForHeatTime) > damperDelay)
  {
   Serial.println(" damper on");
   bdamperState = true;                // ONE equal; it's an assignment statement
  }
}


  Serial.println(" damper on"); will not print

Is there different functions that could affect the millis()?

I have 3 millis() functions within the loop and they work fine, I don't know what else to try  smiley-roll-sweat


Thanks again Paul
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 506
Posts: 19120
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
  if ((bheatOn  == true) && (bheatState != true))

You don't need to compare to "== true". It's like saying "if it is true it is raining" rather than "if it is raining".

More readable is:


Code:
  if (bheatOn && !bheatState)
Logged


British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Nick,

I always enjoy making the code shorter and more readable  smiley

Any thoughts about my current problem? It is really starting to get to me!
Logged

New Jersey
Online Online
Faraday Member
**
Karma: 70
Posts: 3743
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

It's a little hard to analyze a snippet to give useful advice, but I can at least tell you why you never see the damper message.

You check whether heat is required but not being provided. If this is the case, you set your state variable to say that heating is on, note the time and call damperWait. damperWait, contrary to its name does not wait. It checks whether enough time has elapsed for the damper to be open and gives the "damper on" message if it has. Sadly, very little time has elapsed between the time you noted the time and called damperWait, so the message isn't shown.

Next time you call the heat function, the heating is already on, so damperWait isn't called again, so the message is never displayed. An easy fix would be to replace the if in damperWait with a while, but if you do that, you might just as well use delay. Whether that's ok is a matter for you to ponder - generally, it isn't.

Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12630
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The code you posted didn't seem to handle turning the heat off, so I assume this was a simplified example.

It looks to me as if bheatOn represents the instantaneous demand for heat, and bheatState represents whether heat is actually being supplied.

In that case I think bheatState should ideally have four states (it is no longer a boolean, now it is an enum) corresponding to the states of the physical heating system: OFF, OPENING, ON, CLOSING. You might be able to come up with better names. In the OPENING and CLOSING states you would use a timer to predict when the damper has finished moving, as demonstrated in 'blink without delay'. In the OFF state would would check whether there is a need to turn the heat on, and in the ON state you would check whether there was a need to turn the heat off.

A switch on the current state would be the simplest and cleanest way to structure the code.
Logged

I only provide help via the forum - please do not contact me for private consultancy.

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 506
Posts: 19120
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Any thoughts about my current problem? It is really starting to get to me!

As the others have said, the entire code helps reveal the entire problem. The part you are focussing on may not be the problem part, which may account for why changing it isn't helping.
Logged


British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey thanks for the input,
I am still fairly new to writing code and I'm trying hard to get all the lingo down, I will research the enum, I've never heard of that. This is my first project with the Arduino.

Here's the full code

Code:
#include <math.h>// USED FOR THERMISTOR CALCULATIONS
#include <LiquidCrystal.h>
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
LiquidCrystal lcd2(7, 29, 9, 10, 11, 12);

//TEMPERATURE PARAMETERS-
float setTempOne = 0;
float setTempTwo = 0;
int ioffSet = 0;
int outsideOffset = 0;
int soffSet = 0;
double t = Thermister(analogRead(0));
double ot = outsideTemp(analogRead(1));
double st = secondFloorTemp(analogRead(2));
//-
// PIN ASSIGNMENTS----
int HEAT_RELAY = 6;
int COMP_RELAY = 5;
int FAN_RELAY = 4;
int DAMPER_ONE_RELAY = 3;
int END_SWITCH = 26;
int END_SWITCH_TWO = 26;
int DAMPER_TWO_RELAY = 2;
int BUTTON_UP_ONE = 53;
int BUTTON_DOWN_ONE = 51;
int MODE_BUTTON = 49;
int FAN_BUTTON = 47;
int BUTTON_UP_TWO = 24;
int BUTTON_DOWN_TWO = 25;
int ival = 0;
int ivalTwo = 0;
//-
//FIRST FLOOR BOOLEANS----
boolean bheatOn  = false;
boolean bacOn = false;
boolean bheatState = false;
boolean bacState = false;
boolean bdamperState = false;
boolean bdamperOpen = false;
//----------

//SECOND FLOOR BOOLEANS-----
boolean heatOn  = false;
boolean acOn = false;
boolean heatState = false;
boolean acState = false;
boolean damperState = false;
boolean damperOpen = false;

//SYSTEM DELAYS
long checkTemp = 250;
long previoustime = 0;

long systemOverview = 5000;
long previousOutputTime = 0;

long updateTemp = 5000;
long prevTemp = 0;

long damperDelay = 3000;
unsigned long prevDamper;
unsigned long needForHeatTime;


//BUTTON INTEGERS-
int val = 0;
int valDown = 0;
int valMode = 0;
int valFan = 0;
int old_valMode = 0;
int old_valFan = 0;
int fanState = 0;
int modeState = 0;
int valTwo = 0;
int valDownTwo = 0;

//FUNCTIONS
void thermostats();
void thermostatTemps();
void serialUpdate();
void relays();
void buttons();
void heat();
void ac();
void damperWait();

//FORMULA FOR THERMISTOR TO TEMPERATURE CONVERSION
double Thermister(int RawADC) {
  double Temp;
  // See http://en.wikipedia.org/wiki/Thermistor for explanation of formula
  Temp = log(((10240000/RawADC) - 10000));
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;           // Convert Kelvin to Celcius
  return Temp + ioffSet;
}

double outsideTemp(int RawADC) {
  double oTemp;
  oTemp = log(((10240000/RawADC) - 10000));
  oTemp = 1 / (0.001129148 + (0.000234125 * oTemp) + (0.0000000876741 * oTemp * oTemp * oTemp));
  oTemp = oTemp - 273.15;           // Convert Kelvin to Celcius
  return oTemp + outsideOffset;
}

double secondFloorTemp(int RawADC) {
  double sTemp;
  sTemp = log(((10240000/RawADC) - 10000));
  sTemp = 1 / (0.001129148 + (0.000234125 * sTemp) + (0.0000000876741 * sTemp * sTemp * sTemp));
  sTemp = sTemp - 273.15;           // Convert Kelvin to Celcius
  return sTemp + soffSet;
}
//-----------------------------------------------------------------------------------------------------------------------------

void setup()
{
  pinMode (HEAT_RELAY, OUTPUT);
  pinMode (FAN_RELAY, OUTPUT);
  pinMode (COMP_RELAY, OUTPUT);
  pinMode (DAMPER_ONE_RELAY, OUTPUT);
  pinMode (END_SWITCH, INPUT);
  pinMode (BUTTON_UP_ONE, INPUT);
  pinMode (BUTTON_UP_TWO, INPUT);
  pinMode (BUTTON_DOWN_ONE, INPUT);
  pinMode (BUTTON_DOWN_TWO, INPUT);
  pinMode (MODE_BUTTON, INPUT);
  pinMode (FAN_BUTTON, INPUT);
  Serial.begin(9600);

  setTempOne = 20; //Default set point, also it is fail to heat.
  setTempTwo = 20;

  delay(60);
  lcd2.begin(20, 4);
  lcd.begin(20, 4);
  lcd.setCursor(0, 3);
  lcd.print("CB Dignan     v.1.1");
  lcd.setCursor(0, 0);
 
  lcd.clear();                   // start with a blank screen
  lcd.setCursor(0,0);            // set cursor to column 0, row 0


}

void loop()
{

  buttons();
  relays();

  if((millis() - previoustime) > checkTemp)   //CHECKS TEMPERATURE EVERY "checkTemp" INPUT
  {
    previoustime = millis();
    thermostats();
    if (modeState == 0)
    {
      heat();
    }
    if (modeState == 1)
    {
      ac();
    }
  }

  if ((millis() - previousOutputTime) > systemOverview)
  {
    previousOutputTime = millis();
    serialUpdate();
  }


  if ((millis() - prevTemp) > updateTemp)
  {
    prevTemp = millis();
    ThermostatTemps();
  }
}


void ThermostatTemps()
{
  lcd.home();
  lcd2.home();
  delay(50);
  lcd.print(Thermister(analogRead(0)));
  lcd2.print(Thermister(analogRead(2))) + 1;
  lcd.print((char)223);
  lcd2.print((char)223);

  lcd.setCursor(0,3);
  lcd.print("OA:");
  lcd.print(outsideTemp(analogRead(1)));
  lcd.print((char)223);
  lcd2.setCursor(0,3);
  lcd2.print("OA:");
  //lcd2.print(outsideTemp(analogRead(1)));
  lcd2.print((char)223);

}



void thermostats()
{
  lcd.home();
  lcd2.home();
  delay(50);

  lcd.setCursor(0,2);
  lcd.print("SP:");
  lcd.print(setTempOne);
  lcd.print((char)223);

  lcd2.setCursor(0,2);
  lcd2.print("SP:");
  lcd2.print(setTempTwo);
  lcd2.print((char)223);

  lcd.setCursor(15,0);
  lcd.print("10:26");


  lcd.setCursor(11,2);
  lcd.print("Mode:");
  if (modeState == 0)
  {
    lcd.print("Heat");
  }
  if (modeState == 1)
  {
    lcd.print("A/C ");
  }

  lcd.setCursor(11,3);
  lcd.print("Fan :");
  if (fanState == 0)
  {
    lcd.print("Auto");
  }
  if(fanState == 1)
  {
    lcd.print("On  ");
  }
}

void buttons()
{
  {

    val = digitalRead(BUTTON_UP_ONE);
    valDown = digitalRead(BUTTON_DOWN_ONE);


  }


  {
    valMode = digitalRead(MODE_BUTTON);

    if ((valMode == HIGH) && (old_valMode == LOW))
    {
      modeState = 1 - modeState;
      delay(50);
    }

    old_valMode = valMode;


  }
  {
    valFan = digitalRead(FAN_BUTTON);

    if ((valFan == HIGH) && (old_valFan == LOW))
    {
      fanState = 1 - fanState;

      delay(50);
    }

    old_valFan = valFan;
  }
}


void serialUpdate()
{
  Serial.println("Status Update...");
  Serial.println();
  Serial.print("FIRST FLOOR:      ");
  Serial.print(Thermister(analogRead(0)));
  Serial.println("     C     ");
  Serial.print("Set Point:    ");
  Serial.println(setTempOne);
  Serial.print("Mode:   ");
  if (modeState == 0)
  {
    Serial.println("Heating");
  }
  if (modeState == 1)
  {
    Serial.println("Air Conditioning");
  }
  Serial.print("Fan:");
  if (fanState == 0)
  {
    Serial.print("Auto");
  }
  if(fanState == 1)
  {
    Serial.print("On");
  }


  Serial.println();
  Serial.println();
}



void relays()
{

  boolean needfan = false;
  boolean needAc = false;
  boolean needHeat = false;


  {
    if (fanState == 1) needfan = true;
    if (bacOn == true) needfan = true;
    digitalWrite(FAN_RELAY, needfan);
  }
  {
    if ((bacState == true) && (bdamperState == true)) needAc = true;
    digitalWrite(COMP_RELAY, needAc);
  }
  {
    if ((bheatState == true) && (bdamperState == true)) needHeat = true;
    digitalWrite(HEAT_RELAY, needHeat);
  }
}


void damperWait()
{
  Serial.println(" waiting for damper");

  if ((millis() - needForHeatTime) > damperDelay)
  {
   Serial.println(" damper on");
   bdamperState = true;                // ONE equal; it's an assignment statement
  }
}


//HEAT CYCLE FIRST FLOOR----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void heat()
{
  double t = Thermister(analogRead(0));

  if (t <= setTempOne - 0.5) bheatOn = true;
  else if (t >= setTempOne + 0.25) bheatOn = false;

  
   if (bheatOn && !bheatState)
  {
    bheatState = true;
    digitalWrite(DAMPER_ONE_RELAY, HIGH);
    Serial.println("                                        FIRST FLOOR DAMPER OPENING...");
     needForHeatTime = millis();
    damperWait();
  }

  if (!bheatOn && bheatState)
  {
    bheatState = false;
    bdamperState = false;
    Serial.println("                                        FIRST FLOOR HEAT OFF");
    //delay(5000);
    digitalWrite(DAMPER_ONE_RELAY, LOW);
    Serial.println("                                        FIRST FLOOR DAMPER CLOSED");

  }
}











wildbill - I would really prefer to not have long delays in the source code, there will be buttons pushed, 2 zones, and a real time clock.(currently not in the code)

PeterH - thanks for the help I will look it up and try to make it work. Any possibility you could give a rough sketch so I  can get the general idea? Thanks
Logged

British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

My post was too long so I removed the ac cycle in the code along with some button code for the second thermostat. There may be some extra brackets or the like.

Thanks
Logged

UK
Offline Offline
Shannon Member
****
Karma: 223
Posts: 12630
-
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PeterH - thanks for the help I will look it up and try to make it work. Any possibility you could give a rough sketch so I  can get the general idea? Thanks

It's not a sketch, but a code fragment demonstrating how I'd structure the main logic of the state machine:

Code:
// incomplete, uncompiled, untested
void loop()
{
    switch(heatingState)
    {
        case OFF:
            if(heatingDemand)
            {
                openDamper();
                damperStartTime = millis();
                heatingState = SWITCHING_ON;
            }
            break;
        case SWITCHING_ON:
            if(millis() - damperStartTime > DAMPER_DURATION)
            {
                heatingState = ON;
            }
            break;
        case ON:
            if(!heatingDemand)
            {
                closeDamper();
                damperStartTime = millis();
                heatingState = SWITCHING_OFF;
            }
            break;
        case SWITCHING_OFF:
            if(millis() - damperStartTime > DAMPER_DURATION)
            {
                heatingState = OFF;
            }
            break;
     }
}
Logged

I only provide help via the forum - please do not contact me for private consultancy.

British Columbia
Offline Offline
Newbie
*
Karma: 1
Posts: 26
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks everyone for the help!

PeterH - It works! This has been a few weeks in the making, thank you very much!! smiley-lol     Now I can use enums and switch/cases, I can see how these can be very useful!

Best regards

Chris
Logged

Pages: [1]   Go Up
Jump to: