Close
0%
0%

Kerbal Flight Control

This is an input focused control panel for Kerbal Space Program.

Similar projects worth following

This is my dream controller for Kerbal Space Program. It is a hobby project, and I am a PhD student, so expect slow progress.

My focus is on quality inputs. I don't expect to be able to fly with just the panel, and I'd rather be looking at the screen anyway (also I am already stretching my pin allotment).

The brains is currently 1 arduino duemilonove, supplemented with a number of i2c multiplexers and an analog multiplexer. I do have just a few input functions.

Code and electronics design both heavily based on https://github.com/PeteWasEre/Very-Kerbal-Kontroller-V2

Currently I have created a 1 control prototype. I am working on a more extensive draftboard prototype to be cut on my friends laser cutter. Final version will be cut on spray-painted transparent acrylic to allow backlighting.

  • Analog Testing

    Matthew Peverill08/24/2019 at 23:13 2 comments

    Ok today I'll be testing analog input. The shield has room for 20 analog inputs:

    • 2 directly wired for dimmer control.
    • 2 wired through 74HC4051 multiplexers.

    The relevant parts of the shield schematic are useful for this. Here is the pinout from the arduino:

    And here are the multiplexers:

    And here are the direct wires:

    Test code is heavily cribbed from Sparkfun's tutorials. I've modified them to: print the additional analog inputs. Read and print both multiplexers every time pin select happens.

    /******************************************************************************
    Multiple Analog Mux and hardcoded input test.
    Based on: "SparkFun Multiplexer Analog Input Example
    Jim Lindblom @ SparkFun Electronics
    August 15, 2016
    https://github.com/sparkfun/74HC4051_8-Channel_Mux_Breakout"
    
    
    ******************************************************************************/
    /////////////////////
    // Pin Definitions //
    /////////////////////
    const int selectPins[3] = {8, 12, 13}; // S0, S1, S2
    const int zInputa = A0; // Connect common (Z) to A0 (analog input)
    const int zInputb = A1; // Connect common (Z) to A0 (analog input)
    const int DIMp = A2;
    const int bDIMp = A3;
    void setup() 
    {
      Serial.begin(9600); // Initialize the serial port
      // Set up the select pins as outputs:
      for (int i=0; i<3; i++)
      {
        pinMode(selectPins[i], OUTPUT);
        digitalWrite(selectPins[i], HIGH);
      }
      pinMode(zInputa, INPUT); // Set up Z as an input
      pinMode(zInputb, INPUT);
      pinMode(DIMp, INPUT);
      pinMode(bDIMp, INPUT);
    
      // Print the header:
    //  Serial.println("Y0\tY1\tY2\tY3\tY4\tY5\tY6\tY7");
    //  Serial.println("---\t---\t---\t---\t---\t---\t---\t---");
    }
    
    // The selectMuxPin function sets the S0, S1, and S2 pins
    // accordingly, given a pin from 0-7.
    void selectAMuxPin(byte pin)
    {
      for (int i=0; i<3; i++)
      {
        if (pin & (1<<i))
          digitalWrite(selectPins[i], HIGH);
        else
          digitalWrite(selectPins[i], LOW);
      }
    }
    
    void loop() 
    {
      // Loop through the hard wired dimmers
      Serial.print(String(analogRead(DIMp))+" ");
      Serial.print(String(analogRead(bDIMp))+" ");
      // Loop through all eight pins.
      for (byte pin=0; pin<=8; pin++)
      {
        selectAMuxPin(pin); // Select one at a time
        int inputValuea = analogRead(zInputa); // and read Z1
        int inputValueb = analogRead(zInputb); // and read Z2
        Serial.print(String(inputValuea) + " " + String(inputValueb) + " ");
      }
      Serial.println();
    }

    Unfortunately, when I first turned on the Arduino's serial plotter, this is what I get when I turn one of my throttle potentiometers (nothing else does anything):

    Ok first let's cut out everything but the dimmer pots (A2-A3):

    These should not be in sync! Ok so now we need to know if there is an issue with the shield or with the jury-rigged protoboard I have the pots hooked up to. I plugged said board in to the other arduino and got exactly the same result. So probably something dodgy with the protoboard, which is not surprising because it's a mess.  

    Let's not do this again, ok?

    OK, but what about the multiplexers? Even without the dimmer pots plugged in, we get a horrible mess:

    Only one joystick seems to do anything, and what it does is move all the analog pins at once. What's happening here is that the other analog pins are just open (because I only plugged in the 2 pins from the joystick). If you ground them, then suddenly the output looks much more sensible:

    Good news! The circuit on the shield seems to work just fine.

  • Future Proofing for 2.0

    Matthew Peverill08/22/2019 at 19:33 0 comments

    So Kerbal reddit is aflame with news of Kerbal Space Program 2.0! Time will tell how the new team does with it, but what we've seen so far is pretty cool. I'm not hugely in to the far future stuff, but it looks to be a bigger game in general, which is awesome.

    But more relevantly: it would be a shame if we had to rebuild this thing entirely for a new system! How can we guard against that? My feeling is that there will be some analog of kRPC or equivalent that I can talk to, but it may take some time to be feature complete. In the meantime, it would be nice if we could do this without complicated software. How can we do that? By emulating a joystick!

    The ATMEGA328P, which is the chip that powers most arduino based controllers (and both of the ones I've used with this project so far) is too slow to emulate a USB device. But the ATmega32u4, which is in the Teensy 8 bit microcontrollers as well as some of arduino and adafruit's lineup, can. So can the 32 bit chips in Adafruit's M0 line. Possibly I should just go for that, but 8 bit just feels so nostalgic (plus I don't need to do a pass over all my designs to convert them to 3V logic). 

    1. The USB plug: classic arduino uses a USB-B cable. I've been a bit stubborn about this one, because I feel like the micro cables might be less resilient, but at this point everyone has a million of them lying around, so I can probably let it go.

    2. Power: The Arduino has the option for an external power source, bumping up the available power from 400mA or so to about 900mA. One thing we'll need to do during testing is calculate how much power everything takes in operation (we can always install our own power jack separate from the microcontroller's supply).

    3. How big a joystick can we make? We have 11 analog controls and up to 64 digital on this sucker, and even the mightiest HID device can't support that many. BUT we can do a workaround: one board can emulate multiple HID devices, so we can present as more than one joystick. Problem solved.

    This should be a good option to make the board more flexible for either KSP version.

  • Further testing, the urge to start over, and rotary encoders.

    Matthew Peverill08/20/2019 at 15:49 4 comments

    OK - I tested input on my throttle panel! Apart from the afore-mentioned issue with the pull up resistors, it works fine. There are a few (like, 2) that I didn't wire with resistors, I think because I originally planned to build them in to the rotary switches, but no biggie.

    None of these issues is insurmountable (I can just use a breakout to provide 5V to my switches). However, it's really tempting to start over rather than spend time hacking around them when I could just start over. I'm already excited to:

    • Put everything on one board with surface mount connections.
    • Use a button matrix with one I/O expander instead of 4 I/O expanders.
    • Use rotary encoders!

    It's a mistake to actually rebuild before I've tested everything, but there's no harm in thinking about it and planning. 

    Planning for Encoders  

    Today I'm thinking about rotary encoders. These are rotary switches that can be rotated indefinitely. They output to two pins that turn off and on in a stepped pattern such that, if you read constantly, you can tell which way they are being rotated (other people have written about this better than I). They would be so much nicer for the time warp switches and the camera zoom. But there is little consensus about how to configure multiple encoders. I'd need three, so the gold standard method of using interrupts is out. Some sources suggest that, if they are hand operated and don't go too fast, it will be fine to wire the outputs to a button matrix. A more foolproof (but more expensive) way is to purchase a couple of i2c encoder daughter boards for about $20. 

    How can we tell if we'd be ok polling? First, we need to know how often we need to poll to reliably capture hand rotated encoder signals. There's a lot of "it worked for me" and "I would never try that", but little data, so let's do some testing.

    I'm using PaulStroffregen's Encoder library. The github page includes some example sketches that are perfect for what I'm trying to do. First, I loaded up the 'Basic.pde' sketch just to see what things worked like with interrupts (the 'right' way). Then I loaded up 'NoInterrupts'. These two work pretty much the same if there is nothing going on. Output should look like this:

    Basic NoInterrupts Test:
    0
    1
    2
    3
    4
    5
    6
    7
    8
    

    As you turn the encoder, each detent corresponds to an increase (or decrease) of four. Sometimes you the detent won't quite 'land' and you get stuck in between, but it will settle to a multiple of four with a little movement (note to self: going to have to deal with this when we get to actual controls). 

    Next I uncommented the delay line in the NoInterrupts sketch and set it to 20ms, which is a pretty reasonable loop interval for the whole device. No dice at all - first off, even on small turns (one or two detents, a step can be missed (which would screw up some coding schemes). Large turns are completely lost - I can turn 10 detents and the counter only goes up by 9 or so (it should go up 40). So if you were using an encoder for something rather imprecise, that might be fine, but might end up crashing us in to the sun. So i2c controllers it is!

  • Update: digital input testing.

    Matthew Peverill08/17/2019 at 22:50 1 comment

    Ok move is complete, in a new space, time to get hacking!

    New Issues

    Still troubleshooting the prototype box. I've found a few new issues to put on the to-do list for the next iteration:

    • The RCS control is too long, it sticks out of the box in a way that is sort of unpleasant and, more importantly, the interior division that I've used to support it doesn't leave enough room for the cables coming off the arduino, which need a good 2-3 centimeters of headroom. This is workable in the prototype, but it means the box doesn't quite close right.

    Progress

    I identified some cable pairs that aren't broken in my long cables! I was able to verify this by loading up Adafruit's led bargraph test - it works fine. This means I can troubleshoot my daughter boards for real now. PCBs and boxes are expensive so I want to find all the problems I can before I redesign.

    Notes for Today:

    First, Troubleshooting the action button PCB. Once I got my wiring sorted out, I ran my I2C multiplexer test that I've already gotten to work on a breadboard:

    #include <Wire.h>
    
    void mux_Tx(int adr, int reg, byte data) {
      /* This function will send data to a MCP23017 chip */
      Wire.beginTransmission(adr);     /* address the chip */
      Wire.write(reg);                 /* point to the register of choice */
      Wire.write(data);                /* send the data */
      Wire.endTransmission();          /* end the transmission */
    }
    
    void mux_Rx(int adr, int reg, int numbytes, byte *data) {
      /* This function will request n bytes of data from a MCP23017 chip */
      Wire.beginTransmission(adr);     /* address the chip */
      Wire.write(reg);                 /* point to the register of choice */
      Wire.endTransmission();          /* end the transmission */
      Wire.requestFrom(adr, numbytes); /* request the data */
      *data = Wire.read();
    }
    
    void printBin(int var) {
      for (unsigned int test = 0x80; test; test >>= 1) {
        Serial.print(var  & test ? '1' : '0');
      }
    }
    
    
    void setup() {
      Serial.begin(9600); // Initialize the serial port
      Serial.print("I'm awake!\n");
      /* wake up the I2C_bus */
      int n_mux_chips_detected = 0;           /* number of MUX chips detected in the IBIT */
      const int c_num_mux_chips = 2;
      const int c_first_mux_address = 0x20;     /* the first I2C address in the MUX range [-]*/
      const int c_last_mux_address = 0x21;      /* the last I2C address in the MUX range [-]*/
      
      Wire.begin();
      Serial.print("Started Wire\n");
      /* check we have all the MCP23017 chips */
      for (byte a = c_first_mux_address; a <= c_last_mux_address; a++)   /* chip addresses start at 0x20, max of 8 chips */
      {
        Serial.print("Testing ");
        Serial.print(a);
        Wire.beginTransmission (a);
        int testval = Wire.endTransmission ();
        Serial.print(" returned ");
        Serial.print(testval);
        Serial.print("\n");
        if (testval == 0) {
          n_mux_chips_detected++;
        }
      }
      
      Serial.print("Done testing, detected: ");
      Serial.print(n_mux_chips_detected);
      Serial.print("\n");
      if (n_mux_chips_detected != c_num_mux_chips) {
        Serial.print("mux miss");
      }
    
    }
    
    void loop() {
      byte buffb=0;
      mux_Rx(0x20, // i2c 0
             0x13, // GPIOB
             1, // Start at register 1
             &buffb); //write to buff
      byte buffa=0;
      mux_Rx(0x20, // i2c 0
             0x12, // GPIOA
             1, // Start at register 1
             &buffa); //write to buff
      byte buffd=0;
      mux_Rx(0x21, // i2c 0
             0x13, // GPIOB
             1, // Start at register 1
             &buffd); //write to buff
      byte buffc=0;
      mux_Rx(0x21, // i2c 0
             0x12, // GPIOB
             1, // Start at register 1
             &buffc); //write to buff
      Serial.print("0x20A: ");
      printBin(buffa);
      Serial.print(" 0x20B: ");
      printBin(buffb);
      Serial.print(" 0x21A: ");
      printBin(buffc);
      Serial.print(" 0x21B: ");
      printBin(buffd);
      Serial.print("\n");
    //  Serial.println(buffb,BIN);
      delay(100);
    
    }

    When I run this with my action panel wired, serial monitor cheerfully reports:

    I'm awake!
    Started Wire
    Testing 32

    Without going in to too many details, this indicates that Wire.beginTransmission or Wire.endTransmission hung without exiting cleanly. This usually means the i2c device isn't responding, which probably means a hardware issue (at least, if your board was hacked to gether by an amateur that is...

    Read more »

  • The Shield PCB (and a solved mystery)

    Matthew Peverill06/07/2019 at 05:05 3 comments

    The idea with the shield was to host the analog multiplexers and some of the lamp test electronics, as well as to break down the pin connections in to logically sensible headers. The schematic is on github. Here's the board:

    The connector in the top left is the one I'm trying to test. In order from right to left the pins are: Vcc, Gnd, A5 (SCL), A4 (SDA). 

    Continuity tests on the board show those pins connecting to the relevant headers, and not to each other. My i2c device works fine if I plug it directly in to my arduino, but it fails if I plug it in to the ports in my shield. So what could else could be going wrong? It's the cables - the extra long female to female jcp cables I ordered from aliexpress are nearly all completely useless. When I hooked it up with a 6" cable it works fine. Lesson learned - now I just need a different source for 20" female to female ribbon cables :(

  • Time Warp

    Matthew Peverill06/06/2019 at 06:53 0 comments

    Ok so I had LOTS of problems solved up to the present moment. Need to get a little less perfectionist about posting. In brief:

    • If you make your own PCBs, don't just trust the footprints for your components will work (both my rotary switches and my microswitches didn't have big enough drill holes: they had to be widened).
    • Don't buy parts that don't come with a footprint diagram.

    I have a new prototype which looks great:

    It's also mostly intact on the back, including my shiny new PCBs! I have:

    The shield (the thing that snaps on to the back of the arduino to provide connections to the other boards and analog devices)

    The action panel (the thing that provides most of the digital i/o and which has the action buttons on it.

    The throttle panel (which has the rotary switch inputs and some other i/o on it.

    If I had known how PCB pricing works when I started, I would have tried to make one shield panel to go under the action panel and used surface mount parts. Maybe next time.

    It is not, of course, working yet. At the moment I have a problem where, if I hook up a purchased i2c device (like the adafruit bar graphs) to my arduino with a breadboard, it works fine, but if I try to hook it up through my shield board, it doesn't work. So probably there is a flaw in the shield board (bummer). I don't really know how to figure out what it is though - guess I'll have to do some research :)

  • Haptic Motors

    Matthew Peverill02/11/2019 at 06:34 0 comments

    So the second circuit I tried to lay out was the vibration motor circuit. I used one that is fairly typically seen online with a diode and a small capacitor in parallel with the motor. My motor turned on, but I realized pretty quickly that it was going to be difficult to make the sort of effects I want.

    Instead, I will probably use a haptic motor driver, which seems to be the usual response. Otherwise, I think I'd end up running in to weird timing issues, and I want to save processor speed for serial response as much as possible.

  • Circuit Development Part 1 - Light Control

    Matthew Peverill02/05/2019 at 08:40 0 comments

    PCB development is ongoing!  After talking to the amazing Kicad forum people about how to design an amateur-friendly PCB without soldermask, I was convinced that I should just send a design to a board house. This means I want to be extra sure my circuits work, which means prototyping things on breadboard.

    Parts of the circuit I'm quite comfortable with (I2C I/O expanders, switches with pull-up/pull-down resistors). BUT there are two parts that are a little less comfortable. One is the vibration motor, which I'll address next time. The other is LED control.

    My design calls for some LEDs to be driven off the arduino directly for maximum fail-safe capability in the event of a hardware problem (power, error, overflow). Other LEDs are to be driven by ULN2803A darlington pair arrays. The basic premise of these latter is to allow the microcontroller to control logic while providing steady current (and avoid drawing too much current through the controller chips and burning them out.

    Now, the complications are that we also want:

    • A PWM dimmer control wire that is common to all LEDs on the board.
    • A 'lamp test' circuit that turns on all LEDs through a hardware switch when pressed.

    The latter is easier. We just use diodes to provide alternate current to the anode when a button is pressed, which effectively over-rides the arduino pin. You need another set of diodes connecting the arduino to the anodes, so that you don't short out the arduino pins (fun fact - my arduino didn't burn out when I did this the first time, but it did cause flickering when the lamp test was on). In the case of the ULN, you can hook up the common emitter pin to ground the switch. When the switch hooks to ground, the common emitter is grounded and all the LEDs light.

    The PWM is more tricky. For the board controlled lights, you can just map an analog in to the PWM pins and, presto, you're good to go. BUT I don't have enough PWM pins to drive every LED. Instead we use some transistor trickery to turn off the ground pin on the ULN using a common PWM pin. Full disclosure: I don't fully understand how this works and, like a lot of my project, it's cribbed from Petewasere's github page.

    OK so I bought an adafruit metro mini so I don't have to unplug my arduino from the panel (almost got the M4 version, but I wanted an equivalent). I soldered on the pins and plugged it in my breadboard. My soldering is decidedly just ok: 

    Look at those blobs! Seriously those this thing is great for breadboarding.

    I'm going to spare you a lot of the troubleshooting. Here's the final circuit in fritzing (which I'm not using for PCB design, but is (somewhat) helpful for breadboard illustrations:

    Here's a terrible schematic:

    Code can be found here:

    https://gist.github.com/mrpeverill/9cb6b14f50befc2daf1d557ecb9a955d

    Code is pretty simple. The only complication is that you have to sign flip the PWM signal for one set of LEDs for another. I'm pretty sure I could get around that with a PNP transistor, but I don't have any (edit: yes this totally works!).

    Here's a video: 

  • Thoughts about circuits

    Matthew Peverill12/11/2018 at 04:55 0 comments

    Good news, no major problems with the panel. It now looks like this: 

    This led me to start thinking in more detail about how to actually connect everything. My original plan (at least for the prototype) was to use stripboard, and I spent a lot of time today looking in to stripboard prototyping. Unfortunately, when I got home to start doing that I realized that several of my components simply won't work with veroboard because of pitch problems (in particular, the game controller joysticks used for camera controls, and the resource lights. It's possible that, If I'm going to have to use PCBs, I should just use them for everything. I have an old kicad file describing some of the circuits, and I can fab things at a makerspace nearby. 

    More fruitfully, I spent some time thinking about wiring. I was going to just solder wires to headers, but the article above said I shouldn't do that. A little googling and I realized I can get JST connectors to use with ribbon cable, which will be much neater. 

  • Laser Cutting Prototypes

    Matthew Peverill12/02/2018 at 07:22 0 comments

    Just finished lasercutting my first solid prototype on my friend's glowforge! For those not familiar: you start out making a vector drawing in inkscape, then upload it to a web app. Specify which colors are engrave and which cut, and press the button. I used the excellent Tabbed Box Maker to build an enclosure. I tried to cut it so that the top (with the switches) could hinge out from the rest. Here are some pictures:

    There are a few issues, justifying the prototype status:

    1. The final product ended up a bit bigger than I was visualizing it. I may be able to shrink it during development, but the extra space will likely be helpful for now. 
    2. The space/rocket switch isn't labelled. 
    3. The screw holes for the arduino aren't quite big enough. 
    4. I didn't cut holes for the tabs in the rotary switches. These are designed to keep the base rotationally fixed, and are required for them to sit flush with the panel.
    5. The rocker switches I use aren't reacting well to being squeezed in the long direction - they are a bit stiff. 
    6. The box is a bit flimsy - it would have helped to make it deeper, and/or to build in some crossbars. This will also help with the feel of the buttons (since the panel flexes a bit atm when you press).
    7. Still - it's looking pretty good! The action group buttons fit perfectly.

    Over the next few days I'll go through the different parts in more detail as I put it together, and I'll finally talk about how I picked parts. 

View all 12 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates