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.
Files changed (140) hide show
  1. cfclient/__init__.py +16 -11
  2. cfclient/configs/config.json +4 -3
  3. cfclient/configs/input/Generic_OS_X.json +1 -0
  4. cfclient/configs/input/Joystick.json +1 -0
  5. cfclient/configs/input/PS3_Mode_1.json +1 -0
  6. cfclient/configs/input/PS3_Mode_2.json +1 -0
  7. cfclient/configs/input/PS3_Mode_3.json +1 -0
  8. cfclient/configs/input/PS4_Mode_1.json +1 -0
  9. cfclient/configs/input/PS4_Mode_2.json +1 -0
  10. cfclient/configs/input/PS4_shoulder_btns_yaw.json +1 -0
  11. cfclient/configs/input/xbox360_mode1.json +1 -0
  12. cfclient/configs/log/PID_tuning/Attitude.json +46 -0
  13. cfclient/configs/log/PID_tuning/Attitude_rate.json +46 -0
  14. cfclient/configs/log/PID_tuning/Position.json +46 -0
  15. cfclient/configs/log/PID_tuning/Velocity.json +46 -0
  16. cfclient/configs/log/PID_tuning_components/Pitch.json +22 -0
  17. cfclient/configs/log/PID_tuning_components/Pitch_rate.json +22 -0
  18. cfclient/configs/log/PID_tuning_components/Position_x.json +22 -0
  19. cfclient/configs/log/PID_tuning_components/Position_y.json +22 -0
  20. cfclient/configs/log/PID_tuning_components/Position_z.json +22 -0
  21. cfclient/configs/log/PID_tuning_components/Roll.json +22 -0
  22. cfclient/configs/log/PID_tuning_components/Roll_rate.json +22 -0
  23. cfclient/configs/log/PID_tuning_components/Velocity_x.json +22 -0
  24. cfclient/configs/log/PID_tuning_components/Velocity_y.json +22 -0
  25. cfclient/configs/log/PID_tuning_components/Velocity_z.json +22 -0
  26. cfclient/configs/log/PID_tuning_components/Yaw.json +22 -0
  27. cfclient/configs/log/PID_tuning_components/Yaw_rate.json +22 -0
  28. cfclient/gui.py +44 -9
  29. cfclient/headless.py +3 -12
  30. cfclient/resources/log_param_doc.json +1 -0
  31. cfclient/ui/connectivity_manager.py +198 -0
  32. cfclient/ui/dialogs/about.py +53 -36
  33. cfclient/ui/dialogs/about.ui +23 -3
  34. cfclient/ui/dialogs/anchor_position_dialog.py +252 -0
  35. cfclient/ui/dialogs/anchor_position_dialog.ui +138 -0
  36. cfclient/ui/dialogs/basestation_mode_dialog.py +185 -0
  37. cfclient/ui/dialogs/basestation_mode_dialog.ui +186 -0
  38. cfclient/ui/dialogs/bootloader.py +448 -85
  39. cfclient/ui/dialogs/bootloader.ui +387 -134
  40. cfclient/ui/dialogs/cf2config.py +4 -4
  41. cfclient/ui/dialogs/cf2config.ui +3 -4
  42. cfclient/ui/dialogs/inputconfigdialogue.py +24 -19
  43. cfclient/ui/dialogs/inputconfigdialogue.ui +53 -30
  44. cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.py +220 -0
  45. cfclient/ui/dialogs/lighthouse_bs_geometry_dialog.ui +110 -0
  46. cfclient/ui/dialogs/lighthouse_system_type_dialog.py +93 -0
  47. cfclient/ui/dialogs/lighthouse_system_type_dialog.ui +121 -0
  48. cfclient/ui/dialogs/logconfigdialogue.py +401 -101
  49. cfclient/ui/dialogs/logconfigdialogue.ui +117 -72
  50. cfclient/ui/icons/bl.webp +0 -0
  51. cfclient/ui/icons/bolt.webp +0 -0
  52. cfclient/ui/icons/cf21.webp +0 -0
  53. cfclient/ui/icons/checkmark_black.png +0 -0
  54. cfclient/ui/icons/checkmark_white.png +0 -0
  55. cfclient/ui/icons/create.png +0 -0
  56. cfclient/ui/icons/delete.png +0 -0
  57. cfclient/ui/icons/flapper.webp +0 -0
  58. cfclient/ui/icons/tag.webp +0 -0
  59. cfclient/ui/main.py +328 -258
  60. cfclient/ui/main.ui +184 -80
  61. cfclient/ui/pluginhelper.py +7 -1
  62. cfclient/ui/pose_logger.py +116 -0
  63. cfclient/ui/tab_toolbox.py +208 -0
  64. cfclient/ui/tabs/ColorLEDTab.py +752 -0
  65. cfclient/ui/tabs/ConsoleTab.py +48 -13
  66. cfclient/ui/{toolboxes → tabs}/CrtpSharkToolbox.py +19 -34
  67. cfclient/ui/tabs/ExampleTab.py +9 -16
  68. cfclient/ui/tabs/FlightTab.py +437 -325
  69. cfclient/ui/tabs/GpsTab.py +14 -20
  70. cfclient/ui/tabs/LEDRingTab.py +277 -0
  71. cfclient/ui/tabs/LogBlockDebugTab.py +20 -27
  72. cfclient/ui/tabs/LogBlockTab.py +35 -35
  73. cfclient/ui/tabs/LogClientTab.py +85 -0
  74. cfclient/ui/tabs/LogTab.py +50 -27
  75. cfclient/ui/tabs/ParamTab.py +443 -57
  76. cfclient/ui/tabs/PlotTab.py +23 -25
  77. cfclient/ui/tabs/TuningTab.py +292 -0
  78. cfclient/ui/tabs/__init__.py +12 -2
  79. cfclient/ui/tabs/colorLEDTab.ui +624 -0
  80. cfclient/ui/tabs/consoleTab.ui +46 -0
  81. cfclient/ui/tabs/flightActionContainer.ui +103 -0
  82. cfclient/ui/tabs/flightTab.ui +724 -237
  83. cfclient/ui/tabs/{ledTab.ui → ledRingTab.ui} +63 -46
  84. cfclient/ui/tabs/lighthouse_tab.py +714 -0
  85. cfclient/ui/tabs/lighthouse_tab.ui +430 -0
  86. cfclient/ui/tabs/locopositioning_tab.py +606 -389
  87. cfclient/ui/tabs/locopositioning_tab.ui +370 -253
  88. cfclient/ui/tabs/logClientTab.ui +52 -0
  89. cfclient/ui/tabs/logTab.ui +1 -1
  90. cfclient/ui/tabs/paramTab.ui +204 -3
  91. cfclient/ui/tabs/tuningTab.ui +773 -0
  92. cfclient/ui/widgets/ai.py +37 -39
  93. cfclient/ui/widgets/hexspinbox.py +16 -10
  94. cfclient/ui/widgets/plotter.ui +39 -47
  95. cfclient/ui/widgets/plotwidget.py +57 -22
  96. cfclient/ui/widgets/super_slider.py +112 -0
  97. cfclient/ui/wizards/__init__.py +0 -0
  98. cfclient/ui/wizards/bslh_1.png +0 -0
  99. cfclient/ui/wizards/bslh_2.png +0 -0
  100. cfclient/ui/wizards/bslh_3.png +0 -0
  101. cfclient/ui/wizards/bslh_4.png +0 -0
  102. cfclient/ui/wizards/bslh_5.png +0 -0
  103. cfclient/ui/wizards/lighthouse_geo_bs_estimation_wizard.py +465 -0
  104. cfclient/utils/config_manager.py +5 -4
  105. cfclient/utils/input/__init__.py +77 -19
  106. cfclient/utils/input/inputinterfaces/wiimote.py +2 -2
  107. cfclient/utils/input/inputreaderinterface.py +17 -7
  108. cfclient/utils/input/inputreaders/__init__.py +17 -0
  109. cfclient/utils/logconfigreader.py +245 -25
  110. cfclient/utils/logdatawriter.py +3 -1
  111. cfclient/utils/periodictimer.py +1 -1
  112. cfclient/utils/ui.py +336 -0
  113. cfclient/utils/zmq_led_driver.py +5 -0
  114. cfclient/utils/zmq_param.py +6 -0
  115. cfclient/version.py +34 -1
  116. cfclient-2025.12.1.dist-info/METADATA +70 -0
  117. cfclient-2025.12.1.dist-info/RECORD +152 -0
  118. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/WHEEL +1 -1
  119. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/entry_points.txt +0 -1
  120. cfclient-2025.12.1.dist-info/licenses/LICENSE.txt +350 -0
  121. {cfclient-2017.4.dist-info → cfclient-2025.12.1.dist-info}/top_level.txt +1 -0
  122. cfconfig/Makefile +51 -0
  123. cfconfig/configblock.py +111 -0
  124. cfloader/__init__.py +41 -55
  125. cfzmq/__init__.py +22 -14
  126. cfclient/ui/dialogs/cf1config.py +0 -265
  127. cfclient/ui/dialogs/cf1config.ui +0 -260
  128. cfclient/ui/tab.py +0 -96
  129. cfclient/ui/tabs/LEDTab.py +0 -169
  130. cfclient/ui/toolboxes/ConsoleToolbox.py +0 -69
  131. cfclient/ui/toolboxes/DebugDriverToolbox.py +0 -107
  132. cfclient/ui/toolboxes/__init__.py +0 -45
  133. cfclient/ui/toolboxes/consoleToolbox.ui +0 -62
  134. cfclient/ui/toolboxes/debugDriverToolbox.ui +0 -86
  135. cfclient-2017.4.dist-info/DESCRIPTION.rst +0 -3
  136. cfclient-2017.4.dist-info/METADATA +0 -22
  137. cfclient-2017.4.dist-info/RECORD +0 -104
  138. cfclient-2017.4.dist-info/metadata.json +0 -1
  139. /cfclient/{icon-256.png → ui/icons/icon-256.png} +0 -0
  140. /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, TargetTypes
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
- targetnames = {}
110
+ targets = [] # Dict[Target]
114
111
  for t in sys.argv[2:]:
115
- [target, type] = t.split("-")
116
- if target in targetnames:
117
- targetnames[target] += (type,)
112
+ if t.startswith("deck-"):
113
+ [deck, target, type] = t.split("-")
114
+ targets.append(Target("deck", target, type, [], []))
118
115
  else:
119
- targetnames[target] = (type,)
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
- # Get the connection with the bootloader
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
- None # Already done ...
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 = 2000
56
+ ZMQ_SRV_PORT = 0
57
57
  # Log data socket (publish)
58
- ZMQ_LOG_PORT = 2001
58
+ ZMQ_LOG_PORT = 1
59
59
  # Param value updated (publish)
60
- ZMQ_PARAM_PORT = 2002
60
+ ZMQ_PARAM_PORT = 2
61
61
  # Async event for connection, like connection lost (publish)
62
- ZMQ_CONN_PORT = 2003
62
+ ZMQ_CONN_PORT = 3
63
63
  # Control set-poins for Crazyflie (pull)
64
- ZMQ_CTRL_PORT = 2004
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(enable_debug_driver=True)
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", ZMQ_SRV_PORT)
332
- log_srv = self._bind_zmq_socket(zmq.PUB, "log", ZMQ_LOG_PORT)
333
- param_srv = self._bind_zmq_socket(zmq.PUB, "param", ZMQ_PARAM_PORT)
334
- ctrl_srv = self._bind_zmq_socket(zmq.PULL, "ctrl", ZMQ_CTRL_PORT)
335
- conn_srv = self._bind_zmq_socket(zmq.PUB, "conn", ZMQ_CONN_PORT)
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
- (args, unused) = parser.parse_known_args()
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
 
@@ -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()