Shop OBEX P1 Docs P2 Docs Learn Events
Micro SD Data Logging — Parallax Forums

Micro SD Data Logging

aparis1983aparis1983 Posts: 22
edited 2014-06-14 06:39 in Robotics
My project is an altitude sensor displaying altitude above sea level in feet and temperature in Farenheit on an LCD display while storing the data into a micro SD card. It currently displays altitude and temperature on the LCD, but I cannot get it to log more than 1 line of the data into the excel file in the card. The project is for a high altitude balloon that will go upwards of 100,000 feet and come down after a little over 2 hours.

Components: Propeller BOE, Parallax MS5607 altitude sensor, and Parallax 2x16 serial LCD. Schematics provided below.

I've used snippets of code that were suggested when I asked about this project in the past. Currently I only get one line of data into the file. My results display altitude in cell A1 and temperature in B1, then the next altitude and temp reading in C1 and D1; so on all along row 1. However, it always stops at AO1 (about 21 pairs of readings), and never goes to row 2.

Ideally, I would like altitude readings to be recorded down column A and temperature readings down column B. Additionally, one reading per second would be more than sufficient as the balloon flight will likely exceed 2 hours (7,200 readings). The code I'm currently using is:

I would appreciate any help with the data logging portion.

CON
_clkmode = xtal1 + pll16x
_xinfreq = 5_000_000
sd_DO = 22
sd_CLK = 23
sd_DI = 24
sd_CS = 25
TX_PIN = 2
BAUD = 19_200
START_ALT = 20
OBJ
sdfat : "fsrw"
pst : "Parallax Serial Terminal"
LCD : "FullDuplexSerial.spin"
alt : "29124_altimeter"
fp : "FloatString"

PUB start | a , t , mount


LCD.start(TX_PIN, TX_PIN, 00, 19_200) ' Start Parallax FullDuplexSerial.
waitcnt(clkfreq / 100 + cnt) ' Pause for FullDuplexSerial.spin to initialize
mount := \sdfat.mount_explicit (sd_DO, sd_CLK, sd_DI, sd_CS)
LCD.tx(17)
if mount < 0
lcd.str( string( 13, "Failed to mount", 13 ) )
abort
lcd.str(string( 13, "SD card found & mounted", 13) )
alt.start(alt#QUICKSTART, alt#BACKGROUND) ' Start altimeter for QuickStart with background processing.
alt.set_resolution(alt#HIGHEST) ' Set to highest resolution.
alt.set_altitude(alt.m_from_ft(START_ALT * 100)) ' Set the starting altitude, based on average local pressure.
sdfat.popen( string("Data.csv"),"w")
repeat
a := alt.altitude(alt.average_press) ' Get the current altitude in cm
lcd.str(string("Alt:")) ' Print header.
lcd.str(alt.formatn(a, alt#TO_FEET,8)) ' Print altitude in feet.
sdfat.pputs(alt.formatn(a, alt#TO_FEET,8))
t := alt.current_temp ' Get the current temperature.
lcd.str(string("Temp:")) ' Print header.
lcd.str(alt.formatn(t, alt#TO_DEGF,8)) ' Print temperature in Farenheit.
sdfat.pputs(alt.formatn(t, alt#TO_DEGF,8))
LCD.tx(13)
sdfat.pclose
sdfat.unmount

diagram.jpg
600 x 900 - 108K

Comments

  • SRLMSRLM Posts: 5,045
    edited 2014-05-07 19:04
    You should use [ code ] tags to post your code. What you've posted does not include indentation, so it's impossible to tell what the control flow is.

    In any case, you'll probably want to add some commas ( sdfat.pputc(',') ) and newlines ( sdfat.pputc('\n') ) to make your code output CSV.
  • JLockeJLocke Posts: 354
    edited 2014-05-08 06:24
    Here's some code I used in a data logging project that writes temperature/humidity to a micro-SD card:
    {
    ===============================================================================
    }
    VAR
      byte NewLocTemp                 ' flag to indicate local temp changed
      byte NewDwnTemp                 ' flag to indicate downstairs temp changed
      byte NewOutTemp                 ' flag to indicate outside temp changed
      byte SettingRTC                 ' flag to indicate we're setting the date/time
      byte LockLCD                    ' flag to lock access to LCD
      byte Dow                        ' day of the week
      byte RcvChar                    ' Xbee received character
      byte XbData[BUFFSIZE]           ' Xbee receive buffer
      byte DownTemp[STRINGSIZE]       ' downstairs temperature
      byte OutTemp[STRINGSIZE]        ' outside temperature
      byte OutHumid[STRINGSIZE]       ' outside humidity
      byte FilName[FNAMESIZE]         ' filename buffer for SD
      byte FilData[DATASIZE]          ' line buffer for write to SD
      long DateTime                   ' current date/time in Unix format
      long LocTemp                    ' local temperature (F) from SHT11 (float)
      long LocHumid                   ' local humidity (% rh) from SHT11 (float)
              
      long stackTemps[130]            ' stack for temperature cog
      long stackRemote[170]           ' stack for communications cog
      long stackButton[30]            ' stack for button monitoring cog
      long stackLog[250]              ' stack for data logging cog
    
    {
    ===============================================================================
    }
    OBJ
      Comms    : "FullDuplexSerial4port"        ' for comm ports
      Rtc      : "DS1307_RTCEngine"             ' real time clock chip
      Sht      : "Sensirion_full"               ' SHT11 temperature/humidity chip
      Drv      : "fsrw"                         ' file system driver
      SimplNum : "Simple_Numbers"
      Flt      : "FloatMath"                    ' floating point support
      FltStr   : "FloatString"
      Tyme     : "jl_unix_time"                 ' date/time conversion
    
    {
    ===============================================================================
    }
    
    PUB cog_Log | crntDay, minit, locTime, yyyymmdd, hhmmss
      ''* writes current data to µSD on schedule (every 15 minutes)
      ''
      ' == cog initialization ==
      waitcnt((clkfreq * 2) + cnt)                   ' wait for date/time to be valid
      ByteFill(@FilName, 0, FNAMESIZE)               ' clear the filename buffer
      locTime := DateTime                            ' copy the global DateTime variable
      yyyymmdd := Tyme.date(locTime, 0)              ' decode it
      hhmmss   := Tyme.time(locTime, 0)
      
      Concatenate(@FilName, SimplNum.decx(yyyymmdd.word[1], 2)) ' build the filename
      Concatenate(@FilName, string("-"))
      Concatenate(@FilName, SimplNum.decx(yyyymmdd.byte[1], 2))  
      Concatenate(@FilName, string("-"))
      Concatenate(@FilName, SimplNum.decx(yyyymmdd.byte[0], 2))
      Concatenate(@FilName, string(".csv"))          ' save as '.csv' file
      Drv.mount(0)                                   ' mount the disk
      Drv.popen(@FilName, "a")                       ' open the file
      Drv.sdstr(string("=== REBOOT ===", 13))        ' add indicator line
      Drv.sdstr(@FileHeader)                         ' add header line
      Drv.sdstr(string(" ", 13))                     ' blank line
      Drv.pclose                                     ' flush and close the file  
    
      crntDay := yyyymmdd.byte[0]                    ' init the triggers
      minit   := hhmmss.byte[1]
    
      ' == cog loop ==
      repeat
        locTime := DateTime                        ' copy the global DateTime variable
        yyyymmdd := Tyme.date(locTime, 0)          ' decode it
        hhmmss   := Tyme.time(locTime, 0)
        
        if crntDay <> yyyymmdd.byte[0]             ' day changed?
          crntDay := yyyymmdd.byte[0]              ' copy the new value
          ByteFill(@FilName, 0, FNAMESIZE)         ' clear the filename buffer
    
          Concatenate(@FilName, SimplNum.decx(yyyymmdd.word[1], 2)) ' build the filename
          Concatenate(@FilName, string("-"))
          Concatenate(@FilName, SimplNum.decx(yyyymmdd.byte[1], 2))  
          Concatenate(@FilName, string("-"))
          Concatenate(@FilName, SimplNum.decx(yyyymmdd.byte[0], 2))
          Concatenate(@FilName, string(".csv"))    ' save as '.csv' file
    
          Drv.mount(0)                             ' mount the disk
          Drv.popen(@FilName, "a")                 ' open the file
          Drv.sdstr(string(" ", 13))               ' blank line
          Drv.sdstr(@FileHeader)                   ' add header line
          Drv.sdstr(string(" ", 13))               ' blank line
          Drv.pclose                               ' flush and close the file
    
        if minit <> hhmmss.byte[1]                 ' minute changed?
          minit := hhmmss.byte[1]                  ' copy the new value
    
          ifnot (minit // 15)                      ' every 15 minutes
            Drv.mount(0)                           ' mount the disk
            buildDataString                        ' assemble the log line
            Drv.popen(@FilName, "a")               ' open the file
            Drv.sdstr(@FilData)                    ' write data
            Drv.pclose                             ' flush and close the file
    
        waitcnt(clkfreq + cnt)                     ' wait 1 second
    
    {
    ===============================================================================
    }
    PUB buildDataString | locTime
      ''* builds the line to write to the µSD disk
      ''
      locTime := DateTime                            ' copy the global DateTime variable
      ByteFill(@FilData, 0, DATASIZE)                ' clear the buffer
      Concatenate(@FilData, Tyme.jl_date_str(locTime, 0))
      Concatenate(@FilData, string(","))
      Concatenate(@FilData, Tyme.jl_time24_str(locTime, 0))
      Concatenate(@FilData, string(","))
    
      Concatenate(@FilData, FltStr.FloatToFormat(LocTemp, 5, 1)) ' upstairs temp
      Concatenate(@FilData, string(","))
      Concatenate(@FilData, FltStr.FloatToFormat(LocHumid, 5, 1)) ' upstairs humidity
      Concatenate(@FilData, string(","))
      Concatenate(@FilData, @DownTemp)               ' downstairs temp
      Concatenate(@FilData, string(","))
      Concatenate(@FilData, @OutTemp)                ' outside temp
      Concatenate(@FilData, string(","))            
      Concatenate(@FilData, @OutHumid)               ' outside humidity
      Concatenate(@FilData, string(13))    
    
    {
    ===============================================================================
    }
    PUB Concatenate(whereToPut, whereToGet)
      '' Concatenates a string onto the end of another. This method can corrupt memory.
      '' Returns a pointer to the new string.
      ''  whereToPut - address of the string to concatenate a string to.
      ''  whereToGet - address of where to get the string to concatenate.
      ''
      bytemove((whereToPut + strsize(whereToPut)), whereToGet, (strsize(whereToGet) + 1))
      return whereToPut
    
    {
    ===============================================================================
    }
    DAT
      FileHeader  byte "Date,Time,Upstairs (F),Humidity (%),Downstairs (F),Outside (F),Humidity (%)", 13, 0
    
    

    One difference I notice is that in my code I always open the file with 'a'; not sure if that makes a difference here. Anyway, I hope this helps you out.
  • LevLev Posts: 182
    edited 2014-05-08 15:54
    The code I sent you a while ago works fine in high altitude ballooning. I have used it in 4 flights now and accumulated thousands of lines of data in a csv file. I haven't experienced any of the problems you describe so I suspect that there is something different in the way you implemented the code snippets I sent.

    I notice that you do not have a pause in the repeat loop (or, maybe you didn't send all of the code below the repeat). You might try slowing the loop down a bit. That would give some extra time for the card to mount/unmount. In mine, I wrote to the SD card every 10 seconds.
  • aparis1983aparis1983 Posts: 22
    edited 2014-05-22 20:16
    You were right!! I inserted a waitcnt(clkfreq * 5 + cnt), and I also added an sdfat.popen( string("Data.csv"),"a") to the loop so the file is continuously appended. Works perfect and is logging values every 5 seconds.

    Now the only thing I'm worried about is the several corrections needed for lower operational temperatures. I've read about this in the forum but not sure where to begin to include these corrections. At any rate, I have no time to work on it as the balloon is launching on Saturday. Hoping for the best.

    Thanks!
  • LevLev Posts: 182
    edited 2014-06-14 06:39
    aparis1983 wrote: »
    You were right!! I inserted a waitcnt(clkfreq * 5 + cnt), and I also added an sdfat.popen( string("Data.csv"),"a") to the loop so the file is continuously appended. Works perfect and is logging values every 5 seconds.

    Now the only thing I'm worried about is the several corrections needed for lower operational temperatures. I've read about this in the forum but not sure where to begin to include these corrections. At any rate, I have no time to work on it as the balloon is launching on Saturday. Hoping for the best.

    Thanks!

    That is great news! Let us know how the flight goes.
Sign In or Register to comment.