John wrote:
If not, then there is something different between the exe’s code and every code I tried in LabVIEW and Python as well as your code.
From that I take it that only the Qt app works.
The manual talks about TSR (Terminate and Stay Resident) and gives examples in Basic, which is from quite some years ago, so maybe the modern PCs are sending the bytes too fast for it.
The fact that it gives a maximum time between the bytes and has the “Pace” command (to slow down the responses) also suggest that the slower speed of computers at the time could sometimes have been a problem.
So, here’s a new version of the GUI in which you can set the delay between the bytes. It’s initially 0 ms, but you could try increasing it (up to a maximum of 200 ms):
# A simple GUI for testing talking to a FT-1000MP_Mark-V radio via the CAT/serial port.
#
from serial import Serial, SerialException
from tkinter.messagebox import showerror
import tkinter as tk
# The port to use.
DEFAULT_PORT = "COM5"
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title("Radio GUI")
# Add a frame for the buttons.
button_frame = tk.Frame(self)
button_frame.pack(side=tk.TOP, fill=tk.X)
# Add a "Port" entry.
tk.Label(button_frame, text="Port:").pack(side=tk.LEFT)
self.port_var = tk.StringVar(value=DEFAULT_PORT)
self.port_entry = tk.Entry(button_frame, width=10, textvariable=self.port_var)
self.port_entry.pack(side=tk.LEFT)
# Add control over the delay between bytes.
tk.Label(button_frame, text="Delay between bytes (ms):").pack(side=tk.LEFT)
self.delay_var = tk.IntVar(value=0)
self.delay_entry = tk.Entry(button_frame, width=10, textvariable=self.delay_var)
self.delay_entry.pack(side=tk.LEFT)
# Add the "Connect" button.
self.connect_button = tk.Button(button_frame, text="Connect", padx=5, pady=5, command=self.on_connect)
self.connect_button.pack(side=tk.LEFT)
# Add the "Clear log" button.
self.clear_log_button = tk.Button(button_frame, text="Clear log", padx=5, pady=5, command=self.on_clear_log)
self.clear_log_button.pack(side=tk.LEFT)
# Add the "A -> B" button.
self.copy_vfo_a_button = tk.Button(button_frame, text="A -> B", padx=5, pady=5, command=lambda: self.send("A -> B", bytes.fromhex("00 00 00 00 85")))
self.copy_vfo_a_button.pack(side=tk.LEFT)
# Add the "Read status flags" button.
self.read_status_flags_button = tk.Button(button_frame, text="Read status flags", padx=5, pady=5, command=lambda: self.send("Read status flags", bytes.fromhex("00 00 00 00 FA")))
self.read_status_flags_button.pack(side=tk.LEFT)
# Add a frame to display the log.
response_frame = tk.Frame(self)
response_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
# Add a title label for the log frame.
tk.Label(response_frame, text="Log").pack(side=tk.TOP)
# Add a listbox with a scrollbar to display the log.
self.log_listbox = tk.Listbox(response_frame)
self.log_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = tk.Scrollbar(response_frame, orient=tk.VERTICAL, command=self.log_listbox.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.log_listbox.configure(yscrollcommand=scrollbar.set)
# The serial port and response buffer.
self.serial_port = None
self.response_buffer = b""
# Disable the buttons initially.
self.enable_buttons(False)
self.send_buffer = b""
# Periodically check for a response.
self.on_tick()
def on_tick(self):
try:
delay = self.delay_var.get()
except tk.TclError:
delay = 0
if self.serial_port is not None:
if self.send_buffer:
if delay == 0:
# Send it all at once.
self.serial_port.send(self.send_buffer)
self.send_buffer = b""
else:
# Send by 1 byte now, any others later.
self.serial_port.send(self.send_buffer[ : 1])
self.send_buffer = self.send_buffer[1 : ]
# Read any (additional) response from the serial port. We want to append to the current response, if any.
if self.response_buffer:
self.log_listbox.delete(tk.END)
self.response_buffer += self.serial_port.read(self.serial_port.in_waiting)
if self.response_buffer:
self.log_listbox.insert(tk.END, self.response_buffer.hex(" ").upper())
# Schedule the next check or send.
if delay == 0:
# We're sending it all at once, so we don't need a delay for it.
delay = 200
self.after(delay, self.on_tick)
def on_connect(self):
if self.serial_port is None:
# Ensure that the delay is valid.
delay = self.delay_var.get()
if delay < 0:
delay = 0
elif delay > 200:
delay = 200
self.delay_var.set(delay)
# Try to connect to the serial port.
try:
self.serial_port = Serial(port=self.port_var.get(), baudrate=4800, parity="N", stopbits=2, bytesize=8)
except SerialException as ex:
showerror(self.title(), str(ex))
else:
# We connected successfully, so enable the buttons.
self.enable_buttons(True)
self.connect_button["text"] = "Disconnect"
else:
# Disconnect from the serial port and disable the buttons.
self.serial_port.close()
self.serial_port = None
self.connect_button["text"] = "Connect"
self.enable_buttons(False)
def on_clear_log(self):
self.log_listbox.delete(0, tk.END)
self.response_buffer = b""
def send(self, description, data):
if self.serial_port is None:
return
# We're going to send a command, so log the action and clear the response buffer.
self.log_listbox.insert(tk.END, f"[{description}]")
self.response_buffer = b""
try:
self.serial_port.write(data)
self.serial_port.flush()
except SerialException as ex:
showerror(self.title(), str(ex))
self.serial_port.close()
self.serial_port = None
self.connect_button["text"] = "Connect"
self.enable_buttons(False)
def enable_buttons(self, enable):
# These must be disabled when connected.
new_state = tk.DISABLED if enable else tk.NORMAL
self.port_entry["state"] = new_state
self.delay_entry = new_state
# These must be enabled when connected.
new_state = tk.NORMAL if enable else tk.DISABLED
self.copy_vfo_a_button["state"] = new_state
self.read_status_flags_button["state"] = new_state
App().mainloop()