fpanels

package module
v0.0.0-...-504fb3e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 31, 2023 License: MIT Imports: 5 Imported by: 0

README

= fpanels - Go library for Logitech/Saitek flight panels
:toc:

[sidebar]
.Code status
--
image:https://travis-ci.com/bjanders/fpanels.svg?branch=master["Build Status", link="https://travis-ci.com/bjanders/fpanels"]
image:https://goreportcard.com/badge/github.com/bjanders/fpanels["Go Report Card", link="https://goreportcard.com/report/github.com/bjanders/fpanels"]
image:https://godoc.org/github.com/bjanders/fpanels?status.svg["GoDoc", link="https://godoc.org/github.com/bjanders/fpanels"]
--

NOTE: This is work in progress. The code is useable, but not much tested. The API may
still change. The library has been verified to work on Linux and Windows. On macOS 11.1 
I get a "bad access" error code.

This library provides code for interfacing with the following Logitech/Saitek
devices:

- Flight switch panel
- Flight multi panel
- Flight radio panel

Multiple devices of the same type is not supported. For example, you will not
be able to interact with two radio panels.

== The panels

=== Flight switch panel

The flight switch panel has:

- A five position switch
- Thirteen two position switches
- A two position landing gear lever
- Three red/green landing gear indicator LEDs 

The USB vendor:product ID for the panel is 06a3:0d67.

==== Landing gear LEDs

The landing gear LEDs can be enabled by sending a one byte USB control message to the device as follows:

----
00000001 Green N
00000010 Green L
00000100 Green R
00001000 Red N
00010000 Red L
00100000 Red R
xx000000 Not used
----

Enabling both the red and green LED gives a yellow color.

==== Switches

The state of the switches can be obtained by a three byte USB bulk read on
endpoint 1. The bits the bytes are as follows:

----
Byte 1
00000001 BAT
00000010 ALT
00000100 AVIONICS
00001000 FUEL
00010000 DE-ICE
00100000 PITOT
01000000 COWL
10000000 PANEL

Byte 2
00000001 BEACON
00000010 NAV
00000100 STROBE
00001000 TAXI
00010000 LANDING
00100000 OFF
01000000 R
10000000 L

Byte 3
00000001 BOTH
00000010 START
00000100 GEAR UP 
00001000 GEAR DOWN
xxxx0000 Not used
----

=== Flight multi panel

The flight multi panel has:

- A five position switch
- Eight push buttons with individually controlable backlight
- A rotary encoder
- A two position switch
- A two position momentary switch
- A pitch trim rotary encoder
- A two row segment display with five numbers on each row

The text on the left on the segment display is not controllable. The text 
shows the function of the selection switch. When IAS, HDG or CRS is
selected, then only three numbers can be shown on the top row and the
bottom row is blank.

When VS or ALT is selected, then both rows can show 5 numbers each. The 
bottom row can additionally show dashes.

The USB vendor:product ID for the panel 06a3:0d06.

==== Segment display and button LEDs

The segment display and button LEDs are controlled by sending an 11 byte USB
control message to the device. The first five bytes are for the top row, the
second five for the bottom row and the 11th byte controls the button LEDs.
The Logitech software sends 12 bytes, but no function has been found for the 12th
byte. The 12th byte is always sent as 0xff.

The 10 numbers on the segment display are encoded as follows:

----
0000xxxx Binary encoded decimal (0x00 shows 0, 0x01 shows 1, etc.)
00001111 Turns the number off
11011110 Shows a dash on the bottom row (0xde)
----

If 0x0f is set for all 10 numbers then the display is turned off, including the
text on the left of the numbers.

Other values outside the range 0-9 will also turn off the number. The Logitech
software uses 0x0f.

The 11th byte for the push button LEDs are encoded as follows:

----
00000001 AP
00000010 HDG
00000100 NAV
00001000 IAS
00010000 ALT
00100000 VS
01000000 APR
10000000 REV
----

==== Switches, buttons and encoders

The state of the switches can be obtained by a three byte USB bulk read on
endpont 1. The bits the bytes are as follows:

----
Byte 1
00000001 ALT
00000010 VS
00000100 IAS
00001000 HDG
00010000 CRS
00100000 Encoder cw
01000000 Encoder ccw
10000000 AP

Byte 2
00000001 HDG
00000010 NAV
00000100 IAS
00001000 ALT
00010000 VS
00100000 APR
01000000 REV
10000000 Throttle ARM

Byte 3
00000001 Flaps up
00000010 Flaps down
00000100 Pitch down
00001000 Pitch up
xxxx0000 Not used
----

=== Flight radio panel

The flight radio panel has:

- Two seven position function switches
- Two dual rotary encoders
- Two momentary push buttons
- Four five number segment displays

The segment displays can show numbers or dash in each position. In addition
a dot can be displayed in combination with a number.

The vendor:prduct ID for the panel is 06a3:0d05.

==== Segment displays

The segment displays are controlled by sending a 22 byte USB
control message to the device, 5 bytes per display, in the following order:
top left, top right, bottom left, bottom right.

The 20 display numbers are encoded as follows:

----
0000xxxx Binary encoded decimal (0x00 shows 0, 0x01 shows 1, etc.)
00001111 Turns the number off
1101xxxx Adds a dot to the number
1110xxxx Shows dash/minus
----

Two additional bytes have to be sent for it to work on Windows, i.e, 22 bytes
in total. These remaining two bytes can be sent as zero.

==== Switches, buttons and encoders

The state of the switches can be obtained by a three byte USB bulk read on
endpoint 1. The bits in the bytes are as follows:

----
Byte 1
00000001 1 COM1
00000010 1 COM2
00000100 1 NAV1
00001000 1 NAV2
00010000 1 ADF
00100000 1 DME
01000000 1 XPDR
10000000 2 COM1

Byte 2
00000001 2 COM2
00000010 2 NAV1
00000100 2 NAV2
00001000 2 ADF
00010000 2 DME
00100000 2 XPDR
01000000 1 ACT/STDBY
10000000 2 ACT/STDBY

Byte 3
00000001 1 inner encoder cw
00000010 1 inner encoder ccw
00000100 1 outer encoder cw
00001000 1 outer encoder ccw
00010000 2 inner encoder cw 
00100000 2 inner encoder ccw
01000000 2 outer encoder cw 
10000000 2 outer encoder ccw
----

Documentation

Overview

Package fpanels provides an interface to Logitech/Saitek flight panels.

Use the New*Panel() functions to create an instance of the specific panel type. When you are done, call the panel's Close() function.

Index

Constants

View Source
const (
	LEDAP byte = 1 << iota
	LEDHDG
	LEDNAV
	LEDIAS
	LEDALT
	LEDVS
	LEDAPR
	LEDREV
)

Multi panel button LED lights

View Source
const (
	USBVendorPanel   = 0x06a3
	USBProductRadio  = 0x0d05
	USBProductMulti  = 0x0d06
	USBProductSwitch = 0x0d67
)

USB vendor and product IDs

View Source
const (
	LEDNGreen byte = 1 << iota
	LEDLGreen
	LEDRGreen
	LEDNRed
	LEDLRed
	LEDRRed
	LEDNYellow   = LEDNGreen | LEDNRed
	LEDLYellow   = LEDLGreen | LEDLRed
	LEDRYellow   = LEDRGreen | LEDRRed
	LEDAllGreen  = LEDNGreen | LEDLGreen | LEDRGreen
	LEDAllRed    = LEDNRed | LEDLRed | LEDRRed
	LEDAllYellow = LEDNYellow | LEDLYellow | LEDRYellow
	LEDNAll      = LEDNYellow
	LEDLAll      = LEDLYellow
	LEDRAll      = LEDRYellow
	LEDAll       = LEDAllYellow
)

