杰瑞科技汇

python serial tool

Of course! A "Python serial tool" can mean a few different things, from a simple script to send/receive data to a full-fledged GUI application. I'll guide you through the most common and useful approaches.

The Core Library: pyserial

The go-to library for serial communication in Python is pyserial. It's a powerful, cross-platform library that wraps the operating system's serial port functionality.

Installation

First, you need to install it. Open your terminal or command prompt and run:

pip install pyserial

Basic Script: Sending and Receiving Data

This is the simplest form of a "tool." It's a script that you can run from the command line to interact with a serial device.

Let's create a script named serial_test.py.

# serial_test.py
import serial
import serial.tools.list_ports
def find_serial_port():
    """Lists available serial ports and returns the first one."""
    ports = serial.tools.list_ports.comports()
    if not ports:
        print("No serial ports found.")
        return None
    print("Available serial ports:")
    for i, port in enumerate(ports):
        print(f"{i}: {port.device} - {port.description}")
    # For simplicity, we'll just return the first port.
    # In a real tool, you might let the user choose.
    return ports[0].device
def main():
    # --- Configuration ---
    # Find a port
    port_name = find_serial_port()
    if not port_name:
        return
    # Baud rate must match the device's setting
    BAUD_RATE = 9600
    # Timeout in seconds: 0 means non-blocking (read returns immediately),
    # a positive value means wait for that many seconds for data.
    TIMEOUT = 1
    print(f"\nConnecting to {port_name} at {BAUD_RATE} baud...")
    try:
        # The 'with' statement ensures the port is closed automatically
        with serial.Serial(port_name, BAUD_RATE, timeout=TIMEOUT) as ser:
            print(f"Connection successful. Port is open: {ser.is_open}")
            # --- Sending Data ---
            print("\nSending: 'Hello from Python!'")
            # .encode() converts the string to bytes, which is required for serial
            ser.write(b'Hello from Python!\n') 
            print("Data sent.")
            # --- Receiving Data ---
            print("\nWaiting for data (with a 1-second timeout)...")
            # ser.in_waiting checks how many bytes are in the buffer
            if ser.in_waiting > 0:
                # ser.read() reads the specified number of bytes
                # ser.readline() reads until a newline character is found
                received_data = ser.readline().decode('utf-8').strip()
                print(f"Received: '{received_data}'")
            else:
                print("No data received within the timeout period.")
    except serial.SerialException as e:
        print(f"Error: Could not open or use serial port {port_name}.")
        print(f"Details: {e}")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
    main()

How to Use It:

  1. Save the code as serial_test.py.
  2. Connect your serial device (e.g., an Arduino, a USB-to-TTL converter, a sensor).
  3. Run the script from your terminal: python serial_test.py
  4. The script will list available ports and attempt to connect to the first one. Make sure your device is sending data back, or modify the script to send a command that triggers a response.

Creating a Simple Command-Line Interface (CLI) Tool

A more advanced script can take arguments from the command line, making it a much more flexible tool. We'll use Python's built-in argparse module.

Let's create a script named serial_cli.py.

