zitronen

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

Recommended Posts

4 hours ago, BamBam said:

ok, so i have belatedly come to realize that i cannot simply copy somebody's code without having their exact setup.  So, I will be starting from the Demo17 code.  That Demo17 code works just as it should.

Here is my rough plan...  I have a USB-HID interface board that I will be using for the actual input into the game.  that lets me keep the input functionality separate and simple.  I plan to use serialIO to drive my indicator LEDs and a set of 7 segment / 8 digit displays to show altitude, apoapsis, periapsis, velocity.  and some analog guages to show fuel levels, and atmosphere density.  It means that i will have to have two USB cables connected but that is no big deal.

So, instead of somebody to solve my problem for me, what i need is a really basic tutorial on how to make the starter code in Demo17 to do what i tell it.  My programming skills are all from 1994 when we did Turbo Pascal in High School.  Since then i have not done anything more than write batch files and a few really simple if/then operations.  So I have basically zero experience with Arduino.  My 3D printer ran briefly on Marlin but even that code was already setup and you just had to define a few variables.

Dipping into some major wishful thinking here, I saw on this or maybe on the subreddit /r/kerbalcontrollers that somebody had made a sort of DSKY display that used a small LCD panel.  That was beautiful, and if I could figure out how that was implemented that would be a major upgrade to my sort of simplistic plan at the moment.

Taking the demo code and building on it is exactly what I'd recommend, that's how I've gotten started.  Take a look at my starter controller a few posts up, I've gotten something very similar to what you're describing.  7-seg displays for the Ap/Pe/Alt/Velocity, some analog gauges, and nice big buttons for staging.

As for learning the Arduino environment, I wholeheartedly recommend the Sparkfun Inventor's Kit as well as the matching Guidebook.  It's exactly how I got started into the world of micro-electronics, and will give the building-blocks to start learning the Arduino language (which is really just C++ with some additional functions/subroutines built in).  Sparkfun (while they are expensive) also has a bunch of add-ons and products that will make your life much easier, such as their Serial 7-Segment Display and the matching Hookup Guide which makes hooking up a lot of 7-segment displays vastly simpler. 

Their support and guides on how to hook things up have basically saved my ass, almost all of my controller is built off of chunks of their demo code that I've modified to work with my own variable names.

Edited by tsaven

Share this post


Link to post
Share on other sites

ok.  so this function under the output section.

void controls() {
  if (Connected) {

    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);

    if (digitalRead(CG1PIN))   //--------- This is how you do control groups
      ControlGroups(1, HIGH);
    else
      ControlGroups(1, LOW);
      
    /*
       if (getSASMode() == SMPrograde) { //--------- This is how you read SAS modes
         //Blink LED, do stuff, etc.
       }

       if (getNavballMode() == NAVBallTARGET) { //--------- This is how you read navball modes
         //Blink LED, do stuff, etc.
       }
    */


    //This is an example of reading analog inputs to an axis, with deadband and limits
    CPacket.Throttle = constrain(map(analogRead(THROTTLEPIN), THROTTLEDB, 1024 - THROTTLEDB, 0, 1000), 0, 1000);

    //This is an example of reading analog inputs to an axis, with deadband and limits
    //CPacket.Pitch = constrain(map(analogRead(THROTTLEPIN),0,1024,-1000,1000),-1000, 1000);

    KSPBoardSendData(details(CPacket));
  }
}

Where it calls 'if digitalread rcspin'

that means :

  if the digital pin defined as 'whateverpin' is connected to ground (button pressed), the do the following

make 'whatevercommand' have a 1 as its bit 

else

make 'whatevercommand' have a 0 as its bit

 

and then the "maincontrols" function enforces that its binary and not some analog value, and ads it to the packet.  

Have I got that right?  so each button press command you want to send thru serialio follows that syntax?

Share this post


Link to post
Share on other sites
20 hours ago, Freshmeat said:

It is a bit hard for me to understand what your problem is, but if I understand correctly, the demo code runs, but not your own code. To help, we will need to see you own code. Please, do not paste it in a post, but upload it to somewhere we can download and look. I do recall wurmis name, but not any particulars of his project. If it is old, you might have to update the VesselData and ControlPacket structs with the new entries. Also, a sketch is keyed to a specific hardware setup, so unless you have the exact equivalent setup and wiring the sketch will not do anything useful.

By the way, the other guys code I am looking into using is this.

https://github.com/Richi0D/Kerbalcontroller

 

My hardware goals would align with this for the most part.  I quite like those displays, but i dont want to order them unless i can figure out the programming.

 

EUREKA!!!!     The buad rate in this batch of code for the serial connection was different than the one in Demo17.  It was set way higher.  I changed it down, and now it talks!. The tx and rx lights on the Arduino go active when loading a rocket.   Thats enough tinkering for today.  I can go to sleep feeling all accomplished and stuff.