Switch panel landing gear lights

Variables

View Source
var DisplayMap = map[string]DisplayID{

	"ACTIVE_1":  Display1Active,
	"STANDBY_1": Display1Standby,
	"ACTIVE_2":  Display2Active,
	"STANDBY_2": Display2Standby,

	"ROW_1": Row1,
	"ROW_2": Row2,
}

DisplayMap maps the display names to a DisplayID

View Source
var LEDMap = map[string]byte{

	"N_GREEN":  LEDNGreen,
	"L_GREEN":  LEDLGreen,
	"R_GREEN":  LEDRGreen,
	"N_RED":    LEDNRed,
	"L_RED":    LEDLRed,
	"R_RED":    LEDRRed,
	"N_YELLOW": LEDNGreen | LEDNRed,
	"L_YELLOW": LEDLGreen | LEDLRed,
	"R_YELLOW": LEDRGreen | LEDRRed,

	"LED_AP":  LEDAP,
	"LED_HDG": LEDHDG,
	"LED_NAV": LEDNAV,
	"LED_IAS": LEDIAS,
	"LED_ALT": LEDALT,
	"LED_VS":  LEDVS,
	"LED_APR": LEDAPR,
	"LED_REV": LEDREV,
}

LEDMap maps a LED Id string to the corresponding LED bits

View Source
var PanelIDMap = map[string]PanelID{
	"RADIO":  Radio,
	"MULTI":  Multi,
	"SWITCH": Switch,
}

PanelIDMap maps a panel Id string to a PanelID

View Source
var SwitchIDMap = map[string]SwitchID{

	"COM1_1":     Rot1COM1,
	"COM2_1":     Rot1COM2,
	"NAV1_1":     Rot1NAV1,
	"NAV2_1":     Rot1NAV2,
	"ADF_1":      Rot1ADF,
	"DME_1":      Rot1DME,
	"XPDR_1":     Rot1XPDR,
	"COM1_2":     Rot2Com1,
	"COM2_2":     Rot2Com2,
	"NAV1_2":     Rot2NAV1,
	"NAV2_2":     Rot2NAV2,
	"ADF_2":      Rot2ADF,
	"DME_2":      Rot2DME,
	"XPDR_2":     Rot2XPDR,
	"ACT_1":      SwAct1,
	"ACT_2":      SwAct2,
	"ENC1_CW_1":  Enc1CW1,
	"ENC1_CCW_1": Enc1CCW1,
	"ENC2_CW_1":  Enc2CW1,
	"ENC2_CCW_1": Enc2CCW1,
	"ENC1_CW_2":  Enc1CW2,
	"ENC1_CCW_2": Enc1CCW2,
	"ENC2_CW_2":  Enc2CW2,
	"ENC2_CCW_2": Enc2CCW2,

	"ALT":           RotALT,
	"VS":            RotVS,
	"IAS":           RotIAS,
	"HDG":           RotHDG,
	"CRS":           RotCRS,
	"ENC_CW":        EncCW,
	"ENC_CCW":       EncCCW,
	"BTN_AP":        BtnAP,
	"BTN_HDG":       BtnHDG,
	"BTN_NAV":       BtnNAV,
	"BTN_IAS":       BtnIAS,
	"BTN_ALT":       BtnALT,
	"BTN_VS":        BtnVS,
	"BTN_APR":       BtnAPR,
	"BTN_REV":       BtnREV,
	"AUTO_THROTTLE": AutoThrottle,
	"FLAPS_UP":      FlapsUp,
	"FLAPS_DOWN":    FlapsDown,
	"TRIM_DOWN":     TrimDown,
	"TRIM_UP":       TrimUp,

	"BAT":        SwBat,
	"ALTERNATOR": SwAlternator,
	"AVIONICS":   SwAvionics,
	"FUEL":       SwFuel,
	"DEICE":      SwDeice,
	"PITOT":      SwPitot,
	"COWL":       SwCowl,
	"PANEL":      SwPanel,
	"BEACON":     SwBeacon,
	"NAV":        SwNav,
	"STROBE":     SwStrobe,
	"TAXI":       SwTaxi,
	"LANDING":    SwLanding,
	"ENG_OFF":    RotOff,
	"ALT_R":      RotR,
	"ALT_L":      RotL,
	"ALT_BOTH":   RotBoth,
	"ENG_START":  RotStart,
	"GEAR_UP":    GearUp,
	"GEAR_DOWN":  GearDown,
}

