Shop OBEX P1 Docs P2 Docs Learn Events
1-wire Temp Sensor / Spin code Help — Parallax Forums

1-wire Temp Sensor / Spin code Help

WookieWookie Posts: 27
edited 2010-01-13 22:11 in Propeller 1
As some of you may have been following, I am working on reading in several 1-wire DS18B20 temperature sensors along with GPS data. I am on the home stretch here, but I am having trouble implementing one last feature. In short, the ugly code posted below takes in GPS data as well as temp sensor data and sends it out serially in a comma separated format to a PC. I have everything working except getting the three temperature sensor readings independently all on one line. In an effort to fix each sensor reading to one place in the output, I decided that I would enter the sensor serial codes as constants at the beginning of the program and define them as constants so that when I make the conversions, I am sure that my output data is properly ordered. The trouble is that with current code, I can't get the output data to display all three sensor data readings at once. Instead, the temperature reading for one sensor fills all the locations of the temp data for a few outputted lines before another sensor data rotates in place. This goes on and on like this:

.....,73.5, 73.5, 73.5
.....,73.5, 73.5, 73.5
.....,73.5, 73.5, 73.5
.....,80.0, 80.0, 80.0
.....,80.0, 80.0, 80.0
.....,80.0, 80.0, 80.0
.....,40.0. 40.0. 40.0
.....,40.0. 40.0. 40.0
.....,40.0. 40.0. 40.0

If I change each of the sensor serial codes to something non-matching, I drop that sensors reading in the loop, so I know that feature is working. I added several temp conversion routines at the end to try and avoid this, but no luck.

Could someone please look at my admittedly sloppy code and let me know what they think (I have reposted a cleaner version of the code later in this thread)? I have a deadline approaching on Monday, so REALLY appreciate any help I can get!

Thanks!!!!

Joe

Post Edited (Wookie) : 1/11/2010 6:14:56 AM GMT

