Skip to content

Commit

Permalink
Merge pull request bitcraze#479 from bitcraze/dev_deck_flashing
Browse files Browse the repository at this point in the history
Add support for flashing the lighthouse deck FW from the bootloader dialog
  • Loading branch information
ataffanel authored Mar 11, 2021
2 parents f98dcfa + a41ff23 commit 93158ef
Show file tree
Hide file tree
Showing 9 changed files with 133 additions and 187 deletions.
21 changes: 8 additions & 13 deletions src/cfclient/ui/dialogs/anchor_position_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,12 @@ def _get_background(self, row, col):

class AnchorPositionDialog(QtWidgets.QWidget, anchor_postiong_widget_class):

def __init__(self, lps_tab, *args):
super(AnchorPositionDialog, self).__init__(*args)
def __init__(self, lps_tab, helper):
super(AnchorPositionDialog, self).__init__()
self.setupUi(self)

self._current_folder = os.path.expanduser('~')

self._lps_tab = lps_tab
self._helper = helper

self._headers = ['', 'id', 'x', 'y', 'z']
self._data_model = AnchorPositionConfigTableModel(self._headers, self)
Expand Down Expand Up @@ -213,18 +212,16 @@ def anchor_postions_updated(self, anchor_positions):
self._data_model.anchor_postions_updated(anchor_positions)

def _load_button_clicked(self):
names = QFileDialog.getOpenFileName(self, 'Open file',
self._current_folder,
"*.yaml;;*.*")
names = QFileDialog.getOpenFileName(self, 'Open file', self._helper.current_folder, "*.yaml;*.*")

if names[0] == '':
return

self._current_folder = os.path.dirname(names[0])
self._helper.current_folder = os.path.dirname(names[0])

f = open(names[0], 'r')
with f:
data = yaml.load(f)
data = yaml.safe_load(f)

anchor_positions = {}
for id, pos in data.items():
Expand All @@ -237,14 +234,12 @@ def _save_button_clicked(self):
for id, pos in anchor_positions.items():
data[id] = {'x': pos[0], 'y': pos[1], 'z': pos[2]}

names = QFileDialog.getSaveFileName(self, 'Save file',
self._current_folder,
"*.yaml;;*.*")
names = QFileDialog.getSaveFileName(self, 'Save file', self._helper.current_folder, "*.yaml;*.*")

if names[0] == '':
return

self._current_folder = os.path.dirname(names[0])
self._helper.current_folder = os.path.dirname(names[0])

if not names[0].endswith(".yaml") and names[0].find(".") < 0:
filename = names[0] + ".yaml"
Expand Down
142 changes: 69 additions & 73 deletions src/cfclient/ui/dialogs/bootloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,50 +85,43 @@ def __init__(self, helper, *args):
self.menuName = "Service"

# self.tabWidget = tabWidget
self.helper = helper
self._helper = helper

# self.cf = crazyflie
self.clt = CrazyloadThread(self.helper.cf)
self.clt = CrazyloadThread(self._helper.cf)

# Connecting GUI signals (a pity to do that manually...)
self.imagePathBrowseButton.clicked.connect(self.pathBrowse)
self.programButton.clicked.connect(self.programAction)
self.coldBootButton.clicked.connect(self.initiateColdboot)
self.resetButton.clicked.connect(self.resetCopter)
self._cancel_bootloading.clicked.connect(self.close)
self.sourceTab.currentChanged.connect(
lambda _: self.updateChipSelectRadio())

