Logging inputs and outputs for troubleshooting

All, I have been using X27-168 stepper motors with my flight sim game rig to emulate the movement of engine gauges. I've been successful in getting it to work, and been able to use EasyDriver, A4988 and AX1201728SG drivers, plus also directly driving the stepper from the arduino using the pins connected to the motor.

By and large it works pretty well, the game outputs the data via USB to the arduino via an interface program, and the arduino then uses a sketch to work with the stepper - pretty standard stuff.

However the quality of the stepper movement is not very nice, as it is jittery, and not smooth especially at lower speeds. The quality does not change whichever stepper driver you use, or even driven directly. I have tried varying the acceleration and speed rates of the motors both using the libraries and the sketches, plus used the microstepping levels of the EasyDriver and the A4988, all to no avail.

The conclusion that I have come to is that for whatever reason, the game data output is the source of the jittery movement, and so would like some advice on maybe putting in a smoothing algorithm or filter that takes the output from the arduino and gives a damped signal to the stepper driver / motor.

This is an example of the coding used, this particular one is used with the AX1201728SG chip

#define DCSBIOS_IRQ_SERIAL


#include "SwitecX12.h"
#include "DcsBios.h"

const int RESET = 10;

const bool InitToMax = true;

struct StepperConfig {
  unsigned int MaxSteps;
  unsigned int MinSteps;
  unsigned int StepPin;
  unsigned int DirPin;
  unsigned int MaxAdjustment;
  unsigned int MinAdjustment; 
};


struct StepperConfig APUTempConfig = {
  3780, //315 degrees * 3 steps * 4 microsteps;
  0,
  8,
  9,
  0,
  10
};



class X27Stepper : public DcsBios::Int16Buffer {
  private:
    SwitecX12& stepper;
    StepperConfig& stepperConfig;
    unsigned int (*map_function)(unsigned int);
    unsigned char initState;
  public:
    X27Stepper(unsigned int address, SwitecX12& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
    : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
    }
    
    virtual void loop() {
      if (initState == 0) { // not initialized yet
        digitalWrite(RESET, HIGH);
        stepper.zero();
        initState = 1;
      }
      if (initState == 1) { // zeroing
        if (stepper.stopped) {
          if (InitToMax)
            stepper.setPosition(stepperConfig.MaxSteps - stepperConfig.MaxAdjustment);
          else
            stepper.setPosition(stepperConfig.MinSteps + stepperConfig.MinAdjustment);     
          stepper.update();
          initState = 2;
        }
      }
      if (initState == 2) { // running normally
        if (hasUpdatedData()) {
          unsigned int newPosition = map_function(getData());
          newPosition = constrain(newPosition, stepperConfig.MinSteps, stepperConfig.MaxSteps);
          stepper.setPosition(newPosition);
        }
        stepper.update();
      }
    }
};


SwitecX12 APUTempMotor(APUTempConfig.MaxSteps, APUTempConfig.StepPin, APUTempConfig.DirPin);


X27Stepper APUTemp(0x10c0, APUTempMotor, APUTempConfig, [](unsigned int newValue) -> unsigned int {
  return map(newValue, 0, 65535, APUTempConfig.MinSteps + APUTempConfig.MinAdjustment, APUTempConfig.MaxSteps - APUTempConfig.MaxAdjustment);
});



void setup() {
  pinMode(RESET, OUTPUT);
  digitalWrite(RESET, HIGH);
  DcsBios::setup();

}

void loop() {
  DcsBios::loop();

}

As you can see it is not particularly complicated, but would appreciate any advice / help from anyone that can suggest how to damp the output signal

I will confess to not being that great with Arduinos and have to give most of the credit for the basic stuff to others, hence my reaching out to the community!

Thanks for looking and any help / suggestions given

Les

Hi again

Searching though other stuff on here I have found a smoothing algorithm that I have 'successfully' spliced into my sketch that has the Arduino running the stepper directly without a stepper driver. It's the simplest of the sketches for me to try and while it compiles, loads and runs the stepper (hence 'successfully') it has not changed the stepper motion for the better.

It seems to be identical when there is a significant movement, but when the movement slows down, it moves to max CW, pauses, then comes back to the correct position for a moment, then back to max CW, pause, correct position, back to max CW and so on. It seems to be that it is smoothing using numbers extracted from somewhere, so I assume it is my code that has introduced something strange. For info, during the 'correct' movement, there is no indication that the smoothing is working

here's the code I'm using

#define DCSBIOS_DEFAULT_SERIAL

#include "DcsBios.h"
#include "SwitecX25.h"

// 315 degrees of range = 315x3 steps = 945 steps
int newValue;
unsigned int maxSteps = 945;


// declare motor1 with 945 steps on pins 8-11
SwitecX25 motor1(maxSteps, 8, 9, 10, 11);

int sensorVal       = 0;    // store the value coming from the sensor
int smoothedVal     = 0;    // smoothed result
int samples         = 10;    // amount of samples



void setup(void) {
  
  DcsBios::setup();

  motor1.zero();   // this is a slow, blocking operation
  motor1.setPosition(500);

}


void onApuTempChange(unsigned int newValue)
{
  unsigned int sensorVal =  map(newValue, 0, 65535, 0, maxSteps);
  
  smoothedVal = smoothedVal + ((sensorVal - smoothedVal)/samples);
  motor1.setPosition(smoothedVal);
}
DcsBios::IntegerBuffer apuTempBuffer(0x10c0, 0xffff, 0, onApuTempChange);


void loop(void) {

  
  motor1.update();
  DcsBios::loop();

}

What have I done wrong this time?!

Les

Hi again all

I managed to successfully include the smoothing algorithm in the first sketch I posted, and got it working without the curious behaviour of the last attempt. This is what worked

#define DCSBIOS_IRQ_SERIAL


#include "SwitecX12.h"
#include "DcsBios.h"

const int RESET = 10;

const bool InitToMax = true;

struct StepperConfig {
  unsigned int MaxSteps;
  unsigned int MinSteps;
  unsigned int StepPin;
  unsigned int DirPin;
  unsigned int MaxAdjustment;
  unsigned int MinAdjustment; 
};


struct StepperConfig APUTempConfig = {
  3780, //315 degrees * 3 steps * 4 microsteps;
  0,
  8,
  9,
  0,
  10
};

int sensorVal       = 0;    // store the value coming from the sensor
int smoothedVal     = 0;    // smoothed result
int samples         = 10;    // amount of samples

class X27Stepper : public DcsBios::Int16Buffer {
  private:
    SwitecX12& stepper;
    StepperConfig& stepperConfig;
    unsigned int (*map_function)(unsigned int);
    unsigned char initState;
  public:
    X27Stepper(unsigned int address, SwitecX12& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
    : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
    }
    
    virtual void loop() {
      if (initState == 0) { // not initialized yet
        digitalWrite(RESET, HIGH);
        stepper.zero();
        initState = 1;
      }
      if (initState == 1) { // zeroing
        if (stepper.stopped) {
          if (InitToMax)
            stepper.setPosition(stepperConfig.MaxSteps - stepperConfig.MaxAdjustment);
          else
            stepper.setPosition(stepperConfig.MinSteps + stepperConfig.MinAdjustment);     
          stepper.update();
          initState = 2;
        }
      }
      if (initState == 2) { // running normally
        if (hasUpdatedData()) {
          unsigned int newPosition = map_function(getData());
          newPosition = constrain(newPosition, stepperConfig.MinSteps, stepperConfig.MaxSteps);
          stepper.setPosition(newPosition);
        }
        stepper.update();
      }
    }
};


SwitecX12 APUTempMotor(APUTempConfig.MaxSteps, APUTempConfig.StepPin, APUTempConfig.DirPin);


X27Stepper APUTemp(0x10c0, APUTempMotor, APUTempConfig, [](unsigned int smoothedVal) -> unsigned int {
  smoothedVal = smoothedVal + ((sensorVal - smoothedVal)/samples);

  return map(smoothedVal, 0, 65535, APUTempConfig.MinSteps + APUTempConfig.MinAdjustment, APUTempConfig.MaxSteps - APUTempConfig.MaxAdjustment);
});



void setup() {
  
  pinMode(RESET, OUTPUT);
  digitalWrite(RESET, HIGH);
  DcsBios::setup();

}

void loop() {
  DcsBios::loop();

}

However despite the smoothing, the movement of the gauge was still very jerky when the pointer was moving slowly, and so the conclusion is that the data refresh rate of the source data from the game is at fault - nothing that you can help me with!

I posted the response even though it was as a result of my own work as it may help someone! Thanks to anyone who looked in on this

Cheers

Les

I would think that you could set the gauge position outside of the code controlled by DCS and apply smoothing there. So look at the position the sim wants the gauge at in loop and move towards it with whatever smoothing algorithm you want. No need to rely just on sim updates.

Interesting, though as usual my complete incompetence leaves me wondering how to do that! I imagine that you mean that I just use the gauge position as an average target, but I struggle to see how that differs to a smoothed value

Cheers

Les

What I'm suggesting is that you get the desired gauge position from the sim and keep it in a variable.

Keep the current actual gauge position in another variable.

Every ten milliseconds (or whatever you fancy) in your loop function, compare desired against actual. If they differ, move actual one step towards desired and use it to set the gauge position.

