My math is just awful

Hi, my code runs humidity and temperature controllers.
I have been trying to use dewpoint as a way to contol the machine but my maths in no where near good enough for this, I have code for dew point but it comes out at a much lower temp than it really is.
Hoping some maths genius can shed light on where ive gone wrong.

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>


double dewPoint(double celsius, double humidity) {
  // (1) Saturation Vapor Pressure = ESGG(T)
  double RATIO = 373.15 / (273.15 + celsius);
  double RHS = -7.90298 * (RATIO - 1);
  RHS += 5.02808 * log10(RATIO);
  RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / RATIO))) - 1);
  RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1);
  RHS += log10(1013.246);

  // factor -3 is to adjust units - Vapor Pressure SVP * humidity
  double VP = pow(10, RHS - 3) * humidity;

  // (2) DEWPOINT = F(Vapor Pressure)
  double T = log(VP / 0.61078);  // temp var
  return (241.88 * T) / (17.558 - T);
}


#include "DHT.h"

#define DHTPIN 11  // what pin we're connected to

// Uncomment whatever type you're using!
#define DHTTYPE DHT11  // DHT 11
//#define DHTTYPE DHT22 // DHT 22 (AM2302)
//#define DHTTYPE DHT21 // DHT 21 (AM2301)


// Initialize DHT sensor for normal 16mhz Arduino
DHT dht(DHTPIN, DHTTYPE);

//DHT dht(DHTPIN, DHTTYPE, 30);
int humidfan = 2;  //  Intake fan for Humid box
int humidv = 3;    //  Valve for humud intake
int freshfan = 4;  // fresh fan
int freshv = 5;    //  fresh valve, air from outside
int exfan = 6;     // exhaust fan
int exv = 7;       // exhaust valve
int dryfan = 8;    // intake fan for dry box
int dryv = 9;      // dry box valve
//const int resetPin = 10;  // button to reset cycle
// DHT pin 11
int DHTpower = 13;                 // constant 5v to DHT11
int fridge = 12;                   // relay to fridge 240v
const int TEMP_LOWER = 19;         // low
const int TEMP_UPPER = 21;         //
unsigned long previousMillis = 0;  // will store last time LED was updated
const long interval = 14400000;
LiquidCrystal_I2C lcd(0x27, 20, 4);  // Set the LCD address to 0x27 for a 16x2 display

void setup() {
  pinMode(humidfan, OUTPUT);
  pinMode(dryfan, OUTPUT);
  pinMode(fridge, OUTPUT);
  pinMode(humidv, OUTPUT);
  pinMode(dryv, OUTPUT);
  pinMode(exfan, OUTPUT);
  pinMode(exv, OUTPUT);
  pinMode(freshfan, OUTPUT);
  pinMode(freshv, OUTPUT);
  pinMode(DHTpower, OUTPUT);
  digitalWrite(humidfan, LOW);
  digitalWrite(dryfan, LOW);
  digitalWrite(humidv, LOW);
  digitalWrite(dryv, LOW);
  digitalWrite(exfan, LOW);
  digitalWrite(exv, LOW);
  digitalWrite(freshfan, LOW);
  digitalWrite(freshv, LOW);
  digitalWrite(DHTpower, HIGH);
  digitalWrite(fridge, LOW);
  lcd.init();       // Initialize the LCD
  lcd.backlight();  // Turn on the backlight
  lcd.clear();      // Clear the LCD screen
  lcd.setCursor(5, 1);
  lcd.print("The Green");
  lcd.setCursor(6, 2);
  lcd.print("Ninja");
  delay(2200);  // Display the startup message for 2 seconds
  lcd.clear();
  lcd.setCursor(6, 1);
  lcd.print("FCKPGR");
  delay(280);
  lcd.clear();
  dht.begin();
  Serial.begin(600);
}

void loop() {
  unsigned long currentMillis = millis();
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius
  float t = dht.readTemperature();
  // Read temperature as Fahrenheit
  float f = dht.readTemperature(true);
  float currentDew = dewPoint(t, h);  // Dew Point for setting pins high

//  temperature = dht.readTemperature();

  Serial.print("Dew Point,");
  Serial.println(dewPoint(t, h));;
  // Serial.println(",");
  Serial.print("Humidity,");
  Serial.print(dht.readHumidity());
  Serial.print(",");

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t) || isnan(f)) {
    Serial.println("Failed to read from DHT sensor!");
    lcd.clear();
    lcd.print("Failed to read from DHT sensor!");

    return;
  }

  // Compute heat index
  // Must send in temp in Fahrenheit!
  float hi = dht.computeHeatIndex(f, h);
  float hiDegC = dht.convertFtoC(hi);



  if (f < 18) {
    digitalWrite(fridge, LOW);
  }

  if (f > 21) {
    digitalWrite(fridge, HIGH);
  }

  if (currentDew > 11 && (currentDew < 13))  // Level to aim for dew point 12
  {
    digitalWrite(humidfan, LOW);
    digitalWrite(dryfan, LOW);
    digitalWrite(humidv, LOW);
    digitalWrite(dryv, LOW);
    digitalWrite(exfan, LOW);
    digitalWrite(exv, LOW);
    digitalWrite(freshfan, LOW);
    digitalWrite(freshv, LOW);
    digitalWrite(DHTpower, HIGH);

    lcd.setCursor(0, 0);
    lcd.print("Dew Pnt");
    lcd.setCursor(14, 0);
    lcd.print(dewPoint(t, h));
    lcd.setCursor(19, 0);
    lcd.print("c");

    lcd.setCursor(0, 1);
    lcd.print("Temp");
    lcd.setCursor(14, 1);
    lcd.print(dht.readTemperature());
    lcd.setCursor(19, 1);
    lcd.print("c");

    lcd.setCursor(0, 2);
    lcd.print("Humidity");
    lcd.setCursor(14, 2);
    lcd.print(dht.readHumidity());
    lcd.setCursor(19, 2);
    lcd.print("%");

    lcd.setCursor(0, 3);
    lcd.print("Valves Closed");
    delay(4000);
  }




  if (currentDew > 13) {  //   High dew point, send to dry box

    digitalWrite(dryfan, LOW);
    digitalWrite(humidv, HIGH);
    digitalWrite(dryv, LOW);
    digitalWrite(exfan, LOW);
    digitalWrite(exv, LOW);
    digitalWrite(freshfan, LOW);
    digitalWrite(freshv, LOW);
    digitalWrite(DHTpower, HIGH);
    delay(4000);
    digitalWrite(humidfan, HIGH);

    lcd.setCursor(0, 0);
    lcd.print("Dew Pnt");
    lcd.setCursor(14, 0);
    lcd.print(dewPoint(t, h));
    lcd.setCursor(19, 0);
    lcd.print("c");

    lcd.setCursor(0, 1);
    lcd.print("Temp");
    lcd.setCursor(14, 1);
    lcd.print(dht.readTemperature());
    lcd.setCursor(19, 1);
    lcd.print("c");

    lcd.setCursor(0, 2);
    lcd.print("Humidity");
    lcd.setCursor(14, 2);
    lcd.print(dht.readHumidity());
    lcd.setCursor(19, 2);
    lcd.print("%");

    lcd.setCursor(0, 3);
    lcd.print("Dry Box On   ");
  }




  if (currentDew < 13) {  // Dew point low, send to humid box

    digitalWrite(dryfan, LOW);
    digitalWrite(humidv, HIGH);
    digitalWrite(dryv, LOW);
    digitalWrite(exfan, LOW);
    digitalWrite(exv, LOW);
    digitalWrite(freshfan, LOW);
    digitalWrite(freshv, LOW);
    delay(4000);
    digitalWrite(humidfan, HIGH);

    lcd.setCursor(0, 0);
    lcd.print("Dew Pnt");
    lcd.setCursor(14, 0);
    lcd.print(dewPoint(t, h));
    lcd.setCursor(19, 0);
    lcd.print("c");

    lcd.setCursor(0, 1);
    lcd.print("Temp");
    lcd.setCursor(14, 1);
    lcd.print(dht.readTemperature());
    lcd.setCursor(19, 1);
    lcd.print("c");

    lcd.setCursor(0, 2);
    lcd.print("Humidity");
    lcd.setCursor(14, 2);
    lcd.print(dht.readHumidity());
    lcd.setCursor(19, 2);
    lcd.print("%");

    lcd.setCursor(0, 3);
    lcd.print("Humid Box On  ");
  }
}```

this is the part that i cant work out.

double dewPoint(double celsius, double humidity) {
// (1) Saturation Vapor Pressure = ESGG(T)
double RATIO = 373.15 / (273.15 + celsius);
double RHS = -7.90298 * (RATIO - 1);
RHS += 5.02808 * log10(RATIO);
RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1 / RATIO))) - 1);
RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1);
RHS += log10(1013.246);

// factor -3 is to adjust units - Vapor Pressure SVP * humidity
double VP = pow(10, RHS - 3) * humidity;

// (2) DEWPOINT = F(Vapor Pressure)
double T = log(VP / 0.61078); // temp var
return (241.88 * T) / (17.558 - T);
}

What Arduino are you using? The 8-bit AVRs don't really have a 'double' and it's just an alias for float and has only 6-7 significant digits.

If you aren't using an actual double, maybe the higher order terms are discarded by the precision of the arithmetic?

Have you tried a value against an on-line drybulb/wetbulb/humidity calculator? The result might help find any algorithm flaws.

Im using the Arduino Uno, to be honest I dont even know what "double" means in this context.

No I havent looked, I just know its reading about 20 deg c less than it really is

Since an uno doesn't have a "double precision float", all the calculations will round to only 6-7 digits of precision at each step

If you look at your RHS components for 25 degrees:

> RATIO = 373.15 / (273.15 + 25)
> RATIO
[1] 1.251551
> -7.90298 * (RATIO - 1)
[1] -1.988004
> 5.02808 * log10(RATIO)
[1] 0.4899795
> -1.3816e-7 * (10^((11.344 * (1 - 1 / RATIO))) - 1)
[1] -2.619061e-05
> 8.1328e-3 * (10^( (-3.49149 * (RATIO - 1))) - 1);
[1] -0.007056455
> log10(1013.246);
[1] 3.005715
> 

...it's going to throw away a couple calculations worth of significant digits. If those bits are important, the results will be trash.

Please post a link to the formula you are trying to translate to C language.

Hey, this is where i am stuck, math might as well be in a different language to me.

Thanks, would going to an Arduino Mega fix that then? im running out of pins on the Uno

Since humidity is very inaccurately measured by consumer grade sensors, you might just as well use this formula:

2 Likes

I peaked under the weather.gov covers... here is what they use (javascript)...


Ew = parseFloat(esubw(Cwetbulb));
E = parseFloat(VaporPressure(Ew, MBpressure, Ctemp, Cwetbulb));
Es = parseFloat(esubs(Ctemp));
rh = parseFloat(RH(E, Es));

if (rh > 100 ) {
  alert('Your RH is greater than 100%.  You may want to check to see if your wet-bulb is greater than your temperature');
  document.Convert.RH.value = roundOff(rh);
} else {
  document.Convert.RH.value = roundOff(rh);
}

dewpoint = roundOff(Dewpoint(E));
document.Convert.DewpointF.value = parseFloat(roundOff(convertCtoF(dewpoint)));
document.Convert.DewpointC.value = roundOff(dewpoint);
document.Convert.DewpointK.value = parseFloat(roundOff(convertCtoK(dewpoint)));

function convertFtoC(Fahr) {
  var Cels;
  Cels = .55556 * (Fahr - 32);
  return Cels;
}

function convertKtoC(Kel) {
  var Cels;
  Cels = Kel - 273.15;
  return Cels;
}

function convertCtoF(Cels) {
  var Fahr;
  Fahr = 1.8 * Cels + 32;
  return Fahr;
}

function convertCtoK(Cels) {
  var Kelvin;
  Kelvin = Cels + 273.15;
  return Kelvin;
}

function convertinHGtomb(inHG) {
  var mb;
  mb = 33.8639 * inHG;
  return mb;
}

function convertmmHGtomb(mmHG) {
  var mb;
  mb = 1.333224 * mmHG;
  return mb;
}


function esubw(Cwetbulb) {
  var Ew;
  Ew = 6.112 * Math.exp((17.67 * Cwetbulb) / (Cwetbulb + 243.5));
  return Ew;
}

function VaporPressure(Ew, MBpressure, Ctemp, Cwetbulb) {
  var E;
  E = Ew - MBpressure * (Ctemp - Cwetbulb) * 0.00066 * (1 + (0.00115 * Cwetbulb));
  return E;
}

function Dewpoint (E) {
  var Dewpoint;
  Dewpoint = (243.5 * Math.log(E / 6.112)) / (17.67 - Math.log(E / 6.112));
  return Dewpoint;
}

function esubs(Ctemp) {
  var Es;
  Es = 6.112 * Math.exp(17.67 * Ctemp / (Ctemp + 243.5));
  return Es;
}

function RH(E, Es) {
  var RH;
  RH = 100 * E / Es;
  return RH;
}

function roundOff(value) {
  value = Math.round(100 * value) / 100;
  return value;
}
2 Likes

No, the mega also has an 8-bit processor and fake doubles. Try @jremington's soln:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.