Last active
February 19, 2019 18:21
-
-
Save rooreynolds/c4c3b9bcfc1f99f4d21a to your computer and use it in GitHub Desktop.
KSP controller
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rubygems' | |
require 'open-uri' | |
require "json" | |
require 'date' | |
require './matrixorbital_lcd/MatrixOrbital.rb' # https://github.com/rooreynolds/matrixorbital_lcd | |
lcd = MatrixOrbital.new | |
@epoch = Time.at(0).to_date | |
def padding(text, chars) | |
return text.to_s.ljust(chars)[0,chars] | |
end | |
def print_line(lcd, text) | |
lcd.puts(padding(text,16)) #pad to 16 and truncate at 16 chars | |
end | |
def format_date(in_date) | |
date = Time.at(in_date).gmtime | |
days = (date.to_date - @epoch).round | |
time = (days > 0 ? days.to_s+":" : "") + date.strftime('%T').to_s | |
return time | |
end | |
def format_number(number, dp = 1) | |
return number.round(dp).to_s.gsub(/(\d)(?=\d{3}+(?:\.|$))(\d{3}\..*)?/,'\1,\2').to_s | |
end | |
def format_label(label, text) | |
return label + text.rjust(16 - label.size) | |
end | |
def bounded(num, lowerbound, upperbound) | |
return [lowerbound, [upperbound, num].min].max | |
end | |
loop do # forever, until interrupted | |
content = open("http://127.0.0.1:8085/telemachus/datalink?body=v.body&met=v.missionTime&pe=o.PeA&ap=o.ApA&pe_time=o.timeToPe&ap_time=o.timeToAp&speed_surface=v.surfaceSpeed&speed_vertical=v.verticalSpeed&alt=v.altitude&radarheight=v.heightFromTerrain&heading=n.heading&pitch=n.pitch&roll=n.roll&solidfuel=r.resource[SolidFuel]&max_solidfuel=r.resourceMax[SolidFuel]&liquidfuel=r.resource[LiquidFuel]&max_liquidfuel=r.resourceMax[LiquidFuel]&oxidiser=r.resource[Oxidizer]&max_oxidiser=r.resourceMax[Oxidizer]&monoprop=r.resource[MonoPropellant]&max_monoprop=r.resourceMax[MonoPropellant]&electricity=r.resource[ElectricCharge]&max_electricity=r.resourceMax[ElectricCharge]").read | |
# TODO: should this be closed and tidied up? | |
# TODO: only request what we're going to show | |
# &vel=o.relativeVelocity | |
# &solidfuel=r.resource[SolidFuel]&max_solidfuel=r.resourceMax[SolidFuel]&liquidfuel=r.resource[LiquidFuel]&max_fuel=r.resourceMax[LiquidFuel]&oxidiser=r.resource[Oxidizer]&max_oxidiser=r.resourceMax[Oxidizer]&monoprop=r.resource[MonoPropellant]&max_mono=r.resourceMax[MonoPropellant]&electricity=r.resource[ElectricCharge]&max_electricity=r.resourceMax[ElectricCharge] | |
# &solidfuel=r.resource[SolidFuel] | |
# &body=v.body | |
# &name=v.name | |
# &period=o.period&ecc=o.eccentricity&inc=o.inclination | |
begin | |
json = JSON.parse(content) # returns a hash | |
puts 1 | |
puts(format_date(json["met"])) | |
puts("H" + ("%3i" % json["heading"].round()) + " P" + ("%3i" % json["pitch"].round()) + " R" + ("%3i" % json["roll"].round())) | |
#print_line(lcd, json["name"].gsub(/.*\((.*)\)/,'\1')) # only show the bit within ()s | |
puts 2 | |
puts(format_label("Pe ", format_number(json["pe"]))) | |
puts(format_label("Ap ", format_number(json["ap"]))) | |
puts 3 | |
puts(format_label("Pe T- ", format_date(json["pe_time"]))) | |
puts(format_label("Ap T- ", format_date(json["ap_time"]))) | |
puts 4 | |
puts(format_label("Srf ", format_number(json["speed_surface"]))) | |
puts(format_label("Vrt ", format_number(json["speed_vertical"]))) | |
puts 5 | |
puts(format_label("Alt ", format_number(json["alt"]))) | |
radarHeight = format_number(json["radarheight"]) | |
if (radarHeight.to_i > -1) then | |
puts(format_label("Radar ", radarHeight)) | |
else | |
puts(json["body"]) | |
end | |
puts 6 | |
solid = json["solidfuel"] | |
max_solid = json["max_solidfuel"] | |
puts(format_label("SolF ", format_number(json["solidfuel"], 0) + " " + (json["solidfuel"].to_f/json["max_solidfuel"].to_f*100).round().to_s + "%")) | |
puts(format_label("LiqF ", format_number(json["liquidfuel"], 0) + " " + (json["liquidfuel"].to_f/json["max_liquidfuel"].to_f*100).round().to_s + "%")) | |
puts(format_label("Mono ", format_number(json["monoprop"], 1) + " " + (json["monoprop"].to_f/json["max_monoprop"].to_f*100).round().to_s + "%")) | |
puts(format_label("Elec ", format_number(json["electricity"], 1) + " " + (json["electricity"].to_f/json["max_electricity"].to_f*100).round().to_s + "%")) | |
lcd.puts(format_date(json["met"]) + | |
",H" + ("%3i" % json["heading"].round()) + " P" + ("%3i" % json["pitch"].round()) + " R" + ("%3i" % json["roll"].round()) + | |
"," + (bounded(json["radarheight"],0,500).to_f / 500 * 255).round(2).to_s + | |
"," + ((bounded(json["speed_vertical"],-50,50).to_f / 50 * 135)+135).round(2).to_s + | |
"," + (json["liquidfuel"].to_f/json["max_liquidfuel"].to_f*255).round().to_s) | |
sleep 0.25 | |
rescue Interrupt=>i | |
exit | |
rescue Exception=>e | |
puts e | |
print_line(lcd, "Disconnected"); | |
lcd.backlight_on(1) | |
sleep 2 | |
end | |
end # end of loop | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Use an Arduino to control an LCD display and some volt meters. | |
* Pass through data from a serial app | |
* (Future versions will display selected data according to switch toggles. Mainly a stub for now) | |
* | |
* Roo Reynolds - rooreynolds.com | |
*/ | |
#include <Bounce.h> | |
#include <SoftwareSerial.h> | |
#define rxPin 7 // software Rx pin (connect to Tx on LCD) | |
#define txPin 6 // software Tx pin (connect to Rx on LCD) | |
#include <Wire.h> | |
#include <LiquidCrystal_I2C.h> | |
LiquidCrystal_I2C lcd(0x3f,16,2); // set the LCD address to 0x27 for a 16 chars and 2 line display | |
Bounce panel3_down = Bounce(2, 10); | |
Bounce panel3_up = Bounce(3, 10); | |
Bounce panel2_up = Bounce(5, 10); | |
Bounce panel2_down = Bounce(4, 10); | |
Bounce panel1_down = Bounce(7, 10); | |
Bounce panel1_up = Bounce(6, 10); | |
Bounce lcd_down = Bounce(13, 10); | |
Bounce lcd_up = Bounce(12, 10); | |
void setup() { | |
pinMode(rxPin, INPUT); | |
pinMode(txPin, OUTPUT); | |
Serial.begin(19200); | |
delay(200); // (can't use port immediately?) | |
pinMode(11, OUTPUT); | |
pinMode(10, OUTPUT); | |
pinMode(9, OUTPUT); | |
pinMode(2, INPUT_PULLUP); | |
pinMode(3, INPUT_PULLUP); | |
pinMode(4, INPUT_PULLUP); | |
pinMode(5, INPUT_PULLUP); | |
pinMode(6, INPUT_PULLUP); | |
pinMode(7, INPUT_PULLUP); | |
pinMode(12, INPUT_PULLUP); | |
pinMode(13, INPUT_PULLUP); | |
lcd.init(); | |
lcd.backlight(); | |
lcd.print("Hello, world!"); | |
} | |
String inData; | |
int commaPosition; // the position of the next comma in the string | |
int index = 0; | |
void loop() { | |
panel1_down.update(); | |
panel1_up.update(); | |
panel2_down.update(); | |
panel2_up.update(); | |
panel3_down.update(); | |
panel3_up.update(); | |
lcd_down.update(); | |
lcd_up.update(); | |
if (panel1_down.fallingEdge()) { | |
Serial.println("1 down"); | |
} | |
if (panel1_up.fallingEdge()) { | |
Serial.println("1 up"); | |
} | |
if (panel2_down.fallingEdge()) { | |
Serial.println("2 down"); | |
} | |
if (panel2_up.fallingEdge()) { | |
Serial.println("2 up"); | |
} | |
if (panel3_down.fallingEdge()) { | |
Serial.println("3 down"); | |
} | |
if (panel3_up.fallingEdge()) { | |
Serial.println("3 up"); | |
} | |
if (lcd_down.fallingEdge()) { | |
Serial.println("lcd down"); | |
} | |
if (lcd_up.fallingEdge()) { | |
Serial.println("lcd up"); | |
} | |
// retransmit bytes read from the computer to LCD | |
if (Serial.available() > 0) { | |
char recieved = Serial.read(); | |
inData += recieved; | |
// Process message when new line character is recieved | |
if (recieved == '\n') { | |
inData.trim(); | |
// lcd.clear(); | |
do { | |
commaPosition = inData.indexOf(','); | |
if(commaPosition != -1) { | |
handleData(index, inData.substring(0,commaPosition)); | |
inData = inData.substring(commaPosition+1, inData.length()); | |
} else { // here after the last comma is found | |
if(inData.length() > 0) | |
handleData(index, inData); // if there is text after the last comma, print it | |
} | |
index++; | |
} while(commaPosition >=0); | |
inData = ""; // Clear recieved buffer | |
index = 0; | |
delay(100); | |
} | |
} | |
} | |
void handleData(int index, String data) { | |
printData(data); | |
if (index == 0) { | |
lcd.setCursor(0,0); | |
lcd.print(data); | |
if (data.length() < 16) { | |
for (int i = 0; i < 16 - data.length(); i++) { | |
lcd.print(" "); | |
} | |
} | |
delay(10); | |
} | |
if (index == 1) { | |
lcd.setCursor(0,1); | |
lcd.print(data); | |
if (data.length() < 16) { | |
for (int i = 0; i < 16 - data.length(); i++) { | |
lcd.print(" "); | |
} | |
} | |
delay(10); | |
} | |
if (index == 2) { | |
sendMeter(11, data); | |
delay(10); | |
} | |
if (index == 3) { | |
sendMeter(10, data); | |
delay(10); | |
} | |
if (index == 4) { | |
sendMeter(9, data); | |
delay(10); | |
} | |
} | |
void sendMeter(int pin, String data) { | |
char char_string[data.length()+1]; | |
data.toCharArray(char_string, data.length()+1); | |
int num = atoi(char_string); | |
if (num < 0) { | |
num = 0; | |
} else if (num > 255) { | |
num = 255; | |
} | |
analogWrite(pin, num); | |
} | |
void printData(String data) { | |
Serial.print(index); | |
Serial.println(" = " + data); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Use a Teensy (pjrc.com/teensy) to control Kerbal Space Program (kerbalspaceprogram.com) | |
* Various switches connected to various pins on the Teensy board map to various keyboard controls | |
* A connected WiiMote Nunchuck maps to a virtual joystick | |
* (Most KSP settings are the defaults, but alias F6 to Throttle Down and F7 to Throttle Up) | |
* | |
* Roo Reynolds - rooreynolds.com | |
*/ | |
#include <Bounce.h> | |
#include <Wire.h> | |
#include <Nunchuk.h> | |
Nunchuk nc = Nunchuk(); | |
boolean precision_toggle = false; | |
boolean locked = false; | |
boolean safety = false; | |
//outputs | |
int led = 13; //13 = internal LED | |
int blueled = 3; // PWM | |
//inputs | |
int pin_lock = 12; | |
int pin_safety = 23; //with 10k pulldown resistor between this pin and ground | |
int pin_stage = 22; | |
int pin_abort = 0; | |
int pin_kill = 1; | |
int pin_trottleup = 4; | |
int pin_trottledown = 5; | |
int pin_view = 2; | |
int pin_iva = 6; | |
int pin_navball = 8; | |
int pin_map = 7; | |
int pin_sastemp = 16; | |
int pin_sastoggle = 21; | |
int pin_rcstoggle = 20; | |
int pin_prec_on = 15; | |
int pin_prec_off = 14; | |
int pin_lights = 11; | |
int pin_gear = 10; | |
int pin_brakes = 9; | |
Bounce sw_lock = Bounce(pin_lock, 10); | |
Bounce sw_safety = Bounce(pin_safety, 10); | |
Bounce sw_stage = Bounce(pin_stage, 10); | |
Bounce sw_abort = Bounce(pin_abort, 10); | |
Bounce sw_kill = Bounce(pin_kill, 10); | |
Bounce sw_throttleup = Bounce(pin_trottleup, 10); | |
Bounce sw_throttledown = Bounce(pin_trottledown, 10); | |
Bounce sw_iva = Bounce(pin_iva, 10); | |
Bounce sw_view = Bounce(pin_view, 10); | |
Bounce sw_navball = Bounce(pin_navball, 10); | |
Bounce sw_map = Bounce(pin_map, 10); | |
Bounce sw_sastemp = Bounce(pin_sastemp, 10); | |
Bounce sw_sastoggle = Bounce(pin_sastoggle, 10); | |
Bounce sw_rcstoggle = Bounce(pin_rcstoggle, 10); | |
Bounce sw_prec_on = Bounce(pin_prec_on, 10); | |
Bounce sw_prec_off = Bounce(pin_prec_off, 10); | |
Bounce sw_gear = Bounce(pin_gear, 10); | |
Bounce sw_brakes = Bounce(pin_brakes, 10); | |
Bounce sw_lights = Bounce(pin_lights, 10); | |
void setup() { | |
pinMode(led, OUTPUT); | |
pinMode(blueled, OUTPUT); | |
pinMode(pin_lock, INPUT_PULLUP); | |
pinMode(pin_safety, INPUT); | |
pinMode(pin_stage, INPUT_PULLUP); | |
pinMode(pin_abort, INPUT_PULLUP); | |
pinMode(pin_kill, INPUT_PULLUP); | |
pinMode(pin_trottleup, INPUT_PULLUP); | |
pinMode(pin_trottledown, INPUT_PULLUP); | |
pinMode(pin_iva, INPUT_PULLUP); | |
pinMode(pin_view, INPUT_PULLUP); | |
pinMode(pin_navball, INPUT_PULLUP); | |
pinMode(pin_map, INPUT_PULLUP); | |
pinMode(pin_sastemp, INPUT_PULLUP); | |
pinMode(pin_sastoggle, INPUT_PULLUP); | |
pinMode(pin_rcstoggle, INPUT_PULLUP); | |
pinMode(pin_prec_on, INPUT_PULLUP); | |
pinMode(pin_prec_off, INPUT_PULLUP); | |
pinMode(pin_gear, INPUT_PULLUP); | |
pinMode(pin_brakes, INPUT_PULLUP); | |
pinMode(pin_lights, INPUT_PULLUP); | |
analogWrite(blueled, 0); | |
nc.begin(); | |
Joystick.useManualSend(true); | |
if (digitalRead(pin_lock) == HIGH) { | |
locked = true; | |
Serial.println("initial locked = true"); | |
} | |
if (digitalRead(pin_safety) == LOW) { | |
safety = true; | |
Serial.println("initial safety = true"); | |
} | |
setLightStatus(); | |
} | |
void loop() { | |
int DAMPING = 1; | |
if (precision_toggle) { | |
DAMPING = 3; | |
} | |
nc.read(); // read nunchuck data | |
// showDebugNCdata(); | |
if (! locked) { | |
if (nc.getButtonZ() == 0) { | |
Serial.print("pitch "); | |
Serial.print((int) ((float) ((0 - nc.getJoyY() - 7) / DAMPING + 100) / 200 * 1024)); | |
Joystick.Y((int) ((float) ((0 - nc.getJoyY() - 7) / DAMPING + 100) / 200 * 1024)); | |
Serial.print(" / yaw "); | |
Serial.println((int) ((float) ((nc.getJoyX() - 7) / DAMPING + 100) / 200 * 1024)); | |
Joystick.X((int) ((float) ((nc.getJoyX() - 7) / DAMPING + 100) / 200 * 1024)); | |
Joystick.Z(512); // stop any roll | |
} else { | |
Serial.print("roll "); | |
Serial.println((int) ((float) ((nc.getJoyX() - 7) / DAMPING + 100) / 200 * 1024)); | |
Joystick.Z((int) ((float) ((nc.getJoyX() - 7) / DAMPING + 100) / 200 * 1024)); | |
Joystick.X(512); // stop yaw | |
Joystick.Y(512); // stop pitch | |
} | |
} | |
//TODO: could use button C on nunchuck to switch modes? | |
Joystick.send_now(); | |
sw_lock.update(); | |
sw_safety.update(); | |
sw_stage.update(); | |
sw_abort.update(); | |
sw_kill.update(); | |
sw_throttleup.update(); | |
sw_throttledown.update(); | |
sw_iva.update(); | |
sw_view.update(); | |
sw_navball.update(); | |
sw_map.update(); | |
sw_sastemp.update(); | |
sw_sastoggle.update(); | |
sw_rcstoggle.update(); | |
sw_prec_on.update(); | |
sw_prec_off.update(); | |
sw_gear.update(); | |
sw_brakes.update(); | |
sw_lights.update(); | |
if (sw_lock.risingEdge()) { | |
Serial.println("lock ON"); | |
locked = true; | |
setLightStatus(); | |
} | |
if (sw_lock.fallingEdge()) { | |
Serial.println("lock OFF"); | |
locked = false; | |
setLightStatus(); | |
} | |
if (sw_safety.risingEdge()) { | |
Serial.println("safety OFF"); | |
safety = false; | |
setLightStatus(); | |
} | |
if (sw_safety.fallingEdge()) { | |
Serial.println("safety ON"); | |
safety = true; | |
setLightStatus(); | |
} | |
if (sw_stage.fallingEdge()) { | |
if (locked) { | |
Serial.println("(stage inhibited by lock)"); | |
} else if (safety) { | |
Serial.println("(stage inhibited by safety)"); | |
} else { | |
Serial.println("STAGE!"); | |
Keyboard.print(" "); | |
for (int i = 255; i >= 0; i--) { | |
analogWrite(blueled, i); | |
delay(1); | |
} | |
} | |
} | |
if (sw_stage.risingEdge()) { | |
if (! locked && ! safety) { | |
for (int i = 0; i < 255; i++) { | |
analogWrite(blueled, i); | |
delay(1); | |
} | |
} | |
} | |
if (sw_abort.fallingEdge()) { | |
if (locked) { | |
Serial.println("(abort inhibited by lock)"); | |
} else { | |
Serial.println("ABORT!"); | |
Keyboard.press(KEY_BACKSPACE); | |
Keyboard.release(KEY_BACKSPACE); | |
} | |
} | |
if (sw_kill.fallingEdge()) { | |
Serial.println("KILL!"); | |
Keyboard.print("x"); | |
} | |
if (sw_throttleup.fallingEdge()) { | |
if (digitalRead(pin_kill) == LOW) { | |
Serial.println("(throttle UP inhibited by throttle kill switch)"); | |
} else if (locked) { | |
Serial.println("(throttle inhibited by lock)"); | |
} else if (safety) { | |
Serial.println("(throttle inhibited by safety)"); | |
} else { | |
Serial.println("throttle UP on"); | |
Keyboard.press(KEY_F7); | |
} | |
} | |
if (sw_throttleup.risingEdge()) { | |
Serial.println("throttle UP off"); | |
Keyboard.release(KEY_F7); | |
} | |
if (sw_throttledown.fallingEdge()) { | |
if (locked) { | |
Serial.println("(throttle inhibited by lock)"); | |
} else if (safety) { | |
Serial.println("(throttle inhibited by safety)"); | |
} else { | |
Serial.println("throttle DOWN on"); | |
Keyboard.press(KEY_F6); | |
} | |
} | |
if (sw_throttledown.risingEdge()) { | |
Serial.println("throttle DOWN off"); | |
Keyboard.release(KEY_F6); | |
} | |
if (sw_iva.fallingEdge()) { | |
Serial.println("IVA"); | |
Keyboard.print("v"); | |
} | |
if (sw_view.fallingEdge()) { | |
Serial.println("VIEW"); | |
Keyboard.print("c"); | |
} | |
if (sw_navball.fallingEdge()) { | |
Serial.println("NAVBALL"); | |
Keyboard.press(KEYPAD_PERIOD); | |
Keyboard.release(KEYPAD_PERIOD); | |
} | |
if (sw_map.fallingEdge()) { | |
Serial.println("MAP"); | |
Keyboard.print("m"); | |
} | |
if (sw_sastemp.fallingEdge()) { | |
Serial.println("SAS temp down"); | |
Keyboard.press(KEY_F); | |
} | |
if (sw_sastemp.risingEdge()) { | |
Serial.println("SAS temp up"); | |
Keyboard.release(KEY_F); | |
} | |
if (sw_sastoggle.fallingEdge()) { | |
Serial.println("SAS toggle"); | |
Keyboard.print("t"); | |
} | |
if (sw_rcstoggle.fallingEdge()) { | |
Serial.println("RCS toggle"); | |
Keyboard.print("r"); | |
} | |
if (sw_prec_on.fallingEdge()) { | |
Serial.println("Precision on"); | |
precision_toggle = true; | |
} | |
if (sw_prec_off.fallingEdge()) { | |
Serial.println("Precision off"); | |
precision_toggle = false; | |
} | |
if (sw_gear.fallingEdge()) { | |
if (locked) { | |
Serial.println("(gear inhibited by lock)"); | |
} else { | |
Serial.println("GEAR"); | |
Keyboard.print("g"); | |
} | |
} | |
if (sw_brakes.fallingEdge()) { | |
if (locked) { | |
Serial.println("(brakes inhibited by lock)"); | |
} else { | |
Serial.println("BRAKES on"); | |
Keyboard.press(KEY_B); | |
} | |
} | |
if (sw_brakes.risingEdge()) { | |
Serial.println("BRAKES off"); | |
Keyboard.release(KEY_B); | |
} | |
if (sw_lights.fallingEdge()) { | |
if (locked) { | |
Serial.println("(lights inhibited by lock)"); | |
} else { | |
Serial.println("LIGHTS"); | |
Keyboard.print("u"); | |
} | |
} | |
delay(20); | |
} | |
void setLightStatus() { | |
Serial.println("lightStatus"); | |
Serial.println(locked); | |
Serial.println(safety); | |
if (locked || safety) { | |
analogWrite(blueled, 0); | |
Serial.println("OFF"); | |
} else { | |
analogWrite(blueled, 255); | |
Serial.println("ON"); | |
} | |
} | |
void showDebugNCdata() { | |
Serial.print( nc.getAccel(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getAccelX(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getAccelY(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getAccelZ(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getTiltX(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getTiltY(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getTiltZ(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getButtonC() ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getButtonZ() ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getJoyX(), DEC ); | |
Serial.print( "\t" ); | |
Serial.print( nc.getJoyY(), DEC ); | |
Serial.print( "\n" ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment