Jump to content

Actuator control with DCS-BIOS


shreddersa

Recommended Posts

For my Spitfire Cockpit I am using an actuator with bungees to increase the tension in the roll and yaw controls with increasing airspeed.

 

I am trying to figure out whether I can simply read the DCS-BIOS value for the Airspeed from the AIRSPEEDGAUGE value? Sorry, I have controlled actuators with Arduino and a motor control shield before but am new to integrating this into DCS-BIOS.

 

I have provided my proposed code below. Any input would be greatly appreciated!

 

/*
 Tell DCS-BIOS to use a serial connection and use interrupt-driven
 communication. The main program will be interrupted to prioritize
 processing incoming data.
 
 This should work on any Arduino that has an ATMega328 controller
 (Uno, Pro Mini, many others).
*/
#define DCSBIOS_IRQ_SERIAL

#include "DcsBios.h"

/* paste code snippets from the reference documentation here */
DcsBios::Potentiometer trimWheel("TRIM_WHEEL", A1);
DcsBios::Potentiometer rtrimWheel("RTRIM_WHEEL", A2);
DcsBios::Switch2Pos fuelPump("FUEL_PUMP", 2);
DcsBios::Switch2Pos diluter("DILUTER", 3);
DcsBios::Switch2Pos diluterCover("DILUTER_COVER", 4);
DcsBios::Switch2Pos radTest("RAD_TEST", 5);
DcsBios::Switch2Pos radTestCover("RAD_TEST_COVER", 6);
DcsBios::Switch2Pos msTest("MS_TEST", 7);
DcsBios::Switch2Pos msTestCover("MS_TEST_COVER", 33);
DcsBios::Switch2Pos hatch("HATCH", 34);
DcsBios::Switch2Pos sideDoor("SIDE_DOOR", 35);
DcsBios::Switch2Pos pitot("PITOT", 36);
DcsBios::Switch2Pos radiator("RADIATOR", 37);
DcsBios::Switch2Pos wobblePump("WOBBLE_PUMP", 38);
DcsBios::RotaryEncoder uc("UC", "DEC", "INC", 14, 15);
DcsBios::Switch3Pos morseDnMode("MORSE_DN_MODE", 16, 17);
DcsBios::Switch2Pos morseKey("MORSE_KEY", 18);
DcsBios::Switch3Pos morseUpMode("MORSE_UP_MODE", 19, 20);
DcsBios::Switch2Pos iffB("IFF_B", 21);
DcsBios::Switch2Pos iffD("IFF_D", 22);
DcsBios::Switch2Pos iffCover("IFF_COVER", 23);
DcsBios::Switch2Pos iff0("IFF_0", 24);
DcsBios::Switch2Pos iff1("IFF_1", 25);
DcsBios::Switch2Pos droptankCock("DROPTANK_COCK", 26);
DcsBios::Switch2Pos droptankJett("DROPTANK_JETT", 27);
DcsBios::Switch2Pos ucEmer("UC_EMER", 28);
DcsBios::Switch2Pos deicer("DEICER", 29);
DcsBios::Switch2Pos hatchJettison("HATCH_JETTISON", 30);

//This is where I have added my own initiation routine steps:
/*  
Arduino Airspeed Force Feedback Control Rev1 - test program

 code by: Roel Stausebach, incorporating the work by various before me 
 The code addresses the following elements:
   MonsterMoto control
   Indicated airspeed from Airspeedgauge value of DCS-BIOS
   Actuator Potentiometer - to provide current position. 
   This test script assumes the potentiometer value for the actuator to range between 0 and 1023.

 1. OPERATION - READ THE "AIRSPEEDGAUGE" value, ADJUST THE ACTUATOR TO THAT POSITION
                The range can be between 0 and 65535. For the Spitfire the full gauge reading is 480mph
                We want full effect of the Force Feedback to be at 300mph already, so range value 41000
                So actuator will move for 0 to 41000.
                Arduino has an analogRead range from 0 to 1023, we assign AirSpeedConvert value to this.
                and an analogWrite range only from 0 to 255, 
                therefore the data from the actuator potentiometer needs to be converted
                to fit into the smaller range          
 */

const int CS_THRESHOLD = 135;  //this is the overload value for 5A

int inApin = 39;  // INA: Clockwise input
int inBpin =40; // INB: Counter-clockwise input
int pwmpin = 8; // PWM OUTput
int AIRSPEED = 0; // Set AIRSPEED TO 0 
int AirSpeedConvert = 0; //Set initial value
int pwmSpeed = 255; // Set initial ACTUATOR speed to max.

int cspin = 2; 

// these constants are set for picking up the actuator position:
const int actuatorPin = A3;   // the pin that the actuator potentiometer is attached to
int actuatorPosInit = analogRead(actuatorPin); //this will only ever be a value between 0 and 400
int actuatorPosNow = actuatorPosInit; // 




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

void onAirspeedgaugeChange(unsigned int newValue) {
   /* your code here */
   // and this is where I have added my own loop
   // read the Actuator potentiometer:
 actuatorPosNow = analogRead(actuatorPin);
 }
 //Determine what the current position SHOULD be based on airspeed:
 AirSpeedConvert = AIRSPEEDGAUGE //NOT SURE I CAN SIMPLY READ THIS FROM DCS-BIOS???
 if (AirSpeedConvert > 41000) (AirSpeedConvert = 41000)
 AIRSPEED = AirSpeedConvert/41
 
 //if AIRSPEED is same as actuator position, switch motor off
 if (AIRSPEED = actuatorPosNow)
  {
     motorOff();
   }

 //if AIRSPEED is higher, start forward motion
 if (AIRSPEED > actuatorPosNow)   
   {
     digitalWrite(inApin, HIGH); //Direction Forward
     analogWrite(pwmpin, pwmSpeed);  
   }
 
 //if AIRSPEED is lower, start Reverse motion
 if(AIRSPEED < actuatorPosNow)
   {
     digitalWrite(inBpin, HIGH); //Direction Reverse
     analogWrite(pwmpin, pwmSpeed);
   }
}
DcsBios::IntegerBuffer airspeedgaugeBuffer(0x5436, 0xffff, 0, onAirspeedgaugeChange);

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

void motorOff()
{
   digitalWrite(inApin, LOW);
   digitalWrite(inBpin, LOW);
   analogWrite(pwmpin, 0);
}

Link to comment
Share on other sites

 //Determine what the current position SHOULD be based on airspeed:
 AirSpeedConvert = AIRSPEEDGAUGE //NOT SURE I CAN SIMPLY READ THIS FROM DCS-BIOS???

 

The callback function receives the value as a function parameter (named newValue in the code snippets). Try the following:

 

AirSpeedConvert = newValue;

Link to comment
Share on other sites

  • 5 months later...

Solution!

 

Ian, many thanks for pointing me in the right direction. I have used advice from you, others on the forum and external tutorials and guidance. After much experimentation, headscratching, research and long hours I finally managed to come up with the right combination of elements in the code. The actuator now works beautifully, pulling the bungee cords tight to tension the action of the controls. It uses the airspeed reading to move the full range.

 

My publishing it the sketch here is a small token of appreciation for all the help received. Hopefully it will help others too.

 

This is the full sketch::music_whistling:

 

/*
 Tell DCS-BIOS to use a serial connection and use interrupt-driven
 communication. The main program will be interrupted to prioritize
 processing incoming data.
 
 This should work on any Arduino that has an ATMega328 controller
 (Uno, Pro Mini, many others).
*/
#define DCSBIOS_IRQ_SERIAL

#include "DcsBios.h"
DcsBios::PotentiometerEWMA<5, 128, 5> trimWheel("TRIM_WHEEL", A1);
DcsBios::PotentiometerEWMA<5, 128, 5> rtrimWheel("RTRIM_WHEEL", A2);
DcsBios::Switch2Pos fuelPump("FUEL_PUMP", 2);
DcsBios::Switch2Pos diluter("DILUTER", 3);
DcsBios::Switch2Pos diluterCover("DILUTER_COVER", 4, true);
DcsBios::Switch2Pos radTest("RAD_TEST", 5);
DcsBios::Switch2Pos radTestCover("RAD_TEST_COVER", 6, true);
DcsBios::Switch2Pos msTest("MS_TEST", 7);
DcsBios::Switch2Pos msTestCover("MS_TEST_COVER", 33, true);
DcsBios::Switch2Pos hatch("HATCH", 34);
DcsBios::Switch2Pos sideDoor("SIDE_DOOR", 35);
DcsBios::Switch2Pos pitot("PITOT", 36);
DcsBios::Switch2Pos radiator("RADIATOR", 37);
DcsBios::Switch2Pos wobblePump("WOBBLE_PUMP", 38);
DcsBios::RotaryEncoder uc("UC", "DEC", "INC", 14, 15);
DcsBios::Switch3Pos morseDnMode("MORSE_DN_MODE", 17, 16);
DcsBios::Switch2Pos morseKey("MORSE_KEY", 18);
DcsBios::RotaryEncoder morseUpMode("MORSE_UP_MODE", "DEC", "INC", 19, 20);
DcsBios::Switch2Pos iffB("IFF_B", 21);
DcsBios::Switch2Pos iffD("IFF_D", 22);
DcsBios::Switch2Pos iffCover("IFF_COVER", 23);
DcsBios::Switch2Pos iff0("IFF_0", 24);
DcsBios::Switch2Pos iff1("IFF_1", 25);
DcsBios::Switch2Pos droptankCock("DROPTANK_COCK", 26);
DcsBios::Switch2Pos droptankJett("DROPTANK_JETT", 27);
DcsBios::Switch2Pos ucEmer("UC_EMER", 28);
DcsBios::Switch2Pos deicer("DEICER", 29);

/*  
Arduino Airspeed Force Feedback Control Rev6

 code by: Roel Stausebach, building on work by various before me 
 The code addresses the following elements:
   MiniMonsterMoto control (VNH2SP30 Monster Motor Module, single channel)
   Indicated airspeed from Airspeedgauge value of DCS-BIOS
   Actuator Potentiometer - to provide current position. 

 1. OPERATION - READ THE "AIRSPEEDGAUGE" value, ADJUST THE ACTUATOR TO THAT POSITION
                The range can be between 0 and 65535. For the Spitfire the full gauge reading is 480mph
                We want full effect of the Force Feedback to be at 480mph
                Arduino has an analogRead range from 0 to 1023, and the actuator potentiometer is
                usually less than this. (see below, in our case it is 132 to 1020)We assign 
                the AirSpeedConvert value to this.
                In the Prototype the potentiometer range runs from 132 retracted to 1020 extended. 
                This was established using the Actuator_Range_Test sketch and is something that 
                will need to be done for each different actuator.
                To calculate the values to be used:
                   full range of movement: 1020-132=888
                   AirSpeedConvert runs from 0 to 65535=480mph
                   So a speed range of 65535 is represented by 888 on the potentiometer range
                   Thus a factor of 65535/888=73.8+132 to convert to AIRSPEED (the 132 is added
                   as it is the miniumum reading on the actuator potentiometer)
                   Lets say airspeed is 280mph then:
                   AirSpeedConvert=280/480x65535=38228.75 and AIRSPEED=(38228.75/73.8)+132=650
                   and lets assume the current actuator position actuatorPosNow reads 381,
                   so now we need to move the actuator to a position where the readout is 650     
 */
// Variables that remain the same:
const int CS_THRESHOLD = 0.65;  //this is the overload value for 5A at approx 0.13V per amp of output current
const int inApin = 39;  // INA: Clockwise input
const int inBpin =40; // INB: Counter-clockwise input
const int pwmpin = 8; // PWM OUTput
const int actuatorPin = A3;   // the pin that the actuator potentiometer is attached to
const int cspin = A9; // Connect to the CS (current sense) pin on the MiniMonsterMoto

// Generally, you should use "unsigned long" for variables that hold time
// The value will quickly become too large for an int to store
unsigned long previousMillis = 0;        // will store last time LED was updated
unsigned int AirSpeedConvert;

// constants won't change:
const long interval = 100;           // interval at which to move the actuator (milliseconds)

void setup() {
 DcsBios::setup();
 pinMode(inApin, OUTPUT);
 pinMode(inBpin, OUTPUT);
 pinMode(pwmpin, OUTPUT);
 pinMode(cspin, INPUT);
 pinMode(actuatorPin, INPUT);
}

void onAirspeedgaugeChange(unsigned int newValue) {
  AirSpeedConvert = newValue; //Read the airspeed value
}

void AirspeedChange() {
    
  int actuatorPosNow = analogRead(actuatorPin);// read the Actuator potentiometer (this will be a value between 132 and 1020 see above) 
  int AIRSPEED = static_cast<int>((AirSpeedConvert/46.17)+132); //see explanation for this factor above, will change for different actuators
   
   if (AIRSPEED > actuatorPosNow+10){   //if AIRSPEED is higher, start forward motion
     digitalWrite(inApin, HIGH); //Direction Forward
     analogWrite(pwmpin, 255); //set PWM speed to max.
     delay(200);
     motorOff();
      }
     
   else if (AIRSPEED < actuatorPosNow-10) { //if AIRSPEED is lower, start Reverse motion
     digitalWrite(inBpin, HIGH); //Direction Reverse
     analogWrite(pwmpin, 255); //set PWM speed to max.
     delay(200);
     motorOff();
     }
     
    else {  //if AIRSPEED is same as actuator position, switch motor off
      motorOff();
     }
     
     if((analogRead(cspin)>CS_THRESHOLD));
     {
       motorOff();
     }     
     
}
DcsBios::IntegerBuffer airspeedgaugeBuffer(0x5436, 0xffff, 0, onAirspeedgaugeChange);

void motorOff()
{
   digitalWrite(inApin, LOW);
   digitalWrite(inBpin, LOW);
   analogWrite(pwmpin, 0);
}

void loop() {
 DcsBios::loop();
 // we will check the satus of airspeed every second
 unsigned long currentMillis = millis();
   if (currentMillis - previousMillis >= interval) { //check if more than 1/10th second has passed
     previousMillis = currentMillis; // if yes, save the last time you moved the actuator
     AirspeedChange(); // and run the Airspeedchange function
   } // else nothing, just carry on..
}

Link to comment
Share on other sites

  • 3 weeks later...

Thanks for sharing that Shredder. I believe this thread might be just what I have been looking for to help me create some kind of haptic feedback device for DCS. I have been been looking everywhere for some way to read info about FF(vibration only) or induce that vibration with lua but I now realise I missed a trick there. Instead I can get data from the aircraft's current state (thanks to [FSF]Ian) and with your code as a guide drive whatever H/W I decide to use. I am new to all this but excited at all the possibilities!


Edited by Stevo_HR
improved grammar slightly :lol:
Link to comment
Share on other sites

Thanks for sharing that Shredder. I believe this thread might be just what I have been looking for to help me create some kind of haptic feedback device for DCS. I have been been looking everywhere for some way to read info about FF(vibration only) or induce that vibration with lua but I now realise I missed a trick there. Instead I can get data from the aircraft's current state (thanks to [FSF]Ian) and with your code as a guide drive whatever H/W I decide to use. I am new to all this but excited at all the possibilities!

 

Indeed Stevo! You are most welcome. :)

 

More on the force feedback system I have implemented here: Heritage Flight Simulation Spitfire Mk.IX


Edited by shreddersa
Added info
Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...