Friday, October 24, 2014

Share Software with your Team Over Git Hub.

What happens when your main software person is not around and you want to get your robot running? What software is the latest tested version? What happens if your main computer dies right before a robotics match? Which software did I use in my last competition? What if two people edited the same file on two different computers? How do I get the changes back together again? Git is designed to help with all of these problems. Software is edited on a local computer and is pushed to the github cloud. Changes from multiple people are merged together. Any conflicts are hi-lighted and resolvable.

This article covers the basic steps in sharing FTC software files using the GIT software. This article assumes that your computer has access to the internet. It also assumes you know how to navigate the directory structure using the "cd" and "ls" commands. It also assumes that you have installed the Git Shell on your computer. 

The basic scenarios described below include create a repository in the GIT cloud, Clone the repository from the cloud to your computer, synchronize files from a computer onto the cloud, and resolve a conflict when two people change the same file.

SCENARIO 1 - Create a GIT repository in the cloud. Begin by signing up for an account on github.com. By your login name, select the pulldown menu to pick the "New Repository" menu item.



Give your repository a name and description. I will select the free public version. I also like to start my repository with a readme file. 


When you are done creating your repository, select the "Create repository" button.

SCENARIO 2 - Clone a repository from the cloud to your computer.

Begin by by getting the URL address to your github repository. On you github web page, click the copy URL button. See the copy icon on the right side of the image below.

Open your Git Shell program. In the shell window, change to the directory where you want the cloned repository. Type in the command shown below. Paste in the URL to complete the command.

git clone https://github.com/...

You now have a new git repository. Great job!

SCENARIO 3 - Pull files from your computer to the cloud. Do this before you start any edits for the day. This ensures that you have the latest files.

git pull

SCENARIO 4 - Push files from your computer to the cloud.

Open your Git Shell program. Change directory to your current working git repository. Type in the following commands:

git add FILENAME - This lets git know that something has changed. Do this whenever you change a file. You can do this multiple times before you commit. Note: git add -A adds all changes with one command.
git commit -m "Describe your change here" This gives all of the changes that you "add"ed the same comment. Do this to describe the changes you have finished. You can do this multiple times before you push.
git push - This pushes your changes to the github cloud. Check for errors. If you receive a conflict error, see SCENARIO 5 below.

SCENARIO 5 - Resolve a conflict. This will happen if someone changed the same line that you did in a file. Do this if you receive a conflict error when you try to push changes.

git pull - Download and merge the conflict into your current file. Read the comments carefully. Edit each file that was in conflict. Look for the merge marks in the file. Resolve the conflict and remove the merge marks.
git add -A
git commit -m "Resolve merge..."
git push

Merge marks look like:

<<<<<<< HEAD
Change in TestDEF
=======
Change in Test
Change in Testabc
>>>>>>> 14bf03f8eb9cef08884f85935133957d6da54db3

The merge marks are <<<, ====, and >>>>. They tell you what has changed. The <<<<<HEAD is what is in your file. After === is what is what changed in the file you pulled from github. To fix this, make a decision on what is right and fix the file. A fixed file might look like the following:

Change in TestDEF
Change in Testabc

In this case, I left both my change and the change I pulled in from github in the file.

Friday, October 3, 2014

Design Process

Design Process

The new FTC game has been announced for the year and your team is ready to build a robot. Now what? This article lists a possible set of steps that can be followed to build a great robot. It begins with generating ideas and ends with a functional robot 1 week before the first match.

