Jump to content

[KSP 1.10.0] Kerbal Simpit: A KSP serial mod for hardware controllers (1.4.1)


stibbons

Recommended Posts

I'm starting in on my kerbtroller project, and I'm at the part where I'm working on getting the controller prototype and the game talking to each other, and I can't seem to get it working - there's Simpit references in KSP.log, but KerbalSimpitStageDemo that I loaded onto my arduino doesn't seem to do anything, and the built in LED stays on. Any ideas?

Edited by madradhu
Link to comment
Share on other sites

@madradhu What do you mean by simply references in the log? Could you provide a copy of the log file somewhere for us to look over to see what the issue may be?

Have you checked the serial port the Arduino is connected to is not in use by another program?

Is the serial port the Arduino is using defined as the serial port in the config file for Kerbal simpit?

Link to comment
Share on other sites

23 hours ago, LRTNZ said:

@madradhu What do you mean by simply references in the log? Could you provide a copy of the log file somewhere for us to look over to see what the issue may be?

Have you checked the serial port the Arduino is connected to is not in use by another program?

Is the serial port the Arduino is using defined as the serial port in the config file for Kerbal simpit?

This I believe is the plugin loading, right at the top of KSP.log:

[LOG 20:54:04.476] Load(Assembly): KerbalSimpit/KerbalSimpit
[LOG 20:54:04.477] AssemblyLoader: Loading assembly at S:\SteamLibrary\steamapps\common\Kerbal Space Program\GameData\KerbalSimpit\KerbalSimpit.dll
[LOG 20:54:04.507] AssemblyLoader: KSPAssembly 'KerbalSimpit' V1.3.0
[LOG 20:54:04.507] AssemblyLoader: KSPAssemblyDependency 'KerbalSimpitSerial' V1.0.0
[LOG 20:54:04.507] Load(Assembly): KerbalSimpit/KerbalSimpitSerial
[LOG 20:54:04.507] AssemblyLoader: Loading assembly at S:\SteamLibrary\steamapps\common\Kerbal Space Program\GameData\KerbalSimpit\KerbalSimpitSerial.dll
[LOG 20:54:04.508] AssemblyLoader: KSPAssembly 'KerbalSimpitSerial' V1.0.0
[LOG 20:54:04.508] Load(Assembly): KerbalSimpit/Mono.Posix
[LOG 20:54:04.508] AssemblyLoader: Loading assembly at S:\SteamLibrary\steamapps\common\Kerbal Space Program\GameData\KerbalSimpit\Mono.Posix.dll

It's pointed at the correct COM port in Settings.cfg, nothing else is using it as far as I can tell. I mocked up the simple debounce sketch physically on my arduino that KerbalSimpitStageDemo.ino references, but I can't seem to get it to work. Dunno if there's anything obvious to try. 

Link to comment
Share on other sites

  • 2 months later...

I do not get how to access the resourceMessage struct. I want to see my total and available LF (all really, but lets start with that.) Although I follow the example of altitude its just not doing anything. Code below. Lots of stuff wrong, I am sure. All I really need is to see how to access the resource information. Do I have to use Altnerate Resource Panel for it to work at all?

#include <KerbalSimpit.h>

KerbalSimpit mySimpit(Serial);

int DS_pin = 11;
int STCP_pin = 8;
int SHCP_pin = 12;

boolean registers[10];

void setup() {
  // put your setup code here, to run once:
   
  Serial.begin(115200);
  
  pinMode(DS_pin,OUTPUT);
  pinMode(STCP_pin,OUTPUT);
  pinMode(SHCP_pin,OUTPUT);
  pinMode(LED_BUILTIN,OUTPUT);

  //digitalWrite(LED_BUILTIN,HIGH);
  for(int i = 0; i<10; i++)
    {
      registers = LOW;
      writereg();
    }
  
  while(!mySimpit.init()); {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN,LOW);
  }

digitalWrite(LED_BUILTIN,HIGH);

  mySimpit.inboundHandler(messageHandler);
  mySimpit.registerChannel(LF_MESSAGE);
  //mySimpit.registerChannel(ALTITUDE_MESSAGE);
  
  
}


void writereg() {
  digitalWrite(STCP_pin, LOW);
  for(int i = 9; i>=0; i--)
  {
    digitalWrite(SHCP_pin, LOW);
    digitalWrite(DS_pin, registers);
    digitalWrite(SHCP_pin, HIGH);
  }
  digitalWrite(STCP_pin, HIGH);
}


void loop() {
  // put your main code here, to run repeatedly:
  mySimpit.update();
}

void messageHandler(byte messageType, byte msg[], byte msgSize) {

if (msgSize == sizeof(resourceMessage))
  {
    resourceMessage myLF;
    myLF = parseResource(msg);
    //float percentLF = myLF.available / myLF.total;
    if (myLF.available < 10)
    {
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);    
    }
  }
}

Link to comment
Share on other sites

  • 1 month later...
  • 1 month later...
On 2/21/2020 at 3:39 PM, Wasabi said:

I can't figure out what WHEEL_MESSAGE is used for. What does it contain that ROTATION_MESSAGE doesn't?

https://kerbalsimpit-arduino.readthedocs.io/en/stable/payloadstructs.html?highlight=wheelmessage#_CPPv312wheelMessage

Wasabi,

  The Wheel_Message is used to control rovers or wheel motors (Think -- ground based vehicles).  The Rotation_Message is for just that - rotational parameters (think space/airborne).

    myWheel.throttle = buttonCurrent[i];
    myWheel.steer = buttonCurrent[j];
    myWheel.mask = 3;
    mySimpit.send(WHEEL_MESSAGE, myWheel); 

 

  

Link to comment
Share on other sites

On 12/22/2019 at 2:27 PM, TygurDuck said:

I do not get how to access the resourceMessage struct. I want to see my total and available LF (all really, but lets start with that.) Although I follow the example of altitude its just not doing anything. Code below. Lots of stuff wrong, I am sure. All I really need is to see how to access the resource information. Do I have to use Altnerate Resource Panel for it to work at all?

#include <KerbalSimpit.h>

KerbalSimpit mySimpit(Serial);

int DS_pin = 11;
int STCP_pin = 8;
int SHCP_pin = 12;

boolean registers[10];

void setup() {
  // put your setup code here, to run once:
   
  Serial.begin(115200);
  
  pinMode(DS_pin,OUTPUT);
  pinMode(STCP_pin,OUTPUT);
  pinMode(SHCP_pin,OUTPUT);
  pinMode(LED_BUILTIN,OUTPUT);

  //digitalWrite(LED_BUILTIN,HIGH);
  for(int i = 0; i<10; i++)
    {
      registers = LOW;
      writereg();
    }
  
  while(!mySimpit.init()); {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN,LOW);
  }

digitalWrite(LED_BUILTIN,HIGH);

  mySimpit.inboundHandler(messageHandler);
  mySimpit.registerChannel(LF_MESSAGE);
  //mySimpit.registerChannel(ALTITUDE_MESSAGE);
  
  
}


void writereg() {
  digitalWrite(STCP_pin, LOW);
  for(int i = 9; i>=0; i--)
  {
    digitalWrite(SHCP_pin, LOW);
    digitalWrite(DS_pin, registers);
    digitalWrite(SHCP_pin, HIGH);
  }
  digitalWrite(STCP_pin, HIGH);
}


void loop() {
  // put your main code here, to run repeatedly:
  mySimpit.update();
}

void messageHandler(byte messageType, byte msg[], byte msgSize) {

if (msgSize == sizeof(resourceMessage))
  {
    resourceMessage myLF;
    myLF = parseResource(msg);
    //float percentLF = myLF.available / myLF.total;
    if (myLF.available < 10)
    {
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);    
    }
  }
}

I have recently only been able to achieve Controller to KSP communications.   My controller works fine but I can no longer get data from the KSP Client.

just a suggestion - loops and timing checks are much better than repeated hard coded delays.

 

Link to comment
Share on other sites

On 12/22/2019 at 2:27 PM, TygurDuck said:

I do not get how to access the resourceMessage struct. I want to see my total and available LF (all really, but lets start with that.) Although I follow the example of altitude its just not doing anything. Code below. Lots of stuff wrong, I am sure. All I really need is to see how to access the resource information. Do I have to use Altnerate Resource Panel for it to work at all?

#include <KerbalSimpit.h>

KerbalSimpit mySimpit(Serial);

int DS_pin = 11;
int STCP_pin = 8;
int SHCP_pin = 12;

boolean registers[10];

void setup() {
  // put your setup code here, to run once:
   
  Serial.begin(115200);
  
  pinMode(DS_pin,OUTPUT);
  pinMode(STCP_pin,OUTPUT);
  pinMode(SHCP_pin,OUTPUT);
  pinMode(LED_BUILTIN,OUTPUT);

  //digitalWrite(LED_BUILTIN,HIGH);
  for(int i = 0; i<10; i++)
    {
      registers = LOW;
      writereg();
    }
  
  while(!mySimpit.init()); {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN,LOW);
  }

digitalWrite(LED_BUILTIN,HIGH);

  mySimpit.inboundHandler(messageHandler);
  mySimpit.registerChannel(LF_MESSAGE);
  //mySimpit.registerChannel(ALTITUDE_MESSAGE);
  
  
}


void writereg() {
  digitalWrite(STCP_pin, LOW);
  for(int i = 9; i>=0; i--)
  {
    digitalWrite(SHCP_pin, LOW);
    digitalWrite(DS_pin, registers);
    digitalWrite(SHCP_pin, HIGH);
  }
  digitalWrite(STCP_pin, HIGH);
}


void loop() {
  // put your main code here, to run repeatedly:
  mySimpit.update();
}

void messageHandler(byte messageType, byte msg[], byte msgSize) {

if (msgSize == sizeof(resourceMessage))
  {
    resourceMessage myLF;
    myLF = parseResource(msg);
    //float percentLF = myLF.available / myLF.total;
    if (myLF.available < 10)
    {
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);    
    }
  }
}

I sorted out my comm issues between KSP and my controller.

I threw together a basic sketch that 'tries' to do what you were attempting.  It will blink the builtinLED when the fuel level is below 10.0.

PLEASE make sure that you have the correct COM port assigned in the settings.cfg file in the plugindata directory

D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\KerbalSimpit\PluginData

#include <KerbalSimpitMessageTypes.h>
#include <PayloadStructs.h>
#include <KerbalSimpit.h>

KerbalSimpit mySimpit(Serial);
float fuel = 10.0;

unsigned int lastBlink = 0;
int blinkPeriod = 500;
bool blinkBool = false;

void setup() {
  // put your setup code here, to run once:
   
  Serial.begin(115200);
  
  pinMode(LED_BUILTIN,OUTPUT);
  digitalWrite(LED_BUILTIN,LOW);  //TURN THE LED OFF UNTIL WE CONNECT WITH KSP
  
  delay(100);
  while(!mySimpit.init()); {
    delay(100);
  }

  digitalWrite(LED_BUILTIN,HIGH);  //  SHOWS WERE CONNECTED!
  delay(500);
  
  mySimpit.inboundHandler(messageHandler);
  mySimpit.registerChannel(10);   // 10 is the value for LF  --  Outputs a resourceMessage
 
}


void loop() {
  // put your main code here, to run repeatedly:
  mySimpit.update();
  checkFuelLevel();
}

void messageHandler(byte messageType, byte msg[], byte msgSize) {
  
  if (msgSize == sizeof(resourceMessage))
  {
    resourceMessage myLF;
    myLF = parseResource(msg);
    //float percentLF = myLF.available / myLF.total;
    fuel = myLF.available;
  }
}

void checkFuelLevel(){
    unsigned int nowCheck = millis();
  if( fuel < 10.0 ){  //  CHECK FOR FUEL LOW
    if( nowCheck > ( lastBlink + blinkPeriod ) ){
      lastBlink = nowCheck;
      digitalWrite(LED_BUILTIN, blinkBool);
      blinkBool = !blinkBool;
    } 
  }else{
     digitalWrite(LED_BUILTIN,HIGH); 
  }
}

 

Link to comment
Share on other sites

  • 1 month later...
On 12/23/2019 at 8:27 AM, TygurDuck said:

I do not get how to access the resourceMessage struct. I want to see my total and available LF (all really, but lets start with that.) Although I follow the example of altitude its just not doing anything. Code below. Lots of stuff wrong, I am sure. All I really need is to see how to access the resource information. Do I have to use Altnerate Resource Panel for it to work at all?

#include <KerbalSimpit.h>

KerbalSimpit mySimpit(Serial);

int DS_pin = 11;
int STCP_pin = 8;
int SHCP_pin = 12;

boolean registers[10];

void setup() {
  // put your setup code here, to run once:
   
  Serial.begin(115200);
  
  pinMode(DS_pin,OUTPUT);
  pinMode(STCP_pin,OUTPUT);
  pinMode(SHCP_pin,OUTPUT);
  pinMode(LED_BUILTIN,OUTPUT);

  //digitalWrite(LED_BUILTIN,HIGH);
  for(int i = 0; i<10; i++)
    {
      registers = LOW;
      writereg();
    }
  
  while(!mySimpit.init()); {
    digitalWrite(LED_BUILTIN,HIGH);
    delay(100);
    digitalWrite(LED_BUILTIN,LOW);
  }

digitalWrite(LED_BUILTIN,HIGH);

  mySimpit.inboundHandler(messageHandler);
  mySimpit.registerChannel(LF_MESSAGE);
  //mySimpit.registerChannel(ALTITUDE_MESSAGE);
  
  
}


void writereg() {
  digitalWrite(STCP_pin, LOW);
  for(int i = 9; i>=0; i--)
  {
    digitalWrite(SHCP_pin, LOW);
    digitalWrite(DS_pin, registers);
    digitalWrite(SHCP_pin, HIGH);
  }
  digitalWrite(STCP_pin, HIGH);
}


void loop() {
  // put your main code here, to run repeatedly:
  mySimpit.update();
}

void messageHandler(byte messageType, byte msg[], byte msgSize) {

if (msgSize == sizeof(resourceMessage))
  {
    resourceMessage myLF;
    myLF = parseResource(msg);
    //float percentLF = myLF.available / myLF.total;
    if (myLF.available < 10)
    {
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(300);
      digitalWrite(LED_BUILTIN,LOW);
      delay(300);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);
      digitalWrite(LED_BUILTIN,HIGH);
      delay(100);
      digitalWrite(LED_BUILTIN,LOW);
      delay(100);    
    }
  }
}

@TygurDuck The big issue is that you are running blocking code - this is where you prevent the processor from doing anything else, as it is stuck in a delay.

@Mofates your demo code using the boolean operators and the timer code is how I would recommend to do it. There is one really big issue though - that is, you have about 65 seconds of useful time to use it, before you will probably encounter some really weird timing bugs, when lastBlink rolls over back to zero. An unsigned int on an arduino has a maximum value of 65,535. As you are storing milliseconds, that comes to just over a minute, until it rolls over. You need to be using an unsigned long, which can hold a maximum value of 4,294,967,295 - if you manage to exceed that, you really need to stop playing :)

Link to comment
Share on other sites

Hello @stibbons! I really appreciate the work you've done here and it's been really exciting building my own Kontrol box for KSP. I have successfully gotten a load of buttons to work, plus throttle. I'm working through translating my 3-axis analog joystick inputs now to the format that Simpit needs.

 

I wanted to ask if you had any plans to incorporate more of the overall game commands into your mod. For instance, I've put Quicksave, Load, Pause, Time Warp, and Map functions on my Kontrol box. At this time I don't see any such functionality built into Simpit, so it looks like I'm going to have to emulate an HID with a second arduino board to accomplish that. Thanks!

Link to comment
Share on other sites

  • 2 weeks later...

All,

I just finished constructing my prototype control box and everything was working individually until I put it all together. Now it freezes up about 5-10 seconds into game play and seems to stop transmitting data to the computer. I've checked the circuit connections and have found no shorts or faulty connections. Running tests with serial monitor shows that the Arduino is continuously outputting data in these situations. What I've noticed when I play the game is that the Rx LED on the Arduino is constantly lit up, which makes me think that it's locking up because it's getting inundated with information from the USB port. Do the message channels continually broadcast? TIA.

EDIT: Further testing revealed that it is the interaction of the analog joysticks that seems to cause everything to freeze up. I commented out the portion of the code below where the joysticks live and everything else worked flawlessly without freezing. The joysticks work on their own when I troubleshoot with serial monitor, and both power and ground are commonly wired with the joysticks in parallel with the other buttons and switches, so it's not a physical connection issue. It seems to be something with the Simpit code sending/receiving too much data when the joysticks are used that causes it to lock up. What am I doing in the joystick code that's causing this?

 

// Sets up the Arduino Mega to handle the analog joysticks, Action Groups, switches and buttons
#include <KerbalSimpit.h>
#include <KerbalSimpitMessageTypes.h>
#include <PayloadStructs.h>
#include <ezButton.h> // loads ezButton library for button debounce
#include <Rotary.h>

rotationMessage myRotation;
translationMessage myTranslation;

const int ROT_X = A0; // assigns rotation joystick X-axis to pin Analog 0
const int ROT_Y = A1; // assigns rotation joystick Y-axis to pin Analog 1
const int ROT_Z = A2; // assigns rotation joystick Z-axis to pin Analog 2
const int TRANS_X = A3; // assigns translation joystick X-axis to pin Analog 3
const int TRANS_Y = A4; // assigns translation joystick Y-axis to pin Analog 4
const int TRANS_Z = A5; // assigns translation joystick Z-axis to pin Analog 5
const int STAGE_BTN = 2; // assigns the Staging button to pin 2
const int RCS_LED = 3; // assigns the RCS indicator LED to pin 3
const int BRAKES_LED = 4; // assigns the Brakes indicator LED to pin 4
const int GEAR_LED = 5; // assigns the Landing Gear indicator LED to pin 5
const int LIGHTS_LED = 6; // assigns the Lights indicator LED to pin 6
const int SAS_LED = 7; // assigns the SAS indicator LED to pin 7
const int THROT_CLK = 22; // assigns the Throttle rotary encoder CLK output ("A") to pin 22
const int THROT_DT = 23; // assigns the Throttle rotary encoder DT output ("B") to pin 23
const int THROT_BTN = 24; // assigns the Throttle rotary encoder Switch output to pin 24
const int THROT_CUT = 25; // assigns the Throttle Cut button output to pin 25
const int SAS_CLK = 32; // assigns the SAS rotary encoder CLK output ("A") to pin 32
const int SAS_DT = 33; // assigns the SAS rotary encoder DT output ("B") to pin 33
const int SAS_SWITCH = 34; // assigns the SAS switch output to pin 34
const int RCS_SWITCH = 35; // assigns the RCS switch output to pin 35
const int BRAKES_SWITCH = 36; // assigns the Brakes switch output to pin 36
const int GEAR_SWITCH = 37; // assigns the Gear switch output to pin 37
const int LIGHTS_SWITCH = 38; // assigns the Lights switch output to pin 38
const int ABORT_BTN = 39; // assigns the Abort switch output to pin 38
const int AG_01 = 41; // assigns the Action Group 1 switch output to pin 41
const int AG_02 = 42; // assigns the Action Group 2 switch output to pin 42
const int AG_03 = 43; // assigns the Action Group 3 switch output to pin 43
const int AG_04 = 44; // assigns the Action Group 4 switch output to pin 44
const int AG_05 = 45; // assigns the Action Group 5 switch output to pin 45
const int AG_06 = 46; // assigns the Action Group 6 switch output to pin 46
const int AG_07 = 47; // assigns the Action Group 7 switch output to pin 47
const int AG_08 = 48; // assigns the Action Group 8 switch output to pin 48
const int AG_09 = 49; // assigns the Action Group 9 switch output to pin 49
const int AG_10 = 50; // assigns the Action Group 10 switch output to pin 50