self.helper.connectivity_manager.register_ui_elements(
self._helper.connectivity_manager.register_ui_elements(
ConnectivityManager.UiElementsContainer(
interface_combo=self.comboBox,
address_spinner=self.address,
connect_button=self.connectButton,
scan_button=self.scanButton))
self.helper.connectivity_manager.connection_state_changed.connect(self._fw_connection_state_changed)
self._helper.connectivity_manager.connection_state_changed.connect(self._fw_connection_state_changed)

# connecting other signals
self.clt.programmed.connect(self.programDone)
self.clt.statusChanged.connect(self.statusUpdate)
# self.clt.updateBootloaderStatusSignal.connect(
# self.updateBootloaderStatus)
self.clt.connectingSignal.connect(
lambda: self.setUiState(self.UIState.COLD_CONNECTING))
self.clt.connectedSignal.connect(
lambda: self.setUiState(self.UIState.COLD_CONNECTED))
self.clt.connectingSignal.connect(lambda: self.setUiState(self.UIState.COLD_CONNECTING))

self.clt.connectedSignal.connect(lambda: self._load_thread_connection_event(self.UIState.COLD_CONNECTED))
self.clt.disconnectedSignal.connect(lambda: self._load_thread_connection_event(self.UIState.DISCONNECTED))
self.clt.failed_signal.connect(lambda m: self._ui_connection_fail(m))
self.clt.disconnectedSignal.connect(
lambda: self.setUiState(self.UIState.DISCONNECTED))

self._cold_boot_error_message = None
self._state = self.UIState.DISCONNECTED

self._releases = {}
self._release_firmwares_found.connect(self._populate_firmware_dropdown)
self._release_downloaded.connect(self.release_zip_downloaded)
self.firmware_downloader = FirmwareDownloader(
self._release_firmwares_found,
self._release_downloaded)
self.firmware_downloader = FirmwareDownloader(self._release_firmwares_found, self._release_downloaded)
self.firmware_downloader.get_firmware_releases()

self.firmware_downloader.start()
Expand All @@ -152,49 +145,71 @@ def setUiState(self, state):
self.progressBar.setTextVisible(False)
self.progressBar.setValue(0)
self.statusLabel.setText('Status: <b>IDLE</b>')
self.helper.connectivity_manager.set_enable(True)
self.setSourceSelectionUiEnabled(True)
self._helper.connectivity_manager.set_enable(True)
elif (state == self.UIState.COLD_CONNECTING):
self._cold_boot_error_message = None
self.resetButton.setEnabled(False)
self.programButton.setEnabled(False)
self.setStatusLabel("Trying to connect cold bootloader, restart the Crazyflie to connect")
self.coldBootButton.setEnabled(False)
self.helper.connectivity_manager.set_enable(False)
self.setSourceSelectionUiEnabled(True)
self._helper.connectivity_manager.set_enable(False)
elif (state == self.UIState.COLD_CONNECTED):
self._cold_boot_error_message = None
self.resetButton.setEnabled(True)
self.programButton.setEnabled(True)
self.setStatusLabel("Connected to bootloader")
self.coldBootButton.setEnabled(False)
self.helper.connectivity_manager.set_enable(False)
self.imagePathBrowseButton.setEnabled(True)
self.imagePathLine.setEnabled(True)
self.firmwareDropdown.setEnabled(True)
self._helper.connectivity_manager.set_enable(False)
elif (state == self.UIState.FW_CONNECTING):
self._cold_boot_error_message = None
self.resetButton.setEnabled(False)
self.programButton.setEnabled(False)
self.setStatusLabel("Trying to connect in firmware mode")
self.coldBootButton.setEnabled(False)
self.helper.connectivity_manager.set_enable(True)
self.setSourceSelectionUiEnabled(True)
self._helper.connectivity_manager.set_enable(True)
elif (state == self.UIState.FW_CONNECTED):
self._cold_boot_error_message = None
self.resetButton.setEnabled(False)
self.programButton.setEnabled(True)
self.setStatusLabel("Connected in firmware mode")
self.coldBootButton.setEnabled(False)
self.helper.connectivity_manager.set_enable(True)
self.setSourceSelectionUiEnabled(True)
self._helper.connectivity_manager.set_enable(True)
elif (state == self.UIState.FW_SCANNING):
self._cold_boot_error_message = None
self.resetButton.setEnabled(False)
self.programButton.setEnabled(False)
self.setStatusLabel("Scanning")
self.coldBootButton.setEnabled(False)
self.helper.connectivity_manager.set_enable(True)
self.setSourceSelectionUiEnabled(True)
self._helper.connectivity_manager.set_enable(True)
elif (state == self.UIState.FLASHING):
self.resetButton.setEnabled(False)
self.resetButton.setEnabled(False)
self.programButton.setEnabled(False)
self.setStatusLabel("Flashing")
self.coldBootButton.setEnabled(False)
self.setSourceSelectionUiEnabled(False)
self._helper.connectivity_manager.set_enable(False)
elif (state == self.UIState.RESET):
self._cold_boot_error_message = None
self.setStatusLabel("Resetting to firmware, disconnected")
self.resetButton.setEnabled(False)
self.programButton.setEnabled(False)
self.coldBootButton.setEnabled(False)
self.helper.connectivity_manager.set_enable(False)
self.setSourceSelectionUiEnabled(True)
self._helper.connectivity_manager.set_enable(False)

def setSourceSelectionUiEnabled(self, enabled):
self.imagePathBrowseButton.setEnabled(enabled)
self.imagePathLine.setEnabled(enabled)
self.firmwareDropdown.setEnabled(enabled)

def setStatusLabel(self, text):
self.connectionStatus.setText("Status: <b>%s</b>" % text)
Expand All @@ -213,8 +228,6 @@ def closeEvent(self, event):
self.clt.terminate_flashing()
# Remove downloaded-firmware files.
self.firmware_downloader.bootload_complete.emit()
self.setUiState(self.UIState.RESET)
self.clt.resetCopterSignal.emit()

def _populate_firmware_dropdown(self, releases):
""" Callback from firmware-downloader that retrieves all
Expand All @@ -235,50 +248,40 @@ def release_zip_downloaded(self, release_name, release_path):
self.downloadStatus.setText('Downloaded')
self.clt.program.emit(release_path, '')

def updateChipSelectRadio(self):
if self.sourceTab.currentWidget() == self.tabFromFile:
if self.imagePathLine.text().endswith(".zip"):
self.radioBoth.setEnabled(True)
self.radioBoth.setChecked(True)
elif self.imagePathLine.text().endswith(".bin"):
self.radioBoth.setEnabled(False)
self.radioStm32.setChecked(True)
else:
self.radioBoth.setEnabled(True)
self.radioBoth.setChecked(True)
def _load_thread_connection_event(self, new_sate):
if self._state != self.UIState.FLASHING:
self.setUiState(new_sate)

def _fw_connection_state_changed(self, new_state):
if new_state == ConnectivityManager.UIState.DISCONNECTED:
self.setUiState(self.UIState.DISCONNECTED)
elif new_state == ConnectivityManager.UIState.CONNECTING:
self.setUiState(self.UIState.FW_CONNECTING)
elif new_state == ConnectivityManager.UIState.CONNECTED:
self.setUiState(self.UIState.FW_CONNECTED)
elif new_state == ConnectivityManager.UIState.SCANNING:
self.setUiState(self.UIState.FW_SCANNING)
if self._state != self.UIState.FLASHING:
if new_state == ConnectivityManager.UIState.DISCONNECTED:
self.setUiState(self.UIState.DISCONNECTED)
elif new_state == ConnectivityManager.UIState.CONNECTING:
self.setUiState(self.UIState.FW_CONNECTING)
elif new_state == ConnectivityManager.UIState.CONNECTED:
self.setUiState(self.UIState.FW_CONNECTED)
elif new_state == ConnectivityManager.UIState.SCANNING:
self.setUiState(self.UIState.FW_SCANNING)

@pyqtSlot()
def pathBrowse(self):
filename = ""
# Fix for crash in X on Ubuntu 14.04
filename, _ = QtWidgets.QFileDialog.getOpenFileName()
if filename != "" and filename[-4:] in (".bin", ".zip"):
names = QtWidgets.QFileDialog.getOpenFileName(
self, 'Release file to flash', self._helper.current_folder, "*.zip")
if names[0] == '':
return

filename = names[0]
self._helper.current_folder = os.path.dirname(filename)

if filename.endswith('.zip'):
self.imagePathLine.setText(filename)
self.updateChipSelectRadio()
elif filename != "":
else:
msgBox = QtWidgets.QMessageBox()
msgBox.setText("Wrong file extention. Must be .bin or .zip.")
msgBox.setText("Wrong file extention. Must be .zip.")
msgBox.exec_()

pass

@pyqtSlot()
def programAction(self):
# self.setStatusLabel("Initiate programming")
self.resetButton.setEnabled(False)
self.programButton.setEnabled(False)
self.imagePathBrowseButton.setEnabled(False)

if self._state == self.UIState.COLD_CONNECTED:
self.clt.set_boot_mode(self.clt.COLD_BOOT)
else:
Expand All @@ -291,38 +294,31 @@ def programAction(self):
msgBox.setText("Please choose an image file to program.")
msgBox.exec_()

self.resetButton.setEnabled(True)
self.programButton.setEnabled(True)
self.imagePathBrowseButton.setEnabled(True)
return

# by default, both of the mcu:s are flashed
mcu_to_flash = None
self.setUiState(self.UIState.FLASHING)

if self.radioStm32.isChecked():
mcu_to_flash = 'stm32'
elif self.radioNrf51.isChecked():
mcu_to_flash = 'nrf51'
# by default, flash everything in the zip (if possible)
mcu_to_flash = None
self.clt.program.emit(self.imagePathLine.text(), mcu_to_flash)
else:
self.setUiState(self.UIState.FLASHING)

requested_release = self.firmwareDropdown.currentText()
download_url = self._releases[requested_release]
self.downloadStatus.setText('Fetching...')
self.firmware_downloader.download_release(requested_release,
download_url)
self.firmware_downloader.download_release(requested_release, download_url)

@pyqtSlot(bool)
def programDone(self, success):
if success:
self.statusLabel.setText('Status: <b>Programing complete!</b>')
self.downloadStatus.setText('')

else:
self.statusLabel.setText('Status: <b>Programing failed!</b>')

self.resetButton.setEnabled(True)
self.programButton.setEnabled(True)
self.imagePathBrowseButton.setEnabled(True)
self.setUiState(self.UIState.DISCONNECTED)
self.resetCopter()

@pyqtSlot(str, int)
def statusUpdate(self, status, progress):
Expand Down
47 changes: 8 additions & 39 deletions src/cfclient/ui/dialogs/bootloader.ui
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<item>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>1</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
Expand Down Expand Up @@ -130,13 +130,6 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="_cancel_bootloading">
<property name="text">
<string>Cancel bootloading</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_5">
<property name="orientation">
Expand Down Expand Up @@ -294,37 +287,6 @@
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="chipSelectLabel">
<property name="text">
<string>Chip to flash:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioBoth">
<property name="text">
<string>Both</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioStm32">
<property name="text">
<string>stm32</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioNrf51">
<property name="text">
<string>nrf51</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
Expand All @@ -338,6 +300,13 @@
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>The Crazyflie may restart one or more times during the programming process </string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="programButton">
<property name="enabled">
Expand Down
Loading

0 comments on commit 93158ef

Please sign in to comment.