cfclient 2017.4__py3-none-any.whl → 2025.12.1__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.
- cfclient/__init__.py +16 -11
- cfclient/configs/config.json +4 -3
- cfclient/configs/input/Generic_OS_X.json +1 -0
- cfclient/configs/input/Joystick.json +1 -0
- cfclient/configs/input/PS3_Mode_1.json +1 -0
- cfclient/configs/input/PS3_Mode_2.json +1 -0
- cfclient/configs/input/PS3_Mode_3.json +1 -0
- cfclient/configs/input/PS4_Mode_1.json +1 -0
- cfclient/configs/input/PS4_Mode_2.json +1 -0
- cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
- cfclient/configs/input/xbox360_mode1.json +1 -0
- 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/gui.py +44 -9
- cfclient/headless.py +3 -12
- cfclient/resources/log_param_doc.json +1 -0
- cfclient/ui/connectivity_manager.py +198 -0
- cfclient/ui/dialogs/about.py +53 -36
- cfclient/ui/dialogs/about.ui +23 -3
- cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
- cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
- cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
- cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
- cfclient/ui/dialogs/bootloader.py +448 -85
- cfclient/ui/dialogs/bootloader.ui +387 -134
- cfclient/ui/dialogs/cf2config.py +4 -4
- cfclient/ui/dialogs/cf2config.ui +3 -4
- cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
- cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
- cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
- cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
- cfclient/ui/dialogs/logconfigdialogue.py +401 -101
- cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
- cfclient/ui/icons/bl.webp +0 -0
- cfclient/ui/icons/bolt.webp +0 -0
- cfclient/ui/icons/cf21.webp +0 -0
- cfclient/ui/icons/checkmark_black.png +0 -0
- cfclient/ui/icons/checkmark_white.png +0 -0
- cfclient/ui/icons/create.png +0 -0
- cfclient/ui/icons/delete.png +0 -0
- cfclient/ui/icons/flapper.webp +0 -0
- cfclient/ui/icons/tag.webp +0 -0
- cfclient/ui/main.py +328 -258
- cfclient/ui/main.ui +184 -80
- cfclient/ui/pluginhelper.py +7 -1
- cfclient/ui/pose_logger.py +116 -0
- cfclient/ui/tab_toolbox.py +208 -0
- cfclient/ui/tabs/ColorLEDTab.py +752 -0
- cfclient/ui/tabs/ConsoleTab.py +48 -13
- cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
- cfclient/ui/tabs/ExampleTab.py +9 -16
- cfclient/ui/tabs/FlightTab.py +437 -325
- cfclient/ui/tabs/GpsTab.py +14 -20
- cfclient/ui/tabs/LEDRingTab.py +277 -0
- cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
- cfclient/ui/tabs/LogBlockTab.py +35 -35
- cfclient/ui/tabs/LogClientTab.py +85 -0
- cfclient/ui/tabs/LogTab.py +50 -27
- cfclient/ui/tabs/ParamTab.py +443 -57
- cfclient/ui/tabs/PlotTab.py +23 -25
- cfclient/ui/tabs/TuningTab.py +292 -0
- cfclient/ui/tabs/__init__.py +12 -2
- cfclient/ui/tabs/colorLEDTab.ui +624 -0
- cfclient/ui/tabs/consoleTab.ui +46 -0
- cfclient/ui/tabs/flightActionContainer.ui +103 -0
- cfclient/ui/tabs/flightTab.ui +724 -237
- cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
- cfclient/ui/tabs/lighthouse_tab.py +714 -0
- cfclient/ui/tabs/lighthouse_tab.ui +430 -0
- cfclient/ui/tabs/locopositioning_tab.py +606 -389
- cfclient/ui/tabs/locopositioning_tab.ui +370 -253
- cfclient/ui/tabs/logClientTab.ui +52 -0
- cfclient/ui/tabs/logTab.ui +1 -1
- cfclient/ui/tabs/paramTab.ui +204 -3
- cfclient/ui/tabs/tuningTab.ui +773 -0
- cfclient/ui/widgets/ai.py +37 -39
- cfclient/ui/widgets/hexspinbox.py +16 -10
- cfclient/ui/widgets/plotter.ui +39 -47
- cfclient/ui/widgets/plotwidget.py +57 -22
- cfclient/ui/widgets/super_slider.py +112 -0
- cfclient/ui/wizards/__init__.py +0 -0
- cfclient/ui/wizards/bslh_1.png +0 -0
- cfclient/ui/wizards/bslh_2.png +0 -0
- cfclient/ui/wizards/bslh_3.png +0 -0
- cfclient/ui/wizards/bslh_4.png +0 -0
- cfclient/ui/wizards/bslh_5.png +0 -0
- cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
- cfclient/utils/config_manager.py +5 -4
- cfclient/utils/input/__init__.py +77 -19
- cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
- cfclient/utils/input/inputreaderinterface.py +17 -7
- cfclient/utils/input/inputreaders/__init__.py +17 -0
- cfclient/utils/logconfigreader.py +245 -25
- cfclient/utils/logdatawriter.py +3 -1
- cfclient/utils/periodictimer.py +1 -1
- cfclient/utils/ui.py +336 -0
- cfclient/utils/zmq_led_driver.py +5 -0
- cfclient/utils/zmq_param.py +6 -0
- cfclient/version.py +34 -1
- cfclient-2025.12.1.dist-info/METADATA +70 -0
- cfclient-2025.12.1.dist-info/RECORD +152 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
- cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
- {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
- cfconfig/Makefile +51 -0
- cfconfig/configblock.py +111 -0
- cfloader/__init__.py +41 -55
- cfzmq/__init__.py +22 -14
- cfclient/ui/dialogs/cf1config.py +0 -265
- cfclient/ui/dialogs/cf1config.ui +0 -260
- cfclient/ui/tab.py +0 -96
- cfclient/ui/tabs/LEDTab.py +0 -169
- cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
- cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
- cfclient/ui/toolboxes/__init__.py +0 -45
- cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
- cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
- cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
- cfclient-2017.4.dist-info/METADATA +0 -22
- cfclient-2017.4.dist-info/RECORD +0 -104
- cfclient-2017.4.dist-info/metadata.json +0 -1
- /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
- /cfclient/ui/{toolboxes → tabs}/crtpSharkToolbox.ui +0 -0
cfloader/__init__.py
CHANGED
|
@@ -32,26 +32,27 @@
|
|
|
32
32
|
import sys
|
|
33
33
|
|
|
34
34
|
import cflib.crtp
|
|
35
|
-
from cflib.bootloader import Bootloader
|
|
36
|
-
from cflib.bootloader.boottypes import BootVersion
|
|
35
|
+
from cflib.bootloader import Bootloader, Target
|
|
36
|
+
from cflib.bootloader.boottypes import BootVersion
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def main():
|
|
40
40
|
# Initialise the CRTP link driver
|
|
41
|
-
link = None
|
|
42
41
|
try:
|
|
43
42
|
cflib.crtp.init_drivers()
|
|
44
|
-
link = cflib.crtp.get_link_driver("radio://")
|
|
45
43
|
except Exception as e:
|
|
46
44
|
print("Error: {}".format(str(e)))
|
|
47
|
-
if link:
|
|
48
|
-
link.close()
|
|
49
45
|
sys.exit(-1)
|
|
50
46
|
|
|
51
47
|
# Set the default parameters
|
|
52
48
|
clink = None
|
|
53
49
|
action = "info"
|
|
54
50
|
boot = "cold"
|
|
51
|
+
filename = None # type: Optional[str]
|
|
52
|
+
targets = None # type: Optional[List[Target]]
|
|
53
|
+
bl = None # type: Optional[Bootloader]
|
|
54
|
+
|
|
55
|
+
exit_result = 0
|
|
55
56
|
|
|
56
57
|
if len(sys.argv) < 2:
|
|
57
58
|
print()
|
|
@@ -74,8 +75,6 @@ def main():
|
|
|
74
75
|
print(" possible page in flash and reset "
|
|
75
76
|
"to firmware")
|
|
76
77
|
print(" mode.")
|
|
77
|
-
if link:
|
|
78
|
-
link.close()
|
|
79
78
|
sys.exit(0)
|
|
80
79
|
|
|
81
80
|
# Analyse the command line parameters
|
|
@@ -103,81 +102,65 @@ def main():
|
|
|
103
102
|
elif sys.argv[0] == "reset":
|
|
104
103
|
action = "reset"
|
|
105
104
|
elif sys.argv[0] == "flash":
|
|
106
|
-
# print len(sys.argv)
|
|
107
105
|
if len(sys.argv) < 2:
|
|
108
106
|
print("The flash action require a file name.")
|
|
109
|
-
link.close()
|
|
110
107
|
sys.exit(-1)
|
|
111
108
|
action = "flash"
|
|
112
109
|
filename = sys.argv[1]
|
|
113
|
-
|
|
110
|
+
targets = [] # Dict[Target]
|
|
114
111
|
for t in sys.argv[2:]:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
112
|
+
if t.startswith("deck-"):
|
|
113
|
+
[deck, target, type] = t.split("-")
|
|
114
|
+
targets.append(Target("deck", target, type, [], []))
|
|
118
115
|
else:
|
|
119
|
-
|
|
116
|
+
[target, type] = t.split("-")
|
|
117
|
+
targets.append(Target("cf2", target, type, [], []))
|
|
120
118
|
else:
|
|
121
119
|
print("Action", sys.argv[0], "unknown!")
|
|
122
|
-
link.close()
|
|
123
120
|
sys.exit(-1)
|
|
124
121
|
|
|
125
|
-
# Currently there's two different targets available
|
|
126
|
-
targets = ()
|
|
127
|
-
|
|
128
122
|
try:
|
|
129
123
|
# Initialise the bootloader lib
|
|
130
124
|
bl = Bootloader(clink)
|
|
131
125
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
# The connection is done by reseting to the bootloader (default)
|
|
136
|
-
if boot == "reset":
|
|
137
|
-
print("Reset to bootloader mode ..."),
|
|
126
|
+
warm_boot = (boot == "reset")
|
|
127
|
+
if warm_boot:
|
|
128
|
+
print("Reset to bootloader mode ...")
|
|
138
129
|
sys.stdout.flush()
|
|
139
|
-
if bl.start_bootloader(warm_boot=True):
|
|
140
|
-
print(" done!")
|
|
141
|
-
else:
|
|
142
|
-
print("Failed to warmboot")
|
|
143
|
-
bl.close()
|
|
144
|
-
sys.exit(-1)
|
|
145
130
|
else: # The connection is done by a cold boot ...
|
|
146
131
|
print("Restart the Crazyflie you want to bootload in the next"),
|
|
147
132
|
print(" 10 seconds ..."),
|
|
148
133
|
|
|
149
134
|
sys.stdout.flush()
|
|
150
|
-
if bl.start_bootloader(warm_boot=False):
|
|
151
|
-
print(" done!")
|
|
152
|
-
else:
|
|
153
|
-
print("Cannot connect the bootloader!")
|
|
154
|
-
bl.close()
|
|
155
|
-
sys.exit(-1)
|
|
156
|
-
|
|
157
|
-
print("Connected to bootloader on {} (version=0x{:X})".format(
|
|
158
|
-
BootVersion.to_ver_string(bl.protocol_version),
|
|
159
|
-
bl.protocol_version))
|
|
160
|
-
|
|
161
|
-
if bl.protocol_version == BootVersion.CF2_PROTO_VER:
|
|
162
|
-
targets += (bl.get_target(TargetTypes.NRF51),)
|
|
163
|
-
targets += (bl.get_target(TargetTypes.STM32),)
|
|
164
135
|
|
|
165
136
|
######################################
|
|
166
137
|
# Doing something (hopefully) useful
|
|
167
138
|
######################################
|
|
168
139
|
|
|
169
|
-
# Print information about the targets
|
|
170
|
-
for target in targets:
|
|
171
|
-
print(target)
|
|
172
140
|
if action == "info":
|
|
173
|
-
|
|
141
|
+
def print_info(version: int, connected_targets: [Target]):
|
|
142
|
+
print("Connected to bootloader on {} (version=0x{:X})".format(
|
|
143
|
+
BootVersion.to_ver_string(version),
|
|
144
|
+
version
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
for target in connected_targets:
|
|
148
|
+
print(target)
|
|
149
|
+
|
|
150
|
+
# flash_full called with no filename will not flash, just call
|
|
151
|
+
# our info callback
|
|
152
|
+
bl.flash_full(None, None, warm_boot, None, print_info)
|
|
153
|
+
elif action == "flash" and filename:
|
|
154
|
+
is_target_required = not filename.endswith('.zip')
|
|
155
|
+
if (is_target_required and not targets):
|
|
156
|
+
print("The flash action with a non .zip file requires a target")
|
|
157
|
+
sys.exit(-1)
|
|
158
|
+
try:
|
|
159
|
+
bl.flash_full(None, filename, warm_boot, targets)
|
|
160
|
+
except Exception as e:
|
|
161
|
+
print("Failed to flash: {}".format(e))
|
|
162
|
+
exit_result = -1
|
|
174
163
|
elif action == "reset":
|
|
175
|
-
print
|
|
176
|
-
print("Reset in firmware mode ...")
|
|
177
|
-
bl.reset_to_firmware()
|
|
178
|
-
elif action == "flash":
|
|
179
|
-
bl.flash(filename, targetnames)
|
|
180
|
-
print("Reset in firmware mode ...")
|
|
181
164
|
bl.reset_to_firmware()
|
|
182
165
|
else:
|
|
183
166
|
None
|
|
@@ -186,6 +169,7 @@ def main():
|
|
|
186
169
|
|
|
187
170
|
traceback.print_exc(file=sys.stdout)
|
|
188
171
|
print(e)
|
|
172
|
+
exit_result = -1
|
|
189
173
|
|
|
190
174
|
finally:
|
|
191
175
|
#########################
|
|
@@ -193,3 +177,5 @@ def main():
|
|
|
193
177
|
#########################
|
|
194
178
|
if bl:
|
|
195
179
|
bl.close()
|
|
180
|
+
|
|
181
|
+
sys.exit(exit_result)
|
cfzmq/__init__.py
CHANGED
|
@@ -53,15 +53,15 @@ if os.name == 'posix':
|
|
|
53
53
|
os.environ["SDL_VIDEODRIVER"] = "dummy"
|
|
54
54
|
|
|
55
55
|
# Main command socket for control (ping/pong)
|
|
56
|
-
ZMQ_SRV_PORT =
|
|
56
|
+
ZMQ_SRV_PORT = 0
|
|
57
57
|
# Log data socket (publish)
|
|
58
|
-
ZMQ_LOG_PORT =
|
|
58
|
+
ZMQ_LOG_PORT = 1
|
|
59
59
|
# Param value updated (publish)
|
|
60
|
-
ZMQ_PARAM_PORT =
|
|
60
|
+
ZMQ_PARAM_PORT = 2
|
|
61
61
|
# Async event for connection, like connection lost (publish)
|
|
62
|
-
ZMQ_CONN_PORT =
|
|
62
|
+
ZMQ_CONN_PORT = 3
|
|
63
63
|
# Control set-poins for Crazyflie (pull)
|
|
64
|
-
ZMQ_CTRL_PORT =
|
|
64
|
+
ZMQ_CTRL_PORT = 4
|
|
65
65
|
|
|
66
66
|
# Timeout before giving up when verifying param write
|
|
67
67
|
PARAM_TIMEOUT = 2
|
|
@@ -317,9 +317,9 @@ class _CtrlThread(Thread):
|
|
|
317
317
|
class ZMQServer():
|
|
318
318
|
"""Crazyflie ZMQ server"""
|
|
319
319
|
|
|
320
|
-
def __init__(self, base_url):
|
|
320
|
+
def __init__(self, base_url, base_port):
|
|
321
321
|
"""Start threads and bind ports"""
|
|
322
|
-
cflib.crtp.init_drivers(
|
|
322
|
+
cflib.crtp.init_drivers()
|
|
323
323
|
self._cf = Crazyflie(ro_cache=None,
|
|
324
324
|
rw_cache=cfclient.config_path + "/cache")
|
|
325
325
|
|
|
@@ -328,11 +328,16 @@ class ZMQServer():
|
|
|
328
328
|
self._base_url = base_url
|
|
329
329
|
self._context = zmq.Context()
|
|
330
330
|
|
|
331
|
-
cmd_srv = self._bind_zmq_socket(zmq.REP, "cmd",
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
331
|
+
cmd_srv = self._bind_zmq_socket(zmq.REP, "cmd",
|
|
332
|
+
base_port + ZMQ_SRV_PORT)
|
|
333
|
+
log_srv = self._bind_zmq_socket(zmq.PUB, "log",
|
|
334
|
+
base_port + ZMQ_LOG_PORT)
|
|
335
|
+
param_srv = self._bind_zmq_socket(zmq.PUB, "param",
|
|
336
|
+
base_port + ZMQ_PARAM_PORT)
|
|
337
|
+
ctrl_srv = self._bind_zmq_socket(zmq.PULL, "ctrl",
|
|
338
|
+
base_port + ZMQ_CTRL_PORT)
|
|
339
|
+
conn_srv = self._bind_zmq_socket(zmq.PUB, "conn",
|
|
340
|
+
base_port + ZMQ_CONN_PORT)
|
|
336
341
|
|
|
337
342
|
self._scan_thread = _SrvThread(cmd_srv, log_srv, param_srv, conn_srv,
|
|
338
343
|
self._cf)
|
|
@@ -360,14 +365,17 @@ def main():
|
|
|
360
365
|
help="URL where ZMQ will accept connections")
|
|
361
366
|
parser.add_argument("-d", "--debug", action="store_true", dest="debug",
|
|
362
367
|
help="Enable debug output")
|
|
363
|
-
(
|
|
368
|
+
parser.add_argument("-p", "--port", action="store", dest="port", type=int,
|
|
369
|
+
default=2000,
|
|
370
|
+
help="Base port to used for ZMQ sockets")
|
|
371
|
+
(args, _) = parser.parse_known_args()
|
|
364
372
|
|
|
365
373
|
if args.debug:
|
|
366
374
|
logging.basicConfig(level=logging.DEBUG)
|
|
367
375
|
else:
|
|
368
376
|
logging.basicConfig(level=logging.INFO)
|
|
369
377
|
|
|
370
|
-
ZMQServer(args.url)
|
|
378
|
+
ZMQServer(args.url, args.port)
|
|
371
379
|
|
|
372
380
|
# CRTL-C to exit
|
|
373
381
|
|
cfclient/ui/dialogs/cf1config.py
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
#
|
|
4
|
-
# || ____ _ __
|
|
5
|
-
# +------+ / __ )(_) /_______________ _____ ___
|
|
6
|
-
# | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \
|
|
7
|
-
# +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/
|
|
8
|
-
# || || /_____/_/\__/\___/_/ \__,_/ /___/\___/
|
|
9
|
-
#
|
|
10
|
-
# Copyright (C) 2011-2013 Bitcraze AB
|
|
11
|
-
#
|
|
12
|
-
# Crazyflie Nano Quadcopter Client
|
|
13
|
-
#
|
|
14
|
-
# This program is free software; you can redistribute it and/or
|
|
15
|
-
# modify it under the terms of the GNU General Public License
|
|
16
|
-
# as published by the Free Software Foundation; either version 2
|
|
17
|
-
# of the License, or (at your option) any later version.
|
|
18
|
-
#
|
|
19
|
-
# This program is distributed in the hope that it will be useful,
|
|
20
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
21
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
22
|
-
# GNU General Public License for more details.
|
|
23
|
-
|
|
24
|
-
# You should have received a copy of the GNU General Public License along with
|
|
25
|
-
# this program; if not, write to the Free Software Foundation, Inc.,
|
|
26
|
-
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
27
|
-
|
|
28
|
-
"""
|
|
29
|
-
The bootloader dialog is used to update the Crazyflie firmware and to
|
|
30
|
-
read/write the configuration block in the Crazyflie flash.
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
import struct
|
|
34
|
-
from cflib.bootloader import Bootloader
|
|
35
|
-
|
|
36
|
-
import logging
|
|
37
|
-
|
|
38
|
-
from PyQt5 import QtWidgets, uic
|
|
39
|
-
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QThread
|
|
40
|
-
|
|
41
|
-
import cfclient
|
|
42
|
-
from cfclient.utils.config import Config
|
|
43
|
-
from functools import reduce
|
|
44
|
-
|
|
45
|
-
__author__ = 'Bitcraze AB'
|
|
46
|
-
__all__ = ['Cf1ConfigDialog']
|
|
47
|
-
|
|
48
|
-
logger = logging.getLogger(__name__)
|
|
49
|
-
|
|
50
|
-
service_dialog_class = uic.loadUiType(cfclient.module_path +
|
|
51
|
-
"/ui/dialogs/cf1config.ui")[0]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class UIState:
|
|
55
|
-
DISCONNECTED = 0
|
|
56
|
-
CONNECTING = 5
|
|
57
|
-
CONNECT_FAILED = 1
|
|
58
|
-
COLD_CONNECT = 2
|
|
59
|
-
FLASHING = 3
|
|
60
|
-
RESET = 4
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
class Cf1ConfigDialog(QtWidgets.QWidget, service_dialog_class):
|
|
64
|
-
"""Tab for update the Crazyflie firmware and for reading/writing the config
|
|
65
|
-
block in flash"""
|
|
66
|
-
|
|
67
|
-
def __init__(self, helper, *args):
|
|
68
|
-
super(Cf1ConfigDialog, self).__init__(*args)
|
|
69
|
-
self.setupUi(self)
|
|
70
|
-
|
|
71
|
-
self.tabName = "CF1 Config"
|
|
72
|
-
self.menuName = "CF1 Config"
|
|
73
|
-
|
|
74
|
-
# self.tabWidget = tabWidget
|
|
75
|
-
self.helper = helper
|
|
76
|
-
|
|
77
|
-
# self.cf = crazyflie
|
|
78
|
-
self.clt = CrazyloadThread()
|
|
79
|
-
|
|
80
|
-
# Connecting GUI signals (a pity to do that manually...)
|
|
81
|
-
self.coldBootButton.clicked.connect(self.initiateColdboot)
|
|
82
|
-
self.resetButton.clicked.connect(self.resetCopter)
|
|
83
|
-
self.saveConfigblock.clicked.connect(self.writeConfig)
|
|
84
|
-
self._cancel_bootloading.clicked.connect(self.close)
|
|
85
|
-
|
|
86
|
-
self.clt.statusChanged.connect(self.statusUpdate)
|
|
87
|
-
self.clt.connectingSignal.connect(
|
|
88
|
-
lambda: self.setUiState(UIState.CONNECTING))
|
|
89
|
-
self.clt.connectedSignal.connect(
|
|
90
|
-
lambda: self.setUiState(UIState.COLD_CONNECT))
|
|
91
|
-
self.clt.failed_signal.connect(lambda m: self._ui_connection_fail(m))
|
|
92
|
-
self.clt.disconnectedSignal.connect(
|
|
93
|
-
lambda: self.setUiState(UIState.DISCONNECTED))
|
|
94
|
-
self.clt.updateConfigSignal.connect(self.updateConfig)
|
|
95
|
-
|
|
96
|
-
self.clt.start()
|
|
97
|
-
|
|
98
|
-
def _ui_connection_fail(self, message):
|
|
99
|
-
self.setStatusLabel(message)
|
|
100
|
-
self.coldBootButton.setEnabled(True)
|
|
101
|
-
|
|
102
|
-
def setUiState(self, state):
|
|
103
|
-
if (state == UIState.DISCONNECTED):
|
|
104
|
-
self.resetButton.setEnabled(False)
|
|
105
|
-
self.setStatusLabel("Not connected")
|
|
106
|
-
self.coldBootButton.setEnabled(True)
|
|
107
|
-
self.progressBar.setTextVisible(False)
|
|
108
|
-
self.progressBar.setValue(0)
|
|
109
|
-
self.statusLabel.setText('Status: <b>IDLE</b>')
|
|
110
|
-
self.saveConfigblock.setEnabled(False)
|
|
111
|
-
elif (state == UIState.CONNECTING):
|
|
112
|
-
self.resetButton.setEnabled(False)
|
|
113
|
-
self.setStatusLabel("Trying to connect cold bootloader, restart "
|
|
114
|
-
"the Crazyflie to connect")
|
|
115
|
-
self.coldBootButton.setEnabled(False)
|
|
116
|
-
elif (state == UIState.CONNECT_FAILED):
|
|
117
|
-
self.setStatusLabel("Connecting to bootloader failed")
|
|
118
|
-
self.coldBootButton.setEnabled(True)
|
|
119
|
-
elif (state == UIState.COLD_CONNECT):
|
|
120
|
-
self.resetButton.setEnabled(True)
|
|
121
|
-
self.saveConfigblock.setEnabled(True)
|
|
122
|
-
self.setStatusLabel("Connected to bootloader")
|
|
123
|
-
self.coldBootButton.setEnabled(False)
|
|
124
|
-
elif (state == UIState.RESET):
|
|
125
|
-
self.setStatusLabel("Resetting to firmware, disconnected")
|
|
126
|
-
self.resetButton.setEnabled(False)
|
|
127
|
-
self.coldBootButton.setEnabled(False)
|
|
128
|
-
self.rollTrim.setValue(0)
|
|
129
|
-
self.pitchTrim.setValue(0)
|
|
130
|
-
self.radioChannel.setValue(0)
|
|
131
|
-
self.radioSpeed.setCurrentIndex(0)
|
|
132
|
-
|
|
133
|
-
def setStatusLabel(self, text):
|
|
134
|
-
self.connectionStatus.setText("Status: <b>%s</b>" % text)
|
|
135
|
-
|
|
136
|
-
def connected(self):
|
|
137
|
-
self.setUiState(UIState.COLD_CONNECT)
|
|
138
|
-
|
|
139
|
-
def connectionFailed(self):
|
|
140
|
-
self.setUiState(UIState.CONNECT_FAILED)
|
|
141
|
-
|
|
142
|
-
def resetCopter(self):
|
|
143
|
-
self.clt.resetCopterSignal.emit()
|
|
144
|
-
self.setUiState(UIState.RESET)
|
|
145
|
-
|
|
146
|
-
def updateConfig(self, channel, speed, rollTrim, pitchTrim):
|
|
147
|
-
self.rollTrim.setValue(rollTrim)
|
|
148
|
-
self.pitchTrim.setValue(pitchTrim)
|
|
149
|
-
self.radioChannel.setValue(channel)
|
|
150
|
-
self.radioSpeed.setCurrentIndex(speed)
|
|
151
|
-
|
|
152
|
-
def closeEvent(self, event):
|
|
153
|
-
self.setUiState(UIState.RESET)
|
|
154
|
-
self.clt.resetCopterSignal.emit()
|
|
155
|
-
|
|
156
|
-
@pyqtSlot(str, int)
|
|
157
|
-
def statusUpdate(self, status, progress):
|
|
158
|
-
logger.debug("Status: [%s] | %d", status, progress)
|
|
159
|
-
self.statusLabel.setText('Status: <b>' + status + '</b>')
|
|
160
|
-
if progress >= 0 and progress < 100:
|
|
161
|
-
self.progressBar.setTextVisible(True)
|
|
162
|
-
self.progressBar.setValue(progress)
|
|
163
|
-
else:
|
|
164
|
-
self.progressBar.setTextVisible(False)
|
|
165
|
-
self.progressBar.setValue(100)
|
|
166
|
-
if progress >= 100:
|
|
167
|
-
self.resetButton.setEnabled(True)
|
|
168
|
-
|
|
169
|
-
def initiateColdboot(self):
|
|
170
|
-
self.clt.initiateColdBootSignal.emit("radio://0/100")
|
|
171
|
-
|
|
172
|
-
def writeConfig(self):
|
|
173
|
-
pitchTrim = self.pitchTrim.value()
|
|
174
|
-
rollTrim = self.rollTrim.value()
|
|
175
|
-
channel = self.radioChannel.value()
|
|
176
|
-
speed = self.radioSpeed.currentIndex()
|
|
177
|
-
|
|
178
|
-
self.clt.writeConfigSignal.emit(channel, speed, rollTrim, pitchTrim)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
# No run method specified here as the default run implementation is running the
|
|
182
|
-
# event loop which is what we want
|
|
183
|
-
class CrazyloadThread(QThread):
|
|
184
|
-
# Input signals declaration (not sure it should be used like that...)
|
|
185
|
-
initiateColdBootSignal = pyqtSignal(str)
|
|
186
|
-
resetCopterSignal = pyqtSignal()
|
|
187
|
-
writeConfigSignal = pyqtSignal(int, int, float, float)
|
|
188
|
-
# Output signals declaration
|
|
189
|
-
statusChanged = pyqtSignal(str, int)
|
|
190
|
-
connectedSignal = pyqtSignal()
|
|
191
|
-
connectingSignal = pyqtSignal()
|
|
192
|
-
failed_signal = pyqtSignal(str)
|
|
193
|
-
disconnectedSignal = pyqtSignal()
|
|
194
|
-
updateConfigSignal = pyqtSignal(int, int, float, float)
|
|
195
|
-
|
|
196
|
-
def __init__(self):
|
|
197
|
-
super(CrazyloadThread, self).__init__()
|
|
198
|
-
|
|
199
|
-
# Make sure that the signals are handled by this thread event loop
|
|
200
|
-
self.moveToThread(self)
|
|
201
|
-
self._bl = Bootloader()
|
|
202
|
-
self._bl.progress_cb = self.statusChanged.emit
|
|
203
|
-
|
|
204
|
-
self.writeConfigSignal.connect(self.writeConfigAction)
|
|
205
|
-
self.initiateColdBootSignal.connect(self.initiateColdBoot)
|
|
206
|
-
self.resetCopterSignal.connect(self.resetCopter)
|
|
207
|
-
|
|
208
|
-
def __del__(self):
|
|
209
|
-
self.quit()
|
|
210
|
-
self.wait()
|
|
211
|
-
|
|
212
|
-
def initiateColdBoot(self, linkURI):
|
|
213
|
-
self.connectingSignal.emit()
|
|
214
|
-
|
|
215
|
-
try:
|
|
216
|
-
success = self._bl.start_bootloader(warm_boot=False)
|
|
217
|
-
if not success:
|
|
218
|
-
self.failed_signal.emit("Could not connect to bootloader")
|
|
219
|
-
else:
|
|
220
|
-
self.connectedSignal.emit()
|
|
221
|
-
self.readConfigAction()
|
|
222
|
-
except Exception as e:
|
|
223
|
-
self.failed_signal.emit("{}".format(e))
|
|
224
|
-
|
|
225
|
-
def checksum256(self, st):
|
|
226
|
-
return reduce(lambda x, y: x + y, list(st)) % 256
|
|
227
|
-
|
|
228
|
-
def writeConfigAction(self, channel, speed, rollTrim, pitchTrim):
|
|
229
|
-
data = (0x00, channel, speed, pitchTrim, rollTrim)
|
|
230
|
-
image = struct.pack("<BBBff", *data)
|
|
231
|
-
# Adding some magic:
|
|
232
|
-
image = bytearray("0xBC".encode('ISO-8859-1')) + image
|
|
233
|
-
image += struct.pack("B", 256 - self.checksum256(image))
|
|
234
|
-
|
|
235
|
-
self._bl.write_cf1_config(image)
|
|
236
|
-
|
|
237
|
-
def readConfigAction(self):
|
|
238
|
-
self.statusChanged.emit("Reading config block...", 0)
|
|
239
|
-
data = self._bl.read_cf1_config()
|
|
240
|
-
if (data is not None):
|
|
241
|
-
if data[0:4] == bytearray("0xBC".encode('ISO-8859-1')):
|
|
242
|
-
# Skip 0xBC and version at the beginning
|
|
243
|
-
[channel,
|
|
244
|
-
speed,
|
|
245
|
-
pitchTrim,
|
|
246
|
-
rollTrim] = struct.unpack("<BBff", data[5:15])
|
|
247
|
-
self.statusChanged.emit("Reading config block...done!", 100)
|
|
248
|
-
else:
|
|
249
|
-
channel = Config().get("default_cf_channel")
|
|
250
|
-
speed = Config().get("default_cf_speed")
|
|
251
|
-
pitchTrim = Config().get("default_cf_trim")
|
|
252
|
-
rollTrim = Config().get("default_cf_trim")
|
|
253
|
-
self.statusChanged.emit(
|
|
254
|
-
"Could not find config block, showing defaults", 100)
|
|
255
|
-
self.updateConfigSignal.emit(channel, speed, rollTrim, pitchTrim)
|
|
256
|
-
else:
|
|
257
|
-
self.statusChanged.emit("Reading config block failed!", 0)
|
|
258
|
-
|
|
259
|
-
def resetCopter(self):
|
|
260
|
-
try:
|
|
261
|
-
self._bl.reset_to_firmware()
|
|
262
|
-
except Exception:
|
|
263
|
-
pass
|
|
264
|
-
self._bl.close()
|
|
265
|
-
self.disconnectedSignal.emit()
|