Draft: Make Your Own ActivityBot Programming Language
Andy Lindsay (Parallax)
Posts: 1,919
Overview: This is a draft of a tutorial intended to spark some additional interest in more tutorials on the basics of string manipulation. The current plan is to link from this tutorial to some smaller ones that cover more string handling building blocks and fundamental concepts that go into this application. Since it’s intended to be a lighter introduction, certain file and string formalities were omitted from the example. The plan is also to address them in separate tutorials.
Make Your Own ActivityBot Programming Language
Up until now, we’ve been making programs in C language that make the ActivityBot perform various tasks. In this activity, you will experiment with a program that opens a file from the SD card that’s in another programming language. Let’s call it AB. The AB programming language has five commands, forward, backward, left, right, and end. Each command has a single argument, which is the distance the ActivityBot should travel in encoder ticks. Your C program will interpret and execute each AB command.
Circuit
We will not be using any sensor circuits here, just the SD card.
Test Code
Start by downloading the application zip and then copy its folder into your ActivityBot tutorial folder.
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 ActivityBot that will open the file, and fetch and execute its AB language commands. It also has a test ActivityBot library that takes somewhat less space than the ActivityBot 2013-09-03b currently available from learn.parallax.com.
How it Works
i-box: To learn more about SD data storage and retrieval, go to http://learn.parallax.com/propeller-c-simple-devices/sd-card-data. For an introduction to strings, go here (future links).
After opening navset.txt, the application copies up to 512 bytes from the text file to a 512 byte buffer named str. Then, the strLength = strlen(str) function figures out how many bytes are in the string and copies it to the strLength variable. Before any modifications, there are about 90 characters in the navset.txt.
The first task inside the while(1) loop for parsing commands is to find the first printable character. There might be carriage returns or blank spaces leading up to the first character in the command. So, while(!isalpha(str)) 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. sscan(&str, “%s”, cmdbuf copies the word (up to the next non alphanumeric character) into a smaller character array named cmdbuf. Then, the index of for the next character to check in str is updated with i += strlen(cmdbuf). Last, but far from least, if(!strcmp(cmdbuf, “end”)) break checks to see if the end command has been reached. If it has, the break command exits the while(1) loop.
The distance argument is parsed using similar techniques. For example, the statement while(!isdigit(str)) i++ checks and rejects any character that’s not a digit. After it does find a digit, sscan(&str, “%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.
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.
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 has #includes for them, 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:
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/iseries/v7r1m0/index.jsp?topic=%2Frtref%2Fsc41560702.htm
Try this
Let’s try expanding the AB language with a couple new commands: pivotLeft and pivotRight. These commands will keep one wheel still while turning the other so that the ActivityBot “pivots” on one wheel.
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 != ‘\n’ && str != ‘\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?
Make Your Own ActivityBot Programming Language
Up until now, we’ve been making programs in C language that make the ActivityBot perform various tasks. In this activity, you will experiment with a program that opens a file from the SD card that’s in another programming language. Let’s call it AB. The AB programming language has five commands, forward, backward, left, right, and end. Each command has a single argument, which is the distance the ActivityBot should travel in encoder ticks. Your C program will interpret and execute each AB command.
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.
Test Code
Start by downloading the application zip and then copy its folder into your ActivityBot tutorial folder.
- Make sure you have the latest SimpleIDE software, Learn folder, and ActivityBot Library.
- Download the ActivityBot Navigate with SD Code.
- Unzip the folder, and copy its SD Navigation folder to …Documents\SimpleIDE\Learn\Examples\ActivityBot Tutorial.
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 ActivityBot that will open the file, and fetch and execute its AB language commands. It also has a test ActivityBot library that takes somewhat less space than the ActivityBot 2013-09-03b currently available from learn.parallax.com.
- Use your file browser to find and open navset.txt, and verify that it contains the program with the forward, left, right, backward, and end commands.
- Close the text file.
- In SimpleIDE, SimpleIDE’s Open Project button.
- Open AB Interpreter from …Documents\SimpleIDE\Learn\Examples\ActivityBot Tutorial\SD Navigation.
- Click Program and select File to SD Card.
- Select NAVSET.TXT and click Open. This will load the file onto the SD card. You can also verify that it loaded correctly by checking the build status pane. If it isn’t already open, just click the Show Build Status button at the bottom of the SimpleIDE window.
- Now for running the interpreter. Click Load EEPROM and Run.
- 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
i-box: To learn more about SD data storage and retrieval, go to http://learn.parallax.com/propeller-c-simple-devices/sd-card-data. For an introduction to strings, go here (future links).
After opening navset.txt, the application copies up to 512 bytes from the text file to a 512 byte buffer named str. Then, the strLength = strlen(str) function figures out how many bytes are in the string and copies it to the strLength variable. Before any modifications, there are about 90 characters in the navset.txt.
The first task inside the while(1) loop for parsing commands is to find the first printable character. There might be carriage returns or blank spaces leading up to the first character in the command. So, while(!isalpha(str)) 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. sscan(&str, “%s”, cmdbuf copies the word (up to the next non alphanumeric character) into a smaller character array named cmdbuf. Then, the index of for the next character to check in str is updated with i += strlen(cmdbuf). Last, but far from least, if(!strcmp(cmdbuf, “end”)) break checks to see if the end command has been reached. If it has, the break command exits the while(1) loop.
/* AB Interpreter.c */ #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 }
The distance argument is parsed using similar techniques. For example, the statement while(!isdigit(str)) i++ checks and rejects any character that’s not a digit. After it does find a digit, sscan(&str, “%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.
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.
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 has #includes for them, 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/iseries/v7r1m0/index.jsp?topic=%2Frtref%2Fsc41560702.htm
Try this
Let’s try expanding the AB language with a couple 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 so that it looks like this.
- Save and load the modified file to your board’s SD card.
forward = 64
pivotLeft = 53
pivotRight = 106
forward = 64
end - Modify navset.txt so that it looks like this, and use 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.
// Execute command if(strcmp(cmdbuf, "forward") == 0) drive_goto(val, val); else if(strcmp(cmdbuf, "backward") == 0) drive_goto(-val, -val); else if(strcmp(cmdbuf, "left") == 0) drive_goto(-val, val); else if(strcmp(cmdbuf, "right") == 0) drive_goto(val, -val); else if(strcmp(cmdbuf, "pivotLeft") == 0) // <-- Add drive_goto(0, val); // <-- Add else if(strcmp(cmdbuf, "pivotRight") == 0) // <-- Add drive_goto(val, 0); // <-- Add
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 != ‘\n’ && str != ‘\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?
Comments