Problème de calcul de temps de trajet d'un slider photo

Bonjour, j’ai réalisé un projet de slider motorisé trouvé sur internet et j’ai des soucis au niveau du temps de déplacement du slider. Si je règle le temps de trajet sur 800 sec ou moins, le temps de déplacement est correct, mais à partir de 830 sec, le temps ne correspond pas, par exemple avec un réglage de 830 sec, le trajet s’effectue en 5 sec et cela vaut pour les valeurs jusqu'à 1800 sec. J’ai essayé de contacter l’auteur du code mais je n’ai pas eu de réponse, c’est pour cela que je pose la question sur ce forum, car je n’ai pas compris ou était calculé le nombre d’impulsion du moteur pas à pas suivant le temps de trajet demandé. Ci dessous le code du projet. J’espère que j’ai choisi la bonne catégorie du forum.

Merci d’avance

//The DIY Life
//Michael Klements
//13 January 2021
//License: CC BY-NC-SA

#include <SPI.h>  //Import libraries to control the OLED display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <math.h>

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

#define OLED_RESET -1                                                      // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);  // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

// 'The DIY Life', 128x64px
const unsigned char logo[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x01, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x7f, 0xc0, 0x00,
  0x00, 0x02, 0x03, 0x41, 0xfe, 0x00, 0x00, 0x03, 0xfc, 0x07, 0xfe, 0x00, 0x00, 0x5f, 0xc0, 0x00,
  0x00, 0x03, 0x33, 0x47, 0xff, 0x80, 0x00, 0x03, 0xfc, 0x03, 0xfe, 0x00, 0x00, 0x9f, 0xc0, 0x00,
  0x00, 0x03, 0x30, 0x43, 0xff, 0xe0, 0x00, 0x03, 0xfc, 0x03, 0xff, 0x00, 0x00, 0x37, 0x80, 0x00,
  0x00, 0x03, 0x30, 0x43, 0xff, 0xf8, 0x00, 0x03, 0xfc, 0x01, 0xff, 0x00, 0x01, 0x04, 0x00, 0x00,
  0x00, 0x03, 0x33, 0x47, 0xff, 0xfc, 0x00, 0x03, 0xfc, 0x01, 0xff, 0x80, 0x01, 0x80, 0x00, 0x00,
  0x00, 0x03, 0x33, 0x41, 0xff, 0xfe, 0x00, 0x03, 0xfc, 0x00, 0xff, 0x80, 0x02, 0x61, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xfc, 0x00, 0xff, 0xc0, 0x02, 0xd8, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x1f, 0xff, 0x80, 0x03, 0xfc, 0x00, 0x7f, 0xc0, 0x04, 0x1e, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x07, 0xff, 0x80, 0x03, 0xfc, 0x00, 0x7f, 0xe0, 0x04, 0x1c, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x01, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x3f, 0xe0, 0x03, 0x04, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x3f, 0xf0, 0x00, 0xc0, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x1f, 0xf0, 0x0c, 0x38, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x1f, 0xf0, 0x1f, 0x10, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x0f, 0xf8, 0x0f, 0x90, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x0f, 0xf8, 0x23, 0x20, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x07, 0xfc, 0x30, 0x20, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x07, 0xfc, 0x3c, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x07, 0xfe, 0x7f, 0x40, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x03, 0xfe, 0x7f, 0x80, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0xff, 0xe0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x00, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x03, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x07, 0xff, 0x80, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x00, 0x1f, 0xff, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xfe, 0x01, 0xff, 0xff, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00,
  0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

#define minTravDist 25  //Define travel distance initial setting, minimum, maximum and increment
#define maxTravDist 505
#define travDistInc 5
#define initialDur 120  //Define duration initial setting, minimum, maximum and increment
#define minDur 10
#define maxDur 1800
#define durInc 5
#define initialRotAng 180  //Define rotation initial setting, minimum, maximum and increment
#define minRotAng 20
#define maxRotAng 360
#define rotAngInc 10
#define initialObjDist 200  //Define object distance initial setting, minimum, maximum and increment
#define minObjDist 150
#define maxObjDist 5000
#define objInc 50
#define minInterval 400  //Minimum interval time between pulses in microseconds

static int pinA = 2;                    //Hardware interrupt digital pin 2
static int pinB = 3;                    //Hardware interrupt digital pin 3
volatile byte aFlag = 0;                //Rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0;                //Rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile int encoderPos = 0;            //Current value of encoder position, constained between limits below
volatile int prevEncoderPos = 0;        //To track whether the encoder has been turned and the display needs to update
volatile byte reading = 0;              //Stores direct value from interrupt pin
#define encButton 4                     //Define encoder pushbutton pin
byte oldButtonState = HIGH;             //First button state is open because of pull-up resistor
const unsigned long debounceTime = 10;  //Debounce delay time
unsigned long buttonPressTime;          //Time button has been pressed for debounce

int encLowLim = 0;  //Variables to store the encoder limits and increment
int encHighLim = 3;
int encIncrement = 1;
int dataInputNo = 0;
int modeSelected = 0;  //Current operating mode (Pan, Rotate, Pan & Rotate, Track Object)

#define enablePin 5   //Define motor enable pin
#define travDirPin 6  //Define travel & rotation stepper motor pins
#define travStepPin 7
#define rotDirPin 8
#define rotStepPin 9

float travDist = maxTravDist;    //Distance to travel across slider in millimeters
float travTime = initialDur;     //Travel time to cover the required distance in seconds
float objDist = initialObjDist;  //Distance of tracked object from slider in millimeters
int travelDir = 0;               //Deifne initial travel and rotation directions
int rotDir = 0;
int rotAngle = 180;  //Angle to rotate camera around axis

float pulsesPerMM = 50;       //Number of motor pulses for 1mm travel
float pulsesPerDeg = 4.4444;  //Number of motor pulses for 1 degree of rotation
float currentDist = 0;
float currentAngle = 0;

void setup() {
  Serial.begin(9600);                              //Start serial communication for debugging
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))  //Connect to the OLED display
  {
    Serial.println(F("SSD1306 allocation failed"));  //If connection fails
    for (;;)
      ;  //Don't proceed, loop forever
  }
  pinMode(pinA, INPUT_PULLUP);       //Set pinA as an input, pulled HIGH to the logic voltage
  pinMode(pinB, INPUT_PULLUP);       //Set pinB as an input, pulled HIGH to the logic voltage
  attachInterrupt(0, PinA, RISING);  //Set an interrupt on PinA
  attachInterrupt(1, PinB, RISING);  //Set an interrupt on PinB
  pinMode(encButton, INPUT_PULLUP);  //Set the encoder button as an input, pulled HIGH to the logic voltage
  pinMode(enablePin, INPUT);         //Open circuit enable pin, disables motors
  pinMode(travDirPin, OUTPUT);       //Define the travel stepper motor pins
  pinMode(travStepPin, OUTPUT);
  pinMode(rotDirPin, OUTPUT);  //Define the rotation stepper motor pins
  pinMode(rotStepPin, OUTPUT);
  digitalWrite(travDirPin, HIGH);  //Set the initial direction of motion for both motors
  digitalWrite(rotDirPin, HIGH);
  display.clearDisplay();                          //Clear the display
  display.setTextColor(SSD1306_WHITE);             //Set the text colour to white
  display.drawBitmap(0, 0, logo, 128, 64, WHITE);  //Display bitmap from array
  display.display();
  delay(2000);
  display.clearDisplay();            //Clear display
  Serial.println("Setup complete");  //Write to serial monitor to indicate the setup function is complete
}

void loop() {
  encLowLim = 0;  //Mode selection menu, 4 modes
  encHighLim = 3;
  encIncrement = 1;
  updateMainMenu();

  boolean confirmed = false;  //Both used to confirm button push to select mode
  boolean pressed = false;
  encoderPos = 0;     //Encoder starts from 0, first menu option
  while (!confirmed)  //While the user has not confirmed the selection
  {
    byte buttonState = digitalRead(encButton);
    if (buttonState != oldButtonState) {
      if (millis() - buttonPressTime >= debounceTime)  //Debounce button
      {
        buttonPressTime = millis();    //Time when button was pushed
        oldButtonState = buttonState;  //Remember button state for next time
        if (buttonState == LOW) {
          modeSelected = encoderPos;  //If the button is pressed, accept the current digit into the guessed code
          pressed = true;
          Serial.println("Button Pushed");
        } else {
          if (pressed == true)  //Confirm the input once the button is released again
          {
            confirmed = true;
            Serial.println("Mode confirmed");
          }
        }
      }
    }
    if (encoderPos != prevEncoderPos)  //Update the display if the encoder position has changed
    {
      updateMainMenu();
      prevEncoderPos = encoderPos;
    }
  }
  Serial.println("Mode selected");
  if (modeSelected == 0)  //Run required mode function depending on selection
    runPan();
  else if (modeSelected == 1)
    runRotate();
  else if (modeSelected == 2)
    runPanAndRotate();
  else
    runTrack();
}

void PinA()  //Rotary encoder interrupt service routine for one encoder pin
{
  cli();                              //Stop interrupts happening before we read pin values
  reading = PIND & 0xC;               //Read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && aFlag)  //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {
    if (encoderPos <= (encHighLim - encIncrement))
      encoderPos = encoderPos + encIncrement;  //Increment the encoder's position count , only when below upper limit
    else
      encoderPos = encHighLim;      //Stop at maximum, being upper limit
    bFlag = 0;                      //Reset flags for the next turn
    aFlag = 0;                      //Reset flags for the next turn
  } else if (reading == B00000100)  //Signal that we're expecting pinB to signal the transition to detent from free rotation
    bFlag = 1;
  sei();  //Restart interrupts
}

void PinB()  //Rotary encoder interrupt service routine for the other encoder pin
{
  cli();                              //Stop interrupts happening before we read pin values
  reading = PIND & 0xC;               //Read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag)  //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {
    if (encoderPos >= (encLowLim + encIncrement))
      encoderPos = encoderPos - encIncrement;  //Decrement the encoder's position count, only when above lower limit
    else
      encoderPos = encLowLim;       //Stop at minimum, being lower limit
    bFlag = 0;                      //Reset flags for the next turn
    aFlag = 0;                      //Reset flags for the next turn
  } else if (reading == B00001000)  //Signal that we're expecting pinA to signal the transition to detent from free rotation
    aFlag = 1;
  sei();  //Restart interrupts
}

void updateMainMenu()  //Updates the display data for the main menu
{
  display.clearDisplay();  //Clear display
  display.setTextSize(1);  //Set the text size
  display.setCursor(28, 4);
  display.print(F("Camera Slider"));
  display.setCursor(25, 20);  //Set the display cursor position
  display.print(F("Pan"));    //Set the display text
  display.setCursor(25, 30);
  display.print(F("Rotate"));
  display.setCursor(25, 40);
  display.print(F("Pan & Rotate"));
  display.setCursor(25, 50);
  display.print(F("Track Object"));
  int selected = 0;  //Stores cursor vertical position to show selected item
  if (encoderPos == 0)
    selected = 20;
  else if (encoderPos == 1)
    selected = 30;
  else if (encoderPos == 2)
    selected = 40;
  else
    selected = 50;
  display.setCursor(14, selected);  //Set the display cursor position
  display.print(F(">"));
  display.display();  //Output the display text
}

void runPan()  //Runs the pan mode sequence
{
  inputPanData();  //Get user inputs for pan movement
  displayStart();  //Display startup sequence and enable motors
  display.setCursor(55, 30);
  display.print(F("Pan"));
  display.display();
  if (travelDir == 0)  //Set motor travel direction
    digitalWrite(travDirPin, LOW);
  else
    digitalWrite(travDirPin, HIGH);
  int travelPulses = calcTravelPulses();  //Calculate the number of motor pulses required to move the travel distance
  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval(travelPulses);  //Calculate the pulse interval required to move the required distance in the required time
  Serial.print("Interval: ");
  Serial.println(interval);
  for (int i = 1; i <= travelPulses; i++)  //Pulse the motor to move the required distance in the required time
  {
    digitalWrite(travStepPin, HIGH);
    delayMicroseconds(interval / 2);
    digitalWrite(travStepPin, LOW);
    delayMicroseconds(interval / 2);
  }
  displayEnd();  //Display the end sequence and disable motors
}

void runRotate()  //Runs the rotate mode sequence
{
  inputRotateData();  //Get user inputs for pan movement
  displayStart();     //Display startup sequence and enable motors
  display.setCursor(49, 30);
  display.print(F("Rotate"));
  display.display();
  if (rotDir == 0)  //Set motor travel direction
    digitalWrite(rotDirPin, HIGH);
  else
    digitalWrite(rotDirPin, LOW);
  int rotationPulses = calcRotationPulses();  //Calculate the number of motor pulses required to rotate the required angle
  Serial.print("Rotation pulses: ");
  Serial.println(rotationPulses);
  Serial.print("Travel Time: ");
  Serial.println(travTime);
  float interval = calcRotInterval(rotationPulses);  //Calculate the pulse interval required to rotate in the required time
  Serial.print("Interval: ");
  Serial.println(interval);
  for (int i = 1; i <= rotationPulses; i++)  //Pulse the motor to rotate the required angle in the required time
  {
    digitalWrite(rotStepPin, HIGH);
    delay(interval / 2);
    digitalWrite(rotStepPin, LOW);
    delay(interval / 2);
  }
  displayEnd();  //Display the end sequence and disable motors
}

void runPanAndRotate()  //Runs the pan and rotate mode sequence
{
  inputPanAndRotateData();  //Get user inputs for pan movement
  displayStart();           //Display startup sequence and enable motors
  display.setCursor(30, 30);
  display.print(F("Pan & Rotate"));
  display.display();
  if (travelDir == 0)  //Set motor travel direction
    digitalWrite(travDirPin, LOW);
  else
    digitalWrite(travDirPin, HIGH);
  if (rotDir == 0)  //Set motor travel direction
    digitalWrite(rotDirPin, HIGH);
  else
    digitalWrite(rotDirPin, LOW);
  int travelPulses = calcTravelPulses();  //Calculate the number of motor pulses required to move the travel distance
  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval(travelPulses);  //Calculate the pulse interval required to move the required distance in the required time
  Serial.print("Interval: ");
  Serial.println(interval);
  int rotationPulses = calcRotationPulses();  //Calculate the number of motor pulses required to rotate the required angle
  Serial.print("Rotation pulses: ");
  Serial.println(rotationPulses);
  int travelPerRotation = travelPulses / rotationPulses;  //Calculate how much the camera should pan for each rotation step
  for (int i = 1; i <= travelPulses; i++) {
    digitalWrite(travStepPin, HIGH);
    int checkRotate = i % travelPerRotation;  //Check if a rotation step must be made
    if (checkRotate == 0)
      digitalWrite(rotStepPin, HIGH);
    delayMicroseconds(interval / 2);
    digitalWrite(travStepPin, LOW);
    if (checkRotate == 0)
      digitalWrite(rotStepPin, LOW);
    delayMicroseconds(interval / 2);
    /*currentDist = i/pulsesPerMM;
      currentAngle = i/pulsesPerDeg;
      Serial.print("Dist: ");
      Serial.println(currentDist);
      Serial.print("Angle: ");
      Serial.println(currentAngle);*/
  }
  displayEnd();  //Display the end sequence and disable motors
}

void runTrack()  //Runs the object tracking mode sequence
{
  inputTrackData();  //Get user inputs for tracking movement
  displayStart();
  display.setCursor(22, 30);
  display.print(F("Object Tracking"));
  display.display();
  if (travelDir == 0)  //Set motor travel and rotate directions
  {
    digitalWrite(travDirPin, LOW);
    digitalWrite(rotDirPin, LOW);
  } else {
    digitalWrite(travDirPin, HIGH);
    digitalWrite(rotDirPin, HIGH);
  }
  int travelPulses = calcTravelPulses();  //Calculates the number of pulses required to move the travel distance
  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval(travelPulses);  //Calculates the interval required to achieve the required duration
  Serial.print("Interval: ");
  Serial.println(interval);
  currentAngle = atan((objDist) / (travDist / 2)) * 180 / M_PI;  //Calculates the initial camera to object angle
  Serial.print("Current Angle: ");
  Serial.println(currentAngle);
  for (int i = 1; i <= (travelPulses / 2); i++)  //Runs through movement sequence to move motors
  {
    digitalWrite(travStepPin, HIGH);
    boolean rotatePulse = checkRot(i);
    if (rotatePulse == true)
      digitalWrite(rotStepPin, HIGH);
    delayMicroseconds(interval / 2);
    digitalWrite(travStepPin, LOW);
    if (rotatePulse == true)
      digitalWrite(rotStepPin, LOW);
    delayMicroseconds(interval / 2);
    currentDist = i / pulsesPerMM;
    /*Serial.print("Dist: ");
      Serial.println(currentDist);
      Serial.print("Angle: ");
      Serial.println(currentAngle);*/
  }
  currentAngle = 90;
  for (int i = ((travelPulses / 2) + 1); i <= travelPulses; i++)  //Runs through movement sequence to move motors
  {
    digitalWrite(travStepPin, HIGH);
    boolean rotatePulse = checkRot(i);
    if (rotatePulse == true)
      digitalWrite(rotStepPin, HIGH);
    delayMicroseconds(interval / 2);
    digitalWrite(travStepPin, LOW);
    if (rotatePulse == true)
      digitalWrite(rotStepPin, LOW);
    delayMicroseconds(interval / 2);
    currentDist = i / pulsesPerMM;
    /*Serial.print("Dist: ");
      Serial.println(currentDist);
      Serial.print("Angle: ");
      Serial.println(currentAngle);*/
  }
  displayEnd();  //Display the end sequence and disable motors
}

