Text File Maneuver List

So far in this series, we’ve been using C language programs to make the ActivityBot go. This time, we will use a C program that opens a text file from the SD card.  The text file contains a program in another super-simple language we are inventing right now. Let’s call it "AB."

The AB programming language has only five commands:

  • forward
  • backward
  • left
  • right
  • end

Each command has a single argument: the number of encoder ticks that set the distance the ActivityBot should travel.

The C program will parse each line of text, that is, analyze the characters and relate them to predefined actions elsewhere in the C code. In this way, the C code interprets the AB commands to make ActivityBot go. 

 

Circuit

We will not be using any sensor circuits here, just the SD card.

  • Insert your kit’s microSD card into your Propeller Activity Board’s SD socket (original or WX version).

 

Test Code

The zip for this application contains a text file that you will copy to your SD card. After that, you will load a C application into the Propeller that will read the file and execute its AB language commands.

  • Click SimpleIDE’s Open Project button.
  • Open AB Interpreter.side from …Documents\SimpleIDE\Learn\Examples\ActivityBot.
  • Click Program and select File to SD Card.
  • Browse to …Documents\SimpleIDE\Learn\Examples\ActivityBot again, select NAVSET.TXT and click Open. This will load the file onto the SD card.
  • Check the build status pane to make sure it loaded — click the little hammer icon in the bottom to see the status.

 

Now it is time to run the AB Interpreter.side project.

  • Click Load EEPROM and Run.
  • Put your ActivityBot on the floor where it has some room.  Switch power to 2, and reset.
  • Verify that your ActivityBot goes forward, then spins in place to the left, then to the right, then to the left again, and backs up to about where it started.

 

How it Works

The code starts with a file pointer and declarations for character buffers and a variable to use while analyzing the AB command text strings.

FILE *fp;                                   // File pointer for SD
char str[512];                              // File buffer

char cmdbuf[10];                            // Command buffer
char valbuf[4];                             // Text value buffer
int  val;                                   // Value

Inside the main routine, the first three lines set up the SD card connection. They should be familiar from the SD Card Data tutorial. 

