Skip to content

Commit 93b7252

Browse files
authored
Merge pull request #588 from bitcraze/rik/aboutdeckctrl
Add DeckCtrlElement class and integrate with Memory management
2 parents 8900f0d + 3a00e17 commit 93b7252

File tree

3 files changed

+161
-0
lines changed

3 files changed

+161
-0
lines changed

cflib/crazyflie/mem/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from threading import Lock
3333

3434
from .deck_memory import DeckMemoryManager
35+
from .deckctrl_element import DeckCtrlElement
3536
from .i2c_element import I2CElement
3637
from .led_driver_memory import LEDDriverMemory
3738
from .led_timings_driver_memory import LEDTimingsDriverMemory
@@ -533,6 +534,11 @@ def _handle_cmd_info_details(self, payload):
533534
logger.debug(mem)
534535
self.mem_read_cb.add_callback(mem.new_data)
535536
self.mem_read_failed_cb.add_callback(mem.read_failed)
537+
elif mem_type == MemoryElement.TYPE_DECKCTRL:
538+
mem = DeckCtrlElement(id=mem_id, type=mem_type, size=mem_size, mem_handler=self)
539+
logger.debug(mem)
540+
self.mem_read_cb.add_callback(mem.new_data)
541+
self.mem_read_failed_cb.add_callback(mem.read_failed)
536542
else:
537543
mem = MemoryElement(id=mem_id, type=mem_type, size=mem_size, mem_handler=self)
538544
logger.debug(mem)
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# ,---------, ____ _ __
4+
# | ,-^-, | / __ )(_) /_______________ _____ ___
5+
# | ( O ) | / __ / / __/ ___/ ___/ __ `/_ / / _ \
6+
# | / ,--' | / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
7+
# +------` /_____/_/\__/\___/_/ \__,_/ /___/\___/
8+
#
9+
# Copyright (C) 2026 Bitcraze AB
10+
#
11+
# This program is free software: you can redistribute it and/or modify
12+
# it under the terms of the GNU General Public License as published by
13+
# the Free Software Foundation, in version 3.
14+
#
15+
# This program is distributed in the hope that it will be useful,
16+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
# GNU General Public License for more details.
19+
#
20+
# You should have received a copy of the GNU General Public License
21+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
22+
import logging
23+
import struct
24+
25+
from .memory_element import MemoryElement
26+
27+
logger = logging.getLogger(__name__)
28+
29+
# DeckCtrl memory layout at offset 0x0000 (32 bytes):
30+
# Offset | Size | Field
31+
# -------|------|----------------
32+
# 0x00 | 2 | Magic (0xBCDC big-endian)
33+
# 0x02 | 1 | Major Version
34+
# 0x03 | 1 | Minor Version
35+
# 0x04 | 1 | Vendor ID
36+
# 0x05 | 1 | Product ID
37+
# 0x06 | 1 | Board Revision
38+
# 0x07 | 15 | Product Name (null-terminated)
39+
# 0x16 | 1 | Year
40+
# 0x17 | 1 | Month
41+
# 0x18 | 1 | Day
42+
# 0x19 | 6 | Reserved
43+
# 0x1F | 1 | Checksum (makes sum of bytes 0-31 = 0)
44+
45+
DECKCTRL_MAGIC = 0xBCDC
46+
DECKCTRL_INFO_SIZE = 32
47+
48+
49+
class DeckCtrlElement(MemoryElement):
50+
"""Memory class with functionality for DeckCtrl memories"""
51+
52+
def __init__(self, id, type, size, mem_handler):
53+
"""Initialize the memory with good defaults"""
54+
super(DeckCtrlElement, self).__init__(id=id, type=type, size=size,
55+
mem_handler=mem_handler)
56+
57+
self.valid = False
58+
59+
self.vid = None
60+
self.pid = None
61+
self.name = None
62+
self.revision = None
63+
self.fw_version_major = None
64+
self.fw_version_minor = None
65+
self.elements = {}
66+
67+
self._update_finished_cb = None
68+
69+
def new_data(self, mem, addr, data):
70+
"""Callback for when new memory data has been fetched"""
71+
if mem.id == self.id:
72+
if addr == 0:
73+
if self._parse_and_check_info(data[:DECKCTRL_INFO_SIZE]):
74+
self.valid = True
75+
if self._update_finished_cb:
76+
self._update_finished_cb(self)
77+
self._update_finished_cb = None
78+
79+
def read_failed(self, mem, addr):
80+
"""Callback for when a memory read fails"""
81+
if mem.id == self.id:
82+
logger.warning('DeckCtrl memory read failed for id {}'.format(self.id))
83+
if self._update_finished_cb:
84+
self._update_finished_cb(self)
85+
self._update_finished_cb = None
86+
87+
def _parse_and_check_info(self, data):
88+
"""Parse and validate the DeckCtrl info block"""
89+
if len(data) < DECKCTRL_INFO_SIZE:
90+
logger.warning('DeckCtrl data too short: {} bytes'.format(len(data)))
91+
return False
92+
93+
# Validate checksum (sum of all 32 bytes should be 0 mod 256)
94+
checksum = sum(data[:DECKCTRL_INFO_SIZE]) & 0xFF
95+
if checksum != 0:
96+
logger.warning('DeckCtrl checksum failed: {}'.format(checksum))
97+
return False
98+
99+
# Parse the header
100+
magic = struct.unpack('>H', data[0:2])[0] # Big-endian
101+
if magic != DECKCTRL_MAGIC:
102+
logger.warning('DeckCtrl magic mismatch: 0x{:04X}'.format(magic))
103+
return False
104+
105+
self.fw_version_major = data[2]
106+
self.fw_version_minor = data[3]
107+
self.vid = data[4]
108+
self.pid = data[5]
109+
self.revision = chr(data[6]) if data[6] != 0 else ''
110+
111+
# Product name is 15 bytes, null-terminated
112+
name_bytes = data[7:22]
113+
null_pos = name_bytes.find(0)
114+
if null_pos >= 0:
115+
name_bytes = name_bytes[:null_pos]
116+
self.name = name_bytes.decode('ISO-8859-1')
117+
118+
# Manufacturing date
119+
year = data[22]
120+
month = data[23]
121+
day = data[24]
122+
123+
# Populate elements dict for compatibility with OWElement interface
124+
self.elements['Board name'] = self.name
125+
self.elements['Board revision'] = self.revision
126+
if year != 0 or month != 0 or day != 0:
127+
self.elements['Manufacturing date'] = '{:04d}-{:02d}-{:02d}'.format(
128+
2000 + year, month, day)
129+
self.elements['Firmware version'] = '{}.{}'.format(
130+
self.fw_version_major, self.fw_version_minor)
131+
132+
return True
133+
134+
def update(self, update_finished_cb):
135+
"""Request an update of the memory content"""
136+
if not self._update_finished_cb:
137+
self._update_finished_cb = update_finished_cb
138+
self.valid = False
139+
logger.debug('Updating content of DeckCtrl memory {}'.format(self.id))
140+
# Read the 32-byte info block
141+
self.mem_handler.read(self, 0, DECKCTRL_INFO_SIZE)
142+
143+
def __str__(self):
144+
"""Generate debug string for memory"""
145+
return ('DeckCtrl ({:02X}:{:02X}): {}'.format(
146+
self.vid or 0, self.pid or 0, self.elements))
147+
148+
def disconnect(self):
149+
self._update_finished_cb = None

cflib/crazyflie/mem/memory_element.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class MemoryElement(object):
4040
TYPE_DECK_MEMORY = 0x19
4141
TYPE_DECK_MULTIRANGER = 0x1A
4242
TYPE_DECK_PAA3905 = 0x1B
43+
TYPE_DECKCTRL_DFU = 0x20
44+
TYPE_DECKCTRL = 0x21
4345

4446
def __init__(self, id, type, size, mem_handler):
4547
"""Initialize the element with default values"""
@@ -69,6 +71,10 @@ def type_to_string(t):
6971
return 'Lighthouse positioning'
7072
if t == MemoryElement.TYPE_MEMORY_TESTER:
7173
return 'Memory tester'
74+
if t == MemoryElement.TYPE_DECKCTRL:
75+
return 'DeckCtrl'
76+
if t == MemoryElement.TYPE_DECKCTRL_DFU:
77+
return 'DeckCtrl DFU'
7278
return 'Unknown'
7379

7480
def new_data(self, mem, addr, data):

0 commit comments

Comments
 (0)