void displayStart() {
  display.clearDisplay();       //Clear display
  display.setTextSize(1);       //Set the text size
  display.setCursor(45, 20);    //Set the display cursor position
  display.print(F("Push To"));  //Set the display text
  display.setCursor(50, 32);
  display.print(F("Start"));
  display.display();          //Output the display text
  boolean confirmed = false;  //Both used to confirm button push to select mode
  boolean pressed = false;
  while (!confirmed)  //While the user has not started the panning routine
  {
    byte buttonState = digitalRead(encButton);
    if (buttonState != oldButtonState) {
      if (millis() - buttonPressTime >= debounceTime)  //Debounce button
      {
        buttonPressTime = millis();    //Time when button was pushed
        oldButtonState = buttonState;  //Remember button state for next time
        if (buttonState == LOW) {
          pressed = true;
        } else {
          if (pressed == true)  //Confirm the input once the button is released again
          {
            confirmed = true;
          }
        }
      }
    }
  }
  pinMode(enablePin, OUTPUT);  //Enable the motors
  for (int i = 3; i > 0; i--)  //Countdown to start
  {
    display.clearDisplay();     //Clear display
    display.setTextSize(2);     //Set the text size
    display.setCursor(60, 20);  //Set the display cursor position
    display.print(i);           //Set the display text
    display.display();
    delay(1000);
  }
  display.clearDisplay();       //Clear display
  display.setTextSize(1);       //Set the text size
  display.setCursor(45, 15);    //Set the display cursor position
  display.print(F("Running"));  //Set the display text
  display.display();
}

void displayEnd() {
  display.clearDisplay();        //Clear display
  display.setTextSize(1);        //Set the text size
  display.setCursor(40, 15);     //Set the display cursor position
  display.print(F("Complete"));  //Set the display text
  display.setCursor(44, 35);
  display.print(F("Push To"));
  display.setCursor(27, 47);
  display.print(F("Release Motors"));
  display.display();          //Output the display text
  boolean confirmed = false;  //Both used to confirm button push to select mode
  boolean pressed = false;
  while (!confirmed)  //While the user has not started the routine
  {
    byte buttonState = digitalRead(encButton);
    if (buttonState != oldButtonState) {
      if (millis() - buttonPressTime >= debounceTime)  //Debounce button
      {
        buttonPressTime = millis();    //Time when button was pushed
        oldButtonState = buttonState;  //Remember button state for next time
        if (buttonState == LOW) {
          pressed = true;
        } else {
          if (pressed == true)  //Confirm the input once the button is released again
          {
            confirmed = true;
          }
        }
      }
    }
  }
  pinMode(enablePin, INPUT);  //Open circuit enable pin, disables motors
  resetVariables();
}

void inputPanData()  //Input required data for pan mode
{
  dataInputNo = 0;  //Input travel distance
  inputField(maxTravDist, minTravDist, maxTravDist, travDistInc);
  dataInputNo = 1;  //Input travel direction
  inputField(0, 0, 1, 1);
  dataInputNo = 2;  //Input travel duration
  inputField(initialDur, minDur, maxDur, durInc);
}

void inputRotateData()  //Input required data for rotate mode
{
  dataInputNo = 0;  //Input rotation angle
  inputField(initialRotAng, minRotAng, maxRotAng, rotAngInc);
  dataInputNo = 1;  //Input rotation direction
  inputField(0, 0, 1, 1);
  dataInputNo = 2;  //Input rotation duration
  inputField(initialDur, minDur, maxDur, durInc);
}

void inputPanAndRotateData()  //Input required data for pan and rotate mode
{
  dataInputNo = 0;  //Input pan distance
  inputField(maxTravDist, minTravDist, maxTravDist, travDistInc);
  dataInputNo = 1;  //Input pan direction
  inputField(0, 0, 1, 1);
  dataInputNo = 2;  //Input rotation angle
  inputField(initialRotAng, minRotAng, maxRotAng, rotAngInc);
  dataInputNo = 3;  //Input rotation direction
  inputField(0, 0, 1, 1);
  dataInputNo = 4;  //Input total duration
  inputField(initialDur, minDur, maxDur, durInc);
}

void inputTrackData()  //Input required data for object tracking mode
{
  dataInputNo = 0;  //Input pan distance
  inputField(maxTravDist, minTravDist, maxTravDist, travDistInc);
  dataInputNo = 1;  //Input pan direction
  inputField(0, 0, 1, 1);
  dataInputNo = 2;  //Input rotation angle
  inputField(initialObjDist, minObjDist, maxObjDist, objInc);
  dataInputNo = 3;  //Input total duration
  inputField(initialDur, minDur, maxDur, durInc);
}

void updatePanDataDisplay() {
  display.clearDisplay();          //Clear display
  display.setTextSize(1);          //Set the text size
  display.setCursor(2, 10);        //Set the display cursor position
  display.print(F("Distance: "));  //Set the display text
  display.setCursor(2, 20);
  display.print(F("Direction: "));
  display.setCursor(2, 30);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)  //Get the cursor position & update changing variable
  {
    selected = 10;
    travDist = encoderPos;
  } else if (dataInputNo == 1) {
    selected = 20;
    travelDir = encoderPos;
  } else {
    selected = 30;
    travTime = encoderPos;
    if (calcInterval(calcTravelPulses()) < minInterval)  //Flags movement too fast
    {
      display.setCursor(40, 55);     //Set the display cursor position
      display.print(F("Too Fast"));  //Set the display text
    }
  }
  display.setCursor(65, selected);  //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75, 10);  //Display the field data
  display.print(travDist);
  display.print(F("mm"));
  display.setCursor(75, 20);
  if (travelDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75, 30);
  display.print(travTime);
  display.print(F("s"));
  display.display();  //Output the display text
}

void updateRotDataDisplay() {
  display.clearDisplay();           //Clear display
  display.setTextSize(1);           //Set the text size
  display.setCursor(2, 10);         //Set the display cursor position
  display.print(F("Rot. Ang.: "));  //Set the display text
  display.setCursor(2, 20);
  display.print(F("Direction: "));
  display.setCursor(2, 30);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)  //Get the cursor position & update changing variable
  {
    selected = 10;
    rotAngle = encoderPos;
  } else if (dataInputNo == 1) {
    selected = 20;
    rotDir = encoderPos;
  } else {
    selected = 30;
    travTime = encoderPos;
    if (calcRotInterval(calcRotationPulses()) < minInterval)  //Flags movement too fast
    {
      display.setCursor(40, 55);     //Set the display cursor position
      display.print(F("Too Fast"));  //Set the display text
    }
  }
  display.setCursor(65, selected);  //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75, 10);  //Display the field data
  display.print(rotAngle);
  display.print(F("deg"));
  display.setCursor(75, 20);
  if (rotDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75, 30);
  display.print(travTime);
  display.print(F("s"));
  display.display();  //Output the display text
}

void updatePanAndRotateDataDisplay() {
  display.clearDisplay();          //Clear display
  display.setTextSize(1);          //Set the text size
  display.setCursor(2, 2);         //Set the display cursor position
  display.print(F("Distance: "));  //Set the display text
  display.setCursor(2, 12);
  display.print(F("Trav. Dir: "));
  display.setCursor(2, 22);
  display.print(F("Rot. Ang: "));
  display.setCursor(2, 32);
  display.print(F("Rot. Dir: "));
  display.setCursor(2, 42);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)  //Get the cursor position & update changing variable
  {
    selected = 2;
    travDist = encoderPos;
  } else if (dataInputNo == 1) {
    selected = 12;
    travelDir = encoderPos;
  } else if (dataInputNo == 2) {
    selected = 22;
    rotAngle = encoderPos;
  } else if (dataInputNo == 3) {
    selected = 32;
    rotDir = encoderPos;
  } else {
    selected = 42;
    travTime = encoderPos;
    if (calcInterval(calcTravelPulses()) < minInterval)  //Flags movement too fast
    {
      display.setCursor(40, 55);     //Set the display cursor position
      display.print(F("Too Fast"));  //Set the display text
    }
  }
  display.setCursor(65, selected);  //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75, 2);  //Display the field data
  display.print(travDist);
  display.print(F("mm"));
  display.setCursor(75, 12);
  if (travelDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75, 22);
  display.print(rotAngle);
  display.print(F("deg"));
  display.setCursor(75, 32);
  if (rotDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75, 42);
  display.print(travTime);
  display.print(F("s"));
  display.display();  //Output the display text
}

void updateTrackDataDisplay() {
  display.clearDisplay();          //Clear display
  display.setTextSize(1);          //Set the text size
  display.setCursor(2, 10);        //Set the display cursor position
  display.print(F("Distance: "));  //Set the display text
  display.setCursor(2, 20);
  display.print(F("Trav. Dir: "));
  display.setCursor(2, 30);
  display.print(F("Obj. Dist: "));
  display.setCursor(2, 40);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)  //Get the cursor position & update changing variable
  {
    selected = 10;
    travDist = encoderPos;
  } else if (dataInputNo == 1) {
    selected = 20;
    travelDir = encoderPos;
  } else if (dataInputNo == 2) {
    selected = 30;
    objDist = encoderPos;
  } else {
    selected = 40;
    travTime = encoderPos;
    if (calcInterval(calcTravelPulses()) < minInterval)  //Flags movement too fast
    {
      display.setCursor(40, 55);     //Set the display cursor position
      display.print(F("Too Fast"));  //Set the display text
    }
  }
  display.setCursor(65, selected);  //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75, 10);  //Display the field data
  display.print(travDist);
  display.print(F("mm"));
  display.setCursor(75, 20);
  if (travelDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75, 30);
  display.print(objDist);
  display.print(F("mm"));
  display.setCursor(75, 40);
  display.print(travTime);
  display.print(F("s"));
  display.display();  //Output the display text
}

void inputField(int initialSetting, int lowerLimit, int upperLimit, int increment) {
  encLowLim = lowerLimit;
  encHighLim = upperLimit;
  encIncrement = increment;
  encoderPos = initialSetting;      //Encoder starts from initial setting
  prevEncoderPos = encoderPos + 1;  //Set different so that display is updated on first cycle
  boolean confirmed = false;        //Both used to confirm button push to select mode
  boolean pressed = false;
  while (!confirmed)  //While the user has not confirmed the input
  {
    byte buttonState = digitalRead(encButton);
    if (buttonState != oldButtonState) {
      if (millis() - buttonPressTime >= debounceTime)  //Debounce button
      {
        buttonPressTime = millis();    //Time when button was pushed
        oldButtonState = buttonState;  //Remember button state for next time
        if (buttonState == LOW) {
          pressed = true;
        } else {
          if (pressed == true)  //Confirm the input once the button is released again
          {
            confirmed = true;
            Serial.println("Input Confirmed");
          }
        }
      }
    }
    if (encoderPos != prevEncoderPos)  //Update the display if the encoder position has changed
    {
      if (modeSelected == 0)
        updatePanDataDisplay();
      else if (modeSelected == 1)
        updateRotDataDisplay();
      else if (modeSelected == 2)
        updatePanAndRotateDataDisplay();
      else
        updateTrackDataDisplay();
      prevEncoderPos = encoderPos;
    }
  }
}

void resetVariables()  //Reset variables back to initial values after run
{
  travDist = maxTravDist;
  travTime = initialDur;
  objDist = initialObjDist;
  travelDir = 0;
  rotDir = 0;
  rotAngle = initialRotAng;
}

int calcTravelPulses()  //Calculates the number of pulses required to move a certain distance
{
  int travP = travDist * pulsesPerMM;
  return travP;
}

int calcRotationPulses()  //Calculate the number of pulses required to rotate a certain angle
{
  int rotP = rotAngle * pulsesPerDeg;
  return rotP;
}

boolean checkRot(int i)  //Used in tracking to calculate the angle required to track object
{
  boolean rotP = false;
  float deltaAngle;
  if (((travDist / 2) - (i / pulsesPerMM)) > 0) {
    if (currentAngle < 90 - (1 / pulsesPerDeg)) {
      float newAngle = atan((objDist) / ((travDist / 2) - (i / pulsesPerMM))) * 180 / M_PI;
      deltaAngle = newAngle - currentAngle;
    }
  } else if (((travDist / 2) - (i / pulsesPerMM)) < 0) {
    if (currentAngle > 0) {
      float newAngle = atan((objDist) / ((i / pulsesPerMM) - (travDist / 2))) * 180 / M_PI;
      deltaAngle = currentAngle - newAngle;
    }
  }
  if (deltaAngle >= (1 / pulsesPerDeg)) {
    rotP = true;
    if ((travDist / 2) - (i / pulsesPerMM) > 0)
      currentAngle = currentAngle + (1 / pulsesPerDeg);
    else
      currentAngle = currentAngle - (1 / pulsesPerDeg);
  }
  return rotP;
}

float calcInterval(int numPulses)  //Calculate the interval required between pulses to achieve duration
{
  float inter = travTime * 1000000 / numPulses;
  return inter;
}

float calcRotInterval(int numPulses)  //Calculate the interval required between pulses to achieve duration
{
  float inter = travTime * 1000 / numPulses;
  return inter;
}

Post mis dans la mauvaise section, on parle anglais dans les forums généraux, je viens de déplacer le post dans la section francophone.

Merci de prendre en compte les recommandations listées dans "Les bonnes pratiques du Forum Francophone".

hello, regarde à partir de la ligne 813

Merci, j’ai regardé à partir de la ligne 813 et j’ai trouvé comment calculer le nombre d’impulsion nécessaire pour effectuer dans mon cas 505 mm (505* 50), 50 étant le nombre d’impulsions pour 1 mm (ligne 134). Ensuite ligne 852, je ne comprends pas comment est calculé l’intervalle entre chaque impulsion, TravTime étant la durée que je programme, par contre a quoi correspond numPulses et pourquoi si c’est cette formule qui calcule l’intervalle, la durée de trajet change brusquement quand je suis au dessus de 820 sec. Merci d’avance

Bonjour franfran72

Ton problème semble être un débordement sur un type de variable, plus précisément de l'usage de delayMicroseconds
Que donne l'affichage de ces 2 lignes, sans le défaut et avec le défaut?

  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval(travelPulses);  //Calculate the pulse interval required to move the required distance in the required time
  Serial.print("Interval: ");
  Serial.println(interval);

A+
Cordialement
jpbbricole

Bonjour, merci pour votre aide. Donc quand je règle le trajet sur 800 sec et que cela fonctionne bien, le moniteur série indique:

travelPulses: 25250

Interval: 31683,17

Lorsque je règle sur 830 sec, le trajet se fait en environ 5 sec et le moniteur série indique:

TravelPulses: 25250

Interval: 32871,29

Bonjour,

Ca semble confirmer ce que dit @jpbbricole.

Dans le deuxième cas interval dépasse la valeur max d’un entier (sur 16 bits) qui est de 32768.

Je pense qu’il faudrait mettre

`delayMicroseconds((unsigned int)(interval / 2));`

