Shop OBEX P1 Docs P2 Docs Learn Events
Spin Function Calls with multiple arguments — Parallax Forums

Spin Function Calls with multiple arguments

greyfin10greyfin10 Posts: 7
edited 2014-06-18 13:39 in Learn with BlocklyProp
The fact that I can't find anyone who has asked this question suggests I'm barking up the wrong tree, but considering how new I am to the environment, I thought it worth asking (in case it's a common error).

I have created a function to handle writes to my microSD card (Activity Board, using the SD-MMC_FATEngine w/o RTC... though I need to add one). I had enountered some difficulties with corruption of my disk, so I decided that, for every write, I'd mount the disk system, create the file (in case of a card format/no file yet situation), open the file in append mode, then write several arguments to the log (temperature, humidity, and dewpoint). I only mention this in case it ends up giving a clue. I also discovered no good way to append data into a string variable (which is why I pass three arguments instead of one concatenated string).

So, here's what my function looks like. I'm pretty sure my error is in the function, since I use the same data items afterwards to send to an XBee Stick, and it shows up correctly there. The behavior I'm seeing is, no matter what order I arrange the parameters in, the last one in my parameter list ends up being copied over the other two. It's almost as if I'm only allowed one variable (which I know isn't true, since I have some two variable functions in my solution that work fine). Again, this is probably (hopefully) something dumb I'm overlooking... but in case it's a misunderstanding of Spin, I thought I'd ask (been looking at this function for an hour or so and I can't find the issue).
PUB write_temp(fileID, percHum, fahrDP, fahrTemp) | errorString, errorNumber
  fat.mountPartition(0)                                 ' Mount the file system

  errorString := \fat.newFile(fileID)                  ' Initial file creation (will give error if exists... we'll ignore this, since we know this is a valid use-case)
  errorNumber :=  fat.partitionError                   ' Trap any errors (we only care if it's NOT file already exists)

  if(errorNumber)                                      ' Try to handle the "entry_already_exist" error.
    if(errorNumber <> fat#Entry_Already_Exist)
      return errorNumber

  fat.openFile(fileID, "A")                            ' Open tempdata.txt for appending                                                                                                 
  fat.writeString(fahrTemp)                            ' Write data to microSD Card
  fat.writeString(string("|"))
  fat.writeString(percHum)                             ' Write data to microSD Card 
  fat.writeString(string("|"))
  fat.writeString(fahrDP)                              ' Write data to microSD Card 
  fat.writeString(string(13, 10))                      ' Write newline
  fat.closeFile                                        ' Close file
  fat.unmountPartition

Comments

  • greyfin10greyfin10 Posts: 7
    edited 2014-06-09 18:04
    The call to the function (the comment lists the original parameter order... the actual order was rearranged to try and localize the issue.
    ' Function to write temperature (in F), Humidity (in %), and Dewpoint (in F) to the log file
        if write_temp(string("temp_data.txt"),fp.FloatToFormat(percHum, 5, 1),fp.FloatToFormat(fahrDP, 5, 1),fp.FloatToFormat(fahrTemp, 5, 1))
          XB.str(string("error writing to file"))
    
  • kuronekokuroneko Posts: 3,623
    edited 2014-06-09 18:11
    greyfin10 wrote: »
    The call to the function (the comment lists the original parameter order... the actual order was rearranged to try and localize the issue.
    ' Function to write temperature (in F), Humidity (in %), and Dewpoint (in F) to the log file
        if write_temp(string("temp_data.txt"),fp.FloatToFormat(percHum, 5, 1),fp.FloatToFormat(fahrDP, 5, 1),fp.FloatToFormat(fahrTemp, 5, 1))
          XB.str(string("error writing to file"))
    
    That's the important bit here. FloatToFormat uses an object internal buffer and returns a reference to said buffer. By doing three calls you effectively invalidate the first two. To sidestep this issue you'd have to copy the strings to private buffers or - better - just pass the values directly and do the conversion before you write e.g. fat.writeString(fp.FloatToFormat(fahrTemp, 5, 1)).
  • greyfin10greyfin10 Posts: 7
    edited 2014-06-09 18:12
    That matches what I'm seeing exactly. I guess that'll teach me to actually read how these object libraries work instead of blindly plugging them in. Thank you!
  • greyfin10greyfin10 Posts: 7
    edited 2014-06-09 19:08
    Well, the simple fix as described seems to impact the behavior not at all... and the more I look at it, the new format (running the format function within the file write function) is very analogous to how my block that writes to the XBee works... and yet the XBee block works and the write_temp function doesn't. Also, if I seem to be missing letters, I suspect my XStick is interfering with my wireless keyboard!!

    Modified function (as I understood it):
    PUB write_temp(fileID, percHum, fahrDP, fahrTemp) | errorString, errorNumber
      fat.mountPartition(0)                                ' Mount the file system
    
      errorString := \fat.newFile(fileID)                  ' Initial file creation (will give error if exists... we'll ignore this)
      errorNumber :=  fat.partitionError                   ' Trap any errors
      if(errorNumber)                                      ' Try to handle the "entry_already_exist" error.
        if(errorNumber <> fat#Entry_Already_Exist)
          return errorNumber
      fat.openFile(fileID, "A")                            ' Open tempdata.txt for appending                                                                                                 
      fat.writeString(fp.FloatToFormat(fahrTemp, 5, 1))    ' Write data to microSD Card
      fat.writeString(string("|"))
      fat.writeString(fp.FloatToFormat(percHum, 5, 1))     ' Write data to microSD Card 
      fat.writeString(string("|"))
      fat.writeString(fp.FloatToFormat(fahrDP, 5, 1))      ' Write data to microSD Card 
      fat.writeString(string(13, 10))                      ' Write newline
      fat.closeFile                                        ' Close tempdata.txt   
      fat.unmountPartition
    

    The XBee tranmission block (has been working as expected since I first wrote it):
        XB.start(XB_Rx, XB_Tx, 0, XB_Baud)                    ' Initialize comms for XBee 
        
        {{.... the rest of the program, clipped out for clarity... }}
        
        XB.str(fp.FloatToFormat(fahrTemp, 5, 1))            ' Send the temperature string we just logged to our XBee terminal
        XB.str(string("|"))                                 ' Send Pipe delimiter
        XB.str(fp.FloatToFormat(percHum, 5, 1))             ' Send the temperature string we just logged to our XBee terminal
        XB.str(string("|"))                                 ' Send Pipe delimiter
        XB.str(fp.FloatToFormat(fahrDP, 5, 1))              ' Send the temperature string we just logged to our XBee terminal      
        XB.Tx(CR)
    
  • kuronekokuroneko Posts: 3,623
    edited 2014-06-09 19:32
    Beats me. This - similar - version does work for me:
    CON
      _clkmode = XTAL1|PLL16X
      _xinfreq = 5_000_000
    
    OBJ
      dbg: "FullDuplexSerial"
       fp: "FloatString"
       
    PUB null
    
      dbg.start(31, 30, %0000, 115200)
      waitcnt(clkfreq*3 + cnt)
      dbg.tx(0)
    
      write_temp(string("temp.dat", 13), 93.4, 4.5, 2.3)
      
    PUB write_temp(fileID, percHum, fahrDP, fahrTemp)
    
      dbg.str(fileID)
      dbg.str(fp.FloatToFormat(fahrTemp, 5, 1))
      dbg.str(string("|"))
      dbg.str(fp.FloatToFormat(percHum, 5, 1))                                          
      dbg.str(string("|"))
      dbg.str(fp.FloatToFormat(fahrDP, 5, 1))                                           
      dbg.str(string(13, 10))
    
    DAT
    
    which produces:
    temp.dat
      2.3| 93.4|  4.5
    
    So I'd say it's not the FloatToFormat at fault (any more).
  • JLockeJLocke Posts: 354
    edited 2014-06-09 22:26
    Here's something I use to build a very similar data string for uSD card logging...
    {
    ===============================================================================
    }
    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
    
    

    I believe I 'lifted' the Concatenate routine from another Spin library. Make sure that your strings are sized large enough or you'll definitely corrupt something!
  • greyfin10greyfin10 Posts: 7
    edited 2014-06-11 16:49
    Aye, that look like the STRING2 library concatenation function I originally tried. The warning to watch the string size must be related to the corruption I was creating in my file (every time I opened the disk on my machine, Windows asked if I'd like to run diskcheck on it ;)

    I'll try again though, since its so much neater.
    JLocke wrote: »
    Here's something I use to build a very similar data string for uSD card logging...
    {
    ===============================================================================
    }
    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
    
    

    I believe I 'lifted' the Concatenate routine from another Spin library. Make sure that your strings are sized large enough or you'll definitely corrupt something!
  • greyfin10greyfin10 Posts: 7
    edited 2014-06-12 18:01
    First off, thanks for all the help!

    A combination of JLocke's suggestion and messing with exactly how I handle the line feeds between the XBee output and the file write finally did the trick. Btw, I believe that string routine you mentioned is either from the Strings v2.1 library (Brandon Nimon) or a very close approximation. I had previously tried to incorporate that library before, but since I was blissfully unaware of even the basics of how it was doing byte arrays, I was pretty dramatically unsuccessful. This second attempt was much better (thanks all). As far as the carriage returns, XBee wanted its constant CR ($D) transmitted seperately in its TX method, whereas the SD-MMC_Fate (SD card lib) wanted it sent seperately as string(13,10). Appending the carriage return to my byte array didn't seem to work for me (especially since the two libraries handled it so differently. So... final code (that's in the container logging humidity as I type this) we have (the sticky bits anyway):
    PUB main | rawTemp, rawHumidity, celTemp, celDP, fahrTemp, fahrDP, percHum
    
    
      XB.start(XB_Rx, XB_Tx, 0, XB_Baud)                    ' Initialize comms for XBee   
      
      fat.fatEngineStart( _dopin, _clkpin, _dipin, {       ' Initialize the FAT engine    
                         } _cspin, _wppin, _cdpin, {       
                         } _rtcres1, _rtcres2, _rtcres3)
                         
      f.start                                               ' Start Floating Point Object
      sht.start(SHT_DATA, SHT_CLOCK)                        ' Start Sensirion Object
      sht.config(33,sht#off,sht#yes,sht#hires)              'Configure SHT-11 (High Res Mode)
      repeat                                                ' Send temperature and humidity every 30 seconds
        ByteFill(@Buff, 0, 17)
        rawTemp := f.FFloat(sht.readTemperature)            ' Read the raw temperature from the Sensirion
        rawHumidity := f.FFloat(sht.readHumidity)           ' Read the raw humidity from the Sensirion
        celTemp := celsius(rawTemp)                         ' Calculate temperature in Celcius (required for Fahrenheit)
        fahrTemp := fahrenheit(celTemp)                     ' Calculate temperature in Fahrenheit (using temp in Celcius)
        percHum :=  humidity(celTemp, rawHumidity)          ' Calculate humidity percentage
        celDP := dewpoint(celTemp, percHum)                 ' Calculate dewpoint in Celcius (required for Fahrenheit)
        fahrDP := fahrenheit(celDP)                         ' Calculate dewpoint in Fahrenheit (using dewpoint in Celcius)
        STR.Concatenate(@Buff, fp.FloatToFormat(fahrTemp, 5, 1))
        STR.Concatenate(@Buff,string("|"))
        STR.Concatenate(@Buff, fp.FloatToFormat(percHum, 5, 1))     
        STR.Concatenate(@Buff,string("|"))
        STR.Concatenate(@Buff, fp.FloatToFormat(fahrDP, 5, 1))
        XB.str(@Buff)
        XB.tx(CR)
        if write_temp(string("temp_data.txt"))
          XB.str(string("error writing to file"))
                                                                                             
        Delay(10000)   
    

    and
    PUB write_temp(fileID) | errorString, errorNumber
      fat.mountPartition(0)                                ' Mount the file system
      errorString := \fat.newFile(fileID)                  ' Initial file creation (will give error if exists... we'll ignore this)
      errorNumber :=  fat.partitionError                   ' Trap any errors
      if(errorNumber)                                      ' Try to handle the "entry_already_exist" error.
        if(errorNumber <> fat#Entry_Already_Exist)
          return errorNumber
      fat.openFile(fileID, "A")                            ' Open tempdata.txt for appending                                                                                                 
      fat.writeData(@Buff,17)                               ' Write data to microSD Card
      fat.writeString(string(13, 10))                      ' Write newline 
      fat.closeFile                                        ' Close tempdata.txt   
      fat.unmountPartition
    
  • greyfin10greyfin10 Posts: 7
    edited 2014-06-18 13:39
    I'm not entirely sure I didn't imagine it, but I "thought" I saw a message about my wrap up post needing to be "approved"... then it never appeared. That seems far-fetched since none of the previous posts were being moderated to that extent.

    Either way, I took JLocke's advice and paid attention to my array dimension, and suddenly the concatenation worked ;)

    So, the original question was totally related to that. I'm still not 100% on the order of operations of function calls and variables, as I'm seeing very similar problems now trying to integrate a Real Time Clock library. The example code works fine, but when I try and integrate it into my app, it starts acting up. I clearly need to back up and start over again (from a basic code skills perspective). I'm code-cribbing successfully, but I've gone as far as I can with that (lazy) approach ;)

    This post is really just a thank you for the useful information from all. Its nice to have a knowledgeable community to help you get started.
Sign In or Register to comment.