Micro SD Data Logging
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
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


Comments
In any case, you'll probably want to add some commas ( sdfat.pputc(',') ) and newlines ( sdfat.pputc('\n') ) to make your code output CSV.
{ =============================================================================== } 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, 0One 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.
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.
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.