diff --git a/Pedestal/PedestalConnectBox/README.md b/Pedestal/PedestalConnectBox/README.md index 9c193a5..38bb513 100644 --- a/Pedestal/PedestalConnectBox/README.md +++ b/Pedestal/PedestalConnectBox/README.md @@ -1,4 +1,38 @@ # Pedestal Connect Box +## USB HID +### Input +25 Bytes of input, consisting of +* 1 Byte reportId (always value 1) +* 22 Bytes for 11 axis (each axis is 2 bytes and has a range of 0 - 4095) +* 2 Bytes for buttons + +Inputs report usually via USB HID so you only have to map them. + +### Output +9 Byte output, consisting of +* 1 Byte for the reportId (always value 1) +* 2 Bytes for the brightness of the integrated lighting (range 0-48000) +* 2 Bytes for generic controls (such as calibration) +* 4 Bytes for indicator LEDs, each bit controls one item: + * 0x00000001: ECAM CLR + * 0x00000002: ECAM APU + * 0x00000004: ECAM ENG + * 0x00000008: ECAM COND + * 0x00000010: ECAM BLEED + * 0x00000020: ECAM STS + * 0x00000040: ECAM DOOR + * 0x00000080: ECAM PRESS + * 0x00000100: ECAM WHEEL + * 0x00000200: ECAM ELEC + * 0x00000400: ECAM F/CTL + * 0x00000800: ECAM HYD + * 0x00001000: ECAM FUEL + * 0x00002000: DOOR OPEN + * 0x00004000: DOOR FAULT + * 0x00008000: ENG 1 FAULT + * 0x00010000: ENG 1 FIRE + * 0x00020000: ENG 2 FAULT + * 0x00040000: ENG 2 FIRE ## Pinout ### General pins PA15|Backlight PWM diff --git a/Pedestal/PedestalConnectBox/firmware/descriptor.wara b/Pedestal/PedestalConnectBox/firmware/descriptor.wara index de67f75..89af8fd 100644 --- a/Pedestal/PedestalConnectBox/firmware/descriptor.wara +++ b/Pedestal/PedestalConnectBox/firmware/descriptor.wara @@ -7,24 +7,43 @@ usage = ['Generic Desktop', 'Joystick'] usage = ['Generic Desktop', 'Pointer'] [[applicationCollection.inputReport.physicalCollection.variableItem]] -usage = ['Generic Desktop', 'X'] +usage = ['Generic Desktop', 'Z'] logicalValueRange = [0, 65535] [[applicationCollection.inputReport.physicalCollection.variableItem]] usage = ['Generic Desktop', 'Z'] logicalValueRange = [0, 65535] -[[applicationCollection.inputReport.variableItem]] -usageRange = ['Button', 'Button 1', 'Button 16'] -logicalValueRange = [0, 1] - -[[applicationCollection.inputReport]] - -[[applicationCollection.inputReport.physicalCollection]] -usage = ['Generic Desktop', 'Pointer'] +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] [[applicationCollection.inputReport.physicalCollection.variableItem]] -usage = ['Generic Desktop', 'X'] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] + +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] + +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] + +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] + +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] + +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] +logicalValueRange = [0, 65535] + +[[applicationCollection.inputReport.physicalCollection.variableItem]] +usage = ['Generic Desktop', 'Z'] logicalValueRange = [0, 65535] [[applicationCollection.inputReport.physicalCollection.variableItem]] @@ -44,3 +63,11 @@ logicalValueRange = [0, 48000] [[applicationCollection.outputReport.variableItem]] usage = ['Haptics', 'Manual Trigger'] logicalValueRange = [0, 65535] + +[[applicationCollection.outputReport.variableItem]] +usage = ['Haptics', 'Manual Trigger'] +logicalValueRange = [0, 65535] + +[[applicationCollection.outputReport.variableItem]] +usage = ['Haptics', 'Manual Trigger'] +logicalValueRange = [0, 65535] diff --git a/Pedestal/PedestalConnectBox/firmware/src/device.rs b/Pedestal/PedestalConnectBox/firmware/src/device.rs index a3e9eb4..0ca7a8c 100644 --- a/Pedestal/PedestalConnectBox/firmware/src/device.rs +++ b/Pedestal/PedestalConnectBox/firmware/src/device.rs @@ -22,12 +22,20 @@ pub const CUSTOM_DESCRIPTOR: &[u8] = &[ 0x85, 0x01, // ReportId(1) 0x09, 0x01, // UsageId(Pointer[0x0001]) 0xA1, 0x00, // Collection(Physical) - 0x09, 0x30, // UsageId(X[0x0030]) - 0x09, 0x31, // UsageId(Y[0x0031]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) + 0x09, 0x32, // UsageId(Z[0x0032]) 0x09, 0x32, // UsageId(Z[0x0032]) 0x15, 0x00, // LogicalMinimum(0) - 0x26, 0xFF, 0x0F, // LogicalMaximum(4,095) - 0x95, 0x03, // ReportCount(3) + 0x27, 0xFF, 0xFF, 0x00, 0x00, // LogicalMaximum(65,535) + 0x95, 0x0B, // ReportCount(11) 0x75, 0x10, // ReportSize(16) 0x81, 0x02, // Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField) 0xC0, // EndCollection() @@ -38,42 +46,41 @@ pub const CUSTOM_DESCRIPTOR: &[u8] = &[ 0x95, 0x10, // ReportCount(16) 0x75, 0x01, // ReportSize(1) 0x81, 0x02, // Input(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, BitField) - 0x85, 0x02, // ReportId(2) - 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) - 0x09, 0x01, // Usage (0x01) + 0x05, 0x0E, // UsagePage(Haptics[0x000E]) + 0x09, 0x21, // UsageId(Manual Trigger[0x0021]) 0x27, 0x80, 0xBB, 0x00, 0x00, // LogicalMaximum(48,000) 0x95, 0x01, // ReportCount(1) 0x75, 0x10, // ReportSize(16) 0x91, 0x02, // Output(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, NonVolatile, BitField) 0x09, 0x21, // UsageId(Manual Trigger[0x0021]) - 0x27, 0xFF, 0xFF, 0x00, 0x00, // LogicalMaximum(65,535) - 0x91, 0x02, // Output(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, NonVolatile, BitField) + 0x09, 0x21, // UsageId(Manual Trigger[0x0021]) 0x09, 0x21, // UsageId(Manual Trigger[0x0021]) 0x27, 0xFF, 0xFF, 0x00, 0x00, // LogicalMaximum(65,535) + 0x95, 0x03, // ReportCount(3) 0x91, 0x02, // Output(Data, Variable, Absolute, NoWrap, Linear, PreferredState, NoNullPosition, NonVolatile, BitField) 0xC0, // EndCollection() ]; #[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)] -#[packed_struct(endian = "lsb", size_bytes = "9")] // MUST be <= 64 else we get problem cause InBytes64 or OutBytes64 +#[packed_struct(endian = "lsb", size_bytes = "25")] // MUST be <= 64 else we get problem cause InBytes64 or OutBytes64 pub struct CustomInputReport { #[packed_field] pub report_id: u8, #[packed_field] - pub axis: [u16; 3], + pub axis: [u16; 11], #[packed_field] pub buttons: u16, } #[derive(Clone, Copy, Debug, Eq, PartialEq, Default, PackedStruct)] -#[packed_struct(endian = "lsb", size_bytes = "7")] +#[packed_struct(endian = "lsb", size_bytes = "9")] pub struct CustomOutputReport { #[packed_field] pub report_id: u8, #[packed_field] pub integ_lt: u16, pub generic: u16, - pub leds: u16, + pub leds: u32, } pub struct CustomDevice<'a, B: UsbBus> { @@ -90,7 +97,7 @@ impl<'a, B: UsbBus> CustomDevice<'a, B> { } pub fn read_report(&mut self) -> Result { - let mut data = [0; 7]; + let mut data = [0; 9]; self.interface .read_report(&mut data[..]) .map(|_| CustomOutputReport::unpack(&data).unwrap()) diff --git a/Pedestal/PedestalConnectBox/firmware/src/main.rs b/Pedestal/PedestalConnectBox/firmware/src/main.rs index 06fd672..a057bcc 100644 --- a/Pedestal/PedestalConnectBox/firmware/src/main.rs +++ b/Pedestal/PedestalConnectBox/firmware/src/main.rs @@ -32,10 +32,44 @@ use bytemuck::{bytes_of, bytes_of_mut, try_from_bytes, Pod, Zeroable}; // Set layout version const FLASH_LAYOUT_VERSION: u16 = 0; +macro_rules! define_output_states { + ($bit:literal, $pin:ident, $output:ident, $io_pins:ident) => { + if $output.leds & $bit == $bit { + $io_pins.$pin.set_high(); + } + else { + $io_pins.$pin.set_low(); + } + }; +} + +macro_rules! define_ecam_output_states_row { + ($index:literal, $pin:ident, $ecam_row:ident, $io_pins:ident) => { + if $ecam_row[$index] == 1 { + $io_pins.$pin.set_high(); + } + else { + $io_pins.$pin.set_low(); + } + }; +} + +macro_rules! define_ecam_output_states_col { + ($index:literal, $pin:ident, $ecam_col:ident, $io_pins:ident) => { + if $ecam_col[$index] == 1 { + $io_pins.$pin.set_high(); + } + else { + $io_pins.$pin.set_low(); + } + }; +} + struct MyPins { pa1: Pin<'A', 1, Analog>, pa2: Pin<'A', 2, Analog>, pb0: Pin<'B', 0, Output>, + pb12: Pin<'B', 12, Output>, pc1: Pin<'C', 1, Input>, pc3: Pin<'C', 3, Analog>, pc15: Pin<'C', 15, Input>, @@ -65,7 +99,7 @@ impl CalibrationData { #[derive(Clone)] #[derive(Zeroable)] struct Calibration { - data: [CalibrationData; 3], + data: [CalibrationData; 11], } impl Calibration { @@ -75,7 +109,7 @@ impl Calibration { }; fn new () -> Calibration { return Calibration { - data: [CalibrationData::new(0, CalibrationData::ADC_MAX); 3], + data: [CalibrationData::new(0, CalibrationData::ADC_MAX); 11], }; } } @@ -93,6 +127,8 @@ fn main() -> ! { let mut gpioa = p.GPIOA.split(); let mut gpiob = p.GPIOB.split(); let mut gpioc = p.GPIOC.split(); + let mut gpiod = p.GPIOD.split(); + let mut gpioe = p.GPIOE.split(); // configure clock let clocks = rcc @@ -135,25 +171,32 @@ fn main() -> ! { let mut cal = load_calibration(&mut flash_writer); // ====================== Pin setup ================= - let mut input_pins = MyPins { + let mut io_pins = MyPins { pa1: gpioa.pa1.into_analog(&mut gpioa.crl), pa2: gpioa.pa2.into_analog(&mut gpioa.crl), pb0: gpiob.pb0.into_push_pull_output(&mut gpiob.crl), + pb12: gpiob.pb12.into_push_pull_output(&mut gpiob.crh), pc1: gpioc.pc1.into_pull_down_input(&mut gpioc.crl), pc3: gpioc.pc3.into_analog(&mut gpioc.crl), pc15: gpioc.pc15.into_pull_down_input(&mut gpioc.crh), }; -// let mut last = get_report(&mut input_pins, &mut adc1, &cal); +// let mut last = get_report(&mut io_pins, &mut adc1, &cal); // ====================== PWM setup ================= let mut afio = p.AFIO.constrain(); let c1 = gpioa.pa0.into_alternate_push_pull(&mut gpioa.crl); - let mut pwm = p + let mut integ_lt_pwm = p .TIM2 .pwm_hz::(c1, &mut afio.mapr, 1.kHz(), &clocks); - pwm.enable(Channel::C1); - let pwm_max = pwm.get_max_duty(); //48000 in our case + integ_lt_pwm.enable(Channel::C1); + //TODO +// let c3 = gpiob.pb10.into_alternate_push_pull(&mut gpiob.crh); +// let mut rudder_trim_display_pwm = p +// .TIM2 +// .pwm_hz::(c3, &mut afio.mapr, 1.kHz(), &clocks); +// rudder_trim_display_pwm.enable(Channel::C3); + let pwm_max = integ_lt_pwm.get_max_duty(); //48000 in our case // ====================== Timer setup =============== // let timer = Instant; @@ -161,20 +204,21 @@ fn main() -> ! { // ====================== Main loop ================= loop { - let report = get_report(&mut input_pins, &mut adc1, &cal); + let report = get_report(&mut io_pins, &mut adc1, &cal); // TODO figure out timer and only send in like 1ms intervals or on change // if report != last { match consumer.device().write_report(&report) { Err(UsbHidError::WouldBlock) => {} Err(UsbHidError::UsbError(usb_device::UsbError::BufferOverflow)) => { - core::panic!("Failed to write consumer report, report is too big: {:?}", e) + core::panic!("Failed to write consumer report, report is too big") } Ok(_) => { // last = report; } Err(e) => { - // TODO use a suitable pin once we know the pinout -// input_pins.pb0.set_high(); // set as indicator that this has happened + // set as indicator that this has happened +// io_pins.pe1.set_high(); +// io_pins.pe4.set_high(); core::panic!("Failed to write consumer report: {:?}", e) } } @@ -192,31 +236,54 @@ fn main() -> ! { else { pwm_val = output.integ_lt; } - pwm.set_duty(Channel::C1, pwm_val); + integ_lt_pwm.set_duty(Channel::C1, pwm_val); // LED outputs - if output.leds & 0x1 == 0x1 { - input_pins.pb0.set_high(); - } - else { - input_pins.pb0.set_low(); - } + // ECAM + let mut ecam_row = [0; 3]; + let mut ecam_col = [0; 6]; + // Match row and col + if output.leds & 0x54A != 0 { ecam_row[0] = 1; } + if output.leds & 0x1A94 != 0 { ecam_row[1] = 1; } + if output.leds & 0x21 != 0 { ecam_row[2] = 1; } + if output.leds & 0x6 != 0 { ecam_col[0] = 1; } + if output.leds & 0x18 != 0 { ecam_col[1] = 1; } + if output.leds & 0xE0 != 0 { ecam_col[2] = 1; } + if output.leds & 0x300 != 0 { ecam_col[3] = 1; } + if output.leds & 0xC00 != 0 { ecam_col[4] = 1; } + if output.leds & 0x1001 != 0 { ecam_col[5] = 1; } + // Set ECAM Out + define_ecam_output_states_row!(0, pb12, ecam_row, io_pins); // Row 1 +// define_ecam_output_states_row!(1, pe13, ecam_row, io_pins); // Row 2 +// define_ecam_output_states_row!(2, pe9, ecam_row, io_pins); // Row 3 +// define_ecam_output_states_col!(0, pd9, ecam_row, io_pins); // Col 1 +// define_ecam_output_states_col!(1, pc7, ecam_row, io_pins); // Col 2 +// define_ecam_output_states_col!(2, pc8, ecam_row, io_pins); // Col 3 +// define_ecam_output_states_col!(3, pd10, ecam_row, io_pins); // Col 4 +// define_ecam_output_states_col!(4, pd8, ecam_row, io_pins); // Col 5 +// define_ecam_output_states_col!(5, pb11, ecam_row, io_pins); // Col 6 + // Other Indicators +// define_output_states!(0x2000, pb7, output, io_pins); // DOOR OPEN +// define_output_states!(0x4000, pb6, output, io_pins); // DOOR FAULT +// define_output_states!(0x8000, pe2, output, io_pins); // ENG 1 FAULT +// define_output_states!(0x10000, pe1, output, io_pins); // ENG 1 FIRE +// define_output_states!(0x20000, pe3, output, io_pins); // ENG 2 FAULT // Check generic input field // Calibration bit if output.generic & 0x1 == 0x1 { calibration_active = true; if !calibration_min_done && output.generic & 0x2 == 0x2 { - cal.data[0].min = adc1.read(&mut input_pins.pa1).unwrap(); - cal.data[1].min = adc1.read(&mut input_pins.pa2).unwrap(); + cal.data[0].min = adc1.read(&mut io_pins.pa1).unwrap(); + cal.data[1].min = adc1.read(&mut io_pins.pa2).unwrap(); calibration_min_done = true; } } else { if calibration_active { let mut values: [u16; 2] = [0; 2]; - values[0] = adc1.read(&mut input_pins.pa1).unwrap(); - values[1] = adc1.read(&mut input_pins.pa2).unwrap(); + values[0] = adc1.read(&mut io_pins.pa1).unwrap(); + values[1] = adc1.read(&mut io_pins.pa2).unwrap(); let mut i = 0; loop { if values[i] > cal.data[i].min { @@ -233,7 +300,7 @@ fn main() -> ! { } let save_success = save_calibration(&mut flash_writer, &cal); if save_success { - pwm.set_duty(Channel::C1, pwm_max); + integ_lt_pwm.set_duty(Channel::C1, pwm_max); } } calibration_active = false; @@ -256,13 +323,13 @@ fn calculate_factor(min: u16, max: u16) -> f32 { // Returns a CustomInputReport from the inputs given fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc, cal: &Calibration) -> CustomInputReport { - let mut values: [u16; 3] = [0; 3]; + // Read axis + let mut values: [u16; 11] = [0; 11]; values[0] = adc1.read(&mut pins.pa1).unwrap(); values[1] = adc1.read(&mut pins.pc3).unwrap(); - let mut buttons: u16 = 0; - // Axis - let mut values_norm: [u16; 3] = [0; 3]; + // Apply calibration to axis data + let mut values_norm: [u16; 11] = [0; 11]; let mut i = 0; loop { if values[i] < cal.data[i].min { @@ -281,18 +348,8 @@ fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc, cal: &Calibrati } // Buttons -// if pins.pb0.is_high() { -// buttons += 0x01; -// } - if pins.pc1.is_high() { - buttons += 0x02; - } -// if pins.pc3.is_high() { -// buttons += 0x04; -// } - if pins.pc15.is_high() { - buttons += 0x08; - } + let mut buttons: u16 = 0; +// if pins.pb0.is_high() { buttons += 0x1; } CustomInputReport { report_id: 1,