C++ Multitasking with Threads Code Problem

Need clarification on proper format of multi-tasking with threads?

Your code is confused. Topics to go read up on:

Assignment to a variable. Specifically, that it happens once and is not continuous (as you appear to be trying to do with your joystick global variables).

Function declaration. You don’t know the syntax for starters, but you’ve also written 9 functions that could be accomplished by 3. I.e., you don’t need a function for every joystick axis, you need a single function that is passed the value of a joystick axis and returns a modified value:

int add_one (int x) { return x + 1; }

Next, you declare threads in a loop. The best use of threads is to accomplish some specific task that would be onerous to accomplish without them. The functions you are trying to seed these threads with have no blocking elements in them (in the code you have provided, anyway), and could be done synchronously just as easily. Even if there were blocking elements, creating additional threads to do the same task before the first ones have finished would result in them stepping on each others’ toes and malfunctioning.

Lastly for now, as a matter of convention, definitions don’t go in .h files, only declarations. You would put the definitions you have in “drive.h” into a “drive.cpp” instead, and replace the definitions in “drive.h” with forward declarations.

My recommendation for you is forget that threads exist for now and focus on walking before you run.

1 Like

Tyler

Thank you for the advice. I will take your suggestions to improve my code.

As the programmer for our team, I have considered your reply and updated some code accordingly. I believe it matches your suggestions.

However there is still a problem of a “parameter input” issue. The problem is probably due, we are assuming, to a data type mis-match. It is a challenge, you might help.

This is one of the questions we were asking in the original post. The other question, was a inquiry for conformation that our team was using threading correctly.

Thank you.

I’ll be sure to take a look for you tomorrow.

2 Likes

v5code-project-650N.zip (15.1 KB)

Thank you for your offer to help.

I was able to fix the errors by adding input type in your functions.

This input doesn’t state the type:

double ceil_drive_lval( drive_lval ){

but this one does:

double ceil_drive_lval( int drive_lval ){

Additionally, when you call the functions you just write their names like this:

LFmotor.spin( directionType::fwd, cube_drive_lval, velocityUnits::pct );

but when you call a function it needs parentheses for inputs like so:

LFmotor.spin( directionType::fwd, cube_drive_lval(5), velocityUnits::pct );

Looking at your functions it appears you are just returning controller values. I don’t see a use for inputs. Therefore, another solution would be to remove the input:

double  cube_drive_lval(){	
    return  pow(ceil_input_lval/100.0, 3.0)*100.0;
}

LFmotor.spin( directionType::fwd, cube_drive_lval(), velocityUnits::pct );

EDIT:
Also note if you intend to have an input it must be reflected in your header

2 Likes

v5code-project-650N.zip (15.5 KB)

@John_TYler @deicer Thank you for the suggestions.
Updated project, possible solution.
Any feedback will be appreciated.

Your variables are not being updated through the loop. Instead, they’ll spin however fast the initial read told it to. One way would be to add this in your main loop:

while(true){ //this is the loop you already have, don't copy the loop into your loop.
    ceildrive_lval = ceil_drive_lval(drive_lval);
    ceildrive_rval = ceil_drive_rval(drive_rval);
    cubedrive_lval = cube_drive_lval(ceildrive_lval);
    cubedrive_rval = cube_drive_rval(ceildrive_rval);
  //rest of code
}

This’ll update the value continually so the motors can change their velocity.


However, I think you have more variables then you need. You could just call a single function in the motor spin command. Here’s what I got when I combined your functions and called them in the motor spin command (with a slightly reworked while loop):

//couldn't find function 'ceil'.  It can probably be added here as well.
double unifiedTheoryOfPotatoes(double drive_val){
  double var = ceil(drive_val);
  return(pow(var/100.0, 3.0)*100.0);
}

//not returning anything, I'll make this function a void.
void tankDrive_F(){
  double leftPow;
  double rightPow;
  while(1){
    //note these next 2 lines specifically.  It's in the while loop so it'll update the motor power.
    leftPow = unifiedTheoryOfPotatoes(drive_lval);
    rightPow = unifiedTheoryOfPotatoes(drive_rval);

    LFmotor.spin( directionType::fwd, leftPow, velocityUnits::pct );
    LBmotor.spin( directionType::fwd, leftPow, velocityUnits::pct );

    RFmotor.spin( directionType::fwd, rightPow, velocityUnits::pct );
    RBmotor.spin( directionType::fwd, rightPow, velocityUnits::pct );
  }
}

As you can see it’s a lot simpler and cuts out your middleman variables/functions. Obviously you wouldn’t call your function ‘theFundementalTheoryOfPotatoes’, but you get the idea.

3 Likes

I would add vex::task::sleep(10) to any while(1) loop to make sure you are not hogging CPU.

But I still don’t understand how you set values to drive_lval and drive_rval and why would you need to run this in the separate thread?

Why is it not good enough to have a single user control while(1) loop inside usercontrol() function that reads Controller.Axis values and commands motors?

Now that I learned the “secret knowledge”, I totally would!

2 Likes

seems like you had some good feedback today. I think your issues come from not understanding the difference between local and global variables and how/when they get updated. Here’s a revised version of your tank drive code, many of the variables you had as global need to be local and updated within the while loop, assignment to a variable only happens when the line of code is executed, assignment outside of all functions and threads would only happen once when the program is executed, and functions are not even called at that time.

tankDrive.cpp
/*----------------------------------------------------------------------------------------*/
/*                                                                                        */
/*   	Module:       tankDrive.cpp                                                        */
/*   	Author:       NMS_RP: 650N                                                         */
/*   	Created:      16 Dec 2019                                                          */
/*  	Description:  tank drive with deadband and cubic power input for VEX V5            */
/*                                                                                        */
/*----------------------------------------------------------------------------------------*/

// VEX V5 C++ Project
#include "vex.h"
using namespace vex;

// deadband threshold
const int deadband = 15; 

/** 
  * --- Cubic Scale Factor Functions for Power Adjustment --- 
  * Description: Functions used to achieve more sensitivity at low motor power
  *              used for precise movements while still being able to use 100% power
  *
  */
// ceiling function, function definition, function call
double ceil_drive_lval( double drive_lval ){
  return ceil(drive_lval);
}

double ceil_drive_rval( double drive_rval ){
  return ceil(drive_rval);
}

// cube function, function definition, function call
double cube_drive_lval( double ceildrive_lval ){			// tank drive cube scale factor left joy
    return  pow(ceildrive_lval/100.0, 3.0)*100.0;
}

double cube_drive_rval( double ceildrive_rval ){				// tank drive cube scale factor right joy
    return  pow(ceildrive_rval/100.0, 3.0)*100.0;
}

/**
  * --- tankDrive_f is a callback function ---
  * func to be registered to a thread
  * func registered to thread tankdrive_t
  *
  */
int tankDrive_f(){
  int count = 0;
    while(true){
      Brain.Screen.setCursor(1,1);
      Brain.Screen.print("TankDrive has iterated %d times", count);
      count++;

      // tank drive func --- variable intializations
      double drive_lval = Controller1.Axis3.position();          // tank drive control
      double drive_rval = Controller1.Axis2.position();          // tank drive control

      // deadband, set to 0 if below the deadband value
      if( fabs( drive_lval ) < deadband ) drive_lval = 0;
      if( fabs( drive_rval ) < deadband ) drive_rval = 0;

      double ceildrive_lval = ceil_drive_lval( drive_lval );
      double ceildrive_rval = ceil_drive_rval( drive_rval );

      double cubedrive_lval = cube_drive_lval( ceildrive_lval );
      double cubedrive_rval = cube_drive_rval( ceildrive_rval );
    
      // just for fun, display the values
      Brain.Screen.setCursor(3,1);
      Brain.Screen.print("Raw %6.2f cube %6.2f", drive_lval, cubedrive_lval );
      Brain.Screen.setCursor(4,1);
      Brain.Screen.print("Raw %6.2f cube %6.2f", drive_rval, cubedrive_rval );

      /* --- send to motors --- */
      // left tank motors
      LFmotor.spin( directionType::fwd, cubedrive_lval, velocityUnits::pct );
      LBmotor.spin( directionType::fwd, cubedrive_lval, velocityUnits::pct );
      // right tank motors
      RFmotor.spin( directionType::fwd, cubedrive_rval, velocityUnits::pct );
      RBmotor.spin( directionType::fwd, cubedrive_rval, velocityUnits::pct );

      /* You must sleep threads by using the 'this_thread::sleep_for(unit in
        msec)' command to prevent this thread from using all of the CPU's
        resources. */
      this_thread::sleep_for( 25 );
    }
  /* A threads's callback must return an int, even though the code will never
    get here. You must return an int here. Threads can exit, but this one does not. */
  return 0;
}
5 Likes

@jpearman Awesome, Thank you for the clarifications. :pray:

@deicer Thank you for the help. Suggestions were greatly appreciated. :grin: