A reference implementation in python that uses the Sonair ADAR CoAP API.
Project description
ADAR API Python Package
A Python package for communicating with the ADAR 3D Ultrasonic Sensor via Constrained Application Protocol (CoAP)).
Overview
This package serves two main purposes:
-
Easy-to-use Python API: Provides a simple interface for receiving 3D point cloud data from ADAR sensors, for conversion to other ecosystems like ROS or Foxglove.
-
Reference Implementation: Documents the CoAP resources and binary data formats to enable porting to other languages (C, C++, etc.)
Table of Contents
- Installation
- Network Requirements
- Quick Start
- API Reference - For Python developers
- Coordinate System
- Error Handling
- Advanced Usage
- CoAP Resources and Data Formats - For protocol reference
Installation
Quick Installation
Install the ADAR API package from PyPI:
pip install adar-api
Recommended: Using a Virtual Environment
For better dependency management, it's recommended to use a virtual environment:
-
Create a virtual environment:
python -m venv .venv
-
Activate the virtual environment:
Linux/macOS:
source .venv/bin/activate
Windows:
.venv\Scripts\activate
-
Install the package:
pip install adar-api
Note: You need to activate the virtual environment each time you want to use the pointcloud-publisher command or the ADAR API in a new terminal session.
Network Requirements
- Protocol: CoAP over UDP (port 5683)
- Network: IPv4 connectivity to sensor
- Firewall: Ensure UDP port 5683 is accessible
Quick Start
Point cloud Publisher
The easiest way to visualize the ADAR point cloud is to use the built-in pointcloud-publisher:
pointcloud-publisher <ADAR_IP_ADDRESS>
Example for an ADAR with factory-default IP address:
pointcloud-publisher 10.20.30.40
Advanced Usage
Specify a custom Foxglove server host: This can be useful if you want to publish the pointcloud to a different computer than the one which is running the the pointcloud-publisher script.
pointcloud-publisher <ADAR_IP_ADDRESS> --foxglove-host <HOST_IP>
Example for broadcasting to a specific foxglove host:
This will publish the pointcloud from an ADAR with IP address 10.20.30.40 to a foxglove host running on IP address 127.0.0.2
pointcloud-publisher 10.20.30.40 --foxglove-host 127.0.0.2
Command Line Options:
ipaddr(required): IP address of the ADAR device--foxglove-host(optional): Host IP address for the Foxglove server (default: 127.0.0.1)
Visualization with Foxglove Studio
- Start the pointcloud publisher (as shown above)
- Open Foxglove Studio
- Connect to the Foxglove server:
- Go to "Open connection"
- Select "Foxglove WebSocket"
- Enter
ws://127.0.0.1:8765(or your custom host)
- Import layout for ADAR:
- In the top right pane, select the layout drop-down and click "Import from file..."
- Select the
foxglove_layout_ADAR.jsonfile. - The point cloud should now appear in a 3D view and a 2D top-down view.
ADAR API
For custom integrations, use the Python API directly:
import asyncio
from adar_api import Adar
from aiocoap import Context
async def main():
# Create CoAP context
ctx = await Context.create_client_context()
# Connect to ADAR sensor
adar = Adar(ctx, ip_address="10.20.30.40")
# Get device information
device_info = await adar.get_device_info()
print(f"Device: {device_info.device_name}")
print(f"Firmware: {device_info.firmware_version}")
# Get single point cloud frame
point_cloud = await adar.get_point_cloud()
print(f"Received {len(point_cloud.points)} points")
# Continuous observation
async for point_cloud in adar.observe_point_cloud():
print(f"Received {len(point_cloud.points)} points")
for point in point_cloud.points:
print(f"Point: x={point.x:.3f}m, y={point.y:.3f}m, z={point.z:.3f}m, "
f"strength={point.strength}, classification={point.classification}")
if __name__ == "__main__":
asyncio.run(main())
API Reference
Error Handling
The API provides structured exception handling:
from adar_api import CoapException, CoapErrorException
try:
point_cloud = await adar.get_point_cloud()
except CoapErrorException as e:
print(f"CoAP protocol error: {e.response.code}")
except CoapException as e:
print(f"General CoAP error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
Response Code Handling
CoAP errors include response codes that indicate the type of failure or success.
For a complete list of response codes, see CoAP Response Codes in the protocol reference.
Usage Examples
Robust Point Cloud Observation
async def robust_observation():
async for point_cloud in adar.observe_point_cloud(keep_running=True):
try:
# Process point cloud
process_points(point_cloud.points)
except Exception as e:
logger.warning(f"Processing error: {e}")
# Continue observation despite processing errors
Direct CoAP Request
from aiocoap import Message, GET
# Access the underlying CoAP request method
response = await adar.send_request(
Message(code=GET, uri=f"coap://{adar.ip_address}/status/v0")
)
Network Configuration
# Get current configuration
config = await adar.get_network_config()
print(f"Current DHCP setting: {config.dhcp_enabled}")
# Modify network config
config.dhcp_enabled = True
# Apply modified configuration (device will reboot and might be on a different network than before)
await adar.set_network_config(config)
Transmission Code Configuration
# Get current transmission code
code_id = await adar.get_transmission_code_id()
print(f"Current transmission code: {code_id}")
# Set transmission code (valid values: 1, 2, 4, 8)
await adar.set_transmission_code_id(4)
CoAP Resources and Data Formats
For developers integrating towards ADAR using other languages, this section documents the CoAP API resources and data formats.
Resource URI Structure
coap://<device_ip>/<resource>/<version>
Resource Reference
| Resource | Method | Description | Response Format |
|---|---|---|---|
/pointcloud/v0 |
GET/OBSERVE | 3D point cloud data | Point Cloud Format |
/status/v0 |
GET | Device status | Device Status Format |
/device_info/v0 |
GET | Device identification | Device Info Format |
/network_config/v0 |
GET/PUT | Network configuration | Network Config Format |
/statistics/v0 |
GET | Operational statistics | Statistics Format |
/errors/v0 |
GET | Device error codes | Error Format |
/transmission_code/v0 |
GET/PUT | Transmission code ID | Single byte. Legal values: 1, 2, 4, 8 |
/factory_reset/v0 |
PUT | Factory reset | Empty payload |
/observers/v0 |
DELETE | Clear all observers | Empty payload |
Data Format Specifications (little-endian)
Point Cloud Format
Binary payload structure:
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-7 | Timestamp | uint64 | Microseconds since measurement start |
| 8-15 | Device Status | 8 bytes | See Device Status Format |
| 16+ | Point Data | 10 bytes per point | See Point Format |
Note: The number of points is determined by parsing 10-byte chunks until the payload ends. The total payload length minus the 16-byte header must be divisible by 10, or the data is considered corrupted.
Point Format
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-1 | X coordinate | int16 | Millimeters, signed |
| 2-3 | Y coordinate | int16 | Millimeters, signed |
| 4-5 | Z coordinate | int16 | Millimeters, signed |
| 6-7 | Strength | uint16 | Signal strength, unsigned |
| 8 | Reserved | uint8 | Ignore |
| 9 | Classification | uint8 | See Classification Flags |
Note: The Python API converts coordinates from millimeters to meters.
Classification Flags
| Bit 7-4 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|---|
| Reserved | Point in Exclusion Zone | Point in Outer Warning Zone | Point in Inner Warning Zone | Point in Protective Zone |
Device Status Format
8-byte binary structure:
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0 | Zone Selected | uint8 | Currently selected zone |
| 1 | Device State | uint8 | See Device States |
| 2 | Transmission Code | uint8 | Transmission code index (0-3, where code ID = 2^index) |
| 3 | Zone Status | uint8 | See Zone Status Flags |
| 4-7 | Device Error | uint32 | Error code, little-endian |
Note: This format uses a transmission code index (0-3), while the Transmission Code resource uses a code ID (1, 2, 4, 8).
Device States
- 1: Init
- 2: SelfTest
- 3: Enabled
- 4: Disabled
- 5: Config
- 6: Error
- 7: Fault
Zone Status Flags
| Bit 7-3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|
| Reserved | Object in Outer Warning Zone | Object in Inner Warning Zone | Object in Protective Zone |
Device Info Format
Variable-length format with length-prefixed strings:
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-3 | Serial Number | uint32 | Device serial number, little-endian |
| 4-6 | Hardware Version | 3 Bytes (uint8) | Hardware version bytes (major.minor.patch) |
| 7-10 | Product Number Length | uint32 | Length of product number string, little-endian |
| 11+ | Product Number | UTF-8 string | Product number string |
| Next 4 bytes | Device Name Length | uint32 | Length of device name string, little-endian |
| Next N bytes | Device Name | UTF-8 string | Device name string |
| Next 4 bytes | Firmware Version Length | uint32 | Length of firmware version string, little-endian |
| Next N bytes | Firmware Version | UTF-8 string | Firmware version string |
Length-prefixed String Format
- 4 bytes: String length (uint32, little-endian)
- N bytes: UTF-8 string data
Network Config Format
212-byte binary structure:
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-3 | Configuration Flags | uint32 | See Configuration Flags |
| 4-7 | Static IP Address | 4 Bytes (uint8) | Static IP address bytes |
| 8-11 | Subnet Mask | 4 Bytes (uint8) | Subnet mask bytes |
| 12-15 | Gateway Address | 4 Bytes (uint8) | Gateway IP address bytes |
| 16-19 | Sync Server IP | 4 Bytes (uint8) | Synchronization server IP address |
| 20-83 | Reserved | 64 Bytes (uint8) | Reserved bytes |
| 84-211 | Device Tag | 128 Bytes (uint8) | Reserved for Device Tag. Should be set to existing value or all-zero |
Configuration Flags
| Bit 31-3 | Bit 2 | Bit 1 | Bit 0 |
|---|---|---|---|
| Reserved | Sync server mode | Sync enabled | Static IP enabled (0=DHCP, 1=Static) |
Statistics Format
44-byte binary structure:
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-11 | Uptime | Duration format | Device uptime |
| 12-19 | Total Number of Pings | uint64 | Total pings count, little-endian |
| 20-27 | Pings with Object in Protective Zone | uint64 | Protective zone detections, little-endian |
| 28-35 | Pings with Object in Inner Warning Zone | uint64 | Inner warning zone detections, little-endian |
| 36-43 | Pings with Object in Outer Warning Zone | uint64 | Outer warning zone detections, little-endian |
Duration Format
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-7 | Seconds | uint64 | Seconds component, little-endian |
| 8-11 | Nanoseconds | uint32 | Nanoseconds component, little-endian |
Error Format
Variable-length format with error bitmask and description strings:
| Byte Range | Field | Type | Description |
|---|---|---|---|
| 0-3 | Error Bitmask | uint32 | Error bitmask, little-endian |
| 4-7 | Number of Error Strings | uint32 | Count of error strings, little-endian |
| 8+ | Error Strings | Variable | See Error String Format |
Error String Format
- 4 bytes: String length (uint32, little-endian)
- N bytes: UTF-8 error description
Transmission Code Format
Single byte payload:
| Byte | Field | Type | Description |
|---|---|---|---|
| 0 | Code ID | uint8 | Transmission code ID (1, 2, 4, or 8) |
Note: This resource uses the code ID (which is a binary bitmask), while the Device Status uses a transmission code index (0-3) where the code ID is 2^index.
CoAP Response Codes
When working with ADAR resources, you may encounter these CoAP response codes:
Success Responses
2.02 Deleted- Resource successfully deleted2.04 Changed- Resource successfully modified (PUT operations)2.05 Content- Resource successfully retrieved (GET operations)
Client Error Responses
4.00 Bad Request- Malformed request or invalid payload data4.04 Not Found- Resource does not exist (e.g., typos in resource paths like/pointclouds/v0instead of/pointcloud/v0) or unauthorized access4.05 Method Not Allowed- Invalid HTTP method for the resource (e.g., PUT to a read-only resource)4.08 Request Entity Incomplete- Missing or incomplete payload for operations that require data4.29 Too Many Requests- Observer limit exceeded (max 2 point cloud observers)
Server Error Responses
5.01 Not Implemented- Resource is deprecated or not supported in the current firmware version
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file adar_api-1.1.1-py3-none-any.whl.
File metadata
- Download URL: adar_api-1.1.1-py3-none-any.whl
- Upload date:
- Size: 25.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55e951bf4648772f0f14367fc8fa1277b894650283f07707d8598b9bf03d42e3
|
|
| MD5 |
5cb32446c17d0e6cb6726e24b6c831d4
|
|
| BLAKE2b-256 |
fa7abd9e49f8c677e0a3e85b053e4537d6a08d7ac93e170a588872064debba7c
|
Provenance
The following attestation bundles were made for adar_api-1.1.1-py3-none-any.whl:
Publisher:
adar_api.yml on Sonair-AS/Sonair
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adar_api-1.1.1-py3-none-any.whl -
Subject digest:
55e951bf4648772f0f14367fc8fa1277b894650283f07707d8598b9bf03d42e3 - Sigstore transparency entry: 406321228
- Sigstore integration time:
-
Permalink:
Sonair-AS/Sonair@7e0998a1bae596eceaa0325fcf7b78f9b11a37ad -
Branch / Tag:
refs/tags/releases/adar_api/v1.1.1 - Owner: https://github.com/Sonair-AS
-
Access:
internal
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
adar_api.yml@7e0998a1bae596eceaa0325fcf7b78f9b11a37ad -
Trigger Event:
push
-
Statement type: