# Thread: DeadBand Code works from Main, but not as a function, bool returns value of 9?

1. ## DeadBand Code works from Main, but not as a function, bool returns value of 9?

I used this fairly simple code to set a deadband in a microcontroller project I am working on.

Code:
```        if(Temp1 < Set1)
{
Out1 = 0;
}
else if(Temp1 > Set1 + DeadBand1)
{
Out1 = 1;
}```
The above works perfectly if executed out of main.c
(PIC Micro 24FV16KM204, XC-16 ver 1.25 compiler, using MPLab-X)

However, I need to call this 12 times, so, I put i in a function, prototyped it, and it sort of works, but anytime the Temp1 (Temperature, Analog ADC value stored as an int) is greater than the Set1, but less than the Set1 + Deadband 1, I get a return value of 9!
This is the non-working code
Code:
```bool SetOutput(unsigned int a, unsigned int b, unsigned int c)
{
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
if(b > a + c)
{
result = 1;
}
return(result);
}```

If Temp1 is less than Set1, I get a 0, and if Temp1 is > Set1 + DeadBand1, I get a 1, but anywhere in between, I get a return of 9.

So how & why do I get a return value of 9?

I am fairly new to programming, so, please excuse me if this is obvious to some of you.

Edit: Just to clarify, the return value of 9 nulifies the intent of the code, you end up with no deadband. 2. Firstly, I want to say that
Code:
```bool SetOutput(unsigned inta, unsigned intb, unsigned intc){
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
if(b > a + c)
{
result = 1;
}
return(result);
}```
and
Code:
```bool SetOutput(unsigned inta, unsigned intb, unsigned intc){
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
else if(b > a + c)
{
result = 1;
}
return(result);
}```
have different algorithms.First one, when you execute the code you have two condition that program must execute.

However, the second one, when the program runs and first if statement is true, then program will not execute the else if statements. Therefore make your choose, which code that you need.

Other thing is that, if you want to call this function 12 times you can make a basic loop like that,
Code:
```for(x=0;x<12;x++)
SetOutput(a,b,c);``` 3. H ..

I assume as the second 'if' or 'else if' has an expresson with an additional c int....it is possible of a False result....but what is the value given if it is False....you only give a value to the result if its True... 4. Originally Posted by sulfur93 Firstly, I want to say that
Code:
```bool SetOutput(unsigned inta, unsigned intb, unsigned intc){
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
if(b > a + c)
{
result = 1;
}
return(result);
}```
and
Code:
```bool SetOutput(unsigned inta, unsigned intb, unsigned intc){
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
else if(b > a + c)
{
result = 1;
}
return(result);
}```
have different algorithms.First one, when you execute the code you have two condition that program must execute.

However, the second one, when the program runs and first if statement is true, then program will not execute the else if statements. Therefore make your choose, which code that you need.

Other thing is that, if you want to call this function 12 times you can make a basic loop like that,
Code:
```for(x=0;x<12;x++)
SetOutput(a,b,c);```
Oops my bad, I have tried everything I can with this to make it work, & didn't notice the "else if" in the second code example. It does not work as expected if it is 2 "if" statements in a function either. (appears as if once someone replies to a post I can no longer edit it, so the following code also returns the value of "9" whenever the program is above Set1, and below Set1 + DeadBand1)
Code:
```bool SetOutput(unsigned inta, unsigned intb, unsigned intc){
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
if(b > a + c)
{
result = 1;
}
return(result);
}```

Also, by call it 12 times, I meant that I need to call for (Temp1, Set1, DeadBand1), (Temp2, Set2, DeadBand2), (Temp3, Set3, DeadBand3), etc...

Thanks for the responce though 5. Originally Posted by JohnGM H ..

I assume as the second 'if' or 'else if' has an expresson with an additional c int....it is possible of a False result....but what is the value given if it is False....you only give a value to the result if its True...
If I understand what you are saying, you want me to put something like this?
Code:
```bool SetOutput(unsigned int a, unsigned int b, unsigned int c)
{
bool result;

if(b < a)                   // If Temp < Setpoint
{
result = 0;
}
else if(b > a+c)
{
result = 1;
}
else
{
result = 1;
}```
What I am attempting to do, is have an output turn on when a temperature goes below its setpoint, but I do not want it to turn off until it goes a certain amount of deadband above its setpoint, example turns on at 25 deg, turns off at 30 deg, so, I intentionally leave a DeadBand zone between the two "if" test conditions. 6. Originally Posted by davez I put i in a function
You did that wrong. There are three possible results from the check: 0, 1, and unchanged.

One way to write it as a function would be
Code:
```bool check(const bool oldstate, const unsigned int low, const unsigned int value, const unsigned int high)
{
if (value < low)
return 0;
else
if (value > high)
return 1;
else
return oldstate;
}```
where the first parameter is the result of the previous check. For example,
Code:
```    bool state = 0;
unsigned int x;

/* a = low limit, c = dead band, b = sample */

for (x = 0; x < 12; x++) {
state = check(state, a, b, a+c);
/* Do something with state */
}``` Originally Posted by davez So how & why do I get a return value of 9?
Because you return a value you never initialized. Your compiler would warn you about that, if you enable warnings. (I use -Wall -Wextra -O2 with GCC.)

The logic you implemented was basically
Let result have any random value.
If b is less than a ,
set result = 0
If b is greater than a+c ,
set result = 1
So, if b is between a and a+c, you return the random result value. The function I show above returns the old state instead -- but it has to be passed as a parameter (or be a global variable) -- which I do believe was your intent. 7. Originally Posted by Nominal Animal You did that wrong. There are three possible results from the check: 0, 1, and unchanged.

One way to write it as a function would be
Code:
```bool check(const bool oldstate, const unsigned int low, const unsigned int value, const unsigned int high)
{
if (value < low)
return 0;
else
if (value > high)
return 1;
else
return oldstate;
}```
where the first parameter is the result of the previous check. For example,
Code:
```    bool state = 0;
unsigned int x;

/* a = low limit, c = dead band, b = sample */

for (x = 0; x < 12; x++) {
state = check(state, a, b, a+c);
/* Do something with state */
}```

Because you return a value you never initialized. Your compiler would warn you about that, if you enable warnings. (I use -Wall -Wextra -O2 with GCC.)

The logic you implemented was basically
Let result have any random value.
If b is less than a ,
set result = 0
If b is greater than a+c ,
set result = 1
So, if b is between a and a+c, you return the random result value. The function I show above returns the old state instead -- but it has to be passed as a parameter (or be a global variable) -- which I do believe was your intent.

So, I implemented this based on what you have said, and it works perfectly (of course) (seems simple now)

Code:
```bool SetOutput(bool Out, unsigned int SetPoint, unsigned int ProcessVariable, unsigned char DeadBand)
{
if (ProcessVariable < SetPoint)
return 0;
else
if (ProcessVariable > SetPoint + DeadBand)
return 1;
else
return Out;
}```
Thank you very much!!

Still a question though, If I ran my erronious code from whithin main, it functioned fine (returned only a 0 or a 1), but if I ran identical code in a function, it returned 0, 1, or 9, even tho it was a bool return?

Quote:
The logic you implemented was basically
Let result have any random value. As my limited understanding goes, result is a bool, so should only be able to store 0, or 1?
If b is less than a ,
set result = 0 so, set it to 0,
If b is greater than a+c ,
set result = 1 or, set it to 1,

or, do nothing at all, and it returns 9? (this is the part I do not understand.)

Also, as just a little clarification, I do not need to call this code 12 times with the same variables (ie loop it 12 times), I need to call it 12 times, for 12 different sets of variables (I am reading and controlling 12 different temperatures)

Again, thank you very much for the guidance, your direction was PERFECT!! 8. ## Solved, DeadBand Code works from Main, but not as a function, bool returns value of 9

Thanks all for the help, and specifically Nominal Animal, for the actual solution!! 9. Originally Posted by davez I ran identical code in a function, it returned 0, 1, or 9, even tho it was a bool return?
The result was allocated on stack, and because it wasn't initialized, it is "set" to whatever value was in the stack at that position. When you call a function in a loop, it is common for such uninitialized values to get the same value on each call.

In C, the bool type is actually an integer type, with zero being false, and all nonzero values true. It can hold more than two values. If you use logical expressions (like result = (a < b)) the compiler will only store a 0 or a 1, and you can always ensure the value is 0 or 1 by using !!result (which means not-not-result, or (result != 0)). Here, the value is uninitialized, and therefore can be anything. (And since in C it really is some integer type, you happen to get 9.) Originally Posted by davez Also, as just a little clarification, I do not need to call this code 12 times with the same variables (ie loop it 12 times), I need to call it 12 times, for 12 different sets of variables (I am reading and controlling 12 different temperatures)
In that case, here's what I'd use:
Code:
```#define SENSORS 12

int limit[SENSORS] = { 0 };

unsigned char deadband[SENSORS] = { 0 };

bool state[SENSORS] = { 0 };

unsigned int i;
for (i = 0; i < SENSORS; i++) {
state[i] = check(state[i], limit[i] - deadband[i]/2, measurement(i), limit[i] + (deadband[i] + 1)/2);
/* Do something with state[i] */
}```
In C, when initializing an array, you do not need to specify a value for each member. The compiler will initialize the rest to zeros.

Above, limit[] is the centerpoint of the deadband. If the deadband is odd, the upper half of the dead range is one larger. If we used deadband[i]/2 for both, incrementing the deadband by 1 would not do anything; the deadband would always be effectively even (a multiple of 2).

The mathematically correct way would be to use (deadband[i] - deadband[i]/2) for the upper range, as it evaluates to deadband[i]/2 if it is even, but to deadband[i]/2 + 1 if it is odd. However, because it cannot be negative, we can achieve the exact same result with (deadband[i] + 1)/2, which is much easier for the compilers to optimize (if you are using an 8-bit microcontroller, this might matter a lot).

If you have some sort of user interface to allow the user to control the limit and the deadband separately, this is much more intuitive arrangement, as changing limit[] moves the range -- and usually changing that is sufficient --, and changing deadband[] only changes the sensitivity (the total range of the temperature changes allowed). 10. Originally Posted by Nominal Animal The result was allocated on stack, and because it wasn't initialized, it is "set" to whatever value was in the stack at that position. When you call a function in a loop, it is common for such uninitialized values to get the same value on each call.

In C, the bool type is actually an integer type, with zero being false, and all nonzero values true. It can hold more than two values. If you use logical expressions (like result = (a < b)) the compiler will only store a 0 or a 1, and you can always ensure the value is 0 or 1 by using !!result (which means not-not-result, or (result != 0)). Here, the value is uninitialized, and therefore can be anything. (And since in C it really is some integer type, you happen to get 9.)

In that case, here's what I'd use:
Code:
```#define SENSORS 12

int limit[SENSORS] = { 0 };

unsigned char deadband[SENSORS] = { 0 };

bool state[SENSORS] = { 0 };

unsigned int i;
for (i = 0; i < SENSORS; i++) {
state[i] = check(state[i], limit[i] - deadband[i]/2, measurement(i), limit[i] + (deadband[i] + 1)/2);
/* Do something with state[i] */
}```
In C, when initializing an array, you do not need to specify a value for each member. The compiler will initialize the rest to zeros.

Above, limit[] is the centerpoint of the deadband. If the deadband is odd, the upper half of the dead range is one larger. If we used deadband[i]/2 for both, incrementing the deadband by 1 would not do anything; the deadband would always be effectively even (a multiple of 2).

The mathematically correct way would be to use (deadband[i] - deadband[i]/2) for the upper range, as it evaluates to deadband[i]/2 if it is even, but to deadband[i]/2 + 1 if it is odd. However, because it cannot be negative, we can achieve the exact same result with (deadband[i] + 1)/2, which is much easier for the compilers to optimize (if you are using an 8-bit microcontroller, this might matter a lot).

If you have some sort of user interface to allow the user to control the limit and the deadband separately, this is much more intuitive arrangement, as changing limit[] moves the range -- and usually changing that is sufficient --, and changing deadband[] only changes the sensitivity (the total range of the temperature changes allowed).
OK, So, I can see where you are going with this, and while I do like it, I am not sure it will work (easily, anyway) for me.

All of my "measurement" variables, come from a Function (int ADCRead(channel)), where my channel numbers will not be 0, 1, 2, 3, 4, etc, but will be more like 0, 1, 4, 5, 6, 8, etc. (more random)

Would you use a lookup table to assign the correct channel number to each "measurment" variable, a series of if, else if statements, or something else??

I also do have a user interface for changing the Setpoint & DeadBand, but for the most part, the DeadBand will initially get set to some value after commissioning, then likely remain at that value forevermore.

Setpoint will be user adjustable as well, but will also automatically bias itself, based on measured Outdoor temperature. (outside Temp goes down, Setpoint Bias goes up, outside Temp goes up, Setpoint Bias goes down) This is currently unimplemented, but is coming soon.

This controller will control 2 groups of floor Temperatures (a Low Temp zone, and a High Temp zone), such that if any High Temp zone is calling for heat, any other High Temp zone that is not currently at or above its Setpoint + DeadBand will also turn on. Same setup for the Low Temp zone, except with priority given to the High Temp zone. (Low Temp zones have very large thermal mass, & can sustain Temp for long periods of time, so, get less priority)

Because of this control algorithim, implementing the center "limit" you suggested also would in this case, not work as well. (though for most scenarios, it would be an excellent idea, and I am definately filing it away for future use in other projects.)

In the interest of providing a little more info, this controller is being used to control a high-efficiency gas fired boiler, to control domestic hot water Temp, High Temp heating zones (radiators and coils), and Low Temp heat zones (concrete floors)

Domestic hot water will have the highest priority, followed by High Temp zones, followed by Low Temp zones.

The idea being to fire the boiler as hard as possible when it needs to run, but run it less often. (Rather than have each zone randomly turning the boiler on/off, at a low fire rate, have each Temp zone run as a group, thereby running the boiler harder, for a shorter period of time.)

This allows the boiler to actually run more efficiently(a high efficiency boiler only meets it efficiency ratings at full fire rate, at lower fire rates, efficiency actually goes down), while also negating the possibility (in a properly designed & tuned system) of the Low Temp zones never getting allowed to run.

I have so far only referenced FeedForward values in this controller (controlling temperature of the glycol to the loop, based on outdoor ait Temp), but there are also going to be room temperature feedbacks as well.

Anyway, I think I have rambled on too long already, so, again, thanks a lot for your input!! 11. Originally Posted by davez All of my "measurement" variables, come from a Function (int ADCRead(channel)), where my channel numbers will not be 0, 1, 2, 3, 4, etc, but will be more like 0, 1, 4, 5, 6, 8, etc. (more random)

Would you use a lookup table to assign the correct channel number to each "measurment" variable, a series of if, else if statements, or something else??
I'd keep the sensors consecutive in an array, and just have a separate array indicating the relevant channel number for each sensor. Originally Posted by davez This controller will control 2 groups of floor Temperatures (a Low Temp zone, and a High Temp zone), such that if any High Temp zone is calling for heat, any other High Temp zone that is not currently at or above its Setpoint + DeadBand will also turn on. Same setup for the Low Temp zone, except with priority given to the High Temp zone. (Low Temp zones have very large thermal mass, & can sustain Temp for long periods of time, so, get less priority)
Very interesting!

I assume you can control each floor section separately. That is, that you can turn the furnace, and the glycol pump for each section, on and off separately.

I would not divide the sections into zones. I'd just have a separate minimum temperature for each section, and the maximum temperature a section is heated to.

Whenever a section goes below the minimum temperature, the furnace is turned on, and all sections that are below the maximum temperature get the glycol pumping through. As a section reaches the maximum temperature, that glycol pump is turned off. The furnace is kept on until no pumps are on anymore.

Unfortunately, that means the reservoir of glycol heated by the furnace stops circulating as the furnace turns off. There's still lots of thermal energy there, and it is wasted by slightly warming the furnace up.

One way to avoid that would be to add a third temperature limit for each section, say "optimal". It works like the maximum above. However, when all sections have reached optimal, the furnace is turned off, but the glycol pumps are kept pumping, until either the section reaches the maximum, or the temperature of the glycol drops to below the circulation limit.

In the user interface, I would keep a separate counter for each section, to signify the number of times that particular section caused the furnace to turn on. That would tell the owner/user to either lower that temperature limit, or increase the optimum or maximum on all the others, to make the furnace turns on less often.

I hope you've made sure your control circuits fail safely! That is, that the pump relays fail to non-conducting state. I'd also sprinkle quite a lot of the temperature sensors, and assume some of them fail sooner or later, and take that into account in the control logic. (For example, multiple measurements on each section, and using the subset that looks the most sane. What "looks most sane" means depends on how this stuff works in practice, and I don't have the real-world experience with floor-heating systems to say. Here in Finland, they tend to be electric, and much easier to control.)

My background is in computational physics, and I would for sure simulate all this, using real-life measurements as a guide (as to the glycol temperature, rate of temperature changes, and so on). 12. Originally Posted by Nominal Animal I'd keep the sensors consecutive in an array, and just have a separate array indicating the relevant channel number for each sensor.
Yes, that seems fairly obvious, now that you have pointed it out. (I am pretty new to programming (in any language), so, some of the solutions do not seem to just "come to mind" like they do for others, which, I guess is why I asked the question on this site.) Originally Posted by Nominal Animal Very interesting!

I assume you can control each floor section separately. That is, that you can turn the furnace, and the glycol pump for each section, on and off separately.
Yes, except, there are currently 3 glycol pumps, and 11 zone valves.

There is a central "mixing chamber" (actually referred to as a Low Loss Header), an input loop and an output loop. Pump #1 is on the output loop, and provides the glycol flow to low temp zone valves, Pump #3 is parallel tee'd off the output loop and provides glycol to Domestic hot water & the high temp zones.

Pump #2 is in the input loop & drives glycol coming back from the mixing chamber through the boiler & back to mixing chamber.

Currently, any zone valve turns on, it turns on the pump for that temterature zone, and any other valves in that zone that are not above their upper limits. (Zone valves are turned on & off by analog temp controllers configured with failsafe upper limit alarms) Originally Posted by Nominal Animal I would not divide the sections into zones. I'd just have a separate minimum temperature for each section, and the maximum temperature a section is heated to.

Whenever a section goes below the minimum temperature, the furnace is turned on, and all sections that are below the maximum temperature get the glycol pumping through. As a section reaches the maximum temperature, that glycol pump is turned off. The furnace is kept on until no pumps are on anymore.
I have to have them seperated, as if I push the high temp zone water into a low temp zone, it will do physical damage to the slab. (6" thick concrete slabs) Originally Posted by Nominal Animal Unfortunately, that means the reservoir of glycol heated by the furnace stops circulating as the furnace turns off. There's still lots of thermal energy there, and it is wasted by slightly warming the furnace up.

One way to avoid that would be to add a third temperature limit for each section, say "optimal". It works like the maximum above. However, when all sections have reached optimal, the furnace is turned off, but the glycol pumps are kept pumping, until either the section reaches the maximum, or the temperature of the glycol drops to below the circulation limit.
I plan on a re-piping job next summer, to replace the low loss header with the domestic water heater, use the existing TekMar 270 boiler control to control the domestic water temp, and pull glycol for my heating loops out of the domestic water heater.

It will in effect be nearly the same, except, I am adding a modulating 3-way mixing valve to handle the low temp loop, and using the mass of the water heater to make it so the boiler cycles less often, but for longer periods of time. (But more importantly, so the low temp, high temp, and domestic water zones can all run at the same time, if required. (no more priority for a particular zone) Originally Posted by Nominal Animal In the user interface, I would keep a separate counter for each section, to signify the number of times that particular section caused the furnace to turn on. That would tell the owner/user to either lower that temperature limit, or increase the optimum or maximum on all the others, to make the furnace turns on less often.
Excellent idea! Added to the list of waiting to be implemented. Originally Posted by Nominal Animal I hope you've made sure your control circuits fail safely! That is, that the pump relays fail to non-conducting state. I'd also sprinkle quite a lot of the temperature sensors, and assume some of them fail sooner or later, and take that into account in the control logic. (For example, multiple measurements on each section, and using the subset that looks the most sane. What "looks most sane" means depends on how this stuff works in practice, and I don't have the real-world experience with floor-heating systems to say. Here in Finland, they tend to be electric, and much easier to control.)
All will be configured as failsafe.

I poured multiple sensors into the floors, set multiples into the tile beds, etc, and have a long temp plan of using a median select scheme to decide which input to run on (I have 3 sensors for each zone), but currently do not have the i/o to do that. (it is fairly easy to master/slave more micro-controllers, so, coming, but one step at a time) Originally Posted by Nominal Animal My background is in computational physics, and I would for sure simulate all this, using real-life measurements as a guide (as to the glycol temperature, rate of temperature changes, and so on).
My own background is in Industrial Instrumentation, so the control part is pretty easy. (that being said, easy to overlook something as well)
Am currently simulating what is coded so far (which is actually pretty functional already) on a breadboard sitting beside me. It is all running on a PIC 24FV16KM204, with a 4x20 LCD Char interface, and 7 input buttons (they are all picked up on 1 analog input, but I am going to trim that to 4 buttons anyway, just to make a cleaner interface)

As far as actual real world measurements, I have this system already running with a bunch of Omron analog temperature controllers, so I actually have usable Setpoints, DeadBands, etc to load into the new controller as default starting points. (also plan on implementing a software "reset to defaults" for a possible future user who doesn't know what they are doing & get stuff too messed up, they can just re-set, & start over) Popular pages Recent additions 