Shop OBEX P1 Docs P2 Docs Learn Events
Help w/ programming large equation — Parallax Forums

Help w/ programming large equation

In my weather recording program (see "Read 8 switches w/ least pins" in General Discussion), to convert humidity to dew point there is a rather large equation listed in the data sheet for the HTU21D Humidity sensor .

I was hoping someone would help me with coding this in Propeller SPIN. I assume this is possible to do.

I can already get the binary output from the sensor but lack the ability to use the dew point formula with Propeller.

I could not figure out a way to attach the page directly to this post so please search " HTU21D " Page 16!

This is code I have so far
CON  
{{
    Play w/ and learn humidity sensor   2nd try w/ I2C spin driver 1.4
}}    
  _clkmode  = xtal1 + pll16x
  _xinfreq  = 5_000_000 

    'I2C pins
sclpin  =  20
sdapin  =  21
Bitrate = 100_000  'I2C @ 100k

Hum      = $40 ' 7-bit I2C address '%100_0000
  
OBJ
  tv   :   "tv_text"
  i2c  :   "I2C SPIN driver v1.4od"  
  
VAR
  long address, addr0,temp0, humid, RH 
  long addrs0[2], Humi[2], temp[2]
  
  byte timbuf[7]
PUB main
  
  tv.start(0)
  I2C.init(sclpin, sdapin)
  
  tv.str(string("user register bits =  "))
  I2C.command(Hum,$E7)
  tv.bin(I2C.readNext(Hum),8)

  repeat
   tv.cr
   tv.cr
   tv.str(string("msb + lsb = "))
   tv.bin(I2C.readWordB(Hum,$E5),16)
   tv.cr
   tv.cr
   humid:=(I2C.readWordB(Hum,$E5))
   RH:=((humid*125/65536)-6)        'I would rather have Dew Point than relative humidity
   tv.str(string("RH = "))
   tv.dec(RH)
   waitcnt(clkfreq+cnt)
   tv.home

Thanks much for your time.
Aaron

Comments

  • Have a look at F32: http://obex.parallax.com/object/229 for the float calculations
  • Mike GreenMike Green Posts: 23,101
    edited 2015-12-08 15:40
    I did a quick conversion of the printed formulas. Carefully double check my work:
    CON A = 8.1332   ' Magic constants
        B = 1762.39
        C = 235.66
    
    OBJ F32 : "F32_1_6"   ' From OBEX
    
    ' Note: You have to call F32.start somewhere in your initialization code
    
    PUB Td( Tamb, RHamb ) | PP_Tamb   ' Return dew point temp as integer (C)
      Tamb := F32.FFloat(Tamb)   ' Convert to floating point
      PP_Tamb := F32.Exp10(F32.FSub(A,F32.FMul(B,F32.FAdd(Tamb,C))))
      RHamb := F32.FFloat(RHamb)   ' Convert to floating point
      result := F32.FNeg(F32.FAdd(F32.FDiv(B,F32.FSub(F32.Log10(F32.FMul(RHamb,F32.FDiv(PP_Tamb,100.0))),A)),C))
      return F32.FRound(result)
    
  • Edit: I had this post sitting in my browser window for a while. In the meantime rosco_pc has wisely suggested F32. Here's what I had intended to post half an hour ago.

    I haven't looked at the datasheet myself yet but I will soon. I just wanted to post a link to the datasheet (pdf) to make it easier for others to find. Here's the part at DigiKey.

    Here's the equation on page 16.

    DewPointHtu21d.PNG

    I generally avoid using floating point math but this might be one of those those times using a floating point library like F32 would be a good idea.

    I'll likely post a floating point example a bit later.

    (To insert a link use the little chain icon between the YouTube icon and the quote icon.)
  • I haven't tested these, but these are the methods I came up with to calculate the partial pressure and the dew point.
    PUB GetDewPoint(fRelativeHumidity, fPartialPressure)
    
      result := F32.FNeg(F32.FAdd(F32.FDiv(DEW_POINT_B, F32.FSub(F32.Log10(F32.FMul( {
      } fRelativeHumidity, F32.FDiv(fPartialPressure, 100.0))), DEW_POINT_A)), DEW_POINT_C))
    
    PUB GetPartialPressure(temperature) | fTemperature
    
      fTemperature := F32.FFloat(temperature)
    
      result := F32.Exp10(F32.FSub(DEW_POINT_A, F32.FDiv(DEW_POINT_B, F32.FAdd(fTemperature, {
      } DEW_POINT_C))))
      
    

    As one can likely guess, the variables with a "f" prefix are encoded as floating point numbers.
  • For some reason it's more fun to work on other people's programs than the one I should be working on.

    Here's some code I wrote to help display these values.
    CON
    
      _clkmode = xtal1 + pll16x                                                      
      _xinfreq = 5_000_000
    
        'I2C pins
      SCL_PIN  =  20
      SDA_PIN  =  21
    
      BITRATE = 100_000  'I2C @ 100k
       
      HTU21D_ADDRESS = $40 ' 7-bit I2C address '%100_0000
    
      DEW_POINT_A = 8.1332
      DEW_POINT_B = 1762.39
      DEW_POINT_C = 235.66
    
      DEFAULT_TEMPERATURE = 22
      
      BAUD = 115_200
      
    OBJ
      
      I2c : "I2C PASM driver v1.8od"  
      F32 : "F32"
      Pst : "Parallax Serial Terminal"
      Format : "StrFmt"
        
    PUB Main | rawHumidity, temperature, relativeHumidity, fRelativeHumidity, fPartialPressure, {
    } fDewPoint
    
      temperature := DEFAULT_TEMPERATURE
      
      Pst.Start(BAUD)
      
      repeat
        result := Pst.RxCount
        Pst.Str(string(11, 13, "Press any key to start."))
        waitcnt(clkfreq / 4 + cnt)
      until result
       
      Pst.RxFlush
       
      Pst.Clear
    
      F32.Start
        
      I2C.Start(SCL_PIN, SDA_PIN, BITRATE)
      
      Pst.Str(string("user register bits =  "))
      I2c.Command(HTU21D_ADDRESS, $E7)
      ReadableBin(I2C.ReadNext(HTU21D_ADDRESS), 8, 4)
    
      waitcnt(clkfreq * 2 + cnt)
    
      repeat
        Pst.ClearEnd
        Pst.NewLine
        Pst.ClearBelow
        Pst.Home
        Pst.Str(string(11, 13, "HTU21D Dew Point Demo"))
        Pst.Str(string(11, 13, "msb + lsb (rawHumidity) = "))
        rawHumidity := I2C.readWordB(HTU21D_ADDRESS, $E5)
        Pst.Dec(rawHumidity)
        Pst.Str(string(" = "))
        ReadableBin(rawHumidity, 16, 4)
         
        relativeHumidity := RawToRhInteger(rawHumidity)
        fRelativeHumidity := RawToRhFloat(rawHumidity)
        Pst.Str(string(11, 13, "relativeHumidity Integer Math = "))
        Pst.Dec(relativeHumidity)
        Pst.Str(string(11, 13, "relativeHumidity Floating Point Math = "))
        DisplayFloat(fRelativeHumidity, 2)
    
        Pst.Str(string(11, 13, "temperature used for dew point calculation = "))
        Pst.Dec(temperature)
        
    
        fPartialPressure := GetPartialPressure(temperature)
        
        Pst.Str(string(11, 13, "Partial Pressure = "))
        DisplayFloat(fPartialPressure, 2)
    
        fDewPoint := GetDewPoint(fRelativeHumidity, fPartialPressure)
        Pst.Str(string(11, 13, "Dew Point = "))
        DisplayFloat(fDewPoint, 2)
    
        waitcnt(clkfreq + cnt)
    
    PUB GetDewPoint(fRelativeHumidity, fPartialPressure)
    
      result := F32.FNeg(F32.FAdd(F32.FDiv(DEW_POINT_B, F32.FSub(F32.Log10(F32.FMul( {
      } fRelativeHumidity, F32.FDiv(fPartialPressure, 100.0))), DEW_POINT_A)), DEW_POINT_C))
    
    PUB GetPartialPressure(temperature) | fTemperature
    
      fTemperature := F32.FFloat(temperature)
    
      result := F32.Exp10(F32.FSub(DEW_POINT_A, F32.FDiv(DEW_POINT_B, F32.FAdd(fTemperature, {
      } DEW_POINT_C))))
      
    PUB RawToRhInteger(rawRhValue)
    
      result := (rawRhValue * 125 / 65536) - 6
    
    PUB RawToRhFloat(rawRhValue)
      
      result := F32.FSub(F32.FDiv(F32.FMul(F32.FFloat(rawRhValue), 125.0), 65536.0), 6.0)
    
    PRI DisplayFloat(fValue, decimalPlaces) | fScale
    
      fScale := F32.Exp10(F32.FFloat(decimalPlaces))
      result := RoundScaled(fValue, fScale)
      DecPoint(result, decimalPlaces)
      
    PRI RoundScaled(fValue, fScale)
    
      result := F32.FRound(F32.FMul(fValue, fScale))
      
    PRI DecPoint(value, places) | tempBuffer[4]
    
      result := Format.FDec(@tempBuffer, value, 9, places)
      byte[result] := 0
      Pst.Str(@tempBuffer)
    
    PUB ReadableBin(localValue, size, groupSize) | bufferPtr, localBuffer[12]    
    '' This method display binary numbers
    '' in easier to read groups.
    
      bufferPtr := Format.Ch(@localBuffer, "%")
      
      result := size // groupSize
       
      if result
        size -= result
        bufferPtr := Format.Bin(bufferPtr, localValue >> size, result)
        if size
          bufferPtr := Format.Ch(bufferPtr, "_")
          
      size -= groupSize
     
      repeat while size => 0
        'Debug.Bin(localValue >> size, 4)
        bufferPtr := Format.Bin(bufferPtr, localValue >> size, groupSize)
        if size
          bufferPtr := Format.Ch(bufferPtr, "_")  
        size -= groupSize
        
      byte[bufferPtr] := 0  
      Pst.Str(@localBuffer)
    

    I find the method "ReadableBin" very useful. IMO, it's a lot easier to read.
    %0001_1111_0100_1001_1011_0111_1010_0101
    

    Than to read:
    %00011111010010011011011110100101
    

    The "DecPoint" method makes it easy to display scaled integers.

    I used a different I2C object. I used the one I was using in a different project since I already had it open in the Propeller Tool.

    I also used PST to display the data. Hopefully it won't take long to change it back to using a TV output.
  • Thanks Mike and Duane

    I figured some type of float object would have to be used but what threw me the most was the log10. I didn't think that would be in the float object as I'm not too familiar with that. Bad me for failing to look! Also, I took shop math in HS, 45 years ago.

    I'll spend some time w/ your code as soon as I can.

    I don't know who the author is but the object "I2C devices found" has helped me debug and check hardware more than a few times. Thanks to the author whoever you are! In this case I had 2 devices on the board with the same address specified ($40) which instead should have been %100_0000 and %10_0000

    Aaron
  • AGCB: if you don't care about speed and want to save cog, the "Float Math Extended" spin library will drop right in for F32. It also has full trig and log support. FME.spin

    Marty
  • I'd like to use the actual temperature derived from the HTU21D. I have substituted this code to get fTemperature (to then be used in other formulas and the temperature seems to be close but then affects other methods.
    ftemperature:=F32.FSub(F32.FDiv(F32.FMul(F32.FFloat(I2C.ReadWordB(HTU21D_ADDRESS,$E3)){
        },175.72),65536.0),46.85)
        fTemperature:= F32.FAdd(F32.FMul(F32.FDiv(9.0,5.0),fTemperature),32.0) ' degrees F
    

    Aaron

  • fTemperature:= F32.FAdd(F32.FMul(F32.FDiv(9.0,5.0),fTemperature),32.0) ' degrees F[/code]


    You seem to have a typo
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2015-12-10 15:02
    Dew point has a lot of interesting practical uses.

    A. House painting - cross the dew point and the house you are painting may turn into a nightmare.

    B. Packaging of iron products for shipping -- Passing the dew point after packaging and everything might arrive rusty.

    C. Shipment of orchids over long-distance -- Crossing the dew point after placed in a cargo container many ruin the whole shipment in transist. Orchids love high humidity and warmth, but get brown spots from water droplets on their surface.

    D. Food packaging -- Too much humidity while packaging, and the food quality is stale when opened.

    E. Spray paint your car in a controlled environment and moving it outside may cross the dew point and ruin the paint work.

    +++++++++
    I've actually found that 26 to 27 degrees centigrade is optimal air conditioning for me as the humidity content of the air remains low enough not to cause slim molds to grow on my walls, while high enough to save a bit of money. I grew up with the idea that 20 degrees centigrade was optimal.
  • AGCB wrote: »
    I'd like to use the actual temperature derived from the HTU21D. I have substituted this code to get fTemperature (to then be used in other formulas and the temperature seems to be close but then affects other methods.

    What sort of units does the temperature need to be in in order to use it in the partial pressure calculation?

  • Duane Degn wrote: »

    What sort of units does the temperature need to be in in order to use it in the partial pressure calculation?

    All calculations in the datasheet are done with Celcius, so it it is likely that the A, B & C constants are adjusted for Celcius. I would to the conversion for dew point to Fahrenheit afterwards and use ftemperature (small t) for the calculations and fTemperature (capital T) for display only
  • I never considered that converting to degrees F before using it in other calculations would affect it. The output of the sensor is Celsius, 14 bits. I'll rewrite that.
    use ftemperature (small t) for the calculations and fTemperature (capital T) for display only

    Isn't SPIN case insensitive?
  • AGCB wrote: »
    I never considered that converting to degrees F before using it in other calculations would affect it. The output of the sensor is Celsius, 14 bits. I'll rewrite that.
    use ftemperature (small t) for the calculations and fTemperature (capital T) for display only

    Isn't SPIN case insensitive?

    Yes, it is case insensitive. Even if it weren't, it would still be a bad idea to have two variables whose names differ only in case because it would confuse people.

  • Yes, it is case insensitive. Even if it weren't, it would still be a bad idea to have two variables whose names differ only in case because it would confuse people.

    I keep forgotting that :(, have not used Spin in awhile and too many other languages in between
  • Mike Green wrote: »
    I did a quick conversion of the printed formulas. Carefully double check my work:

    I had missed Mike Green's earlier post. (I'm still trying to figure out how I didn't see it sooner.)

    I hope you compare my equation against what Mike wrote.



  • I'm somewhat closer to getting what I'm after but not there yet>

    I've attached both the current code and a screenshot of the PST
    CON
    
      _clkmode = xtal1 + pll16x                                                      
      _xinfreq = 5_000_000
    
        'I2C pins
      SCL_PIN  =  20
      SDA_PIN  =  21
    
      BITRATE = 100_000  'I2C @ 100k
       
      HTU21D_ADDRESS = $40 ' 7-bit I2C address '%100_0000
    
      DEW_POINT_A = 8.1332
      DEW_POINT_B = 1762.39
      DEW_POINT_C = 235.66
    
      'DEFAULT_TEMPERATURE = 22
      
      BAUD = 115_200
      
    OBJ
      
      I2c : "I2C PASM driver v1.8od"  
      F32 : "Float32"
      Pst : "Parallax Serial Terminal"
      Format : "StrFmt"
        
    PUB Main | rawHumidity, ftemperature, relativeHumidity, fRelativeHumidity, fPartialPressure, {
    } fDewPoint, DegFar, temperature, rawTemperature
    
      'temperature := DEFAULT_TEMPERATURE
      
      Pst.Start(BAUD)
      
      repeat
        result := Pst.RxCount
        Pst.Str(string(11, 13, "Press any key to start."))
        waitcnt(clkfreq / 4 + cnt)
      until result
       
      Pst.RxFlush
       
      Pst.Clear
    
      F32.Start
        
      I2C.Start(SCL_PIN, SDA_PIN, BITRATE)
      
      Pst.Str(string("user register bits =  "))
      I2c.Command(HTU21D_ADDRESS, $E7)
      ReadableBin(I2C.ReadNext(HTU21D_ADDRESS), 8, 4)
    
      waitcnt(clkfreq / 2 + cnt)
    
      repeat
        Pst.ClearEnd
        Pst.NewLine
        Pst.ClearBelow
        Pst.Home
        Pst.Str(string(11, 13, "HTU21D Dew Point Demo"))
        Pst.Str(string(11, 13, "msb + lsb (rawHumidity) = "))
        rawHumidity := I2C.readWordB(HTU21D_ADDRESS, $E5)
        Pst.Dec(rawHumidity)
        Pst.Str(string(" = "))
        ReadableBin(rawHumidity, 16, 4)
        pst.str(string(13,13))
        
        Pst.Str(string(11, 13, "msb + lsb (rawTemperature) = ")) 
        rawTemperature := I2C.readWordB(HTU21D_ADDRESS,$E3)
        Pst.dec(rawTemperature)
        Pst.Str(string(" = "))
        ReadableBin(rawTemperature, 16, 4)
        temperature:=F32.FSub(F32.FDiv(F32.FMul(F32.FFloat(rawTemperature),{
        }175.72),65536.0),46.85)
        pst.str(string(13,13,"Centigrade temperature = "))
        DisplayFloat(temperature,2)
        
         
        relativeHumidity := RawToRhInteger(rawHumidity)
        fRelativeHumidity := RawToRhFloat(rawHumidity)
        Pst.Str(string(11, 13,13, "relativeHumidity Integer Math = "))
        Pst.Dec(relativeHumidity)
        Pst.Str(string(11, 13,13, "relativeHumidity Floating Point Math = "))
        DisplayFloat(fRelativeHumidity, 2)
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
          'Get temperature                                                       *****
        Pst.Str(string(11, 13,13, "temperature °F calculated = "))
        
        
        DegFar:= F32.FAdd(F32.FMul(F32.FDiv(9.0,5.0),temperature),32.0)             
        fTemperature := F32.FFloat(temperature)
        displayFloat(DegFar,2)
    ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''    
    
        fPartialPressure := GetPartialPressure(Temperature)
        
        Pst.Str(string(11, 13,13, "Partial Pressure = "))
        DisplayFloat(fPartialPressure, 2)
    
        fDewPoint := GetDewPoint(fRelativeHumidity, fPartialPressure)
        Pst.Str(string(11, 13, "Dew Point = "))
        DisplayFloat(fDewPoint, 2)
    
        waitcnt(clkfreq + cnt)
    
    PUB GetDewPoint(fRelativeHumidity, fPartialPressure)
    
      result := F32.FNeg(F32.FAdd(F32.FDiv(DEW_POINT_B, F32.FSub(F32.Log10(F32.FMul( {
      } fRelativeHumidity, F32.FDiv(fPartialPressure, 100.0))), DEW_POINT_A)), DEW_POINT_C))
    
    PUB GetPartialPressure(temperature) | fTemperature
    
      fTemperature := F32.FFloat(temperature)
    
      result := F32.Exp10(F32.FSub(DEW_POINT_A, F32.FDiv(DEW_POINT_B, F32.FAdd(fTemperature, {
      } DEW_POINT_C))))
      
    PUB RawToRhInteger(rawRhValue)
    
      result := (rawRhValue * 125 / 65536) - 6
    
    PUB RawToRhFloat(rawRhValue)
      
      result := F32.FSub(F32.FDiv(F32.FMul(F32.FFloat(rawRhValue), 125.0), 65536.0), 6.0)
                        ''''     '''      ''          '         '       ''       '''   ''''
    
    PRI DisplayFloat(fValue, decimalPlaces) | fScale
    
      fScale := F32.Exp10(F32.FFloat(decimalPlaces))
      result := RoundScaled(fValue, fScale)
      DecPoint(result, decimalPlaces)
      
    PRI RoundScaled(fValue, fScale)
    
      result := F32.FRound(F32.FMul(fValue, fScale))
      
    PRI DecPoint(value, places) | tempBuffer[4]
    
      result := Format.FDec(@tempBuffer, value, 9, places)
      byte[result] := 0
      Pst.Str(@tempBuffer)
    
    PUB ReadableBin(localValue, size, groupSize) | bufferPtr, localBuffer[12]    
    '' This method display binary numbers
    '' in easier to read groups.
    
      bufferPtr := Format.Ch(@localBuffer, "%")
      
      result := size // groupSize
       
      if result
        size -= result
        bufferPtr := Format.Bin(bufferPtr, localValue >> size, result)
        if size
          bufferPtr := Format.Ch(bufferPtr, "_")
          
      size -= groupSize
     
      repeat while size => 0
        'Debug.Bin(localValue >> size, 4)
        bufferPtr := Format.Bin(bufferPtr, localValue >> size, groupSize)
        if size
          bufferPtr := Format.Ch(bufferPtr, "_")  
        size -= groupSize
        
      byte[bufferPtr] := 0  
      Pst.Str(@localBuffer)
    

    THIS is the PST screen
    HTU21D Dew Point Demo
    msb + lsb (rawHumidity) = 48886 = %1011_1110_1111_0110


    msb + lsb (rawTemperature) = 20176 = %0100_1110_1101_0000

    Centigrade temperature = 7.25

    relativeHumidity Integer Math = 87

    relativeHumidity Floating Point Math = 87.24

    temperature °F calculated = 45.05

    Partial Pressure = 0.03
    Dew Point = 29498.85

    The partial pressure never changes and I don't know what it should be.
    The dew point is what's way off. Everything else is close. I have the unit sitting in a window sill on a 40 degree F rainy day
  • The variable "temperature" in the "Main" method is encoded as a floating point value.
        temperature:=F32.FSub(F32.FDiv(F32.FMul(F32.FFloat(rawTemperature),{
        }175.72),65536.0),46.85)
    

    The method "GetPartialPressure" expects the temperature to be an integer.
    PUB GetPartialPressure(temperature) | fTemperature
    
      fTemperature := F32.FFloat(temperature)
    
      result := F32.Exp10(F32.FSub(DEW_POINT_A, F32.FDiv(DEW_POINT_B, F32.FAdd(fTemperature, {
      } DEW_POINT_C))))
    

    When the method uses the "FFloat" method on a value which is already encoded as a float the resulting value is not a useful value.

    The first thing to try is to change the "GetPartialPressure" method to:
    PUB GetPartialPressure(fTemperature) 
    
      result := F32.Exp10(F32.FSub(DEW_POINT_A, F32.FDiv(DEW_POINT_B, F32.FAdd(fTemperature, {
      } DEW_POINT_C))))
    
  • Thanks Duane

    Seems to me I had commented out that line several times but with the unit in the house it was probably enough to give some strange unexpected readings. Now with it sitting on the window sill, the temperature is about 3 degrees above my other outdoor thermometer. I would expect the dew point to be 4 - 7 degrees F below the temperature because there is no fog but it's not too far from it. It's reading at about -2 degrees C.

    The local airport (3 miles away) shows 42 degrees F(5.6C), 4.4 degrees C dew point, and 96% RH but no fog.

    So it's in the ball park but probably just not accurate enough.
    Here's the latest PST screen
    HTU21D Dew Point Demo
    msb + lsb (rawHumidity) = 49018 = %1011_1111_0111_1010


    msb + lsb (rawTemperature) = 19928 = %0100_1101_1101_1000

    Centigrade temperature = 6.58

    relativeHumidity Integer Math = 87

    relativeHumidity Floating Point Math = 87.49

    temperature °F calculated = 43.85

    Partial Pressure = 4.52
    Dew Point = -0.14°C

    And here's the airport METAR
    METAR for: KIMT (Iron Mountain/Ford F, MI, US)
    Text: KIMT 140108Z AUTO 35008KT 1 1/2SM -RA BR BKN004 OVC008 06/04 A2960 RMK AO2 P0000 T00560044
    Temperature: 5.6°C ( 42°F)
    Dewpoint: 4.4°C ( 40°F) [RH = 92%]
    Pressure (altimeter): 29.60 inches Hg (1002.5 mb)
    Winds: from the N (350 degrees) at 9 MPH (8 knots; 4.1 m/s)
    Visibility: 1.50 sm ( 2.41 km)
    Ceiling: 400 feet AGL
    Clouds: broken clouds at 400 feet AGL, overcast cloud deck at 800 feet AGL
    Weather: -RA BR (light rain, mist)
  • According to this site, the dew point should be about 4 C.

    I kind of wonder if you're getting rounding errors with all those floating point calculations. I don't see any obvious way around the problem.

    You ought to try printing out the raw data needed to make the calculation and punch the numbers in on a good calculator. This should let you know if the problem is in rounding errors or not.
Sign In or Register to comment.