# serial_cli.py
import serial
import serial.tools.list_ports
import argparse
import time
def main():
    parser = argparse.ArgumentParser(description="A simple Python serial communication tool.")
    # Add arguments for port, baud rate, and data to send
    parser.add_argument('--port', '-p', type=str, help="Serial port (e.g., COM3 or /dev/ttyUSB0)")
    parser.add_argument('--baud', '-b', type=int, default=9600, help="Baud rate (default: 9600)")
    parser.add_argument('--send', '-s', type=str, help="Data string to send")
    parser.add_argument('--receive', '-r', action='store_true', help="Read data from the serial port")
    parser.add_argument('--timeout', '-t', type=float, default=1.0, help="Read timeout in seconds (default: 1.0)")
    args = parser.parse_args()
    # If port is not specified, list and ask for one
    if not args.port:
        ports = serial.tools.list_ports.comports()
        if not ports:
            print("Error: No serial ports found.")
            return
        print("Available serial ports:")
        for i, port in enumerate(ports):
            print(f"{i}: {port.device}")
        port_index = int(input("Enter the number of the port to use: "))
        port_name = ports[port_index].device
    else:
        port_name = args.port
    try:
        with serial.Serial(port_name, args.baud, timeout=args.timeout) as ser:
            print(f"Connected to {ser.name} at {ser.baudrate} baud.")
            if args.send:
                print(f"Sending: '{args.send}'")
                ser.write(args.send.encode('utf-8') + b'\n') # Add newline for many devices
            if args.receive:
                print("Waiting for data... (Press Ctrl+C to stop)")
                while True:
                    if ser.in_waiting > 0:
                        data = ser.readline().decode('utf-8').strip()
                        if data:
                            print(f"Received: {data}")
                    time.sleep(0.1) # Small delay to prevent busy-waiting
    except serial.SerialException as e:
        print(f"Error: {e}")
    except KeyboardInterrupt:
        print("\nExiting.")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
    main()

How to Use It:

This tool is much more versatile. You can combine its arguments in many ways.

  • List ports and connect to the 3rd one:

    python serial_cli.py --port /dev/ttyUSB0

    (Or use the interactive prompt if you omit --port).

  • Send a command and exit:

    python serial_cli.py -p COM3 -b 115200 -s "GET_STATUS"
  • Open a port and continuously monitor for incoming data (like a terminal):

    python serial_cli.py -p COM4 -r

    Press Ctrl+C to stop.


Creating a GUI Tool (Advanced)

For a full graphical "tool," you'll need a GUI library. Tkinter is built into Python, making it a great choice for simple tools. PyQt or PySide are more powerful for complex applications.

Here's a basic example using Tkinter. It will have a text area to see received data and an entry box to send data.

Prerequisites: Tkinter usually comes pre-installed with Python. If not, you may need to install it depending on your system.

# serial_gui_tool.py
import tkinter as tk
from tkinter import ttk, scrolledtext
import serial
import serial.tools.list_ports
import threading
class SerialGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("Python Serial Tool")
        self.root.geometry("600x400")
        self.serial_port = None
        self.running = False
        self.create_widgets()
        self.populate_ports()
    def create_widgets(self):
        # --- Port Selection Frame ---
        port_frame = ttk.Frame(self.root, padding="10")
        port_frame.pack(fill=tk.X)
        ttk.Label(port_frame, text="Port:").grid(row=0, column=0, sticky=tk.W)
        self.port_var = tk.StringVar()
        self.port_combo = ttk.Combobox(port_frame, textvariable=self.port_var, state="readonly")
        self.port_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=5)
        ttk.Label(port_frame, text="Baud Rate:").grid(row=0, column=2, sticky=tk.W, padx=(20, 0))
        self.baud_var = tk.StringVar(value="9600")
        self.baud_combo = ttk.Combobox(port_frame, textvariable=self.baud_var, state="readonly", width=10)
        self.baud_combo['values'] = ('9600', '19200', '38400', '57600', '115200')
        self.baud_combo.grid(row=0, column=3, sticky=tk.W)
        self.connect_btn = ttk.Button(port_frame, text="Connect", command=self.toggle_connection)
        self.connect_btn.grid(row=0, column=4, padx=10)
        port_frame.columnconfigure(1, weight=1)
        # --- Communication Frame ---
        comm_frame = ttk.Frame(self.root, padding="10")
        comm_frame.pack(fill=tk.BOTH, expand=True)
        # --- Receive Area ---
        ttk.Label(comm_frame, text="Received Data:").pack(anchor=tk.W)
        self.receive_area = scrolledtext.ScrolledText(comm_frame, wrap=tk.WORD, state='disabled')
        self.receive_area.pack(fill=tk.BOTH, expand=True)
        # --- Send Frame ---
        send_frame = ttk.Frame(comm_frame)
        send_frame.pack(fill=tk.X, pady=(10, 0))
        self.send_entry = ttk.Entry(send_frame)
        self.send_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.send_entry.bind('<Return>', self.send_data)  # Send on Enter key
        self.send_btn = ttk.Button(send_frame, text="Send", command=self.send_data)
        self.send_btn.pack(side=tk.RIGHT, padx=(5, 0))
    def populate_ports(self):
        ports = [port.device for port in serial.tools.list_ports.comports()]
        if ports:
            self.port_combo['values'] = ports
            self.port_combo.current(0)
        else:
            self.port_combo['values'] = ['No ports found']
            self.port_combo.current(0)
    def toggle_connection(self):
        if self.serial_port and self.serial_port.is_open:
            self.disconnect()
        else:
            self.connect()
    def connect(self):
        try:
            port = self.port_var.get()
            baud = int(self.baud_var.get())
            self.serial_port = serial.Serial(port, baud, timeout=1)
            self.connect_btn.config(text="Disconnect")
            self.running = True
            # Start a separate thread for reading data
            self.read_thread = threading.Thread(target=self.read_serial, daemon=True)
            self.read_thread.start()
            self.log_message(f"Connected to {port} at {baud} baud.")
        except Exception as e:
            self.log_message(f"Connection Error: {e}")
    def disconnect(self):
        if self.serial_port and self.serial_port.is_open:
            self.running = False
            self.serial_port.close()
            self.connect_btn.config(text="Connect")
            self.log_message("Disconnected.")
    def read_serial(self):
        """Continuously reads from the serial port in a separate thread."""
        while self.running and self.serial_port and self.serial_port.is_open:
            if self.serial_port.in_waiting > 0:
                data = self.serial_port.readline().decode('utf-8', errors='replace').strip()
                if data:
                    # Use .after to safely update the GUI from another thread
                    self.root.after(0, lambda d=data: self.log_message(d))
            time.sleep(0.1) # Prevent busy-waiting
    def send_data(self, event=None):
        data_to_send = self.send_entry.get()
        if data_to_send and self.serial_port and self.serial_port.is_open:
            try:
                self.serial_port.write(data_to_send.encode('utf-8') + b'\n')
                self.log_message(f"Sent: {data_to_send}")
                self.send_entry.delete(0, tk.END)
            except Exception as e:
                self.log_message(f"Send Error: {e}")
    def log_message(self, message):
        """Appends a message to the receive area."""
        self.receive_area.config(state='normal')
        self.receive_area.insert(tk.END, message + '\n')
        self.receive_area.see(tk.END)  # Auto-scroll to the end
        self.receive_area.config(state='disabled')
    def on_closing(self):
        self.disconnect()
        self.root.destroy()
# You need to import time for the read_thread sleep
import time
if __name__ == "__main__":
    root = tk.Tk()
    app = SerialGUI(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()

How to Use It:

  1. Save the code as serial_gui_tool.py.
  2. Run it: python serial_gui_tool.py.
  3. A window will appear. Select your COM port and baud rate from the dropdowns.
  4. Click "Connect".
  5. You can now type in the "Send" box and press Enter to send data. All received data will appear in the "Received Data" area automatically.

Summary of Tools

Tool Type Best For Pros Cons
Basic Script Quick tests, simple automation. Simple, no dependencies beyond pyserial. Not flexible, hard-coded parameters.
CLI Tool Powerful scripting, automation, server tasks. Flexible (command-line args), scriptable, lightweight. No visual feedback, command-line only.
GUI Tool Interactive use, debugging, user-friendly applications. Visual, easy to use for non-technical users. More complex code, requires a GUI library.

Choose the one that best fits your needs! For most general-purpose "tinkering," the CLI tool is the most powerful and versatile.

分享:
扫描分享到社交APP
上一篇
下一篇