Sun SPOT Blog Feeds


We have a PS3. I got it 'cause it's a great blu-ray player. My son uses it for playing those games. Nevertheless, it will not only play little round discs but movies on a USB stick, or from a "Media Server". The "Media Server" caught my eye - it looked like something that my Solaris NAS box could do.

First shot was to try to set up the server from Windows. It was simple enough, download windows media player and start the services. Of course, it only wanted to play wmv files. That was ok, I just wanted to see what a "media server" was and what it could do. I went a few rounds, learned way more than I wanted about codecs and turned the problem over to my son.

He found Mediatomb on the web and installed it on his Ubuntu box. It worked great even with the mp4s, so we started off on a weekend project to put it on Solaris.

Fortunately, Constantin Schmitz already did this and wrote an excellent blog about how to do it. This makes my blog a whole lot shorter. If you don't want Mediatomb, you can try Twonkymedia, he wrote a blog on setting up that. Follow his blog on Mediatomb on how to install it and I'll make comments from my experiences.

Mediatomb is very configurable. The media organization can be set up through a configuration xml script, javascript and the built in web service. The webpage gives you a view of what the PS3 sees and a file tree to add directories and media files. The organization from the files on the server and what is seen by the PS3 is managed through javascript.

Tweaks in the installation
Blastwave instructions don't use pkg-get -i anymore. The instructions now use pkgutil --install. pkg-get is still around but the default wasn't pointing to anything useful and pkgutil worked fine.

You have to grab a copy of libmagic and will have to poke around ftp.astron.com with ftp to find the latest release. I ended up with 5.0 and that will likely change again. I couldn't get it to compile correctly with cc, I changed to gcc and it worked fine. Compile taglib as per instructions and then grab a copy of Mediatomb. I used gcc for that too.

I used SMF for starting up and shutting down mediatomb.  I'll describe the setup at the end. You do need a start up script. I put the configuration for it in /etc/mediatomb.

mkdir /etc/mediatomb

Cut the following and put into a file /etc/mediatomb/mediatomb.conf

Customize for your own preferences.

--------------------

# install in /etc/mediatomb

MT_INTERFACE="e1000g0"

## MediaTomb port must be private >= 49152
MT_PORT="49194"

# Username and group
MT_USER="bob"
MT_GROUP="family"

# PID file path
MT_PIDFILE="/var/run/mediatomb/mediatomb.pid"

# Log file path
MT_LOGFILE="/var/log/mediatomb/mediatomb.log"

# Config file path
MT_HOME="/export/home/bob"

-------------------

You want to create a mediatomb directory in /var/run and in /var/log

cd /var/run (and /var/log)

mkdir mediatomb

chown user:group mediatomb

chmod 755 mediatomb

Create a startup script in /lib/svc/method/ called svc-mediatomb

-------------------------

#!/sbin/sh

. /etc/mediatomb/mediatomb.conf

LD_LIBRARY_PATH=/opt/csw/lib:/opt/local/lib:/usr/lib/firefox /opt/local/bin/mediatomb \

--interface $MT_INTERFACE \

--port $MT_PORT --daemon \

--pidfile $MT_PIDFILE \

--logfile $MT_LOGFILE \

--home $MT_HOME

A few notes. LD_LIBRARY_PATH is not the best way to go. I don't like LD_PRELOAD either and it was not clear to me from the error message of how to get that to work. The LD_LIBRARY_PATH does work and I'm using it for now. I hope a cleaner method eventually comes to past. I do believe the use of ZFS/Solaris and a mediaserver is a big win.

First time Mediatomb is run, it creates a directory in $HOME/.mediatomb. Inside .mediatomb are the database files and default configuration file config.xml. This is the basic configuration file and it is read when the service is restarted.

You will need to edit the config.xml file:

Set a login for those that want to set up Mediatomb.
      <accounts enabled="yes" session-timeout="30">
        <account user="bob" password="a password"/>
      </accounts>

The name tag is what shows up on the PC and on the PS3 as the media server name. Good idea to make it something identifiable.
    <name>Bob's Solaris Media Server</name>

I set it up to view the media files with javascript and didn't need the directory view for the PS3.
    <pc-directory upnp-hide="yes"/>

We are going to change the javascript files from the default. This requires the following lines changed:
      <common-script>/opt/local/share/mediatomb/js/common.js</common-script>
      <playlist-script>/opt/local/share/mediatomb/js/playlists.js</playlist-script>
      <virtual-layout type="js">
        <import-script>/opt/local/share/mediatomb/js/import.js</import-script>
      </virtual-layout>

The virtual-layout tag must be changed from builtin to js for it to read the javascript that you modified.

Add Constantin's changes to the config.xml file for <filesystem-charset>, <magic-file>, <protocolInfo extend="yes"/> and map to tags as he suggested.

Adding some categories for Video
Mediatomb uses javascript to map the media files over to the upnp container structure that the PS3 uses. It's very smart about this for pictures and audio files as the metadata is quite rich. This is not the case for video default is to dump all videos into a 'Video' container.

I didn't like this but didn't want anything very complicated. I needed basic categories and the movie title. I set it up that each user had a 'media' directory that would contain movies, pictures, and audio. Inside of this directory would be subdirectories to categorize the movies. The filename of the video will be used as the title of the move but with the file extension removed.

This is done by changing the javascript file, import.js.
In import.js, replace the text:
// currently no video metadata supported
function addVideo(obj)
{
    var chain = new Array('Video');
    addCdsObject(obj, createContainerChain(chain));
}


with

function addVideo(obj)
{
  var loc = obj.location.split('/');
  var mediaFound = false;
  var chain = new Array("Video");
  for(i=0; i<loc.length-1;i++) {
     if(mediaFound) chain.push(loc[i]);
     if(loc[i].toLowerCase() == "media") mediaFound = true;
  }
  str = loc[loc.length-1];
  obj.title = str.substring(0,str.lastIndexOf('.'));
  addCdsObject(obj, createContainerChain(chain));
}



Save these files and restart the server.

Setting up the media
You will need to create a media directory in your user space. Within this directory create subdirectories for categories like movies, tv shows, etc. and possibly further subdirectories for series, episodes. Do not get carried away with the hierarchy as there is a limit of subdirectories on the PS3. The audio and pictures can go into the media directory without subdirectories.

If you have upnp on in Windows, it should show up as a networked monitor with rabbit ears in Network Places. Clicking on this should open up your webbrowser to Mediatomb file/database configuration page. If you enabled login, it will then have a login screen for user/password. It will then show two tabs, filesystem and database.

Click on filesystem tab and browse the file tree until you find your media directory. Click on the + icon with the little round arrows (Tooltips would be nice here). This will bring up a form to set up autoscan of this directory. Set the scan level and make sure recursive folders is checked to pick up the subdirectories.
Any files added to the media directory will be automatically added to the upnp container from then on.

Clicking on the Database tab will show the organization of how it will look on the PS3. The only difference is while the PC Directory always shows in the browser, it can be selectively hidden for the PS3.

Setting up a service

This is my first shot at setting up a service. Copy the following xml between the dotted lines and

write as /var/svc/manifest/application/mediatomb.xml

-------------------

<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
    <service_bundle type="manifest" name="mediatomb">
    <service name="application/mediatomb" type="service" version="1">
        <create_default_instance enabled="false"/>
        <single_instance/>

        <dependency
            name="network"
            grouping="require_all"
            restart_on="error"
            type="service">
                <service_fmri value="svc:/milestone/network:default"/>
        </dependency>

        <dependency
            name="filesystem"
            grouping="require_all"
            restart_on="error"
            type="service">
                <service_fmri value="svc:/system/filesystem/local"/>
        </dependency>


        <exec_method
            type="method"
            name="start"
            exec="/lib/svc/method/svc-mediatomb"
            timeout_seconds="60">
                <method_context>
                       <method_credential user="bob" group="family"/>
               </method_context>
    </exec_method>

        <exec_method
            type="method"
            name="stop"
            exec=":kill"
            timeout_seconds="60">
    </exec_method>

        <property_group name="startd" type="framework">
            <propval name="ignore_error" type="astring" value="core,signal"/>
        </property_group>

        <stability value="Evolving"/>

        <template>
            <common_name>
                <loctext xml:lang="C">
                    UPnP Media Server
                </loctext>
            </common_name>
            <documentation>
                <manpage
                    title='mediatomb'
                        section='1'
                    manpath='/opt/local/share/man' />
                <doc_link name='mediatomb.cc'
                    uri='http://mediatomb.cc' />
            </documentation>
        </template>

    </service>

</service_bundle>
-------------------

 The /etc/mediatomb/, /var/run/mediatomb,/var/log/mediatomb, mediatomb.conf and config.xml home tag all should have a username/groupname and appropriate protection fields set. Once everything is set, check the manifest to make sure it's ok with:

svccfg validate /var/svc/manifest/application/mediatomb.xml

if ok, then import

svccfg import /var/svc/manifest/application/mediatomb.xml

and check status

svcs mediatomb

It should be disabled.

Start mediatomb with 

svcadm enable mediatomb

stop it with 

svcadm disable mediatomb

if it isn't running check status with 

svcs -xv

Problems show up as "maintenance" mode

You can check both the smf log files in the svcs error message and the log file from mediatomb to find what is going wrong.

Once the problem is fixed, clear smf with

svcadm clear mediatomb

svcadm disable mediatomb

and try again, svcadm enable mediatomb






Elliptic Curve Cryptography (ECC) is a next-generation public-key cryptographic technology that is more resource efficient than RSA (learn why) and was recently endorsed by the NSA for protecting sensitive US Government Information (see The Case for ECC and Suite B).

Sun Labs has played a major role in promoting wide-spread industry adoption of this technology by:

  1. Leading the standardization of ECC within SSL/TLS, the dominant security protocol used on the Internet (see RFC 4492 and its earlier versions).
  2. Contributing ECC technology to OpenSSL (version 0.9.8 and later) and NSS/Mozilla (version 3.8 and later) -- two cryptographic libraries that power the world's most popular open source web server (Apache) and browser (Firefox), respectively.
  3. Initiating and leading a cross-vendor ECC Interoperability Forum (with participants from Apache, Certicom, Microsoft, Mozilla, OpenSSL, Red Hat, RSA, Sun and Verisign) to ensure seamless interoperability between ECC-enabled offerings from different companies.

ECC has been part of Firefox since October 2006 when version 2.0 was released but isn't yet included in the default build of the Apache web server (see Bug 40132). I recently updated the patch and corresponding instructions to create an ECC-enabled version of Apache 2.2.11 with OpenSSL 1.0.0-beta2. If you happen to try out the patch, I'd love to get your feedback.

In case you are wondering "why should I care?", think of this as another step in reducing the computational cost of security so service providers like Amazon, Facebook, Google and Yahoo can turn on HTTPS by default for all user interactions (not just the login phase), thereby boosting privacy on the Internet.

In the battery blog I did, I mentioned using a java program to measure internal resistance of the battery. I updated the program and will post the snippets here.

The internal resistance of a battery increases with cold, age and power cycles. This limits its ability to provide high currents and eventually make the cell unusable. Most of the increase over age is from oxidation of the electrodes. The higher the internal resistance, the higher are both the charging and discharging losses of the cell.

There are two methods of measuring internal resistance. The first method places a small AC signal, about 1KHz, across the cell and measures the in-phase AC current. This yields Z = E/I, where E is the AC voltage amplitude and I is the measured AC current. The second method is a DC load test. Measure the battery voltage and discharge current under both light and heavy loads. The internal resistance is then R = DE/ DI. The AC measurement is usually an order of magnitude less than the DC load method and can provide more information of the condition of the battery. 

The program I wrote uses the DC load test and requires two SPOTs. One is the SPOT-under-test (SPOUT?) and a basestation that prints the results to a terminal emulator (or ant echo). It uses the 802.15.4 radio to communicate its results. Each SPOT is running a different application. IntresSensor.java goes on the SPOUT and intresHost.java goes on the basestation. I used the latest RED release; however, this should work for BLUE. SPOUT requires an eDemo board and battery.

The SPOUT goes to sleep for sixty seconds, wakes up and takes a low current reading. SPOUT cranks up the current by turning on the radio and set LEDs to full maximum for another sixty seconds. It takes a second reading and transmits the result to the basestation.

The basestation prints the current and voltage measured, calculates the internal resistance and prints it. The code is provided as a snippet, without imports, pauseApp() nor destroyApp() methods. Using a basic SPOT template will provide a skeleton and Netbeans or Eclipse have a means of finding the imports.

Radio Stuff

Most of the work I’ve done on the radio has been low-level stuff – real low-level, for testing and compliance. This was my chance to use some of the basic Radio libraries like RadioDatagram. I wanted to keep this simple, and didn’t need Lowpan or mesh stuff. I did a dirt simple discovery method. I didn’t put any retries, back off strategies, etc. and this may break with a lot of SPOTs around.

The basestation broadcast a message containing an arbitrary string, appName. When the SPOUT receives the broadcast, it sends all subsequent messages to that basestation address. The basestation stops broadcast once it starts receiving packets addressed to itself. More than one SPOT can send to the basestation and the address will be printed with the sensor data. If this is used in a classroom, each student could assign appName to a unique “username” to pair the SPOTs.

SPOUT Code

public class intresSensor extends MIDlet {
  private static final byte PORT = (byte) 93;
  private IBattery battery;
  private ITriColorLED[] leds;
  private static final String appName = "intres";
  private String dstAddress = null;
  private void sendSensor() {
    int sleep_discharge;
    int sleep_vbatt;
    int run_discharge;
    int run_vbatt;
    IPowerController pctrl = null;
    RadiogramConnection rc = null;
    Datagram dg = null;
    try {
      // open a direct connection between this SPOT and the basestation
      // dstAddress is the basestation discovered by lookForHost
      rc = (RadiogramConnection) Connector.open("radiogram://" + dstAddress + ":"+PORT);
      dg = rc.newDatagram(rc.getMaximumLength());
      rc.setRadioPolicy(RadioPolicy.AUTOMATIC);
    } catch (IOException e) {
      System.out.println("Could not Send");
      return;
    }
    pctrl = Spot.getInstance().getPowerController();
    leds = EDemoBoard.getInstance().getLEDs();
    battery = Spot.getInstance().getPowerController().getBattery();
    for(int i = 0; i < 8; i++) {
      leds[i].setColor(LEDColor.WHITE);
      leds[i].setOff();
    }
    while(true) {
      // can do this test only when discharging
      if (battery.getState() == IBattery.DISCHARGING) {
        // turn everything off
        Spot.getInstance().getGreenLed().setOff();
        Spot.getInstance().getRadioPolicyManager().setRxOn(false);
        for(int i = 0; i < 8; i++) {
          leds[i].setOff();
        }
        // this should go into deep sleep but it doesn't
        // there is a bug prob with radio stuff keeping it awake
        Utils.sleep(60000); // 60 sec
        sleep_discharge= pctrl.getIdischarge();
        sleep_vbatt = pctrl.getVbatt();
        //   turn everything on
        Spot.getInstance().getGreenLed().setOn();
        Spot.getInstance().getRadioPolicyManager().setRxOn(true);
        for(int i = 0; i < 8; i++) leds[i].setOn();
        for(int i = 0; i < 600; i++) Utils.sleep(100); // 60 secs
        run_discharge = pctrl.getIdischarge();
        run_vbatt = pctrl.getVbatt();
        try {
          // transmit our data
          dg.reset();
          dg.writeInt(sleep_discharge);
          dg.writeInt(run_discharge);
          dg.writeInt(sleep_vbatt);
          dg.writeInt(run_vbatt);
          dg.writeInt(battery.getBatteryLevel());
          rc.send(dg);
        } catch (IOException e) {
          System.out.println("Could not Send");
        }
      }
    }
  }
  private boolean lookForHost() {
    RadiogramConnection rc = null;
    Datagram dg = null;
    long adr;
    String name;
    dstAddress = null;
    // open a connection to listen for broadcast
    // PORT needs to match between sender and receiver
    try {
      rc = (RadiogramConnection) Connector.open("radiogram://:" + PORT);
      dg = rc.newDatagram(rc.getMaximumLength());
    } catch (IOException e) {
      System.out.println("Could not open receiver");
      return false;
    }
    while(true){
      try {
        dg.reset();
        rc.receive(dg);
        name = dg.readUTF(); // UTF is a java String
        // message sent by broadcast is an arbitrary string
        // appName could be changed to a students name to pair
        // SPOTs in a classroom
        if (name.equals(appName)) {
          dstAddress = dg.getAddress();
          System.out.println("Host found: " + dstAddress);
          return true;
        }
      } catch (IOException e) {
        System.out.println("Nothing received");
        return false;
      }
    }
  }

  protected void startApp() throws MIDletStateChangeException {
    // Listen for downloads/commands over USB connection
    new com.sun.spot.util.BootloaderListener().start();
    // Make sure this SPOT has an eDemo board
    if (Spot.getInstance().getExternalBoardMap().isEmpty()) {
      System.out.println("Requires eDemo board");
    } else {
      // do discovery and if successful, the main loop
      if (lookForHost()) sendSensor();
    }
  }

Host Code

public class intresHost extends MIDlet {
  private static final byte PORT = (byte) 93;
  private static final String appName = "intres";
  private IBattery battery;
  private boolean discovery;
  private ILed greenLite = Spot.getInstance().getGreenLed();
  // broadcast thread
  synchronized private void sendBroadcast() {
    new Thread() {
      public void run() {
        discovery = true;
        RadiogramConnection rc = null;
        Datagram dg = null;
        try {
          // open a broadcast connection
          rc = (RadiogramConnection) Connector.open("radiogram://broadcast:"+PORT);
          dg = rc.newDatagram(rc.getMaximumLength());
        } catch (IOException e) {
          System.out.println("Could not broadcast");
          return;
        }
        discovery = true;
        while(discovery){
          try {
            // Send appName (UTF encoded)
            dg.reset();
            dg.writeUTF(appName);
            rc.send(dg);
            // flash the LED slowly indicate broadcast
            // stops flashing when SPOTs are sending data back
            greenLite.setOn(!greenLite.isOn());
          } catch (IOException e) {
            System.out.println("Could not broadcast");
          }
          Utils.sleep(500);
        }
        greenLite.setOff();
      } // run
    }.start();
  }
  // receiver thread
  private void receiveSensor() {
    new Thread() {
      public void run() {
        double dv;
        double di;
        int ir;
        int sleep_discharge = 0;
        int sleep_vbatt = 0;
        int run_discharge = 0;
        int run_vbatt = 0;
        int batt_level = 0;
        RadiogramConnection rc = null;
        Datagram dg = null;
        try {
          // open port to receive
          rc = (RadiogramConnection)
           Connector.open("radiogram://:" + PORT);
          dg = rc.newDatagram(rc.getMaximumLength());
        } catch (IOException e) {
          System.out.println("Could not open receiver");
          e.printStackTrace();
          return;
        }
        while(true) {
          try {
            // receive the data packet
            dg.reset();
            rc.receive(dg);
            sleep_discharge = dg.readInt();
            run_discharge = dg.readInt();
            sleep_vbatt = dg.readInt();
            run_vbatt = dg.readInt();
            batt_level = dg.readInt();
            discovery = false;
          } catch (IOException e) {
            System.out.println("Nothing received");
          }
          // multiple SPOTs can send to this basestation
          // print the sensor SPOT address to identify
          System.out.println("Device: " + dg.getAddress());
          System.out.println("Battery Level: " + batt_level + "%");
          System.out.println("Low: " + sleep_discharge + "mA " + sleep_vbatt + "mV");
          System.out.println("High: " + run_discharge + "mA " + run_vbatt + "mV");
          // find the delta volts/delta current for internal resistance
          dv = ((double) sleep_vbatt - run_vbatt);
          di = ((double) run_discharge - sleep_discharge);
          if (di > 0 && dv > 0) {
          // internal resistance is dv/di
          // remove the sense resistance (0.1ohm) contribution
          // convert to milliohms and round (why? double to string isn't formatted)
            ir = (int) (((dv/di - 0.1)*1000.0) + 0.5);
            System.out.println("Battery Resistance: " + ir + " milliohms");
          }
        } // while
      } // run()
    }.start();
  }