1) Ideas - As a team we review the rules and brainstorm ideas to achieve game points. Use BrainWriting and BrainSwarming. The result is a chart with points at the top connected to ideas that can achieve the points. The ideas connect to robot resources needed to make the idea work. Write specific ideas down in a paper log book. See the HBR video on how do do Brainswarming to learn more.
2) Robot Story - As robot drivers, we develop a story of what what our robot does to score points during the game. Consider the autonomous, driver period, and end game. Write the story down for your team to review and to work from. After we have a basic story of what the robot does, think about what other teams will do to defend against your strategy. Present the story to the entire team. Use a top-down picture of the field and of the field elements to talk through the story. The team asks questions, offers up new ideas to add to the brainswarm chart, and considers what other teams may do to defend against the story. Write robot story and defense story down in a paper log book.
3) Design Constraints - As robot drivers, develop a list of design constraints from the story. Think about how fast, how reliably, how big/small. Review the results as a team. Write the design constraints down in a paper log book.
5) Paper Prototype Demo - The design teams take the list of ideas, the game story, and the design constraints and develops paper prototypes of the design they want. All paper prototypes are logged in the paper log book. Design is done at the module level. The frame module is detailed out first using a paper prototype. The shape of the frame must match the strategy picked by the team. Everything else will be built on this. Drive modules computing/electronics modules, and scoring module ideas fight for space on the robot frame. The paper prototype is demoed to the entire team. The group talks their way through the drive strategy and design constraints using the paper prototype as a visual. Take pictures of the paper prototypes. Put the pictures in the paper log book along with prototype descriptions.
6) Detailed Design - From paper prototypes, the team uses a CAD tool to create a detailed design for non-standard parts. CAD drawings can also be created of each module for documentation and for future re-use.
7) Working Prototype Demos - Paper prototypes and CAD drawings are turned into the working prototypes. Working prototypes result in a working demo to the entire team. The general sequence of working prototypes is a) Frame complete with discussion of where modules will hang. b) First move demo. (Add drive module, computer module, wiring, and drive software). c) First field demo. (Add one module to interact with a field element.) Demo one small part of the drive story. Repeat for each additional portion of the drive story. At the end of each demo, do a retrospective on what worked well (robot and process) and what can be improved for the next demo. Winning robots build modules that optimize the key design constraints around their story better than other robots.
8) Game Robot Demo - Select a date sometime before the first match (maybe 3 weeks). Each design module team and the SW team decides what is reasonable to have done 1 week before the first match. Prioritize the work that will be done to make sure the most important parts of the robot story work. All CAD design parts need to be complete and attached to their modules. Parts that are not very reliable should have duplicates made for the game match.

Monday, September 15, 2014

FTC Software Toolbox - Rise Edge / Fall Edge

What is your favorite software tool? This post describes some of the tried and true software tools that I have found useful. I would love to hear from you about tools you find useful. Please put a comment at the bottom of this post with a description or a pointer to tools you find useful.

Today we will look at a utility that is very useful to filter button pushes and state machine events. This utility finds the rising edge or a falling edge on a signal.

// Story: As a software designer I want to find the rising edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEqF is initialized equal to false
//   bool globalBoolEqF = false; //global
//   outBool = RiseEdge( inBool, globalBoolEqF );
// Author: David Lempia at GeekMyBot.Blogspot.com
bool RiseEdge(bool in, bool &z1)
{
  bool out;

  out=false;
  if ((in==true) && (z1==false)) out=true;
  z1=in;

  return(out);
}

#define TEST_RISE_EDGE
#ifdef TEST_RISE_EDGE //-------------------------------------------------
// Add this code to a while loop in main
// Unit Test
// [x] Rise Edge of input results in a true output for 1 frame
// [x] Fall Edge of input results in no change to the output
// [x] True input for more than one frame results in a false output
// [x] False input results in a false output
#include "i_debug.c"

task main(){
  bool z=false;
  bool out,in=false;

  for (int i=0; i<10; i++){

    if (i<2) in=false;
    else if (i<4) in=true;
    else if (i<6) in=false;
    else if (i<8) in=true;

    out=RiseEdge(in, z); //Code to test

    DebugBool("In",in);
    DebugBool("Out",out);
    DebugPrint();
  }
}
#endif


Here is the watch window (tabular format) for the tests I ran on a RiseEdge function:



Here is the plot version for the tests I ran on a RiseEdge function:



Whenever the input signal transitions from a false to a true, the output goes true for one frame. This is a great way to trigger an event.
Likewise, here is the software for a falling edge:

// Story: As a software designer I want to find the falling edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEq0 is initialized equal to false
// outBool = RiseEdge( inBool, globalBoolEqF );
// Author: David Lempia at GeekMyBot.Blogspot.com
bool FallEdge(bool in, bool &z1)
{
 bool out;

 out=false;
  if ((in==false) && (z1==true)) out=true;
  z1=in;

 return(out);
}
#define TEST_FALL_EDGE
#ifdef TEST_FALL_EDGE //-------------------------------------------------
// Add this code to a while loop in main
// Unit Test
// [x] Fall Edge of input results in a true output for 1 frame
// [x] Rise Edge of input results in no change to the output
// [x] True input for more than one frame results in a false output
// [x] False input results in a false output
#include "i_debug.c"

task main(){
  bool z=false;
  bool out,in=false;

  for (int i=0; i<10; i++){

    if (i<2) in=false;
    else if (i<4) in=true;
    else if (i<6) in=false;
    else if (i<8) in=true;

    out=FallEdge(in, z); //Code to test

    DebugBool("In",in);
    DebugBool("Out",out);
    DebugPrint();
  }
}
#endif


Here is the resulting output
Let me know if you find this function useful.

Thursday, September 4, 2014

FTC Software Toolbox - Debug

What is your favorite software tool? This post describes some of the tried and true software tools that I have found useful. I would love to hear from you about tools you find useful. Please put a comment at the bottom of this post with a description or a pointer to tools you find useful.

Today we will look at my favorite module, Debug. Of course, setting breakpoints and debugging in Robot C is amazing. Sometimes, however, it is helpful to look at how the program is running using something less intrusive than Robot C debug and more friendly than a print statement. This debug program runs in the background. Results can be viewed in tabular format and as a time-history plot when the robot is reconnected to the PC and Robot C.

#pragma DebuggerWindows("debugstream")
// Story - As a software debugger, I want to see a descriptive string and a variable so that I can understand
//    what my software is doing.
// Usage - To use this software,
//   1. DebugSkip(4); Print out every 5th value. Use any number you want. The default is 0 (no skips).
//   2. Use DebugInt, DebugFloat, or DebugBool to collect the debug information.
//   3. Place the DebugPrint(); after all the debugInt calls. (Takes 10ms or less to run).
//
//   The data is output to the debug stream and to a file called "debug.txt"
//   The data going to the file is limited to 10000 characters. This is about 151 rows with 10 columns.
//
//  Written by David Lempia
//
#define DBG_MAX_COL 10
string debugColName[10];
string debugValue[10];
int debugI=0;
int debugSkip=0;
int debugSkipI=0;
int iFrameCntDebug=0;

short DebugFile;
TFileIOResult isOk;
int nFileSize=10000;
string debugFileName="debug.txt";

// ---------------- Capture integer debug information ---------------------//
void DebugInt(const string name, int value){
  string tmp;
  if (debugI < DBG_MAX_COL){
    if (iFrameCntDebug==0){
      strncat(tmp,name,5);
      sprintf(debugColName[debugI],"%5s",tmp);
    }
    sprintf(debugValue[debugI++],"%5d",value);
  }
  return;
}
// ---------------- Capture float debug information ---------------------//
void DebugFloat(const string name, float value){
  string tmp;
  if (debugI < DBG_MAX_COL){
    if (iFrameCntDebug==0){
      strncat(tmp,name,5);
      sprintf(debugColName[debugI],"%5s",tmp);
    }
    sprintf(debugValue[debugI++],"% 2.2f",value);
  }
  return;
}
// ---------------- Capture boolean debug information ---------------------//
void DebugBool(const string name, bool value){
  string tmp;
  if (debugI < DBG_MAX_COL){
    if (iFrameCntDebug==0){
      strncat(tmp,name,5);
      sprintf(debugColName[debugI],"%5s",tmp);
    }
    if (value==true)
      debugValue[debugI++]="    T";
    else
      debugValue[debugI++]="    F";
  }
  return;
}

// Set the number of times the printout is skipped (Default is to print every time)
void DebugSkip(int skip){
  debugSkip=skip;
}

// Send the print data to the debug window and the debug file "debug.txt"
void DebugPrint(){
  char str[70];
  string tmp1,tmp2;
  int i=0;

  if (debugI>0){// Do not send out Debug info if not needed
    if (iFrameCntDebug==0){
      //writeDebugStream("col size %d", debugI);
      Delete(debugFileName, isOk);
      OpenWrite(DebugFile, isOk, debugFileName, nFileSize);

      strcpy(str,"Frame,");
      for (i=0; i < (debugI-1); ++i){
        strcat(str,debugColName[i]);
        strcat(str,",");
      }
      strcat(str,debugColName[i]);
      strcat(str, "\n");

      writeDebugStream(str);
      WriteString(DebugFile, isOk, str);
    }

    if (debugSkip < debugSkipI++){
      strcpy(str,"");
      sprintf(str,"%5d,",iFrameCntDebug);
      for (i=0; i < (debugI-1); ++i){
        strcat(str,debugValue[i]);
        strcat(str,",");
      }
      strcat(str,debugValue[i]);
      strcat(str,"\n");

      writeDebugStream(str);
      WriteString(DebugFile, isOk, str);

      debugSkipI=0;
    }
    iFrameCntDebug++;
    debugI=0; //Clear the debug buffer
  }
}
//#define TEST_DEBUG //-------------------------------------------------
#ifdef TEST_DEBUG
// Unit Test
// [x] Normal test
// [x] To many debugInt(3) calls
// [x] No calls to debugInt() - No output should happen.
// [x] Skip number is right
// [x] The name is longer than 5 characters. Should be limited to 5.
// [x] Output to file and debug stream match
// [x] Test max file size (File output ends without issue when the max file is hit)
// [x] Test worst case time. It takes between 3 and 10 ms to output all 10 columns of data.
task main(){
  int tmp=0;
  //DebugSkip(2); //

  ClearTimer(T2);

  for (int i=0; i < 10; i++){
    DebugInt("r1",time1[T2]);
    DebugInt("r2",time1[T2]);
    DebugInt("r3",time1[T2]);
    DebugInt("r4",time1[T2]);
    DebugInt("r5",time1[T2]);
    DebugInt("r6",time1[T2]);
    DebugInt("r7",time1[T2]);
    DebugInt("r8",time1[T2]);
    DebugInt("r9",time1[T2]);
    DebugInt("r10",time1[T2]);
    DebugInt("r11",time1[T2]);

    /*DebugInt("1234567",tmp++);
    DebugFloat("r2",2.5);
    DebugBool("r3",false);*/

    //-----------------Print the debug statements out------------------//
    DebugPrint();
  }
}
#endif

Correct usage of the debug software requires a minimum of two function calls, a DebugInt(), DebugFloat(), or DebugBool() (i.e. watch functions) to capture the watch information, and a DebugPrint() to send the print information out to the debug window and the debug file. Up to 10 watch variables can be watched at a time.

Use DebugInt, DebugFloat, or DebugBool function calls to watch variables in the software. Be careful to place the function calls in a place where they will be called on every itteration. If the calls are inside an if-then-else and are not called on an itteration, the past value will be printed out.

Place DebugPrint() in a location that is called after all of the watch functions are called. That is all there is to it. Here is the watch window (tabular format) for the tests I ran on a RiseEdge function:



Here is the plot version for the tests I ran on a RiseEdge function:


To plot this file, I copied the file off of the NXT using Robot C the main menu-> Robot -> NXT Brick -> File Management Utility. I then opened the file in MS Excel and used a line plot.

Let me know if you find this function useful.

Saturday, August 16, 2014

Stories Modular Code and Unit Testing

Stories are a way to define the functionality of software from the perspective of the user of the software. Why use a story instead of a function statement or a requirement? The simple answer is that a story is interesting and starts a conversation. Do you go to a movie at the movie theater to listen to a set of functions or requirements? If you did, people would fall asleep or start to play with their phones after a couple of minutes. No, people go to a movie because the story draws them in and involves them. So what is a story when it relates to software? A great description of a story can be found at Mountain Goat Software. A story takes the form of:

As a <user role/name> I want <goal of function> so that <reason why>.

Stories are a great way to discuss what the software should do. Details or specifics that result from this discussion are captured in the form of short statements called requirements. Requirements are what gets tested.

Modular code is all about taking a big problem, breaking it apart into smaller pieces, and solving the small problems one at a time. Software modules can then be quickly assembled to solve bigger problems. Think of a software module like a tool in your tool box. Is your command to aggressive? Try adding a limiter or a rate limiter. Is your signal noisy? Try adding a lowpass filter. Good software modules can also easily evolve or specialize over time without impacting the software in a negative way. To do this, software modules have 3 very important parts, the function or goal, the interface, and the strategy. Each of these parts needs to exist for the module to be useful as a building block over time. For example, I can take put the software in one module or file that implements one story. Robot C provides one tool to help with modular code, the include statement.

Include will insert any file into my Robot C software.

Unit testing tests the story or function a module of code implements.

Put it all together - Why the introduction to stories, modular code, and unit testing? Because these 3 elements are what makes up the contents of one modular file. Lets walk through the example of a function that I used in the last blog post called "riseedge.c" as shown below:
// Story: As a software designer I want to find the rising edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEqF is initialized equal to false
//   bool globalBoolEqF = false; //global
//   outBool = RiseEdge( inBool, globalBoolEqF );
bool RiseEdge(bool in, bool &z1)
{
  bool out;

  out=false;
  if ((in==true) && (z1==false)) out=true;
  z1=in;

  return(out);
}

#define TEST
#ifdef TEST //-------------------------------------------------
// Add this code to a while loop in main
// Unit Test
// [x] Rise Edge of input results in a true output for 1 frame
// [x] Fall Edge of input results in no change to the output
// [x] True input for more than one frame results in a false output
// [x] False input results in a false output

task main(){
 char str[20];
 
  int iFrameCnt=0;
  bool z=false;
  bool out,in=false;
  short DebugFile;
  //TFileHandle DebugFile;
  TFileIOResult isOk;
  int nFileSize=4000;
  OpenWrite(DebugFile, isOk, "debug.txt", nFileSize);
  while(iFrameCnt<10){

    if (iFrameCnt<2) in=false;
    else if (iFrameCnt<4) in=true;
    else if (iFrameCnt<6) in=false;
    else if (iFrameCnt<8) in=true;

    out=RiseEdge(in, z);
  
    if (iFrameCnt==0){
      sprintf(str,"Frame,In,Out\n");
      WriteString(DebugFile, isOk, str);   
      writeDebugStream(str);
    }
    
    sprintf(str, "%2d,%2d,%2d\n", iFrameCnt, in, out);
    WriteString(DebugFile, isOk, str);
    writeDebugStream(str);
 
    wait1Msec(50);
    iFrameCnt++;
  }
  Close(DebugFile,isOk);
}
#endif
The first four lines of the file contain the story and the usage of the software module as shown below:
// Story: As a software designer I want to find the rising edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEqF is initialized equal to false
//   bool globalBoolEqF = false; //global
//   outBool = RiseEdge( inBool, globalBoolEqF );
The story quickly describes the purpose of the function. The usage helps a person understand how to add this module to their software. With the usage, it is important to describe the details or requirements of the interface. In this case, the interface is a boolean and it must be defined as a global. If the interface contained an integer, it is important to understand the expected range of the interface (i.e. -179 to 180 degrees, positive turns the robot to the left) the units of the interface, and the context. I often find reusable components defined without the the interface details. I end up running experiments to figure out the range of values, the units, and the context. This is part of software programming that is not that much fun. The function called RiseEdge follows the story and usage. This is followed by the software needed for unit testing. The software #ifdef begins the test. If "#define TEST" is commented out, the module can be inserted into your code. If it is not commented out, as shown below, the software can be loaded directly onto the NXT for unit testing. For unit testing use:
#define TEST
#ifdef TEST
To turn off the unit testing and use the file as an insert, use:
//#define TEST
#ifdef TEST
The unit test code begins with the requirements for the use case as shown below:
// Add this code to a while loop in main
// Unit Test
// [x] Rise Edge of input results in a true output for 1 frame
// [x] Fall Edge of input results in no change to the output
// [x] True input for more than one frame results in a false output
// [x] False input results in a false output
The requirements use a [x] to indicate that the test has been run and passes this requirement test. I start with a [] to indicate that the test has not passed yet. This pass/fail marking gives confidence that the function has been tested and should work as advertised. The main portion of the unit test is shown below:
    if (iFrameCnt<2) in=false;
    else if (iFrameCnt<4) in=true;
    else if (iFrameCnt<6) in=false;
    else if (iFrameCnt<8) in=true;
This software toggles the input true and false for 2 frames. This is enough to check the requirements for the test. The results are output to both the debug stream and to a local file on the NXT. If I want a time-history plot of the test output I open the Robot C program. I use the main menu "Robot->NXT Brick->File Management Utility" to copy the "Debug.txt" file to my PC. I can then use MS Excel to plot the results.

State Machines

State Machines

A state machine is a design mechanism that places the software system into one (or more) states. Triggers are used to move from one state to another state.

What can we use this concept for in Robotics? We can use a state machine any time we want to perform a sequential set of steps or any time we want to perform a set of steps driven by an event or behavior. For example, a sequential set of steps may be following a path. An event may be the robot reaching a wall or seeing a line on the floor.

State machines are often drawn as circles with lines joining the circles together. This is called the software design. Design is very useful for understanding the big picture of how the software works. Complex software statements are simplified down into pseudo code (English statements).The lines are events that move the state machine from one state to the next. In a statemachine, the behaviors of interest can be run whenever the state is active, or on transition from one state to the next. Here is an example of a very simple state machine that is used to turn a light on and off:



The state machine begins in the LIGHT_OFF state. (See the black dot with the arrow pointing into the LIGHT_OFF state). In this state, the word "Off is displayed on the NXT screen. When button 1 (the x button) is pressed on the joystick and a rising edge is detected (the symbol before joy1Btn), the state machine transitions into LIGHT_ON.

In the LIGHT_ON state, the word "ON" is displayed on the NXT screen. When button 1 (the x button) is pressed on the joystick and a rising edge is detected (the symbol before joy1Btn), the state machine transitions into LIGHT_OFF.

Why use a rising edge detector instead of looking from a true value on joy1Btn(1)? If a TRUE value from joy1Btn(1) caused the transition, the state machine would jump back and forth between LIGHT_ON and LIGHT_OFF until the joystick button is de-selected. Try this out in the code to see what it does. This is called a race condition. The race condition can be prevented in a number of ways. For example, I could have used a different button to transition back.

Now that the design is complete, it is time to write software. The statemachine needs to be called continually. To do this, the periodic task program, described in a previous blog post, or a while(true) is used. See the following software:

//
// As a home owner, I want to turn a light on and off with my joystick so I can see where I am going when it is dark.
//
#include "JoystickDriver.c"
#include "i_riseEdge.c"

bool btn_z1=false;   // Used with FallEdge

// My states
#define LIGHT_ON 1
#define LIGHT_OFF 2
int sm_light = LIGHT_OFF; //The default state

task main(){
  while(true){

    getJoystickSettings(joystick);     // update buttons and joysticks
    eraseDisplay();

//-------------------------- Start of the State Machine ----------------//
    switch (sm_light) {
    case LIGHT_OFF:                     //State
      // Do this when the light is off
      nxtDisplayString(2, "Light Off");
      if (RiseEdge(joy1Btn(1),btn_z1)){ //Transition
        // Do this on a transition
   sm_light=LIGHT_ON;
      }
      break;
    case LIGHT_ON:                      //State
      nxtDisplayString(2, "Light On");
      if (RiseEdge(joy1Btn(1),btn_z1)){ //Transition
        // Do this on a transition
        sm_light=LIGHT_OFF;
      }
      break;
    }
// ------------------------- End of the State Machine ------------------//
    wait1Msec(50);
  }// While Loop
}// Foreground Task

The states are defined using a "#define STATE_NAME 1" statement. If you want an additional state, just add a new #define statement. Make sure each # define has a unique integer number assigned to it. The current active state is defined as an integer in the line "int sm_light = LIGHT_OFF;"  This line says the "LIGHT_OFF" state is the initial state the machine "sm_light" will enter when called.

The work of the state machine is done with a switch/case structure. Each case is a state for the state machine. Transitions are implemented with an if (){ sm_light=LIGHT_OFF} statement. Any code placed within the case statement will execute every iteration. This is where I placed the nxtDisplayString statement.

The function called RiseEdge looks for a rising edge of a boolean signal. RiseEdge returns a true value only when the input signal is true and the past frame signal is false. Note: the & in front of z1 in the function argument passes the address of z1 into the function call. This allows z1 to act as both an input and an output from the function call. See the following code:

// Story: As a software designer I want to find the rising edge of a signal so that I can trigger an event
// Usage: Make sure globalBoolEqF is initialized equal to false
//   bool globalBoolEqF = false; //global
//   outBool = RiseEdge( inBool, globalBoolEqF );
bool RiseEdge(bool in, bool &z1)
{
  bool out;

  out=false;
  if ((in==true) && (z1==false)) out=true;
  z1=in;

  return(out);
}

This describes the basics behind creating a state machine design and implementing it in software. Here is an example of a state machine that drives a robot in on a path in the shape of a square box 5 inches in size:



Try to develop a state machine that does this. Note: the /num=0 is software that executes on a transition. This means the "num=0" is done when the variable is defined and the /num++ incriments this variable on the transition from Drive_Straight to Turn_Right. The transition "5 inches" and "90 degrees" can be done using the distance of the encoders. Let me know how you do.

Saturday, March 1, 2014

Periodic Tasks

This post looks at periodic tasks. The environment this applies to is the US First Robotics FTC Robot C software running on a Lego NXT brick.

Periodic tasks run at a specific and consistent time interval. For example, a task may be set to run every second or every 50ms. Why are periodic tasks interesting? A periodic task makes filtering signals, counting time, and controlling devices very simple. For example, if I have a periodic task set at 1 second, I can add a simple counter to my task. For example:

int secondsCounter=0;

task Periodic_1_second(){
   secondsCounter += 1;
}

 This will increment the seconds count once each time the task is called. After 60 seconds, I have counted to a minute.

 Periodic tasks make it possible to have signal shaping functions such as a lowPass filter or a rateLimiter. I will talk about these primitive functions in a future blog. For now, here is the Robot C code for the simplest task I can create:


// Periodic Model used to read sensors and control the motors on a NXT brick
//
// As a software programmer, I want my tasks to run at a periodic rate. A periodic rate makes control, filtering,
//    and counting much easier.
//
#pragma DebuggerWindows ("debugStream")// Bring up the debug stream window

int count=0;
int timeLeft=0; // A global variable that sticks around each frame

// ------------------------Foreground Task -----------------------------//
// Run every second
#define FOREGROUND_MS 1000 //I change this to 50 when I control the robot. 1000 is good for testing.
task main(){
 while(true){
  ClearTimer(T1);
  hogCPU(); //Prevent other tasks from running when this one is.

  for(long i=0; i<30000; ++i){} //Add some code to see the time used in the foreground. This takes about 0.5 seconds

  writeDebugStreamLine("Foreground\n"); //Let me know when the foreground runs

   count=count+1; // Count the number of times the foreground runs.

   timeLeft=FOREGROUND_MS-time1[T1]; // Calculate the time used in the foreground
   releaseCPU(); // Let other tasks run now.
   wait1Msec(timeLeft);// The time other tasks have to run before foreground takes control.
   writeDebugStreamLine("Count=[%2i] Time Left = %i\n",count,timeLeft);

   }// While Loop
}// Foreground Task

The next set of code is similar to this one. It has a background task with a low execution priority and a foreground task with a high execution priority.
// Periodic Model used to read sensors and control the motors on a NXT brick
//
// As a software programmer, I want my tasks to run at a periodic rate. A periodic rate makes control, filtering,
//    and counting much easier.
//
#pragma DebuggerWindows ("debugStream")// Bring up the debug stream window

int count=0;
int timeLeft=0;

// ------------------------Foreground Task -----------------------------//
// Run every second
#define FOREGROUND_MS 1000 //I change this to 50 when I control the robot. 1000 is good for testing.
task Foreground(){
 while(true){
  ClearTimer(T1);
  hogCPU(); //Prevent other tasks from running when this one is.

  for(long i=0; i<30000; ++i){} //Add some code to see the time used in the foreground.

  writeDebugStreamLine("Foreground\n"); //Let me know when the foreground runs

   count=count+1; // Count the number of times the foreground runs.

   timeLeft=FOREGROUND_MS-time1[T1]; // Calculate the time used in the foreground
   releaseCPU(); // Let other tasks run now.
   wait1Msec(timeLeft);// The time other tasks have to run before foreground takes control.
 }// While Loop
}// Foreground Task

// -------------------------Main Task----------------------------------//
task main(){
 count=0; //How many

 // Put initilization code here. This code will only run one time

 // Start the forground task with the highest priority
 StartTask(Foreground, 255);

 // This code runs in time left after the foreground task runs
  while(1==1){
   wait1Msec(1000); // This slows the background task down to the same speed as the foreground. Try different numbers.
   writeDebugStreamLine("Count=[%2i] Time Left = %i\n",count,timeLeft);
  }// While Background
}//Main Background
Try changing the wait1Msec statement in the background to 500 or 250. Why does the same number get printed out many times? Try changing the FOREGROUND_MS number to 500 or 250 when the background stays at 10000. Why does the counter count up more than one time? In the next posting, I will show you a simple state machine that moves the robot around in a square. Please give me a shout out if you find this software helpful.

Sunday, February 23, 2014

Why Geek My Bot

Welcome to Geek My Bot or advanced Geeky Robotics solutions for the US First FTC Robotics competition. In this blog I will examine how a US First Robotics team solves some of the stickier mechanical, software, and team collaboration issues. As an engineer I am passionate about developing talented and enthusiastic new engineers to solve some of the worlds toughest issues. Along the journey, I hope to show you some tools and techniques you can add to your FTC robotics toolbox. In a spirit of gracious professionalism, please give a shout out if you read something you like and share back if you find new and better tools and techniques.