Parallax Data logger To help better understand the commands to and from the logger it would be useful to have a copy of the VDAP firmware commands close by while working through these examples Memory Stick Format Before using any memory stick I advise that you format it. I also advise that you take control of the way the memory stick is formatted by using the following procedure (Win XP) Control Panel-à Administrative Tools à Computer Management -à Disk Management Select the memory sticks drive letter and right click, select Format and format the drive as FAT 32 with an allocation unit size of 512 The following code text can be copied and pasted into the Stamp editor The VDAP firmware is very specific about the commands the logger expects and the commands it returns. This means our code has to be very precise or we are going to lock the logger or corrupt the memory stick. The BS2 is limited on the amount of serial data it can receive and handle; this situation can be eased if we run the data logger in Shortened Command Set. This means that the information returned from the logger is in an abbreviated form making it easier for the Stamp to deal with. If you take a look at the VDAP commands under the Response column you will see that most of the commands reply with $0D, in shortened command set what we would see is ‘>’ CR. The arrow prompt (‘>’) plays a crucial part in our code, receiving the arrow is confirmation that the previously transmitted command was carried out successfully. If we don’t receive the arrow then we know there was an error and we can take action accordingly. Step 1. Initialize and Detect Our program is going to consist of a loop that contains a menu. Each menu item will be designed to call one or more sub routines. As you will see later this makes the program very flexible and simplifies modification. Declare the variables. GP_VAR VAR Word ‘A general-purpose variable FILESIZE VAR Word ‘Used to measure file size GP_STRING VAR Byte (12) ’A general-purpose array of 12 bytes FILENAME VAR Byte (7) ’A 7-byte array used to hold the file name Initialization and memory detection code is short and sweet, most of the code for this part is DEBUG statements that provide us with instructions and inform us of what’s going on. This is the case for most of the sub routines. Below is the code for initialization '***********[INITIALISE & DETECT LOGGER]********* DEBUG HOME,” Remove Memory and Press Enter" DEBUGIN GP_VAR SEROUT TX, Baud, ["IPA", CR] PAUSE 200 SEROUT TX, Baud, ["SCS", CR] PAUSE 200 GOSUB DETECT Before we add the DETECT subroutine we will add the main loop and start to form a menu structure. '****************[MAIN LOOP]********************** MAIN: DEBUG "Press Enter to Continue" DEBUGIN GP_VAR DEBUG CLS, “ 0. Remove Memory", CR DEBUGIN DEC1 GP_VAR ON GP_VAR GOSUB DETECT GOTO MAIN The DETECT sub routine provides a safe place to remove or insert a memory stick. '****************[DETECT LOGGER]********************* DETECT: DEBUG CLS, HOME, "It is safe Insert or Remove Memory", CR,"(Remove and re-insert to go again)" SERIN RX, Baud,[WAIT ("DD")] DEBUG CLS, "Device Detected Please Wait...”, CR SERIN RX, Baud,[STR GP_STRING \12] DEBUG STR GP_STRING \12,CR IF GP_STRING (11)<>">" THEN GOTO main DEBUG "Logger is ready...”, CR PAUSE 1000 RETURN When a memory stick is inserted into the logger the logger goes through a sequence that must be completed before we can start to read or write data. First it sends back to the Stamp that a device was detected, “DD” in shortened command set, so our first SERIN waits for the characters “DD”. Secondly the logger examines the memory stick to see if it contains a firmware upgrade, when it has finished it reports back “No Upgrade” followed by a prompt “>” and that is the purpose of our second SERIN. If both of those steps are successful we can display our menu and proceed. Run the above code to see how it works. The code we just wrote is the basis for all our logger programs. Step 2. Open and Close Our next three sub routines deal with opening and closing a file. You may ask why three and the reason is that reading and writing use different commands to open a file. In this program we will not access any of these three sub routines directly. The opening and closing of files will be done automatically in our code. Our final menu will have six options where the most that we as the user will have to do is input a file name. The sub routine OPEN_WRITE will create and open a file if it does not exist, if it does exist the file will be opened and new data will be appended to the existing data. '****************[OPEN FOR WRITE]***************** OPEN_WRITE: DEBUG CLS SEROUT TX, Baud,[$9,$20,STR FILENAME, ".txt", CR] SERIN RX, Baud,[WAIT (">")] DEBUG "File ", STR FILENAME,".txt open", CR RETURN The string FILENAME will be user input built into our menu options that I will be explaining shortly. The .txt extension is automatically added to the file name. The sub routine OPEN_READ will open a file for reading; if the file does not exist the logger will return an error. To prevent that situation occurring we will later provide a sub routine that checks the file exists before we attempt to open for reading '****************[OPEN FOR READ]****************** OPEN_READ: DEBUG CLS SEROUT TX, Baud, [$0E, $20, STR FILENAME ,".txt", CR] SERIN RX, Baud,[WAIT (">")] DEBUG "File ", STR FILENAME, " .txt open", CR RETURN Finally after we have finished with any open file we MUST close it. '****************[CLOSE FILE]********************* CLOSE: SEROUT TX, Baud,[$0A,$20,STR FILENAME ,".txt", CR] SERIN RX, Baud, [WAIT (">")] RETURN Before we can use these subroutines we need to be able to write some data. In the next section we will build a write sub routine and add a menu item so we can use it. Step 3. Write The write routine is where you will probably have to give most thought. The write command has the number of bytes to write as a parameter, if you try and write more or less than the number you tell it to write the logger will lock up and possibly corrupt data on the memory stick. Depending on what you want to write or log governs the way you code the write sub routine. In our menu driven program I am going to use two different methods that should provide good examples for you to expand on. The first example allows for us to write text to a file possibly for making notes, the file can be opened time and time again each time the new text being appended to the old text. '****************[USER WRITE]********************* USER_WRITE: DEBUG CR, "Enter filename to write to", CR DEBUGIN STR FILENAME\7\CR GOSUB OPEN_WRITE DEBUG "Enter text.... Press ESC to finish", CR DO DEBUGIN STR GP_STRING\1\CR IF GP_STRING (0)=27 THEN EXIT SEROUT TX, Baud, [$8, $20,0,0,0,DEC 1,CR, STR GP_STRING, CR, CR] SERIN RX, Baud, [WAIT (">")] LOOP GOSUB CLOSE RETURN The sub routine prompts for a file name that is contained in the FILENAME array, it then calls the OPEN_WRITE sub routine and opens a file with the name contained in FILENAME The Do-Loop takes our keystrokes into the variable array GP_STRING, because of the backslash 1 (\1) it only reads one character at a time from the keyboard. If the ESC key is pressed (ASCII 27) then we exit the routine. The SEROUT instruction writes the characters to the open file, we know we are writing only one character at a time so DEC 1 is used as the number of bytes to write parameter Before the sub routine is exited the open file is closed using the CLOSE sub routine we coded earlier. Finally the RETURN instruction takes us back to the menu. To try out what we have so far all we need to do is be able to access the USER_WRITE sub routine from the menu. Make the following additions in the MAIN loop or just copy this one and paste it over the old one. If everything is well you should be able to write text to the logger. Place the memory stick in a computer to see the file containing the text you wrote. '****************[MAIN LOOP]********************** MAIN: DEBUG "Press Enter to Continue" DEBUGIN GP_VAR DEBUG CLS, “ 0. Remove Memory", CR, “1. User Write” DEBUGIN DEC1 GP_VAR ON GP_VAR GOSUB DETECT, USER_WRITE GOTO MAIN Step 4. File size and Reading It becomes tedious removing and inserting the memory stick from logger to computer to read our files so what we need is a sub routine that can read our files for us, Reading is another file operation we must give careful thought to. The BS2 is limited on how much data it can receive on the serial line so it is impractical to try and read a file in one go. What we need to do is to read the file in small pieces and use the VDAP SEEK instruction to keep track of our position. Ideally before we read a file we need to know the file size, which will be the first sub routine we will cover. The file size sub routine has another benefit, it can be used to check if the file exists or not preventing errors down the line. '****************[FILE SIZE]********************** SIZE: GP_VAR=0 SEROUT TX, Baud, [$01, $20,STR FILENAME, ".txt", $0D] SERIN RX, Baud, 5000, NF, [WAIT (" "), HEX2 FILESIZE.LOWBYTE, HEX2 FILESIZE.HIGHBYTE] SERIN RX, Baud, [WAIT (">")] DEBUG "File size ", DEC FILESIZE," Bytes", CR RETURN NF: GP_VAR=1 DEBUG CR, "File not found..." RETURN If the first SERIN times out then it is assumed the file does not exist and the program jumps to NF: (not found) and GP_VAR is set to 1 which flags to say the file does not exist. If the file does exist the GP_VAR returns with a value of 0 and allows us to continue with the file read. Notice the sub routine has two return statements, one for success and one for fail. The SEEK ROUTINE reads 10 bytes at a time in a loop keeping track of the position in the file using FILESIZE. When FILESIZE drops below 10 bytes the remaining bytes are read outside loop. '****************[SEEK & READ]********************** SEEK: DEBUG "Reading ", STR FILENAME, ".txt", CR GP_VAR=0 DO WHILE FILESIZE>9 SEROUT TX, Baud,[$28,$20,0,0,0,DEC GP_VAR,CR] SERIN RX, Baud, [WAIT (">")] SEROUT TX, Baud,[$0B,$20,0,0,0,DEC 10,CR] SERIN RX, Baud, [STR GP_STRING\10] DEBUG STR GP_STRING\10 GP_VAR=GP_VAR+10 FILESIZE=FILESIZE-10 LOOP SEROUT TX, Baud,[$28,$20,0,0,0,DEC GP_VAR,CR] SERIN RX, Baud, [WAIT (">")] SEROUT TX, Baud,[$0B,$20,0,0,0,DEC FILESIZE,CR] SERIN RX, Baud, [STR GP_STRING\FILESIZE] DEBUG STR GP_STRING\FILESIZE DEBUG CR RETURN Step 5. Menu Items To use the File size and the Seek subroutines from our menu we again need to modify the MAIN loop. We also need to add another two subroutines (Menu item 3 and Menu item 4) that will allow us to use SIZE and SEEK. '****************[MAIN LOOP]********************** MAIN: DEBUG "Press Enter to Continue" DEBUGIN GP_VAR DEBUG CLS, “ 0. Remove Memory", CR, “1. User Write”, CR,”2. File size”, CR, ”3. Read File” DEBUGIN DEC1 GP_VAR ON GP_VAR GOSUB DETECT, USER_WRITE, Menu_3, Menu_4 GOTO MAIN One of the main reasons for using the menu sub routines is to allow for the input of a file name but it also serves as a place we can call other routines. Menu item 3 takes us directly to the size sub routine. '****************[Menu_item_3]******************** Menu_3: DEBUG CR, "Enter filename", CR DEBUGIN STR FILENAME\7\CR GOSUB SIZE RETURN Menu item 4 calls four of the sub routines we have already written, SIZE, OPEN_READ, SEEK and CLOSE. SIZE checks the file exists, if it does not then we abort the read and return to the main menu, this prevents locking the logger. '****************[Menu_item_4]******************** Menu_4: DEBUG CR, "Enter filename to read", CR DEBUGIN STR FILENAME\7\CR GOSUB SIZE IF GP_VAR>0 THEN RETURN GOSUB OPEN_READ GOSUB SEEK DEBUG "Closing", CR GOSUB CLOSE RETURN Step 6. Delete We have one more sub routine and menu item to code and that is the DELETE routine. '****************[Menu_item_5]******************** Menu_5: DEBUG CR, "Enter filename to delete", CR DEBUGIN STR FILENAME\7\CR GOSUB DELETE RETURN '****************[DELETE FILE]********************* DELETE: DEBUG "Deleting", CR SEROUT TX, Baud, [$07, $20,STR FILENAME, ".txt", CR] SERIN RX, Baud, [STR GP_STRING\2\CR] IF GP_STRING (0)=62 THEN RETURN DEBUG "File not found ", STR FILENAME, ".txt", CR RETURN The very last thing to do is include DELETE and Menu_5 in the main loop '****************[MAIN LOOP]********************** MAIN: DEBUG "Press Enter to Continue" DEBUGIN GP_VAR DEBUG CLS, “ 0. Remove Memory", CR, “1. User Write”, CR,”2. File size”, CR, ”3. Read File”, CR, “4. Delete” DEBUGIN DEC1 GP_VAR ON GP_VAR GOSUB DETECT, USER_WRITE, Menu_3, Menu_4,Menu_5 GOTO MAIN That’s the whole of our example program built entirely around sub routines that we can call and use in any order. You probably won’t use every sub routine in every program you will have to decide which ones you need. The USER_WRITE routine is just an example and not really that useful, but it is this routine that with modification that will certainly be in most of your applications. The important thing to remember is that the number of bytes you say your going to write and the actual number of bytes you write must match. The write instruction: SEROUT TX, Baud, [$8, $20,0,0,0,DEC value, CR, STR GP_STRING, CR]