Rotary encoder stops other code working

Hi, I am a complete and utter beginner when it comes to Arduino and coding so please bear with me and I apologise if this is posted in the wrong place but it seems the logical one.
I am trying to combine two projects into one. The first is hacking a Logitech throttle quadrant game controller using a pro micro. I followed a previous project that I found online. It works very well. I then found a project to add a trim wheel to the quadrant using another pro micro. Not wanting to use two boards when there are enough inputs on one I tried to understand and combine the relevant parts of the code. However the trim wheel works fine but the quadrant stops working altogether. I have spent days trawling through the code and reading info online but I can't figure this out.
Can anyone help me with this please? Even just a pointer in the right direction please?

*            Throttle script with filtering
 *  This script takes three axis, and filters the signal for an output 
 *  at 50 Hz. The clock is used to exactly time the polling, 
 *  to avoid sampling bias. A crude bandpass filter throws 
 *  away spikes that are impossible(!) to result from user 
 *  input. This will convert spikes on the control axis into 
 *  a mere stutter. Then a Kalman filter is applied to the 
 *  20 Hz signal to remove remaining gaussian noise. 
 *  All axes each get their own one dimensional Kalman 
 *  because there is no correlation that is relevant for the 
 *  filter. The constants in this script are configured for 
 *  the Saitek Pro throttle quadrant. Thanks to this project
 *  https://forums.x-plane.org/index.php?/forums/topic/240443-saitek-throttle-quadrant-usb-conversion-using-arduino/
*/

// Robust Rotary encoder reading
//
// Copyright John Main - best-microcontroller-projects.com
//
// Modified for trim wheel application by Jason Merrill
//

#include "Joystick.h"
#include "SimpleKalmanFilter.h"

#define REV_THRESHOLD_X 790
#define REV_THRESHOLD_Y 840
#define REV_THRESHOLD_Z 830
#define OUTPUT_LIMIT 890
#define OUTPUT_HZ 20
#define BANDPASS_SPD 13 
#define MEASUREM_UNCERT 10
#define PROC_VARIANCE 2
#define CLK 3
#define DATA 2
/*                      Settings
* REV_THRESHOLD      Past idle notch of the throttle, activates reverse
* OUTPUT_LIMIT       Limit of value that is output for a slider. should
*                    correspond to the idle notch
* OUTPUT_HZ          Desired output frequency to the computer
* BANDPASS_SPD       Rate of change over which measurements will be
*                    discarded. 10 Means 10 times the full range per 
*                    second. Don't set the bandpass speed too low, 
*                    erratic output if it is actually achieved! It 
*                    needs to be a value where with 100% certainty it 
*                    is a potentiometer spike and not user input!
* MEASUREMENT_UNCERT Random noise to be expected from the Pot,
*                    in output resolution units
* PROC_VARIANCE      Normal accelerations of the control to be 
*                    expected, in output resoltution units per sample 
*                    in OUTPUT_HZ. Decreasing this will decrease 
*                    responsiveness
*/

int potX, valX;
int potY, valY;
int potZ, valZ;
int but1, but2, but3, but4, but5, but6;
int revX, revY, revZ;
unsigned int bandPass;
unsigned long lastPoll = 0;
unsigned int sampleTime;
const int16_t MAX_VALUE = 100;
const int16_t MIN_VALUE = -100;
static uint8_t prevNextCode = 0;
static uint16_t store=0;
int16_t counter = 0;

// output configuration
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_JOYSTICK, 9, 0, // 9 Buttons, 0 Hate Switch
  true, true, true,             // X, Y, Z Axis
  false, false, false,          //  No Rx, Ry, Rz
  false, true,                  //  No Rudder but has Throttle( ie Trim Wheel)
  false, false, false);         //  No Accel, Brake or Steering 

SimpleKalmanFilter kalmanFilterX(MEASUREM_UNCERT, MEASUREM_UNCERT, PROC_VARIANCE);
SimpleKalmanFilter kalmanFilterY(MEASUREM_UNCERT, MEASUREM_UNCERT, PROC_VARIANCE);
SimpleKalmanFilter kalmanFilterZ(MEASUREM_UNCERT, MEASUREM_UNCERT, PROC_VARIANCE);

void setup() {
  Joystick.begin();
  Joystick.setXAxisRange(0, OUTPUT_LIMIT);
  Joystick.setYAxisRange(0, OUTPUT_LIMIT);
  Joystick.setZAxisRange(0, OUTPUT_LIMIT);
  Joystick.setThrottleRange(MIN_VALUE, MAX_VALUE);

  pinMode(A0, INPUT); //configure inputs for potentiometers
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);
  pinMode(CLK, INPUT);
  pinMode(CLK, INPUT_PULLUP);
  pinMode(DATA, INPUT);
  pinMode(DATA, INPUT_PULLUP);
  pinMode(4,INPUT_PULLUP); //configure inputs for buttons, use internal pullup resistor
  pinMode(5,INPUT_PULLUP); 
  pinMode(6,INPUT_PULLUP); 
  pinMode(7,INPUT_PULLUP); 
  pinMode(8,INPUT_PULLUP); 
  pinMode(9,INPUT_PULLUP); 
    
  //initialize parameters for the filters and sampling
  sampleTime = 1000000 / OUTPUT_HZ;
  bandPass = 30000; //just the first two seconds, no real bandpass
  
  Serial.begin( 9600 ); //remove comment for debugging on the serial monitor/plotter
}

void loop() {

  //enable band pass filter after 2 seconds stabilization
  if (bandPass ==  30000 && millis() > 2000) {
    bandPass = (BANDPASS_SPD * OUTPUT_LIMIT) / OUTPUT_HZ;
  }
    static int8_t c,val;
    if( val=read_rotary() ) {
     c +=val;
     if ( prevNextCode==0x0b) {
      counter++;
      if(counter > MAX_VALUE){
        counter = MAX_VALUE;
      }
      Joystick.setThrottle(counter);
    }
    if ( prevNextCode==0x07) {
      counter--;
      if(counter < MIN_VALUE){
        counter = MIN_VALUE;
      }
      Joystick.setThrottle(counter);
    }
   }
}

  
  

   // A vald CW or  CCW move returns 1, invalid returns 0.
   int8_t read_rotary() {{
  static int8_t rot_enc_table[] = {0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0};

  prevNextCode <<= 2;
  if (digitalRead(DATA)) prevNextCode |= 0x02;
  if (digitalRead(CLK)) prevNextCode |= 0x01;
  prevNextCode &= 0x0f;
  
  // If valid then store as 16 bit data.
  if  (rot_enc_table[prevNextCode] ) {
    store <<= 4;
    store |= prevNextCode;
    if ((store&0xff)==0x2b) return -1;
    if ((store&0xff)==0x17) return 1;
  }
  return 0;
}

//read inputs
  while (lastPoll+sampleTime > micros()) {delayMicroseconds(4);} //clocked sample rate.
  potX = analogRead(A0); //X axis, Throttle
  potY = analogRead(A1); //Y axis, Prop
  potZ = analogRead(A2); //Z axis, Mixture
  lastPoll = micros();
  but1 = !digitalRead(4); 
  but2 = !digitalRead(5); 
  but3 = !digitalRead(6); 
  but4 = !digitalRead(7); 
  but5 = !digitalRead(8); 
  but6 = !digitalRead(9); 
  //process notch and reverse ranges 
  if (potX > REV_THRESHOLD_X) { //test whether lever is in reverse range
    revX = 1; //reverse on
  } else {
    revX = 0; //reverse off
  }

  if (potX > OUTPUT_LIMIT){ potX = OUTPUT_LIMIT;} //limit the output range
  
  if (potY > REV_THRESHOLD_Y) { //test whether lever is in reverse range
    revY = 1; //reverse on
  } else {
    revY = 0; //reverse off
  }

  if (potY > OUTPUT_LIMIT){ potY = OUTPUT_LIMIT;} //limit the output range

  if (potZ > REV_THRESHOLD_Z) { //test whether lever is in reverse range
    revZ = 1; //reverse on
  } else {
    revZ = 0; //reverse off
  }

  if (potZ > OUTPUT_LIMIT){ potZ = OUTPUT_LIMIT;} //limit the output range

  //throw out too large changes (bandpass)
  if (abs(valX -potX) < bandPass) { //very crude bandpass filter
    valX= potX;                     
    } else {
    Serial.println(" !!!BANDPASS FILTER HAS TWROWN OUT AN INPUT VALUE!!!"); //debug only
  }
  
  if (abs(valY -potY) < bandPass) {
    valY= potY;                     
    } else {
    Serial.println(" !!!BANDPASS FILTER HAS TWROWN OUT AN INPUT VALUE!!!"); //debug only
  }

  if (abs(valZ -potZ) < bandPass) {
    valZ= potZ;                     
    } else {
    Serial.println(" !!!BANDPASS FILTER HAS TWROWN OUT AN INPUT VALUE!!!"); //debug only
  }

  //apply Kalman filter to remove remaining gaussian noise
  valX = kalmanFilterX.updateEstimate(valX); 
  valY = kalmanFilterY.updateEstimate(valY);
  valZ = kalmanFilterZ.updateEstimate(valZ);

//  //remove comment for debugging on the serial monitor/plotter
  Serial.print("X: ");
  Serial.print(valX);
  Serial.print(" Y: ");
  Serial.print(valY);
  Serial.print(" Z: ");
  Serial.print(valZ);
  Serial.print(" rev X: ");
  Serial.print(revX);
  Serial.print(" rev Y: ");
  Serial.print(revY);
  Serial.print(" rev Z: ");
  Serial.print(revZ);
  Serial.println("");

  Joystick.setXAxis(OUTPUT_LIMIT - valX);
  Joystick.setYAxis(OUTPUT_LIMIT - valY);
  Joystick.setZAxis(OUTPUT_LIMIT - valZ);
  Joystick.setButton(0, but1);
  Joystick.setButton(1, but2);
  Joystick.setButton(2, but3);
  Joystick.setButton(3, but4);
  Joystick.setButton(4, but5);
  Joystick.setButton(5, but6);
  Joystick.setButton(6, revX);
  Joystick.setButton(7, revY);
  Joystick.setButton(8, revZ);
  
}

Parts of your code are missing.

1 Like

I can't see anything missing? I just copied and pasted the whole thing. Out of curiosity what makes you say there is something missing?

Please post technical info about those two troubeling parts.

The throttle quadrant is three levers operating pots connected to analog pinns on a Pro micro clone and behaving like a joystick within Windows. There are 6 buttons and the reverse portion of the lever throw operates 3 more buttons on each axis. The trim wheel is basically a kw-040 rotary encoder connected to the clock pin 3 and data pin 2. It operates as the throttle axis within windows. Both function perfectly well seperately but when I try to combine the operations only the rotary encoder works.
The basic code for just quadrant is this:


/*            Throttle script with filtering
 *  This script takes three axis, and filters the signal for an output 
 *  at 50 Hz. The clock is used to exactly time the polling, 
 *  to avoid sampling bias. A crude bandpass filter throws 
 *  away spikes that are impossible(!) to result from user 
 *  input. This will convert spikes on the control axis into 
 *  a mere stutter. Then a Kalman filter is applied to the 
 *  20 Hz signal to remove remaining gaussian noise. 
 *  All axes each get their own one dimensional Kalman 
 *  because there is no correlation that is relevant for the 
 *  filter. The constants in this script are configured for 
 *  the Saitek Pro throttle quadrant.
*/

#include "Joystick.h"
#include "SimpleKalmanFilter.h"

#define REV_THRESHOLD_X 790
#define REV_THRESHOLD_Y 840
#define REV_THRESHOLD_Z 830
#define OUTPUT_LIMIT 890
#define OUTPUT_HZ 20
#define BANDPASS_SPD 13 
#define MEASUREM_UNCERT 10
#define PROC_VARIANCE 2

/*                      Settings
* REV_THRESHOLD      Past idle notch of the throttle, activates reverse
* OUTPUT_LIMIT       Limit of value that is output for a slider. should
*                    correspond to the idle notch
* OUTPUT_HZ          Desired output frequency to the computer
* BANDPASS_SPD       Rate of change over which measurements will be
*                    discarded. 10 Means 10 times the full range per 
*                    second. Don't set the bandpass speed too low, 
*                    erratic output if it is actually achieved! It 
*                    needs to be a value where with 100% certainty it 
*                    is a potentiometer spike and not user input!
* MEASUREMENT_UNCERT Random noise to be expected from the Pot,
*                    in output resolution units
* PROC_VARIANCE      Normal accelerations of the control to be 
*                    expected, in output resoltution units per sample 
*                    in OUTPUT_HZ. Decreasing this will decrease 
*                    responsiveness
*/

int potX, valX;
int potY, valY;
int potZ, valZ;
int but1, but2, but3, but4, but5, but6;
int revX, revY, revZ;
unsigned int bandPass;
unsigned long lastPoll = 0;
unsigned int sampleTime;

// output configuration
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_JOYSTICK, 9, 0,
  true, true, true, false, false, false,
  false, false, false, false, false);

SimpleKalmanFilter kalmanFilterX(MEASUREM_UNCERT, MEASUREM_UNCERT, PROC_VARIANCE);
SimpleKalmanFilter kalmanFilterY(MEASUREM_UNCERT, MEASUREM_UNCERT, PROC_VARIANCE);
SimpleKalmanFilter kalmanFilterZ(MEASUREM_UNCERT, MEASUREM_UNCERT, PROC_VARIANCE);

void setup() {
  Joystick.begin();
  Joystick.setXAxisRange(0, OUTPUT_LIMIT);
  Joystick.setYAxisRange(0, OUTPUT_LIMIT);
  Joystick.setZAxisRange(0, OUTPUT_LIMIT);

  pinMode(A0, INPUT); //configure inputs for potentiometers
  pinMode(A1, INPUT);
  pinMode(A2, INPUT);

  pinMode(4,INPUT_PULLUP); //configure inputs for buttons, use internal pullup resistor
  pinMode(5,INPUT_PULLUP); 
  pinMode(6,INPUT_PULLUP); 
  pinMode(7,INPUT_PULLUP); 
  pinMode(8,INPUT_PULLUP); 
  pinMode(9,INPUT_PULLUP); 
    
  //initialize parameters for the filters and sampling
  sampleTime = 1000000 / OUTPUT_HZ;
  bandPass = 30000; //just the first two seconds, no real bandpass
  
  Serial.begin( 9600 ); //remove comment for debugging on the serial monitor/plotter
}

void loop() {

  //enable band pass filter after 2 seconds stabilization
  if (bandPass ==  30000 && millis() > 2000) {
    bandPass = (BANDPASS_SPD * OUTPUT_LIMIT) / OUTPUT_HZ;
  }

  //read inputs
  while (lastPoll+sampleTime > micros()) {delayMicroseconds(4);} //clocked sample rate.
  potX = analogRead(A0); //X axis, Throttle
  potY = analogRead(A1); //Y axis, Prop
  potZ = analogRead(A2); //Z axis, Mixture
  lastPoll = micros();
  but1 = !digitalRead(4); 
  but2 = !digitalRead(5); 
  but3 = !digitalRead(6); 
  but4 = !digitalRead(7); 
  but5 = !digitalRead(8); 
  but6 = !digitalRead(9); 

  //process notch and reverse ranges
  if (potX > REV_THRESHOLD_X) { //test whether lever is in reverse range
    revX = 1; //reverse on
  } else {
    revX = 0; //reverse off
  }

  if (potX > OUTPUT_LIMIT){ potX = OUTPUT_LIMIT;} //limit the output range
  
  if (potY > REV_THRESHOLD_Y) { //test whether lever is in reverse range
    revY = 1; //reverse on
  } else {
    revY = 0; //reverse off
  }

  if (potY > OUTPUT_LIMIT){ potY = OUTPUT_LIMIT;} //limit the output range

  if (potZ > REV_THRESHOLD_Z) { //test whether lever is in reverse range
    revZ = 1; //reverse on
  } else {
    revZ = 0; //reverse off
  }

  if (potZ > OUTPUT_LIMIT){ potZ = OUTPUT_LIMIT;} //limit the output range

  //throw out too large changes (bandpass)
  if (abs(valX -potX) < bandPass) { //very crude bandpass filter
    valX= potX;                     
    } else {
    Serial.println(" !!!BANDPASS FILTER HAS TWROWN OUT AN INPUT VALUE!!!"); //debug only
  }
  
  if (abs(valY -potY) < bandPass) {
    valY= potY;                     
    } else {
    Serial.println(" !!!BANDPASS FILTER HAS TWROWN OUT AN INPUT VALUE!!!"); //debug only
  }

  if (abs(valZ -potZ) < bandPass) {
    valZ= potZ;                     
    } else {
    Serial.println(" !!!BANDPASS FILTER HAS TWROWN OUT AN INPUT VALUE!!!"); //debug only
  }

  //apply Kalman filter to remove remaining gaussian noise
  valX = kalmanFilterX.updateEstimate(valX); 
  valY = kalmanFilterY.updateEstimate(valY);
  valZ = kalmanFilterZ.updateEstimate(valZ);

  //remove comment for debugging on the serial monitor/plotter
  Serial.print("X: ");
  Serial.print(valX);
  Serial.print(" Y: ");
  Serial.print(valY);
  Serial.print(" Z: ");
  Serial.print(valZ);
  Serial.print(" rev X: ");
  Serial.print(revX);
  Serial.print(" rev Y: ");
  Serial.print(revY);
  Serial.print(" rev Z: ");
  Serial.print(revZ);
  Serial.println("");

  Joystick.setXAxis(OUTPUT_LIMIT - valX);
  Joystick.setYAxis(OUTPUT_LIMIT - valY);
  Joystick.setZAxis(OUTPUT_LIMIT - valZ);
  Joystick.setButton(0, but1);
  Joystick.setButton(1, but2);
  Joystick.setButton(2, but3);
  Joystick.setButton(3, but4);
  Joystick.setButton(4, but5);
  Joystick.setButton(5, but6);
  Joystick.setButton(6, revX);
  Joystick.setButton(7, revY);
  Joystick.setButton(8, revZ);
  
}

Is it a reply to reply #4?

Yes. I was replying to #4. Is this not what you were looking for? I don't know what else to tell you?

Delta_G thanks for your input. I didn't write the code. I tried to unsuccessfully combine two sketches written by the people who made the projects. My first post contains the code for the rotary encoder. The second sketch I posted was for the quadrant alone with no rotary encoder.
The blocking code you point out doesn't seem to affect the operation of the quadrant? I don't fully understand it yet but I assume it has something to do with the Kalman filter?

I thought that was what I had done😬. Thanks for that explanation though it makes much more sense now. I think I need to do a deep dive into rotary encoder examples and see if I can get this fixed. It's a little overwhelming for someone who has never done any sort of coding but you're never too old to learn.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.