Overview

This Python script is designed to establish a serial communication with a device (referred to as the Burro IPA) over a specified serial port. The main purpose of the script is to send a handshake message (@HLWD*) to the device and wait for an acknowledgment response (#ACK*). If the acknowledgment is received, it indicates that the device is ready for communication. The script includes error handling mechanisms to manage issues with the serial connection and allows for graceful termination via a keyboard interrupt.

Functions

  • The main function establishes serial communication over a specified USB or TTL connection using the provided port and baud rate.
  • It enters a continuous loop to send a request message (@HLWD_) to the Burro IPA robot, initiating a handshake to establish communication.
  • Upon receiving a response from the robot, it processes the incoming data to confirm if an acknowledgment (#ACK_) is received.
  • If the acknowledgment is successful, the function prepares to send a status check request (?STTN_) to the robot, querying its current station status.
  • It reads the response from the robot, interprets the data, and checks if the robot is stopped at a station.
  • If the robot is confirmed to be stopped, the function sends a specific message to the robot and activates an LED to indicate the status visually.

Example

example_client_handshake.py
import serial
import logging
import argparse
import sys
import time


def setup_logging():
    """ Set up the logging format. """
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')


def parse_arguments():
    """ Parse command line arguments. """
    parser = argparse.ArgumentParser(description="Send a request for communication over serial to the Burro IPA.",
                                     epilog="Example: python3 example_client_handshake.py --port /dev/ttyUSB1")
    parser.add_argument("--port", default="/dev/ttyUSB0", help="Serial port to connect to.")
    parser.add_argument("--baud", type=int, default=115200, help="Baud rate for the serial connection.")
    parser.add_argument("--timeout", type=float, default=0.5, help="Read timeout for the serial connection.")
    return parser.parse_args()


def main():
    # Setup for some nice script tools
    setup_logging()
    args = parse_arguments()
    logging.info(f"Open serial device: port={args.port}, baud={args.baud}, timeout={args.timeout}")

    while True:
        try:
            # Initialize the serial connection
            device = serial.Serial(args.port, args.baud, timeout=args.timeout)
        except serial.SerialException as e:
            logging.error(f"Failed to open serial port: {e}")

        try:
            # Send the greeting `@HLWD_` in order to let the robot know you are ready to communicate. Expect to get an
            # `ACK_` in return if successful communication has been established.
            handshake = "@HLWD_"
            ack = "#ACK_"

            # Send the greeting
            device.write((handshake + '\r').encode())
            tic = time.time()

            # Read the response
            incoming_data = device.read_until(b'\r').decode().strip()
            toc = time.time()

            logging.info(f"Received: {incoming_data} ({1000*(toc-tic):.3f}ms)")

            # If the response is an ACK then you have made contact. You can exit this example if it is a seperate file
            # or add onto it if you want this to be the main program you run.
            if incoming_data == ack:
                logging.info("ACK received.")
                sys.exit(0)

            # Sleep for a short duration before retrying to prevent spamming date along the connection.
            time.sleep(0.5)
        except KeyboardInterrupt:
            # Quit if you get a `CTRL^C`.
            logging.info("Exiting...")
            sys.exit(0)
        except Exception as e:
            # Log any errors but do not crash.
            logging.error(f"Error: {e}")


if __name__ == '__main__':
    main()