Then, whatever the sim gives you can be smoothly applied, however long it is between updates and you can tweak the smoothing intervals for different gauges if necessary.

Arrays will be handy I suspect.

Ok, I think I get it. In parallel I am also asking the authors of the software that exports the data about the sampling rate to see if that is fixed, or whether it can be increased.

However I will look into your suggestion as conceptually it will allow the instructions to the stepper to be more progressive, rather than large steps

Les

Sounds like the issue is the granularity of the value from getData, or of the map_function.

For slow speeds the granularity will be impossible to smooth out, unless you can track the speed
of change of the data dynamically and tune the low-pass filtering of the data to match its speed.

Any fixed filtering will limit the max speed the pointer can move, and to smooth slow motion
this will completely block fast movement.

Another approach is to always have random flicker added in, then the individual steps during
slow changes will be hidden. In other words add random jitter all the time.

I think the granularity issue is correct; the problem seems to be that the authors of the program are no longer in evidence, I believe they stepped back to focus on their 'real' jobs. As a result I have no way of getting info from them with regards to sampling rates, let alone potential changes to it

What I want to do is try and log the data output to the arduino, and what the arduino is outputting to the stepper and at what rate, so I can at least understand that bit, as it may shape how I go forward with this.

Les

All, I have a thread in the CNC and motors etc section about trying to improve the quality of movement of a stepper motor, but ultimately have concluded that the fault is neither that of the stepper motor or driver, nor of the Arduino driving it. I believe it is due to the data the Arduino is being asked to work with, and more specifically the either the transmission rate or the granularity of that data.

As a result, I want to know if there is a way to output or at least log the input data and output so that I can try and see what is going on in (hopefully) such a way that it can be modified to improve matters.

I’m not a very able user of Arduinos, so I realise my limitations on this, so understand I may not be able to change matters. For example I see that the data from the source is only updating every 200 miliseconds, I know that unless I can change that source data (unlikely) I know that I have no way of changing it, but if the data is being updated every 20 miliseconds it is worth pursuing.

Cheers

Les

Topics merged

Cross-posting is against the rules of the forum. The reason is that duplicate posts can waste the time of the people trying to help. Someone might spend 15 minutes (or more) writing a detailed answer on this topic, without knowing that someone else already did the same in the other topic.

Repeated cross-posting will result in a timeout from the forum.

In the future, please take some time to pick the forum board that best suits the topic of your question and then only post once to that forum board. This is basic forum etiquette, as explained in the sticky "How to use this forum - please read." post you will find at the top of every forum board. It contains a lot of other useful information. Please read it.

Thanks in advance for your cooperation.

Is the problem with configuring the sim itself or DCSBios?

DCS BIOS is the interface program that simply reads from the sim, so as I understand it DCS BIOS has no effect on what the game outputs; that means it must be within whatever the DCS BIOS is programmed to output. As mentioned, attempts to contact the creators of that software has been fruitless (not that I blame them, they did a great job in what they have provided for us for free) so I believe it is unlikely we can change that either.

My hopes rest on being able to manipulate the data that is output in a way that is better suited to the needs of driving the stepper. It may be that the output is of sufficiently high fidelity and sampling rate that it can made to function well by changing the way in which the Arduino processes it.

Cheers

Les

What does the sim output? It might be instructive to see how frequently each gauge is reported. I'd also want to know if that can be changed - I was able to do so in a sim I was using a while back.

That's what I want to see - if I can log the inputs and outputs, maybe I can get a lead

Cheers

Les

I have written this sketch to output the DCS BIOS numbers to an LCD display, which I have done successfully before for other instruments. Despite (apparently) using the same syntax and structure, i keep getting a ‘‘newValue’ does not name a type’ error

#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#define DCSBIOS_IRQ_SERIAL

#include <DcsBios.h>

LiquidCrystal_PCF8574 lcd(0x27); // set the LCD address to 0x27 for a 16 chars and 2 line display

int show;


void onApuTempChange(unsigned int newValue);

  newValue = map(apuTempBufferValue, 0, 65536, 0, 10);
    
  {
  lcd.setCursor(0, 0);
  lcd.print(newValue);
}
DcsBios::IntegerBuffer apuTempBuffer(0x10c0, 0xffff, 0, onApuTempChange);



void setup() {
  lcd.begin(16, 2);
  lcd.clear();
  DcsBios::setup();


}

void loop() {
  DcsBios::loop();
  lcd.setBacklight(255);
}

I am hoping that being able to see the mapped numbers on the LCD that it will show me whether the numbers are continually changing or jumping with large steps.

Any help on resolving that sketch would be appreciated

Les

newValue = map(apuTempBufferValue, 0, 65536, 0, 10);

This line of code is not in a function because the previous line

void onApuTempChange(unsigned int newValue);

ends in a semicolon

Thanks for that. Just getting rid of the semicolon wasn’t enough, I had to make the map part of the sketch non-functioning as it would give an error, however using the sketch below I got the data to show on an LCD display

#include <Wire.h>
#include <LiquidCrystal_PCF8574.h>
#define DCSBIOS_IRQ_SERIAL

#include <DcsBios.h>

LiquidCrystal_PCF8574 lcd(0x27); // set the LCD address to 0x27 for a 16 chars and 2 line display

int show;


void onApuTempChange(unsigned int newValue) {

  //  newValue = map(apuTempBufferValue, 0, 65536, 0, 10);


  lcd.setCursor(0, 0);
  lcd.print(newValue);
}
DcsBios::IntegerBuffer apuTempBuffer(0x10c0, 0xffff, 0, onApuTempChange)


void setup() {
  lcd.begin(16, 2);
  lcd.clear();
  DcsBios::setup();

}

void loop() {
  DcsBios::loop();
  lcd.setBacklight(255);
}

When I ran this in game, with an operating gauge using the stepper motor and a AX1201728SG driver next to it, the LCD display showed a pretty smooth scrolling set of numbers, and even when the magnitude of change of the numbers were not large, the refresh rate of the numbers was sufficient to still outpace the LCD’s ability to display fully. At the same time the pointer movement was jittery, so something must be happening between the input signal and the output to the stepper. This below is the sketch the ‘gauge’ was operating with

#define DCSBIOS_IRQ_SERIAL


#include "SwitecX12.h"
#include "DcsBios.h"

const int RESET = 10;

const bool InitToMax = true;

struct StepperConfig {
  unsigned int MaxSteps;
  unsigned int MinSteps;
  unsigned int StepPin;
  unsigned int DirPin;
  unsigned int MaxAdjustment;
  unsigned int MinAdjustment; 
};


struct StepperConfig APUTempConfig = {
  3780, //315 degrees * 3 steps * 4 microsteps;
  0,
  8,
  9,
  0,
  10
};



class X27Stepper : public DcsBios::Int16Buffer {
  private:
    SwitecX12& stepper;
    StepperConfig& stepperConfig;
    unsigned int (*map_function)(unsigned int);
    unsigned char initState;
  public:
    X27Stepper(unsigned int address, SwitecX12& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
    : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
    }
    
    virtual void loop() {
      if (initState == 0) { // not initialized yet
        digitalWrite(RESET, HIGH);
        stepper.zero();
        initState = 1;
      }
      if (initState == 1) { // zeroing
        if (stepper.stopped) {
          if (InitToMax)
            stepper.setPosition(stepperConfig.MaxSteps - stepperConfig.MaxAdjustment);
          else
            stepper.setPosition(stepperConfig.MinSteps + stepperConfig.MinAdjustment);     
          stepper.update();
          initState = 2;
        }
      }
      if (initState == 2) { // running normally
        if (hasUpdatedData()) {
          unsigned int newPosition = map_function(getData());
          newPosition = constrain(newPosition, stepperConfig.MinSteps, stepperConfig.MaxSteps);
          stepper.setPosition(newPosition);
        }
        stepper.update();
      }
    }
};


SwitecX12 APUTempMotor(APUTempConfig.MaxSteps, APUTempConfig.StepPin, APUTempConfig.DirPin);


X27Stepper APUTemp(0x10c0, APUTempMotor, APUTempConfig, [](unsigned int newValue) -> unsigned int {
  return map(newValue, 0, 65535, APUTempConfig.MinSteps + APUTempConfig.MinAdjustment, APUTempConfig.MaxSteps - APUTempConfig.MaxAdjustment);
});



void setup() {
  pinMode(RESET, OUTPUT);
  digitalWrite(RESET, HIGH);
  DcsBios::setup();

}

void loop() {
  DcsBios::loop();

}

I am starting to wonder whether the way that the sketch handles the ‘map’ function is causing the problem in some way

Les

That's odd, I wouldn't expect APU temperature to be moving around all that much. When you say jittery, do you mean it's jerking up & down?

I'd want to see the data, which I guess is what you're doing with the LCD except that you can't see history and it sounds like you're getting updates so fast you can't read them.

I would be inclined to use a second Arduino, preferably one with multiple serial ports and send the APU data to it so you can print it on the Serial monitor.

Another thing that might be worth doing (if you can find out the format) is to make something that simulates the data from the game, but only sends data for one gauge. Then you can control exactly what is sent and see what DCSBios does to your stepper.

In the end though, I still think you will need to do some damping in software. All the above stuff is just going to help you understand what's going wrong.

I'll try and take some video to show what is happening, it may help with understanding what I mean. However, is it possible to use the smoothing function before the map function?

Les