Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ Camera(
| `channel_read(name, size=None)` | Read from custom channel |
| `channel_write(name, data)` | Write to custom channel |
| `channel_size(name)` | Get available data size |
| `version()` | Get firmware, protocol, and bootloader versions |
| `system_info()` | Get camera system information |
| `host_stats()` / `device_stats()` | Get protocol statistics |

Expand Down
33 changes: 29 additions & 4 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,34 @@ camera.profiler_event(0, 0x0039) # CPU cycles

## System Information

### version()

Get firmware, protocol, and bootloader version information.

```python
ver = camera.version()
```

**Returns:** `dict` - Version information dictionary.

#### Return Value

| Key | Type | Description |
|-----|------|-------------|
| `protocol_version` | tuple | Protocol version (major, minor, patch) |
| `bootloader_version` | tuple | Bootloader version (major, minor, patch) |
| `firmware_version` | tuple | Firmware version (major, minor, patch) |

#### Example

```python
ver = camera.version()
fw = ver['firmware_version']
print(f"Firmware: {fw[0]}.{fw[1]}.{fw[2]}")
```

---

### system_info()

Get camera system information.
Expand All @@ -402,9 +430,9 @@ info = camera.system_info()
|-----|------|-------------|
| `cpu_id` | int | CPU identifier |
| `device_id` | tuple | Device ID (3 words) |
| `sensor_chip_id` | tuple | Sensor chip IDs (3 words) |
| `usb_vid` | int | USB Vendor ID |
| `usb_pid` | int | USB Product ID |
| `chip_id` | tuple | A list of detected sensors (3 words) |
| `gpu_present` | bool | GPU available |
| `npu_present` | bool | NPU available |
| `isp_present` | bool | ISP available |
Expand All @@ -424,9 +452,6 @@ info = camera.system_info()
| `ram_size_kb` | int | RAM size in KB |
| `framebuffer_size_kb` | int | Framebuffer size in KB |
| `stream_buffer_size_kb` | int | Stream buffer size in KB |
| `firmware_version` | bytes | Firmware version (3 bytes) |
| `protocol_version` | bytes | Protocol version (3 bytes) |
| `bootloader_version` | bytes | Bootloader version (3 bytes) |

---

Expand Down
79 changes: 48 additions & 31 deletions src/openmv/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def __init__(self, port, baudrate=921600,
self.channels_by_id = {}
self.pending_channel_events = 0
self.sysinfo = {}
self.verinfo = {}
self.transport = None
self.frame_event = False

Expand Down Expand Up @@ -254,7 +255,8 @@ def connect(self):
# Cache channel list
self.update_channels()

# Cache system info
# Cache system info and version info
self.verinfo = self.version()
self.sysinfo = self.system_info()

# Print system information
Expand Down Expand Up @@ -476,20 +478,20 @@ def read_frame(self):

self.frame_event = False
try:
# Get total size (16-byte header + stream data)
if (size := self._channel_size(stream_id)) <= 16:
# Get total size (20-byte header + stream data)
if (size := self._channel_size(stream_id)) <= 20:
return None

# Read all data (header + stream data)
data = self._channel_read(stream_id, 0, size)
if len(data) < 16:
if len(data) < 20:
return None

# Parse stream header: width(4), height(4), pixformat(4), depth/size(4)
width, height, pixformat, depth = struct.unpack('<IIII', data[:16])
# Parse stream header: width(4), height(4), pixformat(4), depth(4), offset(4)
width, height, pixformat, depth, offset = struct.unpack('<IIIII', data[:20])

# Extract raw frame data
raw_data = data[16:]
# Extract raw frame data (skip header and any padding)
raw_data = data[offset:]

# Convert to RGB888 using image module
rgb_data, fmt_str = omv_image.convert_to_rgb888(raw_data, width, height, pixformat)
Expand Down Expand Up @@ -534,33 +536,51 @@ def has_channel(self, channel):
"""Check if a channel exists"""
return self.get_channel(name=channel) is not None

@retry_if_failed
def version(self):
"""Get version information"""
payload = self._send_cmd_wait_resp(Opcode.PROTO_VERSION)

if len(payload) < 16:
raise OMVException(f"Invalid PROTO_VERSION payload size: {len(payload)}")

# Unpack the structure (16 bytes):
# protocol_version[3] + bootloader_version[3] + firmware_version[3] + reserved[7]
data = struct.unpack('<3s3s3s7x', payload)

return {
'protocol_version': tuple(data[0]),
'bootloader_version': tuple(data[1]),
'firmware_version': tuple(data[2]),
}

@retry_if_failed
def system_info(self):
"""Get system information"""
payload = self._send_cmd_wait_resp(Opcode.SYS_INFO)

if len(payload) < 80:
if len(payload) < 76:
raise OMVException(f"Invalid SYS_INFO payload size: {len(payload)}")

# Unpack the structure:
# cpu_id[1] + dev_id[3] + chip_id[3] + usb_id[1] + rsvd[1] +
# hw_caps[2] + memory[6] + versions[9] + padding[3]
data = struct.unpack('<I 3I 3I I I 2I 6I 3s3s3s 3x', payload)
# Unpack the structure (76 bytes):
# cpu_id[1] + dev_id[3] + usb_id[1] + chip_id[3] + id_reserved[2] +
# hw_caps[2] + memory[4] + memory_reserved[3]
data = struct.unpack('<I 3I I 3I 2I 2I 4I 3I', payload)

# Extract USB VID/PID
usb_id = data[7]
usb_id = data[4]
usb_vid = (usb_id >> 16) & 0xFFFF
usb_pid = usb_id & 0xFFFF

# Extract capability bitfield (always 2 words)
capabilities = data[9] # First hw_caps word
capabilities = data[10] # First hw_caps word

return {
'cpu_id': data[0],
'device_id': data[1:4], # 3 words
'sensor_chip_id': data[4:7], # 3 words
'usb_vid': usb_vid,
'usb_pid': usb_pid,
'chip_id': data[5:8], # 3 words
'gpu_present': bool(capabilities & (1 << 0)),
'npu_present': bool(capabilities & (1 << 1)),
'isp_present': bool(capabilities & (1 << 2)),
Expand All @@ -576,13 +596,10 @@ def system_info(self):
'eth_present': bool(capabilities & (1 << 19)),
'usb_highspeed': bool(capabilities & (1 << 20)),
'multicore_present': bool(capabilities & (1 << 21)),
'flash_size_kb': data[11],
'ram_size_kb': data[12],
'framebuffer_size_kb': data[13],
'stream_buffer_size_kb': data[14],
'firmware_version': data[17],
'protocol_version': data[18],
'bootloader_version': data[19]
'flash_size_kb': data[12],
'ram_size_kb': data[13],
'framebuffer_size_kb': data[14],
'stream_buffer_size_kb': data[15],
}

def print_system_info(self):
Expand All @@ -596,8 +613,8 @@ def print_system_info(self):
dev_id_hex = ''.join(f"{word:08X}" for word in self.sysinfo['device_id'])
logging.info(f"Device ID: {dev_id_hex}")

# Sensor Chip IDs are now an array of 3 words
for i, chip_id in enumerate(self.sysinfo['sensor_chip_id']):
# Chip IDs are an array of 3 words
for i, chip_id in enumerate(self.sysinfo['chip_id']):
if chip_id != 0: # Only show non-zero chip IDs
logging.info(f"CSI{i}: 0x{chip_id:08X}")

Expand Down Expand Up @@ -637,12 +654,12 @@ def print_system_info(self):
logging.info(f"Profiler: {'Available' if profile_available else 'Not available'}")

# Version info
fw = self.sysinfo['firmware_version']
proto = self.sysinfo['protocol_version']
boot = self.sysinfo['bootloader_version']
logging.info(f"Firmware version: {fw[0]}.{fw[1]}.{fw[2]}")
logging.info(f"Protocol version: {proto[0]}.{proto[1]}.{proto[2]}")
logging.info(f"Bootloader version: {boot[0]}.{boot[1]}.{boot[2]}")
protocol = self.verinfo['protocol_version']
bootloader = self.verinfo['bootloader_version']
firmware = self.verinfo['firmware_version']
logging.info(f"Protocol version: {protocol[0]}.{protocol[1]}.{protocol[2]}")
logging.info(f"Bootloader version: {bootloader[0]}.{bootloader[1]}.{bootloader[2]}")
logging.info(f"Firmware version: {firmware[0]}.{firmware[1]}.{firmware[2]}")
logging.info(f"Protocol capabilities: CRC={self.caps['crc']}, SEQ={self.caps['seq']}, "
f"ACK={self.caps['ack']}, EVENTS={self.caps['events']}, "
f"PAYLOAD={self.caps['max_payload']}")
Expand Down
1 change: 1 addition & 0 deletions src/openmv/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Opcode(IntEnum):
PROTO_GET_CAPS = 0x01
PROTO_SET_CAPS = 0x02
PROTO_STATS = 0x03
PROTO_VERSION = 0x04

# System commands
SYS_RESET = 0x10
Expand Down