SwitchIDMap maps a switch ID string to a SwitchID

Functions

func LEDString

func LEDString(s string) (byte, error)

LEDString maps a LED name to the corresponding LED bits. The string s is case insensitive.

Types

type DisplayID

type DisplayID uint

DisplayID identifies a display on a panel

const (
	Row1 DisplayID = iota
	Row2
)

Multi panel displays

const (
	Display1Active DisplayID = iota
	Display1Standby
	Display2Active
	Display2Standby
)

Radio panel displays

func DisplayIDString

func DisplayIDString(s string) (DisplayID, error)

DisplayIDString maps a Display name to the DisplayID. The string s is case insesitive.

type LEDDisplayer

type LEDDisplayer interface {
	LEDs(leds byte)
	LEDsOn(leds byte)
	LEDsOff(leds byte)
	LEDsOnOff(leds byte, val float64)
}

LEDDisplayer priovides an interface to panels that has LEDs

type MultiPanel

type MultiPanel struct {
	// contains filtered or unexported fields
}

MultiPanel represents a Saitek/Logitech multi panel. The panel has:

- A five position switch

- Eight push buttons with individually controlable backlight.

- A rotary encoder

- A two position switch

- A two position momentary switch

- A pitch trim rotary encoder

- A two row segment display with five numbers on each row. Use DisplayString or DisplayInt to display text on the panels. The displays are identified by the Row1 and Row2 constants.

func NewMultiPanel

func NewMultiPanel() (*MultiPanel, error)

NewMultiPanel creates a new instances of the Logitech/Saitek multipanel

func (*MultiPanel) Close

func (panel *MultiPanel) Close()

func (*MultiPanel) DisplayInt

func (panel *MultiPanel) DisplayInt(display DisplayID, n int)

DisplayInt will display the integer n on the given display

func (*MultiPanel) DisplayString

func (panel *MultiPanel) DisplayString(display DisplayID, s string)

DisplayString displays the string given by s on the display given by display. The string is limited to the numbers 0-9 and spaces. Row2 can additionally show a dash/minus '-'. If any other char is used the underlying previous character is left intact. This allows you to update different areas of the dislay in sepeate calls. For example:

panel.DisplayString(Row1, "12   ")
panel.DisplayString(Row1, "** 34")
panel.DisplayString(Row1, "** 56")

will display the the following sequence on the upper display:

12
12 34
12 56

func (*MultiPanel) ID

func (panel *MultiPanel) ID() PanelID

func (*MultiPanel) IsSwitchSet

func (panel *MultiPanel) IsSwitchSet(id SwitchID) bool

func (*MultiPanel) LEDs

func (panel *MultiPanel) LEDs(leds byte)

LEDs turns on/off the LEDs given by leds. See the LED* constants. For example calling

panel.LEDs(LEDAP | LEDVS)

will turn on the AP and VS LEDs and turn off all other LEDs.

func (*MultiPanel) LEDsOff

func (panel *MultiPanel) LEDsOff(leds byte)

LEDsOff turns off the LEDs given by leds and leaves all other LED states intact. See the LED* constants. Multiple LEDs can be ORed together. For example

panel.LEDsOff(LEDAP | LEDVS)

func (*MultiPanel) LEDsOn

func (panel *MultiPanel) LEDsOn(leds byte)

LEDsOn turns on the LEDs given by leds and leaves all other LED states intact. See the LED* constants. Multiple LEDs can be ORed together, for example

panel.LEDsOn(LEDAP | LEDVS)

func (*MultiPanel) LEDsOnOff

func (panel *MultiPanel) LEDsOnOff(leds byte, val float64)

LEDsOnOff turns on or off the LEDs given by leds. If val is 0 then the LEDs will be turned offm else they will be turned on. All other LEDs are left intact. See the LED* constants. Multiple LEDs can be ORed together, for example

panel.LEDsOnOff(LEDAP | LEDVS, 1)

func (*MultiPanel) SwitchCh

func (panel *MultiPanel) SwitchCh() chan SwitchState

SwitchCh returns a channel for switch events

type PanelID

type PanelID int

PanelID identifies the panel type

const (
	Radio PanelID = iota
	Multi
	Switch
)

PanelIDs

func PanelIDString

func PanelIDString(s string) (PanelID, error)

PanelIDString maps a panel string to a PanelID. The string s is case insensitive.

type PanelSwitches

type PanelSwitches uint32

PanelSwitches is the state of all switches on a panel, one bit per switch

func (PanelSwitches) IsSet

func (switches PanelSwitches) IsSet(id SwitchID) bool

IsSet returns true if the switch id is set.

func (PanelSwitches) SwitchState

func (switches PanelSwitches) SwitchState(id SwitchID) uint

SwitchState returns the statee of the switch with ID id, 0 or 1

type RadioPanel

type RadioPanel struct {
	// contains filtered or unexported fields
}

RadioPanel represents a Saitek/Logitech radio panel. The panel has:

- Two seven position function switches

- Two dual rotary encoders

- Two momentary push buttons

- Four five number segment displays

func NewRadioPanel

func NewRadioPanel() (*RadioPanel, error)

NewRadioPanel creats a new instance of the radio panel

func (*RadioPanel) Close

func (panel *RadioPanel) Close()

func (*RadioPanel) DisplayFloat

func (panel *RadioPanel) DisplayFloat(display DisplayID, n float64, decimals int)

DisplayFloat displays the floating point number n with the given number of decimals on the given display

func (*RadioPanel) DisplayInt

func (panel *RadioPanel) DisplayInt(display DisplayID, n int)

DisplayInt displays the integer n on the given display

func (*RadioPanel) DisplayOff

func (panel *RadioPanel) DisplayOff()

DisplayOff turns the display off

func (*RadioPanel) DisplayString

func (panel *RadioPanel) DisplayString(display DisplayID, s string)

DisplayString displays the string s on the given display. The string is limited to the numbers 0-9, a dot '.', dash/minus '-' and space, with a length of max five characters. If any other character is used then the underlying previous character is left intact. This allows you to update different areas of the dislay in separate calls. For example:

panel.DisplayString(Display1Active, "12   ")
panel.DisplayString(Display1Active, "** 34")
panel.DisplayString(Display1Active, "** 56")

will display the the following sequence on the upper left display:

12
12 34
12 56

func (*RadioPanel) ID

func (panel *RadioPanel) ID() PanelID

func (*RadioPanel) IsSwitchSet

func (panel *RadioPanel) IsSwitchSet(id SwitchID) bool

func (*RadioPanel) SwitchCh

func (panel *RadioPanel) SwitchCh() chan SwitchState

SwitchCh returns a channel for switch events

type StringDisplayer

type StringDisplayer interface {
	DisplayString(display DisplayID, s string)
}

StringDisplayer provides an interface to panels that can display strings

type SwitchID

type SwitchID uint

SwitchID identifies a switch on a panel

const (
	RotALT SwitchID = iota
	RotVS
	RotIAS
	RotHDG
	RotCRS
	EncCW
	EncCCW
	BtnAP
	BtnHDG
	BtnNAV
	BtnIAS
	BtnALT
	BtnVS
	BtnAPR
	BtnREV
	AutoThrottle
	FlapsUp
	FlapsDown
	TrimDown
	TrimUp
)