Comments

  • GadgetmanGadgetman Posts: 2,436
    edited 2010-01-08 23:31
    Could you remove the spaces hiding in the 'Max devices' line in your code, so that we don't have to scroll halfway to Russia to read the rest of the post?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Don't visit my new website...
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-08 23:44
    Sorry I don't have time to take a more detailed look at this, but the first thing that jumps out at me is that you have 3 separate readTemperature Methods, when I think you need only one.
    Your Main program should call up the SAME readTemperature Method each time the Main needs to read a temperature from a new device.
    The way your program is working now, I think, is that each called up Method of readTemperature is starting the sensor read process all over again and thus your system is seeing only the first/same device on the line but doing so 3 times in a row.

    I hope that helps.


    smile.gif
  • WookieWookie Posts: 27
    edited 2010-01-08 23:58
    ElectricAye said...
    Sorry I don't have time to take a more detailed look at this, but the first thing that jumps out at me is that you have 3 separate readTemperature Methods, when I think you need only one.
    Your Main program should call up the SAME readTemperature Method each time the Main needs to read a temperature from a new device.
    The way your program is working now, I think, is that each called up Method of readTemperature is starting the sensor read process all over again
    and thus your system is seeing only the first/same device on the line but doing so 3 times in a row.

    I hope that helps.


    smile.gif

    I added the 3 separate read statements, as I was trying to generate 3 separate output variables. I had everything on a single read statement, but the problem still exists.
    I figured it didnt matter though, as I needed to call the routine 3 separate times anyway to read the 1-wire bus for each sensor.
    Of course, I may be missing something with the read statement operation as you suggest.

    Maybe the easier thing to do here is to ask if anyone has a routine or suggestion as to how to read multiple devices on a 1-wire network that will generate 3 variables that I can
    call on to convert to degrees F and then post them as a string to my CSV output statement.
  • WookieWookie Posts: 27
    edited 2010-01-08 23:59
    Gadgetman said...
    Could you remove the spaces hiding in the 'Max devices' line in your code, so that we don't have to scroll halfway to Russia to read the rest of the post?

    I think I got those hidden spaces.
    I guess my space key got stuck sometime during my coding.....sorry.
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-09 02:18
    Wookie,

    take a look at Micah Dowty's SPIN version of the 1-wire routine.

    obex.parallax.com/objects/342/
    If I remember correctly, this has a demo in it that reads the temperature on up to 8 DS18B20s on a single wire and will display it on a VGA or TV.
    I think it shows how to do exactly what you need.
    Sorry, but I'm away from the computer that I have my own code on, otherwise I'd post it for you, but my own stuff was derived almost verbatim from Dowty's code.

    I hope that helps,
    Mark
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-10 23:42
    Wookie,



    I've tried to paste the relevant chunks of code that demonstrate reading multiple DS18B20's on one wire.

    This stuff was extracted from a giant dirty snow ball of code I use in one of my programs, so it's very possible I've left out something.

    Hopefully, it might help you figure out how to read all of your DS18B20's.


    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000 
    
       
    
    {***Set the max number of DS18B20's you will read...}
    MAX_DEVICES = 8 
    
    
    





    OBJ
    {Object for DS18B20 Digital Thermometer...}
    ow      : "SpinOneWire" 
    
    f           : "FloatMath" {Instance of FloatMath used by the Main program.}
    fTemp       : "FloatMath" {Instance of FloatMath used by the method ReadTemperature.} 
    
    
     
    
    
    



    {Create an array used for reading the DS18B20 digital thermometers...}
    LONG addrs[noparse][[/noparse]2*MAX_DEVICES] 'This array contains the DS18B20 ROM laser addresses. 
    
    
    {Declare other variables used for reading the DS18B20 digital thermometers...}
    LONG i, numDevices, addr 
    
    





    PUB Main  
    
      
    
    {Prep Pin 26, the data line for One Wire operation of the DS18B20s.}
    ow.start(26) 
    
    




    REPEAT 
    
    
                 {Check the 1-Wire line to see how many DS18B20s there are...} 
               numDevices := ow.search(ow#REQUIRE_CRC, MAX_DEVICES, @addrs)
                   
               REPEAT i from 0 to MAX_DEVICES-1
                   addr := @addrs + (i << 3) 'Load relevant part of DS18B20 laser ROM address into addr.
                  {***MAKE SURE YOUR IF STATEMENTS LINE UP PROPERLY***}
                   {Measure Temperature 0 = the on-board PCB-mounted DS18B20....}     
                   IF Long[noparse][[/noparse]addr+4] == $84000002
                        {If the value at the address (offset by 4 bytes to get to the relevant portion of
                          the 64-bit ROM code) matches the first 32 bits
                          of the laser ROM name of the DS18B20, then read the sensor and put the value
                          into the proper variable of the TempArray...}
                          TempArray[noparse][[/noparse]0] := ReadTemperature(addr)
                   {Measure Temperature 1 = PMT compartment....}
                   IF Long[noparse][[/noparse]addr+4] == $57000001
                          TempArray := ReadTemperature(addr)       
                          
                   {Measure Temperature 2....}
                   IF Long[noparse][[/noparse]addr+4] == $51000001
                          TempArray := ReadTemperature(addr)
    
    



    PRI ReadTemperature(ThermalAddress) | temperature, degC, degF, WatchDogDS18b20
      {Read the temperature from a DS18B20 sensor.}
      {Waits up to 10 seconds for the sensor to provide data.} 
    
      ow.reset
      ow.writeByte(ow#MATCH_ROM)
      ow.writeAddress(ThermalAddress) 
    
      ow.writeByte(ow#CONVERT_T) 
    
      {Reset WatchDogDS18b20 to zero...}
      WatchDogDS18b20 := 0 
    
      repeat {Repeat until the called sensor tells us it is ready to give data...}
        waitcnt(clkfreq/100 + cnt) 
    
        {Increment the watch dog on every pass and if the wait for a sensor exceeds
          about 1000 iterations, then there is a problem, therefore return to main program.}
        WatchDogDS18b20 := (WatchDogDS18b20 + 1)
          IF WatchDogDS18b20 > 1000 {About 10 seconds.}
            {Consider lighting up a warning LED.}
             return 
    
        IF ow.readBits(1) 'The sensor is ready to provide data.
          ow.reset 'Say okay and prep the data line.
          ow.writeByte(ow#MATCH_ROM) 'Tell the sensor you want to match a ROM
          ow.writeAddress(ThermalAddress) 'Send out the ROM to match. 
    
          ow.writeByte(ow#READ_SCRATCHPAD) 'Open up the sensor's scratchpad.
          temperature := ow.readBits(16) 'Read the data. 
    
          temperature := ~~temperature  {Modified 9 Dec 2008 to provide negative temps.}   
    
          ' Convert from fixed point to floating point
          degC := fTemp.FDiv(fTemp.FFloat(temperature), 16.0) 
    
          ' Convert Celsius to Fahrenheit
          degF := fTemp.FAdd(fTemp.FMul(degC, 1.8), 32.0) 
    
          result := degF  {The result is the temperature in degrees F.} 
    
          return 'Return to the main program... 
    
    {*** End of method ReadTemperature ***} 
    
    
    

    Post Edited (ElectricAye) : 1/10/2010 11:52:09 PM GMT
  • WookieWookie Posts: 27
    edited 2010-01-11 04:24
    ElectricAye,


    First off, thank you for taking the time to post this code up for me. I see how you are reading in the data from the sensors, and am generally doing the same thing as you. For whatever reason, I keep getting other sensor data bleeding into all my declared variables. As I watch the terminal program,
    the sensor data still writes as such...

    .....,73.5, 73.5, 73.5
    .....,73.5, 73.5, 73.5
    .....,73.5, 73.5, 73.5
    .....,80.0, 80.0, 80.0
    .....,80.0, 80.0, 80.0
    .....,80.0, 80.0, 80.0
    .....,40.0. 40.0. 40.0
    .....,40.0. 40.0. 40.0
    .....,40.0. 40.0. 40.0

    If I change a single constant containing a sensor serial number so that it doesnt match, the output data will then rotate on the remaining two sensors. If I change all but one sensor to a non-matching serial number, I then get the data for the remaining sensor in the exact place specified in output dataset.

    An example is if the temp for air is 80F and the location for the air temp data is in the last position of the output string, my output will look like this:
    ,,80
    If I then change the serial number in the constants to match another sensor, say the water temp and that is reading 100F and is supposed to show up in the second to last location of the output string
    ,100,

    This confirms several things to me. I am correctly identifying each sensor by serial number, and that when each sensor is queried individually, the data shows up at the correctly defined variable location.

    I am totally out of ideas at this point. I have cleaned up the code and will post it below. If anyone sees an error, please let me know. Thanks!!!

    CON
      _clkmode = xtal1 + pll16x
      _xinfreq = 5_000_000
    
      MAX_DEVICES = 3                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
    
         ' 1-wire family codes
      AirT      = $8F917A28
     ' AirT      = $8F000000   Fake ID Number for Test
      WaterT    = $8F6E6328
      'WaterT    = $8F600000   Fake ID Number for Test
      FuelT     = $8F9ACB28 
     ' FuelT     = $8F9A0000   Fake ID Number for Test
    OBJ
      debug   : "pc_text"
      ow      : "SpinOneWire"
      f       : "FloatMath"
      fp      : "FloatString"
      gps     : "GPS_IO_mini2"
      
    VAR
      long addrs[noparse][[/noparse]2 * MAX_DEVICES]
      Long Stack0[noparse][[/noparse]200]
      Long Stack1[noparse][[/noparse]200]   
      Long degFAir [noparse][[/noparse]50]
      Long degFWater [noparse][[/noparse]50]
      Long degFFuel [noparse][[/noparse]50]
      Long RAirT [noparse][[/noparse]500]
      Long RFuelT [noparse][[/noparse]500]
      Long RWaterT [noparse][[/noparse]500]
    
    
    PUB start 
    
    CogNew (GPSread, @Stack0)
    CogNew (OnewireData, @Stack1)
     
    PRI GPSread | i, numDevices, addr, main, gmt
    
      gps.start
      debug.start(12) 
    
      repeat
           'debug.str(string(02))
           debug.str((string(" ASTA 00 ")))
          debug.str(gps.satellites)
          debug.str((string(",")))
          debug.str(gps.valid)
          debug.str((string(",")))
          'debug.str((string("Latitude")))
          debug.str(gps.latitude)
          debug.str((string(",")))  
          'debug.str((string("Longitude")))
          debug.str(gps.longitude)
          debug.str((string(",")))
          'debug.str((string("GPS_Altitude")))
          debug.str(gps.GPSaltitude)
          debug.str((string(",")))      
          'debug.str((string("GPS_Speed")))
          debug.str(gps.speed)
          debug.str((string(",")))     
          'debug.str((string("Time_GMT")))                                                                                                                                                                                                                                                                                                                                                    
          debug.str(gps.time)
          debug.str((string(",")))      
          'debug.str((string("Date")))
          debug.str(gps.date)
          debug.str((string(",")))
          'debug.str((string("Air Temp ")))
          debug.str(RAirT)
                debug.str((string(",")))
          'debug.str((string("Water Temp ")))
          debug.str(RWaterT)
          debug.str((string(",")))
          'debug.str((string(",Fuel Temp ")))
          debug.str(RFuelT)
          'debug.str(string(03))
          debug.str(string(13))
    
    PUB OnewireData | i, numDevices, addr
    
         ow.start(24)
    
      repeat
        numDevices := ow.search(ow#REQUIRE_CRC, MAX_DEVICES, @addrs)
        repeat i from 0 to MAX_DEVICES-1
    
            addr := @addrs + (i << 3)
    
          if long [noparse][[/noparse]addr] == AirT
              RAirT := readTemperature1(addr)
             
            
          if long [noparse][[/noparse]addr] == WaterT
               RWaterT := readTemperature1(addr)
    
          if long [noparse][[/noparse]addr] == FuelT
              RFuelT := readTemperature1(addr)
    
    PRI readTemperature1(addr) | temp, degC, degF
      '' Read the temperature from a DS18B20 sensor, and display it.
      '' Blocks while the conversion is taking place.
    
      ow.reset
      ow.writeByte(ow#MATCH_ROM)
      ow.writeAddress(addr)
    
      ow.writeByte(ow#CONVERT_T)
    
      ' Wait for the conversion
      repeat
        waitcnt(clkfreq/100 + cnt)
    
        if ow.readBits(1)
          ' Have a reading! Read it from the scratchpad.
    
          ow.reset
          ow.writeByte(ow#MATCH_ROM)
          ow.writeAddress(addr)
    
          ow.writeByte(ow#READ_SCRATCHPAD)
          temp := ow.readBits(16)
    
          ' Convert from fixed point to floating point
          degC := f.FDiv(f.FFloat(temp), 16.0)
    
          ' Convert celsius to fahrenheit
          degF := f.FAdd(f.FMul(degC, 1.8), 32.0)
          'result := degF  {The result is the temperature in degrees F.}
    
          fp.SetPrecision(3)
          Result := (fp.FloatToString(degF))
           
          return  
    
    
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-11 13:52
    Wookie,

    I'm not sure, but maybe this:

    if long[noparse][[/noparse]addr] == AirT
           RAirT := readTemperature1(addr)
    
    



    should be this:

    if long[noparse][[/noparse]addr+4] == AirT
         RAirT := readTemperature1(addr)
    
    



    I think it might need that +4 to indicate the proper chunk of the LONG it is looking for.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Watching the world pass me by, one photon at a time.

    Post Edited (ElectricAye) : 1/11/2010 1:57:08 PM GMT
  • WookieWookie Posts: 27
    edited 2010-01-12 20:57
    I have tried adding the [noparse][[/noparse]addr +4] to the code and still, no luck. ElectricAye, if you or anyone else has any other ideas, please let me know. I am totally out of ideas at this point.
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-12 22:47
    Wookie,

    I wish I were better at programming and could pinpoint this problem for you but so far the only odd thing I see is that you are using the same local variables in this:

    PRI GPSread | i, numDevices, addr, main, gmt
    
    



    as you are in this:

    PUB OnewireData | i, numDevices, addr
    
    
    



    I don't know if that could create some kind of memory problem or not.

    I must admit, I'm baffled, too.

    shocked.gif
  • ElectricAyeElectricAye Posts: 4,561
    edited 2010-01-13 00:52
    Wookie said...
    I have tried adding the [noparse][[/noparse]addr +4] to the code and still, no luck. ElectricAye, if you or anyone else has any other ideas, please let me know. I am totally out of ideas at this point.

    Wookie,

    the only other thing I can suggest at this point is to place Debug statements every once in a while and insert some fake data, fake temperature readings, etc. and try to sniff out where the program is taking a wrong turn.

    Sorry I can't help you more than that.

    Maybe one of the Forum software gurus can jump on this and take a look???

    blush.gif

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Watching the world pass me by, one photon at a time.
  • WookieWookie Posts: 27
    edited 2010-01-13 19:34
    ElectricEye,

    Ill give your idea about inserting dummy variables along the way. In either case, I wanted to thank you for all of your help over the last couple of days! I really appreciate it!
  • pgbpsupgbpsu Posts: 460
    edited 2010-01-13 22:11
    Wookie-

    I may be wrong but it seem to me the
    if long[noparse][[/noparse]addr] == AirT
           RAirT := readTemperature1(addr)
    
    



    may not always get the correct sensor. In your case this may not be the problem because for the devices on the bus none of the upper or lower halves
    of the 64-bit address match.

    AirT = $8F917A28
    WaterT = $8F6E6328
    FuelT = $8F9ACB28

    But it seems to me to be safe (and to rule this possible problem out) you need to check the upper and lower halves of the address against AirT, WaterT, FuelT.
    I'm not sure about this. It's possible that your code above compares 8 bytes but when something is called as a long (as in long[noparse][[/noparse]addr]) I would suspect it only
    checks 4 bytes. You may need to split up your constants into AirT_Upper=$8F91 and AirT_Lower=$7A28 then check against both before reading the temp:

    if ( (long[noparse][[/noparse]@addr][noparse][[/noparse]0] == AirT_Upper ) AND (long[noparse][[/noparse]@addr] == AirT_Lower) )
    RAirT := readTemperature1(addr)

    I'm not sure this is your problem or if what I've written is even correct, but it might be worth some consideration if you haven't found the problem.

    Best of luck.
    Peter
Sign In or Register to comment.