Jump to content

[Hardware, Plugin] Arduino based physical display + serial port io+ tutorial (24-11-19)


zitronen

Recommended Posts

No. Lets go a bit into the theory behind this, understanding it will help you later.

Analog ports read a voltage, and convert 0-5V to values 0-1024. So in order to get our values, we need to supply a voltage we can control. The easy way to do this is with a variable resistor. It has three wires: is in either end of the resistor, and the last is connected so it divides up the resistance between one end and the other. For example, if you have a 10 000 Ohm  (10K) resistor, and you put the slider 75% of the way towards one end, the resistance between the third wire and that end and the middle wire is 2500 Ohm, while the resistance between the other end and the middle wire is 7500 Ohm.

So how does this help? Well, if you apply a fixed voltage between the end points of the resistor, the voltage will divide up in the same proportion as the resistor is divided. To continue the example above, you connect one end of the resistor to your ground port, and the other end of the resistor to +5V. Then the voltage read by the middle (analog) port will read the same ratio (25%) of 5 volt as the ratio of resistance.

So to sum up, you connect one wire to +5V, the middle to an analog port, and the last to ground. In most literature on the Arduino, the analog ports are named A0-A15, and ground is GND. On the board, you will find several ground ports. The exact layout is shown on this schematic, download it and keep it handy.

I hope the above helps.

Edited by Freshmeat
long story
Link to comment
Share on other sites

Thanks for explain, I already knew that, but the deepening is great (I have also all pinout of my arduino in pdf), but I was talking about button like the action groups, RCS, SAS : what pin is who

After this I will try the connect my 20*04 display ;)

Link to comment
Share on other sites

 

Just as a tip, what I found helped was doing a handful of the Tutorials on the Arduino website. Get comfortable with the code, and how physically connecting things works. Then when you jump into someone elses code, you can decipher it much easier! :) 

11 hours ago, Tobit said:

Thanks for explain, I already knew that, but the deepening is great (I have also all pinout of my arduino in pdf), but I was talking about button like the action groups, RCS, SAS : what pin is who

After this I will try the connect my 20*04 display ;)

At the top of "KSPIODemo16' you have:

//pins for LEDs
#define GLED 5
#define YLED 6
#define RLED 7
#define SASLED 11
#define RCSLED 12
#define CG1LED 13

//pins for input
#define SASPIN 8
#define RCSPIN 9
#define CG1PIN 10
#define THROTTLEPIN 0

This is the nicest and easiest way to 'store' the pin numbers as a text variable, so you can reference them more easily in the code.

By default, the switch to turn on SAS plugs into pin 8 [#define SASPIN 8]; The switch to turn on RCS plugs into pin 9 [#define RCSPIN 9]

Then jump over to the 'output' tab, and you will see 

if (digitalRead(SASPIN)) { //--------- This is how you do main controls
      MainControls(SAS, HIGH);
      setSASMode(SMSAS); //setting SAS mode
      //setNavballMode(NAVBallSURFACE); //setting navball mode
    }
    else {
      //setNavballMode(NAVBallTARGET);
      MainControls(SAS, LOW);
    }

    if (digitalRead(RCSPIN))
      MainControls(RCS, HIGH);
    else
      MainControls(RCS, LOW);

I will do a tutorial video in the next little while (this is something I wanted to do for a long time) which will explain the code... but honestly, most people here have fought with the code and figured it out! :D ) 

But basically, the first part looks at the switch connected to pin 8 (SASPIN) and works out if it is HIGH - that is, is there current going to it. If so, it will send a signal to the game to enable SAS.

Same for the RCS. If this is clear as mud, you should go check out those arduino tutorials ;)  :P 

The 'utilities' tab controls the LEDs that are connected. Once again, you will see a relationship between the code, and the 'defining' of the variables to pins on the board on that first 'KSPIODemo16' tab.

 

 

 

Edited by Sputnix
Link to comment
Share on other sites

I did it with this, and it work with with the stage I will see with the customs actioon groups now :D

Hi,

you just need to do this:


if (digitalRead(somepin))
      MainControls(STAGE, HIGH);
    else
      MainControls(STAGE, LOW);

You can also control RCS, SAS, LIGHTS, and other stuff this way.

Edited by Tobit
Link to comment
Share on other sites

(As i am a C-coder / Arduino noob ->  correct me anyone if totaly wrong in the helpings).

1. Win 10... you have a USB problem. 

and don´t use pin 0 / 1  and keep A4/A5 free for SerialComms / I2C (LCD)

2. the number of the Data or action groups is a simple structure or byte array expession for object definitions as i understood for myself. 
not to be confused with the pinouts of an arduino or an #define substitute.
you have to understand that the RCSpin, RCS, and the RCSLed (example)-defines are totally different things. 

3. as a little help: 

at the top:

#define CG1PIN 10 // the digital number of the pinout 
#define YLED 13 //  YLED is called "13". 

and the other needed stuff. 

in setup:

pinMode(CG1PIN, INPUT_PULLUP);  // for example this switch - define  that as input pullup with no resistors - important! 
pinMode(YLED ,OUTPUT);   // pin physically on Pin 13 out. 
// but the slider don´t need an input def ? (why?) it´s analog? 

in subroutine // put in input/output section 

 if (digitalRead(CG1PIN)){   //--------- This is how you do control groups  // liest CG1PIN aus  reads that pin
      MainControls(STAGE, HIGH);
digitalWrite(YLED,HIGH);   // you see below YLED is 13 and high / on 

}   // wenn Status high dann führe staging aus - switch kann high gelassen werden; besser wäre ein Pushbutton  /if high than ... 
  else {
MainControls(STAGE, LOW);    
digitalWrite(YLED, LOW);

 }   // sonst low    else low

only an example. because the showing of a StageLED is not usefull. could do this with RCS SAS and the ActionGroups 0-10. as long as you have pins. You can also display the stuff in an LCD / 7seg or LEDs  or whatever you want. Only Problem is to cope with the ComputersKeys; if you have the switch on and type the SAS off, the switch is on and the LED off ;-) 

firstly take the minimal config with the leds. then decide which you don´t need any more. for myself i don´t need any hardware pins for leds, because i use a digital LEDstripe (FAstLED). its only software. i realized at a certain point that i had a switch on pin10 and an LED on 10-  that didn´t work (properly). i spared 3-4 pins. 

4. the rest for the 20x4 is sent you in a pmessage. 
5. the slider / pot  you have strictly to define with YOUR min-max values and ranges. see my posts below. and put a resistor to the ground (debounce, because the values are go random crazy). with the super contrain/map function i had driven sucessfully a little 1 Volts analog display. 
6. and don´t put any delay() function in code!  in loop main is only input() and output(). nothing else for timing issues. ;-)

 

PS: i forgot: the Throttle starting Value could be set to 0 or whatever in the settingsMenus

Edited by sec3
Link to comment
Share on other sites

  • 2 weeks later...

Hello,

I plan to build a control panel. First I want to thank #zitronen for his great work and contribution to spread fun and increasing involvement in KSP, coding and making! :D

Now to my Problem. Everything look promissing but I'm stuck with the CPacked.NavballSASMode (set/get). 

I'm more or less familiar with arduino coding but I never got in touch with byte editing and stuff like that. I apologize in advance for my incompetence and bad programming. :(

 

In the first tests I want to switch between SAS Modes 0-10 by pressing two buttons for up and down. Later every mode will get it own butten.

I tryed to understand the functions on page 70 for the NAV ball stuff, but I don't get how I get a values from this example or set a mode.

So I did something like this in the KSPIODemo16 output.ino in void controls(){...

    
	if ((digitalRead(SASMODEUPPIN) == HIGH) && /*(CPacket.NavballSASMode <= 1010) &&*/ (SASPinUpCheck == 0)){     //Reads Pin 3; was to stay under 10 but seems not to be necessary, to sent one button press insteat of spamming
       CPacket.NavballSASMode ++ ;
       SASPinUpCheck = 1;
       }
       
    if (digitalRead(SASMODEUPPIN) == LOW){                                                                        //After releasing the butten can presst again ones 
       SASPinUpCheck = 0;
       }
    

    if ((digitalRead(SASMODEDOWNPIN) == HIGH) && /*(CPacket.NavballSASMode >= 0) &&*/ (SASPinDownCheck == 0)){    //Reads Pin 4; was to stay over 0 but seems not to be necessary, to sent one button press insteat of spamming
       CPacket.NavballSASMode --;
       SASPinDownCheck = 1;
       }
    
   if  (digitalRead(SASMODEDOWNPIN) == LOW){
       SASPinDownCheck = 0;                                                                                        //After releasing the butten can presst again ones
       }

This kind of work but it switchs the Modes super fast even if there is the SASPinUp/DownCheck that should stop it from happen. The declaration is like that

void controls(){

int SASPinUpCheck;

int SASPinDownCheck;...

Maybe it doesn't work correct because of the Data Type of the NavballSASMode (byte)

The Arduino site sayes for Incrementel ++:

Quote

Parameters

x: variable. Allowed data types: integer, long (possibly unsigned)

int SASPinDownCheck;

https://www.arduino.cc/reference/en/language/structure/compound-operators/increment/

So the main question is: "How can I edit the bits/bytes correctly and get Data from it?"

And second because the byte is split in half can I set the whole byte like a number.

Like SAS Mode 5 = Antinormal and Navball Mode 3 = Target means in the byte

B00110101

0011 for Navball 3 and 0101 for SAS Mode 5???

And if I want to change the Navball Mode add or subtract 16 if the byte can be seen and treated as number?

 

Thanks for the help!

Scorch93

 

P.S.: Because I can't find suitable joystick, I want to build my own ones. I'm not sure if I should build some with potentiometers or micro switches or a mix.

Link to comment
Share on other sites

Hey there

I´m working on a kind of hardware Navball. I´ve got all the wiring for the servos finished today and tested with my Arduino (currently an Uno).

So I´ve searched on ways to receive telemetry data (pitch, yaw, roll position; SAS targets like Prograde, Retrograde, etc..) and naturally stumbled across this mod/plugin/framework.

 

It contains the most essential data (pitch, yaw, roll) I will need, but some data are missing for me (well, maybe I diid not find them...):

- position or coordinates of the different SAS-modes (the "symbols" of PO, RE. Target etc... on the ingame navball)

- pitch, yaw and roll rate like for the indicators in the lower left corner of the hud or in IVA mode

 

I´m pretty new to coding for KSP (got some extended basic knowledge of coding of common languages) and so my question is, if it is possible to add an output for these values to the source code and if someone can give me a little hint where to look/start, since I don´t know how KSP handles these two sets of data.

 

Kind regards and thanks in advance for any help, Naru

Link to comment
Share on other sites

@Scorch93 The reason why it changes is that you SASPin<Up,Down>Check are defined inside controls(). It means they get redefined at every loop. You need to declare them as static in their declaration. In Demo16, zitronen has included the functions to separate the first and last bits, call setSASMode with your desired mode, it resides in output.ino.

@naru1305 You want to talk to @stibbons, he did a software navball implementation. And @richfiles has been working on a hardware navball for quite a while, I seem to recall a discussion in the Custom Hardware thread as well, but I think @Pvt. KASA had to abandon it for some reason?

Link to comment
Share on other sites

20 hours ago, Freshmeat said:

 

@naru1305 You want to talk to @stibbons, he did a software navball implementation. And @richfiles has been working on a hardware navball for quite a while, I seem to recall a discussion in the Custom Hardware thread as well, but I think @Pvt. KASA had to abandon it for some reason?

 Thank you verry much for the fast and helpful answer! 

Link to comment
Share on other sites

On 7/2/2018 at 4:12 AM, naru1305 said:

I´m pretty new to coding for KSP (got some extended basic knowledge of coding of common languages) and so my question is, if it is possible to add an output for these values to the source code and if someone can give me a little hint where to look/start, since I don´t know how KSP handles these two sets of data.

The API for the game is at https://www.kerbalspaceprogram.com/api/index.html . If you dig around in either the Vessel or Orbit classes you should be able to find information about current prograde and other orbital information. Looking at how the current vessel heading code works in the KSPSerialIO plugin is valuable as well.

A rough overview of what you need to do: Get a vector for the direction you're interested in, say orbital prograde. Transform that vector so that it's relative to a fixed reference point - from memory the current code that does this for the active vessel heading uses the world normal. Then you need to transform that vector to a set of Euler angles.  The Euler angles are what you should report to the client.

There's been a couple of projects that can take the Euler angles being reported for vessel attitude and render a software navball, the code for mine is based on catmacey's project. It's applying the rotations to a 3D rendering of a navball, but should give you an idea of a client implementation for a real one too.

I've had a few stabs at adding the information you're looking for, but always fell down - getting my head around the Unity-specific bits, and 3D programming in general, proved to be too much to handle on my own. But I wish you well if you want to give it a try!

Link to comment
Share on other sites

@naru1305 You can get the directions without digging in the KSP API by using kRPC. That will however entail writing your own client in python. I am slowly getting my own toes wet in that particular can of worms, but I foresee several weeks of work before I get beyond simple attitude control.

Link to comment
Share on other sites

@stibbons thank you very much for these informations!  That is directly what I was searching for :) I will have a look on it and from there I will see.

 

@Freshmeat thank you, too! That sounds like an valuable alternative! But this would also mean to go back again into the snakepit Python, which gave me in my final schoolyears informatic-class nightmares :sealed: 

I will first give me an idea on how things in detail are working and we will see :D 

Link to comment
Share on other sites

  • 4 weeks later...

@zitronen and others,

is there any demand for making the plugin send the Pitch+Heading of the navball vectors? (Prograde, Target, Maneuver and Normal). I recently worked the code out for how to do that (because I am making a TCP-socket based version of this plugin), and would be happy to send a pull request. However, the amount of data needed to be sent is quite large, adding up to a total of 7 floats (the Normal vector always has a pitch of zero, so need to send that), or 28 bytes. This size could be cut down by replacing floats with something smaller and less accurate, such as a 2-byte fixed point representation, but that's still 14 bytes. 

 

Edit: Well seems like @naru1305 does :V (I didn't bother reading the thread before posting)

 

Edit 2: anyways here's the code to calculate pitch+heading of the vectors by the way:

Spoiler

NavHeading Prograde, Target, Maneuver;

float NormalHeading, VesselPitch, VesselHeading, VesselRoll;

 

void CalculateVectors(Vessel AV){

Vector3d CoM, north, up, east;
            Quaternion rotationSurface;
            CoM = AV.CoM;
            up = (CoM - AV.mainBody.position).normalized;
            north = Vector3d.Exclude(up, (AV.mainBody.position + AV.mainBody.transform.up * (float)AV.mainBody.Radius) - CoM).normalized;
            east = Vector3d.Cross(up, north);

            rotationSurface = Quaternion.LookRotation(north, up);

            Vector3d attitude = Quaternion.Inverse(Quaternion.Euler(90, 0, 0) * Quaternion.Inverse(AV.GetTransform().rotation) * rotationSurface).eulerAngles;

            VesselRoll = (float)((attitude.z > 180) ? (attitude.z - 360.0) : attitude.z);
            VesselPitch = (float)((attitude.x > 180) ? (360.0 - attitude.x) : -attitude.x);
            VesselHeading = (float)attitude.y;

            Vector3d prograde = new Vector3d(0, 0, 0);
            switch (FlightGlobals.speedDisplayMode)
            {
                case FlightGlobals.SpeedDisplayModes.Surface:
                    prograde = AV.srf_velocity.normalized;
                    break;
                case FlightGlobals.SpeedDisplayModes.Orbit:
                    prograde = AV.obt_velocity.normalized;
                    break;
                case FlightGlobals.SpeedDisplayModes.Target:
                    prograde = FlightGlobals.ship_tgtVelocity;
                    break;
            }

            Prograde = WorldVecToNavHeading(up, north, east, prograde);

            if (TargetExists())
            {
                Vector3d targ = AV.targetObject.GetTransform().position - AV.transform.position;

                Target = WorldVecToNavHeading(up, north, east, targ);
            }
            NormalHeading = WorldVecToNavHeading(up, north, east, north).Heading;

            if (AV.patchedConicSolver != null)
            {
                if (AV.patchedConicSolver.maneuverNodes != null)
                {
                    if (AV.patchedConicSolver.maneuverNodes.Count > 0)
                    {
                        Maneuver = WorldVecToNavHeading(up, north, east, AV.patchedConicSolver.maneuverNodes[0].GetBurnVector(AV.patchedConicSolver.maneuverNodes[0].patch));
                    }
                }
            }

}

private static NavHeading WorldVecToNavHeading(Vector3d up, Vector3d north, Vector3d east, Vector3d v)
        {
            NavHeading ret = new NavHeading();
            ret.Pitch = (float)-((Vector3d.Angle(up, v)) - 90.0f);
            Vector3d progradeFlat = Vector3d.Exclude(up, v);
            float NAngle = (float)Vector3d.Angle(north, progradeFlat);
            float EAngle = (float)Vector3d.Angle(east, progradeFlat);
            if (EAngle < 90)
                ret.Heading = NAngle;
            else
                ret.Heading = -NAngle + 360;
            return ret;
        }

public struct NavHeading
        {
            public float Pitch, Heading;
            public NavHeading(float Pitch, float Heading)
            {
                this.Pitch = Pitch;
                this.Heading = Heading;
            }
        }

 

Edited by c4ooo
Link to comment
Share on other sites

  • 2 weeks later...

@zitronen I sent a pull request, but unfortunately I could not test the code becouse I don't have any arduinos at hand. Should work fine though.

I made the 16-bit angles be stored in the range of [-360,360), so that there is no need for negative headings (headings is in the range (0,360] while pitch is in the range (-90,90)). Not sure if this is the best option long term.

 

Edit: I just came to the realization that the Normal Vector's heading (likewise to Radial Vectors pitch and heading) can be calculated easily from the prograde Vector, so there's no need to send it :V That is unless I got something confused again.

Edited by c4ooo
Link to comment
Share on other sites

Hey @c4ooo and everyone, instead of using the full int16, how about just go lazy and and do "FixedpointAngle = Round(Angle x 50)"? We lose some resolution, but get a nice and round resolution of 0.02 degrees and all people have to do in their arduino code is multiply the angle by 50. The trade off is resolution and ease of use.

I tested @c4ooo's code, works great. The only thing is if you set a planet as a target there is no target angle. I think it's because "TargetExists()" is checking for a vessel.

 

Link to comment
Share on other sites

'Angle x 50' would be better do to ease of use I suppose. (The current ratio is Angle x ~91.0222, which isn't an integer as you can see :V ). Although if you would be fine with negative headings (range of -180 to 180), then you can make the ratio 'Angle x 100', and have a resolution of 0.01 degrees. 

 

As for the targeting planets issue: since the 'set navball mode' code also uses the "TargetExcists()" function, I assume it is not possible to set the navball to "Target" speed mode or set SAS to "target" mode if you are targeting a planet, not a vessel. 

Link to comment
Share on other sites

  • 1 month later...

hey @zitronen, i have a question regarding this adddon, i got a saitek pro farming side pannel and it wont work for stock ksp, it wont recognize the key/joystick and i would like to use it, a use on reddit said in this thread that this addon might work, but i havent found anything about controllers on the main post and i dont have time to search through 82 pages of comments. so can i use this addon to get it working?

Link to comment
Share on other sites

20 hours ago, 3X0karibu said:

hey @zitronen, i have a question regarding this adddon, i got a saitek pro farming side pannel and it wont work for stock ksp, it wont recognize the key/joystick and i would like to use it, a use on reddit said in this thread that this addon might work, but i havent found anything about controllers on the main post and i dont have time to search through 82 pages of comments. so can i use this addon to get it working?

Unfortunately, KSPSerialIO is a non configurable communications plugin on the KSP side, so you cannot plug anything into it and get it to work unless you can program it on microcontroller level. Back in the day I connected a wiimote to my PC though PPJoy, which seems to have evolved into vJoy. You might be able to get KSP to recognize your side panel that way.

Link to comment
Share on other sites

Hmm it's strange KSP doens't recognize it. Does it not show up as a joystick in windows? If vJoy can see it then that might be the better option. If nothing works and you are really desperate I guess you can strip out the inside of the thing and replace it with your own electronics.

I think there was also a joystick mod for KSP that allows you to bind more bottons and stuff, but can't seem to remember what it's called now.

Edited by zitronen
Link to comment
Share on other sites

I have my simpit finished and functional, control and all (windows 10 btw!) 

Is there anything that could cause intermittent connection drops? 

The code multi tasks to read 5 ICs and write to one screen but it doesn't stop talking to KSP while it does so, was wondering if there is anything else? 

Link to comment
Share on other sites

You can try increasing the size of the rx buffer in HardwareSerial.h in your Arduino install.  If that doesn't work or if you are very low on SRAM you can turn down the refresh rate in the plugin config file.

Very cool setup btw!

Edited by zitronen
Link to comment
Share on other sites

This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...