This section describes a modification I made to the Scratch programming environment in order to control real, physical robots through a
simple serial protocol. Scratch is a very simple, intuitive, visual programming environment that was designed by
the Lifelong Kindergarten group at the Massachusetts Institute of
Technology (MIT). Take a quick tour here.
was designed to let kids (and adults!) explore the world of programming
in a fun and interesting way. Not only are kids making neat
utilities and animations, but they are also using it to help solve and
present homework and class project exercises.
My objective in modifying Scratch
to extend the blocks in the environment to allow kids at my children's
school to control real, physical robots. This section
describes my effort to do that and the learning that took place (about Smalltalk-80 and Squeak) along the way.
I hope, for those who dig under the Scratch hood, that I have
somehow saved you some time.
NOTE: Scratch has gone
through a number of evolutions. This project began with Version
1.3. During that time, Version 1.4 came out. Recently,
Scratch has become a web-based application running in Flash. I prototyped and learned in Version 1.3. I implemented a "final product" in Version 1.4 specifically for the Raspberry Pi running Raspbian.
What's an "Itchbot?"
confusion, I am obliged by the MIT Scratch license agreement to call my
creation something other than "Scratch". Since I was using scratch to make robots, I thought
"Itchbots" would be an appropriate pun.
This section provides
a quick overview of how to program a robot using the modifications that
I made to the Scratch environment.
Note: "Itchbots" is a modification of Version
1.3 of Scratch so if you
download and use it, you may not find the interface to be exactly what you
expected since Scratch has moved on.
Also note that this tour assumes that you have built a robot and that the robot contains a code module to respond to Itchbot commands.
Step 1: Download the Code
to download the latest zip file. Unzip the contents to a suitable location.
Step 2: Starting Itchbots
to download the latest zip file. Unzip the contents to a suitable location.
Open the directory into which you unzipped
Itchbots. As shown in the figure below, to start Itchbots, drag the file
called HardWareItchbotsXXXX. onto the file
called Itchbots.exe (the one with the little blue robot symbol). The X's represent a date stamp.
Itchbots will open and present an interface similar to the one below.
Step 3: Connect a Robot and Set Up Communications
This step assumes that you have a robot and that it's ready to be controlled. If you are here in the "quick start" then that's not likely the case. You can go here to see how to build one and the code to install on it.
Connect the robot to the serial interface of the computer. If you
are using a pair of XBee wireless modems, connect one to your robot and
one to the comptuer serial port. Alternatively, your computer
may only have USB ports in which case you will need an XBee serial2usb
Turn on your
robot. If your are using the XBee, the red light
should start blinking.
In the ItchBots
window, click on the Extras button. You
get a menu as shown in the figure below.
From this menu, select "Set Robot ID". Type in the ID number
of your robot and click OK. In the figure shown below, the ID number of
the robot is 4
Click on the Extras button again and from the menu, select "Serial
Comms...". A round communications indicator opens
up in the
main scratch window with the word "Off" written in it (shown in the
Right click on the communications monitor. A menu will open
Slide the mouse pointer over and click on "Select serial/USB
port." The communications port options selector shown below
opens. Click on the appropriate comm port.
The communications monitor switches to the word "On." If it
doesn't, double check which port you are working with. You
move the communications monitor to somewhere else within the scratch
interface so that it doesn't get buried under sensor reporters.
You are now ready to start commanding the robot.
Step 4: A Sample Program
The scripts for a
basic first program are shown in the figure
below. The objective of this script is to give directional
control of the robot to the person using Itchbots through the arrow
keys. Using any of the arrow keys will move or rotate the
(there is no need to hold the keys down - the robot does whatever its
told until it is told to do somethine else).
space key will stop all robot motion.
When the letter "a" is clicked, a continous loop directs the robot to
report back on the value of four proximity sensors. In order
prevent saturating the robot with sensor requests, a short time delay
is inserted into the loop.
You can see what the sensor values are by reporting them. If
scroll down the Sensors pane, you will find a list of reporters
as shown in the figure below.
Click on the checkbox beside the Proximity Sensor 1 reporter.
reporter shows up in the stage window as shown in the figure below.
To report on Proximity Sensor 2 as well, select the drop down list of
Proximity Sensor names as shown below.
Select proximity sensor 2 from the list. As shown below, you
notice that the reporter now says Proximity Sensor 2 but the checkbox
Click on the checkbox beside the reporter and you'll see a reporter bar
for Proximity Sensor 2 appear in the stage window (below).
sample program is not
particularly clever but it does identify the basic functionality of the
system. A simplification can be made by replacing each sensor
Update button with a single button that updates all proximity
Developing in Squeak
As I mentioned earlier, the Scratch source code and instructions for loading and looking at it are provided
here. There is also a short tutorial on editing blocks in Scratch that can be found
here that beginners may find useful. I know I did.
To get things going quickly, you need to:
Download and install Scratch;
Download the Scratch sourcecode.
Download and Install Scratch
Go to the Scratch downloads site and pull a copy of Scratch. It may
be a bit of a challenge finding the application since the authors have
changed directions and rebuilt Scratch to run in the browser rather
than running as a native application.
Note also that Scratch 1.3 appears to have
been scrubbed from the MIT website. If you want to download it,
you are pointed to version 1.4. I still have version 1.3 but need
to find it in my archive. When I do, I will post a copy here.
Version 1.3 used to have a feature that allowed you to download and run
it without an installer. That seems to have also gone by the
wayside (version 1.4 must be installed using an installer which may be
problem for those without administrative access to their desktop).
of whether you use my version 1.3 or install version 1.4 from the MIT
website, the process of getting and working with the sourcecode shown
below is equivalent.
Once you've installed a version of
Scratch, you will want to find a file called "Scratch.exe" or
"Scratch.app" in whatever directory or location in which it was placed.
Get the Sourcecode
Go to the source download page and pull down Scratch version 1.4 sourcecode.
Once you've pulled down the source zip file, unzip it into a working space. Inside, you
will find the files shown in the figure below.
To make Scratch run, we need to understand a bit about how Scratch works. Scratch
is written in Squeak which is an open-sourced version of Smalltalk-80.
Smalltalk-80 is an object-oritented language specification
created in the 80's. Like JAVA, code written in Squeak/Smalltalk
does not run natively on X86 processors. In order to enable code
to run, a layer of translation must be performed. This is done
using a Virtual Machine (like the Java Virtual Machine).
has a virtual machine that allows you to run smalltalk code on an x86.
For Scratch, this virtual machine is called Scratch.exe or
Applications written in Squeak are saved in
image files. The ScratchSourceCode1.4.image file shown in the
figure above is a basic Squeak image that has been
modified significantly to create Scratch. The modifications are
exported from the basic programming environment in the
a copy of Scratch.exe and paste it into the working directory
containing the Scratch sources. Your directory should look
something like the one below.
Put the cursor over the ScratchSourceCode1.X.image file, left-click, and drag it onto the Scratch.exe icon as shown below.
Depending on your version of Windows, you might get the following warning:
If you select "Run", you should be rewarded with the following window:
window provides you with the Squeak Browser and all of the tools
necessary to make and save modifications to Scratch Version 1.3 or 1.4.
We are now ready for buisiness.
Navigating the Programming Environment
There are a
number of good texts on the net that describe how to use the Squeak
interface for programming. The thing I want to focus on here are
a few tricks and tips I picked up along the way.
shift-rightClick can give you access to a number of hidden menus in
Scratch. In Scratch version 1.4, if you rightClick "File" in the
Scratch programming environment (rightClick "Extras" in version 1.3),
you get the following options:
However, if you use shift-rightClick, you get:
There are two groups of new entries in the figure above. The first
group concerns writing project summaries. I've never used these
options. The second group allows you to hide the programming
interface. The first one, "Enter User Mode", expands the Scratch
window to the full size of the programming window. If you close
the project and restart, the Scratch window returns to a small portion
of the programming window. If you want it to fill the entire
programming window when you restart, choose "Save Image in User Mode".
This option is useful for hiding the programming details when you
are testing with users.
Note that, when you put the display
into user mode, you will need to use shift-rightClick on "File" (or
"Extras") again in order to exit user mode.
It is also possible to access the Squeak programming environment
in a version of Scratch that does not include the sources. For
example, I downloaded Scratch 1.4 and used the installer to put it on
my machine. I started the program by clicking on the Scratch
desktop icon. To get access to the programming environment, you
need to place the tip of the cursor into the hole in the letter R in
the word "Scratch" and shift-rightClick. This gives you menus as
You can return to filling the entire screen by shift-rightClick in the R again.
Searching for Stuff
know we haven't looked at the various windows in Squeak yet. But
in the near future you will be looking for something in the code and
will wish for a simple, global search box. There are a couple of
crazy ways to do searches and, until I asked, those were what I used.
The way to properly search is as follows:
Type or select the thing you want to search for. Note that this will be done in the text pane of the Squeak browser where code for each class or module is displayed.
Right click on the selected text to get a Squeak context menu to show up (below).
Select "more" from that menu and then "method source with it". This
will display all the source modules that contain the selected text.
Source Code Reconnaissance
This section is a warm-up for understanding and eventually modifying the source code for Scratch from
MIT. The objective is to make the modifications necessary to add a few blocks that will allow the control of motors and sensors for directly
Scratch has some external hardware interfaces.
The focus of this exercise will be to explore the interfaces involved with the GoGo and Pico boards and figure out how the system
works (and then extend that knowledge).
My initial approach to building a robot interface suite was to dig into the Scratch source
code with the aim of building my own serial protocol. I did this because I sensed
from my first look at the Pico board (and the associated forum)
that the communications protocol was constrained to being pretty much input only. While banging around within the Scratch source
(trying to learn the vagarities of Squeak), I confirmed this. However, I also found support for two other ways of
communication that appear to be quite a bit more flexible.
The three ways in which Scratch 1.3 can communicate with the
outside world appear to include:
PicoBoard Communications Protocol
GoGo Board Communications Protocol
Scratch Networking Communications Protocol
I'm not going to review the PicoBoard or Scratch Networking options just yet. In fact, I
do not intend to study the PicoBoard comms protocol at all.
However, I do intend later to fully analyze the Scratch Networking option
because this would be a great way to build a central robot simulator
that kids could compete in.
Since my first efforts were based on the GoGo Board communications
protocol, I provide a few notes about that protocol below. I
willtouch upon the Network Communications Protocol as my analysis
I found the GoGo Board indirectly by examining the Scratch
plumbing. The Scratch SensorBoard class had a whole lot more
functionality than I had expected - including the mention of motors and
other intriguing things. However, I didn't know how to engage
the functionality that I was seeing in the code so I searched the Scratch
site for an answer and discovered the GoGo board.
The GoGo board site, although it seems to be related to MIT LLC, does
not clearly identify the fact that the GoGo board can be used with
Scratch (or how to expose the functionality that I found in the Squeak
code). I found the answer on one of the Scratch forums (pdf):
The GoGo Board works with the current version of Scratch (1.3).
Here are the steps for setting it up to work:
Connect the GoGo board to your computer;
Tell Scratch that you are using a GoGo board:
Select the "sensing" category;
Right-click on the "slider sensor value" blockand choose
"show ScratchBoard watcher" from the menu. A sensor watcher
widget will appear in the stage area but it will say "off"
and all the values will be zero;
Right-click on the sensor watcher while holding the shift
key. This gives you a special "expert"
menu. Release the shift key, and select "use GoGo board" from the menu.
Power-cycle your GoGo board to make sure it is reset;
Right-click (without the shift key) on the
sensor watcher widget and choose "select serial/USB port" from the menu, then
choose the COM or serial port to which the GoGo board is connected. The displays should change to show
values--probably mostly 100 or 99 unless you have sensors connected to theGoGo board.
The GoGo board site has arather dated serial protocol
document that I presume is still valid. It is bundled with the latest version
(Version 3.0) of the GoGo. I tried to pull down the schematic for the board but found the link to be dead.
However, there are gerber and Eagle files for building the PCB (which I'm not
too interested in doing just yet).
The source code for version 3.5 of the GoGo can be browsed here.
This seems to be the location at which development of the board has
split from the website proper. Click on the reference to the
source and then click on the source browser to have a look. I
did as they indicated and pulled a read-only svn copy.
The first step in building a device that can respond to the
GoGo serial protocol is to figure out who
initiates communications and what they say. I examined the source firmware for the GoGo 3.5 but did
not find any indication in any of the initialization routines that the GoGo acutally starts the conversation. I suppose that I could have
dug deeper into the Scratch code but it is just as easy to strap on a logic probe and watch who
I did this. However, I din't have either a
PicoBoard or a GoGoBoard so my protocol exchange measurements were
Here are the steps I took to measure the initial comms from Scratch:
Select and confirm the operation of a serial
port using a Windows terminal connected to my microcontroller. This may
seem like a redundant step, but I've been burned so many times by "not
checking the ON switch" that it is just part of the routine now.
Set up my Intronix Logic Probe on the TX/RX line
Start Scratch (Version 1.3).
Right-click on the slider sensor and select
Set up the logic probe to monitor the channel.
MEASUREMENT SET 1: Right Click on the
Watcher and select "Choose Comm/USB Port."
Capture and save the transmission from the
Restart Scratch and restore experminental state
to the point
just before selecting the comm port.
Hover over the Scratchboard Watcher, press SHIFT
Click, and click on "Use GoGo Board."
MEASUREMENT SET 2: Right Click on the
ScratchBoard Watcher and select "Choose Comm/USB Port."
Capture and save the transmission from the
Measurement Set 1
In these measurements, Scratch assumes it is talking to a PicoBoard. The figure
below shows that Scratch sends the byte FE.
As shown below, the byte is sent with a period of about 23 ms.
Measurement Set 2
Here, Scratch thinks its talking to a GoGo Board. Instead of sending one byte periodically, the computer barfs up a stream.
The image below captures the beginning of that stream.
I captured the comms stream into a CVS for both initiation and for closing the COM Port. The first several bytes sent are as follows (in HEX):
54 FE A0 00
According to the protocol manual, 54 FE signifies the sending of a command byte(s). In the first case, it is A0 00 which is somewhat odd. A0 commands a burst mode but the burst cycle byte 00 then disables the burst mode. Maybe it's just a way of buying time....
Same as above but for motor 2
This process repeats up to motor 5 (20h). Finally, the computer issues A0 FF which sets all eight of the board's sensors to go into burst mode. I expect
that when I go deep into the Scratch source, I will be able to find a block that performs these initialization steps and that should help me gain familiarity
with the code.
ScratchSource SensorBoard Code
I needed to figure out how the sensorboard was implemented from within the ScratchSource so that I could either use it verbatim or make changes to
it for my design purposes. The SensorBoard Class is located in the Scratch-UI-Panes. Highlight the SensorBoard class and then click on
the "?" in the middle. The text box in the window then displays a fairly informative description of the SensorBoard class.
This is shown in the figure below.
The full text of the class is shown below.
I represent the Squeak interface to a sensor board connected via a serial port.
One such board, the Scratch Board, has 8 10-bit sensor inputs capable of
reading switches or resistive sensors. Another alternative, the GoGoBoard,
has 8 10-bit sensors inputs plus three motor control ports.
See http://learning.media.mit.edu/projects/gogo/ for additional info about
the GoGo board.
To create an instance of me:
(Note: You can only have one instance of me for each serial port that has a
Scratch or GoGo board attached.) Use the menu to choose the serial port.
When you're done, you can use the menu to close the serial port.
Scratch Sensor Board Serial Protocol
When the sensor board is powered up or reset, it immediately begins sending
sensor data. Sensor data is sent as two byte messages, one message for each
of the available sensors. The first byte supplies the sensor number and the
high 3 bits of the sensor value. The second byte supplies the low 7 bits
of the sensor value. The values of all the available sensors are sent in
this format, followed by a pause of two full byte transmission times, then
the entire cycle repeats. The pause allows the receiver's UART to regain
synchronization in the rare event that it is lost.
Here is the byte format for the two bytes:
Note that the most significant bit is one for the first byte of a pair
and zero for the second. This allows the receiver to tell if a given
byte is the first or second byte of a message.
Sensor board have various numbers of sensors. A given board with N sensors
will cycle through sensors numbers 0 through N-1, then repeat. For example,
a four-sensor board would send data for sensors 0 through 3. Up to sixteen
sensors can be handled by this protocol.
Sensor values should be normalized to fit a 10-bit, unsigned integer range.
That is, sensor values should range from 0 to ~1023. For example, if a given
sensor board only collects 8-bit values, these values should be shifted left
by two bits so that the maximum value is 1020. Likewise, a board that reads
over 10-bits of sensor resolution should shift its sensor values right so
that only the 10 most-significant bits of the data are sent.
The serial port should be set to 38.4 kbaud, one start bit, one stop bit,
and no parity.
As noted both above (and in earlier experiments), Scratch supports both the SensorBoard (PicoBoard) and the GoGo Board.
Most of the text above is for the SensorBoard.
Let's scroll through the methods within this class to document the full potential of both boards and make notes about where we might want to change
things in the future.
Click on the Initialization category and then on the initialize method as shown in the figure below.
Initialization begins with initialization of the superClass-defined instance variables. These are:
The variable sensorValues becomes an Array of 16 with all values initially zero. I know from reading ahead that the GoGo board is a state machine so the
currentState variable is given the idle state to start. There is an option to use the GoGoboard over the SensorBoard that is selected by holding
down Shift->RightClick over top of the SensorBoardMorph. Clearly, if this option is selected, then the variable useGoGoProtocol would change to true.
The reportRaw variable can be changed to True as a RightClick pulldown when hovering over the SensorBoardMorph. I don't yet know what highByte or scanState are for.
The command self addReadouts tells the instance of SensorBoardMorph to execute the addReadouts method.
The code for this method is shown below (Note: during the conversion the code to these pages, the assignment arrow <- is converted to an underline _ .
If you see an underline and are wondering what it is, that means I missed converting it to :=.)
The variable readoutNames is a method temp that defines eight switch/sensor names. Now, because I'm not that familiar with the Morph,
I elected to repeat the first block of instructions into a workspace within Scratch and then inspect the object that gets created.
To open a workspace, RightClick and select Open -> Workspace. Once I had all the code for the first block typed in, I selected the entire block,
RightClicked, and selected DoIt. Then I selected just the word "column" and RightClicked -> InspectIt. The figure below shows the results.
You can browse the variables that belong to the AlignmentMorph on the left to verify that the code indeed set them correctly.
The second block of code in the intialize method creates a title (StringMorph) to add to the AlignmentMorph.
If you type and try to execute this method in a workspace, it will fail becuase it makes reference to the method updateTitle (trust me, I tried).
I got around that problem by "inlining" the code for updateTitle into the workspace where the call originally existed.
All the update does is exchange the words 'On' and 'Off' depending on the state machine's state.
To have a look at what the column instance holds so far, either execute "self openInWorld" inside the text box of the
AlignmentMorph window or "column openInWindow" within the workspace itself. Don't forget to highlight the line and select DoIt.
The figure below shows what I got.
Note the little rounded square in the corner with the word "off" in it. At this point, I'm not entirely sure of the syntax that updates the variable "s"
but I suspect that it doesn't do anything in this case. I will figure that out as I go...
This intialization code also calls the method addReadoutName which creates the strings for each of the sensors or switches to be appended to the column AlignmentMorph.
That method in turn creates a box in which to put the column of sensor values of type WatcherReadoutFrameMorph (under Scratch-UI-Watchers).
That object contains the drawOn method used by the system to paint the new user interface.
Clicking on the "?" for the class description of WatcherReadoutFrameMorph notes that it is simply a frame to hold the readout of a WatcherMorph.
If you click on "?" for the WatcherMorph class, it claims to be a readout for a variable or reporter
(you've likely seen them pop up on the Scratch stage when you select the check box beside a variable).
So, the WatcherMorph class provides instances of watchers for variables and reporters and the WatcherReadoutFrameMorph provides frames in which to group those readouts.
Together, these classes are used to draw the SensorBoard Watcher.
Examining Block Specs
This section describes the steps I took to build and expose a motor control morph in ScratchSource.
This scriptable morph works in conjunction with the GoGo board.
That is, it uses the GoGo board protocol to write to the serial port and builds on the motor control bits that I found in the Scratch source code.
Open the Scratch source. Highlight the Scratch-Objects category and then the ScriptableScratchMorph class.
Click on the Class button in the lower section of the class box. In the method category box, select Scratch.
In the method block, select blockSpecs. This selection sequence is shown in the figure below.
You'll note that this specification produces a verbose description in the text block. This description is repeated below.
The blockSpecs define the blocks that are linked together to form the scripts that kids write.
The shape of the block is defined by the argument (r, s, c, etc). Each block can also have a method call with arguments and initial values.
The blockSpecs for the ScriptableScratchMorph class only defines a set of blocks that are common to both StageMorphs and SpriteMorphs
(the Stage and the objects you put on the Stage). To see the blockSpecs for the blocks that otherwise appear you need to open up the blockSpecs
for ScratchStageMorph and ScratchSpriteMorph. The figure below shows the blockSpecs selection for the stage.
The text block below shows the contents of the StageMorph blockSpec.
The blocks that interest me are those that create the sensor blocks:
The first line creates a reporter whose task is to return the value of the sensor that is indicated by one of the predefined sensor numbers.
The argument sent to the sensor: method will be %H. So how does this get translated to the actual sensor number being measured?
I am assuming that, during the creation of each sensor block, the appropriate menu selection is stored with the block object.
The lines above return the currently selected item from the menu options with the calls to either sensor: or sensorPressed:.
Of course, the strings 'slider' and 'button pressed' are the default menu items. I have to admit that, at this point I am somewhat hazy on
how the block feeds the currently selected item to the method calls. This is worrysome because I want to define my own block shortly.
However, here is what I think is the proper analysis of the code. I found both methods under the sensorOps of ScriptableScratchMorph as shown below.
The code for the sensor: method is shown below.
The first part of the code looks for a scratch server that could possibly provide the required sensor values.
This was noted as part of my investigation into the various ways Scratch could communicate.
If a server exists, then v is set to the sensor value whose name is sensorName.
If a server doesn't exist, the the variable sb gets the stage's sensorBoard instance. If the port indicated by this instance cannot be opened, the call fails.
Otherwise, sb (which is a sensorBoard instance) gets the message sensor: with the index value (a number) selected by indexForSensorName.
The code for the indexForSensorName method is shown below.
As noted, a call goes out to the sensor: method of the sensorBoard instance which just happens to be a SensorBoardMorph. Therefore, the following method is executed:
This method returns a call to itself into the privateSensor method with the numerical sensorIndex value. The privateSensor method is shown below.
So, this code finally returns a number (raw or not, scaled or not, etc). I have to admit that, even with my limited ability at this point to read directly into the code,
it looks like some kludgework was done to integrate the GoGo board. I'd rather start from scratch (get it?) but I need to go back and re-read my SBE...
Creating a Block
As we saw earlier, the ScratchSensorBoard class contains a suite of motor control methods for the existing GoGo board.
However, I haven't been able to find these hidden in any menu item yet so I plan to export these methods directly into buttons for use by the budding Scratch programmer.
The figure below recaps the available functionality.
The first thing to do is to edit the suitable blockSpec and include a control block.
I suppose it's a personal choice of whether to modify the blockSpec under ScriptableScratchMorph or rather ScratchStageMorph.
Currently, the sensors are defined under the StageMorph so I elected to change the blockSpec there. Here's the code:
The line that I added is highlighted. Since I don't care at the moment for a return value, I left the flags empty (-) and specified a method called exportTurnOnMotor.
I fed this method the value 0.
If you examine the line for sensor value, you see that the method called is sensor: but no method for the ScratchStageMorph exists that is called sensor:.
However, ScratchStageMorph is a subclass of ScriptableScratchMorph as shown in the following figure.
Because of inheritance, the sensor: method is accessible to objects of ScratchStageMorph.
The motor ops belong to the class SensorBoard so I can't send a message to them until I have an object of that class in hand.
So, I took the sensor: method, renamed it to exportTurnOnMotor: and added my own functionality as shown in the code below.
Here, starting out like the sensor: method, I used the stage variable to obtain a sensorBoard object that would be able to answer a message turnOnMotor.
I kept the code that makes a run at the port and pukes on failure. Finally, the code returns true when turnOnMotor: motornum is executed properly. Now, lets see if it works!
Start the ScratchSource with the modified code. Because we created the block as a StageMorph, it won't appear under Sensing until you select the stage context.
The block shows up as indicated below.
Now we need to bring up the GoGoboard monitor and set it to communicate with a serial port that I can monitor with my logic probe.
RightClick on one of the Sensor blocks and select the ScratchBoard watcher. Shift-RightClick over the watcher and select GoGo Board.
Finally, RightClick and select the Com port. On this last step, I noticed my Tx/Rx lights blink indicating that all is good in Scratch land....
I created the basic script shown below, clicked on the green flag to run it once, and captured the results on my logic probe.
Here is the byte-stream that was sent to the serial port.
According to the serial protocol manual, this is exactly what I should be seeing to turn on Motor A (I set 0 in the first try which gets adjusted to 1
when addressing the motors. A second try with 2 as the argument fed the code 80 02).
So, now I'm ready to write blocks for the entire suite of motor control options that come with Scratch.
However, I don't have a GoGo board and, in the long term, probably don't want to get one. So my next step will be to write GoGo protocol responders
for both the 9S12C32 (and its derivatives) as well as the Arduino.
Every hacking effort requires a "hello world" to get the endorphin rush that comes from making something do something. This project is no different.
So in this section, I describe the steps I took to get a robot moving.
But First, A Robot is Needed...
So, at any given moment, there are about ten thousand robot bits lying around my place. Some have been assembled into almost functioning things.
Most are on the "gotta check this out later" list. Is a robot ever really finished? And if it is, do you remember what it was supposed to do? Anyway, I scrounged
up some parts and put together the tiny robot shown below.
This tiny robot is fairly simple. I used a base from the Plantranco Desk Rover (sadly no longer for sale - see Microflight.com for other cool bits) and mounted a Technological Arts Nanocore board
(based on Freescale 68HC9S12C32 microcontroller). The microcontroller is mounted on a 32 pin DIP package that provides some basic power management,
an RS232 level shifter, a Background Debug Mode (BDM) interface for those who have a BDM Pod, and a switch that selects run/program mode. I put this microcontroller onto a
protoboard with a 5 Volt regulator, an L293D motor driver chip (and a 7404 that is required to run the motor driver) as well as some mounting posts for sensors and motors.
Any robot being controlled from Scratch will need a listener to pick up and interpret commands. Eventually, Scratch will need a listener to receive results from the robot.
This means that an efficient protocol will be required.
Although I had commandeered the GoGo protocol, I decided that I wasn't going to implement the commands as they were stated in the GoGo board manual.
To simplify things, I only wanted to see one command byte which would tell me which motor to turn off, on, forward, or reverse
(instead of sending a message to indicate which motor(s) to talk to and then separate messages to tell them what to do).
In order to accomplish this change, I had to re-label the GoGo board commands a bit. This re-labelling is shown in the figure below.
Using the re-labelled commands as shown, I could send the tiny robot a single command byte that would indicate motor commands
(highest order three bits equal 010) and, in specific, which motor and which command to perform the instruction on (all permutations of the next three bits).
Since the original specification has a "don't care" in the last two bits, I left them alone.
What that means for the Scratch interface is that if I want to set, say Motor 1 off, then I have to send the serial command that Scratch
invokes when issuing the Motor Control ON command.
I tried to implement a straight translation in Scratch without modifying the source code too much.
However, I wasn't able to maintain the level of hands-off that I wanted. So I essentially wrote a pile of new serial drivers for the functionality that I did want. Here's
what I did...
First, as I demonstrated earlier, I had to create a series of new blocks in scratch. I did this by modifying the blockSpecs for the ScratchStageMorph class as shown below.
The new blocks have the following looks on the ScratchSource screen.
I wrote three new methods called exportTurnOffMotor, exportMotorForward, and exportMotorReverse that take as an argument 0 or 1 (motor 1 or 2).
Each of these new scriptableScratchMorphs methods simply obtains the handle to the sensorboard object and then sends the appropriate messages to implement the desired functionality.
The code for exportTurnOffMotor is shown below
In this method, I selected the sensorboard object as per normal and then invoked the turnOffMotor method. The motornum argument was just passed through.
The code below shows the method that actually turns off the motors.
In this method, I ask if the motor number argument is greater than zero. If it is, I want to send one byte sequence and if it isn't I want to send a different byte sequence.
If motornumber is 0 (motor 1) send 40h (64). Otherwise send 68.
The other four methods are almost identical in every aspect except the serial byte stream that gets sent to the robot.
Here are the logic probe traces confirming communications to the robot.
Motor 1 Off
Motor 2 Off
Motor 1 Forward
Motor 1 Reverse
Motor 2 Forward
Motor 2 Reverse
Now, before officially interfacing the robot with ScratchSource, there is one last thing I needed to do.
The GoGo board protocol goes through a number of initialization steps as I determined before.
I need to turn those off. The code for this initialization is found in the method startReadingData within the sensorboard class as shown below.
I wanted to keep the top part but prevent the initialization routine from trying to start an automatic flow of data (at least for now).
So I renamed this method to "old" and deleted the code shown in the red circle above.
Finally, in order to get the robot to respond to the GoGo serial commands, there needed to be a some robot-side code to listen to the serial port and react.
The code below is what I placed on the microcontroller. This was written for the Freescale HC12 but the algorithm is obvious and can be ported to any other device.
In order to make interesting control algorithms, I need some feedback from the robot. For this, I used a Sharp sensor.
The picture belwo shows the robot with a single Sharp GP2D12 IR sensor attached to its nose. I plugged it into bit 0 of the A2D port.
In order to report on this sensor, I added the following C code to the while loop previously described:
The first serial output byte (0xOC) tells scratch that this is a sensor value. The second byte tells Scratch which one it represents.
I chose the sensor identifier somewhat randomly here (it ends up being resistance value B in the sensorboard watcher). The final byte is the actual value that the sensor reports.
After trying out this new code, I found that it failed miserably. It turns out the the SCIO_Outchar function busy-waits on the port.
While it's doing that, the robot is busy ignoring the input from Scratch. I modified the code to make use of SCIO_OutStatus which makes a pass at the serial
port to see if it is ready to send a byte. If it is not, the code just passes on by.
This modification worked quite well.
Creating a Scratch Robot Interface
In the previous section, we demonstrated that it is possible to extend Scratch with blocks to control a pair
of motors and to take sensor input from an IR sensor. In this section, we extend what we learned and try to make the user
interface more substantive.
Extending the UI
Under Scratch-Objects-ScriptableScratchMorphs-Sensingops, I opened up hookupSensorNames and found the list of sensorboard
dropdown names (slider light sound, and resistance). I added one of my own for test purposes. Then I did a search of senders
for hookupSensorNames and these are what came up:
ChoiceArgMorph - presentMenu
CommandBlockMorph - nuncoloredArgMorphFor:
The second sender is the one that creates the specific block. What I found neat was that, here in the block creation code,
I found the answer to a question that had been bugging me. In the blockspecs for each class, there are list variables that
are not defined - these contain the lists for the dropdown menus. I couldn't find these defined anywhere until now (primarily
because I didn't know how to do a global search on all the code at the time).
The changes I made to reflect the sensors that I was actually feeding to the ScratchSource are as follows. First, under sensing
ops as shown in the figure below, I selected hookupSensorNames and added IR1 to IR5 (infrared sensors), and left
and right bumper sensors.
Next, I made changes to the indexForSensorName method to map the sensor name to the appropriate sensor reported by
scratchboard morph. This is shown in the figure below.
Changing the drop down menu choices requires a small change to the blockspecs as part of the stage morph because the block
shows a default value that is hardwired in the blockspec We need to change the default selection. I changed it to "IR1"
as shown in the figure below.
Of course, this was for the stage morph only Scratch sensors are defined on sprites as well so we need to change the blockspec
for sprite morphs as well This is shown in the figure below.
Now, I don't particularly have a use for the sensor button that is shown for both sprite and stage morphs because all of
my data is reported as values instead of booleans Even the two buttons on the robot So I deleted the last line of the sensor
specification in each blockSpec as shown below.
Now I want to fix the scratchboard watcher to reflect the new sensor names The sensorboard is selected by right clicking
on the last remaining sensorblock and selecting "Show sensorboard watcher." I want to change that to say "Show Itchbot Watcher."
I found the right click menu under the commandBlockMorph as shown in the Figure below I changed the text of the method accordingly.
When the ItchBot watcher comes up, I want it to always use the GoGo protocol (which is quickly starting to look nothing like
the GoGo protocol...) This probably means changing a flag In fact, this is the case as shown in the sensorboard initialization
routine in the figure below.
Here, I changed the value "false" to "true" which makes the GoGo Protocol come up as the default However, I would also like
to erase the distinction between GoGo and the Picoboard completely Perhaps later. It should also be noted that, during the
initialization, there are 8 sensors defined (the second line of the initialization routine) I'm assuming that the array
is defined as 16 entities because each sensor would have two bytes of data I could be wrong on that. I would also like to
report raw values received from the sensors on the ItchBot so I set "reportRaw := true" Changing the names on the Itchbot
watcher requires making changes to the method "addReadouts" which is the last thing called as part of initialization As
shown in the figure below, I changed the readout names to reflect the actual names of the sensors.
The changes, as shown above, didn't actually work out that well The figure blow shows the result Clearly, having names that
consist of more than one word is a problem.
I fixed the problem by encapsulating the words "Left Bumper" and "Right Bumper" in single quotes When I re-ran the ItchBoard
watcher, the ScratchSource puked with a "subscript out of bounds" error. Clearly, I only have seven sensors being reported
Somewhere, there is an assumption that there are 8. I used the debugger to trace the offending method as shown in the figure
It appears that the sensorboard code goes looking for an open port If it can't find it or if it is closed, the method shown
in the figure above explicitly clears each sensor value I deleted the line from the method closePort that cycles through
each of the 8 sensors Of course, this wasn't the end of that problem - sensors are indexed in the step method In
that case, I wasn't entirely sure what it was doing so I changed the index down by one (to 7) These two changes resulted
in a successful ItchBot watcher as shown below.
Communications Protocol Redux
A robot that is "tethered" to a computer is not very useful. With that in mind, I ordered the MaxStream XBee wireless serial modem shown in the figure below.
This device is a fantastically feature rich yet inexpensive solution to wireless communications. Although it has a full set
of networking features, I don't plan to use them just yet. That means I will use the device in "straight through" mode which
essentially just replaces the serial cable with RF.
However, there is one small glitch. As noted in the protocol summary
above, using a wireless link gives me the desirable capability of "talking" to more than one robot at a time. As a consequence,
we need a way of distinguishing between the robots when we send them commands and when they send us data. If I'm going to
address that problem, I figured I might as well refactor the communications protocol to make it more flexible on the robot end as
well. My solution involved re-writing the GoGo protocol into a seven packet modular transmission as follows:
The response packet will also be seven packets. The command will be "reflected" and any data that resulted from that command
will be provided as part of the last field. In some instances (perhaps even most of them), the second data packet may not
be used. In that case, it is just set to zero.
The preamble to the protocol gives all the robots a heads up that a command
is coming. All of them must listen to the RobotID and determine if the command was meant for them. A 1-byte ID makes it
possible to address up to 256 robots although I don't think that would be practical from a bandwidth saturation point of
A 1-byte Command gives us 256 possible discrete commands which I think should be plenty for any single robot.
More realistically, I think that the command space could be divided up into a base command set (those commands common to
most robots) and groups of distinct commands that are specific to individual robots.
A DevID makes it possible to address
up to 256 separate devices on each robot.
Finally, up to two bytes of data are transmitted.
To implement a new user interface, a number of things need to get done "under the
hood" of the Scratch source. The first is set the RobotID. This should be a pop-up that occurs the first time some
action related to the robot is taken. The value should be tied to one of the sensorboard global attributes. For the moment,
I'm going to blow past the input problem by simply assigning it as shown in the two figures below.
Specifying the motor control should be more flexible than what was allowed when using the GoGo protocol. Ideally, I should
send a motor ID, a speed, and a direction. I could do this by providing one programming block and allowing the programmer
to set each field individually. However, I also want to keep it simple enough for even the youngest to use. So I will tailor
things to my robot environment somewhat. Since my motors don't understand the concept of speed anyways (they are either
on or off), then I won't make reference to it in the command block. In addition, I would like the interface to be as close
to natural language as possible so instead of "motor 0 forward", I'd like to say something like "Left motor forward" with
"Left Motor" being a selectable item between "Left Motor" and "Right Motor".
The first order of business was create a list to hold the possible names as shown below.
The next steps involved duplicating the code that was produced for sensor names but for motor names instead.
Now we need to create the block that can be seen on the ItchBot interface....
Here's what the new block looks like:
Now be sure to capture the motorName to motor number conversion. This method will be needed just before sending the command
out the serial port.
Finally, we need to adjust the exportMotorForward and motorForward methods. First, exportMotorForward:
Now, the use of the function "exportMotorForward" could probably be reduced simply to the motorForward function with the
added benefit of removing the method we created called "motor:". If you look at the "sensorvalue" entry under the blockspecs,
it goes directly to the method called "sensor:" which invokes the indexForSensorValue method. Maybe later I'll come back
and optimize. Now we come to the part where we actually put something out on the wire.
Now let's validate what actually goes downrange....
The figure correctly shows the preamble, the faked robot ID, the hardwired command 0 and the device ID (which is zero here).
The same experiment with the "Right Motor" selected gives the following figure.
The difference is in the DevID byte - this time we want to select the right motor which we've arbitarily designated as 1.
I don't like having a command byte represented by zero so I shall change this motor command to 1. While I'm at it, I will
also arbitrarily assign the first data byte to represent speed and the second to represent direction. Since this particular
command block is always in the forward direction, we can set the direction byte to 1 (for forward) or 0 (for backward).
We should optimize things a bit. The motor command for the "sensorboard" should just receive the information it needs to
put the comms on the wire. In the "exportMotorForward" bit, we should integrate the information required from the particular
In order to call the method shown above, I need to feed it speed and direction as well. In order to reuse the motor
method belonging to the "SensorBoard", I set the speed to 1 (recall: my motors are binary - on or off). When it comes time
to stop the robot, I can send the same low level command with speed set to zero.
Implementing the same procedures for reversing and stopping the motors should just be incremental changes or outright
file copies. Here's the exportMotorReverse API:
To access this code, a new programming block is required. And while I'm editing new blocks, I should also create one for
stopping the motor as well as deleting the old block specs. Here's the result.
Of course, there isn't an "exportMotorStop" API so I had to add that as well Here it is...
Here's what the programming block interface looks like now:
There's just a few left-overs to address in order to complete the communications interface. The first is that I need to get the user
to put in a device ID. I hesitate to make the device ID a part of each command
block - that would be a great approach (one instance of Itchots could control multiple robots) but it would mean setting
the device ID every time a block is pulled in. I decided to add device ID as an "extra menu" option. The menu entry and call
are shown in the figure below.
The setRobotID method call is shown in the figure below.
I learned a few things while writing this method. First, the standard command that set the stage variable didn't work because it is not in scope.
I needed to set this variable by calling workPane. Once I did this, I was able to attch the sb variable to the sensorboard object.
Next, I decided that if the robot ID had already been set, then I wanted to report that value when giving the option to change it. I had to write a one-line
method in the sensorBoard class to report the robotID value (the method name is getRobotID). Before I could use it in a dialog box, however, I needed to
convert it to a string and this is what I did with the following line:
initID := (temp asString)
Using that value, I opened a string dialog box and asked the user to enter a new number. I did some bounds checking after changing it to a number and then
called another short method I wrote within the sensorboard class to set the robot ID (called setRobotID like the present class).
The figure below shows the message that comes up when selecting the Extras menu "Set Robot ID".
Setting the robot number and then trying to set it again confirms that the number is attached to the sensorBoard because it appears as the default in the setting window.
A second hanging issue is the sensor returns. Right now, I have them programmed
on the robot end just to stream back. However, I think this could be problematic from a bandwidth perspective. If all the
robots are doing it, then the instance of collisions and misplaced information will increase. I think the best thing to
do is implement a polling system. But this means the the junior programmer must "ask" for the sensors each time he or she
wants them and I can see problems already where decisions are made on sensor values that haven't finished coming back from
the robot yet. The final hanging issue is the "SensorBoard Watcher". That's currently the only place where I can set the
communications port but I don't necessarily want to start it since I might be controlling more than one robot. I need to
factor the com port settings out of that code and into a button. There's also a few housekeeping chores to do. I think
the motion related blocks should go under the Motion heading when the stage is selected (the "sensorboard" belongs to the
stage). There's also a set of sub menus that show help screens that need adjusting too.
OK, so moving the motor control blocks to the stage under "Motion" is not *that* easy. When the stage is selected, a message
saying that "the stage is selected so - there are no motion blocks available" comes up in the pane It lands right on the
first block that I place in the pane. Since I couldn't figure out where the message came from (in the 40 seconds I took
to look), I used a couple of '-' to tab the buttons down. The change is shown below For the time being, I'll leave it at
There's nothing technically wrong with leaving the comm port control items as a selectable item in the "scratchboard" watcher.
However, I do not like having the "scratchboard" watcher being tied to a command block nor do I like it reporting on sensor
values since these may be coming from different robots. The code below shows how I created a menu item within the "Extras"
selection of the main window that opens the "scratchboard" watcher.
This entry only changes the starting point for the watcher. What I plan to do is remove all of the sensor information from
this "watcher" and just leave communications related stuff. That may make it seem like there's nothing much but who knows
what's in store for the future? I don't want to fiddle too much with the innards of this object however since the "scratchBoard"
does define all of my communications needs. Therefore, I shall limit things at this point to simply removing the sensor
reporting and perhaps "nicing" things up a bit. The first thing I did was to hit the initialization routine - specifically,
the addReadouts method. Here, I snipped out the code that was responsible for actually putting the readouts into the window
(shown below). There's cruft left over, but it is benign so I left it in.
In order to prevent the system from puking, a small change needs to be made to the "step" method. As I noted earlier, step
updates the sensors being reported. However, it assumes that there will be 7 of them. In our case, there are now
none so this method chokes when it is called. I deleted the code shown below.
Now when you select the watcher from the extras menu, you get a *very* simple box that displays the current communications
status. I wish I knew how to put a title in there - something for later Here's a snappy.
The other concerns outlined above are reserved for a future hacking marathon...
I had an open question out to the Scratch team to see if the Scratch source could somehow be imported into a newer version
of squeak. The only reason I wanted to do this was to be able to do text searches for certain things when a problem hits.
Well, they are all quite busy and didn't get back right away, so I "solved" the problem in another way I used the "fileOut"
menu option on the right-mouse over the Scratch Class pane to export each class. Then, I copied all of the text into one
big text file and searched from there (Update: They responded back and I've inlined their response further on. Searching
on text in the current Squeak is actually quite easy - it just requires that you RTFM. Sorry for the newb question folks
:( ). I decided to relocate the motor motion blocks to the Motion category within the Scratch blocks menu However, Scratch
had a program entry to write a couple of sentences into the blocks pane that basically announced no blocks were available
I couldn't find the text for that entry (cause I didn't have a decent search capability at the time) so I just tabbed the
blocks down a couple of lines to get the arrangement shown below.
Doing a search on the text of the source code as described above, I found the culprit responsible for the message as shown
below To fix the problem, I deleted the stuff in red:
I wanted to be able to change the title in the upper left hand corner from "Based on Scratch" to something that included
the word Itchbots. I found the instructions for changing the sketch on one of the forum posts here (thanks Gershmer!).
I'm not sure how long the link will stay valid so I'll repeat the steps below:
alt-click or middle click 3 times on the picture. click the bright red circle with the picture of a list (the "menu for
me" button) click repaint the paint editor should open. import your logo, or draw your logo. If you do these steps properly,
you end up with a window like the one shown below.
Of course, I opted to export the original, edit it with Gimp, and then re-import it. The result is shown below.
I also wanted to change the cat icon to something more representative of Ichbots (like a robot or something) I found a
solution for changing out the cat icon on the Scratch forum here Thanks go to billyedward for the solution I used (and SHOUTS
to everyone else on the thread for contributing solutions) Here's what I did... I dowloaded a software called Resource Hacker and used it to open the Scratch VM (Scratch.exe) I then expanded each icon folder as shown in the figure below.
I then set about to find an icon that fit my "robot" flavour. At some point in the future, I'd like to design one myself.
I went to IconArchive and did a bit of searching. Eventually, I settled upon
the gif shown below.
It's not *exactly* what I want, but it certainly fits the bill. I converted the gif to an icon using Gimp and saved it.
Note that the gif that I got came in a 32X32 size. If it was different (i.e. bigger), then I'd have used the Gimp "Scale"
command to shrink it down. In Resource Hacker, I seleted "Action" and then "Replace Icon". I got the screen shown below.
I selected the button "Open file with new icon" and found the Tiny Robot icon.
I didn't really know which icon I was supposed to replace so I replaced them all. I'm sure there will be consequences
down the line somewhere but I'll cross that bridge when I come to it... I closed the Icon Replacement window and saved
the project and then refreshed the folder window. Voila!
In addition to the replaced folder icon, the cat icon within the program is also replaced as shown below.
Further Defining a Communications Protocol
The first iteration source code for my prototype robot (Marvin) is here. It was designed
with a state machine to support the original GoGo board protocol In order to modify it to the new protocol, I needed
to add a new state to the machine After the two preamble bytes, the robot needs to determine if the RobotID in the packet
stream is his - if so, collect four more bytes and if not break and stop listening until the next preamble. I wrote the
appropriate code on the microcontroller side and performed some rudimentary testing In order to expand this project to
more controllers, a similar exercise needs to be performed for each of them. Processing of the input data from the robot
starts with the method processIncomingData as part of the "sensorBoard morph" The first thing to notice is that
most of the code is not really needed since we are not working with sensorboards I've circled the stuff I plan to terminate
in the figure below.
At some point in time, I'd like to get rid of anything to do with GoGoProtocol altogether But, as with my favourite quote
in "Muppets from Space": baby steps...baby steps... Now, the effective
code here reads everything that's currently in the input queue into a buffer The question that comes to mind is "should
I worry about how big the buffer is?" The answer, surprisingly, is NO As I've already noted, I'm not planning to let
the robot stream data - every sensor byte must be specifically requested Now, that doesn't mean that I must specifically
ask for each and every sensor one at a time I think an optimal approach is to implement a command that requests an update
for all sensors of a given family, type, or range and then have all of these reported back in a single stream That's
quite different from having the data sent without asking for it. The effective code makes a call to processGoGoByte A search on callers of this method turns up only processIncomingBytes so I think I shall change the method name to processInputBytes Here's the modified method.
The processInputByte method will be functionally similar to the state machine that I've implemented in Marvin That is,
it will watch for the two byte sequence that queues everyone to listen, then wait on the robot ID If it applies, it will
enter the appropriate state and process the command If not, it will reset to the start state.
An interesting point to note here is that this communications protocol is as generic as possible This makes it possible
for two ItchBot programs on two different computers to communicate or for two ItchBots to communicate This may be a
problem in the future - or a significant feature For now, I'll label it as a potential mis-feature :) The state-machine
used by the original source code is initialized to an idle state as shown in the initialization method for a scratchBoard
I'm going to define a new state for each of the preamble bytes and the command byte As I noted earlier, I will want to
reflect the command that was sent to the robot back to the controller Like the GoGo board, I'll use a two byte preamble
that is one more bit larger In order to avoid confusion, I may have to limit my data values to being less than these
four byte values So, a return package sent from the robot will have the following form:
The figure below shows the state machine that I implemented on input to receive each return command (some of it is cut
off - mostly repetetive anyway) So far, the entire code is simply geared to filtering the device ID (ie. do I keep taking
the message) and then collecting the next four bytes.
Ideally, there should be a check in the code on each of the command, devID and data bytes to see if they are one of the
reserved bytes If so, then something somewhere went wrong during the transmission and the command reception should be
aborted I'll have to see how resilient things are when I have five or six robots chirping away. I've created four new
variables that need to keep state between messages to this object These are essentially the names of the four bytes that
I will collect after seeing the preamble and my RobotID: commandByte, deviceID, statusByte1, and StatusByte2 They aren't
shown in the figure above because I couldn't scroll down that far I needed to add these variables to the global variables
attached to the sensorBoard object class as shown below.
Of course, it is only good etiquette to also initialize these variables.
In order to test out this new module, I need to generate some traffic in the format that is expected Before going nuts
here, my approach will be to code my prototype robot to send a single sensor value back wrapped in the appropriate protocol
Then, I'll use the old code base to update a fixed sensor value within the sourcecode and, to test, put a sensor watcher
up for that value. The figure below shows a probe of the TX from my prototype robot after configuring it to put out a
steady stream according to the protocol.
The preamble bytes are as expected I hardwired the RobotID to 5, the command is a dummy value (22), as was the DeviceID
(01h) The responses were actually tied to one of the IR sensors on board Marvin. The figure below shows the test case
working properly Here, I just hit the checkmark on the IR sensor that I had from last time (since I don't have the sensor
watcher running any more) This popped up the watcher for that one sensor and it spazzed out
So now I want to write a protocol block that will actively update an individual sensor At this point, I have to make a
few potentially limiting decisions I want to be able to keep the blocks simple and define them by sensor type - for example,
infrared range sensors, sonar sensors, pressure sensors That way, the physical robot can have each of these devices labeled
in a specific way At the same time, I have allocated one byte to identify the sensors In the drop down menu for each
sensor type, I don't want to have to start at some idiotic number like 20 because the previous sensors had the first
20 numbers allocated to them It's got to be simple. Therefore, I think it
would be a good idea to divide the space of sensors up into classes The division that makes the most sense is 16 classes
of 16 The upper nibble will be the device class (IR, Sonar, etc) and the lower nibble will be the ID of that device Therefore,
all of the blocks can have the same drop down menu and the glue logic can add the appropriate upper nibble. Now, the
class names are a bit problematic I want them generic enough to be useful but named appropriately I pulled up the sensor
offerings for robotshop.ca to see what could possibly pass as a sensor nowadays and here's
what I got. Ultrasonic Infrared Light Bend Stretch Accelerometers Gyroscopes RFID Camera Bumper Rotary Magnetic Temp
Humidity Thermal Voltage Encoders Inertial GPS Laser Force Sound Clearly, I need to do a bit of rationalization When
explaining sensors to students, I've always put them into bins according to their underlying mechanism (optical, resistive,
etc) However, that makes for about 6 different classes and could really confuse the younger students. There are likely
to be sensors that there are plenty of on a typical robot and sensors that would only account for one or two on a typical
robot I'll reserve one class slot for the "one-of" types and assign each their own name and 8 bit identifier The rest
I'll try to bin into reasonable groupings. Here's my rationalized list I think most sensors should fit comfortably into
one of these.
Now that I have the "big picture" outlined, it's time to write a block that requests a sensor update and a block that
can be used to report the value of a sensor My experimental robot already has IR, Sonar, and Bump sensors I shall start
with the IR sensors in order to build out the basic functionality. The first step is to customize the new blocks The
figure below shows the format that I eventually decided upon The infrared sensors are a type of Proximity Sensor
Note that the old blockspec had a drop down menu that had to be translated However, in the case above, I have chosen simply
to identify each sensor by number That should simplify the coding slightly but it also makes the interface a bit more
"kludgey." Let's start by refactoring the method sensor: It does not expect a number but rather a list name Since
I do not want to break anything, I think I will just start with a new method I'll call it exportReportProxSensor
The original sensor: method is shown below.
All this method is intended to do is return the value of one of the SensorBoard object's static variables I think that
could be replaced quite easily The figure below shows the modified sensor value reporter.
This method does a basic input validation on the number to ensure that it is between 1 and 16 Then it calls the sensor: method of the SensorBoard object with the user-provided sensor number specified added to one less than 16r30 (30 hex)
Recall our table above - proximity sensors are mapped to the upper nibble 3 But since our user does not start at sensor
0, we need to subtract one from this lower bound. This original sensor: method is shown below.
Of course, this method really doesn't do anything It passes a sensorIndex value to another method called privateSensor:
to do the heavy lifting This method is shown below.
This method is uneccessarily complex It is clear that there was some evolution going on between the scratchBoard and GoGo
protocol I have no intention of filtering the data coming in so there will be raw vs processed decision to make The important
work done in this method is to select the appropriate value in sensorValues and pass that back So that's what we will
back-annotate into the sensor: method Here's my sensor: method.
Of course, the original scratchboard object only reserved 8 spaces for sensor values We have the potential for 256 sensors
so we'll just jack that number up. The figure below shows storage being defined for each of these new variables.
The final requirement is to point the report button at the exportReportProxSensor method.
Earlier I had changed the byte stream input and had left it hardwired to IR sensor1 In order to be able to test my new
sensor, I needed to fix that. When a command comes in from a robot, I chose to compose all of its bytes and then pass
the command off to a separate method for processing as shown in the figure below.
In the command reference, I decided to make hex 02 represent a request for a sensor update Of course, the command is reflected
back when a sensor reports the value, so the incomming command interpreter should be able to understand what it sees.
Here's the command processor that does that.
This command processor is not done yet However, it effectively manages the reception of a sensor update package by simply
stuffing the value statByte1 into the sensorValues storage at devByte.
A Small Bug
So far, sensor reporting (from the robot) for the IR sensors is working fine There is one small problem, however Whenever
I select the check box to report the sensor value (i.e. so I can keep an eye on it independent of what the code is
doing), the reporter box mangles the name as shown in the figure below.
I decided to fix this by re-introducing parameterized names for each sensor type - up to 16 First, because I want name-lists
for each sensor type, I needed to create that list In this case, I will be naming the IR sensors "Proximity Sensors"
The figure below shows the list I created under variable $J.
Proximity Sensor Name Source
Of course, I had to define the option selection list and this is shown below.
I then needed to create a mapping between the names I gave the sensors and their actual numbers Now, here's the thing...
I know I should just number the sensors logically from 1 to 16 and then use a translation later to convert them to
the actual number that needs to go down the wire to the robot However, the opportunity to assign them the numbers that I want at this point is too good to pass up So, since Proximity Sensors start at 0x30 (48 Decimal), I will start the list at 48 (Update: Since the name to number mapping starts at 1 instead of zero, the list should have started at 49).
Now all I needed to do to finish the name change was to adjust the call within the blockspec as shown below.
Note in the figure above how I used the variable $J to point at the appropriate list of names. The final step is to actually invoke a report on a sensor My modified routine is shown below.
Notice in the figure above that I did not perform a translation on the sensor to be reported as I had done last time This is taken care of in the list translation Also notice that the call is to the sensorBoard sensor: method which simply reports back the value of one of 256 sensors. Of course, stopping there leaves the bug in the display that I was originally trying to fix That is, the name variable is not interpreted when the watcher morph is placed in the workpane The figure below shows how I adjusted that behaviour.
The watcherMorph creation methods have a hardwired exception to determin which list might possibly show up and contained instructions on how to handle those I simply added a line item and pointed it at my own list Now when I "checkmark" the sensor to keep and eye on it, this is what I get.
Requesting a Sensor Update
Requesting a sensor update is the other half of the comms protocol I have created a block in the sensor category to request an update for an individual proximity sensor However, I now need to make it look like the sensor watcher and also create the code that makes the request over the serial line. First, I adjusted the button so that the choices were the same as the proximity sensors This is shown in the figure below.
I changed the name of the method call and fed it the first name on the list as can be seen above Because all of the infrastructure was already installed for dealing with the conversion of a sensor name to a number, I did not have to deal with it again here. Next, I created the method proximitySensorUpdate shown in the figure below.
This method calls the updateSensor: method of the sensorboard object As I noted in the comments, this is the only method required in the sensorboard object to update sensors regardless of the sensor type because I am feeding it device IDs The general command in the command reference for a sensor update is 0x02H. The sensorboard's updateSensor: method is shown below.
The method shown in the figure above composes a command string with the preamble bytes, the robotID from the sensorboard static variables, the command 02, and the device ID sensorNumber. To confirm that I was getting what I asked for on the wire, I added couple of blocks to the stage and executed them The blocks and their output on the wire are shown in the figures below.
Here's the comms protocol for one complete handshake: the source asks for sensor 1 and the robot reports sensor 1.