diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index 4ec3b646..649af5a2 100755
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -100,6 +100,9 @@
"tabOsd": {
"message": "OSD"
},
+ "tabPower": {
+ "message": "Power & Battery"
+ },
"tabGPS": {
"message": "GPS"
},
@@ -556,6 +559,9 @@
"featureOSD": {
"message": "On Screen Display"
},
+ "featureVTX": {
+ "message": "Video Transmitter"
+ },
"featureFAILSAFE": {
"message": "Enable Failsafe Stage 2"
},
@@ -1885,6 +1891,128 @@
"failsafeKillSwitchHelp": {
"message": "Set this option to make the failsafe switch, configured in the modes tab, act as a direct kill switch, bypassing the selected failsafe procedure. Note: Arming is blocked with the failsafe kill switch in the ON position"
},
+ "powerFirmwareUpgradeRequired": {
+ "message": "Firmware upgrade required. Battery/Amperage/Voltage configurations using API < 1.22.0 is not supported."
+ },
+
+ "powerButtonSave": {
+ "message": "Save"
+ },
+
+ "powerVoltageHead": {
+ "message": "Voltage"
+ },
+ "powerVoltageValue": {
+ "message": "$1 V"
+ },
+ "powerAmperageValue": {
+ "message": "$1 A"
+ },
+ "powerVoltageId10": {
+ "message": "Battery"
+ },
+ "powerVoltageId20": {
+ "message": "5V"
+ },
+ "powerVoltageId30": {
+ "message": "9V"
+ },
+ "powerVoltageId40": {
+ "message": "12V"
+ },
+ "powerVoltageId50": {
+ "message": "ESC Combined"
+ },
+ "powerVoltageId60": {
+ "message": "ESC Motor 1"
+ },
+ "powerVoltageId61": {
+ "message": "ESC Motor 2"
+ },
+ "powerVoltageId62": {
+ "message": "ESC Motor 3"
+ },
+ "powerVoltageId63": {
+ "message": "ESC Motor 4"
+ },
+ "powerVoltageId80": {
+ "message": "Cell 1"
+ },
+ "powerVoltageId81": {
+ "message": "Cell 2"
+ },
+ "powerVoltageId82": {
+ "message": "Cell 3"
+ },
+ "powerVoltageId83": {
+ "message": "Cell 4"
+ },
+ "powerVoltageId84": {
+ "message": "Cell 5"
+ },
+ "powerVoltageId85": {
+ "message": "Cell 6"
+ },
+
+
+ "powerVoltageScale": {
+ "message": "Scale"
+ },
+ "powerVoltageDivider": {
+ "message": "Divider Value"
+ },
+ "powerVoltageMultiplier": {
+ "message": "Multiplier Value"
+ },
+
+ "powerAmperageHead": {
+ "message": "Amperage"
+ },
+ "powerAmperageId10": {
+ "message": "Battery"
+ },
+ "powerAmperageId50": {
+ "message": "ESC Combined"
+ },
+ "powerAmperageId80": {
+ "message": "Virtual"
+ },
+ "powerMahValue": {
+ "message": "$1 mAh"
+ },
+
+ "powerAmperageScale": {
+ "message": "Scale the output voltage to milliamps [1/10th mV/A]"
+ },
+ "powerAmperageOffset": {
+ "message": "Offset in millivolt steps"
+ },
+
+ "powerBatteryHead": {
+ "message": "Battery"
+ },
+ "powerBatteryConnected": {
+ "message": "Connected"
+ },
+ "powerBatteryConnectedValueYes": {
+ "message": "Yes (Cells: $1)"
+ },
+ "powerBatteryConnectedValueNo": {
+ "message": "No"
+ },
+ "powerBatteryVoltage": {
+ "message": "Voltage"
+ },
+ "powerBatteryCurrentDrawn": {
+ "message": "mAh used"
+ },
+ "powerBatteryAmperage": {
+ "message": "Amperage"
+ },
+ "powerBatteryCapacity": {
+ "message": "Capacity (mAh)"
+ },
+
"mainHelpArmed": {
"message": "Motor Arming"
},
diff --git a/images/icons/cf_icon_power_grey.svg b/images/icons/cf_icon_power_grey.svg
new file mode 100644
index 00000000..2cb96635
--- /dev/null
+++ b/images/icons/cf_icon_power_grey.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/images/icons/cf_icon_power_white.svg b/images/icons/cf_icon_power_white.svg
new file mode 100644
index 00000000..1af9f1f7
--- /dev/null
+++ b/images/icons/cf_icon_power_white.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/js/gui.js b/js/gui.js
index bd28f75d..1767ae8c 100644
--- a/js/gui.js
+++ b/js/gui.js
@@ -21,6 +21,7 @@ var GUI_control = function () {
'failsafe',
'transponder',
'osd',
+ 'power',
'adjustments',
'auxiliary',
'cli',
diff --git a/js/msp/MSPHelper.js b/js/msp/MSPHelper.js
index f3245091..e2d47803 100644
--- a/js/msp/MSPHelper.js
+++ b/js/msp/MSPHelper.js
@@ -158,27 +158,29 @@ MspHelper.prototype.process_data = function(dataHandler) {
ANALOG.amperage = data.read16() / 100; // A
ANALOG.last_received_timestamp = Date.now();
break;
-// case MSPCodes.MSP_VOLTAGE_METERS:
-// VOLTAGE_METERS = [];
-// for (var i = 0; i < (message_length); i++) {
-// var voltageMeter = {};
-// voltageMeter.voltage = data.readU8() / 10.0;
-//
-// VOLTAGE_METERS.push(voltageMeter)
-// }
-// break;
-// case MSPCodes.MSP_CURRENT_METERS:
-// CURRENT_METERS = [];
-// for (var i = 0; i < (message_length / 6); i++) {
-// var amperageMeter = {};
-// amperageMeter.amperage = data.read16() / 1000; // A
-// offset += 2;
-// amperageMeter.mAhDrawn = data.readU32(); // A
-// offset += 4;
-//
-// CURRENT_METERS.push(amperageMeter);
-// }
-// break;
+ case MSPCodes.MSP_VOLTAGE_METERS:
+ VOLTAGE_METERS = [];
+ var voltageMeterLength = 2;
+ for (var i = 0; i < (data.byteLength / voltageMeterLength); i++) {
+ var voltageMeter = {};
+ voltageMeter.id = data.readU8();
+ voltageMeter.voltage = data.readU8() / 10.0;
+
+ VOLTAGE_METERS.push(voltageMeter)
+ }
+ break;
+ case MSPCodes.MSP_CURRENT_METERS:
+ CURRENT_METERS = [];
+ var currentMeterLength = 5;
+ for (var i = 0; i < (data.byteLength / currentMeterLength); i++) {
+ var currentMeter = {};
+ currentMeter.id = data.readU8();
+ currentMeter.mAhDrawn = data.readU16(); // mAh
+ currentMeter.amperage = data.readU16() / 1000; // A
+
+ CURRENT_METERS.push(currentMeter);
+ }
+ break;
case MSPCodes.MSP_BATTERY_STATE:
BATTERY_STATE.cellCount = data.readU8();
BATTERY_STATE.capacity = data.readU16(); // mAh
@@ -1232,7 +1234,7 @@ MspHelper.prototype.crunch = function(code) {
buffer.push8(Math.round(BATTERY_CONFIG.vbatmincellvoltage * 10))
.push8(Math.round(BATTERY_CONFIG.vbatmaxcellvoltage * 10))
.push8(Math.round(BATTERY_CONFIG.vbatwarningcellvoltage * 10))
- .push16(BATTERY_CONFIG.batterycapacity)
+ .push16(BATTERY_CONFIG.capacity)
.push8(BATTERY_CONFIG.voltageMeterSource)
.push8(BATTERY_CONFIG.currentMeterSource);
break;
diff --git a/main.css b/main.css
index 5255facc..ca7290c6 100644
--- a/main.css
+++ b/main.css
@@ -811,6 +811,19 @@ li.active .ic_transponder {
background-image: url(images/icons/icon_osd_white.svg);
}
+.ic_power {
+ background-image: url(images/icons/cf_icon_power_grey.svg);
+ background-position-y: 9px;
+}
+
+.ic_power:hover {
+ background-image: url(images/icons/cf_icon_power_white.svg);
+}
+
+li.active .ic_power {
+ background-image: url(images/icons/cf_icon_power_white.svg);
+}
+
/* SPARE Tab-Icons */
.ic_failsafe {
background-image: url(images/icons/cf_icon_failsafe_grey.svg);
diff --git a/main.html b/main.html
index 8937488d..d3d187fe 100755
--- a/main.html
+++ b/main.html
@@ -27,6 +27,7 @@
+
@@ -87,6 +88,7 @@
+
@@ -216,6 +218,8 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ |
+ ? |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+ |
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tabs/power.js b/tabs/power.js
new file mode 100644
index 00000000..142ff05f
--- /dev/null
+++ b/tabs/power.js
@@ -0,0 +1,275 @@
+'use strict';
+
+TABS.power = {
+ supported: false,
+};
+
+TABS.power.initialize = function (callback) {
+ var self = this;
+
+ if (GUI.active_tab != 'power') {
+ GUI.active_tab = 'power';
+ googleAnalytics.sendAppView('Power');
+ }
+
+ function load_status() {
+ MSP.send_message(MSPCodes.MSP_STATUS, false, false, load_voltage_meters);
+ }
+
+ function load_voltage_meters() {
+ MSP.send_message(MSPCodes.MSP_VOLTAGE_METERS, false, false, load_current_meters);
+ }
+
+ function load_current_meters() {
+ MSP.send_message(MSPCodes.MSP_CURRENT_METERS, false, false, load_current_meter_configs);
+ }
+
+ function load_current_meter_configs() {
+ MSP.send_message(MSPCodes.MSP_CURRENT_METER_CONFIG, false, false, load_voltage_meter_configs);
+ }
+
+ function load_voltage_meter_configs() {
+ MSP.send_message(MSPCodes.MSP_VOLTAGE_METER_CONFIG, false, false, load_battery_state);
+ }
+
+ function load_battery_state() {
+ MSP.send_message(MSPCodes.MSP_BATTERY_STATE, false, false, load_battery_config);
+ }
+
+ function load_battery_config() {
+ MSP.send_message(MSPCodes.MSP_BATTERY_CONFIG, false, false, load_html);
+ }
+
+ function load_html() {
+ $('#content').load("./tabs/power.html", process_html);
+ }
+
+ this.supported = semver.gte(CONFIG.apiVersion, "1.22.0");
+
+ if (!this.supported) {
+ load_html();
+ } else {
+ load_status();
+ }
+
+ function update_ui() {
+ if (!TABS.power.supported) {
+ $(".tab-power").removeClass("supported");
+ return;
+ }
+ $(".tab-power").addClass("supported");
+
+ // voltage meters
+
+ var template = $('#tab-power-templates .voltage-meters .voltage-meter');
+ var destination = $('.tab-power .voltage-meters');
+
+ for (var index = 0; index < VOLTAGE_METERS.length; index++) {
+ var meterElement = template.clone();
+ $(meterElement).attr('id', 'voltage-meter-' + index);
+
+ var message = chrome.i18n.getMessage('powerVoltageId' + VOLTAGE_METERS[index].id);
+ $(meterElement).find('.label').text(message)
+ destination.append(meterElement);
+ }
+
+ var template = $('#tab-power-templates .voltage-configuration');
+ for (var index = 0; index < VOLTAGE_METER_CONFIGS.length; index++) {
+ var destination = $('#voltage-meter-' + index + ' .configuration');
+ var element = template.clone();
+
+ var attributeNames = ["vbatscale", "vbatresdivval", "vbatresdivmultiplier"];
+ for (let attributeName of attributeNames) {
+ $(element).find('input[name="' + attributeName + '"]').attr('name', attributeName + '-' + index);
+ }
+ destination.append(element);
+
+ $('input[name="vbatscale-' + index + '"]').val(VOLTAGE_METER_CONFIGS[index].vbatscale).attr('disabled','disabled');
+ $('input[name="vbatresdivval-' + index + '"]').val(VOLTAGE_METER_CONFIGS[index].vbatresdivval).attr('disabled','disabled');
+ $('input[name="vbatresdivmultiplier-' + index + '"]').val(VOLTAGE_METER_CONFIGS[index].vbatresdivmultiplier).attr('disabled','disabled');
+ }
+
+ // amperage meters
+
+ var template = $('#tab-power-templates .amperage-meters .amperage-meter');
+ var destination = $('.tab-power .amperage-meters');
+
+ for (var index = 0; index < CURRENT_METERS.length; index++) {
+ var meterElement = template.clone();
+ $(meterElement).attr('id', 'amperage-meter-' + index);
+
+ var message = chrome.i18n.getMessage('powerAmperageId' + CURRENT_METERS[index].id);
+ $(meterElement).find('.label').text(message)
+ destination.append(meterElement);
+ }
+
+ var template = $('#tab-power-templates .amperage-configuration');
+ for (var index = 0; index < CURRENT_METER_CONFIGS.length; index++) {
+ var destination = $('#amperage-meter-' + index + ' .configuration');
+ var element = template.clone();
+
+ var attributeNames = ["amperagescale", "amperageoffset"];
+ for (let attributeName of attributeNames) {
+ $(element).find('input[name="' + attributeName + '"]').attr('name', attributeName + '-' + index);
+ }
+ destination.append(element);
+
+ $('input[name="amperagescale-' + index + '"]').val(CURRENT_METER_CONFIGS[index].scale).attr('disabled','disabled');
+ $('input[name="amperageoffset-' + index + '"]').val(CURRENT_METER_CONFIGS[index].offset).attr('disabled','disabled');
+ }
+
+
+ // battery
+
+ var template = $('#tab-power-templates .battery-states .battery-state');
+ var destination = $('.tab-power .battery-state');
+ var element = template.clone();
+ $(element).find('.connection-state').attr('id', 'battery-connection-state');
+ $(element).find('.voltage').attr('id', 'battery-voltage');
+ $(element).find('.mah-drawn').attr('id', 'battery-mah-drawn');
+ $(element).find('.amperage').attr('id', 'battery-amperage');
+
+ destination.append(element.children());
+
+ var template = $('#tab-power-templates .battery-configuration');
+ var destination = $('.tab-power .battery .configuration');
+ var element = template.clone();
+ destination.append(element);
+
+ $('input[name="mincellvoltage"]').val(BATTERY_CONFIG.vbatmincellvoltage);
+ $('input[name="maxcellvoltage"]').val(BATTERY_CONFIG.vbatmaxcellvoltage);
+ $('input[name="warningcellvoltage"]').val(BATTERY_CONFIG.vbatwarningcellvoltage);
+ $('input[name="capacity"]').val(BATTERY_CONFIG.capacity);
+
+ var batteryMeterTypes = [
+ 'None',
+ 'Onboard ADC',
+ 'ESC Sensor'
+ ];
+
+ var batteryMeterType_e = $('select.batterymetersource');
+ for (var i = 0; i < batteryMeterTypes.length; i++) {
+ batteryMeterType_e.append('');
+ }
+
+ batteryMeterType_e.change(function () {
+ BATTERY_CONFIG.voltageMeterSource = parseInt($(this).val());
+ });
+ batteryMeterType_e.val(BATTERY_CONFIG.voltageMeterSource).change();
+
+ // fill current
+ var currentMeterTypes = [
+ 'None',
+ 'Onboard ADC',
+ 'Virtual',
+ 'ESC Sensor'
+ ];
+
+ var currentMeterType_e = $('select.currentmetersource');
+ for (var i = 0; i < currentMeterTypes.length; i++) {
+ currentMeterType_e.append('');
+ }
+
+ currentMeterType_e.change(function () {
+ BATTERY_CONFIG.currentMeterSource = parseInt($(this).val());
+ });
+ currentMeterType_e.val(BATTERY_CONFIG.currentMeterSource).change();
+
+
+
+ function get_slow_data() {
+ MSP.send_message(MSPCodes.MSP_VOLTAGE_METERS, false, false, function () {
+ for (var i = 0; i < VOLTAGE_METERS.length; i++) {
+ var elementName = '#voltage-meter-' + i + ' .value';
+ var element = $(elementName);
+ element.text(chrome.i18n.getMessage('powerVoltageValue', [VOLTAGE_METERS[i].voltage]));
+ }
+ });
+
+ MSP.send_message(MSPCodes.MSP_CURRENT_METERS, false, false, function () {
+ for (var i = 0; i < CURRENT_METERS.length; i++) {
+ var elementName = '#amperage-meter-' + i + ' .value';
+ var element = $(elementName);
+ element.text(chrome.i18n.getMessage('powerAmperageValue', [CURRENT_METERS[i].amperage.toFixed(2)]));
+ }
+ });
+
+ MSP.send_message(MSPCodes.MSP_BATTERY_STATE, false, false, function () {
+ var elementPrefix = '#battery';
+ var element;
+
+ element = $(elementPrefix + '-connection-state .value');
+ element.text(BATTERY_STATE.cellCount > 0 ? chrome.i18n.getMessage('powerBatteryConnectedValueYes', [BATTERY_STATE.cellCount]) : chrome.i18n.getMessage('powerBatteryConnectedValueNo'));
+ element = $(elementPrefix + '-voltage .value');
+ element.text(chrome.i18n.getMessage('powerVoltageValue', [BATTERY_STATE.voltage]));
+ element = $(elementPrefix + '-mah-drawn .value');
+ element.text(chrome.i18n.getMessage('powerMahValue', [BATTERY_STATE.mAhDrawn]));
+ element = $(elementPrefix + '-amperage .value');
+ element.text(chrome.i18n.getMessage('powerAmperageValue', [BATTERY_STATE.amperage]));
+ });
+
+ }
+
+ $('a.save').click(function () {
+
+ /* FIXME update for api 1.33.0
+ for (var index = 0; index < VOLTAGE_METER_CONFIGS.length; index++) {
+ VOLTAGE_METER_CONFIGS[index].vbatscale = parseInt($('input[name="vbatscale-' + index + '"]').val());
+ VOLTAGE_METER_CONFIGS[index].vbatresdivval = parseInt($('input[name="vbatresdivval-' + index + '"]').val());
+ VOLTAGE_METER_CONFIGS[index].vbatresdivmultiplier = parseInt($('input[name="vbatresdivmultiplier-' + index + '"]').val());
+ }
+
+ for (var index = 0; index < CURRENT_METER_CONFIGS.length; index++) {
+ CURRENT_METER_CONFIGS[index].scale = parseInt($('input[name="amperagescale-' + index + '"]').val());
+ CURRENT_METER_CONFIGS[index].offset = parseInt($('input[name="amperageoffset-' + index + '"]').val());
+ }
+ */
+
+ BATTERY_CONFIG.vbatmincellvoltage = parseFloat($('input[name="mincellvoltage"]').val());
+ BATTERY_CONFIG.vbatmaxcellvoltage = parseFloat($('input[name="maxcellvoltage"]').val());
+ BATTERY_CONFIG.vbatwarningcellvoltage = parseFloat($('input[name="warningcellvoltage"]').val());
+ BATTERY_CONFIG.capacity = parseInt($('input[name="capacity"]').val());
+
+ /* FIXME update for api 1.33.0
+ function save_voltage_config() {
+ MSP.sendVoltageMeterConfigs(save_amperage_config);
+ }
+
+ function save_amperage_config() {
+ MSP.sendAmperageMeterConfigs(save_battery_config);
+ }
+ */
+
+ function save_battery_config() {
+ MSP.send_message(MSPCodes.MSP_SET_BATTERY_CONFIG, mspHelper.crunch(MSPCodes.MSP_SET_BATTERY_CONFIG), false, save_to_eeprom);
+ }
+
+ function save_to_eeprom() {
+ MSP.send_message(MSPCodes.MSP_EEPROM_WRITE, false, false, save_completed);
+ }
+
+ function save_completed() {
+ GUI.log(chrome.i18n.getMessage('configurationEepromSaved'));
+
+ TABS.power.initialize();
+ }
+
+ save_battery_config();
+ });
+
+ GUI.interval_add('setup_data_pull_slow', get_slow_data, 200, true); // 5hz
+ }
+
+ function process_html() {
+ update_ui();
+
+ // translate to user-selected language
+ localize();
+
+ GUI.content_ready(callback);
+ }
+};
+
+TABS.power.cleanup = function (callback) {
+ if (callback) callback();
+};