Edited by BamBam

Share this post


Link to post
Share on other sites
18 hours ago, BamBam said:

Diipping into some major wishful thinking here, I saw on this or maybe on the subreddit /r/kerbalcontrollers that somebody had made a sort of DSKY display that used a small LCD panel.  That was beautiful, and if I could figure out how that was implemented that would be a major upgrade to my sort of simplistic at the moment.

As has been said, start small and get the simple little things under your belt first. That DSKY was a fair bit of work, but looking back, it was just a *lot* of simple things all combined together. I did have to learn how to code a WPF form to do it though. Simple is all relative to where you are on the learning curve. Something like taking the entire RPM/MAS variable list and pushing it to the Arduino/Teensy, should not be anywhere near a first step.

It sounds like your making progress though, and that's all that's required for this hobby - "small progress" It just gets built upon each time you add to it. <- There's a Kerbal saying if I've every heard one. :D

Congrats on the baud rate, that's tripped me up a few times too.

I can relate to your plan with doing a software  focus first. I've tried combining the hardware builds at the same time and just burn out for a month or two due to no progress as there wasn't software planned out to run the hardware. Other people can do it the other way around, so there is a bit of personal preference there.

Edited by cyberKerb

Share this post


Link to post
Share on other sites
On 9/16/2019 at 10:20 AM, tsaven said:

@zitronen, I have to give mad props to you for creating and maintaining a mod that has such capabilities.  I'm farther along in my controller than I ever expected to be and I'd never have been able to do it if your demo code hadn't spelled things out so clearly and easily.  Thank you, thank you for documenting and commenting the code out as well as you have.

I'm integrating more and more functions into my controller every day!

 

 

Well done! When you guys get stuff working, it makes me happy. I would suggest adding time to AP, it's really useful to have to know if you are going to slow or to fast to orbit efficiently.

 

12 hours ago, BamBam said:

ok.  so this function under the output section.


void controls() {
  if (Connected) {

    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);

    if (digitalRead(CG1PIN))   //--------- This is how you do control groups
      ControlGroups(1, HIGH);
    else
      ControlGroups(1, LOW);
      
    /*
       if (getSASMode() == SMPrograde) { //--------- This is how you read SAS modes
         //Blink LED, do stuff, etc.
       }

       if (getNavballMode() == NAVBallTARGET) { //--------- This is how you read navball modes
         //Blink LED, do stuff, etc.
       }
    */


    //This is an example of reading analog inputs to an axis, with deadband and limits
    CPacket.Throttle = constrain(map(analogRead(THROTTLEPIN), THROTTLEDB, 1024 - THROTTLEDB, 0, 1000), 0, 1000);

    //This is an example of reading analog inputs to an axis, with deadband and limits
    //CPacket.Pitch = constrain(map(analogRead(THROTTLEPIN),0,1024,-1000,1000),-1000, 1000);

    KSPBoardSendData(details(CPacket));
  }
}

Where it calls 'if digitalread rcspin'

that means :

  if the digital pin defined as 'whateverpin' is connected to ground (button pressed), the do the following

make 'whatevercommand' have a 1 as its bit 

else

make 'whatevercommand' have a 0 as its bit

 

and then the "maincontrols" function enforces that its binary and not some analog value, and ads it to the packet.  

Have I got that right?  so each button press command you want to send thru serialio follows that syntax?

More or less. The way it's written the command goes on when pin is connected to Vcc (3.3 or 5V) not ground, but it's the same principle. Also this is set up for a switch, not a button, unless the button is latching. In order to turn on RCS, a specific bit in the controls byte of the data packet needs to be set. The maincontrols() function just makes the code easier for people to read. Rather than having to remember which bit number is RCS, and doing the bit maths do set it, you just do maincontrols(RCS, HIGH).

 

Share this post


Link to post
Share on other sites
3 hours ago, zitronen said:

Well done! When you guys get stuff working, it makes me happy. I would suggest adding time to AP, it's really useful to have to know if you are going to slow or to fast to orbit efficiently.

I plan to, but right now I'm still having a lot of problems with the timing calculations.  The time displays work fine part of the time, but the seconds counter will randomly get stuck for exactly 59 seconds (until the minute number needs to change) at which point it will work for another 15 seconds before getting stuck again.

I've got a 4x20 LCD that I need to hook up so I can spit variables to it from different parts of the calculations to figure out exactly where it's messing up, but I wanted to make sure I got all of the more basic controls functioning first before I move on to troubleshooting the more ancillary bits.

Share this post


Link to post
Share on other sites

Hey Zitronen, my controller with fastleds / LCD / slider-pot is now working on  IO Vers. 0.19.1 and a reset-up from code base 0.17.   

I altered the RX buffer to 256 and Serial rate to 115200 as i had a connection problem.
but on my arduino i had to write-unlock the hardware.h in the core section, because there was no .h file in the preferences folder. 

 

 

Edited by sec3

Share this post


Link to post
Share on other sites

Hi, I followed this thread and used the examplecode, everything is fine (simple LEDs, SAS and RCS controll), but I just don´t manage it to get my I2C- Display to work. I am using 

LiquidCrystal.h

and want to get my AP and PE displayed. It is a 16x2 Display. Someone her who could offer me an example. To be honest, I have no clue of coding  oder Arduino, but I try my best.... 

 

Edited by Rickental

Share this post


Link to post
Share on other sites

Hi Zitronen, I should give some more information... I use your great KSPserialIO, KSP 1.7.3 and  arduino uno. Loading your example KSPIOdemo17 the LEDs are working and I can controll SAS und RCS via external switches. The Display is working using the arduino library examples. I have put this at the beginning of KSPIOdemo:

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
 
LiquidCrystal_I2C lcd(0x27,20,4);  

I found this code in this forum and put it into Utilities just after void Indicators() [

  lcd.begin(16, 2);
  // Print a message to the LCD.
  lcd.setCursor(0,0);
  lcd.print((long)VData.AP);
  lcd.setCursor(0,1);
  lcd.print((long)VData.PE);
}

void InitTxPackets() {
  HPacket.id = 0;  
  CPacket.id = 101;
} 

Of course, it doesnt work, just because I am only trying without knowing what I really do ;).. I think I will get a starterKit and begin learning some basics. 

Chris

Share this post


Link to post
Share on other sites

@Reckental you probably want to start here  then you test that your LCD actually works. That done you would add pretty much what you have in your post there, other than lcd.begin belongs under setup so it only runs once. Been a while since I looked at i2C LCD printing and long/floats and you may need to customise a print function for them to get reliable placement of the decimal point. For the moment would suggest lcd.print(floor(VData.AP); to just strip the decimal point.

Share this post


Link to post
Share on other sites

Thanks for the hints, the display is OK and working, but is not showing anything in KSP. The examples of working Kerbal Controll Units are usually very complex (for me) and don´t help me with my simple display. Time will tell, I keep trying ;) 

Chris

Share this post


Link to post
Share on other sites

Ok, the display is working with KSP, at launch I get the correct AP an PE. And thats all... The three LEDS stop working and a new LCD-measure shows up at the display after ca. 20 seconds, than its not updated anymore.  I put the library for the display at the beginning of the Demo. Then in setup:

 lcd.init();                     
  lcd.backlight();

than in loop: 

  lcd.setCursor(0,0);
  lcd.print((long)VData.AP);
  lcd.setCursor(0,1);
  lcd.print((long)VData.PE);

 

I played around with the config of the plugin, but no success. 

Chris

Share this post


Link to post
Share on other sites

A suggestion for mysterious halting is to do something like lcd.print(millis()/100);

This will print time since start in tenths of seconds and will tell you if your arduino is halting, or the KSP plugin. Most of my finished display projects do something like blink a pixel or toggle 1 to 0 to make it obvious things are still running.

A suggestion while you sort this out is to print debug to line 1 of the LCD and KSP data to line 0 or similar. Hopefully prevent overwrites of something critical to understanding what is happening.

For a halt after 40 seconds the normal contenders are overflow or memory exhaustion of some sort, in particular how often is that lcd.print block happening? You should only update it once for each data packet from KSP but suspect that is being updated faster than that at the moment which may be disrupting things.

Share this post


Link to post
Share on other sites

OK you do not want to put anything in "loop()", the loop needs to run very fast without being slowed down by slow LCD stuff. Put your LCD code in "input()" under the KSP receive data if statement like this:

 

if (KSPBoardReceiveData()){
    deadtimeOld = now;
    returnValue = id;
    switch(id) {
    case 0: //Handshake packet
      Handshake();
      break;
    case 1:
      Indicators();

      //LCD CODE HERE **************
      lcd.setCursor(0,0);
      lcd.print((long)VData.AP);
      lcd.setCursor(0,1);
      lcd.print((long)VData.PE);

      break;
    }

    //We got some data, turn the green led on
    digitalWrite(GLED,HIGH);
    Connected = true;
  }

 

Share this post


Link to post
Share on other sites
On 3/3/2019 at 3:01 PM, cyberKerb said:

Ok - follow up query for @zitronen regarding the issues I experienced. Just curious - was there a reason (maybe KSP limitation) on why the mod used "OpenNETCF.IO.Ports" instead of "System.IO.Ports" that is in the Debug tool?

It seems that the KSPSERIALIO debugtool doesn't require the serialport.dll and is a little more robust with COM ports > COM9. However, the OpenNETCF SerialPort.dll included with this mod seems to be unable to utilize higher COM ports for some reason. At least that is the comments I see in this thread and with my own experience.

Saw the OP issues notes (sorry! :blush:)
 

 

 

 

 

On 3/4/2019 at 4:17 PM, zitronen said:

It's because Unity does not have System.IO.Ports like MS .net. It can't access serial ports. If it did it would have made everyone's life so much easier.

Hi everyone

I have the same issue with Win10.

After a lot of research i found that a COM port > COM9 must be like \\.\COMxx

I tried putting \\.\COM16 (the COM port of my arduino) as default port in the config.xml of the plugin and i had handshake.

So i made  small additions in KSPIO.cs (0.19.1 version) generated the dll and it works fine with KSP 1.7.3 and also with a fresh install of KSP 1.8.1

You will find the modified KSPIO.cs here : https://www.dropbox.com/s/whqotb7y0dtbug6/KSPIO.cs?dl=0

Hope this could help.

Share this post


Link to post
Share on other sites

These are the modifications i made :

Added line 11

using System.Text.RegularExpressions;

Added lines 266 and 267

public static string NumPortValueString;
public static int NumPortValue;

Added lines 420 to 425

NumPortValueString = Regex.Match(PortNumber, @"\d+").Value;
NumPortValue = int.Parse(NumPortValueString);
if (NumPortValue > 9)
{
    PortNumber = @"\\.\" + PortNumber;
}

Added lines 431 to 436

NumPortValueString = Regex.Match(PortNumber, @"\d+").Value;
NumPortValue = int.Parse(NumPortValueString);
if (NumPortValue > 9)
{
    PortNumber = @"\\.\" + PortNumber;
}

 

Share this post


Link to post
Share on other sites

@Jimbofarrar Good work! I never quite figured out what port number format the serial port dll wanted. Thanks for the research.

I'm away at a conference this week, will update the plugin with your code when I come back next week.

Share this post


Link to post
Share on other sites

There is an update :

I ran some tests in KSP 1.7.3 an 1.8.1 (both without and with Breaking Ground and Making History Expansions) and they reveal that we don't need to test if com port is > 9.

The syntax \\.\COMxx works from COM1

The modification needed in the plugin is just in one line

Line 429

Port.PortName = @"\\.\" + PortNumber;   // add @"\\.\" to PortNumber to have win10 compatibility for com ports > 9

 

According to the terms of use you will find a link to a zip file for the sources and license text, and a second link for the compiled dll

License : CC BY, attribution to Zitronen

Sources : https://www.dropbox.com/s/k5jl6pflr7vooh6/KSPSerialIO.zip?dl=0

Compiled dll : https://www.dropbox.com/s/39tfno3277yy992/KSPSerialIO.dll?dl=0

Edited by Jimbofarrar
Typo

Share this post


Link to post
Share on other sites

I found an explanation here : https://docs.microsoft.com/fr-fr/windows/win32/fileio/naming-a-file#win32-device-namespaces

 

Quote

Win32 Device Namespaces

The "\\.\" prefix will access the Win32 device namespace instead of the Win32 file namespace. This is how access to physical disks and volumes is accomplished directly, without going through the file system, if the API supports this type of access. You can access many devices other than disks this way (using the CreateFile and DefineDosDevice functions, for example).

For example, if you want to open the system's serial communications port 1, you can use "COM1" in the call to the CreateFile function. This works because COM1–COM9 are part of the reserved names in the NT namespace, although using the "\\.\" prefix will also work with these device names. By comparison, if you have a 100 port serial expansion board installed and want to open COM56, you cannot open it using "COM56" because there is no predefined NT namespace for COM56. You will need to open it using "\\.\COM56" because "\\.\" goes directly to the device namespace without attempting to locate a predefined alias.

 

Share this post


Link to post
Share on other sites

Sorry for the late reply, I'm having some issues with your code. It works fine the first time, but when you revert flight and go back to launch it stops working. I'm trying to figure out what's going on.

Share this post


Link to post
Share on other sites

OK I fixed it!

 

Update 0.19.2:

Changes:

  • Fixed very old bug of auto detection and connection with high number (>10) COM ports, thanks to detective work from @Jimbofarrar

Plugin download link:

https://sites.google.com/site/zitronfiles/KSPSerialIO_019_2.zip

Arduino code dowload:

https://sites.google.com/site/zitronfiles/KSPIODemo17.zip

Edited by zitronen

Share this post


Link to post
Share on other sites

Hi all and a big hug to @zitronen

I have been mucking around with this and struggling enjoyably and read that on windows 10 input was no longer accepted yet my pot is working! Is this because it is analog as opposed to digital?

I have not wired a button up yet :) #noob #learningcode

EDIT 2: I got it! this is the best day ever

Edited by Kerbal007
edit: I have a switch working for SAS, investigating a launch button now

Share this post


Link to post
Share on other sites

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.