  protected void startApp() throws MIDletStateChangeException {
    // listen for USB
    new com.sun.spot.util.BootloaderListener().start();
    // start broadcast thread
    sendBroadcast();
    // start receive thread
    receiveSensor();
  }

I've finally caught my breath after JavaOne - we had a busy week. We were able to bring FIRST to a lot of technical people that had never heard of FIRST, and let people know that Java was coming.

JavaOne Pavilion

We had local FRC teams demonstrating robots in the pavilion, along with representatives from FIRST. This was very popular at the show - anytime the robots were running we had an audience.

Some related media:

Technical Session and BOF

Eric Arseneau and Brad Miller presented TS-4945 "FRC-FIRST Robotic Competition" (slides to be posted). And Derek White and Brad Miller lead the Birds-Of-a-Feather session BOF-4953 "FRC-FIRST Robotic Competition" - but the teams were the stars of the BOF.

Setting up for BOF

James Gosling's Toy Show Keynote

Brad Miller and James Gosling talked about FIRST and Java in front of a live audience of thousands and webcast to many more. I helped with a demo along with Scott and Austin.

This was the most nerve-wracking demo I've given, and it wasn't even due to the huge crowd. Imagine running a demo using WiFi in a room with 20-30 industrial-sized access points in the same band. Then add in thousands of laptops. In rehearsal we spent between 2-30 minutes each time to get a link between the robot and the driver station! We started the morning by linking the robot to the router, and left everything on (battery powered) for an hour and a half. When we were called on stage for the live demo, we had to do a quick check for power and connectivity. We had backup plans for the backup plans, but luckily didn't need to use them.

Correction: The C++ experience on the new cRIO-based robots isn't as neolithic as James makes out - the IDE includes modern debugging support. But it's not Java.

Duke's Choice Award

As mentioned previously, WPI won the Duke's Choice Award for it part in porting Java to the new robot system, enabling teams to use Java in next years competition.

Correction: Please refer only to this version of the press release. Other versions make some inaccurate claims.

Thanks

This wouldn't have happened without the work of a lot of people. I'd like to thank the FRC teams for coming to JavaOne right in the middle of final exams, and sharing their robots for the demos. I'd especially like to thank DJ and Scott of team 971 for coming every single day of JavaOne! And I'd like to thank James Gosling for his support and forbearance. You'll note that Brad Miller was involved in just about everything. Jim Beck from FIRST helped get the teams lined up, Eric arranged the whole FIRST @ JavaOne experience, and a pile of Sun people helped everything run smoothly (well, as smoothly as it did).

More Information


Just came from the Sun SPOT Birds of a Feather (BOF) Session.

Wow!  The Sun SPOT team has been busy.

Roger Meike
Randy Smith
Ron Goldman
David Simmons
Arshan
Gupta?


Ygglas?
Wireless Sensor Network - Vodafone
Air Store (the big hash in the sky)
Radio View
Robot View



What is a SPOT?  Can we talk about it?  Pete St. Pierre

What does it mean to be a SPOT?  Is it more than just a Squawk VM?

CircuitMonkey.com 
- into ROBOTS
- robicon.org - Mark Koch?


PlaySIM - card to slip ito PIN slot of cellphone other end of cable to sun SPOT
pats.no  (norway)

Systronix - Platform is a TrackBot
Bruce Boyes (Systronix, Inc)
Brian Jenkins (Santa Clara University)

eGPSyBT
GPS and BlutTooth (Demo at booth)


Bob Alkar (spot hardware)
Ehterwatt
- bump in wire from you
- Cortec-Sim 3 Processor with Ethernet built in

EtherSPOT coming up
Charging circuit with Solar panel 

Etherwatt?

sensor.network
---------------

Javier Rubio from Madrid, SPAIN (Vodafone-Sun WSN project
Wireless Sensor Networks & Sun SPOT
Infrared detects if peoople are in the meeeting rooms and send data to sensor.network

Claudio M. Horivlleur
Cromasoft, S.A. de C.V.
Mexico
claudio@croma.com.mx
An easy way to do hardware development - do it on Sun SPOT, then move to hardware design

Java 5 in Squawk
- Eric Ersineau
- Derek White
They are putting Java 5 features into Squawk

Randy Smith
Airstore



Hiroshi Koide
Large Scale Sofwtare Technology
Master Course

Worcester Polytechnic Institute (WPI) won the 2009 Duke's Choice Award in the Category: Java Technology in Education for it's work bringing Java to the FIRST Robotics Competition.

This was worked started by WPI students Brian O'Keefe, Marouane Afiri, and Albedith Diaz for their "Major Qualifying Project". They ported the open source Squawk JVM to the compactRIO robot control system, and ported WPILib, the C++ robotics library used in the FIRST Robotics competition, to Java. They did a great job in in a very short time - they did the first port of Squawk to VxWorks (it was actually only the second port of Squawk to run "natively" on an OS), and were part of the initial proposal to the FIRST organization. I was very happy to be the team's "industry" advisor - this became my introduction into the world of FIRST robotics!

This work has been continued by the WPI Robotics Resource Center, assisted by the Squawk and Sun SPOT teams at Sun Microsystems Laboratories and other volunteers.

It's great to see WPI get recognized for the efforts to bring Java to the thousands of students developing robots for the FRC. Be on the lookout for robots in James Gosling's Toy Show keynote, and for the award presentation to Brad Miller of WPI!


Author: joakim42
Keywords: IPv6 Sun SPOT Internet of things spaughts
Added: June 1, 2009

Flight delayed en-route to JavaOne 2009 in San Francisco.

Had a chance to put together a quick random color application for a Sun SPOT.




FIRST and the FIRST Robotics Competition will be part of several events at the JavaOne conference June1-5 at the Moscone Center in San Francisco. Many sessions are FREE to all, or FREE to students (registration required)

CommunityOne
Monday, June 1 - S311736: "Lightning Talks, Part 4/FRC-FIRST Robotic Competition" 2:40 PM - 3:30 PM Esplanade 303

CommunityOne Registration
CommunityOne events are free to all. Register at http://www.cplan.com/communityone2009/w ... gistration

JavaOne
Pavilion - Java Playground
Will have representatives from FIRST, WPI, and several FRC teams. Robots will be demoed in the robot arena. Learn about FIRST, the FIRST Robotics Competition, and Java for FRC. Find out how to get involved!
Monday (June 1) 3:00 pm - 7:00 pm,
Tuesday 11:30 am - 7:30 pm
Wednesday 10:00 am - 4:30 pm
Thursday 10:00 am - 2:00 pm

Thursday, June 4 - TS-4945: FRC-FIRST Robotic Competition 2:50 PM - 3:50 PM Esplanade 305

1.The Java™ platform is an interesting vehicle for teaching kids about programming.
2. FIRST is an organization whose mission is to inspire young people to be science and technology leaders by engaging them in exciting mentor-based programs that build science, engineering, and technology skills; inspire innovation; and foster well-rounded life abilities such as self-confidence, communication, and leadership.
3. Robots are cool; robotic competitions are even cooler.
What happens when you mix these three things? You come up with a winning combination that lets kids and "adults" have a lot of fun.

Thursday, June 4 - BOF-4953 (Birds-Of-a-Feather) FRC-FIRST Robotic Competition 6:30 PM - 7:20 PM North Hall 124
What do robots and FIRST have to do with Java™ technology? Come to this session and see firsthand what Java technology is enabling kids to do today with some cool hardware. Get to play with big competition robots and their teams.

Friday, June 5 - James Gosling's Toy Show (general session) 8:30 am - 10:30 am

JavaOne Registration
The JavaOne Pavilion Pass, which includes General Sessions (and James Gosling's Toy Show) is free to all. Register at http://java.sun.com/javaone/2009/regist ... ilion_pass.

All sessions at JavaOne are free to students (under 18 requires chaperon). Register at http://java.sun.com/javaone/2009/regist ... d_students

I attended the 2009 FIRST Championship April 16-18 at the Georgia Dome in Atlanta, GA as part of the Java for FRC announcement. We demoed Java running on several robots, over-the-air debugging, and multi-platform development (Windows, Linux, Mac (and we kept getting asked about Solaris)). We gave a couple of talks with James Gosling about Java for FRC.

IMG_4198 IMG_4108 IMG_4136 IMG_4176

This was my first time at a FIRST Championship and it was pretty overwhelming.

  • 20,000 people
  • Hundreds of FRC, FTC, and FLL teams (and robots)
  • Four simultaneous competitions in the Georgia Dome
  • The World Congress Center filled with pits, practice fields, presentations, and people

But what was really impressive were the teams. It was really clear that FIRST was succeeding at its mission of inspiring students in science and technology. Students didn't just learn that science was cool - they knew that they were cool. These are kids that are not worried about being called geeks, or anything else for that matter:

IMG_1705 IMG_4155

Slideshow

As always, check out FIRST Robotics @ Sun for information on getting involved with FIRST.

Last Friday we had three teams, five robots, WPI and FIRST come to visit Sun's Burlington, MA campus. The teams talked about their robots and experiences with FIRST, and mentors explained what mentoring for FIRST was all about.

Two of the robots were Java-powered - team 1519's little "Speed Racer" (look for the Duke on top), and WPI's demo robot ("demo" may stand for demonstration or demolition). Steve Heller arranged a demonstration of torque:

IMG_4563

Check out FIRST Robotics @ Sun for future FIRST show-and-tells on campus, and more information about FIRST.

Next stop - JavaOne!

IMG_4566
IMG_4555 IMG_4554 IMG_4552

A while back, I built a home PC with the latest and greatest top o’line graphics card, the nVidia GeForce 8800GTX. This monster was longer than your typical PC card,  took two slots and had two independent power connectors to feed it. It was an impressive piece of graphics processing until I got to Rivendell in LOTRO. Then, fwoomp, the dreaded blue screen of death (BSOD). 

This BSOD also occured under other programs that made heavy use of the graphics engine. I did the usual updating of drivers, ran dxdiag and searched the web but all was too new. The computer wasn't overclocked and had worked well with other graphics cards. I RMAed the card but same thing happened again. What I did notice was the card ran hot, to hot to the touch. I remember Nelson Pass of Pass labs writing that a good rule of thumb was if it is too hot to touch, its more than 60°C. So I measured it with an IR remote thermometer and saw temperatures in excess of 80°C. I installed the Microsoft debug tools and found the crashes were occurring in accesses to video memory. The video memory was thermally connected through the heat sink to the GPU and my theory was the GPU was heating the memory and causing it to fail.

I tried to solve it with more fans except it's own fan housing limited how much airflow I could get across the heat sink. Many of the manufacturers were bringing out 8800GTX with built in water blocks. Microelectronics and liquids in the same case seemed like a bad idea, but I was running out of options in salvaging my graphics card. 

First item was a new case that could hold the radiator, pump and reservoir. I went with the Silverstone TJ07 full tower case with a clear side window. This was inspired by a beautiful system made by Puget Systems. This is a well-made expensive case and was *almost* made for water-cooling.

 

I went with the 3/8” ID tubing and the following hardware:

  • Danger Den 8800GTX water block
  • Koolance Socket 775 water block
  • Danger Den Delrin fillport
  • Black Ice Radiator with dual Scythe SFF21E fans
  • Swiftech MCP355 with pump mod
  • Aqua Computer Reservoir

The system was hooked up this way. I had the reservoir backlit with a blue LED and it had a temperature sensor. 

The TJ07 has a top screen for two exhaust fans. Keith and I modified the screen to hold the radiator with pop rivets and the Scythe fans were mounted to the radiator using rubber fan mounts.

 

I drilled one hole in the top for the fill plug. It was a tight fit with the mother board. 

The Swiftech pump was mounted in the bottom behind the power supply.  Originally it was mounted with the enclosed block of double stick foam, but it was loud and sounded like an aquarium pump. A better solution was to use a pair of urethane cylindrical mounts. These are very soft and highly damped rubber mounts. The pump entry line was braced with another standoff to add stability for the pump. This reduced the pump noise considerably.

 

Each water block came with instructions for removal of the existing heatsink and replacement with the water block. I cleaned up the existing heat sink material and spread the included thermal compound with a business card. The boards are put into the case and hoses cut to size and attached to the fittings with o-clamps. I found the fittings that do not capture the o-ring could be easily over tightened and distorted. There is little margin between over- and under- tightening.

I originally used Tygon R3603 tubing. It’s thick-walled, doesn’t kink but it became stiff and brittle over time. I used vinyl tubing from the hardware store and it has worked fine. I tried one of the non-conductive liquid coolants but it built up goo in the tubing and made a mess of things. I had to flush the system and switched to distilled water.

Another thing that came in handy was a combination of the UV lights with a few drops of “Dye-Lite” leak detection dye in the fluid. At first, this was to make it look cool but is very effective for spotting leaks.

I tried a flow meter that was based on a little turbine; however, the shaft seized from the fluid. The turbulence of the fluid in the front panel reservoir window is enough to show flow.

Once the system is put together, This is a closed system and the air in the reservoir needs a way to get out for the fluid to get in. This can be done by running a small plastic into fill port until it reaches the reservoir.  Once it has some liquid in the system, I apply power to the pump for a short period of time and start working the air out of the lines. I repeat this process until the system has no more air in the lines and then check for leaks. Once this is complete, the pump is hooked to the 12V power and the system closed up.

Since I’ve installed the liquid cooling system, I have never seen the BSOD again and the graphics card has been well behaved.  The GPU and CPU temperatures range from 30°C to 38°C and runs remarkably quiet. It has had on occasion, minor leaks from fittings which were either too tight or had worked loose. It also will leak if the hose becomes too old and stiff. 

 

Here are some upcoming events where you can learn more about FIRST, the FIRST Robotics Competition, Java + FIRST, and FIRST @ Sun.

This Friday, for Sun employees, we will have one or two FRC teams visiting the Burlington, MA campus. They will show off their robots  - and you can learn about what FRC means to them, and about mentoring and volunteer opportunities that exist. I'll also be here to talk about Java on the FRC robots, and should have a robot to demo.

Sun has given FIRST a big platform at JavaOne to show off what FIRST is about. It's also a chance to announce that along with Java being available for the robotics competition next year comes a need for experienced Java developers to mentor teams all across the United States and internationally.

There will be live robot demos in the Pavillion, where you can also talk to teams, mentors, and volunteers about FIRST. There will be a Tech Session describing Java for FRC - both how it was ported and how it can be used (TS-4945), a Birds-Of-a-Feather session for existing FIRST participants as well as though interested in learning more (BOF-4953), and perhaps a comment from James Gosling at his closing keynote!

Date

Time

Event
Venue Location
May 22

Noon

FIRST Teams "show-and-tell"
Sun Cafeteria*** Burlington, MA
Jun 1-4
daily

FIRST booth and Robot Arena
Java Playground - Pavilion JavaOne
Jun 4
2:50pm

Tech Session 4945: FRC-FIRST Robotic Competition
Esplanade 305 JavaOne
Jun 4

6:30pm

BOF-4953: FRC-FIRST Robotic Competition
North Hall 124 JavaOne

Keep in mind that the JavaOne Pavilion access is FREE. Students can get access to all JavaOne events FREE

*** The event at the Sun campus in Burlington, MA is open to emplyees only.

On April 16th we announced that Java would be available for the FIRST Robotics Competition. This is joint work between Sun and WPI to port Squawk (an open-source Java virtual machine) to the compactRIO robot control system, as well as the WPILib robotics library from C++ to Java.

We expect this to be a great addition for FRC teams that have some Java programming experience, and will bring not only the combination of power and safety inherent in Java to robotics programming, but also the ecosystem of Java tools, libraries and learning resources.

The exact flavor of Java that will be available is Java ME (Micro Edition), configured with the CLDC and IMP libraries. This is essentially the same flavor of Java that is installed in millions of cell phones (but without the graphic user interface libraries - LCDUI).

In addition the system is based on the libraries and SDK of the Sun SPOT project. It includes over-the-air debugger support as part of integration with the NetBeans and Eclipse IDEs and also includes ant-based command line tools.


The Java system will be in closed alpha and beta releases over the Summer and Fall, and will be ship in the "kit of parts" for the 2010 season (if not earlier).  We have also developed a software-only robot emulator that allows you to learn how to develop robotic software in Java using the Sun SPOT APIs (but doesn't yet include the WPILib APIs).

I'll be blogging more about this, including technical details, the Atlanta Championship, and Java + FIRST @ Java One, but here are some pointers to more information:

On the SunSPOT world forums, there was a query regarding the power consumed by the TI CC2420 radio transceiver. The SPOT has quite a bit going on and the power consumption is very dynamic. The only way I could see to get an accurate reading was to isolate the power into the radio chip. There is a ferrite bead, FB1, in series between the switcher and the radio VDD that could be removed and a milliammeter put in its place.


eSPOT main board CC2420 power

We found a working rev 6 eSPOT (s/n 2DA1) and loaded up red-090429 release. Del removed the bottom shield and ferrite bead and connected the test wires. We used a calibrated Agilent 34410A digital multimeter for the readings. This is a free range SPOT with eDEMO board and battery. It was battery powered during the test and the battery was fully charged.

Current testing CC2420


I used a program written by Syntropy for our FCC compliance test. This test can broadcast both modulated direct sequence spread spectrum and unmodulated carrier on channels 11, 18 and 26. The power output of the transmitter can be adjusted between 0dBm to -24dBm. The test receives on channels 11, 18 and 26 and will indicates packet errors in the LEDs.

The CC2420 is a transceiver - either the transmitter is on or the receiver is on but you cannot have both on at the same time. When the SPOT is active (not in deep sleep) the CC2420 draws 0.44ma without either transmitter or receiver enabled.

The receiver drew 18.57ma (ch11), 18.49ma (ch18) and 18.61ma (ch26) without a nearby transmission. The receive current drops about 30uA when receiving packets from a nearby transmitter. It did not vary noticeably if the packets were sent at low or high signal levels.

Transmit power drew at full output power modulated 18.24ma (ch11), 18.18ma (ch18) and 18.05ma (ch26). At full power unmodulated carrier, it consumed 18.04ma (ch11), 17.98ma (ch18) and 15.54ma (ch26). The higher power setting of ch26 is forced to a lower power output to be compliant with FCC emission requirements for the ISM band. The following is a graph of the current versus RF output power. There was very little variation switching between channels.

CC2420 transmit power


The measured transmit current of 18.24ma was higher by 0.82ma than the typical specification rating of 17.4ma. The measured receive current of 18.57ma was lower by 1.13ma than the specified typical of 19.7ma. The idle mode was slightly higher by 14uA with 440ua measured and 426uA specified.

There were minor rapid fluctuations of power during the test. The transmit power would fluctuate about 20-30uA and the receive current fluctuated <7uA (not including the variation caused by nearby transmitter). The values measured were average values over these fluctuations.

If there are other power measurements on the radio that need to be done, let me know. I'll keep this modified SPOT around for that.


logo.jpgIf you're into robotics (and why wouldn't you be, right?), and especially if you're in the younger crowd, you'll likely be as excited as we are about this news: FIRST™ adds Java™ Technology to the First Robotics Competition Tool Kit. Yeah, that's us. It's Squawk.  


If you want to know more about FIRST, head over to their website. It's cool stuff, and having Java, and Squawk, as part of it is just too cool!


[ "That must be wonderful! I don't understand it at all." ]

starfish.png


No, not this one, or even one of mine! But this one from my boss, Roger.


So now we're going to have to arrange for a Sun SPOT Engineering outing to Great America in San Jose to get some telemetry off of some of the big-boy rides there.


[ Hardware, n.:  The parts of a computer system that can be kicked. ]

Amusing Ourselves with Sensors in the Field

My son's math/science club had an opportunity to see a real rocket launch at Vandenberg Air Force Base this week. Unfortunately, the weather was bad enough that our NASA hosts decided not to attend the launch which took away our inside track to the day's activities. We were already half way to Vandenberg (approximately 250miles away) when we found this out. Faced with the prospect of a long trip with the chance of seeing nothing through the fog we decided to change our plans, so we vectored off to Santa Cruz and its famous beach boardwalk for a little Sun SPOT-based physics fun.

You see, Santa Cruz boardwalk includes a bunch of amusement park rides. Our crack team of researchers (the 4th & 5th grade math/science club) decided to use the Sun SPOTs to measure the G forces experienced in some of these amusement park rides. Fortunately, the standard Sun SPOT Telemetry Demo is just perfect for this application. It displays, in real time, the output of all three axes of the accelerometer and a total G force reading. By simply putting a Sun SPOT in someone's pocket while they ride the ride, we can see what G forces their body is being exposed to. We measured 5 different rides:

  • Starfish - a spinning ride
  • SeaSwing - a large rotating swing ride
  • Bumper Cars
  • Hurricane - a roller coaster
  • Drop Zone - a vertical drop ride

Lets take a look at the project in a little more detail and see what we can learn.

Accelerometer Background

We used the eSPOT 3-axis accelerometer to monitor the motion. The Telemetry Demo application consists of two parts, one that runs on the eSPOT device and one that runs on the host computer. The application that runs in the eSPOT device takes accelerometer readings of all three dimensions and sends them over the base station to the where the data is received and plotted live on a laptop computer and saved for later analysis. (Yes, as you may have guessed, this implies that I was carrying a laptop computer around an amusement park - I wear my geek badge proudly) Accelorometer.png

The accelerometer on the eDemoBoard is actually 3 separate accelerometers, one for each dimension. The accelerometers are very tiny Micro Electro-Mechanical Systems (MEMS) devices hidden in a computer chip package. You can imagine that an accelerometer is made of two main parts suspended between two mounts. One of the parts connects to both mounts; we'll call it a bridge. The other only connects to one of the mounts, we'll call it the diving board. There is a mass on the end of the diving board so that it will wiggle around with respect to the bridge when the accelerometer is moved.

accel.jpg

If you were to grab this device by the two mounts and wiggle it up and down you can imagine that the diving board would bend slightly. When a charge is applied to the bridge and diving board, it creates a sort of capacitor. As that diving board bends ever so slightly, it changes the capacitance between those parts and this can be measured. This allows the accelerometer to report a signal that corresponds to the movement of the device in one dimension. Also, note that the diving board can only bend in on dimension (up and down in the diagram), not side to side. If you imagine three of these packed very tightly together, each oriented 90 degrees from the other, that is a useful model for what goes on inside the accelerometer.

You can also imagine that when a eSPOT device is lying on its back, just sitting stationary, the the Z axis diving board will be bent down slightly by gravity. In fact, it will have 1G (or 9.8 m/sec^2) of bend to it. Now if you turned it over so that it was lying on it's sun roof, that same Z-axis accelerometer would have its diving board board bending the other direction. Therefore, it would measure -1G (or -9.8 m/sec^2). Meanwhile, in both of these orientations, the X and Y axis accelerometers would have no gravity working on them so they would have a zero reading.

In fact, it is interesting to note that when the Sun SPOT device is not accelerating (or decelerating), the accelerometers an tell you how the device is tilted. In other words, it can tell you which way is down. That is because the square root of the sum the squares of all the values will add to 1G in a downward direction no matter what way you tilt it. If the X axis accelerometer is measuring a non-zero value and the Y axis accelerometer is measuring a non-zero value, but the Z axis accelerometer measures 0G then Z axis must be parallel to the horizon.

The Telemetry-onSpot samples the three accelerometers every 10 milliseconds and packages the readings into a wireless network packet to be sent to the host application. The Telemetry-onHost application receives these packets and plots them on a moving graph. The green, blue and red lines represent the X, Y and Z axes respectively. It also calculates the the sum of the absolute values of all three readings together. This can be useful in determining the over acceleration applied to the device.

The Experiment

We were interested in measuring the amount of acceleration that our subject's body is exposed to as it rides the amusement park rides. It is easy to max out the 6G accelerometer readings by just waving a Sun SPOT back and forth vigorously in your hand. Clearly this would not mean that the subject's entire body was experiencing many Gs worth of force, only their hand. So we determined that it is important to make sure that the device is securely fastened somewhere near subject's center of gravity. We found a front pants pocket to be a fine place put it. Also, it is handy that the Telemetry-onHost application provides some smoothing functions that can reduce some of the noise due to ride vibration.

Now its time to ride some rides.


Starfish

starfish.jpg
The starfish is a spinning ride that basically goes around in a circle at high speed. What we expect to see then is the force of gravity pulling down, plus the cetrifugal force of the ride spinning that makes you feel yourself pulled to the outside. We see a slight oscillation in the data that is either caused by the ride not being level, or slight speed variations in the motor. Generally, however, we see observe we are measuring about 1.3G or ~12.5 m/sec^2 total acceleration. If we assume that the centripetal force of the ride is at 90 degrees to the gravitational acceleration, and we know that gravity is contributing 1G worth of acceleration, we can calculate how much acceleration the ride is contributing with the following formula:
200905080549.jpg


200905072344.jpg

By plugging in values we can deduce that the ride is providing an extra ~7.8 m/sec^2 of acceleration or about 0.8G of lateral acceleration. If we knew the diameter of the ride, we could probably calculate the speed the ride was spinning.

starfish.png

A CSV file of the raw data is available here: starfish.csv.zip

Sea Swing

Seaswing.jpg

Like the Starfish ride, this ride spins, but it adds an extra twist. The riders are suspended in a swing and the entire ride tilts. Because of the tilt, the rider feels more acceleration on the upswing and less on the downswing as the ride goes round and round. This is borne out in the data that we see below. Once again, its very easy to observe the period of the rotation.
SeaSwing.png

A CSV file of the raw data is available here: seaswing.csv


Bumper Cars

bumper.jpg

In the bumper cars we see a lot of shaking around, but generally an average of 1G punctuated by the occasional jarring 2G jolt of a collision. This data is much more noisy and ultimately much more difficult to draw conclusions from.
Bumper.png

A CSV file of the raw data is available here: bumper2.csv

Hurricane


hurricane.jpg
The hurricane is a small roller coaster, but it gives us the most acceleration of any of the rides; approximately 4Gs! We can see that the ride lasted only about 40 seconds, but a lot is packed into that short time. If you look closely you can also see a few places early on where the Sun SPOT device changes orientation slightly. These are caused by the subject sitting down and the roller coaster and then heading up the initial hill. Also note that it is really hard to tell a turn from a hill. Because the roller coaster tilts around corners, both tend to push the subject down into their seat. As with many experiments, a gyro would be very helpful here because we could then determine how the subject is tilted in order to understand how to the forces that we are measuring are being applied. For more on this see my Location, Location, Location blog entry.

Hurricane.png

A CSV file of the raw data is available here: hurricane.csv.zip

Drop Zone


dropzone.jpg   DropZoneLong.jpg

This ride carries the subject up and down a very large tower at high speed. It created some of the prettiest data for us to work with. Because the motion of the ride is restricted to a single dimension, we don't have to worry about any missing data on rotation, so we can really figure out some interesting things about this ride. As you can see, there are three main up and down thrusts followed by three or four successively smaller moves. After a couple of runs (good research requires reproducible results), we found an average of about three Gs of acceleration during the initial lift. Interestingly, our data also shows that the free fall portion of the ride is not actually a free fall. In fact the data seems to suggest that the subject is pulled down with an acceleration of approximately -1G!
DropZone.png

A CSV file of the raw data for the two runs are available here: dropzone1.csv dropzone2.csv


Your Challenge

Take some of this data and see if you can analyze it further. In particular the DropZone ride is appropriately constrained to provide an interesting area for further exploration. An interesting exercise would be to take this data and calculate how high you think the ride lifts you and how fast you go up and down. Let me know what you come up with.

If there is interest, I can analyze the data in more detail and see what we come up with in a future blog.

Don't let anyone ever tell you that research isn't fun!


200905080607.jpg

There is now a new Beta version of the Sun SPOT software available.

It includes the latest features like:

  • Radio View - wireless network analysis tool
  • Robot View - Robot Simulation
  • Link Quality Routing Protocol - more reliable wireless communication
  • SPOT Web demo - web integrated sensors
  • Optional encryption library and demos
  • Yggdrasil data collection framework and demos
  • more

Its freely available, easy to install and you can always revert back to previous versions. Take a look for yourself and let us know what you think on the forums. If you haven't installed the development kit yet, you can do so here.

Once you have done that, you can get the beta version using SPOT Manager by clicking on the "Preferences" tab and choosing "Beta Update Center"


Untitled2.png

Then go to the "SDKs" tab, choose the lasted release and click "Install." Its very easy.


Untitled.png

Have Fun!

Ok, it's been a while since we've pushed out a Beta -- we've been doing fairly regular Developer Builds -- so it's that time! Version 5 of the Sun SPOT SDK (we call it 'Red') is now available on the Beta site.


Not sure how to get the Beta Software? Here's a quick tutorial.


If you already have the Sun SPOT SDK Installed (and of course you do, don't you?), then start the Sun SPOT Manager Tool.


Go to the 'Preferences Panel:




SPOTManager1.png


And select the 'Beta Update Center':




SPOTManager2.png


Then go back to the SDKs Panel:




SPOTManager3.png


and install the red Beta1. It's really that easy.


And for those of you that like to live on the bleeding edge with us, you can always go back to the Preferences Pane and select the 'Dev Preview' Update Center for regular Developer Builds. Right now, the Beta1 build and the last Dev Preview build are bit-for-bit exactly the same, but just for future reference ... (Note: The SPOT Manager will remember your choice of Beta Update Center or General Release Update Center, but by design it will always 'forget' the Dev Preview selection, just to keep you from accidentally installing a (potentially unstable) Developer Build.).


Enjoy! And please report any issues you may have on the forums. Also note that the Robotics Emulator and lots of other cool stuff is included in this release, so enjoy!


[ Real Users never know what they want, but they always know when your program doesn't deliver it. ]

The Sun SPOT, our wireless sensor network device,  is powered by a small rechargeable prismatic lithium-ion battery. This battery is similar to the one found in most cell phones and mp3 players. This is some of the what I learned while designing with this battery. To find out more about the Sun SPOT go http://www.sunspotworld.com or https://spots.dev.java.net. 

Our batteries are manufactured by Sanyo. This is a rechargeable lithium-ion 3.7V 720maH (B version) or 770maH (D version) prismatic cell. It is in a prismatic (or rectangular) package and measures 35.8mm wide, 41.5mm tall and 5.5mm thick and weighs 15g. It has a lithium cobalt dioxide anode, a graphite cathode with lithium hexaflourophosphate and carbonic acid ester as the electrolyte. We buy through an OEM which attaches the safety circuit, wiring, connector, protective shell and label to the battery. Our batteries meet the UL and PSE safety compliance.

This battery is rated by its capacity and nominal voltage. The capacity is the amount of energy that a battery can deliver for a single discharge in amp-hours. That is, a 720maH battery would have the capacity to discharge into a 720ma load for one hour (1C) or 360ma load for two hours (0.5C) and so on. Ours has a nominal voltage of 3.7V. The voltage may go as high as 4.2V when fully charged and will drop quickly to the nominal voltage and stay there until almost fully discharged.

The safety circuit is mounted on the battery and acts as an electronic circuit breaker. It will protect the battery from over voltage during charge, over current during charge or discharge, short circuit protection and battery under-voltage. Without the safety circuit, the battery would get hot, very hot, catch fire, vent and possibly explode. 

Some tips for our battery:

  • Don't circumvent the safety circuit. It works very well and makes our batteries safe to use.
  • Use the right battery charger. It should be current limited to 500ma with a maximum of 4.2V (4.1V is even healthier for the battery. It should charge for an hour and check for full charge. If the battery is fully discharged, it should trickle charge until the battery has recovered. It should not trickle charge after the battery is fully charged.
  • Discharging a battery to be completely dead can not and should not be recovered. When storing the battery, keep some charge so that it doesn't self discharge completely.
  • Don't solder to the battery terminals. The heat is bad for the battery, can be bad for anything within a yard of the battery. 
  • Don't expose the battery to extreme temperatures. Even left in a car on a hot summer day can damage the battery. This goes for outdoor installations of SPOTs.
  • If the battery is not placed in the SPOTs plastic, be mindful that the battery will swell normally, may need to vent and will raise in temperature while charging. 

 

The battery performance can be affected by temperature, age, charge/discharge cycles and shelf life.

For charging, the temperature needs to be within 0ºC to +45ºC, for discharging between -20ºC to +60ºC and for storage between -20ºC to +35ºC.

This battery is best stored with 40% charge and kept cold but not frozen. Storing batteries where it is hot will not only accelerate the self discharge but also shorten the life of the battery. The following table was pulled from graphs in the Sanyo's specification of the battery during storage. The recovered capacity is the remaining capacity after permanent losses (damage) and residual capacity is the remaining charge state after self discharge.


If the battery drops to 2.3V, the low voltage threshold will cause the safety circuit to open and the battery output will appear flat. This is considered too low of voltage to safely charge the battery and should be considered dead. The SPOT indicates low battery when it is at 3.5V, forces sleep mode on the ARM9 at 3.3V, the power controller and standby voltage shuts down at 2.9V and the safety circuit will open when the battery drops to 2.3V. The battery charger detects battery below 2.85V it will attempt a trickle charge for about 15 minutes but if it doesn't correct itself, it will not charge.

Sanyo's specification says this battery will lose about 20% of it's capacity after 500 charge cycles. They test this with a 720ma charge/discharge (1C) cycles. We charge with 450ma max and discharge from 70ma to 350ma and tend to last a lot longer than 500 cycles. (350ma if your using it as a SPOT light).  These batteries will age and will gradually lose capacity whether they are being stored or in use. The SPOT batteries are shipped to us at charged to 30% - 40% capacity and we do not charge them any more than this when we ship them out. 

Most battery monitors require a deep discharge and full charge cycle to calibrate the state of charge of the battery although batteries will last longer if not fully discharged.

At cold temperatures, the battery will charge to 4% to 6% below normal capacity at 0ºC and take longer charge. The battery voltage drops at temperatures below 0ºC making the effective capacity about 15% less at -10ºC (-0.1V drop) and 44% less at -20ºC (-0.25V drop). This is for typical SPOT load of 72ma, losses can double at higher discharge currents of 350ma.

The battery may generate some self-heating and swell slightly during charge cycles. This can be seen from the on board temperature sensor. 

Internal Resistance

All batteries have an internal resistance, that is, the battery voltage will drop when a load is placed on the battery. As the battery ages or is subjected to damaging conditions, this resistance increases. It will affect battery voltage measurements and must be compensated for during heavy load if the voltage is used to determine a low battery condition.

Most battery manufactures specify the internal resistance as impedance and use a small AC (1000Hz) test signal into the battery and measure the AC voltage to derive the impedance. Our battery measures around 0.05 ohms and is typical for this test.

Another method for calculating the internal resistance is to measure the unloaded and loaded voltages across the battery and divide by the load current. I measured this to be around 0.27 ohms using a java program I wrote. It shut everything I could off and measured the load current and battery voltage. I turned on all the LEDs and the radio to create a higher load and measured the load current and battery voltage. I did get similar results to the following setup we made for the battery.


Charging the battery

The battery is charged from USB using an integrated battery charger and dual switcher IC, Linear Technology LTC3455. The battery is charged with 4.2V and current limited to about 450ma (high power mode). If the battery has not reached 4.05V after the charge cycle, another cycle is started. If the battery is below 2.85V, the charger will enter a “trickle charge” mode to try and recover a nearly dead battery before attempting a full charge cycle. USB limits current to 100ma until the high power mode is negotiated during USB enumeration. Unfortunately, the switcher supplies somewhat less than this and most is used to run the ARM. It will not get much charge during low power USB mode.

We use power supplies which supply 5V through a USB cable and don't have the brains to enumerate. If we get USB power and do not enumerate, we eventually turn high power mode on so that we can charge the battery. There has been a specification , “Battery Charging v1.1 Spec and Adopters Agreement” which covers this issue. As the dust settles, we'll look into being compliant with it.

Discharging the battery

A typical discharge curve for our battery looks like this:



This curve was sampled once per second with an Agilent 34410A DMM (digital multimeter), a 50 ohm resistor load and a fully charged battery. The data from the DMM was brought into a Mathcad application to be plotted. Here is a drawing of our test setup.  Sanyo's batteries are -0% +10% of the rated capacity when new we confirmed it with this test.

 

Monitoring the battery state

The power controller uses the built ADC to measure battery voltage, charge and discharge current and external voltages. The current is measured by measuring the voltage drop across a low ohm sense resistor.


 

The SPOT uses a pair of Zetex ZXCT1009 high side current monitor across a 0.1 ohm sense resistor.. These are high side differential amplifers (60X gain) differentially amplifying (60X). The gain of our current monitor is set to give us full scale reading at 512ma. 

We use a voltage divider to an ADC channel to measure the battery voltage. Since this divider is on the battery and itself can consume considerable standby power (>250uA), we use a high side P-channel MOSFET to switch the divider on during measurement. We also switch to the less accurate 1.1V internal reference of the Atmega so we can measure battery voltages down to 1.5V.


All ADC channels are sampled every 50msec while the system is awake. The current sense output is low pass filtered for an average current during the 50msec sample period. We also sample USB voltage, Vusb, and external voltage, Vext.

From this information we can deduce:

  • Charging: power detected on Vusb or Vext, measured charge into battery
  • Fully Charged: power detected on Vusb or Vext, was charging, charge below threshold.
  • Discharging: power absent on Vusb and Vext, measured discharge
  • Low battery: power absent on Vusb and Vext, measured discharge, Vbatt reaches low battery threshold.
  • Dead battery: power absent on Vusb and Vext, measured discharge, battery was low, Vbatt dead battery threshold
  • No battery: battery presence detected when power absent on Vusb and Vext.

There are a variety of techniques for measuring the present capacity in percentage. The least accurate is derived from the voltage level. The most common technique used today is “coulomb counting” with variations. Coulomb counting is continually sampling the current flow to and from the battery and maintaining a running sum. We sample every 50msec with sample rate, S, is 20 samples per second.

The batteries actual full capacity varies with battery, it varies over time and it varies over temperature. When a battery is plugged into the SPOT, the initial state of the battery charge and the history of the battery is unknown. If the battery of a SPOT is swapped with another battery, the state of charge (SOC) is also unknown. Some battery monitors go with the battery to maintain the history; however, for a small battery, it isn't practical.

Most monitors require a “calibration cycle”. The cycle is discharging the battery completely followed by a full charge. This way it can measure the full capacity and has a known starting point for the present capacity. This works for a while but will eventually lose accuracy over time and have to be repeated.  Some companies have used clues from internal resistance measurements, chemistry curves, etc to improve the accuracy.

Deep sleep for very long periods of time can contribute to error in capacity.  Most current sense do not measure actual sleep current with microamp accuracy and it isn't practical to run the electronics to measure it during sleep anyway. If extreme temperatures occur during this time, the self discharge may be significantly higher. For periods of deep sleep, we estimate sleep current and multiply it by the period the SPOT was asleep.

One inherent issue with coulomb counting accuracy is that the errors are accumulative. The current sense circuit is a rail to rail amplifier whose bias offset cause higher measurement errors during low currents. We saw 5% to 8% error from the current sense amplifier when compared to the Agilent 34410A into a static load. The current measurement must be averaged over the 50msec sample period as the current can fluctuate dynamically at high rates. We use a filter capacitor on the gain resistor to average high speed current sense. Quantization error from the 10 bit ADC and analog noise contribute less than a <1% to the sample error at average currents (70ma).

Improvements in monitoring

With the existing SPOT, there is a battery class (Ibattery.java and battery.java) which interacts with the power controller to read the battery data. I am moving more of the calculation from the power controller into java code. 

We explored using later versions of the current sense amps the ZXCT1010. These have superior bias offset as compared to their predecessor. Another option is the delta-sigma ADCs with built in amplifiers to sense current and voltage. We have looked at some other devices like the TI impedance match system.

 Monitoring battery capacity is challenging. There are many variables that affect the batteries capacity and state of charge which are difficult to factor in and may not be practical to measure. There are some other angles I'd like to explore and will follow up here.

 

 

I have Two bits of news:

We are looking for your Sun SPOT project video

JavaOne is coming up and Sun SPOTs will be featured in talks and a "Birds of a Feather" as well as at the Change (Y)our World section of the main floor. One of the things that we will have in on the show floor is a large screen showing some of the projects that the Sun SPOT community has done with Sun SPOTs. If you have a project, and would like to make a short video of it in the next two weeks, we may show it on the big screen. Submit your videos to YouTube with the tag "spaughts" and drop me a line to let me know that you'd like participate. No project is too big or two small. Just give us a short video that shows us anything fun, cool, interesting you have done with Sun SPOTs.

Students get in Free!

If you are a student and you are interested in attending JavaOne, you can get in for free. I just learned about this program for students. In fact educators who bring 10 students can also get in for free. That's a pretty sweet deal.

Hope to see you there.

My job at Sun for the past few years has been the hardware lead for the Sun SPOT project. This is a big topic for me and I have a lot of unfinished blogs about it. If you don't know what a Sun SPOT is: go to http://www.sunspotworld.com/

Some History

At the end of 2004, John Nolan ported Squawk, a Java JVM, to an Atmel eval board. Another team, Epsilon project were experimenting with wireless sensor networks and were frustrated with the motes of the day. I was finishing up a PowerPC board with Tom Riddle and was asked if I could do due diligence on a board which could run Java. They had a fuzzy picture, 

no specification and it was tied up with red tape.  It seemed we could make one cheaper than what lawyers would cost to unravel the red tape and we were off.


We hired contractor, Del Peck, our trumpet playing, PC layout artist and design engineer to work with me on this project. We came up with the bSPOT based on the AT91FR40162. A company based out of Great Britain, called Syntropy, did most of the port of Squawk to this new SPOT.

This had a rechargeable battery and a FTDI USB to serial chip for communicationsand charging. We used the popular Chipcon CC2420 radio with the reference inverted-F PC trace antenna. We didn't have the RF design tools nor test equipment to play with the antenna much further than that.


We iterated the design and while I liked the "cSPOT run" pun, the microcontroller left a lot to be desired. It wasn't able to hibernate and drew too much standby power. It had minimal peripherals and we even had to bit bang the most basic of SPI (synchronous peripheral interface). What it did have was a large Flash memory that we needed for Java.


An alternative was the ARM9 family with external memory. VMs tend to play out well in a system with a cache memory and having external memory allowed me to hibernate the system. It was also going to be a more involved port requiring custom USB drivers, memory management code, plastic enclosure, prismatic battery and so on. I sprung this option at the end of my cSPOT design review as an "alternate universe" design with CAD drawings of plastics, built in USB, 8bit microcontrollers, etc. Our VP wondered why I wasted his time talking about "cSPOT" and the "eSPOT" began. 

 

We borrowed John Harada to do the real mechanical design. We also put a shield around the radio to get FCC modular approval. This gave us flexibility to change the digital stuff without having to retest the compliance of the transmitter. We worked with the compliance group to insure it was safe and wasn't going to interfere.


Design Decisions

SPOTs were going to be shared with a community. We open sourced the VM, libraries, applications, design files, schematics, and firmware. We used Sun Java tools (Net Beans, Java SE/ME) and Gnu C tools.


It had to run Java. Our Java used a split model where the suites were compiled on Linux, Windows, Mac, Solaris, whatever and sent via USB or over the air to the SPOT to run. One nice feature was that when all threads were idle for a few seconds, it would go into hibernate mode. The SRAM was powered during this time to be able to recover quickly.


We had to manage low power. The SPOT had similar power pattern of the Cell phone - both have radios, microprocessors, external memory, bells and whistles.

SPOTs had two dedicated 8 bit microcontrollers: one for the eDemo board and one for power control management. The use of these small micros worked out well. One was used like a light's out management and would wake the ARM from sleep. The power consumption of the power controller is still significantly lower overall than most ARMs real time clock and/or the shutdown manager. We also offloaded some of the real time tasks from Java to the microcontrollers and let the ARM be the puppet master. Most of our engineering efforts were around monitoring and managing the power of the SPOT.


Open-Source Hardware

You can see the entire teams efforts here -> https://spots.dev.java.net/

Hardware stuff is here -> https://spots-hardware.dev.java.net/



More SPOT blogs to follow



 Bob

 





 

We have some pretty exciting new features planned for upcoming Red (V5.0) release of the Sun SPOT software environment. In particular I'm really pleased with some of the improvements in the radio stack. We have improved the performance of the system over multiple hops significantly. One of the main difficulties of programming these distributed wireless devices is that it is often debugging communications. The current development release of the Sun SPOT Java Development Kit includes a new Solarium view called RadioVIew. It shows the current state of the network of Sun SPOT devices.

200904151933.jpg

The RadioView is built into Solarium and allows you to see the topology of your radio-space. What deivces can "hear" each other and what ones can't. This can help debug some of those nasty mutihop radio bugs and aid you in tracking down dead points in your radio space. Additionally, you can get information about each node and each link from the view to see what the signal strength is like for any particular link.

200904151934.jpg

How to try it out for yourself

Since our development is completely open, you can take a sneak peek at some of these features now as we develop them. First make sure that you have installed the Sun SPOT Java Development Kit. In the SPOT Manager Tool, select "Dev Preview" under the "Preferences" tab of SPOT Manager.

DevPreview.jpg

Once you have selected "Dev Preview" return to the SDK tab and you will find all the developer releases under "Available SDKs." Install the latest red-XXXXXX release. Then upgrade your Sun SPOT devices. Remember, its an unstable developer build, but any time you run into too many bugs you can always revert back to your previous version of the SDK.

SDKChooser.jpg

Once you are up and running, open Solarium and create a RadioView under the "Views" menu. It takes a few seconds for it to interrogate the network, but once you see your devices there, click around to get information about what is happening.

Also, don't forget that, as with any other Solarium view, Radio View can be "docked" to other Solarium views or made into an independent window simply by clicking and dragging on the area just below the titlebar of the window.

Enjoy!

There is more than just a new look at SunSPOTWorld.com these days. There is new content as well!


In case you haven't been following along, we have been releasing fairly regular "Developer Builds" of the Sun SPOT SDK. The upcoming release, code-named 'Red' is available to all as a Developer Preview Release, and it has a lot of cool new features (which I'm not going to go into here. A different blog post for that, I hope). What is in this release (the latest one anyway) is a Robot Simulator.


Let me say that again: A Robot Simulator. You can now, without physical Sun SPOTs, do a complete robot program to control an iRobot Create! Check out the simulator running a maze program:




Solarium001.png

And you can program the simulated Create in Java, deploy your program to a virtual Sun SPOT in Solarium, and then run it in the Robot Simulator. I have to say, this is one of the coolest things to come along in a while. Seriously.


You can work through the new Tutorial on using the Robot Simulator, or work through the standard Sun SPOT Tutorial, or just go straight to downloading the Sun SPOT SDK and playing with it! I recommend at least working through the standard Tutorial to start with, and reading the Getting Started documentation as well (does anyone really read the docs?).


This is just some of the new content we have developed and are developing to go along with the new release of Sun SPOT SDK.


Note: In order to access the Developer Preview Builds of the Sun SPOT SDK, you will first need to install the standard Blue release (if this is your first foray into the Sun SPOT SDK), and then go to the Preferences Panel of the Sun SPOT Manager and select "Dev Preview." Then return to the SDK tab and install the latest Red release.


Oh, and don't forget to visit the forums for tips, support, ideas, and just to see what other Sun SPOT Developers are up to!


[ Now and then an innocent person is sent to the legislature. ]

Here's another contest for anyone 17 or under who uses open source technology (like the Sun SPOTs hint hint). The contest, called Digital Open, is sponsored by the Institute for the Future, Boing Boing and Sun Microsystems.

Submissions are being accepted for anyone under 18 who creates open projects in the following categories:

It seems to me that Sun SPOT projects would fit in multiple categories!! Have fun!

Take a look at Digital Open. If you don't qualify, pass the info on to a nearby teenagers who would.

The upcoming release of Sun SPOT software (Red v5.0) will feature a new robotics simulator built into to Solarium. It allows you to use virtual Sun SPOTs to program virtual robots using the Sun SPOT Java Development Kit. The great thing about it is you can download it and use it for free right now! Even if you don't have Sun SPOTs.


200904151541.jpg

The simulation roughly mimics the environment that is used in a competition held in San Diego each year called IARoc sponsored by Wintriss School. In this competition students use Sun SPOTs to control and iRobot Create to navigate a maze. The Create is very much like its more popular sibling, the Roomba except it doesn't suck... by which I mean it has no vacuum... (Roombas are great, BTW) The combination of a Create and a Sun SPOT is fantastic for learning the basics of robot motion and navigation. In the simulation you write software for a Sun SPOT, and then in Solarium, create a new Robot View and Add a Robot. This will create a Robot/VIrtual Sun SPOT combination. Back on the Grid View, you will now see a Virtual SPOT. You deploy and run your software on this Virtual SPOT and it will control your simulated robot. You have your choice of three different environments to run in; an empty room, a maze, or an obstacle course. Each view includes an 'X' as a starting point and a 'O' as an ending point. Your robot includes sensors that allow you to sense when your robot is over one of these marks. We also provide a sample application that can find the center of the room, or if you poke around in the code a little, you'll see a simple wall follower. Its just enough to get you started.

Its great fun! ...and you can get as fancy as you like. For instance, you can create multiple robots that will interact in a single view. These robots can interact with each other. They can run the same code or different code... whatever you like. Of course, they can also use the the Sun SPOT radio communication library to communicate with each other. This way your robots can cooperate to carry out a task.

And of course, if it doesn't do what you like, remember you have all the source code to the entire system, so you can make it do exactly what you would like. Give it a try!

To use the simulator follow the instructions here.


200904230648.jpg

200904161750.jpg

We on Project Sun SPOT are on a mission to drive the adoption of Java on small devices everywhere. We believe that with Sun SPOT devices we can help speed the onset of the Internet of Things by providing smart, connected devices and tools to developers, students, researchers and hobbyists. That is why we are so excited this week to announce the port of the Sun SPOT platform to the FIRST Robotics hardware (the National Instruments Compact RIO) and thereby making our software environment available to some of the best and brightest high school students. Yet another step toward Java everywhere. ...and this is a big step

_MG_1071.jpg  

The FIRST Robotics Competition is a truly inspired event originally created by Dean Kamen the inventor of the Segway. Each year tens of thousands of high school students compete with each other in head to head robotics competition. Its really a great event. I've attended the regional competition in San Jose several times now and what I've seen is nothing short of amazing. First, the robots are impressive. Its really hard to believe that high school students are able to put together such fantastic devices. These robots are usually quite large and heavy (the size of several of the students combined). They are designed to fulfill some interesting task such as retrieving large balls (several feet in diameter) and lifting them into a hoop six feet high. The teams go head-to-head with multiple robots on the playing field at one time. This year the challenge has the teams collecting some balls and trying to place them in a trailer that the opposing team is pulling behind their robot, all the time protecting their own trailer. This leads to the second amazing thing about the FIRST competition. It is great entertainment. At the regional championships in San Jose, I am just one of thousands of spectators watching the event. There is music and cheerleading and mascots and a general party atmosphere. It nearly brought a tear to my eye when I saw thousands of screaming fans watching a bunch of (and I use the term affectionately) nerds out there doing their thing. The competition is fierce with often intense rivalries between schools. While the spirit of competition is strong on the playing field, what is truly amazing is the attitude in the pits. As a walked around looking at the amazing creations, I often overheard competitors helping each other out, lending tools, advice, muscle power and general moral support. These kids get the idea that the most satisfying competition is the one where everyone is performing at their best. On the field they are competitors but in the pits they are on a mission together to change the world. These are our future technical leaders.

_MG_1052.jpg

Right now I'm in Atlanta to attend the national competition at the GeorgiaDome... The freakin' GreorgiaDome! the same thing on an even larger scale. Its all very impressive. Its clearly a life experience for the kids lucky enough to compete here. Over the last couple of days I've gotten to meet some of these competitors. They are only in high school, but this competition has already given them real-world war stories that come from trying to make something complex come together under an aggressive time schedule. This is great, real-world experience.

_MG_1021.jpg

Where does Sun SPOT fit in?

The folks at Worchester Polytechnic Institute (WPI), with some help from us, are porting the Squawk virtual Machine that underlies the Sun SPOT software stack to the controller used for this competition. This means that in next years competition the students will have the opportunity to program their robots in Java. It also mea

ns they'll have access to some of the libraries and features that we've built in Sun SPOTs. It seems that last year they did a survey of the students, and a huge percentage of them asked for Java. This makes sense since Java is the language they are tested on in their advanced placement exams. It just so happens that we have a great little open source Java environment at the core of the Sun SPOT system called the Squawk Virtual Machine. So last year, several students from WPI joined us for the summer to try porting the Squawk VM to the Compact RIO. It worked well. Now we are well on the way to making the platform available to the students so that they will have new tools to do their magic. You can learn more about FIRST and the Sun SPOT Java software here and you can get some background on the port itself here.

Welcome FIRST competitors to our little corner or the world of builders and makers. May you take your skills and competitive spirit and save the world for all of us!


_MG_1100.jpg _MG_1085.jpg _MG_1083.jpg  _MG_1081.jpg _MG_1109.jpg _MG_1113.jpg

The old SunSPOTWorld.com website, with its Brady Bunch Navigation is a thing of the past! It looked like this:





spotworld.png


And we've officially gone green! Check out the new layout and navigation and content and everything!





SPOTWorld2.png


Let me know what you think of the new site!






[ Next to being shot at and missed, nothing is really quite as satisfying as an income tax refund.


   -- F. J. Raymond ]

Anyone out there want a chance to show off their Sun SPOT skills and win a trip to Zurich?

Jazoon09 is a Java technology event held in Zurich, Switzerland, June 22 to 25, 2009.   Jazoon Rookie is a contest associated with Jazoon09 that lets bright young developers (must be age 26 or younger on June 22, 2009) win a free trip to the conference. Entrants make a <20 minute video of a presentation their work (a technical talk of your choosing). If your video is one of three chosen, you will be eligible to present your talk live in Switzerland at Jazoon09 (conference and travel fees paid). There isn't much time. To enter you need to submit a video by April 23... so get working now. I'd love to see some innovative Sun SPOT talks among the entries.

Don't forget that if you make a video of your talk or anything else that relates to Sun SPOTs, tag it with the keyword "spaughts" so that it will show up in our feeds (http://planets.sun.com/SunSPOT/group/SunSPOT/).

Thanks to Gary Serda for the tip on Jazoon.