This is my first attempt to build motion simulator.
I'm working on it for several months now and get to the point that I have a nice prototype.
I will try to share it with you and hope to get help and new ideas for my simulator.
My platform based on the grate "Floriske's 2DOF Playseat DC motor Project" but influence from many other grate motion sims here in X sim.
The base of the platform is an old armchair.
I disconnected the chair from the base, solder car U joint to the base and reconnect the chair.
I connected the chair base a wood plate.
For motors I got 2 wiper motors.
The power supply was 12V computer power supply.
I connected 2 pots like Floriske did in his sim.
I had several problems with this setup:
1. The wood plate wasn't strong enough.
2. The motors were strong enough only for my children but not for me (I'm a big guy 110kg…)
3. I didn't like the chair itself…
4. The pots feedback is nonlinear.
So I change the setup totally.
First I went to a junkyard and got myself stronger 24V gear motors…
Then I got from a friend an old car seat (Toyota corolla).
I rebuilt everything on much more massive wood base.
For power supply I got 2 24V 15A.
http://www.ebay.com/itm/190841780932?va ... 1497.l2649
My H bridge is 2 Double BTS7960B 43A H-bridge Motor Driver module smart car driver For Arduino.
http://www.ebay.com/itm/151066039543?ss ... 1497.l2649
The controller is an Arduino uno 3.
Unfortunately the H bridge I'm using is different from what used for Arduino Open Source X-PID firmware incl.360°option. Instead of 2 direction and one PWM I have to connect 2 PWM one for each direction. I wasn't able to change the XPID code for my needs so I rewrote a code I found here
http://www.*/forum/2dof-ar ... t3469.html
(I will be very happy if someone can help me change the XPID code for my H bridge)
- Code: Select all
/*
* -------------------------------------
* Thanks to herctrap and his source
* http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1280139142
*
* and also thanks to juiou and his source
* http://www.*.de/forum/arduino-and-x-sim-t2773-30.html
*
*and thanks to jamesdio and his source
*http://www.*.de/forum/2dof-arduino-miniature-motion-platform-w-2-servos-t3469.html
* I just combined their sources.
*
*/
#include <PID_v1.h>
#include <LiquidCrystal.h>
double SetpointL, InputL, OutputL,InputLT;
double SetpointR, InputR, OutputR,InputRT;
PID myPID1(&InputL, &OutputL, &SetpointL,1,0,0, DIRECT); //L motor
PID myPID2(&InputR, &OutputR, &SetpointR,1,0,0, DIRECT);//R motor
int Degree;
int Gforce = 0;
int Gforce1 = 0;
int Gforce10 = 0;
int Gforce100 = 0;
int LFpwmPin=6;
int LRpwmPin=9;
int RFpwmPin=10;
int RRpwmPin=11;
int LenPin=7;
int RenPin=8;
int LinputMax=640-5;
int LinputMin=442+15;
int RinputMax=670-5;
int RinputMin=392+15;
char kind_of_data;
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(13, 12, 5, 4, 3, 2);
void setup() {
pinMode(RenPin,OUTPUT);
pinMode(LenPin,OUTPUT);
// set up the LCD's number of columns and rows:
lcd.begin(20, 4);
lcd.print("Start!");
delay(1000);
lcd.clear();
//initialize the variables we're linked to
InputLT = analogRead(0); // read from Lpotentiometer
InputL=map(InputLT,LinputMin, LinputMax,0,180); // map that value in degrees
SetpointL = 90;
//turn the PID on
myPID1.SetMode(AUTOMATIC);
myPID1.SetOutputLimits(-255, 255);
InputRT = analogRead(1);
InputR=map(InputRT,RinputMin, RinputMax, 0, 180);
SetpointR =90;
//turn the PID on
myPID2.SetMode(AUTOMATIC);
myPID2.SetOutputLimits(-255, 255);
//open serial connection
Serial.begin(115200);
}
void loop() {
// wait for serial input (min 3 bytes in buffer)
while(Serial.available() > 0) {
kind_of_data = Serial.read(); //string from USO --> L~a01~ R~a02~ 8bit resolution\decimal output
if(kind_of_data == 'L'|| kind_of_data == 'R')
{
Read_Pos(kind_of_data);
}
}
InputLT = analogRead(0);
InputL=map(InputLT, LinputMin, LinputMax, 0, 180);
InputRT = analogRead(1);
InputR=map(InputRT,RinputMin, RinputMax, 0, 180);
myPID1.Compute();
myPID2.Compute();
//PWM
lcd.setCursor(10,0);
lcd.print(OutputL);
lcd.setCursor(10,1);
lcd.print(OutputR);
//setpoints
lcd.setCursor(0,0);
lcd.print(SetpointL);
lcd.setCursor(0,1);
lcd.print(SetpointR);
lcd.setCursor(0,2);
lcd.print(InputLT);
lcd.setCursor(0,3);
lcd.print(InputRT);
lcd.setCursor(10,2);
lcd.print(InputL);
lcd.setCursor(10,3);
lcd.print(InputR);
digitalWrite(LenPin,HIGH); //for - direction
digitalWrite(RenPin,HIGH); //for - direction
if(OutputL<0){
// digitalWrite(1,LOW); //for - direction
analogWrite(6,abs(OutputL)); // PWM signal to H-bridge1 for rotatin impulses
analogWrite(9,0); // PWM signal to H-bridge1 for rotatin impulses
}
else{
// digitalWrite(1,HIGH);//for +direction
analogWrite(9,abs(OutputL)); // PWM signal to H-bridge1 for rotatin impulses
analogWrite(6,0); // PWM signal to H-bridge1 for rotatin impulses
}
if(OutputR<0){
// digitalWrite(2,HIGH);
analogWrite(10,abs(OutputR)); // PWM signal to H-bridge1 for rotatin impulses
analogWrite(11,0); // PWM signal to H-bridge1 for rotatin impulses
}
else{
// digitalWrite(2,LOW);
analogWrite(11,abs(OutputR)); // PWM signal to H-bridge1 for rotatin impulses
analogWrite(10,0); // PWM signal to H-bridge1 for rotatin impulses
}
// The PWM outputs generated on pins 5 and 6 will have higher-than-expected duty cycles.
// This is because of interactions with the millis() and delay() functions,
//which share the same internal timer used to generate those PWM outputs.
// This will be noticed mostly on low duty-cycle settings (e.g 0 - 10)
//and may result in a value of 0 not fully turning off the output on pins 5 and 6.
}
void Read_Pos(char which_servo){
delay(2);
int Gforce100 = Serial.read()- '0';
delay(2);
int Gforce10 = Serial.read()- '0';
delay(2);
int Gforce1 = Serial.read()- '0';
//Verifica se e centena dezena ou decimal... =]
if (Gforce1 < 0) { Gforce = Gforce10 + 10*Gforce100; }
if (Gforce1 < 0 && Gforce10 < 0) { Gforce = Gforce100; }
if (Gforce100 < 0) { Gforce = 127; }
if (Gforce1 >=0 && Gforce10 >= 0 && Gforce100 >= 0) { Gforce = 100 * Gforce100 + 10 * Gforce10 + Gforce1; }
if (which_servo == 'L') {
Degree = map(Gforce, 0, 255, 0, 180);
SetpointL = Degree; //new setpoint
}
if (which_servo == 'R') {
Degree = map(Gforce, 0, 255, 0, 180);
SetpointR = Degree;
}
}
The next step is to connect Falcon 4 bms (combat flight sim) to the platform.
As I understand from reading in the forums there is no plugin available at this point.
I think I will need stage 2 key in order to be able to extract data from sim.
I have file with the shard memory address. I'm not shore if this is what I need.
- Code: Select all
#ifndef _FLIGHT_DATA_H
#define _FLIGHT_DATA_H
class FlightData
{
public:
enum LightBits
{
MasterCaution = 0x1, // Left eyebrow
// Brow Lights
TF = 0x2, // Left eyebrow
OXY_BROW = 0x4, // repurposed for eyebrow OXY LOW (was OBS, unused)
EQUIP_HOT = 0x8, // Caution light; repurposed for cooling fault (was: not used)
WOW = 0x10, // True if weight is on wheels: this is not a lamp bit!
ENG_FIRE = 0x20, // Right eyebrow; upper half of split face lamp
CONFIG = 0x40, // Stores config, caution panel
HYD = 0x80, // Right eyebrow; see also OIL (this lamp is not split face)
Flcs_ABCD = 0x100, // TEST panel FLCS channel lamps; repurposed, was OIL (see HYD; that lamp is not split face)
FLCS = 0x200, // Right eyebrow; was called DUAL which matches block 25, 30/32 and older 40/42
CAN = 0x400, // Right eyebrow
T_L_CFG = 0x800, // Right eyebrow
// AOA Indexers
AOAAbove = 0x1000,
AOAOn = 0x2000,
AOABelow = 0x4000,
// Refuel/NWS
RefuelRDY = 0x8000,
RefuelAR = 0x10000,
RefuelDSC = 0x20000,
// Caution Lights
FltControlSys = 0x40000,
LEFlaps = 0x80000,
EngineFault = 0x100000,
Overheat = 0x200000,
FuelLow = 0x400000,
Avionics = 0x800000,
RadarAlt = 0x1000000,
IFF = 0x2000000,
ECM = 0x4000000,
Hook = 0x8000000,
NWSFail = 0x10000000,
CabinPress = 0x20000000,
AutoPilotOn = 0x40000000, // TRUE if is AP on. NB: This is not a lamp bit!
TFR_STBY = 0x80000000, // MISC panel; lower half of split face TFR lamp
// Used with the MAL/IND light code to light up "everything"
// please update this if you add/change bits!
AllLampBitsOn = 0xBFFFFFEF
};
enum LightBits2
{
// Threat Warning Prime
HandOff = 0x1,
Launch = 0x2,
PriMode = 0x4,
Naval = 0x8,
Unk = 0x10,
TgtSep = 0x20,
// EWS
Go = 0x40, // On and operating normally
NoGo = 0x80, // On but malfunction present
Degr = 0x100, // Status message: AUTO DEGR
Rdy = 0x200, // Status message: DISPENSE RDY
ChaffLo = 0x400, // Bingo chaff quantity reached
FlareLo = 0x800, // Bingo flare quantity reached
// Aux Threat Warning
AuxSrch = 0x1000,
AuxAct = 0x2000,
AuxLow = 0x4000,
AuxPwr = 0x8000,
// ECM
EcmPwr = 0x10000,
EcmFail = 0x20000,
// Caution Lights
FwdFuelLow = 0x40000,
AftFuelLow = 0x80000,
EPUOn = 0x100000, // EPU panel; run light
JFSOn = 0x200000, // Eng Jet Start panel; run light
// Caution panel
SEC = 0x400000,
OXY_LOW = 0x800000,
PROBEHEAT = 0x1000000,
SEAT_ARM = 0x2000000,
BUC = 0x4000000,
FUEL_OIL_HOT = 0x8000000,
ANTI_SKID = 0x10000000,
TFR_ENGAGED = 0x20000000, // MISC panel; upper half of split face TFR lamp
GEARHANDLE = 0x40000000, // Lamp in gear handle lights on fault or gear in motion
ENGINE = 0x80000000, // Lower half of right eyebrow ENG FIRE/ENGINE lamp
// Used with the MAL/IND light code to light up "everything"
// please update this if you add/change bits!
//AllLampBits2On = 0xFFFFFEFF
AllLampBits2On = 0xFFFFF03F //ATARIBABY EWS CMDS bits excluded from test lamps
};
enum LightBits3
{
// Elec panel
FlcsPmg = 0x1,
MainGen = 0x2,
StbyGen = 0x4,
EpuGen = 0x8,
EpuPmg = 0x10,
ToFlcs = 0x20,
FlcsRly = 0x40,
BatFail = 0x80,
// EPU panel
Hydrazine = 0x100,
Air = 0x200,
// Caution panel
Elec_Fault = 0x400,
Lef_Fault = 0x800,
OnGround = 0x1000, // weight-on-wheels
FlcsBitRun = 0x2000, // FLT CONTROL panel RUN light (used to be Multi-engine fire light)
FlcsBitFail = 0x4000, // FLT CONTROL panel FAIL light (used to be Lock light Cue; non-F-16)
DbuWarn = 0x8000, // Right eyebrow DBU ON cell; was Shoot light cue; non-F16
NoseGearDown = 0x10000, // Landing gear panel; on means down and locked
LeftGearDown = 0x20000, // Landing gear panel; on means down and locked
RightGearDown = 0x40000, // Landing gear panel; on means down and locked
ParkBrakeOn = 0x100000, // Parking brake engaged; NOTE: not a lamp bit
Power_Off = 0x200000, // Set if there is no electrical power. NB: not a lamp bit
// Caution panel
cadc = 0x400000,
// Left Aux console
SpeedBrake = 0x800000, // True if speed brake is in anything other than stowed position
// Used with the MAL/IND light code to light up "everything"
// please update this if you add/change bits!
AllLampBits3On = 0x0047EFFF
};
enum HsiBits
{
ToTrue = 0x01, // HSI_FLAG_TO_TRUE == 1, TO
IlsWarning = 0x02, // HSI_FLAG_ILS_WARN
CourseWarning = 0x04, // HSI_FLAG_CRS_WARN
Init = 0x08, // HSI_FLAG_INIT
TotalFlags = 0x10, // HSI_FLAG_TOTAL_FLAGS; never set
ADI_OFF = 0x20, // ADI OFF Flag
ADI_AUX = 0x40, // ADI AUX Flag
ADI_GS = 0x80, // ADI GS FLAG
ADI_LOC = 0x100, // ADI LOC FLAG
HSI_OFF = 0x200, // HSI OFF Flag
BUP_ADI_OFF = 0x400, // Backup ADI Off Flag
VVI = 0x800, // VVI OFF Flag
AOA = 0x1000, // AOA OFF Flag
AVTR = 0x2000, // AVTR Light
OuterMarker = 0x4000, // MARKER beacon light for outer marker
MiddleMarker = 0x8000, // MARKER beacon light for middle marker
FromTrue = 0x10000, // HSI_FLAG_TO_TRUE == 2, FROM
Flying = 0x80000000, // true if player is attached to an aircraft (i.e. not in UI state). NOTE: Not a lamp bit
// Used with the MAL/IND light code to light up "everything"
// please update this is you add/change bits!
AllLampHsiBitsOn = 0xE000
};
// These are outputs from the sim
// Note: some two-engine values removed in this version for compatibility
// reasons.
float x; // Ownship North (Ft)
float y; // Ownship East (Ft)
float z; // Ownship Down (Ft)
float xDot; // Ownship North Rate (ft/sec)
float yDot; // Ownship East Rate (ft/sec)
float zDot; // Ownship Down Rate (ft/sec)
float alpha; // Ownship AOA (Degrees)
float beta; // Ownship Beta (Degrees)
float gamma; // Ownship Gamma (Radians)
float pitch; // Ownship Pitch (Radians)
float roll; // Ownship Pitch (Radians)
float yaw; // Ownship Pitch (Radians)
float mach; // Ownship Mach number
float kias; // Ownship Indicated Airspeed (Knots)
float vt; // Ownship True Airspeed (Ft/Sec)
float gs; // Ownship Normal Gs
float windOffset; // Wind delta to FPM (Radians)
float nozzlePos; // Ownship engine nozzle percent open (0-100)
// float nozzlePos2; // Ownship engine nozzle2 percent open (0-100)
float internalFuel; // Ownship internal fuel (Lbs)
float externalFuel; // Ownship external fuel (Lbs)
float fuelFlow; // Ownship fuel flow (Lbs/Hour)
float rpm; // Ownship engine rpm (Percent 0-103)
// float rpm2; // Ownship engine rpm2 (Percent 0-103)
float ftit; // Ownship Forward Turbine Inlet Temp (Degrees C)
// float ftit2; // Ownship Forward Turbine Inlet Temp2 (Degrees C)
float gearPos; // Ownship Gear position 0 = up, 1 = down;
float speedBrake; // Ownship speed brake position 0 = closed, 1 = 60 Degrees open
float epuFuel; // Ownship EPU fuel (Percent 0-100)
float oilPressure; // Ownship Oil Pressure (Percent 0-100)
// float oilPressure2; // Ownship Oil Pressure2 (Percent 0-100)
int lightBits; // Cockpit Indicator Lights, one bit per bulb. See enum
// These are inputs. Use them carefully
// NB: these do not work when TrackIR device is enabled
// NB2: launch falcon with the '-head' command line parameter to activate !
float headPitch; // Head pitch offset from design eye (radians)
float headRoll; // Head roll offset from design eye (radians)
float headYaw; // Head yaw offset from design eye (radians)
// new lights
int lightBits2; // Cockpit Indicator Lights, one bit per bulb. See enum
int lightBits3; // Cockpit Indicator Lights, one bit per bulb. See enum
// chaff/flare
float ChaffCount; // Number of Chaff left
float FlareCount; // Number of Flare left
// landing gear
float NoseGearPos; // Position of the nose landinggear; caution: full down values defined in dat files
float LeftGearPos; // Position of the left landinggear; caution: full down values defined in dat files
float RightGearPos; // Position of the right landinggear; caution: full down values defined in dat files
// ADI values
float AdiIlsHorPos; // Position of horizontal ILS bar
float AdiIlsVerPos; // Position of vertical ILS bar
// HSI states
int courseState; // HSI_STA_CRS_STATE
int headingState; // HSI_STA_HDG_STATE
int totalStates; // HSI_STA_TOTAL_STATES; never set
// HSI values
float courseDeviation; // HSI_VAL_CRS_DEVIATION
float desiredCourse; // HSI_VAL_DESIRED_CRS
float distanceToBeacon; // HSI_VAL_DISTANCE_TO_BEACON
float bearingToBeacon; // HSI_VAL_BEARING_TO_BEACON
float currentHeading; // HSI_VAL_CURRENT_HEADING
float desiredHeading; // HSI_VAL_DESIRED_HEADING
float deviationLimit; // HSI_VAL_DEV_LIMIT
float halfDeviationLimit; // HSI_VAL_HALF_DEV_LIMIT
float localizerCourse; // HSI_VAL_LOCALIZER_CRS
float airbaseX; // HSI_VAL_AIRBASE_X
float airbaseY; // HSI_VAL_AIRBASE_Y
float totalValues; // HSI_VAL_TOTAL_VALUES; never set
float TrimPitch; // Value of trim in pitch axis, -0.5 to +0.5
float TrimRoll; // Value of trim in roll axis, -0.5 to +0.5
float TrimYaw; // Value of trim in yaw axis, -0.5 to +0.5
// HSI flags
int hsiBits; // HSI flags
//DED Lines
char DEDLines[5][26]; //25 usable chars
char Invert[5][26]; //25 usable chars
//PFL Lines
char PFLLines[5][26]; //25 usable chars
char PFLInvert[5][26]; //25 usable chars
//TacanChannel
int UFCTChan, AUXTChan;
// RWR
int RwrObjectCount;
int RWRsymbol[40];
float bearing[40];
unsigned long missileActivity[40];
unsigned long missileLaunch[40];
unsigned long selected[40];
float lethality[40];
unsigned long newDetection[40];
//fuel values
float fwd, aft, total;
void SetLightBit (int newBit) {lightBits |= newBit;};
void ClearLightBit (int newBit) {lightBits &= ~newBit;};
int IsSet (int newBit) {return ((lightBits & newBit) ? TRUE : FALSE);};
void SetLightBit2 (int newBit) {lightBits2 |= newBit;};
void ClearLightBit2 (int newBit) {lightBits2 &= ~newBit;};
int IsSet2 (int newBit) {return ((lightBits2 & newBit) ? TRUE : FALSE);};
void SetLightBit3 (int newBit) {lightBits3 |= newBit;};
void ClearLightBit3 (int newBit) {lightBits3 &= ~newBit;};
int IsSet3 (int newBit) {return ((lightBits3 & newBit) ? TRUE : FALSE);};
void SetHsiBit (int newBit) {hsiBits |= newBit;};
void ClearHsiBit (int newBit) {hsiBits &= ~newBit;};
int IsSetHsi (int newBit) {return ((hsiBits & newBit) ? TRUE : FALSE);};
int VersionNum; //Version of Mem area
// New values added here for header file compatibility but not implemented
// in this version of the code at present.
float headX; // Head X offset from design eye (feet)
float headY; // Head Y offset from design eye (feet)
float headZ; // Head Z offset from design eye (feet)
int MainPower;
};
// OSB capture for MFD button labeling
#define OSB_STRING_LENGTH 8 // currently strings appear to be max 7 printing chars
typedef struct {
char line1[OSB_STRING_LENGTH];
char line2[OSB_STRING_LENGTH];
bool inverted;
} OsbLabel;
class OSBData
{
public:
OsbLabel leftMFD[20];
OsbLabel rightMFD[20];
};
class FlightData2
{
public:
float nozzlePos2; // Ownship engine nozzle2 percent open (0-100)
float rpm2; // Ownship engine rpm2 (Percent 0-103)
float ftit2; // Ownship Forward Turbine Inlet Temp2 (Degrees C)
float oilPressure2; // Ownship Oil Pressure2 (Percent 0-100)
unsigned char navMode; // current mode selected for HSI/eHSI
float AAUZ; // Ownship barometric altitude given by AAU (depends on calibration)
enum TacanSources
{
UFC = 0,
AUX,
NUMBER_OF_SOURCES,
};
enum TacanBits
{
band = 0x01, // true in this bit position if band is X
mode = 0x02, // true in this bit position if domain is air to air
};
char tacanInfo[NUMBER_OF_SOURCES]; // Tacan band/mode settings for UFC and AUX COMM
// setters for internal use only
void SetUfcTacanToAA(bool t) { if (t) { tacanInfo[UFC] |= mode; } else { tacanInfo[UFC] &= ~mode; } }
void SetAuxTacanToAA(bool t) { if (t) { tacanInfo[AUX] |= mode; } else { tacanInfo[AUX] &= ~mode; } }
void SetUfcTacanToX(bool t) { if (t) { tacanInfo[UFC] |= band; } else { tacanInfo[UFC] &= ~band; } }
void SetAuxTacanToX(bool t) { if (t) { tacanInfo[AUX] |= band; } else { tacanInfo[AUX] &= ~band; } }
// getters for external reader programs
bool UfcTacanIsAA(void) {return ((tacanInfo[UFC] & mode) ? true : false); }
bool AuxTacanIsAA(void) {return ((tacanInfo[AUX] & mode) ? true : false); }
bool UfcTacanIsX(void) {return ((tacanInfo[UFC] & band) ? true : false); }
bool AuxTacanIsX(void) {return ((tacanInfo[AUX] & band) ? true : false); }
};
extern OSBData cockpitOSBData; // "FalconSharedOsbMemoryArea"
extern FlightData cockpitFlightData; // "FalconSharedMemoryArea"
extern FlightData2 cockpitFlightData2; // "FalconSharedMemoryArea2"
#endif
For now I'm just test it with race07 demo.
Any help with Falcon 4 connection or other idea and suggestions will be gladly accepted.
hraanan