Joystick dynamic calibration example.

This sketch is for the bot builders that are having issues with joysticks.

#define Debug//Comment out if you do not want debugging info sent to the serial monitor.
//for the sake of arguement I will assume the x axis is on A2 and the y axis on A3.
//These are the white 3 pin plugs on the Motor v3 board.

long X_Max;
long X_Min;
long Y_Min;
long Y_Max;
long X_Center;
long Y_Center;
boolean IsCalibrated_X;
boolean IsCalibrated_Y;
long mCalSampleX;
long mCalSampleY;

void setup()
  {
    X_Max = 0;
    Y_Max = 0;
    X_Min = 4096;
    Y_Min = 4096;
    IsCalibrated_X = false;
    IsCalibrated_Y = false;
    pinMode(2,INPUT);
    pinMode(3,INPUT);
    #ifdef Debug
    Serial.begin(9600);
    #endif
  }
void calibrate_X()
  {
    while ((mCalSampleX >= X_Max) || (mCalSampleX <= X_Min))
      {//Read and test the A2 pin and store values in X_Max and X_Min. If mCalSample is greater than X_Min then store current Value in X_Min.
       //Likewise if mCalSample is less than X_Min then store it in X_Min.
       if (mCalSampleX > X_Max)
        {X_Max = mCalSampleX;}
      else if (mCalSampleX < X_Min)
        {X_Min = mCalSampleX;}
      mCalSampleX = analogRead(A2);
      delay(1500);//delays the call so that the pot movement is captured. increase delay if you have troubles getting a calibration consistent.
      }
  }
void calibrate_Y()
  {
    while ((mCalSampleY >= Y_Max) || (mCalSampleY <= Y_Min))
      {//Read and test the A3 pin and store values in Y_Max and Y_Min. If mCalSample is greater than Y_Min then store current Value in Y_Min.
       //Likewise if mCalSample is less than Y_Min then store it in Y_Min.
      if (mCalSampleY > Y_Max)
        {Y_Max = mCalSampleY;}
      else if (mCalSampleY < Y_Min)
        {Y_Min = mCalSampleY;}
      mCalSampleY = analogRead(A3);
      delay(1500);//delays the call so that the pot movement is captured. increase delay if you have troubles getting a calibration consistent.
     }
  }
void loop()
  {
    if ((IsCalibrated_X == false) || (IsCalibrated_Y == false))//Conditional statement for exiting the calibration routine.
      {
      #ifdef Debug
      Serial.println("Beginning X Axis Calibration.");
      #endif
      calibrate_X();
      #ifdef Debug
      Serial.print("X Max=");
      Serial.print(X_Max);
      Serial.print("; X Min=");
      Serial.print(X_Min);
      Serial.print("; X Center=");
      Serial.println((X_Max-X_Min)/2);
      Serial.println(analogRead(A2));;
      delay(3500);
      Serial.println("Beginning Y Calibration");
      #endif
      calibrate_Y();
      #ifdef Debug
      Serial.print("Y Max=");
      Serial.print(Y_Max);
      Serial.print("; Y Min=");
      Serial.print(Y_Min);
      Serial.print("; Y Center=");
      Serial.println((Y_Max-Y_Min)/2);
      Serial.println(analogRead(A3));
      delay(3500);
      Serial.println("Calibration Complete");
      #endif
    }
  }

What's the question?
If you're going to initialise globals in setup, you may as well initialise them when you declare them, and save yourself the extra typing.

True I could, but I'll let the end user make that call. I have less problems keeping declarations and assignments apart. As for the question there isn't one. there were several threads in the past 2 days with joystick issues.

Do you want me to move this to Exhibition?

Certainly, if you think it will be more accessible and helpful to others. Although it might be better served to wait until I have written the channel mixer for controlling skid steered vehicles with a single 2 axis stick.

If you're going to post this as an example, don't you think you should explain what you're doing with digital pins 2 and 3, and why you're making them inputs, or remove the superfluous code?

I hate code that has chunks of replicated logic. It's a maintenance bug waiting to happen, as well as wasting screen space in the editor and probably code space at runtime.

Don't these calibration functions cry out to you that they should be using parameters?

Atleast you could elaborate on the points that you are bashing about. No one will learn a thing here if make hateful remarks with no direction given on how you think it would be better written.

Sorry, I thought that was obvious.

The code has two calibration functions calibrate_X() and calibrate_Y() which are identical except for the global variables they operate on. The two copies could be merged by providing these variables as arguments instead of hard-coding the global variable names.

The purpose was to aide a few that have enough trouble with inline code, let alone wrappered code blocks, at least while seperate they can only dork up one motor(hopefully) and still have the similar code to use to restore the erroeous edit. Mayby its just me, I only compress things after it all works the way I want or my chip runs low on program space.

AWOL:
If you're going to post this as an example, don't you think you should explain what you're doing with digital pins 2 and 3, and why you're making them inputs, or remove the superfluous code?

The extra pins were originally going to be a means of invoking the calibration routine. In the motor3 example they are gone.

ajofscott:
The purpose was to aide a few that have enough trouble with inline code, let alone wrappered code blocks, at least while seperate they can only dork up one motor(hopefully) and still have the similar code to use to restore the erroeous edit. Mayby its just me, I only compress things after it all works the way I want or my chip runs low on program space.

I would recommend following best practices, especially given that this example is intended to help novices.

Replicating code is a very common mistake by people who are not experienced programmers. While it is nice and simple in some respects (no need to understand how those pesky functions and arguments work, no need to think about factoring out common functions) it is a poor approach that encourages code bloat and copy/paste errors. Although this is a simple bit of code and only involves a dozen lines, an inexperienced programmer following this example would be tempted to use the same approach for everything else. So you could easily end up with replicated code all over the place. I recommend following the DRY principle (Don't Repeat Yourself). Design the algorithm, implement it once and use that in all the places you need to use it.

By the way, your calibration algorithm doesn't seem to make any allowance for jitter. Perhaps this is the cause of the calibration problems you refer to in your code? You could probably deal with jitter by continuing your loop until your input was some distance below the peak input, rather than stopping as soon as it was below the peak. If you change it, remember to change both copies. :wink:

Have you considered encapsulating this calibration logic and the logic to apply the calibration subsequently? I assume there's some scaling conversion going on to convert the raw joystick position using these calibration values. If you encapsulate the logic to do the initial calibration, and to apply that scaling conversion, into a single object then you have the option to update the calibration later if you find the joystick is going outside the range you saw during the calibration. I'm sure you've seen cases where the joystick didn't hit the end stop very firmly during calibration, and then gets shoved harder later on and goes that bit further. It's the sort of thing that could cause problems if your scaling code isn't designed to cope with it.

This is really quite a poor example to be giving noobs in embedded computing.

long X_Max;
long X_Min;
long Y_Min;
long Y_Max;
long X_Center;
long Y_Center;
long mCalSampleX;
long mCalSampleY;

These variables consume twice as much memory as they need to, and the last two don't need global scope at all, and so are a complete waste of precious memory.

With a simple "struct", I got the sketch without debug down to under 900 bytes from more than 1350.

I shall take all the criticisms in stride such as the "cut and paste noob" reference. Guilty as charged, however, as I have stated before, I compress things after things work, make a class, and reuse code as classes. Which is still cut and paste when you really get down to it, it is just done without use of select, ctl+c, and ctl+v. The routine is really meant to cope with the fact axis pots don't output 0-VPot, some are constrained by hardware such as endpoint fixing resistors as well as electrical trims.