Skip to content

Instantly share code, notes, and snippets.

@rooreynolds
Last active February 19, 2019 18:21
Show Gist options
  • Save rooreynolds/c4c3b9bcfc1f99f4d21a to your computer and use it in GitHub Desktop.
Save rooreynolds/c4c3b9bcfc1f99f4d21a to your computer and use it in GitHub Desktop.
KSP controller
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
/*
* 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);
}
/*
* 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