diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index f38f80d8..7efa5fbd 100755
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -102,7 +102,9 @@
"tabLogging": {
"message": "Logging"
},
-
+ "tabDataflash": {
+ "message": "Dataflash"
+ },
"tabAdjustments": {
"message": "Adjustments"
},
@@ -843,6 +845,13 @@
"message": "Automatically loaded previous log file: $1"
},
+ "dataflashButtonSaveFile": {
+ "message": "Save flash to file..."
+ },
+ "dataflashButtonErase": {
+ "message": "Erase flash"
+ },
+
"firmwareFlasherReleaseSummaryHead": {
"message": "Release info"
},
diff --git a/js/data_storage.js b/js/data_storage.js
index 7d3f4bcb..1c4e1fbb 100755
--- a/js/data_storage.js
+++ b/js/data_storage.js
@@ -149,3 +149,8 @@ var MISC = {
vbatmaxcellvoltage: 0,
vbatwarningcellvoltage: 0
};
+
+var DATAFLASH = {
+ sectors: 0,
+ totalSize: 0
+};
\ No newline at end of file
diff --git a/js/gui.js b/js/gui.js
index f6007a9b..89fab7d7 100644
--- a/js/gui.js
+++ b/js/gui.js
@@ -25,6 +25,7 @@ var GUI_control = function () {
'gps',
'led_strip',
'logging',
+ 'dataflash',
'modes',
'motors',
'pid_tuning',
diff --git a/js/msp.js b/js/msp.js
index 309dc55c..bb03ecd5 100644
--- a/js/msp.js
+++ b/js/msp.js
@@ -22,6 +22,9 @@ var MSP_codes = {
MSP_SONAR: 58,
MSP_PID_CONTROLLER: 59,
MSP_SET_PID_CONTROLLER: 60,
+ MSP_DATAFLASH_SUMMARY: 70,
+ MSP_DATAFLASH_READ: 71,
+ MSP_DATAFLASH_ERASE: 72,
// Multiwii MSP commands
MSP_IDENT: 100,
@@ -666,8 +669,16 @@ var MSP = {
case MSP_codes.MSP_SET_LED_STRIP_CONFIG:
console.log('Led strip config saved');
break;
-
-
+ case MSP_codes.MSP_DATAFLASH_SUMMARY:
+ DATAFLASH.sectors = data.getUint32(0, 1);
+ DATAFLASH.totalSize = data.getUint32(4, 1);
+ break;
+ case MSP_codes.MSP_DATAFLASH_READ:
+ // No-op, let callback handle it
+ break;
+ case MSP_codes.MSP_DATAFLASH_ERASE:
+ console.log("Data flash erased");
+ break;
case MSP_codes.MSP_SET_MODE_RANGE:
console.log('Mode range saved');
break;
@@ -793,6 +804,9 @@ var MSP = {
}
};
+/**
+ * Encode the request body for the MSP request with the given code and return it as an array of bytes.
+ */
MSP.crunch = function (code) {
var buffer = [];
@@ -950,6 +964,27 @@ MSP.crunch = function (code) {
return buffer;
};
+/**
+ * Send a request to read a block of data from the dataflash at the given address and pass that address and a dataview
+ * of the returned data to the given callback (or null for the data if an error occured).
+ */
+MSP.dataflashRead = function(address, onDataCallback) {
+ MSP.send_message(MSP_codes.MSP_DATAFLASH_READ, [address & 0xFF, (address >> 8) & 0xFF, (address >> 16) & 0xFF, (address >> 24) & 0xFF],
+ false, function(response) {
+ var chunkAddress = response.data.getUint32(0, 1);
+
+ // Verify that the address of the memory returned matches what the caller asked for
+ if (chunkAddress == address) {
+ /* Strip that address off the front of the reply and deliver it separately so the caller doesn't have to
+ * figure out the reply format:
+ */
+ onDataCallback(address, new DataView(response.data.buffer, response.data.byteOffset + 4, response.data.buffer.byteLength - 4));
+ } else {
+ // Report error
+ onDataCallback(address, null);
+ }
+ });
+} ;
MSP.sendModeRanges = function(onCompleteCallback) {
var nextFunction = send_next_mode_range;
diff --git a/main.html b/main.html
index f6ace554..79bf742a 100755
--- a/main.html
+++ b/main.html
@@ -22,6 +22,7 @@
+
@@ -68,6 +69,7 @@
+
@@ -146,6 +148,7 @@
+
diff --git a/main.js b/main.js
index 374f4cdc..fa3d54b6 100755
--- a/main.js
+++ b/main.js
@@ -153,6 +153,9 @@ $(document).ready(function () {
case 'logging':
TABS.logging.initialize(content_ready);
break;
+ case 'dataflash':
+ TABS.dataflash.initialize(content_ready);
+ break;
case 'cli':
TABS.cli.initialize(content_ready);
break;
diff --git a/tabs/dataflash.css b/tabs/dataflash.css
new file mode 100644
index 00000000..669151b8
--- /dev/null
+++ b/tabs/dataflash.css
@@ -0,0 +1,81 @@
+.tab-dataflash {
+}
+.tab-dataflash .dataflash-info dd{
+
+}
+
+.tab-dataflash .note {
+ padding: 5px;
+ border: 1px dashed silver;
+}
+.tab-dataflash .properties {
+ margin-top: 10px;
+}
+.tab-dataflash .dataflash-info {
+ overflow:hidden;
+}
+.tab-dataflash .dataflash-info dt {
+ float: left;
+ width: 12em;
+ height: 20px;
+ line-height: 20px;
+
+ font-weight: bold;
+}
+.tab-dataflash .dataflash-info dd {
+ display: block;
+ height: 20px;
+ line-height: 20px;
+}
+.tab-dataflash .speed {
+ margin-top: 5px;
+ width: 80px;
+
+ border: 1px solid silver;
+}
+.tab-dataflash .info {
+ margin-top: 10px;
+}
+.tab-dataflash .info dt {
+ float: left;
+ width: 120px;
+ height: 20px;
+ line-height: 20px;
+
+ font-weight: bold;
+}
+.tab-dataflash .info dd {
+ display: block;
+ margin-left: 130px;
+ height: 20px;
+ line-height: 20px;
+}
+.tab-dataflash .buttons {
+ width: calc(100% - 20px);
+
+ position: absolute;
+ bottom: 10px;
+}
+.tab-dataflash .buttons a {
+ display: block;
+ float: right;
+
+ margin-left: 10px;
+
+ height: 28px;
+ line-height: 28px;
+
+ padding: 0 15px 0 15px;
+
+ text-align: center;
+ font-weight: bold;
+
+ border: 1px solid silver;
+ background-color: #ececec;
+}
+.tab-dataflash .buttons a:hover {
+ background-color: #dedcdc;
+}
+.tab-dataflash .buttons .back {
+ display: none;
+}
\ No newline at end of file
diff --git a/tabs/dataflash.html b/tabs/dataflash.html
new file mode 100644
index 00000000..8b227371
--- /dev/null
+++ b/tabs/dataflash.html
@@ -0,0 +1,14 @@
+
+
Dataflash
+
+ - Capacity (bytes)
+
+ - Capacity (sectors)
+
+
+
+
+
\ No newline at end of file
diff --git a/tabs/dataflash.js b/tabs/dataflash.js
new file mode 100644
index 00000000..38d846d3
--- /dev/null
+++ b/tabs/dataflash.js
@@ -0,0 +1,130 @@
+'use strict';
+
+TABS.dataflash = {};
+TABS.dataflash.initialize = function (callback) {
+ var self = this;
+
+ if (GUI.active_tab != 'dataflash') {
+ GUI.active_tab = 'dataflash';
+ googleAnalytics.sendAppView('dataflash');
+ }
+
+ var requested_properties = [],
+ samples = 0,
+ requests = 0,
+ log_buffer = [];
+
+ if (CONFIGURATOR.connectionValid) {
+ MSP.send_message(MSP_codes.MSP_DATAFLASH_SUMMARY, false, false, function() {
+ $('#content').load("./tabs/dataflash.html", process_html);
+ });
+ }
+
+ function process_html() {
+ // translate to user-selected language
+ localize();
+
+ $(".tab-dataflash .dataflash-capacity").text(DATAFLASH.totalSize);
+ $(".tab-dataflash .dataflash-sectors").text(DATAFLASH.sectors);
+
+ // UI hooks
+ $('.tab-dataflash a.erase_flash').click(erase_flash);
+
+ $('.tab-dataflash a.save_to_file').click(stream_flash_to_file);
+
+ if (callback) callback();
+ }
+
+ // IO related methods
+ function zeroPad(value, width) {
+ value = "" + value;
+
+ while (value.length < width) {
+ value = "0" + value;
+ }
+
+ return value;
+ }
+
+ function stream_flash_to_file() {
+ if (GUI.connected_to) {
+ prepare_file(function(fileWriter) {
+ var
+ nextAddress = 0;
+
+ function onChunkRead(chunkAddress, chunkDataView) {
+ // If we didn't get a zero-byte chunk (indicating end-of-file), request more
+ if (chunkDataView.byteLength > 0) {
+ var blob = new Blob([chunkDataView]);
+
+ fileWriter.write(blob);
+
+ nextAddress += chunkDataView.byteLength;
+ MSP.dataflashRead(nextAddress, onChunkRead);
+ }
+ }
+
+ MSP.dataflashRead(nextAddress, onChunkRead);
+ });
+ }
+ }
+
+ function prepare_file(onComplete) {
+ var
+ date = new Date(),
+ filename = 'blackbox_log_' + date.getFullYear() + '-' + zeroPad(date.getMonth() + 1, 2) + '-'
+ + zeroPad(date.getDate(), 2) + '_' + zeroPad(date.getHours(), 2) + zeroPad(date.getMinutes(), 2)
+ + zeroPad(date.getSeconds(), 2);
+
+ chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: filename,
+ accepts: [{extensions: ['TXT']}]}, function(fileEntry) {
+ if (!fileEntry) {
+ console.log('No file selected');
+ return;
+ }
+
+ // echo/console log path specified
+ chrome.fileSystem.getDisplayPath(fileEntry, function(path) {
+ console.log('Dataflash dump file path: ' + path);
+ });
+
+ prepare_writer(fileEntry, onComplete);
+ });
+ }
+
+ function prepare_writer(fileEntry, onComplete) {
+ fileEntry.createWriter(function (fileWriter) {
+ fileWriter.onerror = function (e) {
+ console.error(e);
+
+ // stop logging if the procedure was/is still running
+ };
+
+ fileWriter.onwriteend = function () {
+ };
+
+ onComplete(fileWriter);
+ }, function (e) {
+ // File is not readable or does not exist!
+ console.error(e);
+ });
+ }
+
+ function erase_flash() {
+ /* var dialog = $("");
+
+ $("body").append(dialog);
+
+ dialog[0].showModal();
+
+ TODO modal dialog to confirm erase */
+
+ MSP.send_message(MSP_codes.MSP_DATAFLASH_ERASE, false, false, function(data) {
+
+ });
+ }
+};
+
+TABS.dataflash.cleanup = function (callback) {
+ if (callback) callback();
+};
\ No newline at end of file