int sas_Counter = 1; // initializes SAS Mode counter value variable at 1
int currentSASStateCLK;
int lastSASStateCLK;
int throt_Counter = 0; // initializes Throttle counter value variable at 0
int currentThrotStateCLK;
int lastThrotStateCLK;
int rot_X_Read;
int rot_Y_Read;
int rot_Z_Read;
int rot_X_Mapped;
int rot_Y_Mapped;
int rot_Z_Mapped;
int trans_X_Read;
int trans_Y_Read;
int trans_Z_Read;
int trans_X_Mapped;
int trans_Y_Mapped;
int trans_Z_Mapped;
int debounce_Time = 25;

KerbalSimpit mySimpit(Serial);

ezButton buttonSTAGE(STAGE_BTN);
ezButton buttonTHROT(THROT_BTN);
ezButton buttonTHROT_CUT(THROT_CUT);
ezButton buttonSAS(SAS_SWITCH);
ezButton buttonRCS(RCS_SWITCH);
ezButton buttonBRAKES(BRAKES_SWITCH);
ezButton buttonGEAR(GEAR_SWITCH);
ezButton buttonLIGHTS(LIGHTS_SWITCH);
ezButton buttonABORT(ABORT_BTN);
ezButton buttonAG_01(AG_01);
ezButton buttonAG_02(AG_02);
ezButton buttonAG_03(AG_03);
ezButton buttonAG_04(AG_04);
ezButton buttonAG_05(AG_05);
ezButton buttonAG_06(AG_06);
ezButton buttonAG_07(AG_07);
ezButton buttonAG_08(AG_08);
ezButton buttonAG_09(AG_09);
ezButton buttonAG_10(AG_10);

Rotary throtRotary = Rotary(THROT_DT, THROT_CLK);
Rotary sasRotary = Rotary(SAS_DT, SAS_CLK);

void setup() {
  Serial.begin(115200); // begins the serial connection to the computer through USB
  pinMode(ROT_X, INPUT); // defines inputs and outputs on Arduino pins
  pinMode(ROT_Y, INPUT);
  pinMode(ROT_Z, INPUT);
  pinMode(TRANS_X, INPUT);
  pinMode(TRANS_Y, INPUT);
  pinMode(TRANS_Z, INPUT);
  pinMode(STAGE_BTN, INPUT_PULLUP);
  pinMode(SAS_LED, OUTPUT); 
  pinMode(RCS_LED, OUTPUT);
  pinMode(BRAKES_LED, OUTPUT);
  pinMode(GEAR_LED, OUTPUT);
  pinMode(LIGHTS_LED, OUTPUT); 
  pinMode(SAS_CLK, INPUT);
  pinMode(SAS_DT, INPUT);
  pinMode(SAS_SWITCH, INPUT);
  pinMode(THROT_CLK, INPUT);
  pinMode(THROT_DT, INPUT);
  pinMode(THROT_BTN, INPUT);
  pinMode(SAS_SWITCH, INPUT_PULLUP);
  pinMode(RCS_SWITCH, INPUT_PULLUP);
  pinMode(BRAKES_SWITCH, INPUT_PULLUP);
  pinMode(GEAR_SWITCH, INPUT_PULLUP);
  pinMode(LIGHTS_SWITCH, INPUT_PULLUP);
  pinMode(ABORT_BTN, INPUT_PULLUP);
  pinMode(AG_01, INPUT_PULLUP);
  pinMode(AG_02, INPUT_PULLUP);
  pinMode(AG_03, INPUT_PULLUP);
  pinMode(AG_04, INPUT_PULLUP);
  pinMode(AG_05, INPUT_PULLUP);
  pinMode(AG_06, INPUT_PULLUP);
  pinMode(AG_07, INPUT_PULLUP);
  pinMode(AG_08, INPUT_PULLUP);
  pinMode(AG_09, INPUT_PULLUP);
  pinMode(AG_10, INPUT_PULLUP);  

  buttonSTAGE.setDebounceTime(debounce_Time); // sets debounce times for buttons
  buttonTHROT.setDebounceTime(debounce_Time);
  buttonTHROT_CUT.setDebounceTime(debounce_Time);
  buttonSAS.setDebounceTime(debounce_Time);
  buttonRCS.setDebounceTime(debounce_Time);
  buttonBRAKES.setDebounceTime(debounce_Time);
  buttonGEAR.setDebounceTime(debounce_Time);
  buttonLIGHTS.setDebounceTime(debounce_Time);
  buttonABORT.setDebounceTime(debounce_Time);
  buttonAG_01.setDebounceTime(debounce_Time);
  buttonAG_02.setDebounceTime(debounce_Time);
  buttonAG_03.setDebounceTime(debounce_Time);
  buttonAG_04.setDebounceTime(debounce_Time);
  buttonAG_05.setDebounceTime(debounce_Time);
  buttonAG_06.setDebounceTime(debounce_Time);
  buttonAG_07.setDebounceTime(debounce_Time);
  buttonAG_08.setDebounceTime(debounce_Time);
  buttonAG_09.setDebounceTime(debounce_Time);
  buttonAG_10.setDebounceTime(debounce_Time);

  digitalWrite(SAS_LED, HIGH); // turns on all the LEDs while the handshake process is happening
  digitalWrite(RCS_LED, HIGH);
  digitalWrite(BRAKES_LED, HIGH);
  digitalWrite(GEAR_LED, HIGH);
  digitalWrite(LIGHTS_LED, HIGH);
  while (!mySimpit.init()) { // initializes (handshakes) with Simpit mod
    delay(100);
  }
  digitalWrite(SAS_LED, LOW); // turns off all the LEDs once the handshake process is complete
  digitalWrite(RCS_LED, LOW);
  digitalWrite(BRAKES_LED, LOW);
  digitalWrite(GEAR_LED, LOW);
  digitalWrite(LIGHTS_LED, LOW);

  mySimpit.inboundHandler(messageHandler); // declares the message handler to read incoming messages from Simpit mod
  mySimpit.registerChannel(ACTIONSTATUS_MESSAGE); // subscribes to the Action Status message channel
  mySimpit.registerChannel(ROTATION_MESSAGE); // subscribes to the Rotation message channel
  mySimpit.registerChannel(TRANSLATION_MESSAGE); // subscribes to the Translation message channel
}