Merci, si je comprends bien, il faut que je remplace (interval / 2);delayMicroseconds des lignes 297, 299 et 426 par delayMicroseconds((unsigned int)(interval / 2));`

Oui c’est à essayer, mais je ne suis pas sur que ça fonctionne car interval/2 est inférieur au max d’un entier.

Ok, je vais essayer cela dès demain. merci encore

Bonsoir,

C'est étonnant que personne n'ait remarqué que certaines variables globales comme volatile int encoderPos (et il y en d'autres ;-) soient accédées sans protection depuis l'interruption void PinA() et void PinB() et allègrement utilisées dans la méthode loop()...

Combien de fois devra-t-on préciser que les accès dans des zones critiques et concurrentes doivent être définies d'une manière atomique comme std::atomic<int16_t> _currentValue;

Sans correction de ce point de vue et sans animosité, pas la peine de chercher à comprendre pourquoi le programme ... tombe en marche

De plus, appeler les 2 méthodes cli() et sei() dans une interruption me semble inutile (je ne félicite pas l'auteur du programme qui n'a pas daigné répondre ;-)

A suivre...

Bonsoir franfran72

Dans le cas de temps si grands en microsecondes, ne serais ce pas plus simple de travailler simplement en millisecondes, avec delay(), en millisecondes.

Cordialement
jpbbricole

Bonjour, après essai de la modification de Kamil, le problème est le même.

Bonjour,

Cela ne m’étonne pas ... Cf. mon post #11 (accès concurrents à des variables dans des zones critiques ;-)

A suivre...

Bonjour jpbbricole, j’ai voulu remplacer delayMicroseconds par delayMilliseconds dans la section void runPan et j’ai le message d’erreur suivant lors de la compilation: “error: 'delayMilliseconds' was not declared in this scope”. Comment faire pour déclarer milliseconds.

Merci d’avance

Bonjour franfran72

C'est delay() tout court.

PS: N'oublies pas de diviser interval par 1000

jpbbricole, j’ai modifié le programme et je pense que je n’ai plus le problème de débordement, par contre le temps de trajet ne correspond pas à celui programmé:

52 s pour 30 s programmé

1min 42 s pour 60 s

32 min 52 s pour 1000

26 min 57 s pour 800

et pour un réglage en dessous de 30s, le moteur fait un bruit strident pendant 2/3s et le slider ne bouge pas.

J’ai vérifié dans le moniteur série, la valeur Interval est toujours de 32871,29 pour 830s

Postez le programme modifié. (delayMicrosecond() prend un paramètre sur 16 bits et est relativement précis jsusqu'à 16383 microsecondes d'après la doc. Si vous devez attendre longtemps, éventuellement passez en millisecondes.

Outre le débordement vous avez aussi le souci de zone critique à regarder

Voici le programme modifié. Pour le souci de zone critique, je suis trop novice en arduino pour voir ou est le problème.

//The DIY Life
//Michael Klements
//13 January 2021
//License: CC BY-NC-SA

#include <SPI.h>                            //Import libraries to control the OLED display
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <math.h>

#define SCREEN_WIDTH 128                    // OLED display width, in pixels
#define SCREEN_HEIGHT 64                    // OLED display height, in pixels

#define OLED_RESET -1                                                           // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);       // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)

// 'The DIY Life', 128x64px
const unsigned char logo [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x01, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x03, 0xfc, 0x07, 0xfc, 0x00, 0x00, 0x7f, 0xc0, 0x00, 
  0x00, 0x02, 0x03, 0x41, 0xfe, 0x00, 0x00, 0x03, 0xfc, 0x07, 0xfe, 0x00, 0x00, 0x5f, 0xc0, 0x00, 
  0x00, 0x03, 0x33, 0x47, 0xff, 0x80, 0x00, 0x03, 0xfc, 0x03, 0xfe, 0x00, 0x00, 0x9f, 0xc0, 0x00, 
  0x00, 0x03, 0x30, 0x43, 0xff, 0xe0, 0x00, 0x03, 0xfc, 0x03, 0xff, 0x00, 0x00, 0x37, 0x80, 0x00, 
  0x00, 0x03, 0x30, 0x43, 0xff, 0xf8, 0x00, 0x03, 0xfc, 0x01, 0xff, 0x00, 0x01, 0x04, 0x00, 0x00, 
  0x00, 0x03, 0x33, 0x47, 0xff, 0xfc, 0x00, 0x03, 0xfc, 0x01, 0xff, 0x80, 0x01, 0x80, 0x00, 0x00, 
  0x00, 0x03, 0x33, 0x41, 0xff, 0xfe, 0x00, 0x03, 0xfc, 0x00, 0xff, 0x80, 0x02, 0x61, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x7f, 0xff, 0x00, 0x03, 0xfc, 0x00, 0xff, 0xc0, 0x02, 0xd8, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x1f, 0xff, 0x80, 0x03, 0xfc, 0x00, 0x7f, 0xc0, 0x04, 0x1e, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x07, 0xff, 0x80, 0x03, 0xfc, 0x00, 0x7f, 0xe0, 0x04, 0x1c, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x01, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x3f, 0xe0, 0x03, 0x04, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x3f, 0xf0, 0x00, 0xc0, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x1f, 0xf0, 0x0c, 0x38, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x1f, 0xf0, 0x1f, 0x10, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x0f, 0xf8, 0x0f, 0x90, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x0f, 0xf8, 0x23, 0x20, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x07, 0xfc, 0x30, 0x20, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x07, 0xfc, 0x3c, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x07, 0xfe, 0x7f, 0x40, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x03, 0xfe, 0x7f, 0x80, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x01, 0xff, 0xff, 0x80, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0xff, 0xfe, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xfe, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf8, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x7f, 0xfc, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x3f, 0xf8, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf8, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x1f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x3f, 0xf0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0x7f, 0xe0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0xff, 0xe0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x00, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x03, 0xff, 0xc0, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x07, 0xff, 0x80, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x00, 0x1f, 0xff, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xfe, 0x01, 0xff, 0xff, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x03, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x1f, 0xf0, 0x00, 0x00, 0x00, 
  0x00, 0x01, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0xfc, 0x00, 0x00, 0x0f, 0xe0, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

#define minTravDist 25                      //Define travel distance initial setting, minimum, maximum and increment
#define maxTravDist 505
#define travDistInc 5
#define initialDur 120                      //Define duration initial setting, minimum, maximum and increment
#define minDur 10
#define maxDur 1800
#define durInc 5
#define initialRotAng 180                   //Define rotation initial setting, minimum, maximum and increment
#define minRotAng 20
#define maxRotAng 360
#define rotAngInc 10
#define initialObjDist 200                  //Define object distance initial setting, minimum, maximum and increment
#define minObjDist 150
#define maxObjDist 5000
#define objInc 50
#define minInterval 400                     //Minimum interval time between pulses in microseconds

static int pinA = 2;                        //Hardware interrupt digital pin 2
static int pinB = 3;                        //Hardware interrupt digital pin 3
volatile byte aFlag = 0;                    //Rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0;                    //Rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile int encoderPos = 0;                //Current value of encoder position, constained between limits below
volatile int prevEncoderPos = 0;            //To track whether the encoder has been turned and the display needs to update
volatile byte reading = 0;                  //Stores direct value from interrupt pin
#define encButton 4                         //Define encoder pushbutton pin
byte oldButtonState = HIGH;                 //First button state is open because of pull-up resistor
const unsigned long debounceTime = 10;      //Debounce delay time
unsigned long buttonPressTime;              //Time button has been pressed for debounce

int encLowLim = 0;                          //Variables to store the encoder limits and increment
int encHighLim = 3;
int encIncrement = 1;
int dataInputNo = 0;
int modeSelected = 0;                       //Current operating mode (Pan, Rotate, Pan & Rotate, Track Object)

#define enablePin 5                         //Define motor enable pin
#define travDirPin 6                        //Define travel & rotation stepper motor pins
#define travStepPin 7
#define rotDirPin 8
#define rotStepPin 9

float travDist = maxTravDist;               //Distance to travel across slider in millimeters
float travTime = initialDur;                //Travel time to cover the required distance in seconds
float objDist = initialObjDist;             //Distance of tracked object from slider in millimeters
int travelDir = 0;                          //Deifne initial travel and rotation directions
int rotDir = 0;
int rotAngle = 180;                         //Angle to rotate camera around axis

float pulsesPerMM = 50;                     //Number of motor pulses for 1mm travel
float pulsesPerDeg = 4.4444;                //Number of motor pulses for 1 degree of rotation
float currentDist = 0;
float currentAngle = 0;

void setup() 
{
  Serial.begin(9600);                                     //Start serial communication for debugging
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C))          //Connect to the OLED display
  {
    Serial.println(F("SSD1306 allocation failed"));       //If connection fails
    for(;;);                                              //Don't proceed, loop forever
  }
  pinMode(pinA, INPUT_PULLUP);                            //Set pinA as an input, pulled HIGH to the logic voltage
  pinMode(pinB, INPUT_PULLUP);                            //Set pinB as an input, pulled HIGH to the logic voltage
  attachInterrupt(0,PinA,RISING);                         //Set an interrupt on PinA
  attachInterrupt(1,PinB,RISING);                         //Set an interrupt on PinB
  pinMode (encButton, INPUT_PULLUP);                      //Set the encoder button as an input, pulled HIGH to the logic voltage
  pinMode(enablePin, INPUT);                              //Open circuit enable pin, disables motors
  pinMode(travDirPin, OUTPUT);                            //Define the travel stepper motor pins
  pinMode(travStepPin, OUTPUT);
  pinMode(rotDirPin, OUTPUT);                             //Define the rotation stepper motor pins
  pinMode(rotStepPin, OUTPUT);
  digitalWrite(travDirPin, HIGH);                         //Set the initial direction of motion for both motors
  digitalWrite(rotDirPin, HIGH);
  display.clearDisplay();                                 //Clear the display
  display.setTextColor(SSD1306_WHITE);                    //Set the text colour to white
  display.drawBitmap(0, 0, logo, 128, 64, WHITE);         //Display bitmap from array
  display.display();
  delay(2000);
  display.clearDisplay();                                 //Clear display
  Serial.println("Setup complete");                       //Write to serial monitor to indicate the setup function is complete
}

void loop() 
{  
  encLowLim = 0;                                            //Mode selection menu, 4 modes
  encHighLim = 3;
  encIncrement = 1;
  updateMainMenu();
  
  boolean confirmed = false;                                //Both used to confirm button push to select mode
  boolean pressed = false;
  encoderPos = 0;                                           //Encoder starts from 0, first menu option
  while(!confirmed)                                         //While the user has not confirmed the selection
  {
    byte buttonState = digitalRead (encButton); 
    if (buttonState != oldButtonState)
    {
      if (millis () - buttonPressTime >= debounceTime)      //Debounce button
      {
        buttonPressTime = millis ();                        //Time when button was pushed
        oldButtonState =  buttonState;                      //Remember button state for next time
        if (buttonState == LOW)
        {
          modeSelected = encoderPos;                        //If the button is pressed, accept the current digit into the guessed code
          pressed = true;
          Serial.println("Button Pushed");
        }
        else 
        {
          if (pressed == true)                              //Confirm the input once the button is released again
          {
            confirmed = true;
            Serial.println("Mode confirmed");
          }
        }  
      }
    }
    if(encoderPos!=prevEncoderPos)                          //Update the display if the encoder position has changed
    {
      updateMainMenu();
      prevEncoderPos=encoderPos;
    }
  }
  Serial.println("Mode selected");
  if (modeSelected == 0)                                    //Run required mode function depending on selection
    runPan();
  else if (modeSelected == 1)
    runRotate ();
  else if (modeSelected == 2)
    runPanAndRotate ();
  else
    runTrack ();
}

void PinA()                                             //Rotary encoder interrupt service routine for one encoder pin
{
  cli();                                                //Stop interrupts happening before we read pin values
  reading = PIND & 0xC;                                 //Read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag)                     //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {     
    if(encoderPos<=(encHighLim-encIncrement))
      encoderPos = encoderPos+encIncrement;             //Increment the encoder's position count , only when below upper limit
    else
      encoderPos = encHighLim;                          //Stop at maximum, being upper limit
    bFlag = 0;                                          //Reset flags for the next turn
    aFlag = 0;                                          //Reset flags for the next turn
  }
  else if (reading == B00000100)                        //Signal that we're expecting pinB to signal the transition to detent from free rotation
    bFlag = 1;
  sei();                                                //Restart interrupts
}

void PinB()                                             //Rotary encoder interrupt service routine for the other encoder pin
{
  cli();                                                //Stop interrupts happening before we read pin values
  reading = PIND & 0xC;                                 //Read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag)                    //Check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
  {
    if(encoderPos>=(encLowLim+encIncrement))
      encoderPos = encoderPos-encIncrement;             //Decrement the encoder's position count, only when above lower limit
    else
      encoderPos = encLowLim;                           //Stop at minimum, being lower limit
    bFlag = 0;                                          //Reset flags for the next turn
    aFlag = 0;                                          //Reset flags for the next turn
  }
  else if (reading == B00001000)                        //Signal that we're expecting pinA to signal the transition to detent from free rotation
    aFlag = 1;
  sei();                                                //Restart interrupts
}

void updateMainMenu()                               //Updates the display data for the main menu
{
  display.clearDisplay();                           //Clear display
  display.setTextSize(1);                           //Set the text size
  display.setCursor(28,4);
  display.print(F("Camera Slider"));
  display.setCursor(25,20);                         //Set the display cursor position
  display.print(F("Pan"));                          //Set the display text
  display.setCursor(25,30);
  display.print(F("Rotate"));
  display.setCursor(25,40);
  display.print(F("Pan & Rotate"));
  display.setCursor(25,50);
  display.print(F("Track Object"));
  int selected = 0;                                 //Stores cursor vertical position to show selected item
  if (encoderPos == 0)
    selected = 20;
  else if (encoderPos == 1)
    selected = 30;
  else if (encoderPos == 2)
    selected = 40;
  else
    selected = 50;
  display.setCursor(14,selected);                    //Set the display cursor position
  display.print(F(">"));
  display.display();                                //Output the display text
}

void runPan ()                                          //Runs the pan mode sequence
{
  inputPanData ();                                      //Get user inputs for pan movement
  displayStart ();                                      //Display startup sequence and enable motors
  display.setCursor(55,30);
  display.print(F("Pan"));
  display.display();
  if (travelDir == 0)                                   //Set motor travel direction
    digitalWrite(travDirPin, LOW);
  else
    digitalWrite(travDirPin, HIGH);
  int travelPulses = calcTravelPulses ();               //Calculate the number of motor pulses required to move the travel distance
  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval (travelPulses);         //Calculate the pulse interval required to move the required distance in the required time
  Serial.print("Interval: ");
  Serial.println(interval);
  for (int i=1; i<=travelPulses; i++)                   //Pulse the motor to move the required distance in the required time
  {
      digitalWrite(travStepPin, HIGH);
      delay(interval/1000);
      digitalWrite(travStepPin, LOW);
      delay(interval/1000);
  }
  displayEnd();                                         //Display the end sequence and disable motors
}

void runRotate ()                                       //Runs the rotate mode sequence
{
  inputRotateData ();                                   //Get user inputs for pan movement
  displayStart ();                                      //Display startup sequence and enable motors
  display.setCursor(49,30);
  display.print(F("Rotate"));
  display.display();
  if (rotDir == 0)                                      //Set motor travel direction
    digitalWrite(rotDirPin, HIGH);
  else
    digitalWrite(rotDirPin, LOW);
  int rotationPulses = calcRotationPulses ();           //Calculate the number of motor pulses required to rotate the required angle
  Serial.print("Rotation pulses: ");
  Serial.println(rotationPulses);
  Serial.print("Travel Time: ");
  Serial.println(travTime);
  float interval = calcRotInterval (rotationPulses);       //Calculate the pulse interval required to rotate in the required time
  Serial.print("Interval: ");
  Serial.println(interval);
  for (int i=1; i<=rotationPulses; i++)                 //Pulse the motor to rotate the required angle in the required time
  {
      digitalWrite(rotStepPin, HIGH);
      delay(interval/2);
      digitalWrite(rotStepPin, LOW);
      delay(interval/2);
  }
  displayEnd();                                         //Display the end sequence and disable motors
}

void runPanAndRotate ()                                       //Runs the pan and rotate mode sequence
{
  inputPanAndRotateData ();                                   //Get user inputs for pan movement
  displayStart ();                                            //Display startup sequence and enable motors
  display.setCursor(30,30);
  display.print(F("Pan & Rotate"));
  display.display();
  if (travelDir == 0)                                         //Set motor travel direction
    digitalWrite(travDirPin, LOW);
  else
    digitalWrite(travDirPin, HIGH);
  if (rotDir == 0)                                            //Set motor travel direction
    digitalWrite(rotDirPin, HIGH);
  else
    digitalWrite(rotDirPin, LOW);
  int travelPulses = calcTravelPulses ();                     //Calculate the number of motor pulses required to move the travel distance
  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval (travelPulses);               //Calculate the pulse interval required to move the required distance in the required time
  Serial.print("Interval: ");
  Serial.println(interval);
  int rotationPulses = calcRotationPulses ();                 //Calculate the number of motor pulses required to rotate the required angle
  Serial.print("Rotation pulses: ");
  Serial.println(rotationPulses);
  int travelPerRotation = travelPulses/rotationPulses;        //Calculate how much the camera should pan for each rotation step
  for (int i=1; i<=travelPulses; i++)
  {
      digitalWrite(travStepPin, HIGH);
      int checkRotate = i % travelPerRotation;                //Check if a rotation step must be made
      if (checkRotate == 0)
        digitalWrite(rotStepPin, HIGH);
      delayMicroseconds(interval/2);
      digitalWrite(travStepPin, LOW);
      if (checkRotate == 0)
        digitalWrite(rotStepPin, LOW);
      delayMicroseconds(interval/2);
      /*currentDist = i/pulsesPerMM;
      currentAngle = i/pulsesPerDeg;
      Serial.print("Dist: ");
      Serial.println(currentDist);
      Serial.print("Angle: ");
      Serial.println(currentAngle);*/
  }
  displayEnd();                                                 //Display the end sequence and disable motors
}

void runTrack ()                                                //Runs the object tracking mode sequence
{
  inputTrackData ();                                             //Get user inputs for tracking movement
  displayStart ();
  display.setCursor(22,30);
  display.print(F("Object Tracking"));
  display.display();
  if (travelDir == 0)                                             //Set motor travel and rotate directions
  {
    digitalWrite(travDirPin, LOW);
    digitalWrite(rotDirPin, LOW);
  }
  else
  {
    digitalWrite(travDirPin, HIGH);
    digitalWrite(rotDirPin, HIGH);
  }
  int travelPulses = calcTravelPulses ();                       //Calculates the number of pulses required to move the travel distance
  Serial.print("Travel pulses: ");
  Serial.println(travelPulses);
  float interval = calcInterval (travelPulses);                 //Calculates the interval required to achieve the required duration
  Serial.print("Interval: ");
  Serial.println(interval);
  currentAngle = atan((objDist)/(travDist/2))*180/M_PI;         //Calculates the initial camera to object angle
  Serial.print("Current Angle: ");
  Serial.println(currentAngle);
  for (int i=1; i<=(travelPulses/2); i++)                           //Runs through movement sequence to move motors
  {
      digitalWrite(travStepPin, HIGH);
      boolean rotatePulse = checkRot (i);
      if (rotatePulse == true)
        digitalWrite(rotStepPin, HIGH);
      delayMicroseconds(interval/2);
      digitalWrite(travStepPin, LOW);
      if (rotatePulse == true)
        digitalWrite(rotStepPin, LOW);
      delayMicroseconds(interval/2);
      currentDist = i/pulsesPerMM;
      /*Serial.print("Dist: ");
      Serial.println(currentDist);
      Serial.print("Angle: ");
      Serial.println(currentAngle);*/
  }
  currentAngle = 90;
  for (int i=((travelPulses/2)+1); i<=travelPulses; i++)                           //Runs through movement sequence to move motors
  {
      digitalWrite(travStepPin, HIGH);
      boolean rotatePulse = checkRot (i);
      if (rotatePulse == true)
        digitalWrite(rotStepPin, HIGH);
      delayMicroseconds(interval/2);
      digitalWrite(travStepPin, LOW);
      if (rotatePulse == true)
        digitalWrite(rotStepPin, LOW);
      delayMicroseconds(interval/2);
      currentDist = i/pulsesPerMM;
      /*Serial.print("Dist: ");
      Serial.println(currentDist);
      Serial.print("Angle: ");
      Serial.println(currentAngle);*/
  }
  displayEnd();                                           //Display the end sequence and disable motors
}

void displayStart()
{
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(45,20);                                 //Set the display cursor position
  display.print(F("Push To"));                              //Set the display text
  display.setCursor(50,32);
  display.print(F("Start"));
  display.display();                                        //Output the display text
  boolean confirmed = false;                                //Both used to confirm button push to select mode
  boolean pressed = false;
  while(!confirmed)                                         //While the user has not started the panning routine
  {
    byte buttonState = digitalRead (encButton); 
    if (buttonState != oldButtonState)
    {
      if (millis () - buttonPressTime >= debounceTime)      //Debounce button
      {
        buttonPressTime = millis ();                        //Time when button was pushed
        oldButtonState =  buttonState;                      //Remember button state for next time
        if (buttonState == LOW)
        {
          pressed = true;
        }
        else 
        {
          if (pressed == true)                              //Confirm the input once the button is released again
          {
            confirmed = true;
          }
        }  
      }
    }
  }
  pinMode(enablePin, OUTPUT);                               //Enable the motors
  for(int i=3 ; i> 0 ; i--)                                 //Countdown to start
  {
    display.clearDisplay();                                 //Clear display
    display.setTextSize(2);                                 //Set the text size
    display.setCursor(60,20);                               //Set the display cursor position
    display.print(i);                                       //Set the display text
    display.display(); 
    delay(1000);
  }
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(45,15);                                 //Set the display cursor position
  display.print(F("Running"));                              //Set the display text
  display.display();
}

void displayEnd()
{
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(40,15);                                 //Set the display cursor position
  display.print(F("Complete"));                             //Set the display text
  display.setCursor(44,35);
  display.print(F("Push To"));
  display.setCursor(27,47);
  display.print(F("Release Motors"));
  display.display();                                        //Output the display text
  boolean confirmed = false;                                //Both used to confirm button push to select mode
  boolean pressed = false;
  while(!confirmed)                                         //While the user has not started the routine
  {
    byte buttonState = digitalRead (encButton); 
    if (buttonState != oldButtonState)
    {
      if (millis () - buttonPressTime >= debounceTime)      //Debounce button
      {
        buttonPressTime = millis ();                        //Time when button was pushed
        oldButtonState =  buttonState;                      //Remember button state for next time
        if (buttonState == LOW)
        {
          pressed = true;
        }
        else 
        {
          if (pressed == true)                              //Confirm the input once the button is released again
          {
            confirmed = true;
          }
        }  
      }
    }
  }
  pinMode(enablePin, INPUT);                                //Open circuit enable pin, disables motors
  resetVariables ();
}

void inputPanData ()                                                              //Input required data for pan mode
{
  dataInputNo = 0;                                                                //Input travel distance
  inputField (maxTravDist, minTravDist, maxTravDist, travDistInc);
  dataInputNo = 1;                                                                //Input travel direction
  inputField (0, 0, 1, 1);
  dataInputNo = 2;                                                                //Input travel duration
  inputField (initialDur, minDur, maxDur, durInc);
}

void inputRotateData ()                                                           //Input required data for rotate mode
{
  dataInputNo = 0;                                                                //Input rotation angle
  inputField (initialRotAng, minRotAng, maxRotAng, rotAngInc);
  dataInputNo = 1;                                                                //Input rotation direction
  inputField (0, 0, 1, 1);
  dataInputNo = 2;                                                                //Input rotation duration
  inputField (initialDur, minDur, maxDur, durInc);
}

void inputPanAndRotateData ()                                                     //Input required data for pan and rotate mode
{
  dataInputNo = 0;                                                                //Input pan distance
  inputField (maxTravDist, minTravDist, maxTravDist, travDistInc);
  dataInputNo = 1;                                                                //Input pan direction
  inputField (0, 0, 1, 1);
  dataInputNo = 2;                                                                //Input rotation angle
  inputField (initialRotAng, minRotAng, maxRotAng, rotAngInc);
  dataInputNo = 3;                                                                //Input rotation direction
  inputField (0, 0, 1, 1);
  dataInputNo = 4;                                                                //Input total duration
  inputField (initialDur, minDur, maxDur, durInc);
}

void inputTrackData ()                                                            //Input required data for object tracking mode
{
  dataInputNo = 0;                                                                //Input pan distance
  inputField (maxTravDist, minTravDist, maxTravDist, travDistInc);
  dataInputNo = 1;                                                                //Input pan direction
  inputField (0, 0, 1, 1);
  dataInputNo = 2;                                                                //Input rotation angle
  inputField (initialObjDist, minObjDist, maxObjDist, objInc);
  dataInputNo = 3;                                                                //Input total duration
  inputField (initialDur, minDur, maxDur, durInc);
}

void updatePanDataDisplay ()
{
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(2,10);                                  //Set the display cursor position
  display.print(F("Distance: "));                           //Set the display text
  display.setCursor(2,20);
  display.print(F("Direction: "));
  display.setCursor(2,30);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)                                     //Get the cursor position & update changing variable
  {
    selected = 10;
    travDist = encoderPos;
  }
  else if (dataInputNo == 1)
  {
    selected = 20;
    travelDir = encoderPos;
  }
  else
  {
    selected = 30;
    travTime = encoderPos;
    if(calcInterval (calcTravelPulses ()) < minInterval)    //Flags movement too fast
    {
      display.setCursor(40,55);                             //Set the display cursor position
      display.print(F("Too Fast"));                         //Set the display text
    }
  }
  display.setCursor(65,selected);                           //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75,10);                                 //Display the field data
  display.print(travDist);
  display.print(F("mm"));
  display.setCursor(75,20);
  if (travelDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75,30);
  display.print(travTime);
  display.print(F("s"));
  display.display();                                        //Output the display text
}

void updateRotDataDisplay ()
{
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(2,10);                                  //Set the display cursor position
  display.print(F("Rot. Ang.: "));                          //Set the display text
  display.setCursor(2,20);
  display.print(F("Direction: "));
  display.setCursor(2,30);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)                                     //Get the cursor position & update changing variable
  {
    selected = 10;
    rotAngle = encoderPos;
  }
  else if (dataInputNo == 1)
  {
    selected = 20;
    rotDir = encoderPos;
  }
  else
  {
    selected = 30;
    travTime = encoderPos;
    if(calcRotInterval (calcRotationPulses ()) < minInterval)  //Flags movement too fast
    {
      display.setCursor(40,55);                             //Set the display cursor position
      display.print(F("Too Fast"));                         //Set the display text
    }
  }
  display.setCursor(65,selected);                           //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75,10);                                 //Display the field data
  display.print(rotAngle);
  display.print(F("deg"));
  display.setCursor(75,20);
  if (rotDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75,30);
  display.print(travTime);
  display.print(F("s"));
  display.display();                                        //Output the display text
}

void updatePanAndRotateDataDisplay ()
{
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(2,2);                                   //Set the display cursor position
  display.print(F("Distance: "));                           //Set the display text
  display.setCursor(2,12);
  display.print(F("Trav. Dir: "));
  display.setCursor(2,22);
  display.print(F("Rot. Ang: "));
  display.setCursor(2,32);
  display.print(F("Rot. Dir: "));
  display.setCursor(2,42);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)                                     //Get the cursor position & update changing variable
  {
    selected = 2;
    travDist = encoderPos;
  }
  else if (dataInputNo == 1)
  {
    selected = 12;
    travelDir = encoderPos;
  }
  else if (dataInputNo == 2)
  {
    selected = 22;
    rotAngle = encoderPos;
  }
  else if (dataInputNo == 3)
  {
    selected = 32;
    rotDir = encoderPos;
  }
  else
  {
    selected = 42;
    travTime = encoderPos;
    if(calcInterval (calcTravelPulses ()) < minInterval)    //Flags movement too fast
    {
      display.setCursor(40,55);                             //Set the display cursor position
      display.print(F("Too Fast"));                         //Set the display text
    }
  }
  display.setCursor(65,selected);                           //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75,2);                                  //Display the field data
  display.print(travDist);
  display.print(F("mm"));
  display.setCursor(75,12);
  if (travelDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75,22);
  display.print(rotAngle);
  display.print(F("deg"));
  display.setCursor(75,32);
  if (rotDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75,42);
  display.print(travTime);
  display.print(F("s"));
  display.display();                                        //Output the display text
}

void updateTrackDataDisplay ()
{
  display.clearDisplay();                                   //Clear display
  display.setTextSize(1);                                   //Set the text size
  display.setCursor(2,10);                                  //Set the display cursor position
  display.print(F("Distance: "));                           //Set the display text
  display.setCursor(2,20);
  display.print(F("Trav. Dir: "));
  display.setCursor(2,30);
  display.print(F("Obj. Dist: "));
  display.setCursor(2,40);
  display.print(F("Duration: "));
  int selected = 0;
  if (dataInputNo == 0)                                     //Get the cursor position & update changing variable
  {
    selected = 10;
    travDist = encoderPos;
  }
  else if (dataInputNo == 1)
  {
    selected = 20;
    travelDir = encoderPos;
  }
  else if (dataInputNo == 2)
  {
    selected = 30;
    objDist = encoderPos;
  }
  else
  {
    selected = 40;
    travTime = encoderPos;
    if(calcInterval (calcTravelPulses ()) < minInterval)    //Flags movement too fast
    {
      display.setCursor(40,55);                             //Set the display cursor position
      display.print(F("Too Fast"));                         //Set the display text
    }
  }
  display.setCursor(65,selected);                           //Set the display cursor position
  display.print(F(">"));
  display.setCursor(75,10);                                 //Display the field data
  display.print(travDist);
  display.print(F("mm"));
  display.setCursor(75,20);
  if (travelDir == 0)
    display.print(F("Forward"));
  else
    display.print(F("Reverse"));
  display.setCursor(75,30);
  display.print(objDist);
  display.print(F("mm"));
  display.setCursor(75,40);
  display.print(travTime);
  display.print(F("s"));
  display.display();                                        //Output the display text
}

void inputField (int initialSetting, int lowerLimit, int upperLimit, int increment)
{
  encLowLim = lowerLimit;
  encHighLim = upperLimit;
  encIncrement = increment;
  encoderPos = initialSetting;                              //Encoder starts from initial setting
  prevEncoderPos = encoderPos+1;                            //Set different so that display is updated on first cycle
  boolean confirmed = false;                                //Both used to confirm button push to select mode
  boolean pressed = false;
  while(!confirmed)                                         //While the user has not confirmed the input
  {
    byte buttonState = digitalRead (encButton); 
    if (buttonState != oldButtonState)
    {
      if (millis () - buttonPressTime >= debounceTime)      //Debounce button
      {
        buttonPressTime = millis ();                        //Time when button was pushed
        oldButtonState =  buttonState;                      //Remember button state for next time
        if (buttonState == LOW)
        {
          pressed = true;
        }
        else 
        {
          if (pressed == true)                              //Confirm the input once the button is released again
          {
            confirmed = true;
            Serial.println("Input Confirmed");
          }
        }  
      }
    }
    if(encoderPos!=prevEncoderPos)                          //Update the display if the encoder position has changed
    {
      if (modeSelected == 0)
        updatePanDataDisplay ();
      else if (modeSelected == 1)
        updateRotDataDisplay ();
      else if (modeSelected == 2)
        updatePanAndRotateDataDisplay ();
      else
        updateTrackDataDisplay ();
      prevEncoderPos=encoderPos;
    }
  }
}

void resetVariables ()                                       //Reset variables back to initial values after run
{
  travDist = maxTravDist;
  travTime = initialDur;
  objDist = initialObjDist;
  travelDir = 0;
  rotDir = 0;
  rotAngle = initialRotAng;
}

int calcTravelPulses ()                                       //Calculates the number of pulses required to move a certain distance
{
  int travP = travDist*pulsesPerMM;
  return travP;
}

int calcRotationPulses ()                                     //Calculate the number of pulses required to rotate a certain angle
{
  int rotP = rotAngle*pulsesPerDeg;
  return rotP;
}

boolean checkRot (int i)                                      //Used in tracking to calculate the angle required to track object
{
  boolean rotP = false;
  float deltaAngle;
  if(((travDist/2)-(i/pulsesPerMM)) > 0)
  {
    if (currentAngle < 90-(1/pulsesPerDeg))
    {
      float newAngle = atan((objDist)/((travDist/2)-(i/pulsesPerMM)))*180/M_PI;
      deltaAngle = newAngle-currentAngle;
    }
  }
  else if (((travDist/2)-(i/pulsesPerMM)) < 0)
  {
    if (currentAngle > 0)
    {
      float newAngle = atan((objDist)/((i/pulsesPerMM)-(travDist/2)))*180/M_PI;
      deltaAngle = currentAngle-newAngle;
    }
  }
  if(deltaAngle >= (1/pulsesPerDeg))
  {
    rotP = true;
    if ((travDist/2)-(i/pulsesPerMM) > 0)
      currentAngle=currentAngle+(1/pulsesPerDeg);
    else
      currentAngle=currentAngle-(1/pulsesPerDeg);
  }
  return rotP;
}

float calcInterval (int numPulses)                                 //Calculate the interval required between pulses to achieve duration
{
  float inter = travTime*1000000/numPulses;
  return inter;
}

float calcRotInterval (int numPulses)                              //Calculate the interval required between pulses to achieve duration
{
  float inter = travTime*1000/numPulses;
  return inter;
}

c'est encore plein de delayMicroseconds() ?