#! /usr/bin/env python3 from dataclasses import dataclass import time import os import sys import socket from enum import Enum import hid hidraw_path = "" for device in hid.enumerate(): if device["vendor_id"] == 0x4098 and device["product_id"] == 0xbb10: hidraw_path = device["path"] if hidraw_path == "": print("Unable to find FCU device") sys.exit(1) class Leds(Enum): BACKLIGHT = 0 # 0 .. 255 SCREEN_BACKLIGHT = 1 # 0 .. 255 LOC_GREEN = 3 # all on/off AP1_GREEN = 5 AP2_GREEN = 7 ATHR_GREEN = 9 EXPED_GREEN = 11 APPR_GREEN = 13 FLAG_GREEN = 17 # 0 .. 255 EXPED_YELLOW = 30 # 0 .. 255 # A # --- # F | G | B # --- # E | | C # --- # D # A=0x80, B=0x40, C=0x20, D=0x10, E=0x02, F=0x08, G=0x04 # Bits are valid for Speed display only, all other share bits in 2 databyte per lcd 7-segment display. # Use function data_from_string_swapped to recalculate values representations = { '0' : 0xfa, '1' : 0x60, '2' : 0xd6, '3' : 0xf4, '4' : 0x6c, '5' : 0xbc, '6' : 0xbe, '7' : 0xe0, '8' : 0xfe, '9' : 0xfc, 'A' : 0xee, 'B' : 0xfe, 'C' : 0x9a, 'D' : 0x76, 'E' : 0x9e, 'F' : 0x8e, 'G' : 0xbe, 'H' : 0x6e, 'I' : 0x60, 'J' : 0x70, 'K' : 0x0e, 'L' : 0x1a, 'M' : 0xa6, 'N' : 0x26, 'O' : 0xfa, 'P' : 0xce, 'Q' : 0xec, 'R' : 0x06, 'S' : 0xbc, 'T' : 0x1e, 'U' : 0x7a, 'V' : 0x32, 'W' : 0x58, 'X' : 0x6e, 'Y' : 0x7c, 'Z' : 0xd6, '-' : 0x04, '#' : 0x36, '/' : 0x60, '\\' : 0xa0, ' ' : 0x00, } class Byte(Enum): H0 = 0 H3 = 1 A0 = 2 A1 = 3 A2 = 4 A3 = 5 A4 = 6 A5 = 7 V2 = 8 V3 = 9 V0 = 10 V1 = 11 S1 = 12 @dataclass class Flag: name : str byte : Byte mask : int value : bool = False flags = dict([("spd", Flag('spd-mach_spd', Byte.H3, 0x08, True)), ("mach", Flag('spd-mach_mach', Byte.H3, 0x04)), ("hdg", Flag('hdg-trk-lat_hdg', Byte.H0, 0x80)), ("trk", Flag('hdg-trk-lat_trk', Byte.H0, 0x40)), ("lat", Flag('hdg-trk-lat_lat', Byte.H0, 0x20, True)), ("vshdg", Flag('hdg-v/s_hdg', Byte.A5, 0x08, True)), ("vs", Flag('hdg-v/s_v/s', Byte.A5, 0x04, True)), ("ftrk", Flag('trk-fpa_trk', Byte.A5, 0x02)), ("ffpa", Flag('trk-fpa_fpa', Byte.A5, 0x01)), ("alt", Flag('alt', Byte.A4, 0x10, True)), ("hdg_managed", Flag('hdg managed', Byte.H0, 0x10)), ("spd_managed", Flag('spd managed', Byte.H3, 0x02)), ("alt_managed", Flag('alt_managed', Byte.V1, 0x10)), ("vs_horz", Flag('v/s plus horizontal', Byte.A0, 0x10, True)), ("vs_vert", Flag('v/s plus vertical', Byte.V2, 0x10)), ("lvl", Flag('lvl change', Byte.A2, 0x10, True)), ("lvl_left", Flag('lvl change left', Byte.A3, 0x10, True)), ("lvl_right", Flag('lvl change right', Byte.A1, 0x10, True)), ("fvs", Flag('v/s-fpa_v/s', Byte.V0, 0x40, True)), ("ffpa2", Flag('v/s-fpa_fpa', Byte.V0, 0x80)), ("fpa_comma", Flag('fpa_comma', Byte.V3, 0x10)), ("mach_comma", Flag('mach_comma', Byte.S1, 0x01)), ]) def swap_nibbles(x): return ( (x & 0x0F)<<4 | (x & 0xF0)>>4 ) def winwing_fcu_set_led(led, brightness): data = [0x02, 0x10, 0xbb, 0, 0, 3, 0x49, led.value, brightness, 0,0,0,0,0] cmd = bytes(data) send_data_to_hidraw(cmd) def lcd_init(): data = [0xf0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0] # init packet cmd = bytes(data) send_data_to_hidraw(cmd) def data_from_string(num_7segments, string): l = num_7segments d = [0] * (l) for i in range(min(l, len(string))): r = representations.get(string.upper()[i]) if r == None: r = 0xef print("ERROR: char '{string.upper()[i]}' not found") d[l-1-i] = r return d def data_from_string_swapped(num_7segments, string): # some 7-segemnts have wired mapping, correct ist here # return array with one byte more than lcd chars l = num_7segments d = data_from_string(l, string) d.append(0) # fix wired segment mapping special elements such at the bars at the LVL/CH for i in range(len(d)): d[i] = swap_nibbles(d[i]) for i in range(0, len(d) - 1): d[l-i] = (d[l-i] & 0x0f) | (d[l-1-i] & 0xf0) d[l-1-i] = d[l-1-i] & 0x0f return d def winwing_fcu_lcd_set(speed, heading, alt,vs, new): s = data_from_string( 3, str(speed)) h = data_from_string_swapped(3, str(heading)) a = data_from_string_swapped(5, str(alt)) v = data_from_string_swapped(4, str(vs)) bl = [0] * len(Byte) for f in flags: bl[flags[f].byte.value] |= (flags[f].mask * flags[f].value) pkg_nr = 1 data = [0xf0, 0x0, pkg_nr, 0x31, 0x10, 0xbb, 0x0, 0x0, 0x2, 0x1, 0x0, 0x0, 0xff, 0xff, 0x2, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, s[2], s[1], s[0], h[3] | bl[Byte.H3.value], h[2], h[1], h[0] | bl[Byte.H0.value], a[5] | bl[Byte.A5.value], a[4] | bl[Byte.A4.value], a[3] | bl[Byte.A3.value], a[2] | bl[Byte.A2.value], a[1] | bl[Byte.A1.value], a[0] | v[4] | bl[Byte.A0.value], v[3] | bl[Byte.V3.value], v[2] | bl[Byte.V2.value], v[1] | bl[Byte.V1.value], v[0] | bl[Byte.V0.value], 0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0] cmd = bytes(data) send_data_to_hidraw(cmd) data = [0xf0, 0x0, pkg_nr, 0x11, 0x10, 0xbb, 0x0, 0x0, 0x3, 0x1, 0x0, 0x0, 0xff, 0xff, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0] cmd = bytes(data) send_data_to_hidraw(cmd) def send_data_to_hidraw(data): global hidraw_path try: # Open the HIDRAW device hidraw = os.open(hidraw_path, os.O_RDWR | os.O_NONBLOCK) # Write data to the HIDRAW device os.write(hidraw, bytes(data)) # print("Data sent successfully to HIDRAW device.") # Close the HIDRAW device os.close(hidraw) except Exception as e: print("Error:", e) lcd_init() speed = 100 heading = 360 alt = 10000 vs = '----' winwing_fcu_set_led(Leds.BACKLIGHT, 70) winwing_fcu_set_led(Leds.EXPED_YELLOW, 70) winwing_fcu_set_led(Leds.SCREEN_BACKLIGHT, 200) winwing_fcu_lcd_set(speed, heading, alt, vs, 0x0) s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM | socket.SOCK_NONBLOCK) s.bind(('', 12000)) try: message = "d,100,360,10000,----,0.0,0.0,d" while True: time.sleep(0.1) try: while True: message, address = s.recvfrom(1024) except BlockingIOError: pass print(message) items = str(message).split(",") winwing_fcu_lcd_set(items[1], items[2], items[3], items[4], 0x0) winwing_fcu_set_led(Leds.BACKLIGHT, int(float(items[5]) * 255)) winwing_fcu_set_led(Leds.EXPED_YELLOW, int(float(items[5]) * 255)) winwing_fcu_set_led(Leds.SCREEN_BACKLIGHT, int(float(items[6]) * 255)) if "spd" in items: flags["spd"].value = True else: flags["spd"].value = False if "mach" in items: flags["mach"].value = True flags["mach_comma"].value = True else: flags["mach"].value = False flags["mach_comma"].value = False if "spd-mgt" in items: flags["spd_managed"].value = True else: flags["spd_managed"].value = False if "lat" in items: flags["lat"].value = True flags["hdg_managed"].value = True else: flags["lat"].value = False flags["hdg_managed"].value = False if "hdg-vs" in items: flags["hdg"].value = True flags["vshdg"].value = True flags["vs"].value = True flags["fvs"].value = True else: flags["hdg"].value = False flags["vshdg"].value = False flags["vs"].value = False flags["fvs"].value = False if "trk-fpa" in items: flags["trk"].value = True flags["ftrk"].value = True flags["ffpa"].value = True flags["ffpa2"].value = True if items[4] != "----": flags["fpa_comma"].value = True else: flags["fpa_comma"].value = False else: flags["trk"].value = False flags["ftrk"].value = False flags["ffpa"].value = False flags["ffpa2"].value = False flags["fpa_comma"].value = False if "plus" in items: flags["vs_vert"].value = True else: flags["vs_vert"].value = False if "ap1" in items: winwing_fcu_set_led(Leds.AP1_GREEN, 1) else: winwing_fcu_set_led(Leds.AP1_GREEN, 0) if "ap2" in items: winwing_fcu_set_led(Leds.AP2_GREEN, 1) else: winwing_fcu_set_led(Leds.AP2_GREEN, 0) if "athr" in items: winwing_fcu_set_led(Leds.ATHR_GREEN, 1) else: winwing_fcu_set_led(Leds.ATHR_GREEN, 0) if "loc" in items: winwing_fcu_set_led(Leds.LOC_GREEN, 1) else: winwing_fcu_set_led(Leds.LOC_GREEN, 0) if "appr" in items: winwing_fcu_set_led(Leds.APPR_GREEN, 1) else: winwing_fcu_set_led(Leds.APPR_GREEN, 0) except KeyboardInterrupt: s.close()