initial commit of content from Adam Dai and Tony Oliverio zumo-b00
authordave russo <d-russo@ti.com>
Mon, 28 Sep 2015 03:46:12 +0000 (20:46 -0700)
committerdave russo <d-russo@ti.com>
Mon, 28 Sep 2015 03:46:12 +0000 (20:46 -0700)
96 files changed:
.gitignore [new file with mode: 0644]
README.html [new file with mode: 0644]
etc/Markdown.pl [new file with mode: 0644]
etc/build.svg [new file with mode: 0644]
etc/github-markdown.css [new file with mode: 0644]
etc/md2html.ksh [new file with mode: 0644]
license.html [new file with mode: 0644]
makefile [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/ZumoCC3200.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/AssistedDrive.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/MotorControlLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/SensorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/WifiLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/AttitudeDisplay.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Balancing/Balancing.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Balancing/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Balancing/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Balancing/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/CommandExample/CommandExample.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/CommandExample/MotorControlLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/CommandExample/SensorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/CommandExample/WifiLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/ManualDrive/ManualDrive.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/ManualDrive/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/ManualDrive/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/ManualDrive/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/MasterSlave/MasterSlave.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/MasterSlave/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/MasterSlave/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/MasterSlave/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Multicast/MCP.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Multicast/MCP.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Multicast/Multicast.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Multicast/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Multicast/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/Multicast/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/SplineDrive/MotionPlanner.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/SplineDrive/MotionPlanner.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/SplineDrive/SplineDrive.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/SplineDrive/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/SplineDrive/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/SplineDrive/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/UDPBroadcast.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPExample/UDPExample.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPExample/apLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPExample/imuLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/examples/UDPExample/motorLoop.ino [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Balancer.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Balancer.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Command.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/CommandManager.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/CommandManager.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/DCM.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/DCM.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/DriveLineCommand.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/DriveLineCommand.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/IMUManager.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/IMUManager.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/L3G.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/L3G.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/LSM303.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/LSM303.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/PIDController.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/PIDController.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Pushbutton.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Pushbutton.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/TurnAngleCommand.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/TurnAngleCommand.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Utilities.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/Utilities.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/WaitCommand.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/WaitCommand.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/ZumoMotors.cpp [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/ZumoMotors.h [new file with mode: 0644]
src/Energia/libraries/ZumoCC3200/utility/spline.h [new file with mode: 0644]
src/Processing/AssistedDrive/AssistedDrive.pde [new file with mode: 0644]
src/Processing/AssistedDrive/GraphClass.pde [new file with mode: 0644]
src/Processing/AttitudeDisplay/AttitudeDisplay.pde [new file with mode: 0644]
src/Processing/Balancing/Balancing.pde [new file with mode: 0644]
src/Processing/Balancing/GraphClass.pde [new file with mode: 0644]
src/Processing/ManualDrive/zecho/zecho.pde [new file with mode: 0644]
src/Processing/ManualDrive/zgraph/GraphClass.pde [new file with mode: 0644]
src/Processing/ManualDrive/zgraph/zgraph.pde [new file with mode: 0644]
src/Processing/MasterSlave/GraphClass.pde [new file with mode: 0644]
src/Processing/MasterSlave/MasterSlave.pde [new file with mode: 0644]
src/Processing/SplineDrive/SplineDrive.pde [new file with mode: 0644]
src/Processing/UDPBroadcast/GraphClass.pde [new file with mode: 0644]
src/Processing/UDPBroadcast/UDPBroadcast.pde [new file with mode: 0644]
src/Processing/UDPExample/GraphClass.pde [new file with mode: 0644]
src/Processing/UDPExample/UDPExample.pde [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..7da50c9
--- /dev/null
@@ -0,0 +1,71 @@
+## Build artifacts in the tree root directory
+/Make.log
+/makelog
+/exports/
+/imports/
+/.tools-*
+/.gnu-*
+/.exports
+/.imports
+/.imports.xml
+/.reltree
+/.generated_files
+/bin/
+
+## emacs backup files
+.#*
+.,*
+*~
+
+## RTSC package generated files & build goals
+package.mak
+package/
+java/
+.xdcenv.mak
+.javadoc
+doc-files
+.dlls
+.docs
+.executables
+.interfaces
+.libraries
+.libraries,*
+
+## Legacy build goals
+.src-files
+.include-files
+.etc-files
+.lib-files
+.bin-files,*
+.test-files
+
+## Generated package archives
+*.zip
+*.gz
+*.tar
+*.jar
+
+## Generated executables, libraries, objects, ...
+*.x86U
+*.x86_64M
+*.xv7A
+*.vcproj
+*.xm4fg
+*.xem4f
+*.xm4g
+*.xem4
+*.xem3
+*.xm3g
+
+*.exe
+*.bin
+*.out
+*.obj
+*.lib
+
+## CCS/Eclipse/RTSC generated project files
+.config/
+.settings/
+.xdchelp
+.launches/
+Debug/
diff --git a/README.html b/README.html
new file mode 100644 (file)
index 0000000..8d9e8fc
--- /dev/null
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Zumo CC3200 README.md</title>
+    <link rel="stylesheet" type="text/css" href="etc/github-markdown.css">
+    <style>
+      .markdown-body {
+         min-width: 200px;
+         max-width: 790px;
+         margin: 0 auto;
+         padding: 30px;
+      }
+    </style>
+  </head>
+  <body ><article class="markdown-body">
+<p><a href="https://git.ti.com/zumo-cc3200/zumo-cc3200/archive-tarball/master"><img src="etc/build.svg" alt="download" title="" /></a></p>
+
+<h1><a href="http://processors.wiki.ti.com/index.php/ZumoCC3200#The_Zumo_CC3200" title="ZumoCC3200 Home Page">Zumo CC3200</a></h1>
+
+<p>The Zumo CC3200 project was created to enable makers to easily create
+applications that leverage the power of a <a href="http://www.ti.com/product/cc3200">TI CC3200</a> connected to a
+motorized hardware platform equipped with Inertial Measurement (IMU) 
+sensors.</p>
+
+<p>This repository provides an Energia library, ZumoCC3200, that provides core
+functions that make it possible to create applications with very little code
+and without requiring in-depth knowledge of the CC3200 or the specific IMU
+sensors used to enable closed-loop motor control.  As with most libraries, 
+ZumoCC3200 includes numerous examples that serve as starting points for
+new projects.</p>
+
+<p>Each of the ZumoCC3200 examples consists of two programs: an Energia program
+that runs on the Zumo CC3200 robot and a <a href="https:/processing.org" title="Processing Home Page">Processing</a>
+sketch that runs on your laptop. These two programs communicate with one
+another via WiFi, enabling you to view telemetry data and control the bot
+from your laptop.</p>
+
+<h2><a href="https://gitorious.design.ti.com/sb/zumo/trees/master/src/Energia/libraries" title="ZumoCC3200 Energia Library Sources">The ZumoCC3200 Library</a></h2>
+
+<p>To use the ZumoCC3200 library, </p>
+
+<ul>
+<li><a href="https://git.ti.com/zumo-cc3200/zumo-cc3200/archive-tarball/master" title="ZumoCC3200 Repo Download">Download the ZumoCC3200 library</a>, </li>
+<li><a href="http://processors.wiki.ti.com/index.php/ZumoCC3200Demos#Install_the_Energia_ZumoCC3200_Library" title="ZumoCC3200 Energia Library Install Instructions">Install the ZumoCC3200 library</a> into your Energia
+sketchbook, and</li>
+<li><a href="http://processors.wiki.ti.com/index.php/ZumoCC3200Demos#Build_and_Upload_a_Zumo_CC3200_Example" title="ZumoCC3200 Energia Build Instructions">Build and run an example</a></li>
+</ul>
+
+<p>Like most Energia/Arduino libraries, the ZumoCC3200 library includes serveral
+examples that serve as simple starting points for your project.</p>
+
+<h2><a href="https://gitorious.design.ti.com/sb/zumo/trees/master/src/Processing" title="ZumoCC3200 Processing Sketch Sources">Processing Sketches</a></h2>
+
+<p>The Processing sketches that communicate with the ZumoCC3200 bot are also 
+<a href="https://gitorious.design.ti.com/sb/zumo/trees/master/src/Processing" title="ZumoCC3200 Processing Sketch Sources">included in this repo</a>.  Note that some of these sketches
+rely on freely available Contributed libraries that must be installed.  </p>
+
+<p>Prerequisite libraries and installation instructions for these sketches is
+<a href="http://processors.wiki.ti.com/index.php/ZumoCC3200Demos#Install_Processing_Libraries" title="Processing Library Install Instructions">here</a>.</p>
+
+<h2><a href="license.html">License</a></h2>
+
+<p>All of the files in this repo are Open Source and most are licensed under 
+either a BSD 3-clause or MIT license.  However, one file does come with a
+GPL-2.0 license.  </p>
+
+<p>Complete license details are provided <a href="license.html">here</a>.</p>
+
+<h2>Contributing</h2>
+
+<p><a href="http://processors.wiki.ti.com/index.php/ZumoCC3200#The_Zumo_CC3200" title="ZumoCC3200 Home Page">Zumo CC3200</a> is a collaborative project originally created as a part of the TI Santa Barbara 2015 Summer Intern program by <a href="http://processors.wiki.ti.com/index.php/ZumoCC3200#Credits" title="Interns">Adam Dai and Tony Oliverio</a>, and you are invited to help. </p>
+
+<p>In-depth details about contributing code, bug fixes, and documentation are
+forthcoming.  </p>
+  </article></body>
+</html>
diff --git a/etc/Markdown.pl b/etc/Markdown.pl
new file mode 100644 (file)
index 0000000..e4c8469
--- /dev/null
@@ -0,0 +1,1450 @@
+#!/usr/bin/perl
+
+#
+# Markdown -- A text-to-HTML conversion tool for web writers
+#
+# Copyright (c) 2004 John Gruber
+# <http://daringfireball.net/projects/markdown/>
+#
+
+
+package Markdown;
+require 5.006_000;
+use strict;
+use warnings;
+
+use Digest::MD5 qw(md5_hex);
+use vars qw($VERSION);
+$VERSION = '1.0.1';
+# Tue 14 Dec 2004
+
+## Disabled; causes problems under Perl 5.6.1:
+# use utf8;
+# binmode( STDOUT, ":utf8" );  # c.f.: http://acis.openlib.org/dev/perl-unicode-struggle.html
+
+
+#
+# Global default settings:
+#
+my $g_empty_element_suffix = " />";     # Change to ">" for HTML output
+my $g_tab_width = 4;
+
+
+#
+# Globals:
+#
+
+# Regex to match balanced [brackets]. See Friedl's
+# "Mastering Regular Expressions", 2nd Ed., pp. 328-331.
+my $g_nested_brackets;
+$g_nested_brackets = qr{
+       (?>                                                             # Atomic matching
+          [^\[\]]+                                                     # Anything other than brackets
+        | 
+          \[
+                (??{ $g_nested_brackets })             # Recursive set of nested brackets
+          \]
+       )*
+}x;
+
+
+# Table of hash values for escaped characters:
+my %g_escape_table;
+foreach my $char (split //, '\\`*_{}[]()>#+-.!') {
+       $g_escape_table{$char} = md5_hex($char);
+}
+
+
+# Global hashes, used by various utility routines
+my %g_urls;
+my %g_titles;
+my %g_html_blocks;
+
+# Used to track when we're inside an ordered or unordered list
+# (see _ProcessListItems() for details):
+my $g_list_level = 0;
+
+
+#### Blosxom plug-in interface ##########################################
+
+# Set $g_blosxom_use_meta to 1 to use Blosxom's meta plug-in to determine
+# which posts Markdown should process, using a "meta-markup: markdown"
+# header. If it's set to 0 (the default), Markdown will process all
+# entries.
+my $g_blosxom_use_meta = 0;
+
+sub start { 1; }
+sub story {
+       my($pkg, $path, $filename, $story_ref, $title_ref, $body_ref) = @_;
+
+       if ( (! $g_blosxom_use_meta) or
+            (defined($meta::markup) and ($meta::markup =~ /^\s*markdown\s*$/i))
+            ){
+                       $$body_ref  = Markdown($$body_ref);
+     }
+     1;
+}
+
+
+#### Movable Type plug-in interface #####################################
+eval {require MT};  # Test to see if we're running in MT.
+unless ($@) {
+    require MT;
+    import  MT;
+    require MT::Template::Context;
+    import  MT::Template::Context;
+
+       eval {require MT::Plugin};  # Test to see if we're running >= MT 3.0.
+       unless ($@) {
+               require MT::Plugin;
+               import  MT::Plugin;
+               my $plugin = new MT::Plugin({
+                       name => "Markdown",
+                       description => "A plain-text-to-HTML formatting plugin. (Version: $VERSION)",
+                       doc_link => 'http://daringfireball.net/projects/markdown/'
+               });
+               MT->add_plugin( $plugin );
+       }
+
+       MT::Template::Context->add_container_tag(MarkdownOptions => sub {
+               my $ctx  = shift;
+               my $args = shift;
+               my $builder = $ctx->stash('builder');
+               my $tokens = $ctx->stash('tokens');
+
+               if (defined ($args->{'output'}) ) {
+                       $ctx->stash('markdown_output', lc $args->{'output'});
+               }
+
+               defined (my $str = $builder->build($ctx, $tokens) )
+                       or return $ctx->error($builder->errstr);
+               $str;           # return value
+       });
+
+       MT->add_text_filter('markdown' => {
+               label     => 'Markdown',
+               docs      => 'http://daringfireball.net/projects/markdown/',
+               on_format => sub {
+                       my $text = shift;
+                       my $ctx  = shift;
+                       my $raw  = 0;
+                   if (defined $ctx) {
+                       my $output = $ctx->stash('markdown_output'); 
+                               if (defined $output  &&  $output =~ m/^html/i) {
+                                       $g_empty_element_suffix = ">";
+                                       $ctx->stash('markdown_output', '');
+                               }
+                               elsif (defined $output  &&  $output eq 'raw') {
+                                       $raw = 1;
+                                       $ctx->stash('markdown_output', '');
+                               }
+                               else {
+                                       $raw = 0;
+                                       $g_empty_element_suffix = " />";
+                               }
+                       }
+                       $text = $raw ? $text : Markdown($text);
+                       $text;
+               },
+       });
+
+       # If SmartyPants is loaded, add a combo Markdown/SmartyPants text filter:
+       my $smartypants;
+
+       {
+               no warnings "once";
+               $smartypants = $MT::Template::Context::Global_filters{'smarty_pants'};
+       }
+
+       if ($smartypants) {
+               MT->add_text_filter('markdown_with_smartypants' => {
+                       label     => 'Markdown With SmartyPants',
+                       docs      => 'http://daringfireball.net/projects/markdown/',
+                       on_format => sub {
+                               my $text = shift;
+                               my $ctx  = shift;
+                               if (defined $ctx) {
+                                       my $output = $ctx->stash('markdown_output'); 
+                                       if (defined $output  &&  $output eq 'html') {
+                                               $g_empty_element_suffix = ">";
+                                       }
+                                       else {
+                                               $g_empty_element_suffix = " />";
+                                       }
+                               }
+                               $text = Markdown($text);
+                               $text = $smartypants->($text, '1');
+                       },
+               });
+       }
+}
+else {
+#### BBEdit/command-line text filter interface ##########################
+# Needs to be hidden from MT (and Blosxom when running in static mode).
+
+    # We're only using $blosxom::version once; tell Perl not to warn us:
+       no warnings 'once';
+    unless ( defined($blosxom::version) ) {
+               use warnings;
+
+               #### Check for command-line switches: #################
+               my %cli_opts;
+               use Getopt::Long;
+               Getopt::Long::Configure('pass_through');
+               GetOptions(\%cli_opts,
+                       'version',
+                       'shortversion',
+                       'html4tags',
+               );
+               if ($cli_opts{'version'}) {             # Version info
+                       print "\nThis is Markdown, version $VERSION.\n";
+                       print "Copyright 2004 John Gruber\n";
+                       print "http://daringfireball.net/projects/markdown/\n\n";
+                       exit 0;
+               }
+               if ($cli_opts{'shortversion'}) {                # Just the version number string.
+                       print $VERSION;
+                       exit 0;
+               }
+               if ($cli_opts{'html4tags'}) {                   # Use HTML tag style instead of XHTML
+                       $g_empty_element_suffix = ">";
+               }
+
+
+               #### Process incoming text: ###########################
+               my $text;
+               {
+                       local $/;               # Slurp the whole file
+                       $text = <>;
+               }
+        print Markdown($text);
+    }
+}
+
+
+
+sub Markdown {
+#
+# Main function. The order in which other subs are called here is
+# essential. Link and image substitutions need to happen before
+# _EscapeSpecialChars(), so that any *'s or _'s in the <a>
+# and <img> tags get encoded.
+#
+       my $text = shift;
+
+       # Clear the global hashes. If we don't clear these, you get conflicts
+       # from other articles when generating a page which contains more than
+       # one article (e.g. an index page that shows the N most recent
+       # articles):
+       %g_urls = ();
+       %g_titles = ();
+       %g_html_blocks = ();
+
+
+       # Standardize line endings:
+       $text =~ s{\r\n}{\n}g;  # DOS to Unix
+       $text =~ s{\r}{\n}g;    # Mac to Unix
+
+       # Make sure $text ends with a couple of newlines:
+       $text .= "\n\n";
+
+       # Convert all tabs to spaces.
+       $text = _Detab($text);
+
+       # Strip any lines consisting only of spaces and tabs.
+       # This makes subsequent regexen easier to write, because we can
+       # match consecutive blank lines with /\n+/ instead of something
+       # contorted like /[ \t]*\n+/ .
+       $text =~ s/^[ \t]+$//mg;
+
+       # Turn block-level HTML blocks into hash entries
+       $text = _HashHTMLBlocks($text);
+
+       # Strip link definitions, store in hashes.
+       $text = _StripLinkDefinitions($text);
+
+       $text = _RunBlockGamut($text);
+
+       $text = _UnescapeSpecialChars($text);
+
+       return $text . "\n";
+}
+
+
+sub _StripLinkDefinitions {
+#
+# Strips link definitions from text, stores the URLs and titles in
+# hash references.
+#
+       my $text = shift;
+       my $less_than_tab = $g_tab_width - 1;
+
+       # Link defs are in the form: ^[id]: url "optional title"
+       while ($text =~ s{
+                                               ^[ ]{0,$less_than_tab}\[(.+)\]: # id = $1
+                                                 [ \t]*
+                                                 \n?                           # maybe *one* newline
+                                                 [ \t]*
+                                               <?(\S+?)>?                      # url = $2
+                                                 [ \t]*
+                                                 \n?                           # maybe one newline
+                                                 [ \t]*
+                                               (?:
+                                                       (?<=\s)                 # lookbehind for whitespace
+                                                       ["(]
+                                                       (.+?)                   # title = $3
+                                                       [")]
+                                                       [ \t]*
+                                               )?      # title is optional
+                                               (?:\n+|\Z)
+                                       }
+                                       {}mx) {
+               $g_urls{lc $1} = _EncodeAmpsAndAngles( $2 );    # Link IDs are case-insensitive
+               if ($3) {
+                       $g_titles{lc $1} = $3;
+                       $g_titles{lc $1} =~ s/"/&quot;/g;
+               }
+       }
+
+       return $text;
+}
+
+
+sub _HashHTMLBlocks {
+       my $text = shift;
+       my $less_than_tab = $g_tab_width - 1;
+
+       # Hashify HTML blocks:
+       # We only want to do this for block-level HTML tags, such as headers,
+       # lists, and tables. That's because we still want to wrap <p>s around
+       # "paragraphs" that are wrapped in non-block-level tags, such as anchors,
+       # phrase emphasis, and spans. The list of tags we're looking for is
+       # hard-coded:
+       my $block_tags_a = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del/;
+       my $block_tags_b = qr/p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math/;
+
+       # First, look for nested blocks, e.g.:
+       #       <div>
+       #               <div>
+       #               tags for inner block must be indented.
+       #               </div>
+       #       </div>
+       #
+       # The outermost tags must start at the left margin for this to match, and
+       # the inner nested divs must be indented.
+       # We need to do this before the next, more liberal match, because the next
+       # match will start at the first `<div>` and stop at the first `</div>`.
+       $text =~ s{
+                               (                                               # save in $1
+                                       ^                                       # start of line  (with /m)
+                                       <($block_tags_a)        # start tag = $2
+                                       \b                                      # word break
+                                       (.*\n)*?                        # any number of lines, minimally matching
+                                       </\2>                           # the matching end tag
+                                       [ \t]*                          # trailing spaces/tabs
+                                       (?=\n+|\Z)      # followed by a newline or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egmx;
+
+
+       #
+       # Now match more liberally, simply from `\n<tag>` to `</tag>\n`
+       #
+       $text =~ s{
+                               (                                               # save in $1
+                                       ^                                       # start of line  (with /m)
+                                       <($block_tags_b)        # start tag = $2
+                                       \b                                      # word break
+                                       (.*\n)*?                        # any number of lines, minimally matching
+                                       .*</\2>                         # the matching end tag
+                                       [ \t]*                          # trailing spaces/tabs
+                                       (?=\n+|\Z)      # followed by a newline or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egmx;
+       # Special case just for <hr />. It was easier to make a special case than
+       # to make the other regex more complicated.     
+       $text =~ s{
+                               (?:
+                                       (?<=\n\n)               # Starting after a blank line
+                                       |                               # or
+                                       \A\n?                   # the beginning of the doc
+                               )
+                               (                                               # save in $1
+                                       [ ]{0,$less_than_tab}
+                                       <(hr)                           # start tag = $2
+                                       \b                                      # word break
+                                       ([^<>])*?                       # 
+                                       /?>                                     # the matching end tag
+                                       [ \t]*
+                                       (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egx;
+
+       # Special case for standalone HTML comments:
+       $text =~ s{
+                               (?:
+                                       (?<=\n\n)               # Starting after a blank line
+                                       |                               # or
+                                       \A\n?                   # the beginning of the doc
+                               )
+                               (                                               # save in $1
+                                       [ ]{0,$less_than_tab}
+                                       (?s:
+                                               <!
+                                               (--.*?--\s*)+
+                                               >
+                                       )
+                                       [ \t]*
+                                       (?=\n{2,}|\Z)           # followed by a blank line or end of document
+                               )
+                       }{
+                               my $key = md5_hex($1);
+                               $g_html_blocks{$key} = $1;
+                               "\n\n" . $key . "\n\n";
+                       }egx;
+
+
+       return $text;
+}
+
+
+sub _RunBlockGamut {
+#
+# These are all the transformations that form block-level
+# tags like paragraphs, headers, and list items.
+#
+       my $text = shift;
+
+       $text = _DoHeaders($text);
+
+       # Do Horizontal Rules:
+       $text =~ s{^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
+       $text =~ s{^[ ]{0,2}([ ]? -[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
+       $text =~ s{^[ ]{0,2}([ ]? _[ ]?){3,}[ \t]*$}{\n<hr$g_empty_element_suffix\n}gmx;
+
+       $text = _DoLists($text);
+
+       $text = _DoCodeBlocks($text);
+
+       $text = _DoBlockQuotes($text);
+
+       # We already ran _HashHTMLBlocks() before, in Markdown(), but that
+       # was to escape raw HTML in the original Markdown source. This time,
+       # we're escaping the markup we've just created, so that we don't wrap
+       # <p> tags around block-level tags.
+       $text = _HashHTMLBlocks($text);
+
+       $text = _FormParagraphs($text);
+
+       return $text;
+}
+
+
+sub _RunSpanGamut {
+#
+# These are all the transformations that occur *within* block-level
+# tags like paragraphs, headers, and list items.
+#
+       my $text = shift;
+
+       $text = _DoCodeSpans($text);
+
+       $text = _EscapeSpecialChars($text);
+
+       # Process anchor and image tags. Images must come first,
+       # because ![foo][f] looks like an anchor.
+       $text = _DoImages($text);
+       $text = _DoAnchors($text);
+
+       # Make links out of things like `<http://example.com/>`
+       # Must come after _DoAnchors(), because you can use < and >
+       # delimiters in inline links like [this](<url>).
+       $text = _DoAutoLinks($text);
+
+       $text = _EncodeAmpsAndAngles($text);
+
+       $text = _DoItalicsAndBold($text);
+
+       # Do hard breaks:
+       $text =~ s/ {2,}\n/ <br$g_empty_element_suffix\n/g;
+
+       return $text;
+}
+
+
+sub _EscapeSpecialChars {
+       my $text = shift;
+       my $tokens ||= _TokenizeHTML($text);
+
+       $text = '';   # rebuild $text from the tokens
+#      my $in_pre = 0;  # Keep track of when we're inside <pre> or <code> tags.
+#      my $tags_to_skip = qr!<(/?)(?:pre|code|kbd|script|math)[\s>]!;
+
+       foreach my $cur_token (@$tokens) {
+               if ($cur_token->[0] eq "tag") {
+                       # Within tags, encode * and _ so they don't conflict
+                       # with their use in Markdown for italics and strong.
+                       # We're replacing each such character with its
+                       # corresponding MD5 checksum value; this is likely
+                       # overkill, but it should prevent us from colliding
+                       # with the escape values by accident.
+                       $cur_token->[1] =~  s! \* !$g_escape_table{'*'}!gx;
+                       $cur_token->[1] =~  s! _  !$g_escape_table{'_'}!gx;
+                       $text .= $cur_token->[1];
+               } else {
+                       my $t = $cur_token->[1];
+                       $t = _EncodeBackslashEscapes($t);
+                       $text .= $t;
+               }
+       }
+       return $text;
+}
+
+
+sub _DoAnchors {
+#
+# Turn Markdown link shortcuts into XHTML <a> tags.
+#
+       my $text = shift;
+
+       #
+       # First, handle reference-style links: [link text] [id]
+       #
+       $text =~ s{
+               (                                       # wrap whole match in $1
+                 \[
+                   ($g_nested_brackets)        # link text = $2
+                 \]
+
+                 [ ]?                          # one optional space
+                 (?:\n[ ]*)?           # one optional newline followed by spaces
+
+                 \[
+                   (.*?)               # id = $3
+                 \]
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $link_text   = $2;
+               my $link_id     = lc $3;
+
+               if ($link_id eq "") {
+                       $link_id = lc $link_text;     # for shortcut links like [this][].
+               }
+
+               if (defined $g_urls{$link_id}) {
+                       my $url = $g_urls{$link_id};
+                       $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+                       $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+                       $result = "<a href=\"$url\"";
+                       if ( defined $g_titles{$link_id} ) {
+                               my $title = $g_titles{$link_id};
+                               $title =~ s! \* !$g_escape_table{'*'}!gx;
+                               $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                               $result .=  " title=\"$title\"";
+                       }
+                       $result .= ">$link_text</a>";
+               }
+               else {
+                       $result = $whole_match;
+               }
+               $result;
+       }xsge;
+
+       #
+       # Next, inline-style links: [link text](url "optional title")
+       #
+       $text =~ s{
+               (                               # wrap whole match in $1
+                 \[
+                   ($g_nested_brackets)        # link text = $2
+                 \]
+                 \(                    # literal paren
+                       [ \t]*
+                       <?(.*?)>?       # href = $3
+                       [ \t]*
+                       (                       # $4
+                         (['"])        # quote char = $5
+                         (.*?)         # Title = $6
+                         \5            # matching quote
+                       )?                      # title is optional
+                 \)
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $link_text   = $2;
+               my $url                 = $3;
+               my $title               = $6;
+
+               $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+               $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+               $result = "<a href=\"$url\"";
+
+               if (defined $title) {
+                       $title =~ s/"/&quot;/g;
+                       $title =~ s! \* !$g_escape_table{'*'}!gx;
+                       $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                       $result .=  " title=\"$title\"";
+               }
+
+               $result .= ">$link_text</a>";
+
+               $result;
+       }xsge;
+
+       return $text;
+}
+
+
+sub _DoImages {
+#
+# Turn Markdown image shortcuts into <img> tags.
+#
+       my $text = shift;
+
+       #
+       # First, handle reference-style labeled images: ![alt text][id]
+       #
+       $text =~ s{
+               (                               # wrap whole match in $1
+                 !\[
+                   (.*?)               # alt text = $2
+                 \]
+
+                 [ ]?                          # one optional space
+                 (?:\n[ ]*)?           # one optional newline followed by spaces
+
+                 \[
+                   (.*?)               # id = $3
+                 \]
+
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $alt_text    = $2;
+               my $link_id     = lc $3;
+
+               if ($link_id eq "") {
+                       $link_id = lc $alt_text;     # for shortcut links like ![this][].
+               }
+
+               $alt_text =~ s/"/&quot;/g;
+               if (defined $g_urls{$link_id}) {
+                       my $url = $g_urls{$link_id};
+                       $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+                       $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+                       $result = "<img src=\"$url\" alt=\"$alt_text\"";
+                       if (defined $g_titles{$link_id}) {
+                               my $title = $g_titles{$link_id};
+                               $title =~ s! \* !$g_escape_table{'*'}!gx;
+                               $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                               $result .=  " title=\"$title\"";
+                       }
+                       $result .= $g_empty_element_suffix;
+               }
+               else {
+                       # If there's no such link ID, leave intact:
+                       $result = $whole_match;
+               }
+
+               $result;
+       }xsge;
+
+       #
+       # Next, handle inline images:  ![alt text](url "optional title")
+       # Don't forget: encode * and _
+
+       $text =~ s{
+               (                               # wrap whole match in $1
+                 !\[
+                   (.*?)               # alt text = $2
+                 \]
+                 \(                    # literal paren
+                       [ \t]*
+                       <?(\S+?)>?      # src url = $3
+                       [ \t]*
+                       (                       # $4
+                         (['"])        # quote char = $5
+                         (.*?)         # title = $6
+                         \5            # matching quote
+                         [ \t]*
+                       )?                      # title is optional
+                 \)
+               )
+       }{
+               my $result;
+               my $whole_match = $1;
+               my $alt_text    = $2;
+               my $url                 = $3;
+               my $title               = '';
+               if (defined($6)) {
+                       $title          = $6;
+               }
+
+               $alt_text =~ s/"/&quot;/g;
+               $title    =~ s/"/&quot;/g;
+               $url =~ s! \* !$g_escape_table{'*'}!gx;         # We've got to encode these to avoid
+               $url =~ s!  _ !$g_escape_table{'_'}!gx;         # conflicting with italics/bold.
+               $result = "<img src=\"$url\" alt=\"$alt_text\"";
+               if (defined $title) {
+                       $title =~ s! \* !$g_escape_table{'*'}!gx;
+                       $title =~ s!  _ !$g_escape_table{'_'}!gx;
+                       $result .=  " title=\"$title\"";
+               }
+               $result .= $g_empty_element_suffix;
+
+               $result;
+       }xsge;
+
+       return $text;
+}
+
+
+sub _DoHeaders {
+       my $text = shift;
+
+       # Setext-style headers:
+       #         Header 1
+       #         ========
+       #  
+       #         Header 2
+       #         --------
+       #
+       $text =~ s{ ^(.+)[ \t]*\n=+[ \t]*\n+ }{
+               "<h1>"  .  _RunSpanGamut($1)  .  "</h1>\n\n";
+       }egmx;
+
+       $text =~ s{ ^(.+)[ \t]*\n-+[ \t]*\n+ }{
+               "<h2>"  .  _RunSpanGamut($1)  .  "</h2>\n\n";
+       }egmx;
+
+
+       # atx-style headers:
+       #       # Header 1
+       #       ## Header 2
+       #       ## Header 2 with closing hashes ##
+       #       ...
+       #       ###### Header 6
+       #
+       $text =~ s{
+                       ^(\#{1,6})      # $1 = string of #'s
+                       [ \t]*
+                       (.+?)           # $2 = Header text
+                       [ \t]*
+                       \#*                     # optional closing #'s (not counted)
+                       \n+
+               }{
+                       my $h_level = length($1);
+                       "<h$h_level>"  .  _RunSpanGamut($2)  .  "</h$h_level>\n\n";
+               }egmx;
+
+       return $text;
+}
+
+
+sub _DoLists {
+#
+# Form HTML ordered (numbered) and unordered (bulleted) lists.
+#
+       my $text = shift;
+       my $less_than_tab = $g_tab_width - 1;
+
+       # Re-usable patterns to match list item bullets and number markers:
+       my $marker_ul  = qr/[*+-]/;
+       my $marker_ol  = qr/\d+[.]/;
+       my $marker_any = qr/(?:$marker_ul|$marker_ol)/;
+
+       # Re-usable pattern to match any entirel ul or ol list:
+       my $whole_list = qr{
+               (                                                               # $1 = whole list
+                 (                                                             # $2
+                       [ ]{0,$less_than_tab}
+                       (${marker_any})                         # $3 = first list item marker
+                       [ \t]+
+                 )
+                 (?s:.+?)
+                 (                                                             # $4
+                         \z
+                       |
+                         \n{2,}
+                         (?=\S)
+                         (?!                                           # Negative lookahead for another list item marker
+                               [ \t]*
+                               ${marker_any}[ \t]+
+                         )
+                 )
+               )
+       }mx;
+
+       # We use a different prefix before nested lists than top-level lists.
+       # See extended comment in _ProcessListItems().
+       #
+       # Note: There's a bit of duplication here. My original implementation
+       # created a scalar regex pattern as the conditional result of the test on
+       # $g_list_level, and then only ran the $text =~ s{...}{...}egmx
+       # substitution once, using the scalar as the pattern. This worked,
+       # everywhere except when running under MT on my hosting account at Pair
+       # Networks. There, this caused all rebuilds to be killed by the reaper (or
+       # perhaps they crashed, but that seems incredibly unlikely given that the
+       # same script on the same server ran fine *except* under MT. I've spent
+       # more time trying to figure out why this is happening than I'd like to
+       # admit. My only guess, backed up by the fact that this workaround works,
+       # is that Perl optimizes the substition when it can figure out that the
+       # pattern will never change, and when this optimization isn't on, we run
+       # afoul of the reaper. Thus, the slightly redundant code to that uses two
+       # static s/// patterns rather than one conditional pattern.
+
+       if ($g_list_level) {
+               $text =~ s{
+                               ^
+                               $whole_list
+                       }{
+                               my $list = $1;
+                               my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
+                               # Turn double returns into triple returns, so that we can make a
+                               # paragraph for the last item in a list, if necessary:
+                               $list =~ s/\n{2,}/\n\n\n/g;
+                               my $result = _ProcessListItems($list, $marker_any);
+                               $result = "<$list_type>\n" . $result . "</$list_type>\n";
+                               $result;
+                       }egmx;
+       }
+       else {
+               $text =~ s{
+                               (?:(?<=\n\n)|\A\n?)
+                               $whole_list
+                       }{
+                               my $list = $1;
+                               my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";
+                               # Turn double returns into triple returns, so that we can make a
+                               # paragraph for the last item in a list, if necessary:
+                               $list =~ s/\n{2,}/\n\n\n/g;
+                               my $result = _ProcessListItems($list, $marker_any);
+                               $result = "<$list_type>\n" . $result . "</$list_type>\n";
+                               $result;
+                       }egmx;
+       }
+
+
+       return $text;
+}
+
+
+sub _ProcessListItems {
+#
+#      Process the contents of a single ordered or unordered list, splitting it
+#      into individual list items.
+#
+
+       my $list_str = shift;
+       my $marker_any = shift;
+
+
+       # The $g_list_level global keeps track of when we're inside a list.
+       # Each time we enter a list, we increment it; when we leave a list,
+       # we decrement. If it's zero, we're not in a list anymore.
+       #
+       # We do this because when we're not inside a list, we want to treat
+       # something like this:
+       #
+       #               I recommend upgrading to version
+       #               8. Oops, now this line is treated
+       #               as a sub-list.
+       #
+       # As a single paragraph, despite the fact that the second line starts
+       # with a digit-period-space sequence.
+       #
+       # Whereas when we're inside a list (or sub-list), that line will be
+       # treated as the start of a sub-list. What a kludge, huh? This is
+       # an aspect of Markdown's syntax that's hard to parse perfectly
+       # without resorting to mind-reading. Perhaps the solution is to
+       # change the syntax rules such that sub-lists must start with a
+       # starting cardinal number; e.g. "1." or "a.".
+
+       $g_list_level++;
+
+       # trim trailing blank lines:
+       $list_str =~ s/\n{2,}\z/\n/;
+
+
+       $list_str =~ s{
+               (\n)?                                                   # leading line = $1
+               (^[ \t]*)                                               # leading whitespace = $2
+               ($marker_any) [ \t]+                    # list marker = $3
+               ((?s:.+?)                                               # list item text   = $4
+               (\n{1,2}))
+               (?= \n* (\z | \2 ($marker_any) [ \t]+))
+       }{
+               my $item = $4;
+               my $leading_line = $1;
+               my $leading_space = $2;
+
+               if ($leading_line or ($item =~ m/\n{2,}/)) {
+                       $item = _RunBlockGamut(_Outdent($item));
+               }
+               else {
+                       # Recursion for sub-lists:
+                       $item = _DoLists(_Outdent($item));
+                       chomp $item;
+                       $item = _RunSpanGamut($item);
+               }
+
+               "<li>" . $item . "</li>\n";
+       }egmx;
+
+       $g_list_level--;
+       return $list_str;
+}
+
+
+
+sub _DoCodeBlocks {
+#
+#      Process Markdown `<pre><code>` blocks.
+#      
+
+       my $text = shift;
+
+       $text =~ s{
+                       (?:\n\n|\A)
+                       (                   # $1 = the code block -- one or more lines, starting with a space/tab
+                         (?:
+                           (?:[ ]{$g_tab_width} | \t)  # Lines must start with a tab or a tab-width of spaces
+                           .*\n+
+                         )+
+                       )
+                       ((?=^[ ]{0,$g_tab_width}\S)|\Z) # Lookahead for non-space at line-start, or end of doc
+               }{
+                       my $codeblock = $1;
+                       my $result; # return value
+
+                       $codeblock = _EncodeCode(_Outdent($codeblock));
+                       $codeblock = _Detab($codeblock);
+                       $codeblock =~ s/\A\n+//; # trim leading newlines
+                       $codeblock =~ s/\s+\z//; # trim trailing whitespace
+
+                       $result = "\n\n<pre><code>" . $codeblock . "\n</code></pre>\n\n";
+
+                       $result;
+               }egmx;
+
+       return $text;
+}
+
+
+sub _DoCodeSpans {
+#
+#      *       Backtick quotes are used for <code></code> spans.
+# 
+#      *       You can use multiple backticks as the delimiters if you want to
+#              include literal backticks in the code span. So, this input:
+#     
+#         Just type ``foo `bar` baz`` at the prompt.
+#     
+#      Will translate to:
+#     
+#         <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
+#     
+#              There's no arbitrary limit to the number of backticks you
+#              can use as delimters. If you need three consecutive backticks
+#              in your code, use four for delimiters, etc.
+#
+#      *       You can use spaces to get literal backticks at the edges:
+#     
+#         ... type `` `bar` `` ...
+#     
+#      Turns to:
+#     
+#         ... type <code>`bar`</code> ...
+#
+
+       my $text = shift;
+
+       $text =~ s@
+                       (`+)            # $1 = Opening run of `
+                       (.+?)           # $2 = The code block
+                       (?<!`)
+                       \1                      # Matching closer
+                       (?!`)
+               @
+                       my $c = "$2";
+                       $c =~ s/^[ \t]*//g; # leading whitespace
+                       $c =~ s/[ \t]*$//g; # trailing whitespace
+                       $c = _EncodeCode($c);
+                       "<code>$c</code>";
+               @egsx;
+
+       return $text;
+}
+
+
+sub _EncodeCode {
+#
+# Encode/escape certain characters inside Markdown code runs.
+# The point is that in code, these characters are literals,
+# and lose their special Markdown meanings.
+#
+    local $_ = shift;
+
+       # Encode all ampersands; HTML entities are not
+       # entities within a Markdown code span.
+       s/&/&amp;/g;
+
+       # Encode $'s, but only if we're running under Blosxom.
+       # (Blosxom interpolates Perl variables in article bodies.)
+       {
+               no warnings 'once';
+       if (defined($blosxom::version)) {
+               s/\$/&#036;/g;  
+       }
+    }
+
+
+       # Do the angle bracket song and dance:
+       s! <  !&lt;!gx;
+       s! >  !&gt;!gx;
+
+       # Now, escape characters that are magic in Markdown:
+       s! \* !$g_escape_table{'*'}!gx;
+       s! _  !$g_escape_table{'_'}!gx;
+       s! {  !$g_escape_table{'{'}!gx;
+       s! }  !$g_escape_table{'}'}!gx;
+       s! \[ !$g_escape_table{'['}!gx;
+       s! \] !$g_escape_table{']'}!gx;
+       s! \\ !$g_escape_table{'\\'}!gx;
+
+       return $_;
+}
+
+
+sub _DoItalicsAndBold {
+       my $text = shift;
+
+       # <strong> must go first:
+       $text =~ s{ (\*\*|__) (?=\S) (.+?[*_]*) (?<=\S) \1 }
+               {<strong>$2</strong>}gsx;
+
+       $text =~ s{ (\*|_) (?=\S) (.+?) (?<=\S) \1 }
+               {<em>$2</em>}gsx;
+
+       return $text;
+}
+
+
+sub _DoBlockQuotes {
+       my $text = shift;
+
+       $text =~ s{
+                 (                                                             # Wrap whole match in $1
+                       (
+                         ^[ \t]*>[ \t]?                        # '>' at the start of a line
+                           .+\n                                        # rest of the first line
+                         (.+\n)*                                       # subsequent consecutive lines
+                         \n*                                           # blanks
+                       )+
+                 )
+               }{
+                       my $bq = $1;
+                       $bq =~ s/^[ \t]*>[ \t]?//gm;    # trim one level of quoting
+                       $bq =~ s/^[ \t]+$//mg;                  # trim whitespace-only lines
+                       $bq = _RunBlockGamut($bq);              # recurse
+
+                       $bq =~ s/^/  /g;
+                       # These leading spaces screw with <pre> content, so we need to fix that:
+                       $bq =~ s{
+                                       (\s*<pre>.+?</pre>)
+                               }{
+                                       my $pre = $1;
+                                       $pre =~ s/^  //mg;
+                                       $pre;
+                               }egsx;
+
+                       "<blockquote>\n$bq\n</blockquote>\n\n";
+               }egmx;
+
+
+       return $text;
+}
+
+
+sub _FormParagraphs {
+#
+#      Params:
+#              $text - string to process with html <p> tags
+#
+       my $text = shift;
+
+       # Strip leading and trailing lines:
+       $text =~ s/\A\n+//;
+       $text =~ s/\n+\z//;
+
+       my @grafs = split(/\n{2,}/, $text);
+
+       #
+       # Wrap <p> tags.
+       #
+       foreach (@grafs) {
+               unless (defined( $g_html_blocks{$_} )) {
+                       $_ = _RunSpanGamut($_);
+                       s/^([ \t]*)/<p>/;
+                       $_ .= "</p>";
+               }
+       }
+
+       #
+       # Unhashify HTML blocks
+       #
+       foreach (@grafs) {
+               if (defined( $g_html_blocks{$_} )) {
+                       $_ = $g_html_blocks{$_};
+               }
+       }
+
+       return join "\n\n", @grafs;
+}
+
+
+sub _EncodeAmpsAndAngles {
+# Smart processing for ampersands and angle brackets that need to be encoded.
+
+       my $text = shift;
+
+       # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
+       #   http://bumppo.net/projects/amputator/
+       $text =~ s/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/&amp;/g;
+
+       # Encode naked <'s
+       $text =~ s{<(?![a-z/?\$!])}{&lt;}gi;
+
+       return $text;
+}
+
+
+sub _EncodeBackslashEscapes {
+#
+#   Parameter:  String.
+#   Returns:    The string, with after processing the following backslash
+#               escape sequences.
+#
+    local $_ = shift;
+
+    s! \\\\  !$g_escape_table{'\\'}!gx;                # Must process escaped backslashes first.
+    s! \\`   !$g_escape_table{'`'}!gx;
+    s! \\\*  !$g_escape_table{'*'}!gx;
+    s! \\_   !$g_escape_table{'_'}!gx;
+    s! \\\{  !$g_escape_table{'{'}!gx;
+    s! \\\}  !$g_escape_table{'}'}!gx;
+    s! \\\[  !$g_escape_table{'['}!gx;
+    s! \\\]  !$g_escape_table{']'}!gx;
+    s! \\\(  !$g_escape_table{'('}!gx;
+    s! \\\)  !$g_escape_table{')'}!gx;
+    s! \\>   !$g_escape_table{'>'}!gx;
+    s! \\\#  !$g_escape_table{'#'}!gx;
+    s! \\\+  !$g_escape_table{'+'}!gx;
+    s! \\\-  !$g_escape_table{'-'}!gx;
+    s! \\\.  !$g_escape_table{'.'}!gx;
+    s{ \\!  }{$g_escape_table{'!'}}gx;
+
+    return $_;
+}
+
+
+sub _DoAutoLinks {
+       my $text = shift;
+
+       $text =~ s{<((https?|ftp):[^'">\s]+)>}{<a href="$1">$1</a>}gi;
+
+       # Email addresses: <address@domain.foo>
+       $text =~ s{
+               <
+        (?:mailto:)?
+               (
+                       [-.\w]+
+                       \@
+                       [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
+               )
+               >
+       }{
+               _EncodeEmailAddress( _UnescapeSpecialChars($1) );
+       }egix;
+
+       return $text;
+}
+
+
+sub _EncodeEmailAddress {
+#
+#      Input: an email address, e.g. "foo@example.com"
+#
+#      Output: the email address as a mailto link, with each character
+#              of the address encoded as either a decimal or hex entity, in
+#              the hopes of foiling most address harvesting spam bots. E.g.:
+#
+#        <a href="&#x6D;&#97;&#105;&#108;&#x74;&#111;:&#102;&#111;&#111;&#64;&#101;
+#       x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;">&#102;&#111;&#111;
+#       &#64;&#101;x&#x61;&#109;&#x70;&#108;&#x65;&#x2E;&#99;&#111;&#109;</a>
+#
+#      Based on a filter by Matthew Wickline, posted to the BBEdit-Talk
+#      mailing list: <http://tinyurl.com/yu7ue>
+#
+
+       my $addr = shift;
+
+       srand;
+       my @encode = (
+               sub { '&#' .                 ord(shift)   . ';' },
+               sub { '&#x' . sprintf( "%X", ord(shift) ) . ';' },
+               sub {                            shift          },
+       );
+
+       $addr = "mailto:" . $addr;
+
+       $addr =~ s{(.)}{
+               my $char = $1;
+               if ( $char eq '@' ) {
+                       # this *must* be encoded. I insist.
+                       $char = $encode[int rand 1]->($char);
+               } elsif ( $char ne ':' ) {
+                       # leave ':' alone (to spot mailto: later)
+                       my $r = rand;
+                       # roughly 10% raw, 45% hex, 45% dec
+                       $char = (
+                               $r > .9   ?  $encode[2]->($char)  :
+                               $r < .45  ?  $encode[1]->($char)  :
+                                                        $encode[0]->($char)
+                       );
+               }
+               $char;
+       }gex;
+
+       $addr = qq{<a href="$addr">$addr</a>};
+       $addr =~ s{">.+?:}{">}; # strip the mailto: from the visible part
+
+       return $addr;
+}
+
+
+sub _UnescapeSpecialChars {
+#
+# Swap back in all the special characters we've hidden.
+#
+       my $text = shift;
+
+       while( my($char, $hash) = each(%g_escape_table) ) {
+               $text =~ s/$hash/$char/g;
+       }
+    return $text;
+}
+
+
+sub _TokenizeHTML {
+#
+#   Parameter:  String containing HTML markup.
+#   Returns:    Reference to an array of the tokens comprising the input
+#               string. Each token is either a tag (possibly with nested,
+#               tags contained therein, such as <a href="<MTFoo>">, or a
+#               run of text between tags. Each element of the array is a
+#               two-element array; the first is either 'tag' or 'text';
+#               the second is the actual value.
+#
+#
+#   Derived from the _tokenize() subroutine from Brad Choate's MTRegex plugin.
+#       <http://www.bradchoate.com/past/mtregex.php>
+#
+
+    my $str = shift;
+    my $pos = 0;
+    my $len = length $str;
+    my @tokens;
+
+    my $depth = 6;
+    my $nested_tags = join('|', ('(?:<[a-z/!$](?:[^<>]') x $depth) . (')*>)' x  $depth);
+    my $match = qr/(?s: <! ( -- .*? -- \s* )+ > ) |  # comment
+                   (?s: <\? .*? \?> ) |              # processing instruction
+                   $nested_tags/ix;                   # nested tags
+
+    while ($str =~ m/($match)/g) {
+        my $whole_tag = $1;
+        my $sec_start = pos $str;
+        my $tag_start = $sec_start - length $whole_tag;
+        if ($pos < $tag_start) {
+            push @tokens, ['text', substr($str, $pos, $tag_start - $pos)];
+        }
+        push @tokens, ['tag', $whole_tag];
+        $pos = pos $str;
+    }
+    push @tokens, ['text', substr($str, $pos, $len - $pos)] if $pos < $len;
+    \@tokens;
+}
+
+
+sub _Outdent {
+#
+# Remove one level of line-leading tabs or spaces
+#
+       my $text = shift;
+
+       $text =~ s/^(\t|[ ]{1,$g_tab_width})//gm;
+       return $text;
+}
+
+
+sub _Detab {
+#
+# Cribbed from a post by Bart Lateur:
+# <http://www.nntp.perl.org/group/perl.macperl.anyperl/154>
+#
+       my $text = shift;
+
+       $text =~ s{(.*?)\t}{$1.(' ' x ($g_tab_width - length($1) % $g_tab_width))}ge;
+       return $text;
+}
+
+
+1;
+
+__END__
+
+
+=pod
+
+=head1 NAME
+
+B<Markdown>
+
+
+=head1 SYNOPSIS
+
+B<Markdown.pl> [ B<--html4tags> ] [ B<--version> ] [ B<-shortversion> ]
+    [ I<file> ... ]
+
+
+=head1 DESCRIPTION
+
+Markdown is a text-to-HTML filter; it translates an easy-to-read /
+easy-to-write structured text format into HTML. Markdown's text format
+is most similar to that of plain text email, and supports features such
+as headers, *emphasis*, code blocks, blockquotes, and links.
+
+Markdown's syntax is designed not as a generic markup language, but
+specifically to serve as a front-end to (X)HTML. You can  use span-level
+HTML tags anywhere in a Markdown document, and you can use block level
+HTML tags (like <div> and <table> as well).
+
+For more information about Markdown's syntax, see:
+
+    http://daringfireball.net/projects/markdown/
+
+
+=head1 OPTIONS
+
+Use "--" to end switch parsing. For example, to open a file named "-z", use:
+
+       Markdown.pl -- -z
+
+=over 4
+
+
+=item B<--html4tags>
+
+Use HTML 4 style for empty element tags, e.g.:
+
+    <br>
+
+instead of Markdown's default XHTML style tags, e.g.:
+
+    <br />
+
+
+=item B<-v>, B<--version>
+
+Display Markdown's version number and copyright information.
+
+
+=item B<-s>, B<--shortversion>
+
+Display the short-form version number.
+
+
+=back
+
+
+
+=head1 BUGS
+
+To file bug reports or feature requests (other than topics listed in the
+Caveats section above) please send email to:
+
+    support@daringfireball.net
+
+Please include with your report: (1) the example input; (2) the output
+you expected; (3) the output Markdown actually produced.
+
+
+=head1 VERSION HISTORY
+
+See the readme file for detailed release notes for this version.
+
+1.0.1 - 14 Dec 2004
+
+1.0 - 28 Aug 2004
+
+
+=head1 AUTHOR
+
+    John Gruber
+    http://daringfireball.net
+
+    PHP port and other contributions by Michel Fortin
+    http://michelf.com
+
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (c) 2003-2004 John Gruber   
+<http://daringfireball.net/>   
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright notice,
+  this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimer in the
+  documentation and/or other materials provided with the distribution.
+
+* Neither the name "Markdown" nor the names of its contributors may
+  be used to endorse or promote products derived from this software
+  without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as
+is" and any express or implied warranties, including, but not limited
+to, the implied warranties of merchantability and fitness for a
+particular purpose are disclaimed. In no event shall the copyright owner
+or contributors be liable for any direct, indirect, incidental, special,
+exemplary, or consequential damages (including, but not limited to,
+procurement of substitute goods or services; loss of use, data, or
+profits; or business interruption) however caused and on any theory of
+liability, whether in contract, strict liability, or tort (including
+negligence or otherwise) arising in any way out of the use of this
+software, even if advised of the possibility of such damage.
+
+=cut
diff --git a/etc/build.svg b/etc/build.svg
new file mode 100644 (file)
index 0000000..5df26ce
--- /dev/null
@@ -0,0 +1,34 @@
+<svg contentScriptType="text/ecmascript" width="133"
+     xmlns:xlink="http://www.w3.org/1999/xlink" zoomAndPan="magnify"
+     contentStyleType="text/css" height="20" preserveAspectRatio="xMidYMid meet"
+     xmlns="http://www.w3.org/2000/svg" version="1.0">
+
+
+    <linearGradient xmlns:xlink="http://www.w3.org/1999/xlink" x2="0" y2="100%"
+                    xlink:type="simple" xlink:actuate="onLoad" id="a"
+                    xlink:show="other">
+        <stop stop-opacity=".1" stop-color="#bbb" offset="0"/>
+        <stop stop-opacity=".1" offset="1"/>
+    </linearGradient>
+
+    <rect rx="3" fill="#555" width="80" height="20" class="sWidth"/>
+    <rect rx="3" fill="url(#a)" width="80" height="20" class="sWidth"/>
+    <rect rx="3" fill="#4c1" width="56" x="72" height="20" class="vWidth tMove"/>
+    <rect        fill="#4c1" width="13" x="72" height="20" class="tMove"/>
+
+    <g font-size="11" font-family="DejaVu Sans,Verdana,Geneva,sans-serif"
+       fill="#fff">
+        <text x="6" fill="#010101" fill-opacity=".3" y="15">
+            Download
+        </text>
+        <text x="6" id="tText" y="14">
+            Download
+        </text>
+        <text fill="#010101" x="78" fill-opacity=".3" y="15" class="tMove">
+            1.0.0.0
+        </text>
+        <text x="78" id="vText" y="14" class="tMove">
+            1.0.0.0
+        </text>
+    </g>
+</svg>
diff --git a/etc/github-markdown.css b/etc/github-markdown.css
new file mode 100644 (file)
index 0000000..1bd00d8
--- /dev/null
@@ -0,0 +1,652 @@
+@font-face {
+  font-family: octicons-anchor;
+  src: url(data:font/woff;charset=utf-8;base64,d09GRgABAAAAAAYcAA0AAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABGRlRNAAABMAAAABwAAAAca8vGTk9TLzIAAAFMAAAARAAAAFZG1VHVY21hcAAAAZAAAAA+AAABQgAP9AdjdnQgAAAB0AAAAAQAAAAEACICiGdhc3AAAAHUAAAACAAAAAj//wADZ2x5ZgAAAdwAAADRAAABEKyikaNoZWFkAAACsAAAAC0AAAA2AtXoA2hoZWEAAALgAAAAHAAAACQHngNFaG10eAAAAvwAAAAQAAAAEAwAACJsb2NhAAADDAAAAAoAAAAKALIAVG1heHAAAAMYAAAAHwAAACABEAB2bmFtZQAAAzgAAALBAAAFu3I9x/Nwb3N0AAAF/AAAAB0AAAAvaoFvbwAAAAEAAAAAzBdyYwAAAADP2IQvAAAAAM/bz7t4nGNgZGFgnMDAysDB1Ml0hoGBoR9CM75mMGLkYGBgYmBlZsAKAtJcUxgcPsR8iGF2+O/AEMPsznAYKMwIkgMA5REMOXicY2BgYGaAYBkGRgYQsAHyGMF8FgYFIM0ChED+h5j//yEk/3KoSgZGNgYYk4GRCUgwMaACRoZhDwCs7QgGAAAAIgKIAAAAAf//AAJ4nHWMMQrCQBBF/0zWrCCIKUQsTDCL2EXMohYGSSmorScInsRGL2DOYJe0Ntp7BK+gJ1BxF1stZvjz/v8DRghQzEc4kIgKwiAppcA9LtzKLSkdNhKFY3HF4lK69ExKslx7Xa+vPRVS43G98vG1DnkDMIBUgFN0MDXflU8tbaZOUkXUH0+U27RoRpOIyCKjbMCVejwypzJJG4jIwb43rfl6wbwanocrJm9XFYfskuVC5K/TPyczNU7b84CXcbxks1Un6H6tLH9vf2LRnn8Ax7A5WQAAAHicY2BkYGAA4teL1+yI57f5ysDNwgAC529f0kOmWRiYVgEpDgYmEA8AUzEKsQAAAHicY2BkYGB2+O/AEMPCAAJAkpEBFbAAADgKAe0EAAAiAAAAAAQAAAAEAAAAAAAAKgAqACoAiAAAeJxjYGRgYGBhsGFgYgABEMkFhAwM/xn0QAIAD6YBhwB4nI1Ty07cMBS9QwKlQapQW3VXySvEqDCZGbGaHULiIQ1FKgjWMxknMfLEke2A+IJu+wntrt/QbVf9gG75jK577Lg8K1qQPCfnnnt8fX1NRC/pmjrk/zprC+8D7tBy9DHgBXoWfQ44Av8t4Bj4Z8CLtBL9CniJluPXASf0Lm4CXqFX8Q84dOLnMB17N4c7tBo1AS/Qi+hTwBH4rwHHwN8DXqQ30XXAS7QaLwSc0Gn8NuAVWou/gFmnjLrEaEh9GmDdDGgL3B4JsrRPDU2hTOiMSuJUIdKQQayiAth69r6akSSFqIJuA19TrzCIaY8sIoxyrNIrL//pw7A2iMygkX5vDj+G+kuoLdX4GlGK/8Lnlz6/h9MpmoO9rafrz7ILXEHHaAx95s9lsI7AHNMBWEZHULnfAXwG9/ZqdzLI08iuwRloXE8kfhXYAvE23+23DU3t626rbs8/8adv+9DWknsHp3E17oCf+Z48rvEQNZ78paYM38qfk3v/u3l3u3GXN2Dmvmvpf1Srwk3pB/VSsp512bA/GG5i2WJ7wu430yQ5K3nFGiOqgtmSB5pJVSizwaacmUZzZhXLlZTq8qGGFY2YcSkqbth6aW1tRmlaCFs2016m5qn36SbJrqosG4uMV4aP2PHBmB3tjtmgN2izkGQyLWprekbIntJFing32a5rKWCN/SdSoga45EJykyQ7asZvHQ8PTm6cslIpwyeyjbVltNikc2HTR7YKh9LBl9DADC0U/jLcBZDKrMhUBfQBvXRzLtFtjU9eNHKin0x5InTqb8lNpfKv1s1xHzTXRqgKzek/mb7nB8RZTCDhGEX3kK/8Q75AmUM/eLkfA+0Hi908Kx4eNsMgudg5GLdRD7a84npi+YxNr5i5KIbW5izXas7cHXIMAau1OueZhfj+cOcP3P8MNIWLyYOBuxL6DRylJ4cAAAB4nGNgYoAALjDJyIAOWMCiTIxMLDmZedkABtIBygAAAA==) format('woff');
+}
+
+.markdown-body {
+  -webkit-text-size-adjust: 100%;
+  text-size-adjust: 100%;
+  color: #333;
+  overflow: hidden;
+  font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
+  font-size: 16px;
+  line-height: 1.6;
+  word-wrap: break-word;
+}
+
+.markdown-body a {
+  background-color: transparent;
+}
+
+.markdown-body a:active,
+.markdown-body a:hover {
+  outline: 0;
+}
+
+.markdown-body strong {
+  font-weight: bold;
+}
+
+.markdown-body h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+.markdown-body img {
+  border: 0;
+}
+
+.markdown-body hr {
+  box-sizing: content-box;
+  height: 0;
+}
+
+.markdown-body pre {
+  overflow: auto;
+}
+
+.markdown-body code,
+.markdown-body kbd,
+.markdown-body pre {
+  font-family: monospace, monospace;
+  font-size: 1em;
+}
+
+.markdown-body input {
+  color: inherit;
+  font: inherit;
+  margin: 0;
+}
+
+.markdown-body html input[disabled] {
+  cursor: default;
+}
+
+.markdown-body input {
+  line-height: normal;
+}
+
+.markdown-body input[type="checkbox"] {
+  box-sizing: border-box;
+  padding: 0;
+}
+
+.markdown-body table {
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+.markdown-body td,
+.markdown-body th {
+  padding: 0;
+}
+
+.markdown-body * {
+  box-sizing: border-box;
+}
+
+.markdown-body input {
+  font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol";
+}
+
+.markdown-body a {
+  color: #4078c0;
+  text-decoration: none;
+}
+
+.markdown-body a:hover,
+.markdown-body a:active {
+  text-decoration: underline;
+}
+
+.markdown-body hr {
+  height: 0;
+  margin: 15px 0;
+  overflow: hidden;
+  background: transparent;
+  border: 0;
+  border-bottom: 1px solid #ddd;
+}
+
+.markdown-body hr:before {
+  display: table;
+  content: "";
+}
+
+.markdown-body hr:after {
+  display: table;
+  clear: both;
+  content: "";
+}
+
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3,
+.markdown-body h4,
+.markdown-body h5,
+.markdown-body h6 {
+  margin-top: 15px;
+  margin-bottom: 15px;
+  line-height: 1.1;
+}
+
+.markdown-body h1 {
+  font-size: 30px;
+}
+
+.markdown-body h2 {
+  font-size: 21px;
+}
+
+.markdown-body h3 {
+  font-size: 16px;
+}
+
+.markdown-body h4 {
+  font-size: 14px;
+}
+
+.markdown-body h5 {
+  font-size: 12px;
+}
+
+.markdown-body h6 {
+  font-size: 11px;
+}
+
+.markdown-body blockquote {
+  margin: 0;
+}
+
+.markdown-body ul,
+.markdown-body ol {
+  padding: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.markdown-body ol ol,
+.markdown-body ul ol {
+  list-style-type: lower-roman;
+}
+
+.markdown-body ul ul ol,
+.markdown-body ul ol ol,
+.markdown-body ol ul ol,
+.markdown-body ol ol ol {
+  list-style-type: lower-alpha;
+}
+
+.markdown-body dd {
+  margin-left: 0;
+}
+
+.markdown-body code {
+  font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  font-size: 12px;
+}
+
+.markdown-body pre {
+  margin-top: 0;
+  margin-bottom: 0;
+  font: 12px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+}
+
+.markdown-body .select::-ms-expand {
+  opacity: 0;
+}
+
+.markdown-body .octicon {
+  font: normal normal normal 16px/1 octicons-anchor;
+  display: inline-block;
+  text-decoration: none;
+  text-rendering: auto;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-user-select: none;
+  -moz-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+.markdown-body .octicon-link:before {
+  content: '\f05c';
+}
+
+.markdown-body>*:first-child {
+  margin-top: 0 !important;
+}
+
+.markdown-body>*:last-child {
+  margin-bottom: 0 !important;
+}
+
+.markdown-body a:not([href]) {
+  color: inherit;
+  text-decoration: none;
+}
+
+.markdown-body .anchor {
+  position: absolute;
+  top: 0;
+  left: 0;
+  display: block;
+  padding-right: 6px;
+  padding-left: 30px;
+  margin-left: -30px;
+}
+
+.markdown-body .anchor:focus {
+  outline: none;
+}
+
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3,
+.markdown-body h4,
+.markdown-body h5,
+.markdown-body h6 {
+  position: relative;
+  margin-top: 1em;
+  margin-bottom: 16px;
+  font-weight: bold;
+  line-height: 1.4;
+}
+
+.markdown-body h1 .octicon-link,
+.markdown-body h2 .octicon-link,
+.markdown-body h3 .octicon-link,
+.markdown-body h4 .octicon-link,
+.markdown-body h5 .octicon-link,
+.markdown-body h6 .octicon-link {
+  display: none;
+  color: #000;
+  vertical-align: middle;
+}
+
+.markdown-body h1:hover .anchor,
+.markdown-body h2:hover .anchor,
+.markdown-body h3:hover .anchor,
+.markdown-body h4:hover .anchor,
+.markdown-body h5:hover .anchor,
+.markdown-body h6:hover .anchor {
+  padding-left: 8px;
+  margin-left: -30px;
+  text-decoration: none;
+}
+
+.markdown-body h1:hover .anchor .octicon-link,
+.markdown-body h2:hover .anchor .octicon-link,
+.markdown-body h3:hover .anchor .octicon-link,
+.markdown-body h4:hover .anchor .octicon-link,
+.markdown-body h5:hover .anchor .octicon-link,
+.markdown-body h6:hover .anchor .octicon-link {
+  display: inline-block;
+}
+
+.markdown-body h1 {
+  padding-bottom: 0.3em;
+  font-size: 2.25em;
+  line-height: 1.2;
+  border-bottom: 1px solid #eee;
+}
+
+.markdown-body h1 .anchor {
+  line-height: 1;
+}
+
+.markdown-body h2 {
+  padding-bottom: 0.3em;
+  font-size: 1.75em;
+  line-height: 1.225;
+  border-bottom: 1px solid #eee;
+}
+
+.markdown-body h2 .anchor {
+  line-height: 1;
+}
+
+.markdown-body h3 {
+  font-size: 1.5em;
+  line-height: 1.43;
+}
+
+.markdown-body h3 .anchor {
+  line-height: 1.2;
+}
+
+.markdown-body h4 {
+  font-size: 1.25em;
+}
+
+.markdown-body h4 .anchor {
+  line-height: 1.2;
+}
+
+.markdown-body h5 {
+  font-size: 1em;
+}
+
+.markdown-body h5 .anchor {
+  line-height: 1.1;
+}
+
+.markdown-body h6 {
+  font-size: 1em;
+  color: #777;
+}
+
+.markdown-body h6 .anchor {
+  line-height: 1.1;
+}
+
+.markdown-body p,
+.markdown-body blockquote,
+.markdown-body ul,
+.markdown-body ol,
+.markdown-body dl,
+.markdown-body table,
+.markdown-body pre {
+  margin-top: 0;
+  margin-bottom: 16px;
+}
+
+.markdown-body hr {
+  height: 4px;
+  padding: 0;
+  margin: 16px 0;
+  background-color: #e7e7e7;
+  border: 0 none;
+}
+
+.markdown-body ul,
+.markdown-body ol {
+  padding-left: 2em;
+}
+
+.markdown-body ul ul,
+.markdown-body ul ol,
+.markdown-body ol ol,
+.markdown-body ol ul {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.markdown-body li>p {
+  margin-top: 16px;
+}
+
+.markdown-body dl {
+  padding: 0;
+}
+
+.markdown-body dl dt {
+  padding: 0;
+  margin-top: 16px;
+  font-size: 1em;
+  font-style: italic;
+  font-weight: bold;
+}
+
+.markdown-body dl dd {
+  padding: 0 16px;
+  margin-bottom: 16px;
+}
+
+.markdown-body blockquote {
+  padding: 0 15px;
+  color: #777;
+  border-left: 4px solid #ddd;
+}
+
+.markdown-body blockquote>:first-child {
+  margin-top: 0;
+}
+
+.markdown-body blockquote>:last-child {
+  margin-bottom: 0;
+}
+
+.markdown-body table {
+  display: block;
+  width: 100%;
+  overflow: auto;
+  word-break: normal;
+  word-break: keep-all;
+}
+
+.markdown-body table th {
+  font-weight: bold;
+}
+
+.markdown-body table th,
+.markdown-body table td {
+  padding: 6px 13px;
+  border: 1px solid #ddd;
+}
+
+.markdown-body table tr {
+  background-color: #fff;
+  border-top: 1px solid #ccc;
+}
+
+.markdown-body table tr:nth-child(2n) {
+  background-color: #f8f8f8;
+}
+
+.markdown-body img {
+  max-width: 100%;
+  box-sizing: border-box;
+}
+
+.markdown-body code {
+  padding: 0;
+  padding-top: 0.2em;
+  padding-bottom: 0.2em;
+  margin: 0;
+  font-size: 85%;
+  background-color: rgba(0,0,0,0.04);
+  border-radius: 3px;
+}
+
+.markdown-body code:before,
+.markdown-body code:after {
+  letter-spacing: -0.2em;
+  content: "\00a0";
+}
+
+.markdown-body pre>code {
+  padding: 0;
+  margin: 0;
+  font-size: 100%;
+  word-break: normal;
+  white-space: pre;
+  background: transparent;
+  border: 0;
+}
+
+.markdown-body .highlight {
+  margin-bottom: 16px;
+}
+
+.markdown-body .highlight pre,
+.markdown-body pre {
+  padding: 16px;
+  overflow: auto;
+  font-size: 85%;
+  line-height: 1.45;
+  background-color: #f7f7f7;
+  border-radius: 3px;
+}
+
+.markdown-body .highlight pre {
+  margin-bottom: 0;
+  word-break: normal;
+}
+
+.markdown-body pre {
+  word-wrap: normal;
+}
+
+.markdown-body pre code {
+  display: inline;
+  max-width: initial;
+  padding: 0;
+  margin: 0;
+  overflow: initial;
+  line-height: inherit;
+  word-wrap: normal;
+  background-color: transparent;
+  border: 0;
+}
+
+.markdown-body pre code:before,
+.markdown-body pre code:after {
+  content: normal;
+}
+
+.markdown-body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font-size: 11px;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+.markdown-body .pl-c {
+  color: #969896;
+}
+
+.markdown-body .pl-c1,
+.markdown-body .pl-s .pl-v {
+  color: #0086b3;
+}
+
+.markdown-body .pl-e,
+.markdown-body .pl-en {
+  color: #795da3;
+}
+
+.markdown-body .pl-s .pl-s1,
+.markdown-body .pl-smi {
+  color: #333;
+}
+
+.markdown-body .pl-ent {
+  color: #63a35c;
+}
+
+.markdown-body .pl-k {
+  color: #a71d5d;
+}
+
+.markdown-body .pl-pds,
+.markdown-body .pl-s,
+.markdown-body .pl-s .pl-pse .pl-s1,
+.markdown-body .pl-sr,
+.markdown-body .pl-sr .pl-cce,
+.markdown-body .pl-sr .pl-sra,
+.markdown-body .pl-sr .pl-sre {
+  color: #183691;
+}
+
+.markdown-body .pl-v {
+  color: #ed6a43;
+}
+
+.markdown-body .pl-id {
+  color: #b52a1d;
+}
+
+.markdown-body .pl-ii {
+  background-color: #b52a1d;
+  color: #f8f8f8;
+}
+
+.markdown-body .pl-sr .pl-cce {
+  color: #63a35c;
+  font-weight: bold;
+}
+
+.markdown-body .pl-ml {
+  color: #693a17;
+}
+
+.markdown-body .pl-mh,
+.markdown-body .pl-mh .pl-en,
+.markdown-body .pl-ms {
+  color: #1d3e81;
+  font-weight: bold;
+}
+
+.markdown-body .pl-mq {
+  color: #008080;
+}
+
+.markdown-body .pl-mi {
+  color: #333;
+  font-style: italic;
+}
+
+.markdown-body .pl-mb {
+  color: #333;
+  font-weight: bold;
+}
+
+.markdown-body .pl-md {
+  background-color: #ffecec;
+  color: #bd2c00;
+}
+
+.markdown-body .pl-mi1 {
+  background-color: #eaffea;
+  color: #55a532;
+}
+
+.markdown-body .pl-mdr {
+  color: #795da3;
+  font-weight: bold;
+}
+
+.markdown-body .pl-mo {
+  color: #1d3e81;
+}
+
+.markdown-body kbd {
+  display: inline-block;
+  padding: 3px 5px;
+  font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+  line-height: 10px;
+  color: #555;
+  vertical-align: middle;
+  background-color: #fcfcfc;
+  border: solid 1px #ccc;
+  border-bottom-color: #bbb;
+  border-radius: 3px;
+  box-shadow: inset 0 -1px 0 #bbb;
+}
+
+.markdown-body .task-list-item {
+  list-style-type: none;
+}
+
+.markdown-body .task-list-item+.task-list-item {
+  margin-top: 3px;
+}
+
+.markdown-body .task-list-item input {
+  margin: 0 0.35em 0.25em -1.6em;
+  vertical-align: middle;
+}
+
+.markdown-body :checked+.radio-label {
+  z-index: 1;
+  position: relative;
+  border-color: #4078c0;
+}
diff --git a/etc/md2html.ksh b/etc/md2html.ksh
new file mode 100644 (file)
index 0000000..35f4c07
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/ksh
+#
+# Convert a markdown file to an html file
+#
+# usage: ms2html <markdown_file>
+#
+# html content is output to stdout.
+#
+# github style sheet from: 
+#    https://github.com/sindresorhus/github-markdown-css
+# Markdown.pl is from:
+#    http://daringfireball.net/projects/downloads/Markdown_1.0.1.zip
+#
+echo '<!DOCTYPE html>'
+echo '<html xmlns="http://www.w3.org/1999/xhtml">'
+echo '  <head>'
+echo "    <title>Zumo CC3200 `basename $1`</title>"
+echo '    <link rel="stylesheet" type="text/css" href="etc/github-markdown.css">'
+echo '    <style>'
+echo '      .markdown-body {'
+echo '         min-width: 200px;'
+echo '         max-width: 790px;'
+echo '         margin: 0 auto;'
+echo '         padding: 30px;'
+echo '      }'
+echo '    </style>'
+echo '  </head>'
+echo '  <body ><article class="markdown-body">'
+perl etc/Markdown.pl $1
+echo '  </article></body>'
+echo '</html>'
+
diff --git a/license.html b/license.html
new file mode 100644 (file)
index 0000000..582bc0f
--- /dev/null
@@ -0,0 +1,473 @@
+<!--
+Texas Instruments Manifest Format 2.0
+-->
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
+<!-- @Start Style -->
+<!-- Default style in case someone doesnt have Internet Access -->
+<style type="text/css" id="internalStyle">
+       body, div, p {
+               font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+               font-size: 13px;
+               line-height: 1.3;
+       }
+       body {
+               margin: 20px;   
+       }
+       h1 {
+               font-size: 150%;
+       }
+       h2 {
+               font-size: 120%;
+       }
+       h3 {
+               font-size: 100%;
+       }
+       img {
+               border: 0px;
+               vertical-align: middle;
+       }
+       table, th, td, tr {
+               border: 1px solid black;        
+               font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+               font-size: 13px;
+               line-height: 1.3;
+               empty-cells: show;  
+               padding: 5px;
+       }
+       table {
+               border-collapse: collapse; 
+               width: 100%;
+       }
+       tr {
+               page-break-inside: avoid;
+       }
+       #TIlogoLeft {
+               background-color: black; 
+               padding: 0;
+               width: 20%;
+       }
+       #TIlogoRight {
+               background-color: red; 
+               padding: 0;
+       }
+       #ProductName {
+               text-align: center;
+       }
+       #ReleaseDate {
+               text-align: center;
+       }
+       .LogoSection {
+               margin: 0;
+               padding: 0;
+       }
+       .HeaderSection {
+               margin: 25px 0 25px 0;
+               padding: 0;
+       }
+       .LegendSection {
+               margin: 25px 0 25px 0;
+       }
+       .ExportSection {
+               margin: 25px 0 25px 0;
+       }
+       .DisclaimerSection {
+               margin: 25px 0 25px 0;  
+       }
+       .CreditSection {
+               margin: 25px 0 25px 0;  
+       }
+       .LicenseSection {
+               margin: 25px 0 25px 0;  
+       }
+       .ManifestTable {
+               margin: 25px 0 25px 0;  
+       }
+</style> 
+<!-- Override style from TI if they have Internet Access -->
+<link type="text/css" rel="stylesheet" href="timanifeststyle.css">
+<!-- @End Style -->
+<title>Texas Instruments Manifest</title>
+</head>
+
+<body><!-- Logo display, will need to fix up the URLs, this is just for testing.. Image alternate display not wporking well yet -->
+<div class="LogoSection">
+<table>
+  <tbody>
+    <tr>
+      <td id="TIlogoLeft">
+        <a href="http://www.ti.com/">
+          <!-- img src="tilogo.gif" alt="Texas Instruments Incorporated" -->
+                 <img alt="" src="data:image/gif;base64,R0lGODlh3gA2AKIAAAAAAP///7u7u29vbz8/PwYGBujo6BgYGCH5BAAAAAAALAAAAADeADYAAAP/CLrc/jDKSau9OOvNu/9gKI5kaZ5oqq5s675wLM90bd94ru987//AoHBILBqPyKRyyWw6n9CodHorDALYLIHKJVqz2q44eAUHtoDB4DBu48rgLQErcNtnX7NhMDcICIB3gix5ZmtqAAZZew8EAo+QkQIDNVZqiIM1cHGKZ4YPAmaiAWw0c1gFmZqjB3SbZ6kNe6WhsAeOlDV0qjSFAXUAp7lwuREFtVsFgMvLB7fNAM+BCs+lDLd8BNYOuxfV22PL0RiWlwO1u3kDqejAEsjR6GB86FsHoYwA6gxWnVgGEegUuIelWJk6jswAGlXQ36J1xBSoQwfulIEDr/6l+VeK/+AehrAGOHRnAWRBbbWegckXAV6wk4AeRQtDQBEaBYsYlMl2hUCsBt0iKgilT9EfAlfO7SmzdKkrkQUT/fqZSECqLCSlntH375IAA1tqGUilLIBSNVnU+NmJNBRVChlF1QwAdlRWBy5P3QymwCLBYhs73cTHYBq3X33nDQ2wcWuBgef0FRD4GK3jU3VCZZUJAIw1OGg0P+4bFiubOWoOsEP1+KvZn3wurDbZ6lfcuw3yYkFjRSeYzRe7ARAbW0K3PmGIMi0OFDG1Mmha+RnufAHn3xL9ha6uTZ/rXagZ1GKAtTsHeWb+FEQvHILuX4+mLzj2j2r4TrFesTwMbE5Cuv8JzbTSGuRV1xgfUJFC3WbA0JWFalcItpgf8YU2yT/qATaedent5cBb8zk0DzIitgfKbonRFV9Wp2xl3UXq5Ccibp05598BnRigiAIJmrZAexkJQIuBwzX4CB3SQbeYQkPVAUco63DI2HzsAdYAiAvEZdYlaVQ5wXs3+bQAjovEUoBRR9LVAFLaPXCcY/KMqVRasQB5kiJgLcYgTkJiuCWKC2ZpIY/z/LRhYefkBAGW1HTyRy2UjObLHxSAOZ948EUVGCSC3SLZbB7iZKOLc2GRRgMH/VhdHnJwFCgD8iEGx0VKvpqbO+hoaCppEg3UiTES1CTkhNaQ+Qs4LQGql07/lET4mIQ6SvTSVGZ9Bmhz/bkYzK+PFKtpje6wumRm1wrLZzSdQASoZvyswdmSuk7p616HfkjBTxZBQucFgqXCFKdn1NpiUlQJhs8kteBWG0AbATbXS2tBlaeoVkmJRova4KkGPmhMFdiSYmq8cbTRYhrlkiHaNufJ9mIgVqEXnAOJM5JE4sgjudQ8bF82x+cKBP4Iiedecyjgx2/WtMNjjhcL9h+S4xq9RYJgsbeeUbmdrPTSQbPccsyijEXOfI8xyuinVJH1wdkS/MQ2Bc5Iq08DyHYwGglvPyCilbz0fa8GLV7r9+Btb7CJ14Qnzg8HpdKoOOF5Py752JNXvrblNphzEHnmnF/a+ecTbA465qKPXnnppkuOeuqKr8465K+z7nrsfc9Ouyq23z5I7rrfwXvvbhSQAAA7" />
+        </a>
+      </td>
+      <td id="TILogoRight">
+        <!-- img src="titagline.gif" alt="Technology for Innovators(tm)"-->
+               <img alt="" src="data:image/gif;base64,R0lGODlhOgEaALMAAP8AAP////92dv+3t/+Njf/W1v/t7f8hIf/19f+jo//Hx/8/P/9cXP/j4//6+v/+/iH5BAAAAAAALAAAAAA6ARoAAAT/EMhJq7046827/2AojmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqft0NDMCBQodis1jcADBKE7nYcCpjPgU5AQBKkVYOHAeRudqtXsh60/vRHdSoBBCGBNAkLe4o4f2psgG8pjR6GM5OLmDB/DA0GBoQADAgICRIBBQUOYgwGCg2kEgudBgUHAIGcBg0MsZ0NCnMGYgsBtqEGAbCynrW3AQONgcIFBgiErK6wAAfUtLbCscWiowoAyLDczLZu0AIJCAYOoJn0G38ObAwPEvLEts/O1vUhsA8AAjGonEmA9W6hGAVpEjiQoKBAhT8HJSRkVyEQQAAJ//a5YeMPQIFyACqCnJjSIgFCB4oB+HOSokWOAB6wIWCxnk8MfYh5QsYg5sVHfQLVMSqhztJIxWIaC6QzJy8KfZgqrNT0zR+nUNl8fSMvZ6IDwJCJRfoI7IR4Cub9nDsha6RwR02xUZpGq1utUWUq9FKgYV6/abgOHjt45tquEgY0SDDHoJg+fxhXolKNrmfH/EoR5EdAKmjQfB1qvPmGIQIJ3g4gC2egVF7LqxtP8Ng2cViTKFUCIGbNFKEEmB/VbDlYdqLRn+du8oTg6jjbmfe+CbTM2+BcuySgbQVtQoOCt7s3U8wbsqGs3ZppZLnylwFe8Uql825ogANPckUnYDoOCogxQGXADajggjcw4AA8DSSyTQASMmjhhTQscBWGHHbo4YcghijiiCSWaOKJKKao4oostugiFBEAADs=" />
+      </td>
+    </tr>
+  </tbody>
+</table>
+</div><div class="HeaderSection">
+<h1 id="ProductName">
+<!-- @Start Product -->
+zumo cc3200 Manifest
+<!-- @End Product -->
+</h1>
+
+<h2 id="ReleaseDate">
+<!-- @Start Date -->
+09-11-2015
+<!-- @End Date -->
+</h2>
+
+
+<h2 id="SRASID">
+<!-- @Start Date -->
+Manifest ID - <a href="https://opbu.itg.ti.com/SRAS/UserForms/SRAS_Record.aspx?Action=Modify&DB=SRAS&ID=SRAS00002135">SRAS00002135</a>
+<!-- @End Date -->
+</h2>
+</div><div class="LegendSection">
+<h2>Legend</h2>
+<p>(explanation of the fields in the Manifest Table below)</p>
+<table>
+<tbody>
+<tr>
+<td>
+<b>Software Name </b>
+</td>
+<td>
+The name of the application or file
+</td>
+</tr>
+<tr>
+<td>
+<b>Version</b>
+</td>
+<td>
+Version of the application or file
+</td>
+</tr>
+<tr>
+<td>
+<b>License Type</b>
+</td>
+<td>
+Type of license(s) under which TI will be providing
+software to the licensee (e.g. BSD-3-Clause, GPL-2.0, TI TSPA License, TI
+Commercial License). The license could be under Commercial terms or Open Source. See Open Source Reference License Disclaimer in
+the Disclaimers Section. Whenever possible, TI will use an <a href="http://spdx.org/licenses/"> SPDX Short Identifier </a> for an Open Source
+License. TI Commercial license terms are not usually included in the manifest and are conveyed through a variety 
+of means such as a clickwrap license upon install, 
+a signed license agreement and so forth.
+</td>
+</tr>
+<tr>
+<td>
+<b>Location</b>
+</td>
+<td>
+The directory name and path on the media or a specific file where the Software is located. Typically fully qualified path names 
+are not used and instead the relevant top level directory of the application is given. 
+A notation often used in the manifests is [as installed]/directory/*. Note that the asterisk implies that all
+files under that directory are licensed as the License Type field denotes. Any exceptions to this will 
+generally be denoted as [as installed]/directory/* except as noted below which means as shown in subsequent rows of 
+the manifest.
+</td>
+</tr>
+<tr>
+<td>
+<b>Delivered As</b>
+</td>
+<td>
+This field will either be &#8220;Source&#8221;, &#8220;Binary&#8221; or &#8220;Source
+and Binary&#8221; and is the primary form the content of the Software is delivered
+in. If the Software is delivered in an archive format, this field
+applies to the contents of the archive. If the word Limited is used
+with Source, as in &#8220;Limited Source&#8221; or &#8220;Limited Source and Binary&#8221; then
+only portions of the Source for the application are provided.
+</td>
+</tr>
+<tr>
+<td>
+<b>Modified by TI</b>
+</td>
+<td>
+This field will either be &#8220;Yes&#8221; or &#8220;No&#8221;. A &#8220;Yes&#8221; means
+TI has made changes to the Software. A &#8220;No&#8221; means TI has not made any
+changes. Note: This field is not applicable for Software &#8220;Obtained
+from&#8221; TI.
+</td>
+</tr>
+<tr>
+<td>
+<b>Obtained from</b>
+</td>
+<td>
+This field specifies from where or from whom TI obtained
+the Software. It may be a URL to an Open Source site, a 3<sup>rd</sup>
+party licensor, or TI. See Links Disclaimer in the Disclaimers
+Section.
+</td>
+</tr>
+</tbody>
+</table>
+</div><div class="DisclaimerSection">
+<h2>Disclaimers</h2>
+<h3>Export Control Classification Number (ECCN)</h3>
+<p>Any use of ECCNs listed in the Manifest is at the user&#8217;s risk
+and without recourse to TI. Your
+company, as the exporter of record, is responsible for determining the
+correct classification of any item at
+the time of export. Any export classification by TI of Software is for
+TI&#8217;s internal use only and shall not be construed as a representation
+or warranty
+regarding the proper export classification for such Software or whether
+an export
+license or other documentation is required for exporting such Software</p>
+<h3>Links in the Manifest</h3>
+<p>Any
+links appearing on this Manifest
+(for example in the &#8220;Obtained from&#8221; field) were verified at the time
+the Manifest was created. TI makes no guarantee that any listed links
+will
+remain active in the future.</p>
+<h3>Open Source License References</h3>
+<p>Your company is responsible for confirming the
+applicable license terms for any open source Software
+listed in this Manifest that was not &#8220;Obtained from&#8221; TI. Any open
+source license
+specified in this Manifest for Software that was
+not &#8220;Obtained from&#8221; TI is for TI&#8217;s internal use only and shall not be
+construed as a representation or warranty regarding the proper open
+source license terms
+for such Software.</p>
+</div><div class="ExportSection">
+<h2>Export Information</h2>
+<p>ECCN for Software included in this release:</p>
+Publicly Available  - Open Source or TI TSPA License
+</div><div class="ManifestTable">
+<!-- h2>Manifest Table</h2 -->
+ <table> 
+ <tbody> 
+ <h2> 
+  zumo cc3200 Manifest Table 
+ </h2> 
+  
+ <p> 
+ See the Legend above for a description of these columns. 
+ </p> 
+  
+ <table id="targetpackages" name="targetpackages"> 
+ <thead>  
+       <tr> 
+               <td><b>Software Name</b></td> 
+               <td><b>Version</b></td> 
+               <td><b>License Type</b></td> 
+               <td><b>Delivered As</b></td> 
+               <td><b>Modified by TI</b></td> 
+               <td></td> 
+               <td></td> 
+       </tr> 
+ </thead>  
+ <tbody> 
+       <tr> 
+               <td id="name" name="name" rowspan="2"> 
+ Zumo demos 
+ </td> 
+               <td id="version" name="version" rowspan="2"> 
+ 1.0 
+ </td> 
+               <td id="license" name="license" rowspan="2"> 
+ BSD-3-clause 
+ </td> 
+               <td id="delivered" name="delivered" rowspan="2"> 
+ source 
+ </td> 
+               <td id="modified" name="modified" rowspan="2"> 
+ na 
+ </td> 
+               <td><b>Location</b></td> 
+               <td id="location" name="location"> 
+ [as installed]/* except as noted in this manifest 
+ </td> 
+       </tr> 
+       <tr> 
+               <td><b>Obtained from</b></td> 
+               <td id="obtained" name="obtained"> 
+ TI 
+ </td> 
+       </tr> 
+ <tbody> 
+       <tr> 
+               <td id="name" name="name" rowspan="2"> 
+ LSM303 
+ </td> 
+               <td id="version" name="version" rowspan="2"> 
+  
+ </td> 
+               <td id="license" name="license" rowspan="2"> 
+ MIT 
+ </td> 
+               <td id="delivered" name="delivered" rowspan="2"> 
+ source 
+ </td> 
+               <td id="modified" name="modified" rowspan="2"> 
+ yes 
+ </td> 
+               <td><b>Location</b></td> 
+               <td id="location" name="location"> 
+ [as installed]/src/Processing/*/GraphClass.pde 
+ </td> 
+       </tr> 
+       <tr> 
+               <td><b>Obtained from</b></td> 
+               <td id="obtained" name="obtained"> 
+ https://github.com/pololu/lsm303-arduino 
+ </td> 
+       </tr> 
+ <tbody> 
+       <tr> 
+               <td id="name" name="name" rowspan="2"> 
+ L3G 
+ </td> 
+               <td id="version" name="version" rowspan="2"> 
+  
+ </td> 
+               <td id="license" name="license" rowspan="2"> 
+ MIT 
+ </td> 
+               <td id="delivered" name="delivered" rowspan="2"> 
+ source 
+ </td> 
+               <td id="modified" name="modified" rowspan="2"> 
+ yes 
+ </td> 
+               <td><b>Location</b></td> 
+               <td id="location" name="location"> 
+ [as installed]/src/Processing/*/GraphClass.pde 
+ </td> 
+       </tr> 
+       <tr> 
+               <td><b>Obtained from</b></td> 
+               <td id="obtained" name="obtained"> 
+ https://github.com/pololu/l3g-arduino 
+ </td> 
+       </tr> 
+ <tbody> 
+       <tr> 
+               <td id="name" name="name" rowspan="2"> 
+ Pushbutton and ZumoMotors 
+ </td> 
+               <td id="version" name="version" rowspan="2"> 
+  
+ </td> 
+               <td id="license" name="license" rowspan="2"> 
+ MIT 
+ </td> 
+               <td id="delivered" name="delivered" rowspan="2"> 
+ source 
+ </td> 
+               <td id="modified" name="modified" rowspan="2"> 
+ yes 
+ </td> 
+               <td><b>Location</b></td> 
+               <td id="location" name="location"> 
+ [as installed]/src/Processing/*/GraphClass.pde 
+ </td> 
+       </tr> 
+       <tr> 
+               <td><b>Obtained from</b></td> 
+               <td id="obtained" name="obtained"> 
+ https://github.com/pololu/zumo-shield 
+ </td> 
+       </tr> 
+ <tbody> 
+       <tr> 
+               <td id="name" name="name" rowspan="2"> 
+ Line Graphing class 
+ </td> 
+               <td id="version" name="version" rowspan="2"> 
+  
+ </td> 
+               <td id="license" name="license" rowspan="2"> 
+ MIT 
+ </td> 
+               <td id="delivered" name="delivered" rowspan="2"> 
+ source 
+ </td> 
+               <td id="modified" name="modified" rowspan="2"> 
+ no 
+ </td> 
+               <td><b>Location</b></td> 
+               <td id="location" name="location"> 
+ [as installed]/src/Processing/*/GraphClass.pde 
+ </td> 
+       </tr> 
+       <tr> 
+               <td><b>Obtained from</b></td> 
+               <td id="obtained" name="obtained"> 
+ https://github.com/sebnil/RealtimePlotter 
+ </td> 
+       </tr> 
+ <tbody> 
+       <tr> 
+               <td id="name" name="name" rowspan="2"> 
+ Cubic Spline interpolation in C++ 
+ </td> 
+               <td id="version" name="version" rowspan="2"> 
+  
+ </td> 
+               <td id="license" name="license" rowspan="2"> 
+ GPL2 
+ </td> 
+               <td id="delivered" name="delivered" rowspan="2"> 
+ source 
+ </td> 
+               <td id="modified" name="modified" rowspan="2"> 
+ no 
+ </td> 
+               <td><b>Location</b></td> 
+               <td id="location" name="location"> 
+ [as installed]/src/Energia/ZumoCC3200/utility/Spline.h 
+ </td> 
+       </tr> 
+       <tr> 
+               <td><b>Obtained from</b></td> 
+               <td id="obtained" name="obtained"> 
+ http://kluge.in-chemnitz.de/opensource/spline/ 
+ </td> 
+       </tr> 
+ </tbody> 
+ </table> 
+  
+ </p> 
+ </p> 
+ <p> 
+
+</div><div class="CreditSection">
+<h2>Credits</h2>
+<BR> <BR><BR><BR><BR>
+</div><div class="LicenseSection">
+<h2>Licenses</h2>
+<BR><h3><b> zumo cc3200 Licenses </b></h3><BR> <BR><BR>LSM303<BR><BR>Copyright (c) 2014 Pololu Corporation.  For more information, see<BR><BR>http://www.pololu.com/<BR>http://forum.pololu.com/<BR><BR>Permission is hereby granted, free of charge, to any person<BR>obtaining a copy of this software and associated documentation<BR>files (the "Software"), to deal in the Software without<BR>restriction, including without limitation the rights to use,<BR>copy, modify, merge, publish, distribute, sublicense, and/or sell<BR>copies of the Software, and to permit persons to whom the<BR>Software is furnished to do so, subject to the following<BR>conditions:<BR><BR>The above copyright notice and this permission notice shall be<BR>included in all copies or substantial portions of the Software.<BR><BR>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,<BR>EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES<BR>OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND<BR>NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT<BR>HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,<BR>WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<BR>FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR<BR>OTHER DEALINGS IN THE SOFTWARE.<BR><BR>L3G<BR>Copyright (c) 2014 Pololu Corporation.  For more information, see<BR><BR>http://www.pololu.com/<BR>http://forum.pololu.com/<BR><BR>Permission is hereby granted, free of charge, to any person<BR>obtaining a copy of this software and associated documentation<BR>files (the "Software"), to deal in the Software without<BR>restriction, including without limitation the rights to use,<BR>copy, modify, merge, publish, distribute, sublicense, and/or sell<BR>copies of the Software, and to permit persons to whom the<BR>Software is furnished to do so, subject to the following<BR>conditions:<BR><BR>The above copyright notice and this permission notice shall be<BR>included in all copies or substantial portions of the Software.<BR><BR>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,<BR>EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES<BR>OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND<BR>NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT<BR>HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,<BR>WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<BR>FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR<BR>OTHER DEALINGS IN THE SOFTWARE.<BR><BR>Pushbutton and ZumoMotors<BR>Copyright (c) 2014 Pololu Corporation.  For more information, see<BR><BR>http://www.pololu.com/<BR>http://forum.pololu.com/<BR><BR>Permission is hereby granted, free of charge, to any person<BR>obtaining a copy of this software and associated documentation<BR>files (the "Software"), to deal in the Software without<BR>restriction, including without limitation the rights to use,<BR>copy, modify, merge, publish, distribute, sublicense, and/or sell<BR>copies of the Software, and to permit persons to whom the<BR>Software is furnished to do so, subject to the following<BR>conditions:<BR><BR>The above copyright notice and this permission notice shall be<BR>included in all copies or substantial portions of the Software.<BR><BR>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,<BR>EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES<BR>OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND<BR>NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT<BR>HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,<BR>WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<BR>FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR<BR>OTHER DEALINGS IN THE SOFTWARE.<BR><BR>Line Graphing class<BR>The MIT License (MIT)<BR><BR>Copyright (c) 2013 Sebatian Nilsson<BR><BR>Permission is hereby granted, free of charge, to any person obtaining a copy<BR>of this software and associated documentation files (the "Software"), to deal<BR>in the Software without restriction, including without limitation the rights<BR>to use, copy, modify, merge, publish, distribute, sublicense, and/or sell<BR>copies of the Software, and to permit persons to whom the Software is<BR>furnished to do so, subject to the following conditions:<BR><BR>The above copyright notice and this permission notice shall be included in all<BR>copies or substantial portions of the Software.<BR><BR>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<BR>IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<BR>FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE<BR>AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<BR>LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,<BR>OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE<BR>SOFTWARE.<BR><BR>Cubic Spline interpolation in C++<BR>                    GNU GENERAL PUBLIC LICENSE<BR>                       Version 2, June 1991<BR><BR> Copyright (C) 1989, 1991 Free Software Foundation, Inc.,<BR> 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA<BR> Everyone is permitted to copy and distribute verbatim copies<BR> of this license document, but changing it is not allowed.<BR><BR>                            Preamble<BR><BR>  The licenses for most software are designed to take away your<BR>freedom to share and change it.  By contrast, the GNU General Public<BR>License is intended to guarantee your freedom to share and change free<BR>software--to make sure the software is free for all its users.  This<BR>General Public License applies to most of the Free Software<BR>Foundation's software and to any other program whose authors commit to<BR>using it.  (Some other Free Software Foundation software is covered by<BR>the GNU Lesser General Public License instead.)  You can apply it to<BR>your programs, too.<BR><BR>  When we speak of free software, we are referring to freedom, not<BR>price.  Our General Public Licenses are designed to make sure that you<BR>have the freedom to distribute copies of free software (and charge for<BR>this service if you wish), that you receive source code or can get it<BR>if you want it, that you can change the software or use pieces of it<BR>in new free programs; and that you know you can do these things.<BR><BR>  To protect your rights, we need to make restrictions that forbid<BR>anyone to deny you these rights or to ask you to surrender the rights.<BR>These restrictions translate to certain responsibilities for you if you<BR>distribute copies of the software, or if you modify it.<BR><BR>  For example, if you distribute copies of such a program, whether<BR>gratis or for a fee, you must give the recipients all the rights that<BR>you have.  You must make sure that they, too, receive or can get the<BR>source code.  And you must show them these terms so they know their<BR>rights.<BR><BR>  We protect your rights with two steps: (1) copyright the software, and<BR>(2) offer you this license which gives you legal permission to copy,<BR>distribute and/or modify the software.<BR><BR>  Also, for each author's protection and ours, we want to make certain<BR>that everyone understands that there is no warranty for this free<BR>software.  If the software is modified by someone else and passed on, we<BR>want its recipients to know that what they have is not the original, so<BR>that any problems introduced by others will not reflect on the original<BR>authors' reputations.<BR><BR>  Finally, any free program is threatened constantly by software<BR>patents.  We wish to avoid the danger that redistributors of a free<BR>program will individually obtain patent licenses, in effect making the<BR>program proprietary.  To prevent this, we have made it clear that any<BR>patent must be licensed for everyone's free use or not licensed at all.<BR><BR>  The precise terms and conditions for copying, distribution and<BR>modification follow.<BR><BR>                    GNU GENERAL PUBLIC LICENSE<BR>   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION<BR><BR>  0. This License applies to any program or other work which contains<BR>a notice placed by the copyright holder saying it may be distributed<BR>under the terms of this General Public License.  The "Program", below,<BR>refers to any such program or work, and a "work based on the Program"<BR>means either the Program or any derivative work under copyright law:<BR>that is to say, a work containing the Program or a portion of it,<BR>either verbatim or with modifications and/or translated into another<BR>language.  (Hereinafter, translation is included without limitation in<BR>the term "modification".)  Each licensee is addressed as "you".<BR><BR>Activities other than copying, distribution and modification are not<BR>covered by this License; they are outside its scope.  The act of<BR>running the Program is not restricted, and the output from the Program<BR>is covered only if its contents constitute a work based on the<BR>Program (independent of having been made by running the Program).<BR>Whether that is true depends on what the Program does.<BR><BR>  1. You may copy and distribute verbatim copies of the Program's<BR>source code as you receive it, in any medium, provided that you<BR>conspicuously and appropriately publish on each copy an appropriate<BR>copyright notice and disclaimer of warranty; keep intact all the<BR>notices that refer to this License and to the absence of any warranty;<BR>and give any other recipients of the Program a copy of this License<BR>along with the Program.<BR><BR>You may charge a fee for the physical act of transferring a copy, and<BR>you may at your option offer warranty protection in exchange for a fee.<BR><BR>  2. You may modify your copy or copies of the Program or any portion<BR>of it, thus forming a work based on the Program, and copy and<BR>distribute such modifications or work under the terms of Section 1<BR>above, provided that you also meet all of these conditions:<BR><BR>    a) You must cause the modified files to carry prominent notices<BR>    stating that you changed the files and the date of any change.<BR><BR>    b) You must cause any work that you distribute or publish, that in<BR>    whole or in part contains or is derived from the Program or any<BR>    part thereof, to be licensed as a whole at no charge to all third<BR>    parties under the terms of this License.<BR><BR>    c) If the modified program normally reads commands interactively<BR>    when run, you must cause it, when started running for such<BR>    interactive use in the most ordinary way, to print or display an<BR>    announcement including an appropriate copyright notice and a<BR>    notice that there is no warranty (or else, saying that you provide<BR>    a warranty) and that users may redistribute the program under<BR>    these conditions, and telling the user how to view a copy of this<BR>    License.  (Exception: if the Program itself is interactive but<BR>    does not normally print such an announcement, your work based on<BR>    the Program is not required to print an announcement.)<BR><BR>These requirements apply to the modified work as a whole.  If<BR>identifiable sections of that work are not derived from the Program,<BR>and can be reasonably considered independent and separate works in<BR>themselves, then this License, and its terms, do not apply to those<BR>sections when you distribute them as separate works.  But when you<BR>distribute the same sections as part of a whole which is a work based<BR>on the Program, the distribution of the whole must be on the terms of<BR>this License, whose permissions for other licensees extend to the<BR>entire whole, and thus to each and every part regardless of who wrote it.<BR><BR>Thus, it is not the intent of this section to claim rights or contest<BR>your rights to work written entirely by you; rather, the intent is to<BR>exercise the right to control the distribution of derivative or<BR>collective works based on the Program.<BR><BR>In addition, mere aggregation of another work not based on the Program<BR>with the Program (or with a work based on the Program) on a volume of<BR>a storage or distribution medium does not bring the other work under<BR>the scope of this License.<BR><BR>  3. You may copy and distribute the Program (or a work based on it,<BR>under Section 2) in object code or executable form under the terms of<BR>Sections 1 and 2 above provided that you also do one of the following:<BR><BR>    a) Accompany it with the complete corresponding machine-readable<BR>    source code, which must be distributed under the terms of Sections<BR>    1 and 2 above on a medium customarily used for software interchange; or,<BR><BR>    b) Accompany it with a written offer, valid for at least three<BR>    years, to give any third party, for a charge no more than your<BR>    cost of physically performing source distribution, a complete<BR>    machine-readable copy of the corresponding source code, to be<BR>    distributed under the terms of Sections 1 and 2 above on a medium<BR>    customarily used for software interchange; or,<BR><BR>    c) Accompany it with the information you received as to the offer<BR>    to distribute corresponding source code.  (This alternative is<BR>    allowed only for noncommercial distribution and only if you<BR>    received the program in object code or executable form with such<BR>    an offer, in accord with Subsection b above.)<BR><BR>The source code for a work means the preferred form of the work for<BR>making modifications to it.  For an executable work, complete source<BR>code means all the source code for all modules it contains, plus any<BR>associated interface definition files, plus the scripts used to<BR>control compilation and installation of the executable.  However, as a<BR>special exception, the source code distributed need not include<BR>anything that is normally distributed (in either source or binary<BR>form) with the major components (compiler, kernel, and so on) of the<BR>operating system on which the executable runs, unless that component<BR>itself accompanies the executable.<BR><BR>If distribution of executable or object code is made by offering<BR>access to copy from a designated place, then offering equivalent<BR>access to copy the source code from the same place counts as<BR>distribution of the source code, even though third parties are not<BR>compelled to copy the source along with the object code.<BR><BR>  4. You may not copy, modify, sublicense, or distribute the Program<BR>except as expressly provided under this License.  Any attempt<BR>otherwise to copy, modify, sublicense or distribute the Program is<BR>void, and will automatically terminate your rights under this License.<BR>However, parties who have received copies, or rights, from you under<BR>this License will not have their licenses terminated so long as such<BR>parties remain in full compliance.<BR><BR>  5. You are not required to accept this License, since you have not<BR>signed it.  However, nothing else grants you permission to modify or<BR>distribute the Program or its derivative works.  These actions are<BR>prohibited by law if you do not accept this License.  Therefore, by<BR>modifying or distributing the Program (or any work based on the<BR>Program), you indicate your acceptance of this License to do so, and<BR>all its terms and conditions for copying, distributing or modifying<BR>the Program or works based on it.<BR><BR>  6. Each time you redistribute the Program (or any work based on the<BR>Program), the recipient automatically receives a license from the<BR>original licensor to copy, distribute or modify the Program subject to<BR>these terms and conditions.  You may not impose any further<BR>restrictions on the recipients' exercise of the rights granted herein.<BR>You are not responsible for enforcing compliance by third parties to<BR>this License.<BR><BR>  7. If, as a consequence of a court judgment or allegation of patent<BR>infringement or for any other reason (not limited to patent issues),<BR>conditions are imposed on you (whether by court order, agreement or<BR>otherwise) that contradict the conditions of this License, they do not<BR>excuse you from the conditions of this License.  If you cannot<BR>distribute so as to satisfy simultaneously your obligations under this<BR>License and any other pertinent obligations, then as a consequence you<BR>may not distribute the Program at all.  For example, if a patent<BR>license would not permit royalty-free redistribution of the Program by<BR>all those who receive copies directly or indirectly through you, then<BR>the only way you could satisfy both it and this License would be to<BR>refrain entirely from distribution of the Program.<BR><BR>If any portion of this section is held invalid or unenforceable under<BR>any particular circumstance, the balance of the section is intended to<BR>apply and the section as a whole is intended to apply in other<BR>circumstances.<BR><BR>It is not the purpose of this section to induce you to infringe any<BR>patents or other property right claims or to contest validity of any<BR>such claims; this section has the sole purpose of protecting the<BR>integrity of the free software distribution system, which is<BR>implemented by public license practices.  Many people have made<BR>generous contributions to the wide range of software distributed<BR>through that system in reliance on consistent application of that<BR>system; it is up to the author/donor to decide if he or she is willing<BR>to distribute software through any other system and a licensee cannot<BR>impose that choice.<BR><BR>This section is intended to make thoroughly clear what is believed to<BR>be a consequence of the rest of this License.<BR><BR>  8. If the distribution and/or use of the Program is restricted in<BR>certain countries either by patents or by copyrighted interfaces, the<BR>original copyright holder who places the Program under this License<BR>may add an explicit geographical distribution limitation excluding<BR>those countries, so that distribution is permitted only in or among<BR>countries not thus excluded.  In such case, this License incorporates<BR>the limitation as if written in the body of this License.<BR><BR>  9. The Free Software Foundation may publish revised and/or new versions<BR>of the General Public License from time to time.  Such new versions will<BR>be similar in spirit to the present version, but may differ in detail to<BR>address new problems or concerns.<BR><BR>Each version is given a distinguishing version number.  If the Program<BR>specifies a version number of this License which applies to it and "any<BR>later version", you have the option of following the terms and conditions<BR>either of that version or of any later version published by the Free<BR>Software Foundation.  If the Program does not specify a version number of<BR>this License, you may choose any version ever published by the Free Software<BR>Foundation.<BR><BR>  10. If you wish to incorporate parts of the Program into other free<BR>programs whose distribution conditions are different, write to the author<BR>to ask for permission.  For software which is copyrighted by the Free<BR>Software Foundation, write to the Free Software Foundation; we sometimes<BR>make exceptions for this.  Our decision will be guided by the two goals<BR>of preserving the free status of all derivatives of our free software and<BR>of promoting the sharing and reuse of software generally.<BR><BR>                            NO WARRANTY<BR><BR>  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY<BR>FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN<BR>OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES<BR>PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED<BR>OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF<BR>MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS<BR>TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE<BR>PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,<BR>REPAIR OR CORRECTION.<BR><BR>  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING<BR>WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR<BR>REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,<BR>INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING<BR>OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED<BR>TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY<BR>YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER<BR>PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE<BR>POSSIBILITY OF SUCH DAMAGES.<BR><BR>                     END OF TERMS AND CONDITIONS<BR><BR>            How to Apply These Terms to Your New Programs<BR><BR>  If you develop a new program, and you want it to be of the greatest<BR>possible use to the public, the best way to achieve this is to make it<BR>free software which everyone can redistribute and change under these terms.<BR><BR>  To do so, attach the following notices to the program.  It is safest<BR>to attach them to the start of each source file to most effectively<BR>convey the exclusion of warranty; and each file should have at least<BR>the "copyright" line and a pointer to where the full notice is found.<BR><BR>    &ltone line to give the program's name and a brief idea of what it does.><BR>    Copyright (C) &ltyear>  &ltname of author><BR><BR>    This program is free software; you can redistribute it and/or modify<BR>    it under the terms of the GNU General Public License as published by<BR>    the Free Software Foundation; either version 2 of the License, or<BR>    (at your option) any later version.<BR><BR>    This program is distributed in the hope that it will be useful,<BR>    but WITHOUT ANY WARRANTY; without even the implied warranty of<BR>    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<BR>    GNU General Public License for more details.<BR><BR>    You should have received a copy of the GNU General Public License along<BR>    with this program; if not, write to the Free Software Foundation, Inc.,<BR>    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.<BR><BR>Also add information on how to contact you by electronic and paper mail.<BR><BR>If the program is interactive, make it output a short notice like this<BR>when it starts in an interactive mode:<BR><BR>    Gnomovision version 69, Copyright (C) year name of author<BR>    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.<BR>    This is free software, and you are welcome to redistribute it<BR>    under certain conditions; type `show c' for details.<BR><BR>The hypothetical commands `show w' and `show c' should show the appropriate<BR>parts of the General Public License.  Of course, the commands you use may<BR>be called something other than `show w' and `show c'; they could even be<BR>mouse-clicks or menu items--whatever suits your program.<BR><BR>You should also get your employer (if you work as a programmer) or your<BR>school, if any, to sign a "copyright disclaimer" for the program, if<BR>necessary.  Here is a sample; alter the names:<BR><BR>  Yoyodyne, Inc., hereby disclaims all copyright interest in the program<BR>  `Gnomovision' (which makes passes at compilers) written by James Hacker.<BR><BR>  &ltsignature of Ty Coon>, 1 April 1989<BR>  Ty Coon, President of Vice<BR><BR>This General Public License does not permit incorporating your program into<BR>proprietary programs.  If your program is a subroutine library, you may<BR>consider it more useful to permit linking proprietary applications with the<BR>library.  If this is what you want to do, use the GNU Lesser General<BR>Public License instead of this License.<BR><BR><BR><BR><BR>
+</div>
+
+</body></html>
diff --git a/makefile b/makefile
new file mode 100644 (file)
index 0000000..818331e
--- /dev/null
+++ b/makefile
@@ -0,0 +1,10 @@
+GOALS = README.html
+
+all: $(GOALS)
+
+%.html:%.md
+       rm -f $@
+       etc/md2html.ksh $< > $@
+
+clean:
+       rm -f $(GOALS)
diff --git a/src/Energia/libraries/ZumoCC3200/ZumoCC3200.h b/src/Energia/libraries/ZumoCC3200/ZumoCC3200.h
new file mode 100644 (file)
index 0000000..a2efdc9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2015, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef ZumoCC3200_h
+#define ZumoCC3200_h 1
+
+#include <ZumoMotors.h>
+
+#endif
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/AssistedDrive.ino b/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/AssistedDrive.ino
new file mode 100644 (file)
index 0000000..5ade3ee
--- /dev/null
@@ -0,0 +1,36 @@
+
+/*  ======== AssistedDrive.ino ========
+ *  Simple demo of IMU-assisted driving of the Zumo CC3200
+ *
+ *  This example creates an access point named zumo-drive with password
+ *  "password" and a simple command/telemetry server on port 8080.
+ *
+ *  The command server allows any WiFi client to drive the Zumo using WASD
+ *  keystrokes, drive in stright lines and turn 90-degrees (as determined by
+ *  IMU sensors), and acquire IMU telemetry data for real-time display by a
+ *  client program running on a laptop.
+ *
+ *  An example of such a client is provided in the ./Processing/AssistedDrive
+ *  sub-directory.  This is a Processing application (which
+ *  uses the development IDE that inspired the Energia/Wiring development
+ *  environment; see https://processing.org).
+ */
+
+#include <IMUManager.h>
+#include <Utilities.h>
+
+/* imu sketch external declarations */
+extern IMUManager imu;
+extern bool isZeroing;
+extern Utilities::MotorInfo motorStatus;
+
+/* motor sketch external declarations */
+extern char motorWASD;
+extern bool needToZero;
+extern bool readyToRun;
+
+/* AP sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+extern float driveRate;
+extern float driveTime;
+extern int angleToTurn;
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/MotorControlLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/MotorControlLoop.ino
new file mode 100644 (file)
index 0000000..f74c02e
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+#include <math.h>
+#include <PIDController.h>
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+#define TEST 0
+
+char motorWASD = ' '; /* current motor drive command */
+float targetAngle = 0.0;
+bool targetAngleSet = false;
+
+static ZumoMotors motors; /* Zumo motor driver provided by Pololu */
+
+PIDController steerPID(0.01f, 0.0f, 0.0f);
+PIDController spinPID(2.0f, 0.0f, 0.0f);
+
+Utilities::MotorInfo motorStatus;
+int count = 0;
+bool timedOut = false;
+Utilities util;
+long initTime;
+
+static int   clip(int speed);
+static float clip(float speed);
+static void  drive(char wasd, int speed, unsigned int duration);
+static int   next(int cur, int goal);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.begin(9600);
+    Serial.println("motorSetup ...");
+
+    /* initialize the Pololu driver motor library */
+    ZumoMotors::setRightSpeed(0);
+    ZumoMotors::setLeftSpeed(0);
+    motorStatus.leftSpeed = 0;
+    motorStatus.rightSpeed = 0;
+
+    Serial.println("motorSetup done.");
+    util = Utilities();
+}
+
+/*
+ *  ======== motorLoop ========
+ *
+ *  Based on user input, drive open loop or
+ *  perform closed-loop maneuvers
+ */
+void motorLoop(void)
+{
+    static char lastWASD = ' ';
+    if (lastWASD != motorWASD) {
+        lastWASD = motorWASD;
+        
+        Serial.print("new motor cmd: "); Serial.println(motorWASD);
+        Serial.print("  is angle set: "); Serial.println(targetAngleSet);
+        Serial.print("  target angle: "); Serial.println(targetAngle);
+        Serial.print("  timed out   : "); Serial.println(timedOut);
+    }
+
+    if (isZeroing) {
+        spinPID = PIDController(2.0f, 0.0f, 0.0f);
+        steerPID = PIDController(0.01f, 0.0f, 0.0f);
+        ZumoMotors::setLeftSpeed(0);
+        ZumoMotors::setRightSpeed(0);
+        motorStatus.leftSpeed = 0;
+        motorStatus.rightSpeed = 0;
+        delay(500);
+    }
+    else {
+        switch (motorWASD) {
+            case 's':
+            case 'w': {
+                targetAngleSet = false;
+                drive(motorWASD, 250, PERIOD);
+                break;
+            }
+
+            case 'd':
+            case 'a': {
+                targetAngleSet = false;
+                drive(motorWASD, 100, PERIOD);
+                break;
+            }
+
+            case 't': {
+                turnAngle(angleToTurn, driveRate);
+                break;
+            }
+            case 'g': {
+                driveStraight(driveTime, driveRate);
+                break;
+            }
+
+            default: {
+                targetAngleSet = false;
+                motorWASD = ' ';
+                drive(' ', 0, 10);
+                break;
+            }
+        }
+    }
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+static float clip(float speed)
+{
+    if (speed < -400) {
+        speed = -400.0;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+        motorStatus.leftSpeed = leftSpeed;
+        motorStatus.rightSpeed = rightSpeed;
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
+
+/*
+ *  ======== turnAngle ========
+ *  Turn an angle using a PID controller
+ */
+void turnAngle(float angle, float rate)
+{
+    float error;
+    float power;
+
+    if (!targetAngleSet) {
+        Utilities::reInitErrorVals();
+        float currHeading = IMUManager::getGyroYaw();
+        targetAngle = Utilities::wrapAngle(currHeading + angle);
+        targetAngleSet = true;
+    }
+    else {
+        error = Utilities::wrapAngle(IMUManager::getGyroYaw() - targetAngle);
+        power = spinPID.calculate(error);
+        bool done = Utilities::inRange(error, -0.5f, 0.5f);
+        if (done) {
+            motorWASD = ' ';
+            targetAngleSet = false;
+            power = 0;
+        }
+
+        /* if power is too low, the bot doesn't turn */
+        if (power > 0 && power < 70) {
+            power = 70;
+        }
+        else if (power < 0 && power > -70) {
+            power = -70;
+        }
+
+        /* if power is higher than 250, a buildup of error in the
+         * integrated gyro angle occurs
+         */
+        if (power > 250) {
+            power = 250;
+        }
+        else if (power < -250) {
+            power = -250;
+        }
+
+        motorStatus.leftSpeed = power;
+        motorStatus.rightSpeed = -power;
+
+        ZumoMotors::setLeftSpeed(power);
+        ZumoMotors::setRightSpeed(-power);
+
+        motorStatus.error = error;
+        motorStatus.time = (float)millis();
+    }
+
+    delay(10);
+}
+
+/*
+ *  ======== driveStraight ========
+ *  Use the gyro and PID control to drive the robot straight
+ */
+void driveStraight(float numSeconds, float rate)
+{
+    bool forward = true;
+    
+    if (rate < 0) {
+        forward = false;
+        rate = -rate;
+    }
+
+    if (!targetAngleSet) {
+        float currHeading = IMUManager::getGyroYaw();
+        targetAngle = currHeading;
+        timedOut = false;
+        targetAngleSet = true;
+        initTime = millis();
+    }
+    else {
+        float error = util.wrapAngle(IMUManager::getGyroYaw() - targetAngle);
+        float power = steerPID.calculate(error);
+        power = util.saturate(power, rate - 1.0f, 1.0f - rate);
+        float lTotal = clip((rate + power) * 400);
+        float rTotal = clip((rate - power) * 400);
+
+        if (!forward) {
+            float temp = lTotal;
+            lTotal = -rTotal;
+            rTotal = -temp;
+        }
+
+        if (timedOut) {
+            lTotal = 0;
+            rTotal = 0;
+            motorWASD = ' ';
+        }
+        else {
+            timedOut = ((millis() - initTime) > ((int)(numSeconds * 1000.0f)));
+        }
+
+        ZumoMotors::setLeftSpeed(lTotal);
+        ZumoMotors::setRightSpeed(rTotal);
+
+        motorStatus.error = error;
+        motorStatus.leftSpeed = lTotal;
+        motorStatus.rightSpeed = rTotal;
+        motorStatus.time = (float)millis();
+    }
+
+    delay(10);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/SensorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/SensorLoop.ino
new file mode 100644 (file)
index 0000000..c6a38bc
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 200Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+/* Pololu IMU data instance objects */
+IMUManager imu;
+
+bool needToZero = false; /* set by WiFi to recalibrate Gyro */
+bool isZeroing = true;   /* flag indicating we are in "zero'ing mode */
+
+static void calibrateMagnetometer();
+
+/*
+ *  ======== sensorSetup ========
+ */
+void sensorSetup()
+{
+    Serial.begin(9600);
+    Serial.println("sensorSetup ...");
+
+    Wire.begin();
+    imu = IMUManager();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imu.initAccel();
+    imu.enableAccelDefault();
+
+    /* initialize Zumo gyro */
+    if (!imu.initGyro()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imu.enableGyroDefault();
+    
+    Serial.println("Calibrating gyro for 2 seconds: keep zumo still during calibration period");
+    isZeroing = true;
+    imu.calibrateGyro(2);
+    imu.zeroGyroXAxis();
+    imu.zeroGyroYAxis();
+    imu.zeroGyroZAxis();
+    isZeroing = false;    
+    
+    //calibrateMagnetometer();
+
+    Serial.println("sensorSetup done.");
+}
+
+/*
+ *  ======== sensorLoop ========
+ */
+void sensorLoop()
+{
+    static int imuCount = 0;
+
+    /* update IMU data every 5 ms (200 Hz) */
+
+    if (needToZero) {
+        imu.calibrateGyro(1);
+        imu.zeroGyroXAxis();
+        imu.zeroGyroYAxis();
+        imu.zeroGyroZAxis();
+        needToZero = false;
+    }
+
+    /* read data from all IMU sensors */
+    imu.readGyro();
+    imu.readAccel();
+    imu.readMag();
+
+    delay(5);
+}
+
+/*
+ *  ======== calibrateMagnetometer ========
+ *  Calibrate the magnetometer 
+ */
+static void calibrateMagnetometer()
+{
+    static bool calibrated = 0;
+    if (!calibrated) {
+        calibrated = 1;
+        
+        /* calibrate magnetometer */
+        Serial.print("Calibrating magnetometer: rotate the zumo along all axes during calibration period ... ");
+        
+        /* illuminate LED while calibrating */
+        digitalWrite(MAIN_LED_PIN, HIGH);
+        
+        imu.calibrateMagnetometer(200);
+        Serial.println("done.");
+        
+        /* shutoff LED */
+        digitalWrite(MAIN_LED_PIN, LOW);
+    }
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/WifiLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/AssistedDrive/WifiLoop.ino
new file mode 100644 (file)
index 0000000..20dc455
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ *  ======== wifiLoop ========
+ *  This sketch starts a network and listens on port PORTNUM for
+ *  command that can control the zumo motors.
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <ti/sysbios/knl/Task.h>
+
+#include <string.h>
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-drive";
+static const char wifipw[] = "password";
+
+/* port number of the server listening for commands at 192.168.1.1 */
+#define PORTNUM 8080
+
+float driveRate;
+float driveTime;
+int angleToTurn;
+bool readyToRun;
+
+/* create data server on port PORTNUM */
+static WiFiServer server(PORTNUM);
+
+static void doWASD(char wasd, WiFiClient client);
+static int readBuf(WiFiClient client, uint8_t *buf, unsigned int len);
+
+/*
+ *  ======== wifiSetup ========
+ */
+void wifiSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: ");
+    Serial.println(ssid);
+    WiFi.beginNetwork((char *) ssid, (char *) wifipw);
+    while (WiFi.localIP() == INADDR_NONE) {
+        Serial.print(".");
+        delay(300);
+    }
+
+    /* startup the command server on port PORTNUM */
+    server.begin();
+    Serial.print("dataserver started on port ");
+    Serial.println(PORTNUM);
+    readyToRun = false;
+}
+
+/*
+ *  ======== readBuf ========
+ */
+static int readBuf(WiFiClient client, uint8_t *buf, unsigned int len)
+{
+    /* wait for len characters to arrive */
+    for (unsigned int i = 0; client.available() < len; i++) {
+        /* yield or sleep for an ever increasing period to save power */
+        i > 0 ? delay(i) : Task_yield();
+
+        if (!client.connected()) {
+            return -1;
+        }
+    }
+    client.read(buf, len);
+    return 0;
+}
+
+/*
+ *  ======== wifiLoop ========
+ */
+void wifiLoop()
+{
+    /* Did a client connect/disconnect since the last time we checked? */
+    if (WiFi.getTotalDevices() > 0) {
+
+        /* listen for incoming clients */
+        WiFiClient client = server.available();
+        if (client) {
+            readyToRun = true;
+
+            /* if there's a client, read and process commands */
+            static char buffer[64] = { 0 };
+            int bufLen = 0;
+            doWASD(' ', client);
+
+            /* while connected to the client, read commands and send results */
+            while (client.connected()) {
+
+                /* if there's a byte to read from the client .. */
+                if (client.available()) {
+                    /* copy it to the command buffer, byte at a time */
+                    char c = client.read();
+
+                    /* ignore bogus characters */
+                    if (c == '\0' || c == '\r') {
+                        continue;
+                    }
+
+                    /* never overrun the command buffer */
+                    if (bufLen >= (int) (sizeof(buffer))) {
+                        bufLen = sizeof(buffer) - 1;
+                    }
+                    buffer[bufLen++] = c;
+
+                    if (c == 'g') {
+                        uint8_t cmdBuf[2] = { 0 };
+                        if (readBuf(client, cmdBuf, sizeof(cmdBuf)) < 0) {
+                            break;
+                        }
+                        processDriveCommand(cmdBuf);
+                        doWASD('g', client);
+                    }
+                    else if (c == 't') {
+                        Serial.println("Received t, going to send IMU data");
+                        uint8_t cmdBuf[3] = { 0 };
+                        if (readBuf(client, cmdBuf, sizeof(cmdBuf)) < 0) {
+                            break;
+                        }
+                        processTurnCommand(cmdBuf);
+                        doWASD('t', client);
+                    }
+
+                    /* if there's a new line, we have a complete command */
+                    if (c == '\n') {
+                        doWASD(buffer[0], client);
+                        /* reset command buffer index to get next command */
+                        bufLen = 0;
+                    }
+                }
+            }
+
+            /* client disconnected or timed out, close the connection */
+            client.flush();
+            client.stop();
+
+            /* disconnect => implicitly stop the motor */
+            motorWASD = ' ';
+        }
+    }
+
+    /* check for new connections 2 times per second */
+    delay(500);
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void doWASD(char wasd, WiFiClient client)
+{
+    static char report[124];
+    static char lastCmd = ' ';
+    
+    if (wasd == 'z') {
+        if (lastCmd != 'z') {
+            needToZero = true;
+        }
+    }
+    lastCmd = wasd;
+
+    if (wasd != '.') {     /* set new motor command */
+        motorWASD = wasd == 'z' ? ' ' : wasd;
+    }
+
+    int angle = (int16_t)imu.getGyroYaw() * 100;
+    int error = motorStatus.error * 100;
+    int leftSpeed = motorStatus.leftSpeed * 100;
+    int rightSpeed = motorStatus.rightSpeed * 100;
+    long time = millis();
+    
+    int magX = (int16_t)(imu.getMagX() * 100);
+    int magY = (int16_t)(imu.getMagY() * 100);
+    int magZ = (int16_t)(imu.getMagZ() * 100);
+
+    /* send current IMU data */
+    int len = System_snprintf(report, sizeof(report),
+                    "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d I: %6d U: %6d %6d %6d %6d",
+                    imu.accel_x, imu.accel_y, imu.accel_z,
+                    imu.gyro_x,  imu.gyro_y,  imu.gyro_z,
+                    magX,        magY,        magZ,
+                    angle,
+                    error, leftSpeed, rightSpeed, time);
+    if (client.write((unsigned char *)report, 120) != 120) {
+        Serial.println("Error: reply failed, status != 120");
+    }
+}
+
+/*
+ *  ======== processDriveCommand ========
+ */
+static void processDriveCommand(uint8_t cmd[])
+{
+    driveRate = (int8_t)cmd[0] / 128.0f;
+    driveTime = cmd[1];
+    Serial.print("drive rate: "); Serial.println(driveRate);
+}
+
+/*
+ *  ======== processTurnCommand ========
+ */
+static void processTurnCommand(uint8_t cmd[])
+{
+    driveRate = (int8_t)cmd[0] / 128.0f;
+
+    int combined = (cmd[2] << 8) | cmd[1];
+    if (driveRate < 0) {
+        combined *= -1;
+    }
+    angleToTurn = combined;
+    Serial.print("Turn angle: "); Serial.println(angleToTurn);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/AttitudeDisplay.ino b/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/AttitudeDisplay.ino
new file mode 100644 (file)
index 0000000..905d192
--- /dev/null
@@ -0,0 +1,26 @@
+/*  ======== AttitudeDisplay.ino ========
+ *  Example that calculates the direction cosine matrix from IMU data
+ *
+ *  This example creates an access point named zumo-attitude with password
+ *  "password" and a simple command/telemetry server on port 8080.
+ *
+ *  The command server sends the IMU and DCM data to a WiFi client for 
+ *  display purposes, while listening for any motor commands.
+ *
+ *  An example of such a client are provided in the ./Processing
+ *  sub-directory: AttitudeDisplay.  This is a Processing application (which
+ *  uses the development IDE that inspired the Energia/Wiring development
+ *  environment; see https://processing.org).
+ */
+#include <IMUManager.h>
+#include <DCM.h>
+
+/* imu sketch external declarations */
+extern IMUManager imu;
+extern DCM_Handle imuDCM;
+
+/* motor sketch external declarations */
+extern char motorWASD;
+
+/* main external declarations */
+#define MAIN_LED_PIN RED_LED
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/apLoop.ino
new file mode 100644 (file)
index 0000000..86a15ae
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch starts a network and listens on port PORTNUM for
+ *  command that can control the zumo motors.
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <DCM.h>
+
+#include <ti/sysbios/knl/Task.h>
+#include <string.h>
+
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-attitude";
+static const char wifipw[] = "password";
+
+/* port number of the server listening for commands at 192.168.1.1 */
+#define PORTNUM 8080
+
+/* create data server on port PORTNUM */
+static WiFiServer server(PORTNUM);
+
+static void doWASD(char wasd);
+static void writeData(WiFiClient client);
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: "); Serial.println(ssid);
+    WiFi.beginNetwork((char *)ssid, (char *)wifipw);
+    while (WiFi.localIP() == INADDR_NONE) {
+        Serial.print(".");
+        delay(300);
+    }
+
+    /* startup the command server on port PORTNUM */
+    server.begin();
+    Serial.print("dataserver started on port "); Serial.println(PORTNUM);
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+    /* Did a client connect/disconnect since the last time we checked? */
+    if (WiFi.getTotalDevices() > 0) {
+
+        /* listen for incoming clients */
+        WiFiClient client = server.available();
+        if (client) {
+
+            /* if there's a client, read and process commands */
+            static char buffer[64] = {0};
+            int bufLen = 0;
+
+            /* while connected to the client, read commands and send results */
+            while (client.connected()) {
+              
+                /* if there's a byte to read from the client .. */
+                if (client.available()) {
+                    /* copy it to the command buffer, byte at a time */
+                    char c = client.read();
+
+                    /* ignore bogus characters */
+                    if (c == '\0' || c == '\r') continue;
+                   
+                    /* never overrun the command buffer */
+                    if (bufLen >= (int)(sizeof (buffer))) { 
+                        bufLen = sizeof (buffer) - 1;
+                    }
+                    buffer[bufLen++] = c;
+
+                    /* if there's a new line, we have a complete command */
+                    if (c == '\n') {
+                        doWASD(buffer[0]);
+                        writeData(client);
+                        /* reset command buffer index to get next command */
+                        bufLen = 0;
+                    }
+                }
+                else{
+                    //writeData(client);
+                    delay(2);
+                }
+            }
+
+            /* disconnect => implicitly stop the motor */
+            motorWASD = ' ';
+            
+            /* client disconnected or timed out, close the connection */
+            client.flush();
+            client.stop();
+        }
+    }
+
+    /* check for new connections 2 times per second */
+    delay(500);
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void doWASD(char wasd)
+{
+    /* set new motor command */
+    motorWASD = wasd;
+}
+
+/*
+ *  ======== writeData ========
+ */
+static void writeData(WiFiClient client)
+{
+    static char report[146];
+    
+    float matrix[3][3];
+    DCM_getMatrix(imuDCM, matrix);
+    
+    /* convert DCM data for ease of tranfer */
+    int matrix00 = (int16_t)(matrix[0][0] * 100);
+    int matrix01 = (int16_t)(matrix[0][1] * 100);
+    int matrix02 = (int16_t)(matrix[0][2] * 100);
+    int matrix10 = (int16_t)(matrix[1][0] * 100);
+    int matrix11 = (int16_t)(matrix[1][1] * 100);
+    int matrix12 = (int16_t)(matrix[1][2] * 100);
+    int matrix20 = (int16_t)(matrix[2][0] * 100);
+    int matrix21 = (int16_t)(matrix[2][1] * 100);
+    int matrix22 = (int16_t)(matrix[2][2] * 100);
+    
+//    /* print the DCM data to console */
+//    Serial.println("--------DCM--------");
+//    Serial.print(matrix00);Serial.print(" ");Serial.print(matrix01);Serial.print(" ");Serial.println(matrix02);
+//    Serial.print(matrix10);Serial.print(" ");Serial.print(matrix11);Serial.print(" ");Serial.println(matrix12);
+//    Serial.print(matrix20);Serial.print(" ");Serial.print(matrix21);Serial.print(" ");Serial.println(matrix22);
+//    Serial.println("-------------------");
+    
+    //Serial.println("***************************");
+    //Serial.print(imu.mag_x);Serial.print(" ");Serial.print(imu.mag_y);Serial.print(" ");Serial.println(imu.mag_z);
+    //Serial.print(imu.getMagX());Serial.print(" ");Serial.print(imu.getMagY());Serial.print(" ");Serial.println(imu.getMagZ());
+    //Serial.println("***************************");
+
+    /* send current IMU data */
+    System_snprintf(report, sizeof(report),
+      /* accelerometer, gyro,          magnetometer   DCM */
+      /* 0   1   2   3  4   5   6   7  8   9   10  11 12  13  14  15  16  17  18  19  20  21 */
+        "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d D: %6d %6d %6d %6d %6d %6d %6d %6d %6d",
+                    imu.accel_x, imu.accel_y, imu.accel_z,
+                    imu.gyro_x,  imu.gyro_y,  imu.gyro_z, 
+                    imu.mag_x,   imu.mag_y,   imu.mag_z,
+                    matrix00,    matrix01,    matrix02,
+                    matrix10,    matrix11,    matrix12,
+                    matrix20,    matrix21,    matrix22);
+    if (client.write((unsigned char *)report, 138) != 138) {
+        Serial.println("Error: reply failed, status != 138");
+    }
+ }
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/imuLoop.ino
new file mode 100644 (file)
index 0000000..e1ef13c
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+*  ======== imuLoop ========
+*  This sketch intializes the direction cosine matrix state with initial
+*  sensor readings, then continuously reads the sensors at 200 Hz and 
+*  updates the DCM based on the sensor data.
+*
+*  In addition, this sketch implements the magnetometer calibration sequence
+*  which normalizes the raw magnetometer data for use in DCM calculations. During
+*  setup, an LED will light up, indicating that the calibration sequence is running.
+*  rotating the Zumo sufficiently will complete the calibration and the LED will
+*  turn off.
+*/
+
+#include <Wire.h>
+#include <Energia.h>
+
+#include <DCM.h>
+#include <IMUManager.h>
+
+/* IMU data instance objects */
+IMUManager imu;
+DCM_Handle imuDCM;
+
+static void calibrateDCM(DCM_Handle dcm);
+static void getIMUData(DCM_Handle dcm);
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+
+    Wire.begin();
+    
+    imu = IMUManager();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imu.initAccel();
+    imu.enableAccelDefault();
+
+    /* initialize Zumo gyro */
+    if (!imu.initGyro()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imu.enableGyroDefault();
+
+    /* calibrate the gyro */
+    Serial.println("Calibrating gyro for 2 seconds: keep zumo still during calibration period");
+    imu.calibrateGyro(2);
+    imu.zeroGyroZAxis();
+    imu.zeroGyroYAxis();
+    imu.zeroGyroZAxis();
+    
+    /* create a new DCM 'object' with
+     * update time 5 ms, and scaling constants (0.03, 0.97, 0.001)
+     */
+    imuDCM = DCM_create(0.005f, 0.03f, 0.97f, 0.01f);
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* calibrate DCM filters */
+    calibrateDCM(imuDCM);
+    
+    /* update imuDCM's IMU data every 5 ms (200 Hz) */
+    getIMUData(imuDCM);
+    
+    /* re-compute imuDCM's values */
+    DCM_update(imuDCM);
+
+    delay(5);
+}
+
+/*
+ *  ======== calibrateDCM ========
+ *  Calibrate the magnetometer and initialize the DCM object (if necessary)
+ */
+static void calibrateDCM(DCM_Handle dcm)
+{
+    static bool calibrated = 0;
+    if (!calibrated) {
+        calibrated = 1;
+        
+        /* calibrate magnetometer */
+        Serial.print("Calibrating magnetometer: rotate the zumo along all axes during calibration period ... ");
+        
+        /* illuminate LED while calibrating */
+        digitalWrite(MAIN_LED_PIN, HIGH);
+        
+        imu.calibrateMagnetometer(200);
+        Serial.println("done.");
+        
+        /* shutoff LED */
+        digitalWrite(MAIN_LED_PIN, LOW);
+
+        /* get initial calibrated IMU data for dcm */
+        getIMUData(dcm);
+
+        /* reset the dcm's filters starting from initial estimates */
+        DCM_start(dcm);
+    }   
+}
+
+/*
+ *  ======== getIMUData ========
+ *  Provide a new reading of the IMU data to the DCM object
+ */
+static void getIMUData(DCM_Handle dcm)
+{
+    float gyroX, gyroY, gyroZ;
+    
+    /* read data from the sensors */
+    imu.readGyro();
+    imu.readAccel();
+    imu.readMag();
+    
+    /* convert gyro angular velocity from deg/s to rad/s  */
+    gyroX = 0.0174532925f * (imu.getGyroX());
+    gyroY = 0.0174532925f * (imu.getGyroY());
+    gyroZ = 0.0174532925f * (imu.getGyroZ());
+    
+    /* push initial IMU data into the DCM "filter" */
+    DCM_updateAccelData(dcm, imu.getAccelX(), imu.getAccelY(), imu.getAccelZ());
+    DCM_updateGyroData(dcm, gyroX, gyroY, gyroZ);
+    DCM_updateMagnetoData(dcm, imu.getMagX(), imu.getMagY(), imu.getMagZ());
+}
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/AttitudeDisplay/motorLoop.ino
new file mode 100644 (file)
index 0000000..ab0dd08
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w'  - drive forward
+ *      's'  - drive backward
+ *      'a'  - turn left
+ *      'd'  - turn right
+ *      ' '  - stop
+ *      '\0' - idle (sleep for 10 ms witout making any changes to current motor power settings)
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+
+char motorWASD = '\0';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+static int clip(int speed);
+static void drive(char wasd, int goal, unsigned int duration);
+static int next(int cur, int goal);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+    Serial.println("motorSetup done.");
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    /* state used to blink LED */
+    static unsigned count = 0;
+    static char state = 0;
+    
+    switch (motorWASD) {
+        case 's':
+        case 'w': {
+            /* illuminate LED while driving */
+            digitalWrite(MAIN_LED_PIN, HIGH);
+            drive(motorWASD, 200, PERIOD);
+            break;
+        }
+
+        case 'd':
+        case 'a': {
+            /* blink LED while turning */
+            if (count == ((count / 100) * 100)) {
+                state ^= 1;
+                digitalWrite(MAIN_LED_PIN, state);
+            }   
+            drive(motorWASD, 100, PERIOD);
+            break;
+        }
+        
+        case '\0':
+            delay(10); /* delay so that the other threads have a chance to run */  
+            break;
+            
+        default: {
+            /* turn off LED while stopped */
+            motorWASD = ' ';
+            digitalWrite(MAIN_LED_PIN, LOW);
+            drive(' ', 0, 10);
+            break;
+        }
+    }
+
+    count++;
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Balancing/Balancing.ino b/src/Energia/libraries/ZumoCC3200/examples/Balancing/Balancing.ino
new file mode 100644 (file)
index 0000000..a41f57e
--- /dev/null
@@ -0,0 +1,23 @@
+/*  ======== Balancing.ino ========
+ *  Project which implements a "Balancer" class with methods to balance 
+ *  the zumo horizontally and vertically.
+ *  
+ *  This example uses a complementary filter on the gyroscope and 
+ *  accelerometer data to form an accurate tilt angle reading, then
+ *  employs PID feedback control to regulate that angle to 0.
+ *  
+ *  This example interacts with the Processing application titled 
+ *  "Balancing", which retains most of the functionality of the 
+ *  basic graphing application but enables the user to start/stop the 
+ *  balancing loop and tune the PID gains in real-time.
+ */
+
+#include <IMUManager.h>
+#include <Balancer.h>
+
+/* imu sketch external declarations */
+extern IMUManager imu;
+
+/* motor sketch external declarations */
+extern char     motorWASD;
+extern Balancer motorBal;
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Balancing/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/Balancing/apLoop.ino
new file mode 100644 (file)
index 0000000..da61629
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch starts a network and listens on port PORTNUM for
+ *  command that can control the zumo motors.
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+#include <WiFi.h>
+#include <ti/sysbios/knl/Task.h>
+#include <string.h>
+#include <stdio.h>
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-balance";
+static const char wifipw[] = "password";
+
+/* port number of the server listening for commands at 192.168.1.1 */
+#define PORTNUM 8080
+
+/* create data server on port PORTNUM */
+static WiFiServer server(PORTNUM);
+static void doWASD(char cmd[], WiFiClient client);
+static void readBuf(WiFiClient client, uint8_t *buf, unsigned int len);
+static void readString(WiFiClient client, char *buf);
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: "); Serial.println(ssid);
+    WiFi.beginNetwork((char *)ssid, (char *)wifipw);
+    while (WiFi.localIP() == INADDR_NONE) {
+        Serial.print(".");
+        delay(300);
+    }
+
+    /* startup the command server on port PORTNUM */
+    server.begin();
+
+    Serial.print("dataserver started on port "); Serial.println(PORTNUM);
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+    /* Did a client connect/disconnect since the last time we checked? */
+    if (WiFi.getTotalDevices() > 0) {
+
+        /* listen for incoming clients */
+        WiFiClient client = server.available();
+
+        if (client) {
+
+            /* if there's a client, read and process commands */
+            static char buffer[64] = {0};
+
+            int bufLen = 0;
+
+            /* while connected to the client, read commands and send results */
+            while (client.connected()) {
+
+                /* if there's a byte to read from the client .. */
+                if (client.available()) {
+
+                    /* copy it to the command buffer, byte at a time */
+                    char c = client.read();
+
+                    /* ignore bogus characters */
+                    if (c == '\0' || c == '\r') continue;
+
+                    /* never overrun the command buffer */
+                    if (bufLen >= (int)(sizeof (buffer))) { 
+                        bufLen = sizeof (buffer) - 1;
+                    }
+
+                    buffer[bufLen++] = c;
+
+                    /* if there's a new line, we have a complete command */
+                    if (c == '\n') {
+                        /* do the command and send status */
+                        doWASD(buffer, client);
+
+                        /* reset command buffer index to get next command */
+                        bufLen = 0;
+                    }
+                }
+            }
+
+            /* client disconnected or timed out, close the connection */
+            client.flush();
+            client.stop();
+
+            /* disconnect => implicitly stop the motor */
+            motorWASD = ' ';
+        }
+    }
+
+    /* check for new connections 2 times per second */
+    delay(500);
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void doWASD(char cmd[], WiFiClient client)
+{
+    static char report[104];
+    char c = cmd[0];
+    
+    if (c == 'P') {
+        double newP = atof(cmd + 1);
+        Serial.print("new p gain: "); Serial.println(newP);
+        motorBal.verticalPID.setP(newP);
+    }
+    else if (c == 'I') {
+        double newI = atof(cmd + 1);
+        Serial.print("new i gain: "); Serial.println(newI);
+        motorBal.verticalPID.setI(newI);
+    }
+    else if (c == 'D') {
+        Serial.print("new d gain: ");
+        double newD = atof(cmd + 1);
+        Serial.print("new d gain: "); Serial.println(newD);
+        motorBal.verticalPID.setD(newD);
+    }
+    else if (c == 'w' || c == 'a' || c == 's'  || c == 'd' || c == ' '
+             || c == 'b' || c == 'B') {
+        motorWASD = c;
+    }
+    /* get current IMU data */
+    int tiltAngle = (int16_t)(imu.getFilteredTiltAngle() * 100);
+    int motorSpeed = (int16_t)motorBal.balanceStatus.motorPower;
+    int balanceError = (int16_t)(motorBal.balanceStatus.error * 100);
+    int angularVel = (int16_t)(imu.getGyroY() * 100);
+
+    /* send current IMU data */
+    int len = System_snprintf(report, sizeof(report),
+        "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d B: %6d %6d %6d",
+                    imu.accel_x, imu.accel_y, imu.accel_z,
+                    imu.gyro_x,  imu.gyro_y,  imu.gyro_z, 
+                    imu.mag_x,   imu.mag_y,   imu.mag_z, 
+                    tiltAngle,   motorSpeed,  angularVel);
+
+    if (client.write((unsigned char *)report, 96) != 96) {
+        Serial.println("Error: reply failed, status != 96");
+    }
+}
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Balancing/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/Balancing/imuLoop.ino
new file mode 100644 (file)
index 0000000..ef1b675
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch reads the Zumo IMU sensors at 20Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+/* IMU sketch global data */
+IMUManager imu;
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+
+    Wire.begin();
+    
+    imu = IMUManager();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imu.initAccel();
+    imu.enableAccelDefault();
+
+    /* initialize Zumo gyro */
+
+    if (!imu.initGyro()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imu.enableGyroDefault();
+    Serial.println("sensorSetup done.");
+    
+    imu.calibrateGyro(2);
+    imu.zeroGyroZAxis();
+    imu.zeroGyroYAxis();
+    imu.zeroGyroZAxis();
+    
+    imu.readGyro();
+    imu.readAccel();
+    imu.readMag();
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* update IMU data every 5 ms (200 Hz) */
+    imu.readGyro();
+    imu.readAccel();
+    imu.readMag();
+
+    delay(5);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Balancing/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/Balancing/motorLoop.ino
new file mode 100644 (file)
index 0000000..ce48f87
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      'b' - start balancing vertically
+ *      'B' - start balancing horizontally
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+
+#include <Energia.h>
+#include <math.h>
+
+#include <ZumoMotors.h>
+#include <Balancer.h>
+#include <IMUManager.h>
+
+#define PERIOD  0           /* period of motor control updates */
+char motorWASD = ' ';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+static void drive(char wasd, int goal, unsigned int duration);
+static int next(int cur, int goal);
+
+Balancer motorBal;
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+  
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    Serial.println("motorSetup done.");
+  
+    motorBal = Balancer();
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    switch (motorWASD) {
+    case 's':
+    case 'w': 
+      {
+        drive(motorWASD, 200, PERIOD);
+        break;
+      }
+    case 'd':
+    case 'a': 
+      {
+        drive(motorWASD, 100, PERIOD);
+        break;
+      }
+    case 'b':
+      {
+        motorBal.verticalBalance(motors, imu);
+        break;
+      }  
+    case 'B':
+      {
+        motorBal.horizontalBalance(motors, imu);
+        break;
+      }
+    default: 
+      {
+        motorWASD = ' ';
+        drive(' ', 0, 10);
+        break;
+      }
+    }
+
+    delay(5);
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/CommandExample/CommandExample.ino b/src/Energia/libraries/ZumoCC3200/examples/CommandExample/CommandExample.ino
new file mode 100644 (file)
index 0000000..c778dee
--- /dev/null
@@ -0,0 +1,28 @@
+/*  ======== CommandExample.ino ========
+ *  Simple demo of the CC3200 Zumo platform
+ *
+ *  This example creates an access point named zumo-command with password
+ *  "password" and a simple command/telemetry server on port 8080.
+ */
+
+#include <ZumoMotors.h>
+#include <Utilities.h>
+#include <IMUManager.h>
+#include <WiFiClient.h>
+
+/* imu sketch external declarations */
+extern IMUManager imu;
+extern bool isZeroing;
+extern Utilities::MotorInfo motorStatus;
+extern bool needToZero;
+extern bool readyToRun;
+
+/* motor sketch external declarations */
+extern char motorWASD;
+extern float motorInfo[4];
+
+
+/* AP sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+extern float driveRate;
+extern int angleToTurn;
diff --git a/src/Energia/libraries/ZumoCC3200/examples/CommandExample/MotorControlLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/CommandExample/MotorControlLoop.ino
new file mode 100644 (file)
index 0000000..aac92e4
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+#include <math.h>
+#include <Utilities.h>
+#include <ZumoMotors.h>
+#include <CommandManager.h>
+#include <TurnAngleCommand.h>
+#include <DriveLineCommand.h>
+#include <WaitCommand.h>
+
+#define PERIOD  0           /* period of motor control updates */
+
+char motorWASD = ' '; /* current motor drive command */
+static ZumoMotors motors; /* Zumo motor driver provided by Pololu */
+Utilities util;
+CommandManager manager;
+Command* turnCommand;
+Command* driveCommand;
+Command* waitCommand;
+Utilities::MotorInfo motorStatus;
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.begin(9600);
+    Serial.println("motorSetup ...");
+
+    /* initialize the Pololu driver motor library */
+    ZumoMotors::setRightSpeed(0);
+    ZumoMotors::setLeftSpeed(0);
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+
+    util = Utilities();
+    manager = CommandManager();
+    turnCommand = (Command*) new TurnAngleCommand(-90, 2.5f);
+    driveCommand = (Command*) new DriveLineCommand(0.5f, 3.0f);
+    waitCommand = (Command*) new WaitCommand(1.0f);
+    manager.addCommand(driveCommand);
+    manager.addCommand(waitCommand);
+    manager.addCommand(turnCommand);
+    manager.addCommand(waitCommand);
+    manager.addCommand(driveCommand);
+    manager.addCommand(waitCommand);
+    manager.addCommand(turnCommand);
+    manager.addCommand(waitCommand);
+    manager.addCommand(driveCommand);
+    manager.addCommand(waitCommand);
+    manager.addCommand(turnCommand);
+    manager.addCommand(waitCommand);
+    manager.addCommand(driveCommand);
+    motorStatus.error = 0;
+    motorStatus.leftSpeed = 0;
+    motorStatus.rightSpeed = 0;
+    motorStatus.time = 0;
+    Serial.println("motorSetup done.");
+}
+
+/*
+ *  ======== motorLoop ========
+ *
+ *  Based on user input, drive open loop or
+ *  perform closed-loop maneuvers
+ */
+void motorLoop(void)
+{
+    if (isZeroing) {
+        motors.setLeftSpeed(0);
+        motors.setRightSpeed(0);
+    }
+
+    else {
+        manager.run(motorStatus);
+    }
+    delay(25);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/CommandExample/SensorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/CommandExample/SensorLoop.ino
new file mode 100644 (file)
index 0000000..a6a41c8
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 200Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+/* Pololu IMU data instance objects */
+IMUManager imu;
+float angle = 0;
+int dc_offset = 0;
+float noise = 0;
+bool hasCalibrated = 0;
+bool needToZero = false; /* set by WiFi to recalibrate Gyro */
+bool isZeroing = true;   /* flag indicating we are in "zero'ing mode */
+
+/*
+ *  ======== sensorSetup ========
+ */
+void sensorSetup()
+{
+    Serial.begin(9600);
+    Serial.println("sensorSetup ...");
+    Wire.begin();
+    imu = IMUManager();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imu.initAccel();
+    imu.enableAccelDefault();
+
+    /* initialize Zumo gyro */
+    if (!imu.initGyro()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imu.enableGyroDefault();
+
+    isZeroing = true;
+    imu.calibrateGyro(2);
+    imu.zeroGyroXAxis();
+    imu.zeroGyroYAxis();
+    imu.zeroGyroZAxis();
+    isZeroing = false;
+
+    Serial.println("sensorSetup done.");
+}
+
+/*
+ *  ======== sensorLoop ========
+ */
+void sensorLoop()
+{
+    static int imuCount = 0;
+
+    /* update IMU data every 10 ms (100 Hz) */
+    if (needToZero) {
+        imu.zeroGyroZAxis();
+        imu.zeroGyroYAxis();
+        needToZero = false;
+    }
+
+    /* read data from all IMU sensors */
+    imu.readGyro();
+    imu.readAccel();
+    imu.readMag();
+    
+    delay(10);
+    imuCount++;
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/CommandExample/WifiLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/CommandExample/WifiLoop.ino
new file mode 100644 (file)
index 0000000..a31c069
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *  ======== wifiLoop ========
+ *  This sketch starts a network and listens on port PORTNUM for
+ *  command that can control the zumo motors.
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <ti/sysbios/knl/Task.h>
+
+#include <string.h>
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-command";
+static const char wifipw[] = "password";
+
+/* port number of the server listening for commands at 192.168.1.1 */
+#define PORTNUM 8080
+
+float driveRate;
+int angleToTurn;
+char lastCmd;
+bool readyToRun;
+
+/* create data server on port PORTNUM */
+static WiFiServer server(PORTNUM);
+
+static void doWASD(char wasd, WiFiClient client);
+
+/*
+ *  ======== wifiSetup ========
+ */
+void wifiSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: ");
+    Serial.println(ssid);
+    WiFi.beginNetwork((char *) ssid, (char *) wifipw);
+    while (WiFi.localIP() == INADDR_NONE) {
+        Serial.print(".");
+        delay(300);
+    }
+
+    /* startup the command server on port PORTNUM */
+    server.begin();
+    Serial.print("dataserver started on port ");
+    Serial.println(PORTNUM);
+    readyToRun = false;
+}
+
+static int readBuf(WiFiClient client, uint8_t *buf, unsigned int len)
+{
+    /* wait for len characters to arrive */
+    for (unsigned int i = 0; client.available() < len; i++) {
+        /* yield or sleep for an ever increasing period to save power */
+        i > 0 ? delay(i) : Task_yield();
+
+        if (!client.connected()) {
+            return -1;
+        }
+    }
+    client.read(buf, len);
+    return 0;
+}
+
+/*
+ *  ======== wifiLoop ========
+ */
+void wifiLoop()
+{
+    /* Did a client connect/disconnect since the last time we checked? */
+    if (WiFi.getTotalDevices() > 0) {
+
+        /* listen for incoming clients */
+        WiFiClient client = server.available();
+        if (client) {
+            readyToRun = true;
+
+            /* if there's a client, read and process commands */
+            static char buffer[64] = { 0 };
+            int bufLen = 0;
+            doWASD(' ', client);
+
+            /* while connected to the client, read commands and send results */
+            while (client.connected()) {
+
+                /* if there's a byte to read from the client .. */
+                if (client.available()) {
+                    /* copy it to the command buffer, byte at a time */
+                    char c = client.read();
+
+                    /* ignore bogus characters */
+                    if (c == '\0' || c == '\r') {
+                        continue;
+                    }
+
+                    /* never overrun the command buffer */
+                    if (bufLen >= (int) (sizeof(buffer))) {
+                        bufLen = sizeof(buffer) - 1;
+                    }
+                    buffer[bufLen++] = c;
+
+                    if (c == 'g') {
+                        uint8_t cmdBuf[2] = { 0 };
+                        if (readBuf(client, cmdBuf, sizeof(cmdBuf)) < 0) {
+                            break;
+                        }
+                        processDriveCommand(cmdBuf);
+                        doWASD('g', client);
+                    }
+                    else if (c == 't') {
+                        Serial.println("Received t, going to send IMU data");
+                        uint8_t cmdBuf[3] = { 0 };
+                        if (readBuf(client, cmdBuf, sizeof(cmdBuf)) < 0) {
+                            break;
+                        }
+                        processTurnCommand(cmdBuf);
+                        doWASD('t', client);
+                    }
+                    /* if there's a new line, we have a complete command */
+                    if (c == '\n') {
+                        doWASD(buffer[0], client);
+                        /* reset command buffer index to get next command */
+                        bufLen = 0;
+                    }
+                }
+            }
+
+            /* client disconnected or timed out, close the connection */
+            client.flush();
+            client.stop();
+
+            /* disconnect => implicitly stop the motor */
+            motorWASD = ' ';
+        }
+    }
+
+    /* check for new connections 2 times per second */
+    delay(500);
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void doWASD(char wasd, WiFiClient client)
+{
+    static char report[124];
+
+    if (wasd == 'z' && lastCmd != 'z') {
+        //Serial.println("Z received");
+        needToZero = true;
+    }
+    else {     /* set new motor command */
+        motorWASD = wasd;
+    }
+    lastCmd = wasd;
+
+    int angle = (int16_t) imu.getGyroYaw() * 100;
+    int error = motorStatus.error * 100;
+    int leftSpeed = motorStatus.leftSpeed * 100;
+    int rightSpeed = motorStatus.rightSpeed * 100;
+    long time = millis();
+
+    /* send current IMU data */
+    int len = System_snprintf(report, sizeof(report),
+                    "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d I: %6d U: %6d %6d %6d %6d",
+                    imu.accel_x, imu.accel_y, imu.accel_z,
+                    imu.gyro_x,  imu.gyro_y,  imu.gyro_z,
+                    imu.mag_x,   imu.mag_y,   imu.mag_z,
+                    angle,
+                    error, leftSpeed, rightSpeed, time);
+    if (client.write((unsigned char *)report, 120) != 120) {
+        Serial.println("Error: reply failed, status != 120");
+    }
+}
+
+static void processDriveCommand(uint8_t cmd[])
+{
+    float rate = (cmd[1] / 100.0f);
+    driveRate = rate;
+    Serial.print("driveRate: "); Serial.println(driveRate);
+}
+
+static void processTurnCommand(uint8_t cmd[])
+{
+    int combined = (cmd[2] << 8) | cmd[1];
+    if (cmd[0]) {
+        combined *= -1;
+    }
+    angleToTurn = combined;
+    Serial.print("Turn angle: "); Serial.println(angleToTurn);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/ManualDrive.ino b/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/ManualDrive.ino
new file mode 100644 (file)
index 0000000..20fe4dd
--- /dev/null
@@ -0,0 +1,30 @@
+/*  ======== ManualDrive.ino ========
+ *  Simple demo of the CC3200 Zumo platform's manual drive
+ *
+ *  This example creates an access point named zumo-manual with password
+ *  "password" and a simple command/telemetry server on port 8080.
+ *
+ *  The command server allows any WiFi client to drive the Zumo using WASD
+ *  keystrokes and acquire IMU telemetry data for real-time display.
+ *
+ *  Two examples of such a client are provided in the src/Processing
+ *  sub-directory of git@gitorious.design.ti.com:sb/zumo.git: zgraph and
+ *  zecho.  Both are Processing applications (the development IDE that
+ *  originally inspired the Energia/Wiring development environment; see
+ *  https://processing.org).
+ */
+
+#include <ZumoCC3200.h>
+#include <L3G.h>
+#include <LSM303.h>
+
+/* imu sketch external declarations */
+extern LSM303 imuCompass; /* acceleration and magnetometer */
+extern L3G    imuGyro;    /* gyro data */
+
+/* motor sketch external declarations */
+extern char motorWASD;
+
+/* ap sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/apLoop.ino
new file mode 100644 (file)
index 0000000..5b9747a
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch starts a network and listens on port PORTNUM for
+ *  command that can control the zumo motors.
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <ti/sysbios/knl/Task.h>
+
+#include <string.h>
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-manual";
+static const char wifipw[] = "password";
+
+/* port number of the server listening for commands at 192.168.1.1 */
+#define PORTNUM 8080
+
+/* create data server on port PORTNUM */
+static WiFiServer server(PORTNUM);
+
+static void doWASD(char wasd, WiFiClient client);
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: "); Serial.println(ssid);
+    WiFi.beginNetwork((char *)ssid, (char *)wifipw);
+    while (WiFi.localIP() == INADDR_NONE) {
+        Serial.print(".");
+        delay(300);
+    }
+
+    /* startup the command server on port PORTNUM */
+    server.begin();
+    Serial.print("dataserver started on port "); Serial.println(PORTNUM);
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+    /* Did a client connect/disconnect since the last time we checked? */
+    if (WiFi.getTotalDevices() > 0) {
+
+        /* listen for incoming clients */
+        WiFiClient client = server.available();
+        if (client) {
+
+            /* if there's a client, read and process commands */
+            static char buffer[64] = {0};
+            int bufLen = 0;
+
+            /* while connected to the client, read commands and send results */
+            while (client.connected()) {
+                /* if there's a byte to read from the client .. */
+                if (client.available()) {
+                    /* copy it to the command buffer, byte at a time */
+                    char c = client.read();
+
+                    /* ignore bogus characters */
+                    if (c == '\0' || c == '\r') continue;
+                    
+                    /* never overrun the command buffer */
+                    if (bufLen >= (int)(sizeof (buffer))) { 
+                        bufLen = sizeof (buffer) - 1;
+                    }
+                    buffer[bufLen++] = c;
+
+                    /* if there's a new line, we have a complete command */
+                    if (c == '\n') {
+                        doWASD(buffer[0], client);
+                        /* reset command buffer index to get next command */
+                        bufLen = 0;
+                    }
+                }
+            }
+
+            /* client disconnected or timed out, close the connection */
+            client.flush();
+            client.stop();
+
+            /* disconnect => implicitly stop the motor */
+            motorWASD = ' ';
+        }
+    }
+
+    /* check for new connections 2 times per second */
+    delay(500);
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void doWASD(char wasd, WiFiClient client)
+{
+    static char report[80];
+
+    /* set new motor command */
+    motorWASD = wasd;
+
+    /* send current IMU data */
+    System_snprintf(report, sizeof(report),
+        "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d",
+                    imuCompass.a.x, imuCompass.a.y, imuCompass.a.z,
+                    imuGyro.g.x,    imuGyro.g.y,    imuGyro.g.z, 
+                    imuCompass.m.x, imuCompass.m.y, imuCompass.m.z);
+    if (client.write((unsigned char *)report, 72) != 72) {
+        Serial.println("Error: reply failed, status != 72");
+    }
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/imuLoop.ino
new file mode 100644 (file)
index 0000000..be55408
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 20Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+#include <L3G.h>
+#include <LSM303.h>
+
+/* Pololu IMU data instance objects */
+LSM303 imuCompass; /* acceleration and magnetometer */
+L3G    imuGyro;    /* gyro data */
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+    Wire.begin();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imuCompass.init();
+    imuCompass.enableDefault();
+
+    /* initialize Zumo gyro */
+    if (!imuGyro.init()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imuGyro.enableDefault();
+    Serial.println("imuSetup done.");
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* update IMU data every 50 ms (200 Hz) */
+    imuGyro.read();
+    imuCompass.read();
+    delay(50);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/ManualDrive/motorLoop.ino
new file mode 100644 (file)
index 0000000..69b2dd1
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+
+char motorWASD = ' ';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+static int clip(int speed);
+static void drive(char wasd, int goal, unsigned int duration);
+static int next(int cur, int goal);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+    Serial.println("motorSetup done.");
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    /* state used to blink LED */
+    static unsigned count = 0;
+    static char state = 0;
+    
+    switch (motorWASD) {
+        case 's':
+        case 'w': {
+            /* illuminate LED while driving */
+            digitalWrite(MAIN_LED_PIN, HIGH);
+            drive(motorWASD, 200, PERIOD);
+            break;
+        }
+
+        case 'd':
+        case 'a': {
+            /* blink LED while turning */
+            if (count == ((count / 100) * 100)) {
+                state ^= 1;
+                digitalWrite(MAIN_LED_PIN, state);
+            }   
+            drive(motorWASD, 100, PERIOD);
+            break;
+        }
+
+        default: {
+            /* turn off LED while stopped */
+            motorWASD = ' ';
+            digitalWrite(MAIN_LED_PIN, LOW);
+            drive(' ', 0, 10);
+            break;
+        }
+    }
+
+    count++;
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/MasterSlave.ino b/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/MasterSlave.ino
new file mode 100644 (file)
index 0000000..9e0c952
--- /dev/null
@@ -0,0 +1,30 @@
+/*  ======== MasterSlave.ino ========
+ *  Simple demo of the CC3200 Zumo platform basics
+ *
+ *  This example creates an access point named zumo_group with password
+ *  "password" and a simple command/telemetry server on port 8080.
+ *
+ *  The command server allows any WiFi client to drive the Zumo using WASD
+ *  keystrokes and acquire IMU telemetry data for real-time display.
+ *
+ *  Two examples of such a client are provided in the src/Processing
+ *  sub-directory of git@gitorious.design.ti.com:sb/zumo.git: zgraph and
+ *  zecho.  Both are Processing applications (the development IDE that
+ *  originally inspired the Energia/Wiring development environment; see
+ *  https://processing.org).
+ */
+
+#include <ZumoCC3200.h>
+#include <L3G.h>
+#include <LSM303.h>
+
+/* imu sketch external declarations */
+extern LSM303 imuCompass; /* acceleration and magnetometer */
+extern L3G    imuGyro;    /* gyro data */
+
+/* motor sketch external declarations */
+extern char motorWASD;
+
+/* ap sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/apLoop.ino
new file mode 100644 (file)
index 0000000..46e1c20
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch waits on either a button press or a 
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <Pushbutton.h>
+#include <ti/sysbios/knl/Task.h>
+
+#include <string.h>
+
+/* name of the network and its password */
+char ssid[] = "zumo_group";
+char wifipw[] = "password";
+
+/* zumo command port */
+#define CMD_PORT 8080
+
+/* IMU data port */
+#define DATA_PORT 6000
+
+/* Create a Pushbutton object for pin 12 (the Zumo user pushbutton pin) */
+Pushbutton button(ZUMO_BUTTON);
+
+IPAddress broadcastIP(192,168,1,255);
+
+
+static bool waiting = true;
+
+/* 1 is master, 0 is slave, -1 is unassigned */
+int rank = -1;
+
+static void sendIMUPacket(WiFiUDP Udp, IPAddress ip, int localPort);
+
+//WiFiUDP UdpRX; /* UDP connection for receiving */ 
+//WiFiUDP UdpTX; /* UDP connection for transmitting */
+
+WiFiUDP Udp;
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    //Task_setPri(Task_self(), 1);
+    
+    // attempt to connect to group network:
+    Serial.print("Attempting to connect to Network named: ");
+    Serial.println(ssid); 
+    WiFi.begin(ssid, wifipw);
+    
+    while (waiting) {
+      /* if group network is found */
+      if(WiFi.status() == WL_CONNECTED){
+         Serial.println("Connected to the group network");
+         Serial.println("Waiting for an ip address");
+  
+         while (WiFi.localIP() == INADDR_NONE) {
+           // print dots while we wait for an ip addresss
+           Serial.print(".");
+           delay(300);
+         }
+         
+         Serial.println("\nIP Address obtained");
+         printWifiStatus();
+         
+         /* this zumo is a slave */
+         rank = 0;
+         
+         waiting = false;
+      }
+      /* if zumo button is pressed */
+      else if(button.getSingleDebouncedRelease()){
+         Serial.println("Button press detected");
+         /* startup a new network and get the first IP address: 192.168.1.1 */
+         Serial.print("Starting a new network: "); Serial.println(ssid);
+         WiFi.beginNetwork((char *)ssid, (char *)wifipw);
+         while (WiFi.localIP() == INADDR_NONE) {
+            Serial.print(".");
+            delay(300);
+         }
+         
+         /* this zumo is the master */
+         rank = 1;
+         
+         waiting = false;
+      }
+      else{
+         WiFi.begin(ssid, wifipw);
+         Serial.print(".");
+         delay(300);
+      }
+    }
+    
+    Udp.begin(CMD_PORT);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+  int packetSize = 0;
+  
+  switch(rank){
+      case -1: /* unassigned case: should theoretically never happen */
+        Serial.println("rank was not assigned, please exit the program and try again");
+        break;
+        
+      case 0: /* slave case: listen for motor commands from group network */
+        // if there's data available, read a packet
+        packetSize = Udp.parsePacket();
+        if (packetSize){
+           static char buffer[64] = {0}; 
+          
+           Serial.print("Received packet from ");
+           IPAddress remoteIp = Udp.remoteIP();
+           Serial.print(remoteIp);
+           Serial.print(", port ");
+           Serial.println(Udp.remotePort());
+        
+           /* if packet is from master */
+           if(Udp.remotePort() == CMD_PORT){
+             // read the packet into packetBufffer
+             int len = Udp.read(buffer, 64);
+             
+             if (len > 0) buffer[len] = 0;
+             Serial.print("Contents: ");
+             Serial.println(buffer);
+             
+             motorWASD = buffer[0];  
+           }  
+           
+        }      
+        break;
+        
+      case 1: /* master case: listen for motor commands from processing, and multicast them over the group network */
+        // if there's data available, read a packet
+        packetSize = Udp.parsePacket();
+        if (packetSize){
+           static char buffer[64] = {0}; 
+          
+           Serial.print("Received packet from ");
+           IPAddress remoteIp = Udp.remoteIP();
+           Serial.print(remoteIp);
+           Serial.print(", port ");
+           Serial.println(Udp.remotePort());
+           
+           /* if packet is from processing */
+           if(Udp.remotePort() == DATA_PORT){
+             // read the packet into packetBufffer
+             int len = Udp.read(buffer, 64);
+             
+             if (len > 0) buffer[len] = 0;
+             Serial.print("Contents: ");
+             Serial.println(buffer);
+             
+             motorWASD = buffer[0];  
+             
+             /* broadcast the command */
+             Udp.beginPacket(broadcastIP, CMD_PORT);
+             Udp.write(motorWASD);
+             Udp.endPacket();
+           }
+        }
+        /* broadcast the command */
+        Udp.beginPacket(broadcastIP, CMD_PORT);
+        Udp.write("hello slaves");
+        Udp.endPacket();
+        break;
+  }
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void sendIMUPacket(WiFiUDP Udp, IPAddress ip, int localPort)
+{
+    static char report[80];
+
+    /* send current IMU data */
+    System_snprintf(report, sizeof(report),
+        "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d",
+        imuCompass.a.x, imuCompass.a.y, imuCompass.a.z,
+        imuGyro.g.x,    imuGyro.g.y,    imuGyro.g.z, 
+        imuCompass.m.x, imuCompass.m.y, imuCompass.m.z);
+    
+    Udp.beginPacket(ip, localPort);
+    Udp.write(report);
+    Udp.endPacket();
+}
+
+void printWifiStatus() {
+  // print the SSID of the network you're attached to:
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi IP address:
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print the received signal strength:
+  long rssi = WiFi.RSSI();
+  Serial.print("signal strength (RSSI):");
+  Serial.print(rssi);
+  Serial.println(" dBm");
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/imuLoop.ino
new file mode 100644 (file)
index 0000000..be55408
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 20Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+#include <L3G.h>
+#include <LSM303.h>
+
+/* Pololu IMU data instance objects */
+LSM303 imuCompass; /* acceleration and magnetometer */
+L3G    imuGyro;    /* gyro data */
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+    Wire.begin();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imuCompass.init();
+    imuCompass.enableDefault();
+
+    /* initialize Zumo gyro */
+    if (!imuGyro.init()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imuGyro.enableDefault();
+    Serial.println("imuSetup done.");
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* update IMU data every 50 ms (200 Hz) */
+    imuGyro.read();
+    imuCompass.read();
+    delay(50);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/MasterSlave/motorLoop.ino
new file mode 100644 (file)
index 0000000..e7705e1
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+
+char motorWASD = ' ';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+static int clip(int speed);
+static void drive(char wasd, int goal, unsigned int duration);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+    Serial.println("motorSetup done.");
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    /* state used to blink LED */
+    static unsigned count = 0;
+    static char state = 0;
+    
+    switch (motorWASD) {
+        case 's':
+        case 'w': {
+            /* illuminate LED while driving */
+            digitalWrite(MAIN_LED_PIN, HIGH);
+            drive(motorWASD, 200, PERIOD);
+            break;
+        }
+
+        case 'd':
+        case 'a': {
+            /* blink LED while turning */
+            if (count == ((count / 100) * 100)) {
+                state ^= 1;
+                digitalWrite(MAIN_LED_PIN, state);
+            }   
+            drive(motorWASD, 100, PERIOD);
+            break;
+        }
+
+        default: {
+            /* turn off LED while stopped */
+            motorWASD = ' ';
+            digitalWrite(MAIN_LED_PIN, LOW);
+            drive(' ', 0, 10);
+            break;
+        }
+    }
+
+    count++;
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Multicast/MCP.cpp b/src/Energia/libraries/ZumoCC3200/examples/Multicast/MCP.cpp
new file mode 100644 (file)
index 0000000..ae3afb9
--- /dev/null
@@ -0,0 +1,25 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "MCP.h"
+#include <WiFi.h>
+
+/* multicast address and port */
+IPAddress MC_IP(226, 1, 1, 1);
+int MC_PORT = 1717;
+
+WiFiUDP Udp;
+
+void MCP_init()
+{
+    Udp.begin(MC_PORT);    
+}
+
+int MCP_printf(char *message)
+{
+    Udp.beginPacket(MC_IP, MC_PORT);
+    Udp.write(message);
+    Udp.endPacket();
+}
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Multicast/MCP.h b/src/Energia/libraries/ZumoCC3200/examples/Multicast/MCP.h
new file mode 100644 (file)
index 0000000..804659c
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef MCP_h
+#define MCP_h
+
+extern void MCP_init();
+
+extern int MCP_printf(char *message);
+
+#endif /* MCP_h */
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Multicast/Multicast.ino b/src/Energia/libraries/ZumoCC3200/examples/Multicast/Multicast.ino
new file mode 100644 (file)
index 0000000..ccd053f
--- /dev/null
@@ -0,0 +1,22 @@
+/*  ======== Multicast.ino ========
+ *  Simple demo of the CC3200 Zumo platform basics
+ *
+ *  This example creates an access point named zumo-multicast with password
+ *  "password" and a simple command/telemetry server on port 8080.
+ */
+
+#include <ZumoCC3200.h>
+#include <L3G.h>
+#include <LSM303.h>
+
+/* imu sketch external declarations */
+extern LSM303 imuCompass; /* acceleration and magnetometer */
+extern L3G    imuGyro;    /* gyro data */
+
+/* motor sketch external declarations */
+extern char motorWASD;
+
+/* ap sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+extern long signalStrength;
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Multicast/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/Multicast/apLoop.ino
new file mode 100644 (file)
index 0000000..c0174d2
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch waits on either a button press or a 
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <Pushbutton.h>
+#include <ti/sysbios/knl/Task.h>
+#include "MCP.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* name of the network and its password */
+char ssid[] = "zumo-multicast";
+char wifipw[] = "password";
+
+/* shared network */
+char netID[] = "TINK-NET";
+
+/* zumo command port */
+#define CMD_PORT 8080
+
+/* IMU data port */
+#define DATA_PORT 6000
+
+IPAddress broadcastIP(192,168,1,255);
+
+long signalStrength = 0;
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    //Task_setPri(Task_self(), 1);
+    
+    // attempt to connect to Wifi network:
+    Serial.print("Connecting to target: ");
+    // print the network name (SSID);
+    Serial.println(netID); 
+    // Connect to target zumo
+    //WiFi.begin(ssid, wifipw);
+    WiFi.begin(netID);
+    while ( WiFi.status() != WL_CONNECTED) {
+      // print dots while we wait to connect
+      Serial.print(".");
+      delay(300);
+    }
+    
+    Serial.println("Connected");
+    Serial.println("Waiting for an ip address");
+    
+    while (WiFi.localIP() == INADDR_NONE) {
+      // print dots while we wait for an ip addresss
+      Serial.print(".");
+      delay(300);
+    }
+  
+    Serial.println("\nIP Address obtained");
+    printWifiStatus();
+    
+    MCP_init();
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{  
+    signalStrength = WiFi.RSSI();
+    Serial.println(signalStrength);
+    static char buf[16] = {0};
+    snprintf(buf, sizeof(buf), "%d", signalStrength);
+    MCP_printf(buf); 
+
+    delay(1000);    
+}
+
+void printWifiStatus() {
+    // print the SSID of the network you're attached to:
+    Serial.print("SSID: ");
+    Serial.println(WiFi.SSID());
+
+    // print your WiFi IP address:
+    IPAddress ip = WiFi.localIP();
+    Serial.print("IP Address: ");
+    Serial.println(ip);
+
+    // print the received signal strength:
+    long rssi = WiFi.RSSI();
+    Serial.print("signal strength (RSSI):");
+    Serial.print(rssi);
+    Serial.println(" dBm");
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Multicast/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/Multicast/imuLoop.ino
new file mode 100644 (file)
index 0000000..be55408
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 20Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+#include <L3G.h>
+#include <LSM303.h>
+
+/* Pololu IMU data instance objects */
+LSM303 imuCompass; /* acceleration and magnetometer */
+L3G    imuGyro;    /* gyro data */
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+    Wire.begin();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imuCompass.init();
+    imuCompass.enableDefault();
+
+    /* initialize Zumo gyro */
+    if (!imuGyro.init()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imuGyro.enableDefault();
+    Serial.println("imuSetup done.");
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* update IMU data every 50 ms (200 Hz) */
+    imuGyro.read();
+    imuCompass.read();
+    delay(50);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/Multicast/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/Multicast/motorLoop.ino
new file mode 100644 (file)
index 0000000..69b2dd1
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+
+char motorWASD = ' ';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+static int clip(int speed);
+static void drive(char wasd, int goal, unsigned int duration);
+static int next(int cur, int goal);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+    Serial.println("motorSetup done.");
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    /* state used to blink LED */
+    static unsigned count = 0;
+    static char state = 0;
+    
+    switch (motorWASD) {
+        case 's':
+        case 'w': {
+            /* illuminate LED while driving */
+            digitalWrite(MAIN_LED_PIN, HIGH);
+            drive(motorWASD, 200, PERIOD);
+            break;
+        }
+
+        case 'd':
+        case 'a': {
+            /* blink LED while turning */
+            if (count == ((count / 100) * 100)) {
+                state ^= 1;
+                digitalWrite(MAIN_LED_PIN, state);
+            }   
+            drive(motorWASD, 100, PERIOD);
+            break;
+        }
+
+        default: {
+            /* turn off LED while stopped */
+            motorWASD = ' ';
+            digitalWrite(MAIN_LED_PIN, LOW);
+            drive(' ', 0, 10);
+            break;
+        }
+    }
+
+    count++;
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/MotionPlanner.cpp b/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/MotionPlanner.cpp
new file mode 100644 (file)
index 0000000..25a0f27
--- /dev/null
@@ -0,0 +1,301 @@
+#include <vector>
+#include <cassert>
+#include <math.h>
+
+#include "MotionPlanner.h"
+
+#include <ZumoMotors.h>
+#include <PIDController.h>
+#include <IMUManager.h>
+#include <vector>
+#include <spline.h>
+
+MotionPlanner::MotionPlanner()
+{
+    dataReceived  = false;
+    trajCalculated = false;
+    iteration = 0;
+}
+
+/*
+ *  ======== setPoints ========
+ *  update the list of points and reset the trajectory and iteration
+ *
+ */
+void MotionPlanner::setPoints(std::vector<int> inputPoints)
+{ 
+    /* ensure that input set has equal number of x and y values */
+    assert(inputPoints.size() % 2 == 0);
+  
+    iteration = 0;
+    points.clear();
+    trajectory.clear();
+  
+    dataReceived = true;
+  
+    /* convert each pair of entries of inputPoints into a Point and add it to
+     * points
+     */
+    for (int i = 0; i < inputPoints.size(); i += 2) {
+        MotionPlanner::Point newPoint = {inputPoints[i], inputPoints[i+1]};
+        points.push_back(newPoint);
+    }
+}
+
+/*
+ *  ======== generateSpline ========
+ *  Generate a single spline and calculate the corresponding trajectory
+ *
+ *  Since length is determined by iterations (which translates to drive time),
+ *  this algorithm treats each segment of the spline as equal "length" since it
+ *  allocates an equal number of iterations to the trajectory vector for each
+ *  segment, even if the actual distance between segments is different.
+ */
+void MotionPlanner::generateSpline()
+{
+    /* if points vector contains points */
+    if (dataReceived) {
+        /* manipulate data and feed it into the spline class */
+        int length = points.size(); //number of points
+    
+        int orderedX[length], orderedY[length]; /* x and y points in order of trajectory */
+    
+        for (int i = 0; i < length; i++) {
+            orderedX[i] = points[i].x;
+            orderedY[i] = points[i].y;
+        }
+    
+        int sortedX[length], sortedY[length]; /* x is sorted, y corresponds to x */
+    
+        for (int i = 0; i < length; i++) {
+            sortedX[i] = orderedX[i];
+            sortedY[i] = orderedY[i];  
+        }
+    
+        /* sort the X and Y data sets */
+        slowSort(sortedX, sortedY, length);
+
+        std::vector<double> X(length), Y(length); /* inputs to the spline */
+        for (int i =0; i < length; i++) {
+            X[i] = (double) sortedX[i];
+            Y[i] = (double) sortedY[i];  
+        }
+    
+        /* create the spline */
+        tk::spline s;
+        s.set_points(X, Y);
+    
+        /* use spline to calculate a series of desired headings */
+    
+        /* for each segment of the spline */
+        for (int i = 0; i < (length-1); i++) { /* numbers of segments = number of points - 1 */
+      
+            /* split the segment into *resolution* number of intervals */
+            double interval = (orderedX[i+1]-orderedX[i])/((double) RESOLUTION);
+      
+            /* poll for the discrete time rate of change at evenly spaced intervals */
+            for (int j = 0; j < RESOLUTION; j++) { 
+                Serial.print(orderedX[i]+j*interval);Serial.print(", ");Serial.println(s(orderedX[i]+j*interval));
+        
+                double slope = (s(orderedX[i]+(j+1)*interval)-s(orderedX[i]+j*interval))/interval;
+                double heading = Utilities::toDegrees(atan2(s(orderedX[i]+(j+1)*interval)-s(orderedX[i]+j*interval),interval));
+                trajectory.push_back(Utilities::wrapAngle(heading-90));
+            }
+            Serial.println("");
+        }
+        Serial.print("trajectory data points: ");Serial.println(trajectory.size());
+        trajCalculated = true;
+    }
+    else {
+        Serial.println("no data to generate spline with"); 
+    }
+}
+
+/*
+ *  ======== followSpline ========
+ *  Drive the zumo along the calculated trajectory  
+ */
+void MotionPlanner::followSpline(ZumoMotors motors, PIDController pid, double rate)
+{    
+    if (iteration < trajectory.size()) {
+        double error = Utilities::wrapAngle(IMUManager::getGyroYaw() - trajectory[iteration]); 
+        Serial.print(trajectory[iteration]);Serial.print(", ");Serial.print(IMUManager::getGyroYaw());Serial.print(", ");Serial.println(iteration);
+        float power = pid.calculate(error);
+        power = Utilities::saturate(power, rate - 1.0, 1.0 - rate);
+       
+        float lTotal = Utilities::saturate((rate + power) * 400, -400, 400);
+        float rTotal = Utilities::saturate((rate - power) * 400, -400, 400);
+     
+        motors.setLeftSpeed(lTotal);
+        motors.setRightSpeed(rTotal); 
+        iteration++;
+    }
+    else {
+        motors.setLeftSpeed(0);
+        motors.setRightSpeed(0); 
+    }
+    delay(1);
+}
+
+/*
+ *  ======== intervalInterpolate ========
+ *  Given an interval and two tangents, create a spline
+ *
+ *  leftPoint.x < rightPoint.x
+ */
+tk::spline MotionPlanner::intervalInterpolate(
+    MotionPlanner::Point leftPoint,
+    MotionPlanner::Point rightPoint, 
+    double leftTangent, double rightTangent)
+{
+}
+
+/*
+ *  ======== generateTrajectory ========
+ *  Calculate a spline for each interval between control points
+ */
+void MotionPlanner::generateTrajectory()
+{
+    std::vector<tk::spline> splineChain;
+  
+    for (int i = 0; i < (points.size()-1); i++) {
+        double t1, t2;
+     
+        tk::spline s;
+     
+        /* calculate tangents using a finite three-point difference */
+     
+        /* calculate Catmull-Rom spline tangents */
+     
+        if (points[i].x < points[i+1].x) {
+            s = intervalInterpolate(points[i], points[i+1], t1, t2); 
+            splineChain.push_back(s);
+        }
+        else if (points[i].x > points[i+1].x) {
+            s = intervalInterpolate(points[i+1], points[i], t2, t1); 
+            splineChain.push_back(s);
+        }
+        else {
+            /* x values are equal */
+        }
+     
+        double segmentLen = dist(points[i], points[i+1]);
+     
+        /* conversion: 800 pixels = 8 ft = 800 iterations 
+           1 pixel = 1 iteration */
+    }
+}
+
+
+/*
+ *  ======== generateParameterizedSpline ========
+ *  Calculate a spline for both X and Y data sets to create a trajectory
+ *
+ *  Parameterize the X and Y data sets according to a approximated distance
+ *  parameter t.
+ */
+void MotionPlanner::generateParameterizedSpline()
+{
+    /* if points vector contains points */
+    if (dataReceived) {
+        /* manipulate data and feed it into the spline class */
+        int length = points.size(); /* number of points */
+
+        std::vector<double> X(length), Y(length); /* X and Y coordinates vectors */
+
+        /* initialize the vectors */
+        for (int i = 0; i < length; i++) {
+            X[i] = (double) points[i].x;
+            Y[i] = (double) points[i].y;  
+        }
+
+        std::vector<double> t(length); /* vector of parameter t values, depende data set to X and Y splines */
+        double splineLength = 0.0; /* total length, sum of individual segment length */
+  
+        /* approximate the unitized arc length of each segment of the trajectory in order to assign 
+           the appropriate distance parameter value t to each point */
+
+        t[0] = 0.0; /* first value is always 0 */
+
+        for (int i = 1; i < length; i++) {
+
+            /* linear apporximation of the segment length */
+            double segLen = (double) dist(points[i-1], points[i]);
+            splineLength += segLen;
+      
+            /* i-th t parameter is the current spline length at the end of the i-th segment */
+            t[i] = splineLength;
+        }
+    
+    
+        /* create the X vs. t spline */
+        tk::spline xS;
+        xS.set_points(t, X);
+
+        /* create the Y vs. t spline */
+        tk::spline yS;
+        yS.set_points(t, Y);
+    
+        /* use splines to calculate a series of desired headings */
+    
+        /* for every 4 pixels of spline length, add a heading data point to the trajectory vector
+           this way, we end up with trajectory vector whose size corrsponds to the length of the spline */
+        for (double pos = 0.0; pos < splineLength; pos += INTERVAL_LENGTH) { 
+
+            /* print out the interpolated coordinate */
+            //Serial.print(xS(pos));Serial.print(", ");Serial.println(yS(pos));
+        
+            /* use trig to calculate the angle formed by the discrete rate of change vector WRTO normal */ 
+            double heading = Utilities::toDegrees(atan2(yS(pos) - yS(pos - INTERVAL_LENGTH), xS(pos) - xS(pos - INTERVAL_LENGTH)));
+
+            /* shift the heading 90 degrees counter-clockwise so that 0 is facing forward for the zumo */
+            trajectory.push_back(Utilities::wrapAngle(heading - 90));
+      
+        }
+        Serial.print("trajectory data points: ");Serial.println(trajectory.size());
+        trajCalculated = true;
+    }
+    else {
+        Serial.println("no data to generate spline with"); 
+    }
+}
+
+bool MotionPlanner::ready()
+{
+    return dataReceived && trajCalculated;
+}
+
+/*
+ *  ======== slowSort ========
+ *  Used to sort data before creating spline
+ *
+ *  Sorts vector X into increasing order, and reorders Y's elements to 
+ *  correspond to the sorted X
+ */
+void MotionPlanner::slowSort(int X[], int Y[], int length)
+{
+    int tempX[length];
+    for (int i = 0; i < length; i++) {
+        tempX[i] = X[i];
+    }
+    int tempY[length];
+    for (int i = 0; i < length; i++) {
+        tempY[i] = Y[i];
+    }
+    for (int i = 0; i < length; i++) {
+        int indexOfMin = 0;
+        for (int j = 0; j < length; j++) {
+            if (tempX[j] < tempX[indexOfMin]) {
+                indexOfMin = j;
+            }
+        }
+        X[i] = tempX[indexOfMin];
+        Y[i] = tempY[indexOfMin];
+        tempX[indexOfMin] = 1000;
+    }
+}
+
+double MotionPlanner::dist(MotionPlanner::Point a, MotionPlanner::Point b)
+{
+    return hypot(a.x - b.x, a.y - b.y);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/MotionPlanner.h b/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/MotionPlanner.h
new file mode 100644 (file)
index 0000000..203b743
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef MotionPlanner_h
+#define MotionPlanner_h
+
+#include <vector>
+
+#include <ZumoMotors.h>
+#include <spline.h>
+#include <PIDController.h>
+
+class MotionPlanner
+{
+  public:
+    struct Point {
+        double x;
+        double y; 
+    };
+
+    MotionPlanner();
+
+    void setPoints(std::vector<int> inputPoints);
+    void generateSpline();
+    void followSpline(ZumoMotors motors, PIDController pid, double rate);
+    tk::spline intervalInterpolate(MotionPlanner::Point leftPoint, MotionPlanner::Point rightPoint, double leftTangent, double rightTangent);
+    void generateTrajectory();
+    void generateParameterizedSpline();
+    bool ready();
+
+  private:
+    int iteration; /* current iteration of followSpline */
+    std::vector<Point> points;
+    std::vector<double> trajectory; /* list of headings to regulate to while driving spline */
+    bool dataReceived;
+    bool trajCalculated;
+
+    /* private member methods */
+
+    void slowSort(int X[], int Y[], int length); /* sort data before creating spline */
+    double dist(MotionPlanner::Point a, MotionPlanner::Point b);
+
+    /* constants */
+        
+    /* 800 pixels = 8 ft = 800 iterations -> 1 pixel = 1 iteration */
+    static const float INTERVAL_LENGTH = 1.0f; 
+
+    /* number of segments to evaulate between each pair of input points */
+    static const int RESOLUTION; 
+};   
+
+#endif /* MotionPlanner_h */
diff --git a/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/SplineDrive.ino b/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/SplineDrive.ino
new file mode 100644 (file)
index 0000000..a8081c0
--- /dev/null
@@ -0,0 +1,31 @@
+/*  ======== SplineDrive.ino ========
+ *  This sketch interacts with a Processing application to enable
+ *  the Zumo to drive along a spline path specified by the user.
+ *   
+ *  The Processing client connects to the Zumo's network then sends 
+ *  list of points to create a trajectory from. The Zumo then uses
+ *  a cubic spline interpolation library to generate trajectory 
+ *  (a list of headings), and attempts to follow that trajectory. 
+ *
+ *  NOTE: Currently there are possible memory issues with large data
+ *  sets/spline curves that cause the program to crash mid-calculation
+ */
+
+#include <ZumoCC3200.h>
+#include <IMUManager.h>
+#include <Utilities.h>
+#include "MotionPlanner.h"
+#include <WiFiClient.h>
+
+/* imu sketch external declarations */
+extern IMUManager imu;
+extern bool   isZeroing;
+
+/* motor sketch external declarations */
+extern Utilities util;
+
+/* AP sketch external declarations */
+#define MAIN_LED_PIN LED_LED
+extern int targetPoints[64];
+extern MotionPlanner mp;
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/apLoop.ino
new file mode 100644 (file)
index 0000000..4d819c2
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch starts a network and listens on port PORTNUM for
+ *  a new set of points from a Processing client.
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <ti/sysbios/knl/Task.h>
+#include <string.h>
+#include "MotionPlanner.h"
+#include <unistd.h>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-spline";
+static const char wifipw[] = "password";
+
+/* port number of the server listening for commands at 192.168.1.1 */
+#define PORTNUM 8080
+
+MotionPlanner mp;
+
+/* create data server on port PORTNUM */
+static WiFiServer server(PORTNUM);
+
+static int readString(WiFiClient client, char *buf);
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+    
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: "); Serial.println(ssid);
+    WiFi.beginNetwork((char *)ssid, (char *)wifipw);
+    
+    while (WiFi.localIP() == INADDR_NONE) {
+        Serial.print(".");
+        delay(300);
+    }
+    
+    mp = MotionPlanner();
+
+    /* startup the command server on port PORTNUM */
+    server.begin();
+
+    Serial.print("dataserver started on port "); Serial.println(PORTNUM);
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+    /* Did a client connect/disconnect since the last time we checked? */
+    if (WiFi.getTotalDevices() > 0) {
+
+        /* listen for incoming clients */
+        WiFiClient client = server.available();
+        if (client) {
+
+            /* if there's a client, listen for incoming point data */
+            static int buffer[64] = {0};
+            static char temp[16] = {0};
+
+            int bufLen = 0;
+            
+            /* while connected to the client */
+            while (client.connected()) {
+
+                /* if there's a byte to read from the client .. */
+                if (client.available()) {
+                     
+                    char c = client.read();  
+                    
+                    /* if there's a new line, we have a complete set of points */
+                    if(c == '\n'){
+                        std::vector<int> points(bufLen);
+                        for(int i=0; i < bufLen; i++){
+                           points[i] = buffer[i];
+                           if(i%2==0){
+                              Serial.print("(");Serial.print(buffer[i]);Serial.print(",");
+                           }
+                           else{
+                              Serial.print(buffer[i]);Serial.println(")");
+                           }
+                           
+                        } 
+                        mp.setPoints(points);
+                        mp.generateParameterizedSpline();
+
+                        /* reset command buffer index to get next command */
+                        bufLen = 0;
+                        memset(buffer, 0, sizeof(buffer));
+                        points.clear();                      
+                    }
+                    /* there's a new point, add it to the buffer */
+                    else if(c == 'p'){
+                        readString(client, temp); 
+                        
+                        int newPoint = atoi(temp);
+                        Serial.print("new point: ");
+                        Serial.println(newPoint);
+                        
+                        /* never overrun the buffer */
+                        if (bufLen >= (int)(sizeof (buffer))) { 
+                            bufLen = sizeof (buffer) - 1;
+                        }
+    
+                        buffer[bufLen++] = newPoint;    
+      
+                        memset(temp, 0, sizeof(temp));                  
+                    }
+
+                }
+
+            }
+
+            /* client disconnected or timed out, close the connection */
+            client.flush();
+            client.stop();
+
+        }
+
+    }
+
+    /* check for new connections 2 times per second */
+    delay(500);
+
+}
+
+/*
+ *  ======== readString ========
+ * read a null-terminated string from the client into a char buffer 
+ */
+static int readString(WiFiClient client, char *buf){
+    for (unsigned int i = 0; ; ) {
+      if(client.available()){
+        char c = client.read();
+        if(c != '\0'){
+          buf[i] = c;
+        }
+        else{
+          return 0;
+        }
+        i++;
+      }
+    }
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/imuLoop.ino
new file mode 100644 (file)
index 0000000..4d7a366
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+*  ======== imuLoop ========
+*  This sketch simply reads the Zumo IMU sensors at 20Hz and prints
+*  the current value once per second to the UART.
+*/
+
+#include <Wire.h>
+
+/* Pololu IMU data instance objects */
+IMUManager imu;
+float angle = 0;
+int imuCount = 0;
+int dc_offset = 0;
+double noise = 0;
+
+bool hasCalibrated = 0;
+bool needToZero = false;
+bool isZeroing = false;
+
+/*
+*  ======== imuSetup ========
+*/
+
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+    Wire.begin();
+
+    imu = IMUManager();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imu.initAccel();
+    imu.enableAccelDefault();
+
+    /* initialize Zumo gyro */
+    if (!imu.initGyro()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imu.enableGyroDefault();
+    Serial.println("imuSetup done.");
+    imuCount = 0;
+    
+    imu.calibrateGyro(2);
+    imu.zeroGyroXAxis();
+    imu.zeroGyroYAxis();
+    imu.zeroGyroZAxis();
+    
+
+}
+
+/*
+*  ======== imuLoop ========
+*/
+void imuLoop()
+{
+    
+    /*update IMU data every 50 ms (20 Hz) */
+    imu.readGyro();
+    imu.readAccel();
+    imu.readMag();
+
+    delay(50);
+
+    imuCount++;
+
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/SplineDrive/motorLoop.ino
new file mode 100644 (file)
index 0000000..3bcd4e0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch waits until trajectory is generated, then proceeeds
+ *  to use PID heading control to follow the calculated trajectory
+ */
+
+#include <Energia.h>
+#include <math.h>
+
+#include <ZumoMotors.h>
+#include <PIDController.h>
+#include "MotionPlanner.h"
+
+#define PERIOD  0           /* period of motor control updates */
+#define TEST 0
+
+float targetAngle = 0.0;
+bool targetAngleSet = false;
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+PIDController steerPID;
+
+Utilities util;
+
+/*
+ *  ======== motorSetup ========
+ */
+
+void motorSetup(void)
+{
+  Serial.begin(9600);
+  Serial.println("motorSetup ...");
+  
+  /* initialize the Pololu driver motor library */
+  motors.setRightSpeed(0);
+  motors.setLeftSpeed(0);
+
+  Serial.println("motorSetup done.");
+
+  util = Utilities();
+  
+  steerPID = PIDController(0.01717, 0.0, 0.003);
+
+}
+
+/*
+
+ *  ======== motorLoop ========
+ */
+
+void motorLoop(void)
+{
+  
+  if(mp.ready()){
+
+    mp.followSpline(motors, steerPID, 0.35); 
+    
+  }
+
+  delay(20);
+}
+
+
+
+
+
+
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/UDPBroadcast.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/UDPBroadcast.ino
new file mode 100644 (file)
index 0000000..ad766d5
--- /dev/null
@@ -0,0 +1,28 @@
+/*  ======== UDPBroadcast.ino ========
+ *  A platform for controlling mutiple Zumos using a single Processing 
+ *  application.
+ *
+ *  All Zumos involved and the PC running Processing must connect to a shared
+ *  network, in this case, the open network "TINK-NET".
+ *
+ *  Broadcasting the commands using UDP from the Processing side results in
+ *  all Zumos currently running the example to simultaneously drive. Additionally,
+ *  all Zumos receiving commands will send back their current IMU readings,
+ *  allowing the Processing sketch to simultaneously display data from the Zumos.
+ *
+ */
+
+#include <ZumoCC3200.h>
+#include <L3G.h>
+#include <LSM303.h>
+
+/* imu sketch external declarations */
+extern LSM303 imuCompass; /* acceleration and magnetometer */
+extern L3G    imuGyro;    /* gyro data */
+
+/* motor sketch external declarations */
+extern char motorWASD;
+
+/* ap sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/apLoop.ino
new file mode 100644 (file)
index 0000000..25d5f51
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ *  ======== apLoop ========
+ *  This sketch connects to an open network named "TINK-NET" and listens
+ *  for motor control commands over a UDP socket connection. Upon receiving
+ *  a command, the sketch sends a IMU data packet as a reply to the sender.
+ *
+ */
+
+#include <WiFi.h>
+#include <Pushbutton.h>
+#include <ti/sysbios/knl/Task.h>
+
+#include <string.h>
+
+/* name of the network and its password */
+char ssid[] = "TINK-NET";
+
+/* zumo command port */
+#define CMD_PORT 8080
+
+/* IMU data port */
+#define DATA_PORT 6000
+
+IPAddress broadcastIP(192,168,1,255);
+
+static void sendIMUPacket(WiFiUDP Udp, IPAddress ip, int localPort);
+
+WiFiUDP Udp;
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+    
+    // attempt to connect to Wifi network:
+    Serial.print("Attempting to connect to Network named: ");
+    // print the network name (SSID);
+    Serial.println(ssid); 
+    // Connect to open network
+    WiFi.begin(ssid);
+    while ( WiFi.status() != WL_CONNECTED) {
+      // print dots while we wait to connect
+      Serial.print(".");
+      delay(300);
+    }
+    
+    Serial.println("Connected");
+    Serial.println("Waiting for an ip address");
+    
+    while (WiFi.localIP() == INADDR_NONE) {
+      // print dots while we wait for an ip addresss
+      Serial.print(".");
+      delay(300);
+    }
+  
+    Serial.println("\nIP Address obtained");
+    printWifiStatus();
+    
+    /* socket listening for motor commands */
+    Udp.begin(CMD_PORT);                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+
+  /* listen for motor commands from group network */
+  // if there's data available, read a packet
+  int packetSize = Udp.parsePacket();
+  if (packetSize){
+      static char buffer[16] = {0}; 
+          
+      Serial.print("Received packet from ");
+      IPAddress remoteIp = Udp.remoteIP();
+      Serial.print(remoteIp);
+      Serial.print(", port ");
+      Serial.println(Udp.remotePort());
+        
+      
+      // read the packet into packetBufffer
+      int len = Udp.read(buffer, 64);
+             
+      if (len > 0) buffer[len] = 0;
+      Serial.print("Contents: ");
+      Serial.println(buffer);
+             
+      motorWASD = buffer[0];  
+      
+      /* send back an IMU packet */ 
+      sendIMUPacket(Udp, Udp.remoteIP(), Udp.remotePort());     
+   }      
+           
+}
+
+/*
+ *  ======== doWASD ========
+ */
+static void sendIMUPacket(WiFiUDP Udp, IPAddress ip, int localPort)
+{
+    static char report[80];
+
+    /* send current IMU data */
+    System_snprintf(report, sizeof(report),
+        "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d",
+        imuCompass.a.x, imuCompass.a.y, imuCompass.a.z,
+        imuGyro.g.x,    imuGyro.g.y,    imuGyro.g.z, 
+        imuCompass.m.x, imuCompass.m.y, imuCompass.m.z);
+    
+    Udp.beginPacket(ip, localPort);
+    Udp.write(report);
+    Udp.endPacket();
+}
+
+void printWifiStatus() {
+  // print the SSID of the network you're attached to:
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi IP address:
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print the received signal strength:
+  long rssi = WiFi.RSSI();
+  Serial.print("signal strength (RSSI):");
+  Serial.print(rssi);
+  Serial.println(" dBm");
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/imuLoop.ino
new file mode 100644 (file)
index 0000000..be55408
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 20Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+#include <L3G.h>
+#include <LSM303.h>
+
+/* Pololu IMU data instance objects */
+LSM303 imuCompass; /* acceleration and magnetometer */
+L3G    imuGyro;    /* gyro data */
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+    Wire.begin();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imuCompass.init();
+    imuCompass.enableDefault();
+
+    /* initialize Zumo gyro */
+    if (!imuGyro.init()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imuGyro.enableDefault();
+    Serial.println("imuSetup done.");
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* update IMU data every 50 ms (200 Hz) */
+    imuGyro.read();
+    imuCompass.read();
+    delay(50);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPBroadcast/motorLoop.ino
new file mode 100644 (file)
index 0000000..8d34fba
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+#include <PIDController.h>
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+
+char motorWASD = ' ';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+PIDController steerPID;
+
+static int clip(int speed);
+static void drive(char wasd, int goal, unsigned int duration);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+    Serial.println("motorSetup done.");
+    
+    steerPID = PIDController(0.01717, 0.0, 0.003);
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    /* state used to blink LED */
+    static unsigned count = 0;
+    static char state = 0;
+    
+    switch (motorWASD) {
+        case 's':
+        case 'w': {
+            /* illuminate LED while driving */
+            digitalWrite(MAIN_LED_PIN, HIGH);
+            drive(motorWASD, 200, PERIOD);
+            break;
+        }
+
+        case 'd':
+        case 'a': {
+            /* blink LED while turning */
+            if (count == ((count / 100) * 100)) {
+                state ^= 1;
+                digitalWrite(MAIN_LED_PIN, state);
+            }   
+            drive(motorWASD, 100, PERIOD);
+            break;
+        }
+
+        default: {
+            /* turn off LED while stopped */
+            motorWASD = ' ';
+            digitalWrite(MAIN_LED_PIN, LOW);
+            drive(' ', 0, 10);
+            break;
+        }
+    }
+
+    count++;
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPExample/UDPExample.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPExample/UDPExample.ino
new file mode 100644 (file)
index 0000000..d8d7d72
--- /dev/null
@@ -0,0 +1,24 @@
+/*  ======== basics.ino ========
+ *  Simple demo of the CC3200 Zumo platform basics
+ *
+ *  This example creates an access point named zumo-udp with password
+ *  "password" and a UDP socket on port 8080.
+ *
+ *  The datagram connections listens for incoming packets and interprets
+ *  them as motor commands, and send packets containing IMU data.
+ */
+
+#include <ZumoCC3200.h>
+#include <L3G.h>
+#include <LSM303.h>
+
+/* imu sketch external declarations */
+extern LSM303 imuCompass; /* acceleration and magnetometer */
+extern L3G    imuGyro;    /* gyro data */
+
+/* motor sketch external declarations */
+extern char motorWASD;
+
+/* ap sketch external declarations */
+#define MAIN_LED_PIN RED_LED
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPExample/apLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPExample/apLoop.ino
new file mode 100644 (file)
index 0000000..ab1c843
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  ======== apLoop ========
+ *  Unicast UDP communicating with Processing
+ *
+ *  The name and password of the network and the port number of the server
+ *  (always at IP address 192.168.1.1) can be changed below.
+ */
+
+#include <WiFi.h>
+#include <Pushbutton.h>
+#include <ti/sysbios/knl/Task.h>
+
+#include <string.h>
+
+/* name of the network and its password */
+static const char ssid[] = "zumo-udp";
+static const char wifipw[] = "password";
+
+/* port number for UDP communications */
+#define PORTNUM 8080
+
+/* IP Address of processing app */
+IPAddress IP(192,168,1,2);
+
+static void sendIMUPacket(WiFiUDP Udp, IPAddress ip, int localPort);
+
+WiFiUDP Udp;
+
+/*
+ *  ======== apSetup ========
+ */
+void apSetup()
+{
+    Serial.begin(9600);
+
+    /* set priority of this task to be lower than other tasks */
+    Task_setPri(Task_self(), 1);
+   
+    /* startup a new network and get the first IP address: 192.168.1.1 */
+    Serial.print("Starting a new network: "); Serial.println(ssid);
+    WiFi.beginNetwork((char *)ssid, (char *)wifipw);
+    while (WiFi.localIP() == INADDR_NONE) {
+       Serial.print(".");
+       delay(300);
+    } 
+    Serial.println("Datagram socket opened on port 8080");
+    Udp.begin(PORTNUM);
+}
+
+/*
+ *  ======== apLoop ========
+ */
+void apLoop()
+{
+    // if there's data available, read a packet
+    int packetSize = Udp.parsePacket();
+    if (packetSize){
+       static char buffer[64] = {0}; 
+      
+       Serial.print("Received packet from ");
+       IPAddress remoteIp = Udp.remoteIP();
+       Serial.print(remoteIp);
+       Serial.print(", port ");
+       Serial.println(Udp.remotePort());
+    
+       // read the packet into packetBufffer
+       int len = Udp.read(buffer, 64);
+       
+       if (len > 0) buffer[len] = 0;
+       Serial.print("Contents: ");
+       Serial.println(buffer);
+       
+       motorWASD = buffer[0];
+       
+    }
+    
+    /* send IMU data */
+    sendIMUPacket(Udp, IP, PORTNUM);
+}
+
+/*
+ *  ======== sendIMUPacket ========
+ */
+static void sendIMUPacket(WiFiUDP Udp, IPAddress ip, int localPort)
+{
+    static char report[80];
+
+    /* send current IMU data */
+    System_snprintf(report, sizeof(report),
+        "A: %6d %6d %6d G: %6d %6d %6d M: %6d %6d %6d",
+        imuCompass.a.x, imuCompass.a.y, imuCompass.a.z,
+        imuGyro.g.x,    imuGyro.g.y,    imuGyro.g.z, 
+        imuCompass.m.x, imuCompass.m.y, imuCompass.m.z);
+    
+    Udp.beginPacket(ip, localPort);
+    Udp.write(report);
+    Udp.endPacket();
+}
+
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPExample/imuLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPExample/imuLoop.ino
new file mode 100644 (file)
index 0000000..be55408
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ *  ======== imuLoop ========
+ *  This sketch simply reads the Zumo IMU sensors at 20Hz and prints
+ *  the current value once per second to the UART.
+ */
+
+#include <Wire.h>
+
+#include <L3G.h>
+#include <LSM303.h>
+
+/* Pololu IMU data instance objects */
+LSM303 imuCompass; /* acceleration and magnetometer */
+L3G    imuGyro;    /* gyro data */
+
+/*
+ *  ======== imuSetup ========
+ */
+void imuSetup()
+{
+    Serial.begin(9600);
+    Serial.println("imuSetup ...");
+    Wire.begin();
+
+    /* initialize Zumo accelerometer and magnetometer */
+    imuCompass.init();
+    imuCompass.enableDefault();
+
+    /* initialize Zumo gyro */
+    if (!imuGyro.init()) {
+        Serial.print("Failed to autodetect gyro type!");
+        delay(1000);
+    }
+    imuGyro.enableDefault();
+    Serial.println("imuSetup done.");
+}
+
+/*
+ *  ======== imuLoop ========
+ */
+void imuLoop()
+{
+    /* update IMU data every 50 ms (200 Hz) */
+    imuGyro.read();
+    imuCompass.read();
+    delay(50);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/examples/UDPExample/motorLoop.ino b/src/Energia/libraries/ZumoCC3200/examples/UDPExample/motorLoop.ino
new file mode 100644 (file)
index 0000000..69b2dd1
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ *  ======== motorLoop ========
+ *  This sketch controls the motors on the Zumo by polling a global
+ *  variable, motorWASD, once per PERIOD milliseconds for one of the
+ *  following motor commands:
+ *      'w' - drive forward
+ *      's' - drive backward
+ *      'a' - turn left
+ *      'd' - turn right
+ *      ' ' - stop
+ *
+ *  Other sketches or interrupts can control the zumo by simply writing the
+ *  desired command to motorWASD.
+ */
+#include <Energia.h>
+
+#include <ZumoMotors.h>
+
+#define PERIOD  1           /* period of motor control updates */
+
+char motorWASD = ' ';       /* current motor drive command */
+
+static ZumoMotors motors;   /* Zumo motor driver provided by Pololu */
+
+static int clip(int speed);
+static void drive(char wasd, int goal, unsigned int duration);
+static int next(int cur, int goal);
+
+/*
+ *  ======== motorSetup ========
+ */
+void motorSetup(void)
+{
+    Serial.println("motorSetup ...");
+    /* initialize the Pololu driver motor library */
+    motors.setRightSpeed(0);
+    motors.setLeftSpeed(0);
+
+    /* setup an LED to indcate forward/backward movement or turning */
+    pinMode(MAIN_LED_PIN, OUTPUT);
+    Serial.println("motorSetup done.");
+}
+
+/*
+ *  ======== motorLoop ========
+ */
+void motorLoop(void)
+{
+    /* state used to blink LED */
+    static unsigned count = 0;
+    static char state = 0;
+    
+    switch (motorWASD) {
+        case 's':
+        case 'w': {
+            /* illuminate LED while driving */
+            digitalWrite(MAIN_LED_PIN, HIGH);
+            drive(motorWASD, 200, PERIOD);
+            break;
+        }
+
+        case 'd':
+        case 'a': {
+            /* blink LED while turning */
+            if (count == ((count / 100) * 100)) {
+                state ^= 1;
+                digitalWrite(MAIN_LED_PIN, state);
+            }   
+            drive(motorWASD, 100, PERIOD);
+            break;
+        }
+
+        default: {
+            /* turn off LED while stopped */
+            motorWASD = ' ';
+            digitalWrite(MAIN_LED_PIN, LOW);
+            drive(' ', 0, 10);
+            break;
+        }
+    }
+
+    count++;
+}
+
+/*
+ *  ======== clip ========
+ */
+static int clip(int speed)
+{
+    if (speed < -400) {
+        speed = -400;
+    }
+    else if (speed > 400) {
+        speed = 400;
+    }
+    return (speed);
+}
+
+/*
+ *  ======== drive ========
+ *  Drive motors in the direction and speed specified by wasd and goal
+ *
+ *  Note: negative goal values imply a reversal of the wasd direction
+ */
+static void drive(char wasd, int goal, unsigned int duration)
+{
+    static int leftSpeed = 0;
+    static int rightSpeed = 0;
+    
+    while (duration > 0) {
+        duration--;
+
+        /* gradually adjust curent speeds to goal */
+        switch (wasd) {
+            case ' ': { /* stop */
+                leftSpeed = next(leftSpeed, 0);
+                rightSpeed = next(rightSpeed, 0);
+                break;
+            }
+            case 'w': { /* forward */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            case 's': { /* backward */
+                leftSpeed = next(leftSpeed, -goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'd': { /* turn right */
+                leftSpeed = next(leftSpeed, goal);
+                rightSpeed = next(rightSpeed, -goal);
+                break;
+            }
+
+            case 'a': { /* turn left */
+                leftSpeed = next(leftSpeed, -goal);;
+                rightSpeed = next(rightSpeed, goal);
+                break;
+            }
+
+            default: {
+                break;
+            }
+        }
+
+        /* clip speeds to allowable range */
+        leftSpeed = clip(leftSpeed);
+        rightSpeed = clip(rightSpeed);
+    
+        /* set motor speeds */
+        ZumoMotors::setLeftSpeed(leftSpeed);
+        ZumoMotors::setRightSpeed(rightSpeed);
+
+        /* sleep for 1 ms (so duration is in milliseconds) */
+        delay(1);
+    }
+}
+
+/*
+ *  ======== next ========
+ *  Compute the next motor speed value given the cur speed and a new goal
+ */
+static int next(int cur, int goal)
+{
+    int tmp = (goal - cur) * 0.0625f + cur;
+    return (tmp == cur ? goal : tmp);
+}
diff --git a/src/Energia/libraries/ZumoCC3200/utility/Balancer.cpp b/src/Energia/libraries/ZumoCC3200/utility/Balancer.cpp
new file mode 100644 (file)
index 0000000..65c8212
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2015, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ *  ======== Balancer ========
+ *  This class provides methods for balancing the zumo using PID
+ *  control as well as modifying the PID gains and accessing 
+ *  state information for display and/or debugging purposes
+ */
+
+#include "Balancer.h"
+#include "PIDController.h"
+#include "ZumoMotors.h"
+#include "IMUManager.h"
+
+#include <math.h>
+
+PIDController Balancer::horizontalPID;
+PIDController Balancer::verticalPID;
+
+Balancer::BalanceInfo Balancer::balanceStatus;
+
+Balancer::Balancer()
+{
+     horizontalPID = PIDController(10.6f, 0.0f, 0.0f);
+
+     /* The D term for vertical balancing is not calculated by the PID 
+      * controller. Instead it is taken directly from the gyro y-axis
+      * angular velocity measurements.
+      */
+     verticalPID = PIDController(27.6f, 0.27f, 0.2f);
+
+     balanceStatus.error = 0;
+     balanceStatus.motorPower = 0;
+     balanceStatus.PIDTerms[0] = 0;
+     balanceStatus.PIDTerms[1] = 0;
+     balanceStatus.PIDTerms[2] = 0;
+}
+
+/*
+ *  ======== horizontalBalance ========
+ *  Balance horizontally using PID feedback control
+ *
+ *  Meant to be used on a pivoting balance board, so 
+ *  the robot will use drive actions to find the center
+ *  of balance.
+ */
+void Balancer::horizontalBalance(ZumoMotors motors, IMUManager imu)
+{
+     float angle = imu.getFilteredTiltAngle();
+     
+     /* regulate the error to -89 degrees */
+     float power = horizontalPID.calculate(HORIZONTAL_BALANCING_ANGLE - angle);
+
+     power = Utilities::saturate(power, -40, 40);
+     motors.setLeftSpeed((int)power);
+     motors.setRightSpeed((int)power);
+}
+
+/*
+ *  ======== verticalBalance ========
+ *  Balance vertically using PID feedback control
+ *
+ *  The robot uses its front wheels to stabilize itself
+ *  vertically on a medium-friction surface
+ */
+void Balancer::verticalBalance(ZumoMotors motors, IMUManager imu)
+{
+     float angle = imu.getFilteredTiltAngle();
+     float angularVelocity = imu.getGyroY();
+     float error = angle - VERTICAL_BALANCING_ANGLE;
+     int power = 0;
+     
+     /* if the robot hasn't fallen over */
+     if (abs(error) < 30) {
+         
+         /* calculate the reactionary output with the derivative term being
+          * calculated separately */
+         power = (int) (verticalPID.calculate(error) + verticalPID.getD() * angularVelocity);
+         
+         /* limit oscillations in the stable regime */
+//         if(util.abs(error) < 1.5){
+//            power = util.saturate(power, -50, 50); 
+//         }
+         
+         /* if error changes signs drastically, i.e. significant oscillation detected, limit power */
+//         if((error * prevError < 0) && util.abs(error-prevError) > 1.2){
+//            power = util.saturate(power, -60, 60); 
+//         }
+
+     }
+     else {
+         power = 0;
+     }
+
+     motors.setLeftSpeed(power);
+     motors.setRightSpeed(power);
+     
+     /* store new data in balanceInfo */
+     balanceStatus.error = error;
+     balanceStatus.motorPower = power;
+     balanceStatus.PIDTerms[0] = verticalPID.getPContribution();
+     balanceStatus.PIDTerms[1] = verticalPID.getIContribution();
+     balanceStatus.PIDTerms[2] = verticalPID.getDContribution();
+}
+
+/*
+ *  ======== verticalDrive ========
+ *  Drive the zumo while it is balanced vertically
+ *
+ *  The method takes in a WASD command and moves in the
+ *  corresponding direction while using a PID controller
+ *  to balance vertically
+ */
+void Balancer::verticalDrive(ZumoMotors motors, char cmd)
+{
+  
+  /* unimplemented */
+  
+}
+
diff --git a/src/Energia/libraries/ZumoCC3200/utility/Balancer.h b/src/Energia/libraries/ZumoCC3200/utility/Balancer.h
new file mode 100644 (file)
index 0000000..52e00b5
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Balancer_h
+#define Balancer_h
+
+#include "ZumoMotors.h"
+#include "PIDController.h"
+#include "IMUManager.h"
+
+class Balancer
+{
+  public:
+    struct BalanceInfo {
+       float error;
+       int motorPower;
+       float PIDTerms[3];
+    };
+
+    Balancer();  
+
+    static void horizontalBalance(ZumoMotors motors, IMUManager imu);
+    static void verticalBalance(ZumoMotors motors, IMUManager imu);
+    static void verticalDrive(ZumoMotors motors, char cmd);
+    static void setHorizontalGains(float p, float i, float d);
+    static void setVerticalGains(float p, float i, float d);
+
+    static BalanceInfo balanceStatus;
+    static PIDController horizontalPID;
+    static PIDController verticalPID;
+
+  private:
+    /* the angle at which the zumo balances vertically */
+    const static float VERTICAL_BALANCING_ANGLE = -1.337f;
+
+    /* the angle at which the zumo balances horizontally */
+    const static float HORIZONTAL_BALANCING_ANGLE = -89.0f;
+};
+#endif
diff --git a/src/Energia/libraries/ZumoCC3200/utility/Command.h b/src/Energia/libraries/ZumoCC3200/utility/Command.h
new file mode 100644 (file)
index 0000000..abf6909
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2015, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Command_h
+#define Command_h
+
+#include "Utilities.h"
+
+class Command {
+public:
+    virtual void run(Utilities::MotorInfo &info) = 0; //format should be {angle/error, left motor power, right motor power, time}
+    virtual void initialize() = 0;
+    virtual void end() = 0;
+    virtual bool isFinished() = 0;
+    virtual bool hasBeenInitialized() = 0;
+    virtual bool isTimedOut() = 0;
+
+private:
+    long initTime;
+    long finishTime;
+};
+
+#endif
+
diff --git a/src/Energia/libraries/ZumoCC3200/utility/CommandManager.cpp b/src/Energia/libraries/ZumoCC3200/utility/CommandManager.cpp
new file mode 100644 (file)
index 0000000..3a8103e
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2015, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <Energia.h>
+#include "CommandManager.h"
+
+vector<Command*> CommandManager::commandList;
+CommandManager::CommandManager()
+{
+}
+
+void CommandManager::addCommand(Command* cmd)
+{
+    commandList.push_back(cmd);
+}
+
+void CommandManager::run(Utilities::MotorInfo &info)
+{
+    if (commandList.size() != 0) {
+        Command* currCommand = commandList.at(0);
+        if (!(currCommand->hasBeenInitialized())) {
+            Serial.println("Initialize");
+            currCommand->initialize();
+        }
+        else if (currCommand->hasBeenInitialized()
+                && !(currCommand->isFinished())) {
+            Serial.println("Running");
+            currCommand->run(info);
+        }
+        else if (currCommand->isFinished()) {
+            Serial.println("End");
+            currCommand->end();
+            commandList.erase(commandList.begin());
+        }
+    }
+    else {
+        info.error = 0;
+        info.leftSpeed = 0;
+        info.rightSpeed = 0;
+        info.time = (float) millis();
+    }
+}
+
+void CommandManager::clearAllCommands(void)
+{
+    for (int i = 0; i < commandList.size(); i++) {
+        Command* cmd = commandList[i];
+        cmd->end();
+    }
+}
diff --git a/src/Energia/libraries/ZumoCC3200/utility/CommandManager.h b/src/Energia/libraries/ZumoCC3200/utility/CommandManager.h
new file mode 100644 (file)
index 0000000..0c4c414
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef CommandManager_h
+#define CommandManager_h
+
+#include <vector>
+#include "Command.h"
+#include "Utilities.h"
+
+using namespace std;
+
+class CommandManager {
+public:
+    CommandManager();
+    void addCommand(Command* cmd);
+    void clearAllCommands();
+    void run(Utilities::MotorInfo &info);
+private:
+    static vector<Command *> commandList;
+};
+#endif
diff --git a/src/Energia/libraries/ZumoCC3200/utility/DCM.cpp b/src/Energia/libraries/ZumoCC3200/utility/DCM.cpp
new file mode 100644 (file)
index 0000000..90f1fbe
--- /dev/null
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2015, Texas Instruments Incorporated
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * *  Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * *  Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * *  Neither the name of Texas Instruments Incorporated nor the names of
+ *    its contributors may be used to endorse or promote products derived
+ *    from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * DCM.c
+ *
+ *  Created on: Jul 22, 2015
+ *      Author: x0236197
+ */
+#include <xdc/std.h>
+
+#include <stdlib.h>
+
+#include <math.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include "DCM.h"
+
+typedef struct DCM_Object
+{
+    /* The state of the direction cosine matrix */
+    float dcm[3][3];
+
+    /* The time delta between updates to the DCM */
+    float deltaT;
+
+    /* The scaling factor for DCM update based on the accelerometer reading */
+    float scaleA;
+
+    /* The scaling factor for DCM update based on the gyroscope reading */
+    float scaleG;
+
+    /* The scaling factor for DCM update based on the magnetometer reading */
+    float scaleM;
+
+    /* The most recent accelerometer readings */
+    float pfAccel[3];
+
+    /* The most recent gyroscope readings */
+    float pfGyro[3];
+
+    /* The most recent magnetometer readings */
+    float pfMagneto[3];
+} DCM_Object;
+
+static float dotProduct(float v1[3], float v2[3]);
+static void  crossProduct(float vout[3], float v1[3], float v2[3]);
+static void  scaleVector(float vout[3], float vin[3], float fScale);
+static void  addVector(float vout[3], float v1[3], float v2[3]);
+static void  normalize(float vin[3]);
+
+/*
+ *  ======== DCM_create ========
+ */
+DCM_Handle DCM_create(float deltaT,
+                      float scaleA, float scaleG, float scaleM)
+{
+    DCM_Handle handle = (DCM_Object *)malloc(sizeof(struct DCM_Object));
+
+    /* Initialize the DCM matrix to the identity matrix */
+    handle->dcm[0][0] = 1.0f;
+    handle->dcm[0][1] = 0.0f;
+    handle->dcm[0][2] = 0.0f;
+    handle->dcm[1][0] = 0.0f;
+    handle->dcm[1][1] = 1.0f;
+    handle->dcm[1][2] = 0.0f;
+    handle->dcm[2][0] = 0.0f;
+    handle->dcm[2][1] = 0.0f;
+    handle->dcm[2][2] = 1.0f;
+
+    /* Save the time delta between DCM updates */
+    handle->deltaT = deltaT;
+
+    /* Save the scaling factors that are applied to the accelerometer,
+       gyroscope, and magnetometer readings */
+    handle->scaleA = scaleA;
+    handle->scaleG = scaleG;
+    handle->scaleM = scaleM;
+
+    return handle;
+}
+
+/*
+ *  ======== DCM_delete ========
+ */
+void DCM_delete(DCM_Handle handle)
+{
+    free(handle);
+}
+
+/*
+ *  ======== DCM_updateAccelData ========
+ *  Updates the accelerometer reading used by the complementary filter DCM
+ *  algorithm.
+ * 
+ *  handle is a pointer to the DCM state structure.
+ *  fAccelX is the accelerometer reading in the X body axis.
+ *  fAccelY is the accelerometer reading in the Y body axis.
+ *  fAccelZ is the accelerometer reading in the Z body axis.
+ * 
+ *  This function updates the accelerometer reading used by the complementary
+ *  filter DCM algorithm.  The accelerometer readings provided to this function
+ *  are used by subsequent calls to DCM_start() and DCM_update() to
+ *  compute the attitude estimate.
+ */
+void DCM_updateAccelData(DCM_Handle handle,
+                         float fAccelX, float fAccelY, float fAccelZ)
+{
+    /* The user should never pass in values that are not-a-number */
+    assert(!isnan(fAccelX));
+    assert(!isnan(fAccelY));
+    assert(!isnan(fAccelZ));
+
+    /* Save the new accelerometer reading */
+    handle->pfAccel[0] = fAccelX;
+    handle->pfAccel[1] = fAccelY;
+    handle->pfAccel[2] = fAccelZ;
+}
+
+/*
+ *  ======== DCM_updateGyroData ========
+ *  Updates the gyroscope reading used by the complementary filter DCM
+ *  algorithm.
+ * 
+ *  handle is a pointer to the DCM state structure.
+ *  fGyroX is the gyroscope reading in the X body axis.
+ *  fGyroY is the gyroscope reading in the Y body axis.
+ *  fGyroZ is the gyroscope reading in the Z body axis.
+ * 
+ *  This function updates the gyroscope reading used by the complementary
+ *  filter DCM algorithm.  The gyroscope readings provided to this function are
+ *  used by subsequent calls to DCM_update() to compute the attitude
+ *  estimate.
+ */
+void DCM_updateGyroData(DCM_Handle handle,
+                        float fGyroX, float fGyroY, float fGyroZ)
+{
+    /* The user should never pass in values that are not-a-number */
+    assert(!isnan(fGyroX));
+    assert(!isnan(fGyroY));
+    assert(!isnan(fGyroZ));
+
+    /* Save the new gyroscope reading */
+    handle->pfGyro[0] = fGyroX;
+    handle->pfGyro[1] = fGyroY;
+    handle->pfGyro[2] = fGyroZ;
+}
+
+/*
+ *  ======== DCM_updateMagnetoData ========
+ *  Updates the magnetometer reading used by the complementary filter DCM
+ *  algorithm.
+ * 
+ *  handle is a pointer to the DCM state structure.
+ *  fMagnetoX is the magnetometer reading in the X body axis.
+ *  fMagnetoY is the magnetometer reading in the Y body axis.
+ *  fMagnetoZ is the magnetometer reading in the Z body axis.
+ * 
+ *  This function updates the magnetometer reading used by the complementary
+ *  filter DCM algorithm.  The magnetometer readings provided to this function
+ *  are used by subsequent calls to DCM_start() and DCM_update() to
+ *  compute the attitude estimate.
+ */
+void DCM_updateMagnetoData(DCM_Handle handle,
+                           float fMagnetoX, float fMagnetoY, float fMagnetoZ)
+{
+    /* The user should never pass in values that are not-a-number */
+    assert(!isnan(fMagnetoX));
+    assert(!isnan(fMagnetoY));
+    assert(!isnan(fMagnetoZ));
+
+    /* Save the new magnetometer reading */
+    handle->pfMagneto[0] = fMagnetoX;
+    handle->pfMagneto[1] = fMagnetoY;
+    handle->pfMagneto[2] = fMagnetoZ;
+}
+
+/*
+ *  ======== cancelDrift ========
+ *  Use orientation reference vectors to cancel out gyro drift with a PI
+ *  feedback controller. This method assumes that the device experiences 
+ *  negligible acceleration. Adjustments can be made to allow this algorithm
+ *  to work with accelerating systems. Only called internally within the 
+ *  DCM calculuation.
+ */
+static void cancelDrift(DCM_Handle handle)
+{
+    /* two correction vectors fed into the controller */
+    float pfKError[3], pfIError[3]; 
+        
+    /* calcluate the Z-axis correction based on accelerometer reading */
+    crossProduct(pfKError, handle->dcm[2], handle->pfAccel); 
+        
+    /* calcluate the X-axis correction based on magnetometer reading */
+    crossProduct(pfIError, handle->dcm[0], handle->pfMagneto); 
+}
+
+/*
+ *  ======== DCM_start ========
+ *  Starts the complementary filter DCM attitude estimation from an initial
+ *  sensor reading.
+ * 
+ *  handle is a pointer to the DCM state structure.
+ * 
+ *  This function computes the initial complementary filter DCM attitude
+ *  estimation state based on the initial accelerometer and magnetometer
+ *  reading.  While not necessary for the attitude estimation to converge,
+ *  using an initial state based on sensor readings results in quicker
+ *  convergence.
+ */
+void DCM_start(DCM_Handle handle)
+{
+    /* The I, J and K basis vectors */
+    float pfI[3], pfJ[3], pfK[3];
+
+    /* The magnetometer reading forms the initial I vector, pointing north */
+    pfI[0] = handle->pfMagneto[0];
+    pfI[1] = handle->pfMagneto[1];
+    pfI[2] = handle->pfMagneto[2];
+
+    /* The accelerometer reading forms the initial K vector, pointing down */
+    pfK[0] = handle->pfAccel[0];
+    pfK[1] = handle->pfAccel[1];
+    pfK[2] = handle->pfAccel[2];
+
+    /* Compute the initial J vector, which is the cross product of the 
+     * K and I vectors
+     */
+    crossProduct(pfJ, pfK, pfI);
+
+    /* Recompute the I vector from the cross product of the J and K vectors.
+     * This makes it fully orthogonal, which it wasn't before since magnetic
+     * north points inside the Earth in many places. 
+     */ 
+    crossProduct(pfI, pfJ, pfK);
+
+    /* Normalize the I, J, and K vectors */
+    normalize(pfI);
+    normalize(pfJ);
+    normalize(pfK);
+
+    /* Initialize the DCM matrix from the I, J, and K vectors */
+    handle->dcm[0][0] = pfI[0];
+    handle->dcm[0][1] = pfI[1];
+    handle->dcm[0][2] = pfI[2];
+    handle->dcm[1][0] = pfJ[0];
+    handle->dcm[1][1] = pfJ[1];
+    handle->dcm[1][2] = pfJ[2];
+    handle->dcm[2][0] = pfK[0];
+    handle->dcm[2][1] = pfK[1];
+    handle->dcm[2][2] = pfK[2];
+}
+
+/*
+ *  ======== DCM_update ========
+ *  Updates the complementary filter DCM attitude estimation based on an
+ *  updated set of sensor readings.
+ * 
+ *  handle is a pointer to the DCM state structure.
+ * 
+ *  This function updates the complementary filter DCM attitude estimation
+ *  state based on the current sensor readings.  This function must be called
+ *  at the rate specified in initialization, with new readings supplied at an
+ *  appropriate rate (for example, magnetometers typically sample at a much
+ *  slower rate than accelerometers and gyroscopes).
+ * 
+ *  Notes: split up into smaller operations
+ */
+
+void DCM_update(DCM_Handle handle)
+{
+    /* The I, J and K basis vectors */
+    float pfI[3], pfJ[3], pfK[3];
+
+    /* The rotation from the previous state */
+    float pfDelta[3]; 
+        
+    /* A temporary storage vector */
+    float pfTemp[3];
+        
+    /* The orthogonality error */
+    float fError;
+    bool bNAN = false;
+
+    /* Form the I, J, K global unit vectors as measured from the body frame
+     * of reference
+     */
+
+    /* The magnetometer reading forms the new Im vector, pointing north */
+    pfI[0] = handle->pfMagneto[0];
+    pfI[1] = handle->pfMagneto[1];
+    pfI[2] = handle->pfMagneto[2];
+
+    /* The accelerometer reading forms the new Ka vector, pointing down */
+    pfK[0] = handle->pfAccel[0];
+    pfK[1] = handle->pfAccel[1];
+    pfK[2] = handle->pfAccel[2];
+
+    /* Compute the new J vector, which is the cross product of the Ka and Im
+     * vectors 
+     */
+    crossProduct(pfJ, pfK, pfI);
+
+    /* Recompute the Im vector from the cross product of the J and Ka vectors.
+     * This makes it fully orthogonal, which it wasn't before since magnetic
+     * north points inside the Earth in many places 
+     */
+    crossProduct(pfI, pfJ, pfK);
+
+    /* Normalize the Im and Ka vectors */
+    normalize(pfI);
+    normalize(pfK);
+
+    /* Compute and scale the rotation as inferred from the accelerometer,
+     * storing it in the rotation accumulator 
+     */
+
+    /* old K crossed with new K -> axis of K rotation */
+    crossProduct(pfTemp, handle->dcm[2], pfK); 
+    scaleVector(pfDelta, pfTemp, handle->scaleA);
+
+    /* Compute and scale the rotation as measured by the gyroscope, adding
+     * it to the rotation accumulator 
+     */
+    pfTemp[0] = -(handle->pfGyro[0]) * (handle->deltaT) * (handle->scaleG); 
+    pfTemp[1] = -(handle->pfGyro[1]) * (handle->deltaT) * (handle->scaleG); 
+    pfTemp[2]