void loop() {
  mySimpit.update(); // necessary updates and loops for called functions
  buttonSTAGE.loop();
  buttonTHROT.loop();
  buttonTHROT_CUT.loop();
  buttonSAS.loop();
  buttonRCS.loop();
  buttonBRAKES.loop();
  buttonGEAR.loop();
  buttonLIGHTS.loop();
  buttonABORT.loop();
  buttonAG_01.loop();
  buttonAG_02.loop();
  buttonAG_03.loop();
  buttonAG_04.loop();
  buttonAG_05.loop();
  buttonAG_06.loop();
  buttonAG_07.loop();
  buttonAG_08.loop();
  buttonAG_09.loop();
  buttonAG_10.loop();  

  throt_Counter = constrain(throt_Counter, 0, 32767); // sets upper and lower limits for counter variables for rotary encoders
  sas_Counter = constrain(sas_Counter, 1, 10);

  rot_X_Read = analogRead(ROT_X); // takes a reading for the X-axis; from testing determined X-min = 330, X-mid = 505, X-max = 693
  if (rot_X_Read < 510 && rot_X_Read > 500) { // determines if the X-axis pot is in the middle deadzone to eliminate jitter
    rot_X_Mapped = 0;
  }
  if (rot_X_Read <= 500) { // determines if X-axis pot is in the negative portion of its motion
    rot_X_Mapped = map(rot_X_Read, 330, 500, -32768, 0); // sets the mapping for the negative portion of the axis
  }
  if (rot_X_Read >= 510) { // determined if X-axis pot is in the positive portion of its motion
    rot_X_Mapped = map(rot_X_Read, 510, 693, 0, 32767); // sets the mapping for the positive portion of the axis
  }

  rot_X_Mapped = constrain(rot_X_Mapped, -32768, 32767); // constrains the mapped value of the X-axis reading to valid results
  myRotation.mask = 2; // applies the bitmask required to only send roll information to Simpit
  myRotation.roll = rot_X_Mapped; // applies the X-axis value as the rotation roll value
  mySimpit.send(ROTATION_MESSAGE, myRotation); // sends the roll value to Simpit
  delay(1);  
  
  rot_Y_Read = analogRead(ROT_Y);
  if (rot_Y_Read < 518 && rot_Y_Read > 508) {
    rot_Y_Mapped = 0;
  }
  if (rot_Y_Read <= 508) {
    rot_Y_Mapped = map(rot_Y_Read, 344, 508, -32768, 0);
  }
  if (rot_Y_Read >= 518) {
    rot_Y_Mapped = map(rot_Y_Read, 518, 680, 0, 32767);
  }

  rot_Y_Mapped = constrain(rot_Y_Mapped, -32768, 32767);
  myRotation.mask = 1;
  myRotation.pitch = rot_Y_Mapped;
  mySimpit.send(ROTATION_MESSAGE, myRotation);
  delay(1);

  rot_Z_Read = analogRead(ROT_Z);
  if (rot_Z_Read < 520 && rot_Z_Read > 510) {
    rot_Z_Mapped = 0;
  }
  if (rot_Z_Read <= 510) {
    rot_Z_Mapped = map(rot_Z_Read, 296, 510, -32768, 0);
  }
  if (rot_Z_Read >= 520) {
    rot_Z_Mapped = map(rot_Z_Read, 520, 733, 0, 32767);
  }

  rot_Z_Mapped = constrain(rot_Z_Mapped, -32768, 32767);
  myRotation.mask = 4;
  myRotation.yaw = rot_Z_Mapped;
  mySimpit.send(ROTATION_MESSAGE, myRotation);
  delay(1);

  trans_X_Read = analogRead(TRANS_X); // takes a reading for the X-axis; from testing determined X-min = 330, X-mid = 505, X-max = 693
  if (trans_X_Read < 510 && trans_X_Read > 500) { // determines if the X-axis pot is in the middle deadzone to eliminate jitter
    trans_X_Mapped = 0;
  }
  if (trans_X_Read <= 500) { // determines if X-axis pot is in the negative portion of its motion
    trans_X_Mapped = map(trans_X_Read, 330, 500, -32768, 0); // sets the mapping for the negative portion of the axis
  }
  if (trans_X_Read >= 510) { // determined if X-axis pot is in the positive portion of its motion
    trans_X_Mapped = map(trans_X_Read, 510, 693, 0, 32767); // sets the mapping for the positive portion of the axis
  }

  trans_X_Mapped = constrain(trans_X_Mapped, -32768, 32767); // constrains the mapped value of the X-axis reading to valid results
  myTranslation.mask = 1; // applies the bitmask required to only send X information to Simpit
  myTranslation.X = trans_X_Mapped; // applies the X-axis value as the X translation value
  mySimpit.send(TRANSLATION_MESSAGE, myTranslation); // sends the x translation value to Simpit
  delay(1);  
  
  trans_Y_Read = analogRead(TRANS_Y);
  if (trans_Y_Read < 518 && trans_Y_Read > 508) {
    trans_Y_Mapped = 0;
  }
  if (trans_Y_Read <= 508) {
    trans_Y_Mapped = map(trans_Y_Read, 344, 508, -32768, 0);
  }
  if (trans_Y_Read >= 518) {
    trans_Y_Mapped = map(trans_Y_Read, 518, 680, 0, 32767);
  }

  trans_Y_Mapped = constrain(trans_Y_Mapped, -32768, 32767);
  myTranslation.mask = 2;
  myTranslation.Y = trans_Y_Mapped;
  mySimpit.send(TRANSLATION_MESSAGE, myTranslation);
  delay(1);

  trans_Z_Read = analogRead(TRANS_Z);
  if (trans_Z_Read < 520 && trans_Z_Read > 510) {
    trans_Z_Mapped = 0;
  }
  if (trans_Z_Read <= 510) {
    trans_Z_Mapped = map(trans_Z_Read, 296, 510, -32768, 0);
  }
  if (trans_Z_Read >= 520) {
    trans_Z_Mapped = map(trans_Z_Read, 520, 733, 0, 32767);
  }

  trans_Z_Mapped = constrain(trans_Z_Mapped, -32768, 32767);
  myTranslation.mask = 4;
  myTranslation.Z = trans_Z_Mapped;
  mySimpit.send(TRANSLATION_MESSAGE, myTranslation);
  delay(1);

  if (buttonSTAGE.isPressed()) {
    mySimpit.toggleAction(STAGE_ACTION);
  }

  if (buttonTHROT.isPressed()) {
    throt_Counter = 32767;
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter);
  }

  if (buttonTHROT_CUT.isPressed()) {
    throt_Counter = 0;
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter);
  }
  
  if (buttonSAS.isPressed()) {
    mySimpit.toggleAction(SAS_ACTION);
  }
  
  if (buttonRCS.isPressed()) { // declares action if the RCS button is pressed (toggles RCS on/off)
    mySimpit.toggleAction(RCS_ACTION);
  }
  if (buttonBRAKES.isPressed()) { // declares action if the Brakes button is pressed (toggles Brakes on/off)
    mySimpit.toggleAction(BRAKES_ACTION);
  }
  if (buttonGEAR.isPressed()) { // declares action if the Gear button is pressed (toggles Gear up/down)
    mySimpit.toggleAction(GEAR_ACTION);
  }
  if (buttonLIGHTS.isPressed()) { // declares action if the Lights button is pressed (toggles Lights on/off)
    mySimpit.toggleAction(LIGHT_ACTION);
  }

  if (buttonABORT.isPressed()) { // declares action if the Abort button is pressed (activates the Abort action group)
    mySimpit.toggleAction(ABORT_ACTION);
  }

  if (buttonAG_01.isPressed()) { // declares action if the Action Group 1 is pressed (toggles Action Group 1)
    mySimpit.toggleCAG(1); 
  }

  if (buttonAG_02.isPressed()) { // declares action if the Action Group 2 is pressed (toggles Action Group 2)
    mySimpit.toggleCAG(2); 
  }

  if (buttonAG_03.isPressed()) { // declares action if the Action Group 3 is pressed (toggles Action Group 3)
    mySimpit.toggleCAG(3); 
  }

  if (buttonAG_04.isPressed()) { // declares action if the Action Group 4 is pressed (toggles Action Group 4)
    mySimpit.toggleCAG(4); 
  }

  if (buttonAG_05.isPressed()) { // declares action if the Action Group 5 is pressed (toggles Action Group 5)
    mySimpit.toggleCAG(5); 
  }

  if (buttonAG_05.isPressed()) { // declares action if the Action Group 6 is pressed (toggles Action Group 6)
    mySimpit.toggleCAG(6); 
  }

  if (buttonAG_07.isPressed()) { // declares action if the Action Group 7 is pressed (toggles Action Group 7)
    mySimpit.toggleCAG(7); 
  }

  if (buttonAG_08.isPressed()) { // declares action if the Action Group 8 is pressed (toggles Action Group 8)
    mySimpit.toggleCAG(8); 
  }

  if (buttonAG_09.isPressed()) { // declares action if the Action Group 9 is pressed (toggles Action Group 9)
    mySimpit.toggleCAG(9); 
  }

  if (buttonAG_10.isPressed()) { // declares action if the Action Group 10 is pressed (toggles Action Group 10)
    mySimpit.toggleCAG(10); 
  }

  unsigned char throtResult = throtRotary.process();

  if (throtResult == DIR_CW && (throt_Counter <= 32349)) {
    throt_Counter = throt_Counter + 327; // increments the Throttle counter by ~1%
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter); // sends the new throttle setting to Simpit
  }
  if (throtResult == DIR_CCW && (throt_Counter >= 327)) {
    throt_Counter = throt_Counter - 327; // decrements the Throttle counter by ~1%
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter); // sends the new throttle setting to Simpit
  }

  unsigned char sasResult = sasRotary.process();

  if (sasResult == DIR_CW) {
    sas_Counter++; // increments the SAS counter by 1
    mySimpit.send(28, (unsigned char*) &sas_Counter, 1); // sends the new SAS mode setting to Simpit 
  }

  if (sasResult == DIR_CCW) {
    sas_Counter--; // decrements the SAS Counter by 1
    mySimpit.send(28, (unsigned char*) &sas_Counter, 1); // sends the new SAS mode setting to Simpit
  }


} 
  
void messageHandler(byte messageType, byte msg[], byte msgSize) { // sets up the message handler to receive messages from Simpit
  switch(messageType) {
  case ACTIONSTATUS_MESSAGE: // defines the set of actions for messages coming from ACTIONSTATUS_MESSAGE
  byte actions = msg[0]; // assigns the ACTIONSTATUS_MESSAGE to the variable actions
    if (actions & SAS_ACTION) { // checks to see if SAS is turned on
      digitalWrite(SAS_LED, HIGH); // turns on the SAS LED indicator if SAS is on
    } else {
      digitalWrite(SAS_LED, LOW); // set SAS LED indicator off if SAS is off
    }
    if (actions & GEAR_ACTION) { // checks to see if Gear is down
      digitalWrite(GEAR_LED, HIGH); // turns on Gear LED indicator if gear is down
    } else {
      digitalWrite(GEAR_LED, LOW); // set the Gear indicator off if gear is up
    }
    if (actions & LIGHT_ACTION) { // checks to see if Lights are on
      digitalWrite(LIGHTS_LED, HIGH); // turns on Lights LED indicator if lights are on
    } else {
      digitalWrite(LIGHTS_LED, LOW); // set Lights indicator off if lights are off
    }
    if (actions & RCS_ACTION) { // checks to see if RCS is active
      digitalWrite(RCS_LED, HIGH); // turns on the RCS LED indicator if RCS is active
    } else {
      digitalWrite(RCS_LED, LOW); // set RCS indicator off if RCS is inactive
    }
    if (actions & BRAKES_ACTION) { // checks to see if Brakes are on
      digitalWrite(BRAKES_LED, HIGH); // turns on Brakes LED indicator if brakes are on
    } else {
      digitalWrite(BRAKES_LED, LOW); // set Brakes indicator off if brakes are off
    }
    break;
}
}

 

UPDATE: Here is the joystick portion of the code that I finally got to work:

  int rot_X_Read = analogRead(ROT_X); // takes a reading for the X-axis; from testing determined X-min = 330, X-mid = 505, X-max = 693
  if (rot_X_Read < 510 && rot_X_Read > 500) { // determines if the X-axis pot is in the middle deadzone to eliminate jitter
    rot_X_Mapped = 0;
  }
  if (rot_X_Read <= 500) { // determines if X-axis pot is in the negative portion of its motion
    rot_X_Mapped = map(rot_X_Read, 330, 500, -32768, 0); // sets the mapping for the negative portion of the axis
  }
  if (rot_X_Read >= 510) { // determined if X-axis pot is in the positive portion of its motion
    rot_X_Mapped = map(rot_X_Read, 510, 693, 0, 32767); // sets the mapping for the positive portion of the axis
  }

  int rot_Y_Read = analogRead(ROT_Y);
  if (rot_Y_Read < 518 && rot_Y_Read > 508) {
    rot_Y_Mapped = 0;
  }
  if (rot_Y_Read <= 508) {
    rot_Y_Mapped = map(rot_Y_Read, 344, 508, -32768, 0);
  }
  if (rot_Y_Read >= 518) {
    rot_Y_Mapped = map(rot_Y_Read, 518, 680, 0, 32767);
  }

  int rot_Z_Read = analogRead(ROT_Z);
  if (rot_Z_Read < 520 && rot_Z_Read > 510) {
    rot_Z_Mapped = 0;
  }
  if (rot_Z_Read <= 510) {
    rot_Z_Mapped = map(rot_Z_Read, 296, 510, -32768, 0);
  }
  if (rot_Z_Read >= 520) {
    rot_Z_Mapped = map(rot_Z_Read, 520, 733, 0, 32767);
  }

  myRotation.mask = 1|2|4;
  myRotation.pitch = rot_Y_Mapped;
  myRotation.roll = rot_X_Mapped; // applies the X-axis value as the rotation roll value
  myRotation.yaw = rot_Z_Mapped;
  mySimpit.send(ROTATION_MESSAGE, myRotation); // sends the roll value to Simpit
 
  int trans_X_Read = analogRead(TRANS_X); // takes a reading for the X-axis; from testing determined X-min = 330, X-mid = 505, X-max = 693
  if (trans_X_Read < 510 && trans_X_Read > 500) { // determines if the X-axis pot is in the middle deadzone to eliminate jitter
    trans_X_Mapped = 0;
  }
  if (trans_X_Read <= 500) { // determines if X-axis pot is in the negative portion of its motion
    trans_X_Mapped = map(trans_X_Read, 330, 500, -32768, 0); // sets the mapping for the negative portion of the axis
  }
  if (trans_X_Read >= 510) { // determined if X-axis pot is in the positive portion of its motion
    trans_X_Mapped = map(trans_X_Read, 510, 693, 0, 32767); // sets the mapping for the positive portion of the axis
  }

  int trans_Y_Read = analogRead(TRANS_Y);
  if (trans_Y_Read < 518 && trans_Y_Read > 508) {
    trans_Y_Mapped = 0;
  }
  if (trans_Y_Read <= 508) {
    trans_Y_Mapped = map(trans_Y_Read, 344, 508, -32768, 0);
  }
  if (trans_Y_Read >= 518) {
    trans_Y_Mapped = map(trans_Y_Read, 518, 680, 0, 32767);
  }

  int trans_Z_Read = analogRead(TRANS_Z);
  if (trans_Z_Read < 520 && trans_Z_Read > 510) {
    trans_Z_Mapped = 0;
  }
  if (trans_Z_Read <= 510) {
    trans_Z_Mapped = map(trans_Z_Read, 296, 510, -32768, 0);
  }
  if (trans_Z_Read >= 520) {
    trans_Z_Mapped = map(trans_Z_Read, 520, 733, 0, 32767);
  }
  
  myTranslation.mask = 1|2|4;
  myTranslation.X = trans_X_Mapped; // applies the X-axis value as the X translation value
  myTranslation.Y = trans_Y_Mapped;
  myTranslation.Z = trans_Z_Mapped;
  mySimpit.send(TRANSLATION_MESSAGE, myTranslation); // sends the x translation value to Simpit

 

Edited by PSU_Jedi
Update
Link to comment
Share on other sites

  • 4 weeks later...

Back after an extended break from all things Kerbal with some small maintenance releases.

Arduino library is now up to 1.4.0. This release has some minor changes to the function definitions, to work with recent Arduino releases. I've tested with Arduino 1.8.13, on macOS.

Kerbal Simpit plugin has been updated to (coincidentally) 1.4.0 as well. This release builds against KSP 1.9.1, and removes a lot of old code that's now bundled with Unity.

CKAN and AVC should find the new plugin now. Arduino is waiting for a library manager rescan, and I promise for sure I'll actually check to make sure it updated tomorrow. :(

No real changes apart from just blowing off the dust, but I expect to find some time to catch up a little more in coming weeks.

Link to comment
Share on other sites

  • 2 weeks later...
On 5/19/2020 at 9:51 PM, LRTNZ said:

@TygurDuck The big issue is that you are running blocking code - this is where you prevent the processor from doing anything else, as it is stuck in a delay.

@Mofates your demo code using the boolean operators and the timer code is how I would recommend to do it. There is one really big issue though - that is, you have about 65 seconds of useful time to use it, before you will probably encounter some really weird timing bugs, when lastBlink rolls over back to zero. An unsigned int on an arduino has a maximum value of 65,535. As you are storing milliseconds, that comes to just over a minute, until it rolls over. You need to be using an unsigned long, which can hold a maximum value of 4,294,967,295 - if you manage to exceed that, you really need to stop playing :)

@LRTNZyes, of course it should have been an unsigned long!  I have no idea what part of my brain saw a squirrel while I was writing that.  : )

Edited by Mofates
Fixed @Tag
Link to comment
Share on other sites

On 12/23/2019 at 6:27 AM, TygurDuck said:

Do I have to use Altnerate Resource Panel for it to work at all?

To answer a fairly old question: yes, without ARP the resource channels will not be sent at all. I'll make sure the documentation is clearer on this. 

Getting stage and vessel resources isn't exactly a trivial thing - there's some calculation involved. Rather than reinvent the wheel I chose to use the very tidy API that Alternate Resources Panel provides for its data.

(for bonus points, even though simpit only supports stock resources, using ARP makes it really really easy to write a companion plugin for modded resources) 

Link to comment
Share on other sites

My control box becomes unresponsive ~20-30 minutes into gameplay, or sometimes it seems shorter if I'm using the analog joysticks a lot. Given that, it seems that maybe the information exchange between the Mega board and the game is getting overloaded? Also, the rotary encoder for my SAS mode selector isn't working anymore. Could be that the wiring came loose (hard to check without pulling the whole panel off the control box), but if you see anything in that part of the code that would give you pause, let me know please. BTW, I also have a Leonardo emulating keyboard commands into KSP running on the same control box. It continues to function indefinitely, even after the Mega quits functioning.

Code is below...any help would be appreciated! 

// Sets up the Arduino Mega to handle the analog joysticks, Action Groups, switches and buttons
#include <KerbalSimpit.h>
#include <KerbalSimpitMessageTypes.h>
#include <PayloadStructs.h>
#include <ezButton.h> // loads ezButton library for button debounce
#include <Rotary.h>

rotationMessage myRotation;
translationMessage myTranslation;

const int ROT_X = A0; // assigns rotation joystick X-axis to pin Analog 0
const int ROT_Y = A1; // assigns rotation joystick Y-axis to pin Analog 1
const int ROT_Z = A2; // assigns rotation joystick Z-axis to pin Analog 2
const int TRANS_X = A3; // assigns translation joystick X-axis to pin Analog 3
const int TRANS_Y = A4; // assigns translation joystick Y-axis to pin Analog 4
const int TRANS_Z = A5; // assigns translation joystick Z-axis to pin Analog 5
const int STAGE_BTN = 2; // assigns the Staging button to pin 2
const int RCS_LED = 3; // assigns the RCS indicator LED to pin 3
const int BRAKES_LED = 4; // assigns the Brakes indicator LED to pin 4
const int GEAR_LED = 5; // assigns the Landing Gear indicator LED to pin 5
const int LIGHTS_LED = 6; // assigns the Lights indicator LED to pin 6
const int SAS_LED = 7; // assigns the SAS indicator LED to pin 7
const int THROT_CLK = 22; // assigns the Throttle rotary encoder CLK output ("A") to pin 22
const int THROT_DT = 23; // assigns the Throttle rotary encoder DT output ("B") to pin 23
const int THROT_BTN = 24; // assigns the Throttle rotary encoder Switch output to pin 24
const int THROT_CUT = 25; // assigns the Throttle Cut button output to pin 25
const int SAS_CLK = 32; // assigns the SAS rotary encoder CLK output ("A") to pin 32
const int SAS_DT = 33; // assigns the SAS rotary encoder DT output ("B") to pin 33
const int SAS_SWITCH = 34; // assigns the SAS switch output to pin 34
const int RCS_SWITCH = 35; // assigns the RCS switch output to pin 35
const int BRAKES_SWITCH = 36; // assigns the Brakes switch output to pin 36
const int GEAR_SWITCH = 37; // assigns the Gear switch output to pin 37
const int LIGHTS_SWITCH = 38; // assigns the Lights switch output to pin 38
const int ABORT_BTN = 39; // assigns the Abort switch output to pin 38
const int AG_01 = 41; // assigns the Action Group 1 switch output to pin 41
const int AG_02 = 42; // assigns the Action Group 2 switch output to pin 42
const int AG_03 = 43; // assigns the Action Group 3 switch output to pin 43
const int AG_04 = 44; // assigns the Action Group 4 switch output to pin 44
const int AG_05 = 45; // assigns the Action Group 5 switch output to pin 45
const int AG_06 = 46; // assigns the Action Group 6 switch output to pin 46
const int AG_07 = 47; // assigns the Action Group 7 switch output to pin 47
const int AG_08 = 48; // assigns the Action Group 8 switch output to pin 48
const int AG_09 = 49; // assigns the Action Group 9 switch output to pin 49
const int AG_10 = 50; // assigns the Action Group 10 switch output to pin 50

int sas_Counter = 1; // initializes SAS Mode counter value variable at 1
int currentSASStateCLK;
int lastSASStateCLK;
int throt_Counter = 0; // initializes Throttle counter value variable at 0
int currentThrotStateCLK;
int lastThrotStateCLK;
int rot_X_Read;
int rot_Y_Read;
int rot_Z_Read;
int rot_X_Mapped;
int rot_Y_Mapped;
int rot_Z_Mapped;
int trans_X_Read;
int trans_Y_Read;
int trans_Z_Read;
int trans_X_Mapped;
int trans_Y_Mapped;
int trans_Z_Mapped;
int debounce_Time = 25;

KerbalSimpit mySimpit(Serial);

ezButton buttonSTAGE(STAGE_BTN);
ezButton buttonTHROT(THROT_BTN);
ezButton buttonTHROT_CUT(THROT_CUT);
ezButton buttonSAS(SAS_SWITCH);
ezButton buttonRCS(RCS_SWITCH);
ezButton buttonBRAKES(BRAKES_SWITCH);
ezButton buttonGEAR(GEAR_SWITCH);
ezButton buttonLIGHTS(LIGHTS_SWITCH);
ezButton buttonABORT(ABORT_BTN);
ezButton buttonAG_01(AG_01);
ezButton buttonAG_02(AG_02);
ezButton buttonAG_03(AG_03);
ezButton buttonAG_04(AG_04);
ezButton buttonAG_05(AG_05);
ezButton buttonAG_06(AG_06);
ezButton buttonAG_07(AG_07);
ezButton buttonAG_08(AG_08);
ezButton buttonAG_09(AG_09);
ezButton buttonAG_10(AG_10);

Rotary throtRotary = Rotary(THROT_DT, THROT_CLK);
Rotary sasRotary = Rotary(SAS_DT, SAS_CLK);

void setup() {
  Serial.begin(115200); // begins the serial connection to the computer through USB
  pinMode(ROT_X, INPUT); // defines inputs and outputs on Arduino pins
  pinMode(ROT_Y, INPUT);
  pinMode(ROT_Z, INPUT);
  pinMode(TRANS_X, INPUT);
  pinMode(TRANS_Y, INPUT);
  pinMode(TRANS_Z, INPUT);
  pinMode(STAGE_BTN, INPUT_PULLUP);
  pinMode(SAS_LED, OUTPUT); 
  pinMode(RCS_LED, OUTPUT);
  pinMode(BRAKES_LED, OUTPUT);
  pinMode(GEAR_LED, OUTPUT);
  pinMode(LIGHTS_LED, OUTPUT); 
  pinMode(SAS_CLK, INPUT);
  pinMode(SAS_DT, INPUT);
  pinMode(SAS_SWITCH, INPUT);
  pinMode(THROT_CLK, INPUT);
  pinMode(THROT_DT, INPUT);
  pinMode(THROT_BTN, INPUT);
  pinMode(SAS_SWITCH, INPUT_PULLUP);
  pinMode(RCS_SWITCH, INPUT_PULLUP);
  pinMode(BRAKES_SWITCH, INPUT_PULLUP);
  pinMode(GEAR_SWITCH, INPUT_PULLUP);
  pinMode(LIGHTS_SWITCH, INPUT_PULLUP);
  pinMode(ABORT_BTN, INPUT_PULLUP);
  pinMode(AG_01, INPUT_PULLUP);
  pinMode(AG_02, INPUT_PULLUP);
  pinMode(AG_03, INPUT_PULLUP);
  pinMode(AG_04, INPUT_PULLUP);
  pinMode(AG_05, INPUT_PULLUP);
  pinMode(AG_06, INPUT_PULLUP);
  pinMode(AG_07, INPUT_PULLUP);
  pinMode(AG_08, INPUT_PULLUP);
  pinMode(AG_09, INPUT_PULLUP);
  pinMode(AG_10, INPUT_PULLUP);  

  buttonSTAGE.setDebounceTime(debounce_Time); // sets debounce times for buttons
  buttonTHROT.setDebounceTime(debounce_Time);
  buttonTHROT_CUT.setDebounceTime(debounce_Time);
  buttonSAS.setDebounceTime(debounce_Time);
  buttonRCS.setDebounceTime(debounce_Time);
  buttonBRAKES.setDebounceTime(debounce_Time);
  buttonGEAR.setDebounceTime(debounce_Time);
  buttonLIGHTS.setDebounceTime(debounce_Time);
  buttonABORT.setDebounceTime(debounce_Time);
  buttonAG_01.setDebounceTime(debounce_Time);
  buttonAG_02.setDebounceTime(debounce_Time);
  buttonAG_03.setDebounceTime(debounce_Time);
  buttonAG_04.setDebounceTime(debounce_Time);
  buttonAG_05.setDebounceTime(debounce_Time);
  buttonAG_06.setDebounceTime(debounce_Time);
  buttonAG_07.setDebounceTime(debounce_Time);
  buttonAG_08.setDebounceTime(debounce_Time);
  buttonAG_09.setDebounceTime(debounce_Time);
  buttonAG_10.setDebounceTime(debounce_Time);

  digitalWrite(SAS_LED, HIGH); // turns on all the LEDs while the handshake process is happening
  digitalWrite(RCS_LED, HIGH);
  digitalWrite(BRAKES_LED, HIGH);
  digitalWrite(GEAR_LED, HIGH);
  digitalWrite(LIGHTS_LED, HIGH);
  while (!mySimpit.init()) { // initializes (handshakes) with Simpit mod
    delay(100);
  }
  digitalWrite(SAS_LED, LOW); // turns off all the LEDs once the handshake process is complete
  digitalWrite(RCS_LED, LOW);
  digitalWrite(BRAKES_LED, LOW);
  digitalWrite(GEAR_LED, LOW);
  digitalWrite(LIGHTS_LED, LOW);

  mySimpit.inboundHandler(messageHandler); // declares the message handler to read incoming messages from Simpit mod
  mySimpit.registerChannel(ACTIONSTATUS_MESSAGE); // subscribes to the Action Status message channel
  mySimpit.registerChannel(ROTATION_MESSAGE); // subscribes to the Rotation message channel
  mySimpit.registerChannel(TRANSLATION_MESSAGE); // subscribes to the Translation message channel
  mySimpit.registerChannel(SCENE_CHANGE_MESSAGE); // subscribes to the Scene Change message channel
}

void loop() {
  mySimpit.update(); // necessary updates and loops for called functions
  buttonSTAGE.loop();
  buttonTHROT.loop();
  buttonTHROT_CUT.loop();
  buttonSAS.loop();
  buttonRCS.loop();
  buttonBRAKES.loop();
  buttonGEAR.loop();
  buttonLIGHTS.loop();
  buttonABORT.loop();
  buttonAG_01.loop();
  buttonAG_02.loop();
  buttonAG_03.loop();
  buttonAG_04.loop();
  buttonAG_05.loop();
  buttonAG_06.loop();
  buttonAG_07.loop();
  buttonAG_08.loop();
  buttonAG_09.loop();
  buttonAG_10.loop();  

  throt_Counter = constrain(throt_Counter, 0, 32767); // sets upper and lower limits for counter variables for rotary encoders
  sas_Counter = constrain(sas_Counter, 1, 10);

  int rot_X_Read = analogRead(ROT_X); // takes a reading for the X-axis; from testing determined X-min = 330, X-mid = 505, X-max = 693
  if (rot_X_Read < 510 && rot_X_Read > 500) { // determines if the X-axis pot is in the middle deadzone to eliminate jitter
    rot_X_Mapped = 0;
  }
  if (rot_X_Read <= 500) { // determines if X-axis pot is in the negative portion of its motion
    rot_X_Mapped = map(rot_X_Read, 330, 500, -32768, 0); // sets the mapping for the negative portion of the axis
  }
  if (rot_X_Read >= 510) { // determined if X-axis pot is in the positive portion of its motion
    rot_X_Mapped = map(rot_X_Read, 510, 693, 0, 32767); // sets the mapping for the positive portion of the axis
  }

  int rot_Y_Read = analogRead(ROT_Y);
  if (rot_Y_Read < 518 && rot_Y_Read > 508) {
    rot_Y_Mapped = 0;
  }
  if (rot_Y_Read <= 508) {
    rot_Y_Mapped = map(rot_Y_Read, 344, 508, -32768, 0);
  }
  if (rot_Y_Read >= 518) {
    rot_Y_Mapped = map(rot_Y_Read, 518, 680, 0, 32767);
  }

  int rot_Z_Read = analogRead(ROT_Z);
  if (rot_Z_Read < 520 && rot_Z_Read > 510) {
    rot_Z_Mapped = 0;
  }
  if (rot_Z_Read <= 510) {
    rot_Z_Mapped = map(rot_Z_Read, 296, 510, -32768, 0);
  }
  if (rot_Z_Read >= 520) {
    rot_Z_Mapped = map(rot_Z_Read, 520, 733, 0, 32767);
  }

  myRotation.mask = 1|2|4;
  myRotation.pitch = rot_Y_Mapped;
  myRotation.roll = rot_X_Mapped; // applies the X-axis value as the rotation roll value
  myRotation.yaw = rot_Z_Mapped;
  mySimpit.send(ROTATION_MESSAGE, myRotation); // sends the rotation value to Simpit
 
  int trans_X_Read = analogRead(TRANS_X); // takes a reading for the X-axis; from testing determined X-min = 330, X-mid = 505, X-max = 693
  if (trans_X_Read < 510 && trans_X_Read > 500) { // determines if the X-axis pot is in the middle deadzone to eliminate jitter
    trans_X_Mapped = 0;
  }
  if (trans_X_Read <= 500) { // determines if X-axis pot is in the negative portion of its motion
    trans_X_Mapped = map(trans_X_Read, 330, 500, -32768, 0); // sets the mapping for the negative portion of the axis
  }
  if (trans_X_Read >= 510) { // determined if X-axis pot is in the positive portion of its motion
    trans_X_Mapped = map(trans_X_Read, 510, 693, 0, 32767); // sets the mapping for the positive portion of the axis
  }

  int trans_Y_Read = analogRead(TRANS_Y);
  if (trans_Y_Read < 518 && trans_Y_Read > 508) {
    trans_Y_Mapped = 0;
  }
  if (trans_Y_Read <= 508) {
    trans_Y_Mapped = map(trans_Y_Read, 344, 508, -32768, 0);
  }
  if (trans_Y_Read >= 518) {
    trans_Y_Mapped = map(trans_Y_Read, 518, 680, 0, 32767);
  }

  int trans_Z_Read = analogRead(TRANS_Z);
  if (trans_Z_Read < 520 && trans_Z_Read > 510) {
    trans_Z_Mapped = 0;
  }
  if (trans_Z_Read <= 510) {
    trans_Z_Mapped = map(trans_Z_Read, 296, 510, -32768, 0);
  }
  if (trans_Z_Read >= 520) {
    trans_Z_Mapped = map(trans_Z_Read, 520, 733, 0, 32767);
  }
  
  myTranslation.mask = 1|2|4;
  myTranslation.X = trans_X_Mapped; // applies the X-axis value as the X translation value
  myTranslation.Y = trans_Y_Mapped;
  myTranslation.Z = trans_Z_Mapped;
  mySimpit.send(TRANSLATION_MESSAGE, myTranslation); // sends the translation value to Simpit

  if (buttonSTAGE.isPressed()) {
    mySimpit.toggleAction(STAGE_ACTION);
  }

  if (buttonTHROT.isPressed()) {
    throt_Counter = 32767;
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter);
  }

  if (buttonTHROT_CUT.isPressed()) {
    throt_Counter = 0;
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter);
  }
  
  if (buttonSAS.isPressed()) {
    mySimpit.toggleAction(SAS_ACTION);
  }
  
  if (buttonRCS.isPressed()) { // declares action if the RCS button is pressed (toggles RCS on/off)
    mySimpit.toggleAction(RCS_ACTION);
  }
  if (buttonBRAKES.isPressed()) { // declares action if the Brakes button is pressed (toggles Brakes on/off)
    mySimpit.toggleAction(BRAKES_ACTION);
  }
  if (buttonGEAR.isPressed()) { // declares action if the Gear button is pressed (toggles Gear up/down)
    mySimpit.toggleAction(GEAR_ACTION);
  }
  if (buttonLIGHTS.isPressed()) { // declares action if the Lights button is pressed (toggles Lights on/off)
    mySimpit.toggleAction(LIGHT_ACTION);
  }

  if (buttonABORT.isPressed()) { // declares action if the Abort button is pressed (activates the Abort action group)
    mySimpit.toggleAction(ABORT_ACTION);
  }

  if (buttonAG_01.isPressed()) { // declares action if the Action Group 1 is pressed (toggles Action Group 1)
    mySimpit.toggleCAG(1); 
  }

  if (buttonAG_02.isPressed()) { // declares action if the Action Group 2 is pressed (toggles Action Group 2)
    mySimpit.toggleCAG(2); 
  }

  if (buttonAG_03.isPressed()) { // declares action if the Action Group 3 is pressed (toggles Action Group 3)
    mySimpit.toggleCAG(3); 
  }

  if (buttonAG_04.isPressed()) { // declares action if the Action Group 4 is pressed (toggles Action Group 4)
    mySimpit.toggleCAG(4); 
  }

  if (buttonAG_05.isPressed()) { // declares action if the Action Group 5 is pressed (toggles Action Group 5)
    mySimpit.toggleCAG(5); 
  }

  if (buttonAG_05.isPressed()) { // declares action if the Action Group 6 is pressed (toggles Action Group 6)
    mySimpit.toggleCAG(6); 
  }

  if (buttonAG_07.isPressed()) { // declares action if the Action Group 7 is pressed (toggles Action Group 7)
    mySimpit.toggleCAG(7); 
  }

  if (buttonAG_08.isPressed()) { // declares action if the Action Group 8 is pressed (toggles Action Group 8)
    mySimpit.toggleCAG(8); 
  }

  if (buttonAG_09.isPressed()) { // declares action if the Action Group 9 is pressed (toggles Action Group 9)
    mySimpit.toggleCAG(9); 
  }

  if (buttonAG_10.isPressed()) { // declares action if the Action Group 10 is pressed (toggles Action Group 10)
    mySimpit.toggleCAG(10); 
  }

  unsigned char throtResult = throtRotary.process();

  if (throtResult == DIR_CW && (throt_Counter <= 29487)) {
    throt_Counter = throt_Counter + 3276; // increments the Throttle counter by ~10%
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter); // sends the new throttle setting to Simpit
  }
  if (throtResult == DIR_CCW && (throt_Counter >= 3276)) {
    throt_Counter = throt_Counter - 3276; // decrements the Throttle counter by ~10%
    mySimpit.send(THROTTLE_MESSAGE, throt_Counter); // sends the new throttle setting to Simpit
  }

  unsigned char sasResult = sasRotary.process();

  if (sasResult == DIR_CW) {
    sas_Counter++; // increments the SAS counter by 1
    mySimpit.send(28, (unsigned char*) &sas_Counter, 1); // sends the new SAS mode setting to Simpit 
  }

  if (sasResult == DIR_CCW) {
    sas_Counter--; // decrements the SAS Counter by 1
    mySimpit.send(28, (unsigned char*) &sas_Counter, 1); // sends the new SAS mode setting to Simpit
  }
} 
  
