phoenixgarage.org
As someone who has used Linux since 1995 (I started my Linux wanderings with a copy of TurboLinux 2.0, and sucessfully installed it on a 486 laptop. I currently use Mandrake 10.1. Oh yes, I am a masochist, why do you ask?), I have come to expect (especially for niche applications, such as the tokenizing and uploading of code for a third-party microcontroller product whose supplied tools and drivers tend to favor a Windows-based approach) that things may not always work out as planned.
So I was pleasantly surprised to find, albeit after some minor hiccoughs (which will be detailed later), that configuring Linux to tokenize and upload PBASIC code to the BASIC Stamp 2 turned out to be simpler than expected.
Installing the BASIC Stamp Tools for Linux Toolset
To begin your my journey down this road, I first obtained a copy of the BASIC Stamp Tools for Linux (new window) toolset, which contains everything needed to begin. Please note that the package only works under x86 Linux (ie, IBM-PC compatibles), and will only tokenize code meant for the Parallax BASIC Stamp 2 series of microcontrollers, which include:
- BASIC Stamp 2
- BASIC Stamp 2e
- BASIC Stamp 2sx
- BASIC Stamp 2p
- BASIC Stamp 2pe
- BASIC Stamp 2px
Once it was downloaded, I opened a shell and extracted the toolset package to a sub-directory located in my home directory. After the extraction process completed, I then changed to the extracted toolset package directory and read the README file, then installed the toolset according to the directions (like other source code installs, you need to become root to complete the process).
The installation process compiled and installed the toolset, including the most recent version (1.23) of Parallax's tokenizer library. This also seems to be the only way to get the Linux version of the tokenizer library. Parallax offers a link to download it from their web site, but the link doesn't resolve to a download page for the library. I have emailed the webmaster about this oversight, but as of this writing, I have yet to hear back from him.
Setting up the Serial Port
After installing the toolset, I then needed to configure an available serial port. My system had the first serial port free (ie, "/dev/ttyS0" or COM1), so I decided to configure the system to use that port. Had the serial port been in use (say, by a mouse or other device, for instance), then I would have had to choose and configure the system to use a different free port.
After I decideded on which serial port to use, I then needed to set up a symbolic link to the device called "/dev/bstamp", (the Parallax tokenizer library communicates to the port via this symbolic link, instead of directly through the port itself). I did this by adding a new ruleset to the udev rules directory ("/etc/udev/rules.d") named "bstamp.rules". This ruleset definition contained the following single line:
KERNEL="ttyS0", SYMLINK="bstamp"
I have created a tar.gz archive file in which you can find a copy of this ruleset to use on your own system, which may be downloaded via a link, below (this archive will henceforth be referred to as "the archive"). Once extracted, you will find a sub-directory named "/udev", which contains a copy of the "bstamp.rules" file.
This file instructs udev to dynamically create the symbolic link named "bstamp" in the devices directory, and link it to the kernel device "/dev/ttyS0". In my case, udev creates the device and symbolic link at boot time, but if you have a system in which you can dynamically alter the number of serial ports available without a shutdown, udev should create and/or remove the symbolic link to "/dev/bstamp" as the situation demands.
Setting up LD_LIBRARY_PATH - Part 1
The last thing to configure is the LD_LIBRARY_PATH environment variable. I wasn't aware this was necessary when I first attempted to tokenize a Stamp program (a "Hello World" example program, which comes with the toolset, named "hello.bs2", designed to flash an LED connected to P0):
$ ls -als
total 40
4 drwxrwxr-x 2 andrewa andrewa 4096 Aug 4 23:34 ./
4 drwxrwxr-x 3 andrewa andrewa 4096 Jul 27 22:59 ../
4 -rw-rw-r-- 1 andrewa andrewa 95 Apr 16 2003 hello.bs2
$
'{$STAMP BS2}
LOOP:
DEBUG "Hello world", CR
HIGH 0
PAUSE 1000
LOW 0
PAUSE 1000
GOTO LOOP
I ended up getting the following errors:
$ bstamp_tokenize hello.bs2 hello.tok
libbstamptokenizer.so: cannot open shared object file: No such file or directory: Success
./tokenizer.so: cannot open shared object file: No such file or directory: Illegal seek
$
These errors meant that 'bstamp_tokenize' couldn't find the shared tokenizer library provided by Parallax. It then looked for a copy of the tokenizer in the current directory (where the source code was), and it wasn't able to find it there, either.
After I verified that the tokenizer library was installed as a shared library in the expected location (which it was, in "/usr/local/lib/libbstamptokenizer.so"), I did some research on Google for as to why the error was occurring. I eventually found discussion surrounding the environment variable LD_LIBRARY_PATH.
Essentially, this variable needs to be set to point at the location of the library. However, it shouldn't permanently point at that location, because in the future it may need to be set to something else (for compiling other software, or for other developmental use, I think).
I found that if I set this variable to the proper path, the tokenizer worked properly and the program was converted:
$ export LD_LIBRARY_PATH="/usr/local/lib"
$ bstamp_tokenize hello.bs2 hello.tok
PBASIC Tokenizer Library version 1.23
$ ls -als
total 40
4 drwxrwxr-x 2 andrewa andrewa 4096 Aug 4 23:34 ./
4 drwxrwxr-x 3 andrewa andrewa 4096 Jul 27 22:59 ../
4 -rw-rw-r-- 1 andrewa andrewa 95 Apr 16 2003 hello.bs2
4 -rw-r--r-- 1 andrewa andrewa 54 Aug 4 23:34 hello.tok
$
Setting up LD_LIBRARY_PATH - Part 2
To avoid all of the above confusion, before you attempt to tokenize anything, type the following into a console shell:
$ env | grep LD_LIBRARY_PATH
If nothing is returned, then you will definitely need to set this variable up. If something is returned it should contain a reference to where the tokenizer library is installed, typically in "/usr/local/lib":
$ env | grep LD_LIBRARY_PATH
LD_LIBRARY_PATH=:/usr/local/lib
$
If it doesn't have a reference to where the tokenizer library is installed, then you will need to set the LD_LIBRARY_PATH variable to contain the reference. The quickest and easiest way to do so is to set it directly, as detailed above in "Part 1":
$ export LD_LIBRARY_PATH="/usr/local/lib"
$
This method, while simple, has a couple of glaring drawbacks: First, you have to remember and type that entire string any time you open a new console to tokenize a PBASIC program, and second, the settings of the path prior to the export are lost. Since I knew from previous experience that both of these issues would cause me headaches, so I decided to dispatch them early. I ended up writing a small shell script to perform these actions:
#!/bin/bash
# Usage:
#
# . pblibset -set | -s | -unset | -u | -status
#
# NOTE: Must use sourcing of script to have the path exported in proper run scope
ISSET=`echo ${LD_LIBRARY_PATH} | /bin/fgrep -e "/usr/local/lib"`
if [ "$1" = "-set" -o "$1" = "-s" ]
then
if [ ! $ISSET ]
then
export OLD_LD_LIBRARY_PATH=$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH":/usr/local/lib"
echo "Set"
env | grep LD_LIBRARY_PATH
fi
else
if [ "$1" = "-unset" -o "$1" = "-u" ]
then
if [ $ISSET ]
then
export LD_LIBRARY_PATH=$OLD_LD_LIBRARY_PATH
if [ ! $LD_LIBRARY_PATH ]
then
unset LD_LIBRARY_PATH
fi
unset OLD_LD_LIBRARY_PATH
echo "Unset"
fi
else
if [ "$1" = "-status" ]
then
if [ $ISSET ]
then
echo "Status: Set"
env | grep LD_LIBRARY_PATH
else
echo "Status: Unset"
fi
else
echo "Usage: . pblibset -set | -s | -unset | -u | -status"
fi
fi
fi
By using this script, I could set or reset LD_LIBRARY_PATH to the proper setting as needed. This shell script is available in the archive, within the sub-directory named "/pblibset". Locate this script either in a common area (that is, in a sub-directory near to your PBASIC source code), or place it in a shared directory on your system (ie, /user/local/bin). Either way, it needs to be run by using "sourcing" of a script:
$ . pblibset -s
Set
LD_LIBRARY_PATH=:/usr/local/lib
OLD_LD_LIBRARY_PATH=
$ env | grep LD_LIBRARY_PATH
LD_LIBRARY_PATH=:/usr/local/lib
OLD_LD_LIBRARY_PATH=
$ . pblibset -u
Unset
$ env | grep LD_LIBRARY_PATH
$
Note the period prior to the script name, which tells the shell to use sourcing to run the script. Sourcing allows the script to properly set the LD_LIBRARY_PATH environment variable within the scope context of the current shell it is running within (instead of its own). This is the primary usage of sourcing a shell script; another is to use it to act as a method to include files into a shell script (the explanation of which is beyond the scope of this article).
BASIC Stamp 2 Testing Schematic
Before I could actually test my BASIC Stamp 2 by tokenizing and running the "Hello World" PBASIC program, I needed to set up a simple test circuit on a breadboard:
BASIC Stamp 2 OEM Test Circuit Questions or Comments? |
The following schematic details the layout of the circuit in the above image:
BASIC Stamp 2 OEM Test Circuit (Schematic) Questions or Comments? |
Tokenizing and Running a PBASIC Program - Part 1
Once I had my port selected and configured properly, and a method to update my LD_LIBRARY_PATH, and a test circuit constructed, I was ready to connect my BASIC Stamp 2 to my PC. For the OEM module I was using, all that I needed to do was connect a 9-pin D-Sub M-F serial extension cable from the PC to the module's connector. Other versions of the BASIC Stamp 2 module may require additional hardware to connect it to the PC (there are several ways to do so, I won't detail them here).
Tokenizing and running a PBASIC program on a BASIC Stamp 2 is nominally a two part process: The PBASIC source code is first tokenized (using the 'bstamp_tokenize' command from the toolset), then the resulting tokenized code is "run" (using the 'bstamp_run' command from the toolset), which uploads the tokenized code from the PC to the Stamp, which then executes it.
Let's look at the steps individually, then we'll see how to combine them into a single line.
Tokenizing a PBASIC Program
I have already shown how a PBASIC program is tokenized, and with careful reading one should be able to figure out how to incorporate 'pblibset' into the mix. Change to the directory which contains 'pblibset', and run it to set up the LD_LIBRARY_PATH:
$ . pblibset -s
Set
LD_LIBRARY_PATH=:/usr/local/lib
OLD_LD_LIBRARY_PATH=
$
Then change to the directory which contains the Basic Stamp 2 code you want to tokenize:
$ ls -als
total 40
4 drwxrwxr-x 2 andrewa andrewa 4096 Aug 4 23:34 ./
4 drwxrwxr-x 3 andrewa andrewa 4096 Jul 27 22:59 ../
4 -rw-rw-r-- 1 andrewa andrewa 95 Apr 16 2003 hello.bs2
$
The Basic Stamp 2 source code file ("hello.bs2") contains the following code:
'{$STAMP BS2}
LOOP:
DEBUG "Hello world", CR
HIGH 0
PAUSE 1000
LOW 0
PAUSE 1000
GOTO LOOP
Now use the 'bstamp_tokenize' command to tokenize the code:
$ bstamp_tokenize hello.bs2 hello.tok
PBASIC Tokenizer Library version 1.23
$ ls -als
total 40
4 drwxrwxr-x 2 andrewa andrewa 4096 Aug 4 23:34 ./
4 drwxrwxr-x 3 andrewa andrewa 4096 Jul 27 22:59 ../
4 -rw-rw-r-- 1 andrewa andrewa 95 Apr 16 2003 hello.bs2
4 -rw-r--r-- 1 andrewa andrewa 54 Aug 4 23:34 hello.tok
$
That's all there is to it!
Running a PBASIC Program
Uploading and running the tokenized code is a simple process as well. First, change to the directory which contains 'pblibset', and run it to set up the LD_LIBRARY_PATH (if not previously set):
$ . pblibset -s
Set
LD_LIBRARY_PATH=:/usr/local/lib
OLD_LD_LIBRARY_PATH=
$
Then change to the directory which contains the Basic Stamp 2 code you want to run:
$ ls -als
total 40
4 drwxrwxr-x 2 andrewa andrewa 4096 Aug 4 23:34 ./
4 drwxrwxr-x 3 andrewa andrewa 4096 Jul 27 22:59 ../
4 -rw-rw-r-- 1 andrewa andrewa 95 Apr 16 2003 hello.bs2
4 -rw-r--r-- 1 andrewa andrewa 54 Aug 4 23:34 hello.tok
$
Then use the 'bstamp_run' command to upload the code to the Stamp and run it:
$ bstamp_run hello.tok
Basic Stamp detected!Model: Basic Stamp 2
Firmware version in BCD = 16
18 characters transmitted
18 characters echoed
18 characters transmitted
18 characters echoed
18 characters transmitted
18 characters echoed
DEBUG OUTPUT: (Press [Control]-[C] to complete sequence)
_____________________________________________________________________
Hello world
Hello world
Hello world
_____________________________________________________________________
Received [Control]-[C]!
H
Shutting down communication!
$
Once again, simplicity!
Tokenizing and Running a PBASIC Program - Part 2
It isn't necessary to perform each of these steps separately, unless the tokenized output of 'bstamp_tokenize' is needed as a separate file. Instead, use pipes to combine the commands into a single line:
$ ls -als
total 40
4 drwxrwxr-x 2 andrewa andrewa 4096 Aug 4 23:34 ./
4 drwxrwxr-x 3 andrewa andrewa 4096 Jul 27 22:59 ../
4 -rw-rw-r-- 1 andrewa andrewa 95 Apr 16 2003 hello.bs2
$ cat hello.bs2 | bstamp_tokenize | bstamp_run
PBASIC Tokenizer Library version 1.23
Basic Stamp detected!Model: Basic Stamp 2
Firmware version in BCD = 16
18 characters transmitted
18 characters echoed
18 characters transmitted
18 characters echoed
18 characters transmitted
18 characters echoed
DEBUG OUTPUT: (Press [Control]-[C] to complete sequence)
_____________________________________________________________________
Hello world
Hello world
Hello world
_____________________________________________________________________
Received [Control]-[C]!
Shutting down communication!
$
Finally, unset the LD_LIBRARY_PATH:
$ . pblibset -u
Unset
$
All of these steps could be combined into a single shell script, but a makefile is more efficient.
Using a Makefile to Tokenize and Run a PBASIC Program
A makefile can be easily created, which when used with the 'make' command, allows for efficient flexibility in retokenizing and running an existing PBASIC code project. The make command can be invoked with or without specifying a "target":
$ make
or
$ make hello
An example makefile will help to illustrate:
# makefile for hello.bs2
all: hello.tok
clean:
-rm hello.tok
hello.tok: hello.bs2
bstamp_tokenize hello.bs2 hello.tok
run: hello.tok
bstamp_run hello.tok
This makefile would be saved in the same directory as the source code (this isn't absolutely necessary, provided that the paths to code and objects are properly identified), and named "Makefile".
The makefile consists of individual labeled sections called "targets":
- all:
- clean:
- hello.tok:
- run:
The first target, "all" is the default target when the 'make' command is used. In other words, running 'make' is the same thing as running 'make all'.
Each target label is followed by a dependency list. These dependency names identify individual targets in the makefile whose own dependencies must be satisfied in order to execute the target section. If the depedency name does not match any of the target labels, then the dependency name represents a file which must exist in the directory in which the makefile resides before the section will be executed. If the dependencies for the target are not satisfied, 'make' outputs an error message and exits.
Thus, with the example makefile, running 'make' (ie, 'make all') causes a cascade effect:
- The "all" target is found, it has a single dependency name, and it has no other executable section commands. it is dependent on any dependencies defined for the "hello.tok" target.
- The "hello.tok" target is found, it is dependent on the file "hello.bs2" existing in the same directory as the makefile (the source code is necessary to create a tokenized version; it is also the only dependency of this target, and the dependency name "hello.bs2" did not correspond to any other labeled targets).
- The executable command section of the target "hello.tok" is run, all dependencies having been satisfied. The 'bstamp_tokenize' command is used to tokenize the source code of "hello.bs2" into "hello.tok".
- Since there was no executable command section for the target "all", 'make' is completed and exits.
After the source code is updated with an editor, running 'make' will verify the existance of the code and tokenize it.
The "clean" target serves to remove the tokenized form of the "hello.bs2" file.
The "run" target is a little more complex: running 'make run' also causes a cascade, but a slightly more complex one:
- The "run" target is found, it has a single dependency name of "hello.tok". It has an executable command section with a command, but the command won't be executed until the dependencies are satisfied, which are the ones for the "hello.tok" target.
- The "hello.tok" target is found, it is dependent on the file "hello.bs2" existing in the same directory as the makefile (the source code is necessary to create a tokenized version; it is also the only dependency of this target, and the dependency name "hello.bs2" did not correspond to any other labeled targets).
- The executable command section of the target "hello.tok" is run, all dependencies having been satisfied. The 'bstamp_tokenize' command is used to tokenize the source code of "hello.bs2" into "hello.tok".
- The dependencies for the "run" target have been met, since the dependencies for "hello.tok" were met. Thus, the executable command section of the target "run" is run: The 'bstamp_run' command is used to upload the tokenized code "hello.tok" to the Stamp, which then runs the program.
- All commands complete, 'make' is completed and exits.
Basically, invoking 'make run' causes all of the same things to occur as invoking 'make', with the addition of actually uploading the tokenized code onto the Stamp.
The above represents a simple example of a makefile, which can be even more complex. The following tutorial details extra makefile abilities, including variables and comments:
In addition, the GNU make utility manual is available here:
Going Beyond the Command Line
All of the above command line oriented detail has its place and uses, but for my own current needs, I wanted to utilize my preferred text editor, the KDE Kate editor, to edit my code (preferably with syntax highlighting). If I could use the editor to send the code to the Stamp (without needing to switch context from the GUI to a console and back again), it would be perfect. So, I started to look into what could be done for using Kate as an IDE frontend for PBASIC.
I found the following tutorial for those who have a recent version of Kate:
In the archive, you will find a sub-directory named "/gaess", which contains updated versions of the BS-compile* scripts to properly set and reset the LD_LIBRARY_PATH as needed. Install these only if you actually need them (see the discussion in the above sections entitled "Setting up LD_LIBRARY_PATH" on setting the path and why it may be needed).
I had an older version of Kate, and the above tutorial wouldn't work for me, as my version (Kate v2.2.1) is rather outdated and only had DCOP scripting available, not the new external scripting system available in more recent versions of Kate.
I did install the syntax highlighting file. I found that it worked fine and without any problems. I then decided to create my own DCOP script to perform the same function as the BS-compile* scripts. With a lot of trial and error while browsing details in 'kdcop' (DCOP object reference browser), and studying the following tutorial:
I was able to create my DCOP application, which can be found in the archive under the sub-directory named "/bstamper". Two files are available:
- bstamper.desktop
- bstamper.sh
Place both of these files in the following directory:
~/.kde/share/apps/kate/scripts/
Shutdown and restart Kate, and if you check the menu item "Tools -> KDE Scripts", you should find an entry for "BStamper (by phoenixgarage.org)". Load up a Basic Stamp 2 PBASIC file in Kate to tokenize, and run the BStamper tool by selecting it from the menu. It should be fairly self-explanatory and easy to use; contact me if you have any problems.
Please don't alter the BStamper script unless you are absolutely certain you know what you are doing. DCOP is really picky. If you do decide to alter the code, keep backups in case you get stuck so you can revert back to a prior version.
Conclusion
Developing PBASIC software for upload to the Parallax BASIC Stamp 2 turned out to be both easier than expected, but nonetheless fairly complicated. Even so, I believe that developing software to control my UGV will prove to be both fun and educational, not to mention fairly quick. I am betting that the development and debugging of the hardware is going to be a greater challenge than the development of the initial control software. Linux is proving to be an excellent platform for robotics development.
Appendix
An older project, called Ptoke, pre-dates the "BASIC Stamp Tools for Linux" by a few months, but doesn't seem to be active at all:
Ptoke's sourceforge repository was created on January 6, 2003, while the "BASIC Stamp Tools for Linux" SF repository was created on April 9, 2003. As of this writing, the "BASIC Stamp Tools for Linux" site hasn't been updated since May 31, 2006, while Ptoke's SF repository hasn't been updated since January 10, 2003...
I have read that the "BASIC Stamp Tools for Linux" toolset has an issue handling the PBASIC DEBUGIN (as opposed to DEBUG) statement (within the console 'bstamp_run' invokes):
The example program (from the forum) demostrating this:
'{$STAMP BS2}
'{$PBASIC 2.5}
counter VAR Word
pulses VAR Word
duration VAR Word
DO
DEBUG CLS, "Enter number of pulses:", CR
DEBUGIN DEC pulses
DEBUG "Enter pulsout duration:", CR
DEBUGIN DEC duration
DEBUG "Servo is running....", CR
FOR counter = 1 to pulses
PULSOUT 14, duration
PAUSE 20
NEXT
DEBUG "DONE"
PAUSE 1000
LOOP
The temporary solution (until 'bstamp_run' is fixed), is to 'echo' the input to the Stamp via another console session:
$ echo 100 > /dev/bstamp
$
As an alternative solution to the above problem, you might try out "BSide - A Basic Stamp IDE":
According to the page, the release of "Bside 0.05 Beta 2" corrects the DEBUGIN issue.
Take a look at the above example again, and compare it to the example in the "Tokenizing a PBASIC Program" section near the beginning of this article.
Notice something?
At the top of the source code, is the follow line
'{$STAMP BS2}
While at the top of the above sample code, are the lines:
'{$STAMP BS2}
'{$PBASIC 2.5}
These are both important tokenizer directives - you should always use both when compiling for the BASIC Stamp 2. The first directive, $STAMP, tells the tokenizer what version of the Basic Stamp to target, in this case, the BASIC Stamp 2. The second directive, $PBASIC, tells the tokenizer what version of PBASIC tokens to generate, in this case PBASIC version 2.5.
The test circuit described has two LEDs. The example code, "hello.bs2", only blinks one of the LEDs, slowly. The following code will blink both LEDs, in a quicker manner:
'{$STAMP BS2}
'{$PBASIC 2.5}
LOOP:
DEBUG "Hello world 2", CR
HIGH 0
PAUSE 250
LOW 0
HIGH 1
PAUSE 250
LOW 1
GOTO LOOP
Although I haven't tried it, it should be possible to use a USB port and an FTDI-chipset USB-to-Serial cable or dongle to connect the PC to the Stamp. The PC side would be connected to a USB port, and the serial port converter would be connected to the Stamp. Create the symbolic link for "/dev/bstamp" as described in the section "Setting up the Serial Port" above, but instead of linking it to "/dev/ttyS0", link it to "/dev/usb/ttyUSBx", where "x" is the number of the serial port to use. See also:
| Downloads | |||
| Name | Description | Type | Size |
| archive.tar.gz | The Archive | 3.18 Kb | |
Post New Thread