Home AMX User Forum NetLinx Studio

Writing Variables to Flash

Greetings,

I would like to start writing variables to flash memory of Netlinx masters. Could someone give me a few pointers?

Let say I have an integer varaible of 50 bytes, nVaraible

I would like to write it to the memory during the course of program running then be able to retreive it at a startup.

Thanks!

Comments

  • Simply making it persistent achieves that I believe.

    Alternatively use file_open, file_write_line, file_read_line, file_close.
  • vincenvincen Posts: 526
    As indicated you can use File system (excepted if these variables change often as there is a little delay to read/write to file than memory ;) But it works well and easily with STRING_TO_VARIABLE and VARIABLE_TO_STRING so you don't need to care how to encode/decode your variables or structures to save them on disk and read them from disk :D

    Something about PERSISTENT is that it doesn't work in modules :( Only in main program ;)

    Vince
  • TurnipTruckTurnipTruck Posts: 1,485
    I should have been more specific. The values of this variable are created in a module and therfore cannot be persistent. This is why I am wanting to save to disk.

    Thanks.
  • vincenvincen Posts: 526
    I should have been more specific. The values of this variable are created in a module and therfore cannot be persistent. This is why I am wanting to save to disk.

    So you can do something like that to get back content of your structure from your file at startup:

    File_Index = FILE_OPEN('RSS_News_Reader_Feeds.backup',FILE_READ_ONLY) // Open File in Read Only Mode
    IF (File_Index > 0) // File exists with content of structure so we read it
    {
    FILE_READ(File_Index,Buffer_Feeds,MAX_LENGTH_STRING(Buffer_Feeds)) // Read data from file and put it in buffer
    STRING_TO_VARIABLE(Feeds_Bookmark,Buffer_Feeds,1) // Put data back from buffer in structure
    FILE_CLOSE(File_Index)
  • viningvining Posts: 4,368
    I just happened to be converting old files to work on XM module to hold preset values this morning so I pulled out the apllicable files.

    There's a function in a button event to initate write the file to RAM which I usually put on the "EXIT" device button and another function in the devices data_event to read when the device comes online. I haven't tested this to see if this is where it should be for this device but that's where it is now.

    This should almost be plug and play once you change names around.
    PROGRAM_NAME='Read_N_Write_RAM'
    (***********************************************************)
    (*  FILE_LAST_MODIFIED_ON: 04/14/2007  AT: 12:48:07        *)
    (***********************************************************)
    
    DEFINE_DEVICE
    
    dvXM   		= 5001:4:0		  // Serial Port
    dvTP_XM    	= 10001:12:0   		 // Touch Panel
    vdvXM		= 41004:1:0		// Virtual Device for Read N Write buffer "only"
    
    DEFINE_CONSTANT
    
    (*(VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV)*)
    (*(          FILENAME TO USE WHEN WRITING TO RAM            )*)
    (*(VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV)*) 
    
    CHAR XM_XML_FILENAME[] = 'XM_STRUCTURE_DATA.xml' ;
         
    (*(VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV)*)
    (*(       BUFFER LENGHT TO HOLD XML READ & WRITE DATA       )*)
    (*(VAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAVAV)*)
     
    LONG XM_READ_BUFLENGTH = 25000 ; 
    
    DEFINE_TYPE
    
    structure sXM_PresetData
    
         {
         INTEGER XMChNum ;
         CHAR XMCategory[18] ;
         CHAR XMChName[18] ;  
         }
    
    structure sXM_Zone
    
         {
         INTEGER XMChNum ;
         CHAR XMCategory[18] ;
         CHAR XMChName[18] ;  
         CHAR XMArtist[18] ;
         CHAR XMSong[18] ;
         INTEGER XMPower ;
         INTEGER XMSigLVL ;
         INTEGER XMMute ;
         INTEGER XMMode ;
         CHAR XMXID[8] ;
         sXM_PresetData XMPreData[10] ;
         }
         
    structure sXM_Sent
    
         {
         INTEGER XMChNum ;
         CHAR XMCategory[18] ;
         CHAR XMChName[18] ;  
         CHAR XMArtist[18] ;
         CHAR XMSong[18] ;
         INTEGER XMPower ;
         INTEGER XMSigLVL ;
         INTEGER XMMute ;
         INTEGER XMMode ;
         CHAR XMXID[8] ;
         sXM_PresetData XMPreData[10] ;
         }
        
    DEFINE_VARIABLE
    
    VOLATILE sXM_Zone sXMZone[3] ;
    VOLATILE sXM_Sent sXMSent[3] ;
    VOLATILE sXM_PresetData XMPreData ;
    
    
    VOLATILE INTEGER nXM_DeBug ;
    VOLATILE CHAR cXM_Var_to_XML ;
    
    DEFINE_FUNCTION CHAR fnXM_FileWrite_ToFile(CHAR iWriteFileName[],CHAR iWriteFileBuf[])
                                                   //filename     //buffer containing data to write
         {                                       
         stack_var slong nWriteFHandle ;
         stack_var slong nWriteFResult ;
         
         nWriteFHandle = file_open(iWriteFileName,FILE_RW_NEW) ;
         if (nWriteFHandle > 0) 
    	  {
    	  if (nXM_DeBug)
    	       {
    	       SEND_STRING 0,"'FUNCTION fnFileWrite_ToFile. File_Open successful for *',
    					     iWriteFileName,'*! line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  nWriteFResult = FILE_WRITE (nWriteFHandle,iWriteFileBuf,length_string(iWriteFileBuf)) ;
    	  if (nWriteFResult > 0) 
    	       {
    	       if (nXM_DeBug)
    		    {
    		    SEND_STRING 0,"'FUNCTION fnFileWrite_ToFile. File_Write OK. Wrote a ',
    					     itoa(nWriteFResult),
    					     ' CHAR String!  line-<',ITOA(__LINE__),'>',crlf" ; 
    		    }
    	       }
    	  else
    	       {
    	       stack_var char cErrorMsg [20] ;
    	       switch (itoa(nWriteFResult)) 
    		    {
    		    case '-11': {cErrorMsg = 'disk full'} ;
    		    case '-5' : {cErrorMsg = 'disk I/O error'} ;
    		    case '-1' : {cErrorMsg = 'invalid file handle'} ;
    		    case '-0' : {cErrorMsg = 'zero bits returned?'} ;
    		    }
    	       if (nXM_DeBug)
    		    {
    		    SEND_STRING 0,"'FUNCTION fnFileWrite_ToFile. Bad File_Write_Line: ',cErrorMsg,
    						  '! line-<',ITOA(__LINE__),'>',crlf" ;
    		    }
    	       }
    	  file_close(nWriteFHandle) ;
         	  }
         else
    	  {
    	  stack_var char cErrorMsg [40] ;
    	  switch (itoa(nWriteFHandle)) 
    	       {
    	       case '-2': {cErrorMsg = 'invalid file path or name'} ;
    	       case '-5': {cErrorMsg = 'disk I/O error'} ;
    	       case '-3': {cErrorMsg = 'invalid value supplied for IOFlag'} ;
    	       }
    	  if (nXM_DeBug)
    	       {
    	       SEND_STRING 0,"'FUNCTION fnFileWrite_ToFile. Bad File_Open: ',cErrorMsg,
    						  '! line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  }
         file_close(nWriteFHandle) ;
         
         RETURN TRUE ; 
         }     
    
    DEFINE_FUNCTION INTEGER fnXM_FileRead(CHAR icReadFile[])
    
         {
         STACK_VAR SLONG nReadFHandle ;
         STACK_VAR SLONG nReadFResult ;
            
         nReadFHandle = file_open(icReadFile,FILE_READ_ONLY) ;
         if (nReadFHandle > 0) 
    	  {
    	  if (nXM_DeBug)
    	       {
    	       SEND_STRING 0,"'FUNCTION fnReadFile. File_Open successful for *',
    					     icReadFile,'*! line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  nReadFResult = file_read(nReadFHandle,cXM_Var_to_XML,XM_READ_BUFLENGTH) ;
    	  if (nReadFResult > 0) 
    	       {
    	       if (nXM_DeBug)
    		    {
    		    SEND_STRING 0,"'FUNCTION fnReadFile. File_Read OK. Rcvd: a ',
    					     itoa(length_string(cXM_Var_to_XML)),
    					     ' CHAR String!  line-<',ITOA(__LINE__),'>',crlf" ; 
    		    }
    	       }
    	  else
    	       {
    	       stack_var char cErrorMsg [20] ;
    	       switch (itoa(nReadFResult)) 
    		    {
    		    case '-9': {cErrorMsg = 'end-of-file reached'} ;
    		    case '-6': {cErrorMsg = 'invalid parameter'} ;
    		    case '-5': {cErrorMsg = 'disk I/O error'} ;
    		    case '-1': {cErrorMsg = 'invalid file handle'} ;
    		    case '-0': {cErrorMsg = 'zero bits returned?'} ;
    		    }
    	       if (nXM_DeBug)
    		    {
    		    SEND_STRING 0,"'FUNCTION fnReadFile. Bad Read_File: ',cErrorMsg,
    						  '! line-<',ITOA(__LINE__),'>',crlf" ;
    		    }
    	       RETURN FALSE ;
    	       }
    	  file_close(nReadFHandle) ;
    	  }
         else
    	  {
    	  stack_var char cErrorMsg [40] ;
    	  switch (itoa(nReadFHandle)) 
    	       {
    	       case '-2': {cErrorMsg = 'invalid file path or name'} ;
    	       case '-5': {cErrorMsg = 'disk I/O error'} ;
    	       case '-3': {cErrorMsg = 'invalid value supplied for IOFlag'} ;
    	       }
    	  if (nXM_DeBug)
    	       {
    	       SEND_STRING 0,"'FUNCTION fnReadFile. Bad Open_File: ',cErrorMsg,
    						  '! line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  RETURN FALSE ;
    	  }
         file_close(nReadFHandle) ;
         
         RETURN TRUE ;
         }
         
    DEFINE_FUNCTION fnXM_StoreData_XML()
    
         {
         STACK_VAR SINTEGER nXML_Encode_Return ;
         STACK_VAR LONG nXM_EncodePOS ;
         STACK_VAR CHAR cXML_MSG[100] ;
         
         nXM_EncodePOS = 1 ;
         nXML_Encode_Return = VARIABLE_TO_XML(sXMZone,cXM_Var_to_XML,nXM_EncodePOS,0 ) ;
         if (nXM_DeBug)
    	  {
    	  switch (itoa(nXML_Encode_Return))
    	       {
    	       case '-1': {cXML_MSG = 'ERROR -1 = decode variable type mismatch' } ;
    	       case '-2': {cXML_MSG = 'ERROR -2 = decode data too small, decoder ran out of data. Most likely poorly formed XML.' } ;
    	       case '-3': {cXML_MSG = 'ERROR -3 = output character buffer was too small' } ;
    	       case '3' : {cXML_MSG = 'ERROR  3 = XML decode data type mismatch' } ;
    	       case '2' : {cXML_MSG = 'ERROR  2 = XML decode data too small, more members in structure' } ;
    	       case '1' : {cXML_MSG = 'ERROR  1 = structure too small, more members in XML decode string' } ;
    	       case '0' : {cXML_MSG = 'Returned 0 = decoded OK! ' } ;
    	       }
    	  SEND_STRING 0,"'XM_Module: XML Loading: ',cXML_MSG,' Line-<',ITOA(__LINE__),'>',crlf" ;
    	  }
         fnXM_FileWrite_ToFile(XM_XML_FILENAME,cXM_Var_to_XML) ;
         CLEAR_BUFFER cXM_Var_to_XML ;
         }
    
    DEFINE_FUNCTION CHAR fnXM_LoadStartingValues() 
    
         {
         STACK_VAR SLONG nXM_READ_OK ;
           
         nXM_READ_OK = fnXM_FileRead(XM_XML_FILENAME) ;
         if (nXM_READ_OK)
    	  {
    	  stack_var sinteger nXML_Decode_Return ;
    	  stack_var char cXML_MSG[100] ;
    	  stack_var long nXM_POS ;
    	  
    	  if (nXM_DeBug)
    	       {
    	       SEND_STRING 0,"'XM_Module: Loading RAM FILES: Line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  nXM_POS = 1 ;
    	  nXML_Decode_Return = XML_TO_VARIABLE (sXMZone,cXM_Var_to_XML,nXM_POS,0) ;
    	  if (nXM_DeBug)
    	       {
    	       switch (itoa(nXML_Decode_Return))
    		    {
    		    case '-1': {cXML_MSG = 'ERROR -1 = decode variable type mismatch' } ;
    		    case '-2': {cXML_MSG = 'ERROR -2 = decode data too small, decoder ran out of data. Most likely poorly formed XML.' } ;
    		    case '-3': {cXML_MSG = 'ERROR -3 = output character buffer was too small' } ;
    		    case '3' : {cXML_MSG = 'ERROR  3 = XML decode data type mismatch' } ;
    		    case '2' : {cXML_MSG = 'ERROR  2 = XML decode data too small, more members in structure' } ;
    		    case '1' : {cXML_MSG = 'ERROR  1 = structure too small, more members in XML decode string' } ;
    		    case '0' : {cXML_MSG = 'Returned 0 = decoded OK! ' } ;
    		    }
    	       SEND_STRING 0,"'XM_Module: XML Loading: ',cXML_MSG,' Line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  }
         //else //if you create default settings
    //	  {
    //	  fnXM_Load_Defaults('BAD_XML') ;
    //	  }
         
         CLEAR_BUFFER cXM_Var_to_XML ;// use if global VAR or LOCAL is used!!
         
         RETURN TRUE ;
         }
    DEFINE_START
    
    CREATE_BUFFER vdvXM,cXM_Var_to_XML ;
    
    DEFINE_EVENT
    DATA_EVENT [dvXM]
         
         {
         ONLINE:
    	  {
    	  SEND_COMMAND dvXM,"'SET BAUD 9600,N,8,1 485 DISABLE'" ; 
    	  SEND_STRING dvXM,"'UN1',CRLF" ;//enable unsolicited responses.
    	  if (nXM_DeBug)
    	       {
    	       SEND_STRING 0,"'dvXM is online! Line-<',ITOA(__LINE__),'>',crlf" ;
    	       }
    	  fnXM_LoadStartingValues() ;
    	  wait 700
    	       {
    	       fnXM_InitialQuery(nXM_Zone) ;
    	       }
    	  }
         STRING:
    	  {
    	  //
    	  }
         }
    BUTTON_EVENT[dvTP_XM,nXM_BtnArry]
    
         {
         PUSH:
    	  {
    	  //I usually put his it an exit device button
    	  fnXM_StoreData_XML() ;
    	  }
         HOLD[5,REPEAT]:
    	  {
    	  //stuff
    	  }
         RELEASE:
    	  {
    	  //stuff
    	  }
         }
         
    
    
    
  • viningvining Posts: 4,368
    I change the code in the last post. In the replaced code I used STACK_VARS to hold the encoded and decoded data which didn't work so I went back to what did work which is a GLOBAL which I made into a buffer using CREATE_BUFFER to hold large amount of data. I thought because this wasn't an input buffer assigned to a port a STACK_VAR would work but it didn't. I created the buffer using a virtual device that has no purpose other than to create this buffer.

    The function "fnXM_FileRead(CHAR icReadFile[])" doesn't appear to do any thing in this example so don't let it confuse you. I just grabbed it while getting other stuff.

    Any way with the change from STACKS back to a created buffer it's decoding properly so now it's almost plug and play.
  • The values of this variable are created in a module and therfore cannot be persistent.

    Fair enough, but you *can* use persistent variables in a module, you just can't declare them in the module. You have to pass them through the module header.

    As adding a new variable is such a pain when you pass it through the header, I use a large generic array so that the header never changes and you can just add a subscript constant to an include file to refer to a new variable.
  • vincenvincen Posts: 526
    Fair enough, but you *can* use persistent variables in a module, you just can't declare them in the module. You have to pass them through the module header.
    As adding a new variable is such a pain when you pass it through the header, I use a large generic array so that the header never changes and you can just add a subscript constant to an include file to refer to a new variable.

    Excepted you can't do that with structure :-(
  • vincen wrote:
    Excepted you can't do that with structure :-(

    Yep, that's why you have to use a single generic array instead if you don't want to change the module header all the time..
  • DHawthorneDHawthorne Posts: 4,584
    I've come to the conclusion it's far more efficient using a persistent variable as a parameter than bothering to write it to flash and restore it every time. I personally don't mind changing the module header if the structure of the variable changes, but I can see the value in using a generic array too.

    However, I will write to flash if I have a large amount of data to store, or to log events.
  • TurnipTruckTurnipTruck Posts: 1,485
    DHawthorne wrote:
    I've come to the conclusion it's far more efficient using a persistent variable as a parameter than bothering to write it to flash and restore it every time. I personally don't mind changing the module header if the structure of the variable changes, but I can see the value in using a generic array too.

    However, I will write to flash if I have a large amount of data to store, or to log events.

    Good point. As I'm new to writing modules, I'm loving the fact that they can be droppoed right into a program without modifying declarations in any way. But, it seems reasonable to pass in the occasional variable that needs to be persistent.
  • DHawthorne wrote:
    I personally don't mind changing the module header if the structure of the variable changes

    Plan A:

    a) Declare the new variable in the mainline in as many instances as you need, all with different names.

    b) Add it to each of the module instance declarations in the mainline, making sure you get the different spellings right and you put them in the same place each time.

    c) Add it to the module header, deciding whether you want to use the same naming, and ensuring that you declare it in the same way, and you put it in the right place.

    d) Write the code that uses it, ensuring that you use the internal name not the external name if they are different.

    Plan B:

    a) Add a constant to your general include file which is a subscript to the generic array.

    b) Write the code that uses it.

    Plan A isn't that arduous but I found when building a large multi-room project (80 rooms in a dozen phases) that incrementally grew (sometimes the best way to build large projects) that I was endlessly adding configuration variables (sounds iffy but it was fine) and Plan B was much more likely to compile first time and work first time so I could get on with the important stuff.

    Of course you do need wrapper code for your generic array, but you only have to write that once.
Sign In or Register to comment.