void messageHandler(byte messageType, byte msg[], byte msgSize) { // sets up the message handler to receive messages from Simpit
  switch(messageType) {
  case ACTIONSTATUS_MESSAGE: // defines the set of actions for messages coming from ACTIONSTATUS_MESSAGE
  byte actions = msg[0]; // assigns the ACTIONSTATUS_MESSAGE to the variable actions
    if (actions & SAS_ACTION) { // checks to see if SAS is turned on
      digitalWrite(SAS_LED, HIGH); // turns on the SAS LED indicator if SAS is on
    } else {
      digitalWrite(SAS_LED, LOW); // set SAS LED indicator off if SAS is off
    }
    if (actions & GEAR_ACTION) { // checks to see if Gear is down
      digitalWrite(GEAR_LED, HIGH); // turns on Gear LED indicator if gear is down
    } else {
      digitalWrite(GEAR_LED, LOW); // set the Gear indicator off if gear is up
    }
    if (actions & LIGHT_ACTION) { // checks to see if Lights are on
      digitalWrite(LIGHTS_LED, HIGH); // turns on Lights LED indicator if lights are on
    } else {
      digitalWrite(LIGHTS_LED, LOW); // set Lights indicator off if lights are off
    }
    if (actions & RCS_ACTION) { // checks to see if RCS is active
      digitalWrite(RCS_LED, HIGH); // turns on the RCS LED indicator if RCS is active
    } else {
      digitalWrite(RCS_LED, LOW); // set RCS indicator off if RCS is inactive
    }
    if (actions & BRAKES_ACTION) { // checks to see if Brakes are on
      digitalWrite(BRAKES_LED, HIGH); // turns on Brakes LED indicator if brakes are on
    } else {
      digitalWrite(BRAKES_LED, LOW); // set Brakes indicator off if brakes are off
    }
    break;
  case SCENE_CHANGE_MESSAGE: // defines the set of actions for messages coming from SCENE-CHANGE-MESSAGE
  byte scene_state = msg[0]; // assigns the SCENE_CHANGE_MESSAGE to the variable scene_state
    if (scene_state & 0x01) { // checks to see if we are leaving the Flight scene
      digitalWrite(SAS_LED, LOW); // turns off all LED indicators is leaving the Flight scene
      digitalWrite(GEAR_LED, LOW);
      digitalWrite(LIGHTS_LED, LOW);
      digitalWrite(RCS_LED, LOW);
      digitalWrite(BRAKES_LED, LOW);
    }
    break;
}
}

 

Link to comment
Share on other sites

Hey mate. Sorry you've been having troubles. Let me start with the easiest stuff first:

19 hours ago, PSU_Jedi said:

Also, the rotary encoder for my SAS mode selector isn't working anymore. Could be that the wiring came loose (hard to check without pulling the whole panel off the control box), but if you see anything in that part of the code that would give you pause, let me know please.

I'm not familiar with rotary encoders at all tbh, but I assume you're using the library at https://github.com/brianlow/Rotary to work with it (that's the one I installed in my IDE to get your sketch to compile).

You're right, the first thing to check is that it actually works at all. If it were me I'd test it with one of the Rotary library examples - it looks like your code basically replicates what the Polling example does.

After that, I'd be thinking about whether or not turning rotary encoder events are just getting lost because your code is so busy doing other things that it can't poll fast enough. Did it just recently stop working after you had the rest of your setup finalised? Or did it stop somewhere along the way of adding more functionality to your sketch? It might be that you have to delve in to figuring out how to use interrupts to do some processing of events from your encoders.

That said, I can't see anything wrong with your code, but I do have some suggestions!

Your SAS calls, like

mySimpit.send(28, (unsigned char*) &sas_Counter, 1); // sends the new SAS mode setting to Simpit

feel clumsy. Do you really need to use the pointers there? I thought just `mySimpit.send(28, sas_Counter, 1);` would work. And, indeed, I had to change the code to that to make it compile on my local machine using Arduino 1.8.3 and Simpit 1.4.0. Haven't actually run it though. :(

Your joystick read routines will be slightly faster with a series of if/else statements rather than just ifs. Like this

  int rot_Y_Read = analogRead(ROT_Y);
  if (rot_Y_Read < 518 && rot_Y_Read > 508) {
    rot_Y_Mapped = 0;
  } else if (rot_Y_Read <= 508) {
    rot_Y_Mapped = map(rot_Y_Read, 344, 508, -32768, 0);
  } else if (rot_Y_Read >= 518) {
    rot_Y_Mapped = map(rot_Y_Read, 518, 680, 0, 32767);
  }

The only other thing I'll say is that digitalWrite is surprisingly slow. Your messageHandler currently writes every single action LED whenever you get an ACTIONSTATUS message. If you keep variables tracking what the LED currently is, you can write messageHandler so it only updates the LED if it changes. Something like

// First we need a global variable to remember SAS state:
int sasActive = 0; // You could use a shorter type here if you're tight on space.

// Then we do this in messageHandler:
// Check if SAS_ACTION in our actions packet matches what we remember.
// If not, remember the current value and write it to the LED.
if (actions & SAS_ACTION != sasActive) {
  sasActive = actions & SAS_ACTION;
  digitalWrite(SAS_LED, sasActive);
} // no else required any more!

OK. That's the easy stuff.

Link to comment
Share on other sites

Now the harder stuff.

20 hours ago, PSU_Jedi said:

My control box becomes unresponsive ~20-30 minutes into gameplay, or sometimes it seems shorter if I'm using the analog joysticks a lot. Given that, it seems that maybe the information exchange between the Mega board and the game is getting overloaded?

I do not know why. Yet. But let me ask the stupid questions: what exactly do you mean by unresponsive? Does it just stop updating LEDs? Does it just stop sending data from your joysticks? Is it both? Does it catch fire (OK maybe not, but I definitely had a small fire on my controller thanks to a shorted LED at one point)?

My first thought was maybe you were leaking memory somehow and the arduino was resetting. But that doesn't seem likely - you've got a lot of free RAM, and I don't see any obvious leaks in this code (although there might be some in the library?). And you're also turning all of your LEDs on in your init. If the arduino was resetting then your board would light up. So that's not it.

Your thinking around the volume of info getting from the game to your mega is overloading things could be right. I think that would mean that the serial buffer on your Arduino is filling up faster than your sketch is able to read it. You can check that! The serial buffer should be 64 bytes, I think, and `Serial.available()` will tell you how many bytes are in the buffer. So you could temporarily repurpose one of your LEDs and use it to tell you if the buffer ever gets full. Remove the code from messageHandler that updates your Brake LED (because nobody should ever be using the brakes, gosh!), and then put something like this at the top of your loop(). Note that the best place for it is right before you call the simpit update, because that's the fullest the buffer will get.

// The serial receive buffer should be 64 bytes.
// Check how much is there, and flip a warning light on if we've filled it:
if (Serial.available() > 63) {
  digitalWrite(BRAKES_LED, HIGH);
}

And then play until  your board stops. If the brake light comes on at any point there, then you know to blame the buffer.

Finally, you can also get more logging out of the game. It's worth turning on the debug flag in the Kerbal Simpit config file. Play until you get the problem, and then upload the KSP.log file somewhere I can get to it. With the debug flag on, Simpit logs a lot. You should be able to find messages from it by just searching the log file for "KerbalSimpit".

Edited by stibbons
Link to comment
Share on other sites

On 7/21/2020 at 12:21 AM, PSU_Jedi said:

Given that, it seems that maybe the information exchange between the Mega board and the game is getting overloaded?

Gah, I forgot the one other thing fairly simple thing you can try to test this theory, without having to recompile your controller fimrware. It might be worth experimenting with the RefreshRate in the config file. The default is 125 milliseconds, maybe try doubling that to 250 and see how you go.

Link to comment
Share on other sites

I'm running KSP version 1.10.0.2917 on Windows 10. Acording to CKAN I have Kerbal Simpit version 1.4.1.66. I'm on Arduino IDE 1.8.13 (Windows Store 1.8.42.0).

On 7/21/2020 at 6:49 AM, stibbons said:

 


mySimpit.send(28, (unsigned char*) &sas_Counter, 1); // sends the new SAS mode setting to Simpit

feel clumsy. Do you really need to use the pointers there? I thought just `mySimpit.send(28, sas_Counter, 1);` would work. And, indeed, I had to change the code to that to make it compile on my local machine using Arduino 1.8.3 and Simpit 1.4.0. Haven't actually run it though. :(

I tried your code here and strangely I got this error: no matching function for call to 'KerbalSimpit::send(int, int*, int)'

Quote

Your joystick read routines will be slightly faster with a series of if/else statements rather than just ifs. Like this


  int rot_Y_Read = analogRead(ROT_Y);
  if (rot_Y_Read < 518 && rot_Y_Read > 508) {
    rot_Y_Mapped = 0;
  } else if (rot_Y_Read <= 508) {
    rot_Y_Mapped = map(rot_Y_Read, 344, 508, -32768, 0);
  } else if (rot_Y_Read >= 518) {
    rot_Y_Mapped = map(rot_Y_Read, 518, 680, 0, 32767);
  }

 

 

On 7/21/2020 at 7:09 AM, stibbons said:

 


// The serial receive buffer should be 64 bytes.
// Check how much is there, and flip a warning light on if we've filled it:
if (Serial.available() > 63) {
  digitalWrite(BRAKES_LED, HIGH);
}

 

These two pieces of advice seemed to work really well. I played for well over a half hour and had no issues with the controller locking up at all. I didn't change the RefreshRate at all. So based on a couple of test runs, I want to say it was the code for the analog stick that was overloading everything.

On 7/21/2020 at 6:49 AM, stibbons said:

 


// First we need a global variable to remember SAS state:
int sasActive = 0; // You could use a shorter type here if you're tight on space.

// Then we do this in messageHandler:
// Check if SAS_ACTION in our actions packet matches what we remember.
// If not, remember the current value and write it to the LED.
if (actions & SAS_ACTION != sasActive) {
  sasActive = actions & SAS_ACTION;
  digitalWrite(SAS_LED, sasActive);
} // no else required any more!

OK. That's the easy stuff.

Also, something I did in this part of code didn't work right. The LEDs no longer responded properly and only seemed to randomly come on and never turned off once on.

THANKS for your advice above! It has really helped me enjoy my custom controller again!!!

Link to comment
Share on other sites

  • 1 month later...
On 7/21/2020 at 1:09 PM, stibbons said:

Your thinking around the volume of info getting from the game to your mega is overloading things could be right. I think that would mean that the serial buffer on your Arduino is filling up faster than your sketch is able to read it. You can check that! The serial buffer should be 64 bytes, I think, and `Serial.available()` will tell you how many bytes are in the buffer. So you could temporarily repurpose one of your LEDs and use it to tell you if the buffer ever gets full. Remove the code from messageHandler that updates your Brake LED (because nobody should ever be using the brakes, gosh!), and then put something like this at the top of your loop(). Note that the best place for it is right before you call the simpit update, because that's the fullest the buffer will get.


// The serial receive buffer should be 64 bytes.
// Check how much is there, and flip a warning light on if we've filled it:
if (Serial.available() > 63) {
  digitalWrite(BRAKES_LED, HIGH);
}

And then play until  your board stops. If the brake light comes on at any point there, then you know to blame the buffer.

Just wanted to chime in on this discussion, because I have similar problems. It doesn't seem like the serial buffer filling up is the issue. I followed your other advice though and that solved the problem (at least this one :rolleyes:).

Edited by midnitemax
Link to comment
Share on other sites

  • 2 weeks later...

So, I've run into this problem again (controller becoming unresponsive) and now it happens right away when I want to send a translation message. Here's my code:

 

#include <KerbalSimpit.h>
#include <PayloadStructs.h>
#include <DebounceInput.h>

#define forwardPin 51
#define backwardPin 50
#define rightPin 52
#define leftPin 53
#define upPin 48
#define downPin 49
#define stagePin 26

int lastUpdte = 0;
int updateDelay = 125;

DebouncedInput displaySelect(4);
DebouncedInput forward(forwardPin);
DebouncedInput backward(backwardPin);
DebouncedInput right(rightPin);
DebouncedInput left(leftPin);
DebouncedInput up(upPin);
DebouncedInput down(downPin);
DebouncedInput stage(stagePin);

KerbalSimpit mySimpit(Serial);


void setup() {
  Serial.begin(115200);
  while (!mySimpit.init());
}

void loop() {
  mySimpit.update();
  
  stage.read();
  if (stage.falling()) {
    mySimpit.activateAction(STAGE_ACTION);
  }
  
  translation();
}

void translation() {
  translationMessage translationMsg;
  translationMsg.mask=1|2|4;
  
  forward.read();
  backward.read();
  right.read();
  left.read();
  up.read();
  down.read();

  if (forward.falling() || forward.low()) {
    translationMsg.X = 32767;
  }
  if (backward.falling() || backward.low()) {
    translationMsg.X = -32767;
  }
  if (right.falling() || right.low()) {
    translationMsg.Y = 32767;
  }
  if (left.falling() || left.low()) {
    translationMsg.Y = -32767;
  }
  if (up.falling() || up.low()) {
    translationMsg.Z = 32767;
  }
  if (down.falling() || down.low()) {
    translationMsg.Z = -32767;
  }

  mySimpit.send(TRANSLATION_MESSAGE,translationMsg);
}

If I comment out the translation(); it works fine (well, all it really does then is staging). But if I leave it in, the staging doesn't work either, indicating to me, that something somewhere has crashed.

Link to comment
Share on other sites

  • 1 month later...
On 9/21/2020 at 5:36 AM, midnitemax said:

So, I've run into this problem again (controller becoming unresponsive) and now it happens right away when I want to send a translation message. Here's my code:

 


#include <KerbalSimpit.h>
#include <PayloadStructs.h>
#include <DebounceInput.h>

#define forwardPin 51
#define backwardPin 50
#define rightPin 52
#define leftPin 53
#define upPin 48
#define downPin 49
#define stagePin 26

int lastUpdte = 0;
int updateDelay = 125;

DebouncedInput displaySelect(4);
DebouncedInput forward(forwardPin);
DebouncedInput backward(backwardPin);
DebouncedInput right(rightPin);
DebouncedInput left(leftPin);
DebouncedInput up(upPin);
DebouncedInput down(downPin);
DebouncedInput stage(stagePin);

KerbalSimpit mySimpit(Serial);


void setup() {
  Serial.begin(115200);
  while (!mySimpit.init());
}

void loop() {
  mySimpit.update();
  
  stage.read();
  if (stage.falling()) {
    mySimpit.activateAction(STAGE_ACTION);
  }
  
  translation();
}

void translation() {
  translationMessage translationMsg;
  translationMsg.mask=1|2|4;
  
  forward.read();
  backward.read();
  right.read();
  left.read();
  up.read();
  down.read();

  if (forward.falling() || forward.low()) {
    translationMsg.X = 32767;
  }
  if (backward.falling() || backward.low()) {
    translationMsg.X = -32767;
  }
  if (right.falling() || right.low()) {
    translationMsg.Y = 32767;
  }
  if (left.falling() || left.low()) {
    translationMsg.Y = -32767;
  }
  if (up.falling() || up.low()) {
    translationMsg.Z = 32767;
  }
  if (down.falling() || down.low()) {
    translationMsg.Z = -32767;
  }

  mySimpit.send(TRANSLATION_MESSAGE,translationMsg);
}

If I comment out the translation(); it works fine (well, all it really does then is staging). But if I leave it in, the staging doesn't work either, indicating to me, that something somewhere has crashed.

What happens if you put the translation code directly into loop() instead of in a separate call?

Link to comment
Share on other sites

18 minutes ago, TheDicko said:

Hi, just started out with what looks to be an amazing mod. Quick question, how do you guys debug code? I was thinking of printing to the serial monitor, but it says port busy, when using ksp.

You can use the built-in LED in most arduinos (LED_BUILTIN) and have it turn on/off depending on where it is in the code. Alternatively, if you have any other LED indicators or displays (like a 7 segment display) you could have the arduino send debug messages there.

Link to comment
Share on other sites

Guest
This topic is now closed to further replies.
×
×
  • Create New...