(simulator) DCS > C# > Arduino (motor)

Hello,

I’m working on a project which takes data from DCS world ( a flight simulator ) and sends it to an Arduino UNO via a program written in C#.

The way it does this is via a plugin for the simulator called export.lua.
I’ve just used an example provided by eagle dynamics (dev of DCS) .
I have no experience in LUA but just used the example and adjusted it to my needs


MainPanel = GetDevice(0)

function LuaExportStart()

	package.path  = package.path..";"..lfs.currentdir().."/LuaSocket/?.lua"
	package.cpath = package.cpath..";"..lfs.currentdir().."/LuaSocket/?.dll"
	
	socket = require("socket")
	IPAddress = "127.0.0.1"
	Port = 31090

	MySocket = socket.try(socket.connect(IPAddress, Port))
	MySocket:setoption("tcp-nodelay",true) 
	
	
	
	

end

function LuaExportBeforeNextFrame()
end

function LuaExportAfterNextFrame()

--getting all the variables:
	
	local pitch, bank, yaw = LoGetADIPitchBankYaw() -- Pitch, bank & yaw in radians
	local AccelX = LoGetAccelerationUnits().x   -- X-acceleration (G)
	local AccelY = LoGetAccelerationUnits().y	-- Y-acceleration (G)
	local AccelZ  = LoGetAccelerationUnits().z	-- Z-acceleration (G)
	
	local PitchAngularVelocity= LoGetAngularVelocity().x 	-- Rotation speed around x (pitch in rad/s)
	local RollAngularVelocity = LoGetAngularVelocity().y 	-- Rotation speed around y (roll in rad/s)
															-- Rotation speed around z (yaw in rad/s)
						
	local PitchAngularAcceleration = LoGetHelicopterFMData().angular_acceleration.x	 	-- Pitch angular acceleration (rad/s2)
	local RollAngularAcceleration = LoGetHelicopterFMData().angular_acceleration.y 		-- Roll angular acceleration (rad/s2)
																						-- Yaw angular acceleration (rad/s2)
	local AirborneState = LoGetAltitudeAboveGroundLevel()
	
	
		
		
--Sending it in the string format required for arduino
	
	socket.try(MySocket:send(string.format("<Data: \n Game:3 \n Pitch: %.2f \n PitchAngularAcceleration:%.2f \n Bank:%.2f \n RollAngularAcceleration: %.2f \n AccelX: %.2f \n AccelY: %.2f \n AccelZ:%.2f \n Xseat: %.2f \n Yseat:%.2f \n Simstate:%.2f \n Airbornestate:%.2f \n Crashstate:%.2f \n,> \n", 
	pitch * 57.2958, 
	PitchAngularAcceleration,
	bank,
	RollAngularAcceleration, 			
	AccelX,  			
	AccelY, 
	AccelZ,
	Xseat,
	YSeat,
	Simstate,
	AirborneState,
	Crashstate
	
	)))
	
end

function LuaExportStop()

	if MySocket then 
		socket.try(MySocket:send("exit"))
		MySocket:close()
	end
end

function LuaExportActivityNextEvent(t)
end


The way the string is formatted is important for the arduino part:

<Data, Game, Pitch, PitchAngularAcceleration, Roll, RollAngularAcceleration, AccelX, AccelY, AccelZ, AccelXSeat,AccelYSeat, SimState, AirborneState, crashstate, >

The code used in the C# program:

public MainWindow()
        {
            InitializeComponent();

         SendTimer.Interval = TimeSpan.FromMilliseconds(10);            // Timer to send data at specified interval (ms)

            Ports = SerialPort.GetPortNames();                             // storing available ports
            foreach (var port in Ports)
            {
                PortSelection.Items.Add(port);                            // adding available ports to dropdown box
            }

            if (PortSelection.SelectedValue == null)                // Setting the first item in the portselection
            {                                                       // combobox to prevent NULL exception.
                PortSelection.SelectedIndex = 0;
            }

            ComBridge.BaudRate = 115200;

            listener.Start();                                                           // Start listener for DCS connection

            }

 private void DCS_Tick(object sender, EventArgs e)
        {
            DCSStream = reader.ReadLine();
            DCSDataBox.Text = DCSStream;
            ComBridge.Write(DCSStream);

        }   


I'm adding following method to the timer tick via a button click:

SendTimer.Tick += DCS_Tick;

The code used in the Arduino program:

//experimental
#include <AccelStepper.h>


#define FULLSTEP 4
#define HALFSTEP 8

//Seat motor
const int MotorPin1 = 2;
const int MotorPin2 = 3;
const int MotorPin3 = 4;
const int MotorPin4 = 5;



AccelStepper Motor (HALFSTEP, MotorPin1, MotorPin3, MotorPin2, MotorPin4);


const byte numChars = 250;
char receivedChars[numChars];
char tempChars[numChars];        // temporary array for use when parsing

// variables to hold the parsed data

char messageFromPC[numChars] = {0};
float Game = 0.0;
float Pitch = 0.0;
float PitchAngularAcceleration = 0.0;
float Roll = 0.0;
float RollAngularAcceleration = 0.0;
float AccelX = 0.0;
float AccelY = 0.0;
float AccelZ = 0.0;
float SimState = 0.0;
float AirborneState = 0.0;
float AccelXConstant = 0.0;
float AccelYConstant = 0.0;
float CrashState = 0.0;

unsigned long currentMill = 0;
unsigned long previousMillis = 0;

const long interval = 100;

float positiveDifference = 544;  //90 * 11 * 0,6      
float negativeDifference = -544;

boolean newData = false;


//=============================================================================================================================================================================================================================
//=============================================================================================================================================================================================================================

void setup() {

  Serial.begin(115200);
}

//========================================================================================================================================================================================================================================================================
//========================================================================================================================================================================================================================================================================
void loop() {

Motor.setAcceleration(2000);
Motor.setMaxSpeed(1000);
Motor.moveTo(-Pitch);
Motor.run();




  //================================================================================================================================================================================================================================================================================================================

  //================================== storing the values of the sim ================================
  //=================================================================================================
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    parseData();
    newData = false;
  }
}



//======================= method for storing the serial data =============================================



void recvWithStartEndMarkers() {          //part where we tell te program that '<' is the start of the data and '>' the end.
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}

//============

void parseData() {      // split the data into its parts

  char * strtokIndx; // this is used by strtok() as an index

  strtokIndx = strtok(tempChars, ",");     // get the first part - the string
  strcpy(messageFromPC, strtokIndx);       // copy it to messageFromPC

  strtokIndx = strtok(NULL, ",");
  Game = atof(strtokIndx);                 // convert the Game value to a float    (1 = Xplane 11 , 2 = Assetto Corsa (Competizione)

  strtokIndx = strtok(NULL, ",");
  Pitch = atof(strtokIndx);                 // convert the pitch value to a float

  strtokIndx = strtok(NULL, ",");
  PitchAngularAcceleration = atof(strtokIndx);         // convert the PitchAngularAcceleration value to a float

  strtokIndx = strtok(NULL, ",");
  Roll = atof(strtokIndx);                    // convert the Roll value to a float

  strtokIndx = strtok(NULL, ",");
  RollAngularAcceleration = atof(strtokIndx);    // convert the RollAngularAcceleration value to a float

  strtokIndx = strtok(NULL, ",");
  AccelX = atof(strtokIndx);                // convert the accelX value to a float

  strtokIndx = strtok(NULL, ",");
  AccelY = atof(strtokIndx);                // convert the accelY value to a float

  strtokIndx = strtok(NULL, ",");
  AccelZ = atof(strtokIndx);                // convert the accelZ value to a float

  strtokIndx = strtok(NULL, ",");
  AccelXConstant = atof(strtokIndx);         // convert the AccelXConstant value to a float

  strtokIndx = strtok(NULL, ",");
  AccelYConstant = atof(strtokIndx);         // convert the AccelYConstant value to a float

  strtokIndx = strtok(NULL, ",");
  SimState = atof(strtokIndx);              // convert the simstate value to a float

  strtokIndx = strtok(NULL, ",");
  AirborneState = atof(strtokIndx);         // convert the airbornestate value to a float

  strtokIndx = strtok(NULL, ",");
  CrashState = atof(strtokIndx);        // convert the CrashState value to a float




}


//============================================================================================





//==================================================================================================================================================================

I’m getting the data out of DCS, but there seems to be a significant delay for the motor to move. (couple of seconds.)

I’m using a single motor as output just to test the data.

When I’m using a console application to check the data, so not sending it to the Arduino, there seems to be no delay so my guess would be that the serial communication is causing this delay.

Could anyone provide me with an insight as to why this delay might be happening and how to possibly solve it?

Does the TCP communication have anything to do with it?
Would communicating via UDP solve it?

Rgds

Does the C# program open the serial port each time before data is sent ?

1 Like

No, the serial port is opened once. Once the program is shut, it's closed again.

Try testing it from the serial monitor and just paste example data and send it.

It would be handy if you had an Arduino with more than one serial port so that you can do some debugging prints.

testing it from a serial monitor:
do you mean getting some data, copying it and then manually sending it from the c# program to arduino?

EDIT: I think I know what you mean, taking the data sent from DCS in the format and then use the serial monitor to send the data.
So let's say there's a delay there, what would be the issue?
Or if there's no delay, what would be the issue then?

I'm using the c# program in other sims as well and they work fine.

I'm not sure what you mean with an arduino with more than one serial port, do you mean to test other ports?

Debugging Arduino programs, you don't have many options. Serial.print is usually your primary tool but you're already using the only port to talk to the C# program on the PC.

You could otherwise print out some text to tell you where execution has got to and when (using millis). Of course, you could have your C# code accept that output and render it for you in a text box.

If you're getting a substantial delay each time, I suggest you revisit whether the serial port is being reopened because it will restart the Arduino. Again, the ability to serial.print something in setup would tell you that.

Ok I see what you mean.
I used an LCD for this in the past but as my project was increasing in pin utilization, I can't use the LCD anymore.

I've got a textbox in my C# program which shows the datastream it is sending and this doesn't show any delay.
EDIT: not correct, see my reply #8

But the strange thing is that other simulators don't have the issue of having any delay, so not quite sure where the problem lies.

Actually thank you for mentioning the debug.
I had the datastream in my C# program but always (wrongfully) assumed it was realtime.
Just did a flight and it showed delay so it's it's an issue before 'entering' the c# part.

I think I've used DCS but I may be mixing it up with another sim. I recall though that there were config options to tell it how often each parameter was to be put on the wire.

Hmm, so I think I've narrowed it down.

I have a separate console application with the sole purpose of printing out the received datastream from DCS which doesn't show any delay.

So the way I implemented the datastream in my main C# program is the issue, as confirmed again by the delayed stream.

So not the issue with the export.lua nor is it an issue with the arduino program.

I'll have to figure out what's wrong with my C# program which causes the delay.

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