int main()                                  // Main function
{
  int DO = 22, CLK = 23;                    // SD I/O pins
  int DI = 24, CS = 25;
  sd_mount(DO, CLK, DI, CS);                // Mount SD file system

The line that follows opens the navset.txt file on the SD card for reading.  Then, the fread function copies up to 512 bytes from navset.txt to a 512 byte buffer named str. Next, strlen(str) figures out how many bytes are actually in the string and copies that number to the strLength variable. Before any modifications, there are about 90 characters in the navset.txt file.

  fp = fopen("navset.txt", "r");            // Open navset.txt

  fread(str, 1, 512, fp);                   // navset.txt -> str
  int strLength = strlen(str);              // Count chars in str

There are just two more setup tasks: declaring a variable for indexing, and making sure the robot is not moving to begin with:

  int i = 0;                                 // Declare index variable

  drive_speed(0, 0);                         // Speed starts at 0

Now the action happens!  The program's first task inside the while(1) is parsing the content of navset.txt is to find the first printable character — not spaces or line feeds. So, while(!isalpha(str[i])) i++ repeatedly checks each successive character in the str string until it finds an alphanumeric character, which is assumed to be the first character in the next command. Then, sscan(&str[i], “%s”, cmdbuf copies the word — all the characters until it reaches another non-alphanumeric — into a smaller character array named cmdbuf. Then, the index of the next character to check in str is updated with i += strlen(cmdbuf). Finally, if(!strcmp(cmdbuf, “end”)) break checks to see if the end AB command in the text file has been reached. If it has, break exits the while(1) loop.

  while(1)                                   // Loop through commands
  {
    // Parse command
    while(!isalpha(str[i])) i++;             // Find 1st command char
    sscan(&str[i], "%s", cmdbuf);            // Command -> buffer
    i += strlen(cmdbuf);                     // Idx up by command char count
    if(!strcmp(cmdbuf, "end")) break;        // If command is end, break

The distance argument for the AB command is parsed using similar techniques. The statement while(!isdigit(str[i])) i++ checks and rejects any character that’s not a digit. After it does find a digit, sscan(&str[i], “%s”, valbuf) copies the value to a smaller character array named valbuf. Again, the index of for the next character to check in str is updated, this time with i += strlen(valbuf). After figuring out the position of the next character to check in the str array, it’s time to convert the character representation of the distance argument to a value the program can use. That’s what val = atoi(valbuf) does. The term atoi is short for ascii to integer.

    // Parse distance argument
    while(!isdigit(str[i])) i++;             // Find 1st digit after command
    sscan(&str[i], "%s", valbuf);            // Value -> buffer
    i += strlen(valbuf);                     // Idx up by value char count
    val = atoi(valbuf);                      // Convert string to value

After parsing the command and distance arguments, all that’s left is to check what the command is and use the distance arguments accordingly. To figure out what command string was actually stored in cmdbuf, we use strcmp. This function returns a zero if two strings match, or nonzero if they do not. So, if(strcmp(cmdbuf, “forward”) == 0 drive_goto(val, val) checks to see if the contents of cmdbuf match the string “forward”. If it does, then it uses the distance argument stored in val and drive_goto to make the activitybot go the distance. If cmdbuf instead stored the string “backward”, the first if statement would not be true, but else if(strcmp(cmdbuf, "left") == 0)… would be true.

    // Execute command
    if(strcmp(cmdbuf, "forward") == 0)       // If forward
      drive_goto(val, val);                  // ...go forward by val
    else if(strcmp(cmdbuf, "backward") == 0) // If backward
      drive_goto(-val, -val);                // ... go backward by val
    else if(strcmp(cmdbuf, "left") == 0)     // If left
      drive_goto(-val, val);                 // ...go left by val
    else if(strcmp(cmdbuf, "right") == 0)    // If right
      drive_goto(val, -val);                 // ... go right by val

Once the parsing detects the AB end command and thus breaks out of the while(1) loop, the only thing left to do is close the navset.txt file on the SD card:

  }
 
  fclose(fp);                                // Close SD file
}

Here is the full code listing:

/*
 AB Interpreter.c

 http://learn.parallax.com/activitybot

 Fetches text commands from file on SD card, interprets them,
 and executes maneuvers. Requires abdrive 0.5.5 from
 ActivityBot library 2013-10-31 or later.

*/

#include "simpletools.h"                     // Library includes
#include "abdrive.h"  

FILE *fp;                                    // File pointer for SD
char str[512];                               // File buffer

char cmdbuf[10];                             // Command buffer
char valbuf[4];                              // Text value buffer
int  val;                                    // Value

int main()                                   // Main function
{
  int DO = 22, CLK = 23;                     // SD I/O pins
  int DI = 24, CS = 25;
  sd_mount(DO, CLK, DI, CS);                 // Mount SD file system
  fp = fopen("navset.txt", "r");             // Open navset.txt

  fread(str, 1, 512, fp);                    // navset.txt -> str
  int strLength = strlen(str);               // Count chars in str
  int i = 0;                                 // Declare index variable

  drive_speed(0, 0);                         // Speed starts at 0

  while(1)                                   // Loop through commands
  {
    // Parse command
    while(!isalpha(str[i])) i++;             // Find 1st command char
    sscan(&str[i], "%s", cmdbuf);            // Command -> buffer
    i += strlen(cmdbuf);                     // Idx up by command char count
    if(!strcmp(cmdbuf, "end")) break;        // If command is end, break

    // Parse distance argument
    while(!isdigit(str[i])) i++;             // Find 1st digit after command
    sscan(&str[i], "%s", valbuf);            // Value -> buffer
    i += strlen(valbuf);                     // Idx up by value char count
    val = atoi(valbuf);                      // Convert string to value

    // Execute command
    if(strcmp(cmdbuf, "forward") == 0)       // If forward
      drive_goto(val, val);                  // ...go forward by val
    else if(strcmp(cmdbuf, "backward") == 0) // If backward
      drive_goto(-val, -val);                // ... go backward by val
    else if(strcmp(cmdbuf, "left") == 0)     // If left
      drive_goto(-val, val);                 // ...go left by val
    else if(strcmp(cmdbuf, "right") == 0)    // If right
      drive_goto(val, -val);                 // ... go right by val
  }
 
  fclose(fp);                                // Close SD file
}


 


Did You Know?

Many of the functions used for files, strings, and characters come from the Standard C Libraries built into Propeller GCC. The simpletools library includes them for you, so they do not need to be included at the top of AB Interpreter. Here is a list of standard libraries and the functions used:

  • stdio.h: fopen, fread
  • string.h: strcmp, strlen
  • ctype.h: isalpha, isdigit
  • stdlib.h: atoi

My favorite resource for looking up and reading the documentation on these functions is IBM’s C library reference: http://pic.dhe.ibm.com/infocenter/is...sc41560702.htm


 

Try This

Let’s try expanding our AB language with two new commands: pivotLeft and pivotRight. These commands will keep one wheel still while turning the other so that the ActivityBot “pivots” on one wheel.

  • Modify NAVSET.TXT to use the new commands as shown below, then re-save it to the SD card.

    forward = 64
    pivotLeft = 53
    pivotRight = 106
    forward = 64
    end
     
  • Update AB Interpreter.side so it knows what to do with the new commands.  This means adding two new else if (strcmp... conditions and drive_goto calls.

  • Load EEPROM and run to run the modified example.
  • Verify that the ActivityBot goes forward a wheel turn, pivots about a quarter turn to the left, then about a half turn to the right, then goes forward and stops.

 

Your Turn

This program can also be modified to detect and handle line comments. For example, you could to place REM (for remark) at the beginning of the line that contained programmer’s notes. You could support this in the program by checking for the string, and then using while(str[i] != ‘\n’ && str[i] != ‘\r’) i++ to disregard all the characters to the end of the line.

Also consider that this current example limits the program size to 512 characters. Modifying the program to load the next 512 characters could be tricky. How would you approach it?