I'm trying to improve upon my old code to controll humidity and temperature inside my terrarium.
The fans used to be drivven by a linear map() function but this results in short, powerfull bursts that pushed beyond the target value. I'm trying to use a pow() function ( x^(x/100) ) to follow a nice graph like this:
I have something that seems to be working (still testing) but the math looks kinda rubbish. I map de sensor readings to the target temperature (desiredHumidity) and the maximum tolerable value (90% in this case). After mapping I use pow() to adjust "severity" and after I map it again to get a PWM output signal. Any tips to clean this up?
You have the 'mappedVal' in the exponent. That is too complex for me.
There are online curve fitting sites.
For example: https://mycurvefit.com/. Enter the data points, then try all the curve fitting formulas. It shows how well the curve goes through the points.
Your curve gave me both a linear and a power curve feeling, so that is what I made:
// Messy math with map() and pow()
// Forum:
// https://forum.arduino.cc/t/messy-math-with-map-and-pow/1275821
// This Wokwi project: https://wokwi.com/projects/401785042678168577
//
//
// A simple power curve could be enough.
// But the lower part of the graph has a more linear part.
// Therefor I made a smooth transition from linear to
// a power curve.
//
// The "y(forum)" are guessed values from the graph
// on the forum
// x y(forum) y(this sketch)
// -----------------------------
// 0 1 1
// 20 2 2.8
// 40 4.5 4.5
// 60 12 10.4
// 80 33 33.5
// 100 100 100
//
// The values are calculated between 0...1 for simplicity.
void setup()
{
Serial.begin(115200);
float x = 0;
for(int i=0; i<=10; i++)
{
float ypow = pow(x,4.2); // the power curve
float ylin = 0.01 + x/8.0; // the linear curve
float y = (1.0-x)*ylin + x*ypow; // smooth transistion
Serial.print(x*100.0);
Serial.print(", ");
Serial.println(y*100.0);
x += 0.1;
}
}
void loop()
{
delay(10);
}
Try it in Wokwi:
The left side of the curve is linear and the right side of the curve is a power curve. With a smooth transition from 0 to 100%.
That website can make a good curve with the 'x' value in the exponent of the power function This is a good matching curve: y = 1.27847 - (-0.0207948/-0.05583161)*(1 - e^(+0.05583161*x)).
It can be entered as y=1.27847-(-0.0207948/-0.05583161)*(1-exp(0.05583161*x)) here: https://www.desmos.com/calculator
If you make a function that returns the calculated value, then you can always alter and tune the curve in the function.
I was using desmos.com for the initial graph I posted, using X ^ ( X/100).
I didn't know about mycurvefit.com though, so thanks. Frustratingly, even after registering, I can't finish what I started after refining datapoints. Maxed out calculations for trial version
Either way: is there any benefit using a long incomprehensible row of numbers for a calculation instead of using pow() ?
I had to google what a PID is but that sounds about right, except the rest of my code won't work with it.
I'm controlling both temperature and humidity with, currently, 1 set of fans.
It's a long story but currently I'm focussing on controlling 1 of these values. There is some code for situations where both values are deviating, but I'd rather have something working for either first.
The linear distribution using map() was working but always overshooting and causing a counter reaction. So I started looking into a way to have a very fine low setting, so the closer I get to the target value, the less of an active influence is exerted.
Counter to the fan for a high value is, in case of the humidity, an ultrasonic mist maker. Hence I don't want to overshoot.
Maybe a PID is the way to go, though it does not sound much easier. I'll read up on it.
I read about your MultiMap function when I was reading this old thread today. It's where I got the pow() idea from.
To be honest I could not quite wrap my head around it, but let me hazard a guess.
I make an array with values corresponding to the X-axis and a similar array for the Y-axis, and MultiMap will use this to calculate all other points between the given values, even though they may not be linear?
Please note that the multimap allows non-equidistant IN or X values. They only need to be in increasing order.
This allows to have less points where the curve is almost linear and more points where the curve is more dynamic ==> keep the maximum error under control.
we use analogWrite() because it can output PWM, the average voltage is:
val÷255×Vcc
whereas val is the PWM value (0-255) and Vcc is the power source voltage.
@LoganTheEngineer7 If the Microsoft Copilot wrote that, can you please remove it ?
It is weird, it makes no sense, it is not to the point and it is wrong. Only AI can produce such an answer.
The digitalWrite() function does not expect a bool value, but HIGH or LOW.
Timed I/O can be used in this project, but it does not have to.
The calculation is what a teacher does tell, but in practice it is just 0...255 from low output to full on.
Here is the analogWrite() for an Arduino Uno: https://github.com/arduino/ArduinoCore-avr/blob/master/cores/arduino/wiring_analog.c#L96
The PWM value is not a unsigned 8-bits value. It is just an 'int'.