From 7e002a49aeae2b89457368221e04a5cbbe5c5702 Mon Sep 17 00:00:00 2001 From: Mohamed Zarzoura Date: Sat, 11 Jul 2026 00:45:22 +0300 Subject: [PATCH] feat(examples): improve Ping360 auto-scan guidance - Update `examples/ping360AutoScan.py` to show bounded raw samples, handle timeouts, and stop scanning safely. - Expand `README.md` with Ping360 setup, message fields, and serial and UDP usage. Refs #99 --- README.md | 71 +++++++++++++++++++++++++++++++++++++ examples/ping360AutoScan.py | 68 +++++++++++++++++++++-------------- 2 files changed, 112 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index a0148b1d..924a0c3d 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,8 @@ It's also possible to connect via UDP server using the `--udp` option with IP:PO ## Usage +### Ping1D + The [Ping1D](https://docs.bluerobotics.com/ping-python/classPing_1_1Ping1D_1_1Ping1D.html) class provides an easy interface to configure a Ping device and retrieve data. A Ping1D object must be initialized with the serial device path and the baudrate. @@ -107,3 +109,72 @@ Use the [`set_*`](https://github.com/bluerobotics/ping-protocol#set) messages (e ``` See the [doxygen](https://docs.bluerobotics.com/ping-python/) documentation for complete API documentation. + +### Ping360 Auto Scan + +The `Ping360` class provides an easy interface to configure a Ping360 device +and retrieve sonar profiles. + +A Ping360 object must be connected over serial or UDP. + +```py +from brping import definitions +from brping import Ping360 + +myPing360 = Ping360() +myPing360.connect_serial("/dev/ttyUSB0", 2000000) +# For UDP +# myPing360.connect_udp("192.168.2.2", 9092) +``` + +Call `initialize()` to establish communications with the device. + +```py +if myPing360.initialize() is False: + print("Failed to initialize Ping!") + exit(1) +``` + +Use `control_auto_transmit()` to start a continuous full-sector auto-scan. The +following API snippets are partial; use the [Ping360 auto-scan +example](examples/ping360AutoScan.py) for a runnable scan. + +```py +myPing360.control_auto_transmit( + mode=1, + gain_setting=0, + transmit_duration=80, + sample_period=80, + transmit_frequency=750, + number_of_samples=1024, + start_angle=0, + stop_angle=399, + num_steps=1, + delay=0 +) +``` + +The device sends a `PING360_AUTO_DEVICE_DATA` message for each angle. Each +message contains the scan angle in `angle` and raw echo-strength samples in +`data`. Use `wait_message()` to receive these messages. See the [`2301 +auto_device_data` message +definition](https://docs.bluerobotics.com/ping-protocol/pingmessage-ping360/#2301-auto_device_data) +for the complete list of fields and units. + +```py +message = myPing360.wait_message([definitions.PING360_AUTO_DEVICE_DATA]) +if message: + angle = message.angle + data = message.data + print("Angle: %s gradians\tSample count: %s\tPreview: %s" % + (angle, len(data), list(data[:8]))) +else: + print("Failed to get profile data") +``` + +To stop the device when using serial connections use a break and autobaud +sequence before `control_motor_off()`; in case of UDP connections call +`control_motor_off()` directly. + +Auto-transmit and `PING360_AUTO_DEVICE_DATA` require Ping Protocol `v1.1.0` and +supporting Ping360 firmware. diff --git a/examples/ping360AutoScan.py b/examples/ping360AutoScan.py index 6bd43f3c..d019884b 100644 --- a/examples/ping360AutoScan.py +++ b/examples/ping360AutoScan.py @@ -10,8 +10,8 @@ parser = argparse.ArgumentParser(description="Ping360 auto scan example") parser.add_argument('--device', action="store", required=False, type=str, help="Ping device port. E.g: /dev/ttyUSB0") -parser.add_argument('--baudrate', action="store", type=int, default=2000000, help="Ping device baudrate. E.g: 115200") -parser.add_argument('--udp', action="store", required=False, type=str, help="Ping UDP server. E.g: 192.168.2.2:9090") +parser.add_argument('--baudrate', action="store", type=int, default=2000000, help="Ping device baudrate. E.g: 2000000") +parser.add_argument('--udp', action="store", required=False, type=str, help="Ping UDP server. E.g: 192.168.2.2:9092") args = parser.parse_args() if args.device is None and args.udp is None: parser.print_help() @@ -36,30 +36,44 @@ input("Press Enter to continue...") -myPing360.control_auto_transmit( - mode = 1, - gain_setting = 0, - transmit_duration = 80, - sample_period = 80, - transmit_frequency = 750, - number_of_samples = 1024, - start_angle = 0, - stop_angle = 399, - num_steps = 1, - delay = 0 -) +try: + myPing360.control_auto_transmit( + mode = 1, + gain_setting = 0, + transmit_duration = 80, + sample_period = 80, + transmit_frequency = 750, + number_of_samples = 1024, + start_angle = 0, + stop_angle = 399, + num_steps = 1, + delay = 0 + ) -# Print the scanning head angle -for n in range(400): - m = myPing360.wait_message([definitions.PING360_AUTO_DEVICE_DATA]) - if m: - print(m.angle) - time.sleep(0.001) + # Print a compact summary of each received profile. + for n in range(400): + message = myPing360.wait_message([definitions.PING360_AUTO_DEVICE_DATA]) + if message is None: + print("Timed out waiting for a Ping360 profile.") + break -# if it is a serial device, reconnect to send a line break -# and stop auto-transmitting -if args.device is not None: - myPing360.connect_serial(args.device, args.baudrate) - -# turn the motor off -myPing360.control_motor_off() + angle = message.angle + # data contains raw return-strength samples ordered nearest-to-farthest. + data = message.data + print( + f"Angle: {angle} gradians, samples: {len(data)}, " + f"preview: {list(data[:8])}" + ) + time.sleep(0.001) +except KeyboardInterrupt: + print("\nScan interrupted.") +finally: + print("Stopping auto scan and disabling the motor...") + try: + if args.device is not None: + # A serial break stops auto-transmit and starts device autobaud. + myPing360.iodev.send_break() + time.sleep(0.001) + myPing360.iodev.write(b"U") + finally: + myPing360.control_motor_off()