Multi panel switches and buttons

const (
	Rot1COM1 SwitchID = iota
	Rot1COM2
	Rot1NAV1
	Rot1NAV2
	Rot1ADF
	Rot1DME
	Rot1XPDR
	Rot2Com1
	Rot2Com2
	Rot2NAV1
	Rot2NAV2
	Rot2ADF
	Rot2DME
	Rot2XPDR
	SwAct1
	SwAct2
	Enc1CW1
	Enc1CCW1
	Enc2CW1
	Enc2CCW1
	Enc1CW2
	Enc1CCW2
	Enc2CW2
	Enc2CCW2
)

Radio panel switches

const (
	SwBat SwitchID = iota
	SwAlternator
	SwAvionics
	SwFuel
	SwDeice
	SwPitot
	SwCowl
	SwPanel
	SwBeacon
	SwNav
	SwStrobe
	SwTaxi
	SwLanding
	RotOff
	RotR
	RotL
	RotBoth
	RotStart
	GearUp
	GearDown
)

Switch panel switches

func SwitchIDString

func SwitchIDString(s string) (SwitchID, error)

SwitchIDString maps a Switch ID string to a SwitchID. The ID string s is case insensitive.

type SwitchPanel

type SwitchPanel struct {
	// contains filtered or unexported fields
}

SwitchPanel represents a Saitek/Logitech switch panel. The panel has:

- A five position switch

- Thirteen two position switches

- A two position landing gear lever

- Three red/green landing gear indicator LEDs

func NewSwitchPanel

func NewSwitchPanel() (*SwitchPanel, error)

NewSwitchPanel create a new instance of the Logitech/Saitek switch panel

func (*SwitchPanel) Close

func (panel *SwitchPanel) Close()

func (*SwitchPanel) ID

func (panel *SwitchPanel) ID() PanelID

func (*SwitchPanel) IsSwitchSet

func (panel *SwitchPanel) IsSwitchSet(id SwitchID) bool

func (*SwitchPanel) LEDs

func (panel *SwitchPanel) LEDs(leds byte)

LEDs turns on/off the LEDs given by leds. See the LED* constants. For example calling

panel.LEDs(LEDLGreen | LEDRGreen)

will turn on the left and right green landing gear LEDs and turn off all other LEDs

func (*SwitchPanel) LEDsOff

func (panel *SwitchPanel) LEDsOff(leds byte)

LEDsOff turn off the LEDs given by leds and leaves all other LED states instact. See the switch panel LED constants. Multiple LEDs can be ORed together. For example:

panel.LEDsOff(LEDLGreen | LEDRGreen)

func (*SwitchPanel) LEDsOn

func (panel *SwitchPanel) LEDsOn(leds byte)

LEDsOn turns on the ELDS given by leds and leaves the other LED states intact. See the switch panel LED constants. Multiple LEDs can be ORed together. For example:

panel.LEDsOn(LEDLGreen | LEDRGreen)

func (*SwitchPanel) LEDsOnOff

func (panel *SwitchPanel) LEDsOnOff(leds byte, val float64)

LEDsOnOff turns on or off the LEDs given by leds. If val is 0 then the LEDs will be turned off, else they will be turned on. All other LEDs are left intact. See the switch panel LED constants. Multiple LEDs can be ORed togethe, for example:

panel.LEDsOnOff(LEDLGreen | LEDRGreen, 1)

func (*SwitchPanel) SwitchCh

func (panel *SwitchPanel) SwitchCh() chan SwitchState

SwitchCh returns a channel for switch events

type SwitchState

type SwitchState struct {
	Panel  PanelID
	Switch SwitchID
	On     bool
}

SwitchState contains the state of a switch on a panel

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL