You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
AirTrapMine/src/GUI/xbee_gcs.py

128 lines
3.8 KiB
Python

import serial
import json
import threading
import time
from pynput import keyboard
SYSTEM_ID = 2
try:
ser = serial.Serial('/dev/ttyUSB1', 115200, timeout=0.1)
print("XBee 地面站啟動")
print("[1] START [2] STOP [3] GOTO [Esc] 退出程式")
except Exception as e:
print(f"無法開啟序列埠: {e}")
exit()
GPS_TYPE_LABELS = {
0: "no GPS",
1: "no fix",
2: "2D",
3: "3D",
4: "DGPS",
5: "RTK float",
6: "RTK fixed",
}
def format_uav_data(uav_data):
system_id = uav_data.get("s", "?")
mode = uav_data.get("m", "?")
battery = uav_data.get("b", "?")
position = uav_data.get("p", "?")
parts = [
f"[UAV] ID:{system_id}",
f"Mode:{mode}",
f"Bat:{battery}%",
f"Pos:{position}",
]
if "a" in uav_data:
armed_text = "ARMED" if int(uav_data.get("a", 0)) == 1 else "DISARMED"
parts.append(f"Arm:{armed_text}")
ypr = uav_data.get("ypr")
if isinstance(ypr, list) and len(ypr) >= 3:
parts.append(f"Height:{uav_data.get('h', '?')}m")
parts.append(f"Yaw:{ypr[0]}deg")
parts.append(f"Pitch:{ypr[1]}deg")
parts.append(f"Roll:{ypr[2]}deg")
elif "y" in uav_data:
parts.append(f"Height:{uav_data.get('h', '?')}m")
parts.append(f"Yaw:{uav_data.get('y')}deg")
elif "h" in uav_data:
parts.append(f"Heading:{uav_data.get('h')}")
if "v" in uav_data:
parts.append(f"Vel:{uav_data.get('v')}m/s")
if "g" in uav_data:
gps_type = uav_data.get("g")
parts.append(f"GPS:{gps_type}({GPS_TYPE_LABELS.get(gps_type, 'unknown')})")
dop = uav_data.get("d")
if isinstance(dop, list) and len(dop) >= 2:
parts.append(f"DOP(H/V):{dop[0]}/{dop[1]}")
return " | ".join(parts)
def receive_thread():
while True:
try:
if ser.in_waiting > 0:
line = ser.readline().decode('utf-8', errors='ignore').strip()
if line:
try:
data = json.loads(line)
# 判斷是確認訊息ack還是 UAV 狀態
if "result" in data and "command" in data and "message" in data:
# 這是確認訊息
result = data.get("result")
cmd = data.get("command")
msg = data.get("message")
print(f"\n[確認] 命令:{cmd} 狀態:{result} 訊息:{msg}", flush=True)
else:
# 這是 UAV 狀態訊息
print(format_uav_data(data), flush=True)
except json.JSONDecodeError:
pass
except Exception as e:
print(f"\n[接收錯誤] {e}")
time.sleep(0.01)
t = threading.Thread(target=receive_thread, daemon=True)
t.start()
def send_command(command, position=None):
try:
if command not in {"START", "STOP", "GOTO"}:
raise ValueError("指令必須為大寫 START、STOP 或 GOTO")
cmd = {
"s": SYSTEM_ID,
"c": command
}
if position is not None:
cmd["p"] = position
payload = json.dumps(cmd, separators=(',', ':')) + "\n"
ser.write(payload.encode('utf-8'))
print(f"\n>> [指令已發送] {payload.strip()}")
except Exception as e:
print(f"\n發送失敗: {e}")
def on_press(key):
try:
if key.char == "1":
send_command("START")
elif key.char == "2":
send_command("STOP")
elif key.char == "3":
send_command("GOTO", [24.1199660, 120.677230])
except AttributeError:
if key == keyboard.Key.esc:
print("\n程式結束")
return False
with keyboard.Listener(on_press=on_press) as listener:
listener.join()