cfclient 2024.11__py3-none-any.whl → 2025.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cfclient might be problematic. Click here for more details.
- cfclient/__init__.py +9 -7
- cfclient/configs/log/PID_tuning/Attitude.json +46 -0
- cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
- cfclient/configs/log/PID_tuning/Position.json +46 -0
- cfclient/configs/log/PID_tuning/Velocity.json +46 -0
- cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
- cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
- cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
- cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
- cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
- cfclient/resources/log_param_doc.json +1 -1
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +0 -12
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +0 -7
- cfclient/ui/main.py +3 -3
- cfclient/ui/tabs/FlightTab.py +1 -1
- cfclient/ui/tabs/LogTab.py +2 -2
- cfclient/ui/tabs/ParamTab.py +1 -1
- cfclient/utils/input/__init__.py +3 -3
- cfclient/utils/periodictimer.py +1 -1
- cfclient/version.py +34 -0
- cfclient-2025.9.dist-info/METADATA +71 -0
- {cfclient-2024.11.dist-info → cfclient-2025.9.dist-info}/RECORD +35 -16
- {cfclient-2024.11.dist-info → cfclient-2025.9.dist-info}/WHEEL +1 -1
- {cfclient-2024.11.dist-info → cfclient-2025.9.dist-info}/top_level.txt +1 -0
- cfconfig/Makefile +51 -0
- cfconfig/configblock.py +111 -0
- cfclient-2024.11.dist-info/METADATA +0 -53
- {cfclient-2024.11.dist-info → cfclient-2025.9.dist-info}/entry_points.txt +0 -0
- {cfclient-2024.11.dist-info → cfclient-2025.9.dist-info/licenses}/LICENSE.txt +0 -0
|
@@ -31,7 +31,6 @@ import cfclient
|
|
|
31
31
|
from PyQt6 import QtWidgets
|
|
32
32
|
from PyQt6 import uic
|
|
33
33
|
from PyQt6.QtCore import QVariant, Qt, QAbstractTableModel, pyqtSignal
|
|
34
|
-
from cflib.localization import LighthouseBsGeoEstimator
|
|
35
34
|
from cflib.localization import LighthouseSweepAngleAverageReader
|
|
36
35
|
from cflib.crazyflie.mem import LighthouseBsGeometry
|
|
37
36
|
from cfclient.ui.wizards.lighthouse_geo_bs_estimation_wizard import LighthouseBasestationGeometryWizard
|
|
@@ -138,13 +137,6 @@ class LighthouseBsGeometryDialog(QtWidgets.QWidget, basestation_geometry_widget_
|
|
|
138
137
|
self._lighthouse_tab = lighthouse_tab
|
|
139
138
|
|
|
140
139
|
self._estimate_geometry_button.clicked.connect(self._estimate_geometry_button_clicked)
|
|
141
|
-
self._simple_estimator = LighthouseBsGeoEstimator()
|
|
142
|
-
self._estimate_geometry_simple_button.clicked.connect(self._estimate_geometry_simple_button_clicked)
|
|
143
|
-
try:
|
|
144
|
-
if not self._simple_estimator.is_available():
|
|
145
|
-
self._estimate_geometry_simple_button.setEnabled(False)
|
|
146
|
-
except Exception as e:
|
|
147
|
-
print(e)
|
|
148
140
|
|
|
149
141
|
self._write_to_cf_button.clicked.connect(self._write_to_cf_button_clicked)
|
|
150
142
|
|
|
@@ -205,10 +197,6 @@ class LighthouseBsGeometryDialog(QtWidgets.QWidget, basestation_geometry_widget_
|
|
|
205
197
|
self._base_station_geometry_wizard.show()
|
|
206
198
|
self.hide()
|
|
207
199
|
|
|
208
|
-
def _estimate_geometry_simple_button_clicked(self):
|
|
209
|
-
self._sweep_angle_reader.start_angle_collection()
|
|
210
|
-
self._update_ui()
|
|
211
|
-
|
|
212
200
|
def _write_to_cf_button_clicked(self):
|
|
213
201
|
if len(self._newly_estimated_geometry) > 0:
|
|
214
202
|
self._lighthouse_tab.write_and_store_geometry(self._newly_estimated_geometry)
|
|
@@ -41,13 +41,6 @@
|
|
|
41
41
|
</property>
|
|
42
42
|
</spacer>
|
|
43
43
|
</item>
|
|
44
|
-
<item>
|
|
45
|
-
<widget class="QPushButton" name="_estimate_geometry_simple_button">
|
|
46
|
-
<property name="text">
|
|
47
|
-
<string>Estimate Geometry Simple</string>
|
|
48
|
-
</property>
|
|
49
|
-
</widget>
|
|
50
|
-
</item>
|
|
51
44
|
</layout>
|
|
52
45
|
</item>
|
|
53
46
|
<item>
|
cfclient/ui/main.py
CHANGED
|
@@ -227,7 +227,7 @@ class MainUI(QtWidgets.QMainWindow, main_window_class):
|
|
|
227
227
|
self.linkQualityBar.setTextVisible(False)
|
|
228
228
|
|
|
229
229
|
# Connect link quality feedback
|
|
230
|
-
self.cf.link_quality_updated.add_callback(self.linkQualitySignal.emit)
|
|
230
|
+
self.cf.link_statistics.link_quality_updated.add_callback(self.linkQualitySignal.emit)
|
|
231
231
|
self.linkQualitySignal.connect(
|
|
232
232
|
lambda percentage: self.linkQualityBar.setValue(int(percentage)))
|
|
233
233
|
|
|
@@ -683,8 +683,8 @@ class MainUI(QtWidgets.QMainWindow, main_window_class):
|
|
|
683
683
|
if e.errno == 13: # Permission denied
|
|
684
684
|
link = "<a href='https://www.bitcraze.io/documentation/repository/crazyflie-lib-python/master/installation/usb_permissions/'>Install USB Permissions</a>" # noqa
|
|
685
685
|
msg = QMessageBox()
|
|
686
|
-
msg.setIcon(QMessageBox.Information)
|
|
687
|
-
msg.setTextFormat(Qt.RichText)
|
|
686
|
+
msg.setIcon(QMessageBox.Icon.Information)
|
|
687
|
+
msg.setTextFormat(Qt.TextFormat.RichText)
|
|
688
688
|
msg.setText("Could not access Crazyradio")
|
|
689
689
|
msg.setInformativeText(link)
|
|
690
690
|
msg.setWindowTitle("Crazyradio permissions")
|
cfclient/ui/tabs/FlightTab.py
CHANGED
|
@@ -446,7 +446,7 @@ class FlightTab(TabToolbox, flight_tab_class):
|
|
|
446
446
|
# To prevent conflicting commands from the controller and the flight panel
|
|
447
447
|
if JoystickReader().available_devices():
|
|
448
448
|
self.commanderBox.setToolTip(
|
|
449
|
-
'
|
|
449
|
+
'Cannot use both a controller and Command Based Flight'
|
|
450
450
|
)
|
|
451
451
|
self.commanderBox.setEnabled(False)
|
|
452
452
|
return
|
cfclient/ui/tabs/LogTab.py
CHANGED
|
@@ -99,7 +99,7 @@ class LogTab(TabToolbox, param_tab_class):
|
|
|
99
99
|
log_groups = cfclient.log_param_doc['logs'][group]
|
|
100
100
|
log_variable = log_groups['variables'][param]
|
|
101
101
|
item.setData(4, Qt.ItemDataRole.DisplayRole, log_variable['short_desc'])
|
|
102
|
-
except:
|
|
102
|
+
except (KeyError, TypeError, AttributeError):
|
|
103
103
|
pass
|
|
104
104
|
|
|
105
105
|
groupItem.addChild(item)
|
|
@@ -112,5 +112,5 @@ class LogTab(TabToolbox, param_tab_class):
|
|
|
112
112
|
label = QtWidgets.QLabel(log_groups['desc'])
|
|
113
113
|
label.setWordWrap(True)
|
|
114
114
|
self.logTree.setItemWidget(groupItem, 4, label)
|
|
115
|
-
except:
|
|
115
|
+
except (KeyError, TypeError, AttributeError):
|
|
116
116
|
pass
|
cfclient/ui/tabs/ParamTab.py
CHANGED
|
@@ -398,7 +398,7 @@ class ParamTab(TabToolbox, param_tab_class):
|
|
|
398
398
|
|
|
399
399
|
self.paramDetailsDescription.setWordWrap(True)
|
|
400
400
|
self.paramDetailsDescription.setText(desc.replace('\n', ''))
|
|
401
|
-
except:
|
|
401
|
+
except (KeyError, TypeError, AttributeError):
|
|
402
402
|
self.paramDetailsDescription.setText('')
|
|
403
403
|
|
|
404
404
|
complete = f'{group}.{param}'
|
cfclient/utils/input/__init__.py
CHANGED
|
@@ -453,7 +453,7 @@ class JoystickReader(object):
|
|
|
453
453
|
vx = data.roll
|
|
454
454
|
vy = data.pitch
|
|
455
455
|
vz = data.thrust
|
|
456
|
-
yawrate = data.yaw
|
|
456
|
+
yawrate = -data.yaw
|
|
457
457
|
# The odd use of vx and vy is to map forward on the
|
|
458
458
|
# physical joystick to positive X-axis
|
|
459
459
|
self.assisted_input_updated.call(vy, -vx, vz, yawrate)
|
|
@@ -473,7 +473,7 @@ class JoystickReader(object):
|
|
|
473
473
|
if self._target_height < MIN_HOVER_HEIGHT:
|
|
474
474
|
self._target_height = MIN_HOVER_HEIGHT
|
|
475
475
|
|
|
476
|
-
yawrate = data.yaw
|
|
476
|
+
yawrate = -data.yaw
|
|
477
477
|
# The odd use of vx and vy is to map forward on the
|
|
478
478
|
# physical joystick to positive X-axis
|
|
479
479
|
self.hover_input_updated.call(vy, -vx, yawrate,
|
|
@@ -499,7 +499,7 @@ class JoystickReader(object):
|
|
|
499
499
|
and data.assistedControl:
|
|
500
500
|
roll = data.roll + self.trim_roll
|
|
501
501
|
pitch = data.pitch + self.trim_pitch
|
|
502
|
-
yawrate = data.yaw
|
|
502
|
+
yawrate = -data.yaw
|
|
503
503
|
# Scale thrust to a value between -1.0 to 1.0
|
|
504
504
|
vz = (data.thrust - 32767) / 32767.0
|
|
505
505
|
# Integrate velocity setpoint
|
cfclient/utils/periodictimer.py
CHANGED
|
@@ -57,7 +57,7 @@ class PeriodicTimer:
|
|
|
57
57
|
logger.warning("Timer already started, not restarting")
|
|
58
58
|
return
|
|
59
59
|
self._thread = _PeriodicTimerThread(self._period, self._callbacks)
|
|
60
|
-
self._thread.
|
|
60
|
+
self._thread.daemon = True
|
|
61
61
|
self._thread.start()
|
|
62
62
|
|
|
63
63
|
def stop(self):
|
cfclient/version.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '2025.9'
|
|
32
|
+
__version_tuple__ = version_tuple = (2025, 9)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cfclient
|
|
3
|
+
Version: 2025.9
|
|
4
|
+
Summary: Crazyflie PC client
|
|
5
|
+
Author-email: Bitcraze and contributors <contact@bitcraze.io>
|
|
6
|
+
License: GPLv2+
|
|
7
|
+
Project-URL: Homepage, https://www.bitcraze.io
|
|
8
|
+
Project-URL: Documentation, https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/
|
|
9
|
+
Project-URL: Repository, https://github.com/bitcraze/crazyflie-clients-python
|
|
10
|
+
Project-URL: Issues, https://github.com/bitcraze/crazyflie-clients-python/issues
|
|
11
|
+
Keywords: quadcopter,crazyflie
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
16
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
17
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
18
|
+
Classifier: Intended Audience :: Science/Research
|
|
19
|
+
Classifier: Intended Audience :: Education
|
|
20
|
+
Classifier: Intended Audience :: Developers
|
|
21
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
22
|
+
Classifier: Operating System :: MacOS
|
|
23
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
26
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
27
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
28
|
+
Requires-Python: >=3.10
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE.txt
|
|
31
|
+
Requires-Dist: cflib~=0.1.29
|
|
32
|
+
Requires-Dist: setuptools
|
|
33
|
+
Requires-Dist: appdirs~=1.4.0
|
|
34
|
+
Requires-Dist: pyzmq~=26.0
|
|
35
|
+
Requires-Dist: pyqtgraph~=0.13
|
|
36
|
+
Requires-Dist: PyYAML~=6.0.1
|
|
37
|
+
Requires-Dist: numpy~=2.2
|
|
38
|
+
Requires-Dist: vispy~=0.15.2
|
|
39
|
+
Requires-Dist: pyopengl~=3.1.7
|
|
40
|
+
Requires-Dist: pyserial~=3.5
|
|
41
|
+
Requires-Dist: PyQt6~=6.7.1
|
|
42
|
+
Requires-Dist: PyQt6-sip~=13.8
|
|
43
|
+
Requires-Dist: pysdl2~=0.9.14; platform_system == "Windows" or platform_system == "Darwin"
|
|
44
|
+
Requires-Dist: pysdl2-dll==2.24.0; platform_system == "Windows" or platform_system == "Darwin"
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: pre-commit; extra == "dev"
|
|
47
|
+
Requires-Dist: cx_freeze==5.1.1; platform_system == "Windows" and extra == "dev"
|
|
48
|
+
Requires-Dist: jinja2==2.10.3; platform_system == "Windows" and extra == "dev"
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+
# Crazyflie PC client [](https://github.com/bitcraze/crazyflie-clients-python/actions?query=workflow%3ACI) [](https://snapcraft.io/cfclient)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
The Crazyflie PC client enables flashing and controlling the Crazyflie.
|
|
55
|
+
It implements the user interface and high-level control (for example gamepad handling).
|
|
56
|
+
The communication with Crazyflie and the implementation of the CRTP protocol to control the Crazyflie is handled by the [cflib](https://github.com/bitcraze/crazyflie-lib-python) project.
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
See the [installation instructions](docs/installation/install.md) in the GitHub docs folder.
|
|
60
|
+
|
|
61
|
+
## Official Documentation
|
|
62
|
+
|
|
63
|
+
Check out the [Bitcraze crazyflie-client-python documentation](https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/) on our website.
|
|
64
|
+
|
|
65
|
+
## Contribute
|
|
66
|
+
Go to the [contribute page](https://www.bitcraze.io/contribute/) on our website to learn more.
|
|
67
|
+
|
|
68
|
+
### Test code for contribution
|
|
69
|
+
Run the automated build locally to test your code
|
|
70
|
+
|
|
71
|
+
python3 tools/build/build
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
cfclient/__init__.py,sha256=
|
|
1
|
+
cfclient/__init__.py,sha256=1-s1f5yEocDIsIQYUukuImVujGb0OANFKMQ-qOz_Iso,1986
|
|
2
2
|
cfclient/gui.py,sha256=98OT0wTR_itVbiO_E6vjOkL2f0H9XUvjsCCCXQEuLRQ,6419
|
|
3
3
|
cfclient/headless.py,sha256=Hgp81TLpvKyo00oAEn5nBW49BC4RNZEXcYNXDrLyJn8,6287
|
|
4
|
+
cfclient/version.py,sha256=lzyMr149SJA6gYM3u_MLV9WzORol180OekFdOZ4VAfI,705
|
|
4
5
|
cfclient/configs/config.json,sha256=WT4ptz3q_3hoDASSLECniUErGIwTXLBfkEH97egdpJk,842
|
|
5
6
|
cfclient/configs/input/Generic_OS_X.json,sha256=LwAYms0RC2gutp2v_b5QcsjAFYNsEO40AeS__2zn07Q,1057
|
|
6
7
|
cfclient/configs/input/Joystick.json,sha256=4VrLEX6o2T5AogPZjFLNjaNx415fLwkrS8Htop7zSD0,2005
|
|
@@ -12,12 +13,28 @@ cfclient/configs/input/PS4_Mode_2.json,sha256=iqXWmysjeB9YrizxO64HTzYigG3EIK_lwi
|
|
|
12
13
|
cfclient/configs/input/PS4_shoulder_btns_yaw.json,sha256=A1FLfueb22eyuU_kAsWfrCULgsSdvGKsUWeUgH6315E,1914
|
|
13
14
|
cfclient/configs/input/xbox360_mode1.json,sha256=_beeR0vJsVeCFKTvrv9tSfn0q4_2cUowilABWI3aywk,1845
|
|
14
15
|
cfclient/configs/log/stabilizer.json,sha256=GLS6SFk7atFPm1wzDVVTe4cxu36WJyECfAZVA_V0QD4,387
|
|
15
|
-
cfclient/
|
|
16
|
+
cfclient/configs/log/PID_tuning/Attitude.json,sha256=u7HG2qtjag52hCF5CAMkTRauK_4GeqyPl0YUDf8Qtws,971
|
|
17
|
+
cfclient/configs/log/PID_tuning/Attitude_rate.json,sha256=8z28vW7o_law6P_CfcuBm-OsKddEh5kDBKcAJB33YxI,953
|
|
18
|
+
cfclient/configs/log/PID_tuning/Position.json,sha256=JUtrvDi16zLKRAWVNOQoH2Bo81Vi08-xHdM4xK8k41A,959
|
|
19
|
+
cfclient/configs/log/PID_tuning/Velocity.json,sha256=c3gugwFjvqH362etgV4s1XmHaAtEseFDbo_WM04SJ3E,965
|
|
20
|
+
cfclient/configs/log/PID_tuning_components/Pitch.json,sha256=893uMWAk-9GfEXHS3w-P4oxI0UQa1WyHCql0R_JgPIo,405
|
|
21
|
+
cfclient/configs/log/PID_tuning_components/Pitch_rate.json,sha256=9oGf68Hv6Mf7yWecJpy7LCiw2XExuA6wpqIJ8HUKDHE,401
|
|
22
|
+
cfclient/configs/log/PID_tuning_components/Position_x.json,sha256=XN-JL-cP-xJBmZ6rn9FLEtauM45hsVPANNTgG_7eVog,403
|
|
23
|
+
cfclient/configs/log/PID_tuning_components/Position_y.json,sha256=gFuHE4rUr9Ipx4y7n3vk_kSwIVxjbfwWJDHDhOU4yzI,403
|
|
24
|
+
cfclient/configs/log/PID_tuning_components/Position_z.json,sha256=6s7sxvxXOABBDuoYg6ld0BYACKed3fBDMVUeKeSFPWg,403
|
|
25
|
+
cfclient/configs/log/PID_tuning_components/Roll.json,sha256=a9YUTEKiNHuDy4kyRClWTX4Q2jGV0uoKYe30k4rzrYc,401
|
|
26
|
+
cfclient/configs/log/PID_tuning_components/Roll_rate.json,sha256=8mSJXn3vU_XRkJUo_6Wz2vKiU4X3TzlQsTO3s2HJTjs,398
|
|
27
|
+
cfclient/configs/log/PID_tuning_components/Velocity_x.json,sha256=GwNRKgdIT6dmP6KWHxgdAcWm3kU9s-7bBE7-gp0XPhU,405
|
|
28
|
+
cfclient/configs/log/PID_tuning_components/Velocity_y.json,sha256=dgLf8_KYeJWJ-IYK3RyWaVQA-1wDC82OdY5w3WOoNvw,405
|
|
29
|
+
cfclient/configs/log/PID_tuning_components/Velocity_z.json,sha256=4Ft9zscDrthqpgrTOlXZMP1NTO4FvjO5XiszQ5ejvCA,405
|
|
30
|
+
cfclient/configs/log/PID_tuning_components/Yaw.json,sha256=hAypXUh8auOBvDHVN-NNYhNTJWpsxh3JZov7jLCidzQ,399
|
|
31
|
+
cfclient/configs/log/PID_tuning_components/Yaw_rate.json,sha256=OviQ8oqp8zerQxNBawzSfnNnfugvcOWYVKUtkmNJwjw,397
|
|
32
|
+
cfclient/resources/log_param_doc.json,sha256=_icuv483NjdW-FcalfhwoYGOclIsWT-GvkqCCuhcK1Y,134117
|
|
16
33
|
cfclient/resources/map.html,sha256=pGwY2GW0F1rAtSImKbu-cFZyb6tJGwrDhkAgOZ98S68,360
|
|
17
34
|
cfclient/resources/map.js,sha256=0KyyJ-5W5qnrlEgTsp_oHk7b3w8k9qoyX4XjhThOkrU,839
|
|
18
35
|
cfclient/ui/__init__.py,sha256=8XHGfW8w3DnQGiL6R7GVob1KrKg4waKNzvSy79lRjMs,1342
|
|
19
36
|
cfclient/ui/connectivity_manager.py,sha256=DdkHL48pBOI9lctc2peHqzQy7u7-sGZTVHYdmd2T3Sk,8275
|
|
20
|
-
cfclient/ui/main.py,sha256=
|
|
37
|
+
cfclient/ui/main.py,sha256=X5_alyqcBsvVTwiDLK2L09YdFdJNvj9qcrNaLSmUzTs,36956
|
|
21
38
|
cfclient/ui/main.ui,sha256=Np1l54_f3EKsa8E8MtqqRcVYivsCBe2fCAx1Fpt_AYc,16092
|
|
22
39
|
cfclient/ui/pluginhelper.py,sha256=dSq6sCz5TbESfdTPzRzLExHnIZeKdDyw3cuWSViXqL8,1590
|
|
23
40
|
cfclient/ui/pose_logger.py,sha256=1vADKP4-iCj9avqcuPI_amtZg3IKeCVTX0zYao3HPCE,4041
|
|
@@ -35,8 +52,8 @@ cfclient/ui/dialogs/cf2config.py,sha256=2xYeTeTpK1h42HReInwfr06dvnXmSseiS8D_VDcT
|
|
|
35
52
|
cfclient/ui/dialogs/cf2config.ui,sha256=gistq-KuUB6E7SMub6ElCG2u3XpUe6i-a19VHcYv7vg,5850
|
|
36
53
|
cfclient/ui/dialogs/inputconfigdialogue.py,sha256=4yhPRTc2-l5yDu2VvavuQUEt629wEkJ3T38YexA3I4w,17922
|
|
37
54
|
cfclient/ui/dialogs/inputconfigdialogue.ui,sha256=5zJ5-S639ctNiPvR_GlBHpbFayQahK4JuQ3ggbOlnSc,24426
|
|
38
|
-
cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py,sha256=
|
|
39
|
-
cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui,sha256=
|
|
55
|
+
cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py,sha256=RmmVv8GSVTDl__D_hn2sQXfz3VKi9zygJkJv54VikOM,8421
|
|
56
|
+
cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui,sha256=KWISXmuFQBR3ogu2FCi_0-e36oJ2h4HefgdsnywECL0,2865
|
|
40
57
|
cfclient/ui/dialogs/lighthouse_system_type_dialog.py,sha256=vff5me_1PMvVOVTv9WCw9X7wKY_G4XCWwKOusuRSrEE,2999
|
|
41
58
|
cfclient/ui/dialogs/lighthouse_system_type_dialog.ui,sha256=0aWRgeXOGptg-gY4qn3OvFWg5To09Y9_V3Lbx3wOx4A,3279
|
|
42
59
|
cfclient/ui/dialogs/logconfigdialogue.py,sha256=Qbtb4ISxTi-a3_GaYKRDm_dEK1XswctDbdffWoFf8Fk,22543
|
|
@@ -49,14 +66,14 @@ cfclient/ui/icons/icon-256.png,sha256=SrUxND4cRHSRC_0LCPHIzwYRo1r9ygR1aw_D0BRxfo
|
|
|
49
66
|
cfclient/ui/tabs/ConsoleTab.py,sha256=IP8FB_ev4iq5Bp4FfH9PrIIcEk_vtfdgaD3LYCv4ytw,5191
|
|
50
67
|
cfclient/ui/tabs/CrtpSharkToolbox.py,sha256=2TLhfunC7vqLBzl8Ld3sPPPrPbBvazfPU4UOV7woo24,4242
|
|
51
68
|
cfclient/ui/tabs/ExampleTab.py,sha256=PQZVNV2Rn0hp_WyY0jOQmUJKvZNMJSwIM77JIJOfgQg,3736
|
|
52
|
-
cfclient/ui/tabs/FlightTab.py,sha256=
|
|
69
|
+
cfclient/ui/tabs/FlightTab.py,sha256=3H4viBwM6yMJLOEJ1R0YVfLTFMTCVOK6GsWpQqAEgcI,36514
|
|
53
70
|
cfclient/ui/tabs/GpsTab.py,sha256=TZKoiSQomc-TSnQ4iXanMzmp4BnO07BGjbEFFmW0Tak,6118
|
|
54
71
|
cfclient/ui/tabs/LEDTab.py,sha256=H0ujpD5XSejdGQf67eJppirJ3eeMLQzX7EZOKj7Vl3A,5767
|
|
55
72
|
cfclient/ui/tabs/LogBlockDebugTab.py,sha256=mamAHKwrqifiXFu5zxROB2HCvxBuvh0A8Yjpf8Agczc,3892
|
|
56
73
|
cfclient/ui/tabs/LogBlockTab.py,sha256=Iktp20J8Rzxoaf8grcSw1n14DSmH_X32tmCRH_nT-oc,12327
|
|
57
74
|
cfclient/ui/tabs/LogClientTab.py,sha256=cEogxYAM0rkMHLbmJqp_6sNIIefqCGdZnbJZQXNpvEE,2630
|
|
58
|
-
cfclient/ui/tabs/LogTab.py,sha256=
|
|
59
|
-
cfclient/ui/tabs/ParamTab.py,sha256=
|
|
75
|
+
cfclient/ui/tabs/LogTab.py,sha256=A1xj0xlW25QjrpdnRYzrKXChGitf4MAePA_XWd2dtTM,4568
|
|
76
|
+
cfclient/ui/tabs/ParamTab.py,sha256=_A90cI_HOFpmHm3v7bk1fj13fJIu4hjgYjwUUTVOlig,21613
|
|
60
77
|
cfclient/ui/tabs/PlotTab.py,sha256=M9HRunURxqQHlLqCeQSZaz_lYl3HA-oK0Y2BGBT2b4w,9097
|
|
61
78
|
cfclient/ui/tabs/TuningTab.py,sha256=iJHz0LehnfAb4iIPR_MMY5m-8iCtVhbAxm6_h_yobvc,13217
|
|
62
79
|
cfclient/ui/tabs/__init__.py,sha256=n_1-TsSh2H5YaaTP9u54tqRx1qIq0_pV9fPsCkTo-fc,2052
|
|
@@ -96,12 +113,12 @@ cfclient/utils/config.py,sha256=yAcCJtixnzAGScKFLh1Pb5hg524OkYePkdwlGiXfxlE,3418
|
|
|
96
113
|
cfclient/utils/config_manager.py,sha256=sqyIHv9s9ztof0v-YKp8fGk0ftYvId6ppUe77HvdB3g,6915
|
|
97
114
|
cfclient/utils/logconfigreader.py,sha256=Y5RRhYm2Pk4R8s9W8EN643jbsiOMzDS_XJrBGth-cm0,13674
|
|
98
115
|
cfclient/utils/logdatawriter.py,sha256=Qso_w3WeTksrSGcacfmRpOy4KMOTHytCQeIkuo9bO7o,4012
|
|
99
|
-
cfclient/utils/periodictimer.py,sha256=
|
|
116
|
+
cfclient/utils/periodictimer.py,sha256=HHE8lhBeYMGSZlcHyZGdaAtLdKuzuVuMzQUgFXzKDJI,2592
|
|
100
117
|
cfclient/utils/singleton.py,sha256=yz-JMLr7F_rO6XpVn4VmGWt2c3vIascsZl0cUeoWqy0,1513
|
|
101
118
|
cfclient/utils/ui.py,sha256=85MeNFeFIjNE2PYn-rjtpfSaOEqFiyZPOZlEfEDS_yo,9687
|
|
102
119
|
cfclient/utils/zmq_led_driver.py,sha256=IJA4Q-FiqVEJH9lOOnFOTp85ygt56FBt4fOajrZ6Y2k,3591
|
|
103
120
|
cfclient/utils/zmq_param.py,sha256=Ok2XqQrv6Y7aalLePgd-bW_lbC1Y5QD7HDnGjjMjMNw,3936
|
|
104
|
-
cfclient/utils/input/__init__.py,sha256=
|
|
121
|
+
cfclient/utils/input/__init__.py,sha256=dGkzxKaf7INCUrjB2qOV63plxpPYvaO990TwLy2erFE,22487
|
|
105
122
|
cfclient/utils/input/inputreaderinterface.py,sha256=4tNV7fdO6cSTgPEvAHlhR3QvY2t36nN5GzuCXx73wwo,9698
|
|
106
123
|
cfclient/utils/input/inputinterfaces/__init__.py,sha256=kqBz9KEj6dgK8Wncr1i2xS2FzUgcY0OS7UOf3kwLi5A,3447
|
|
107
124
|
cfclient/utils/input/inputinterfaces/leapmotion.py,sha256=-jPtWPCqqUisKfMLsgWgi11EszMPFbqu0tPXL0A5-aY,4780
|
|
@@ -114,13 +131,15 @@ cfclient/utils/input/mux/__init__.py,sha256=TXP6Fqnj3eCZ5jrpzDhCjTbdnLDjuHn5cYcF
|
|
|
114
131
|
cfclient/utils/input/mux/nomux.py,sha256=HnQCoi-MqJvnOF3anyrlVMEWPJEBud_NYCoDFyJRC_o,1627
|
|
115
132
|
cfclient/utils/input/mux/takeovermux.py,sha256=7aftcx7-Kv2m2Cgs-Fuvs8i6NtvtPICSjUNsD04NGmA,1771
|
|
116
133
|
cfclient/utils/input/mux/takeoverselectivemux.py,sha256=nKX1mV331o37TrVT4FcsGdErFnzkyoxgSRj7rtBbZtI,2415
|
|
134
|
+
cfclient-2025.9.dist-info/licenses/LICENSE.txt,sha256=UMjaTs39CS8X5zNtH4UWpmDBTydaVVQSDG8j1N285mI,18415
|
|
135
|
+
cfconfig/Makefile,sha256=O2IZLnrGtKPfUC_eT_OWpAU5d9ULNjfBUdLi6VfPcK0,1775
|
|
136
|
+
cfconfig/configblock.py,sha256=aOzCnI-JJEyYI1-tJZMmzeB2p9bQL0M3fY9FZeNwQas,2901
|
|
117
137
|
cfloader/__init__.py,sha256=Cg4mKk5YlQytgZ-LXifDwW1YmYUZpw4nXbk4OtJUMgE,6023
|
|
118
138
|
cfloader/__main__.py,sha256=Ct5fO0RV6foPBMmTHpEASS6mZ-4roJKoXWL8lhuuda0,65
|
|
119
139
|
cfzmq/__init__.py,sha256=ik4QKjZP9syOqpEjSnzv57u_VTa-Cb3QcpaeyVpL_gc,14025
|
|
120
140
|
cfzmq/__main__.py,sha256=AoNwiCOwva0pyNZXMLRW5COKB6Fh3bPgQO-gE5hD_Cg,62
|
|
121
|
-
cfclient-
|
|
122
|
-
cfclient-
|
|
123
|
-
cfclient-
|
|
124
|
-
cfclient-
|
|
125
|
-
cfclient-
|
|
126
|
-
cfclient-2024.11.dist-info/RECORD,,
|
|
141
|
+
cfclient-2025.9.dist-info/METADATA,sha256=R8mH1-D8ymqP7H89lfZR2FMXVdbiLOD3ku63_JZcTT8,3358
|
|
142
|
+
cfclient-2025.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
143
|
+
cfclient-2025.9.dist-info/entry_points.txt,sha256=8_UbQo4eRlY2aks3MbiuNTTpaDvjVSsraMj_fVcrBtY,127
|
|
144
|
+
cfclient-2025.9.dist-info/top_level.txt,sha256=kEMGlBZwPaeAdpoqXh5uBLBb0xaanO_wPW14T6BuJKs,33
|
|
145
|
+
cfclient-2025.9.dist-info/RECORD,,
|
cfconfig/Makefile
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
|
|
2
|
+
######### JTAG and environment configuration ##########
|
|
3
|
+
OPENOCD_INTERFACE ?= interface/jtagkey.cfg
|
|
4
|
+
OPENOCD_TARGET ?= target/stm32.cfg
|
|
5
|
+
#######################################################
|
|
6
|
+
|
|
7
|
+
CONFIGFILE := cblock.cfg
|
|
8
|
+
BINFILE := cblock.bin
|
|
9
|
+
|
|
10
|
+
help:
|
|
11
|
+
@echo "Crazyflie config block makefile utility"
|
|
12
|
+
@echo
|
|
13
|
+
@echo "This script permits to read and write the config block of Crazyflie using"
|
|
14
|
+
@echo "openocd and any JTAG probe supported by it. The copter has to be connected"
|
|
15
|
+
@echo "to the jtag probe"
|
|
16
|
+
@echo
|
|
17
|
+
@echo "Available action:"
|
|
18
|
+
@echo " help : Display this message"
|
|
19
|
+
@echo " read : Read the configuration block from the copter using openOCD,"
|
|
20
|
+
@echo " decode it and save it in a configuration file"
|
|
21
|
+
@echo " write : Compile the configuration file to a block and write the block"
|
|
22
|
+
@echo " in the copter using openOCD"
|
|
23
|
+
@echo
|
|
24
|
+
@echo "Parameters that can be configured: [current value] (can be altered from command line)"
|
|
25
|
+
@echo " CONFIGFILE [$(CONFIGFILE)]"
|
|
26
|
+
@echo " BINFILE [$(BINFILE)]"
|
|
27
|
+
@echo " OCD_INTERFACE [$(OPENOCD_INTERFACE)]"
|
|
28
|
+
@echo " OCD_TARGET [$(OPENOCD_TARGET)]"
|
|
29
|
+
|
|
30
|
+
read: readbin bin2cfg
|
|
31
|
+
|
|
32
|
+
write: cfg2bin writebin
|
|
33
|
+
|
|
34
|
+
readbin:
|
|
35
|
+
openocd -d0 -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init -c targets \
|
|
36
|
+
-c "reset halt" -c "dump_image $(BINFILE) 0x1FC00 1024" \
|
|
37
|
+
-c "reset run" -c shutdown
|
|
38
|
+
|
|
39
|
+
writebin:
|
|
40
|
+
openocd -d0 -f $(OPENOCD_INTERFACE) -f $(OPENOCD_TARGET) -c init -c targets \
|
|
41
|
+
-c "reset halt" -c "flash erase_sector 0 127 127" \
|
|
42
|
+
-c "flash write_bank 0 $(BINFILE) 0x1FC00" -c "verify_image $(BINFILE) 0x1FC00" \
|
|
43
|
+
-c "reset run" -c shutdown
|
|
44
|
+
|
|
45
|
+
bin2cfg:
|
|
46
|
+
./configblock.py extract $(BINFILE) $(CONFIGFILE)
|
|
47
|
+
|
|
48
|
+
cfg2bin:
|
|
49
|
+
./configblock.py generate $(CONFIGFILE) $(BINFILE)
|
|
50
|
+
|
|
51
|
+
|
cfconfig/configblock.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Configuration block compiler/decompiler
|
|
4
|
+
# Fully static version (to be considered as a prototype...)!
|
|
5
|
+
from configparser import ConfigParser
|
|
6
|
+
import struct
|
|
7
|
+
import sys
|
|
8
|
+
from functools import reduce
|
|
9
|
+
|
|
10
|
+
# Radio speed enum type
|
|
11
|
+
speeds = ["250K", "1M", "2M"]
|
|
12
|
+
radioSpeedPos = 2
|
|
13
|
+
|
|
14
|
+
defaultConfig = """#Crazyflie config block
|
|
15
|
+
#default version generated from squatch
|
|
16
|
+
[radio]
|
|
17
|
+
channel= 100
|
|
18
|
+
speed= 2M
|
|
19
|
+
|
|
20
|
+
[calib]
|
|
21
|
+
pitchTrim= 0.0
|
|
22
|
+
rollTrim= 0.0
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
config = """#Crazyflie config block
|
|
26
|
+
#Block version %d extracted from copter
|
|
27
|
+
[radio]
|
|
28
|
+
channel= %d
|
|
29
|
+
speed= %s
|
|
30
|
+
|
|
31
|
+
[calib]
|
|
32
|
+
pitchTrim= %f
|
|
33
|
+
rollTrim= %f
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
configVersion = 0
|
|
37
|
+
structFormat = "<BBBff"
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def checksum256(st):
|
|
41
|
+
return reduce(lambda x, y: x + y, list(map(ord, st))) % 256
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def compileBlock(configFile, binFile):
|
|
45
|
+
config = ConfigParser()
|
|
46
|
+
|
|
47
|
+
config.read(configFile)
|
|
48
|
+
|
|
49
|
+
block = (configVersion,)
|
|
50
|
+
|
|
51
|
+
block += (config.getint("radio", "channel"),)
|
|
52
|
+
block += (speeds.index(config.get("radio", "speed").upper()),)
|
|
53
|
+
|
|
54
|
+
block += (config.getfloat("calib", "pitchTrim"),)
|
|
55
|
+
block += (config.getfloat("calib", "rollTrim"),)
|
|
56
|
+
|
|
57
|
+
bin = struct.pack(structFormat, *block)
|
|
58
|
+
|
|
59
|
+
# Adding some magic:
|
|
60
|
+
bin = "0xBC" + bin
|
|
61
|
+
|
|
62
|
+
bin += struct.pack("B", 256 - checksum256(bin))
|
|
63
|
+
|
|
64
|
+
# print("Config block checksum: %02x" % bin[len(bin)-1])
|
|
65
|
+
|
|
66
|
+
bfile = open(binFile, "w")
|
|
67
|
+
bfile.write(bin)
|
|
68
|
+
bfile.close()
|
|
69
|
+
|
|
70
|
+
print("Config block compiled successfully to", binFile)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def decompileBlock(binFile, configFile):
|
|
74
|
+
bfile = open(binFile)
|
|
75
|
+
bin = bfile.read()
|
|
76
|
+
bfile.close()
|
|
77
|
+
|
|
78
|
+
if (bin[0:4] != "0xBC" or len(bin) < (struct.calcsize(structFormat) + 5) or
|
|
79
|
+
checksum256(bin[0:struct.calcsize(structFormat) + 5]) != 0):
|
|
80
|
+
print("Config block erased of altered, generating default file")
|
|
81
|
+
cfile = open(configFile, "w")
|
|
82
|
+
cfile.write(defaultConfig)
|
|
83
|
+
cfile.close()
|
|
84
|
+
else:
|
|
85
|
+
block = struct.unpack(structFormat,
|
|
86
|
+
bin[4:struct.calcsize(structFormat) + 4])
|
|
87
|
+
if block[0] != configVersion:
|
|
88
|
+
print("Error! wrong configuration block version, "
|
|
89
|
+
"this program must certainly be updated!")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
block = (block[0:radioSpeedPos] + (speeds[block[radioSpeedPos]],) +
|
|
93
|
+
block[radioSpeedPos + 1:len(block)])
|
|
94
|
+
|
|
95
|
+
cfile = open(configFile, "w")
|
|
96
|
+
cfile.write(config % block)
|
|
97
|
+
cfile.close()
|
|
98
|
+
print("Config block successfully extracted to", configFile)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
if __name__ == "__main__":
|
|
102
|
+
if (len(sys.argv) < 4 or
|
|
103
|
+
(sys.argv[1] != "generate" and sys.argv[1] != "extract")):
|
|
104
|
+
print("Configuration block compiler/decompiler.")
|
|
105
|
+
print(" Usage: %s <generate|extract> <infile> <outfile>"
|
|
106
|
+
% sys.argv[0])
|
|
107
|
+
|
|
108
|
+
if sys.argv[1] == "generate":
|
|
109
|
+
compileBlock(sys.argv[2], sys.argv[3])
|
|
110
|
+
else:
|
|
111
|
+
decompileBlock(sys.argv[2], sys.argv[3])
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: cfclient
|
|
3
|
-
Version: 2024.11
|
|
4
|
-
Summary: Bitcraze Cazyflie quadcopter client
|
|
5
|
-
Home-page: http://www.bitcraze.io
|
|
6
|
-
Author: Bitcraze team
|
|
7
|
-
Author-email: contact@bitcraze.se
|
|
8
|
-
Keywords: quadcopter crazyflie
|
|
9
|
-
Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)
|
|
10
|
-
Classifier: Programming Language :: Python :: 3.4
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.5
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
License-File: LICENSE.txt
|
|
14
|
-
Requires-Dist: cflib >=0.1.27
|
|
15
|
-
Requires-Dist: setuptools
|
|
16
|
-
Requires-Dist: appdirs ~=1.4.0
|
|
17
|
-
Requires-Dist: pyzmq ~=26.0
|
|
18
|
-
Requires-Dist: pyqtgraph ~=0.13
|
|
19
|
-
Requires-Dist: PyYAML ~=6.0.1
|
|
20
|
-
Requires-Dist: numpy ~=1.20
|
|
21
|
-
Requires-Dist: vispy ~=0.14.2
|
|
22
|
-
Requires-Dist: pyopengl ~=3.1.7
|
|
23
|
-
Requires-Dist: pyserial ~=3.5
|
|
24
|
-
Requires-Dist: pyqt6 >=6.4
|
|
25
|
-
Requires-Dist: PyQt6-sip >=13.4
|
|
26
|
-
Requires-Dist: pysdl2 ~=0.9.14 ; platform_system == "Windows" or platform_system == "Darwin"
|
|
27
|
-
Requires-Dist: pysdl2-dll ==2.24.0 ; platform_system == "Windows" or platform_system == "Darwin"
|
|
28
|
-
Provides-Extra: dev
|
|
29
|
-
Requires-Dist: pre-commit ; extra == 'dev'
|
|
30
|
-
Requires-Dist: cx-freeze ==5.1.1 ; (platform_system == "Windows") and extra == 'dev'
|
|
31
|
-
Requires-Dist: jinja2 ==2.10.3 ; (platform_system == "Windows") and extra == 'dev'
|
|
32
|
-
|
|
33
|
-
# Crazyflie PC client [](https://github.com/bitcraze/crazyflie-clients-python/actions?query=workflow%3ACI) [](https://snapcraft.io/cfclient)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
The Crazyflie PC client enables flashing and controlling the Crazyflie.
|
|
37
|
-
It implements the user interface and high-level control (for example gamepad handling).
|
|
38
|
-
The communication with Crazyflie and the implementation of the CRTP protocol to control the Crazyflie is handled by the [cflib](https://github.com/bitcraze/crazyflie-lib-python) project.
|
|
39
|
-
|
|
40
|
-
## Installation
|
|
41
|
-
See the [installation instructions](docs/installation/install.md) in the GitHub docs folder.
|
|
42
|
-
|
|
43
|
-
## Official Documentation
|
|
44
|
-
|
|
45
|
-
Check out the [Bitcraze crazyflie-client-python documentation](https://www.bitcraze.io/documentation/repository/crazyflie-clients-python/master/) on our website.
|
|
46
|
-
|
|
47
|
-
## Contribute
|
|
48
|
-
Go to the [contribute page](https://www.bitcraze.io/contribute/) on our website to learn more.
|
|
49
|
-
|
|
50
|
-
### Test code for contribution
|
|
51
|
-
Run the automated build locally to test your code
|
|
52
|
-
|
|
53
|
-
python3 tools/build/build
|
|
File without changes
|
|
File without changes
|