mirror of
https://github.com/betaflight/betaflight-configurator.git
synced 2025-07-21 15:25:22 +03:00
commit
50b46c3c97
16 changed files with 50324 additions and 8 deletions
|
@ -93,6 +93,9 @@
|
||||||
"tabTransponder": {
|
"tabTransponder": {
|
||||||
"message": "Race Transponder"
|
"message": "Race Transponder"
|
||||||
},
|
},
|
||||||
|
"tabOsd": {
|
||||||
|
"message": "OSD"
|
||||||
|
},
|
||||||
"tabGPS": {
|
"tabGPS": {
|
||||||
"message": "GPS"
|
"message": "GPS"
|
||||||
},
|
},
|
||||||
|
|
25
images/icons/icon_osd.svg
Normal file
25
images/icons/icon_osd.svg
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="227 -351.2 595.3 841.9" style="enable-background:new 227 -351.2 595.3 841.9;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#818181;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M677.5,19.8h-13.3v94.8h10.2c13.9,0,24.1-4,30.5-12.1c6.5-8.1,9.7-20.4,9.7-37c0-15.5-3.1-27-9.2-34.5
|
||||||
|
C699.3,23.5,690,19.8,677.5,19.8z"/>
|
||||||
|
<path class="st0" d="M369.2,17.7c-22.9,0-34.4,16.5-34.4,49.5c0,32.7,11.4,49.1,34.1,49.1c11.6,0,20.1-4,25.7-11.9
|
||||||
|
c5.6-7.9,8.4-20.3,8.4-37.1c0-16.9-2.8-29.4-8.5-37.4C388.9,21.7,380.4,17.7,369.2,17.7z"/>
|
||||||
|
<path class="st0" d="M726.8-134.3H321.9c-36.3,0-65.9,29.5-65.9,65.9V207c0,36.3,29.5,65.9,65.9,65.9h404.9
|
||||||
|
c36.3,0,65.9-29.5,65.9-65.9V-68.4C792.7-104.8,763.1-134.3,726.8-134.3z M430.3,132.1c-14,14.8-34.4,22.2-61.3,22.2
|
||||||
|
c-26.5,0-46.9-7.4-61-22.3c-14.2-14.9-21.2-36.6-21.2-65c0-28.1,7-49.7,21.1-64.5c14.1-14.8,34.5-22.3,61.4-22.3
|
||||||
|
c26.9,0,47.3,7.4,61.2,22.1c13.9,14.7,20.8,36.4,20.8,64.9C451.2,95.7,444.2,117.3,430.3,132.1z M525.5,36.8
|
||||||
|
c3.4,2.6,12.7,7.4,27.9,14.3c14.6,6.6,24.7,13.6,30.4,21.1c5.7,7.5,8.5,17,8.5,28.4c0,10.5-2.7,19.8-8,27.9c-5.3,8.1-13,14.5-23,19
|
||||||
|
c-10,4.5-21.8,6.8-35.3,6.8c-11.3,0-20.7-0.8-28.3-2.4s-15.6-4.3-23.8-8.3V103c8.7,4.5,17.8,8,27.2,10.5c9.4,2.5,18,3.8,25.9,3.8
|
||||||
|
c6.8,0,11.8-1.2,14.9-3.5c3.1-2.3,4.7-5.4,4.7-9.1c0-2.3-0.6-4.3-1.9-6.1s-3.3-3.5-6.1-5.3c-2.8-1.8-10.3-5.4-22.5-10.9
|
||||||
|
c-11-5-19.3-9.9-24.8-14.6c-5.5-4.7-9.6-10.1-12.3-16.2c-2.7-6.1-4-13.3-4-21.6c0-15.6,5.7-27.7,17-36.4
|
||||||
|
c11.3-8.7,26.9-13.1,46.7-13.1c17.5,0,35.4,4,53.6,12.1l-14,35.3c-15.8-7.2-29.5-10.9-41-10.9c-5.9,0-10.3,1-13,3.1s-4,4.7-4,7.8
|
||||||
|
C520.4,31.2,522.1,34.2,525.5,36.8z M738.8,129.1c-15.5,15.3-37.4,22.9-65.5,22.9h-54.7V-17.1H677c27.2,0,48.1,6.9,62.9,20.8
|
||||||
|
c14.8,13.9,22.2,33.9,22.2,60.2C762.1,92.1,754.3,113.8,738.8,129.1z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
25
images/icons/icon_osd_white.svg
Normal file
25
images/icons/icon_osd_white.svg
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="227 -351.2 595.3 841.9" style="enable-background:new 227 -351.2 595.3 841.9;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<g>
|
||||||
|
<path class="st0" d="M677.5,19.8h-13.3v94.8h10.2c13.9,0,24.1-4,30.5-12.1c6.5-8.1,9.7-20.4,9.7-37c0-15.5-3.1-27-9.2-34.5
|
||||||
|
C699.3,23.5,690,19.8,677.5,19.8z"/>
|
||||||
|
<path class="st0" d="M369.2,17.7c-22.9,0-34.4,16.5-34.4,49.5c0,32.7,11.4,49.1,34.1,49.1c11.6,0,20.1-4,25.7-11.9
|
||||||
|
c5.6-7.9,8.4-20.3,8.4-37.1c0-16.9-2.8-29.4-8.5-37.4C388.9,21.7,380.4,17.7,369.2,17.7z"/>
|
||||||
|
<path class="st0" d="M726.8-134.3H321.9c-36.3,0-65.9,29.5-65.9,65.9V207c0,36.3,29.5,65.9,65.9,65.9h404.9
|
||||||
|
c36.3,0,65.9-29.5,65.9-65.9V-68.4C792.7-104.8,763.1-134.3,726.8-134.3z M430.3,132.1c-14,14.8-34.4,22.2-61.3,22.2
|
||||||
|
c-26.5,0-46.9-7.4-61-22.3c-14.2-14.9-21.2-36.6-21.2-65c0-28.1,7-49.7,21.1-64.5c14.1-14.8,34.5-22.3,61.4-22.3
|
||||||
|
c26.9,0,47.3,7.4,61.2,22.1c13.9,14.7,20.8,36.4,20.8,64.9C451.2,95.7,444.2,117.3,430.3,132.1z M525.5,36.8
|
||||||
|
c3.4,2.6,12.7,7.4,27.9,14.3c14.6,6.6,24.7,13.6,30.4,21.1c5.7,7.5,8.5,17,8.5,28.4c0,10.5-2.7,19.8-8,27.9c-5.3,8.1-13,14.5-23,19
|
||||||
|
c-10,4.5-21.8,6.8-35.3,6.8c-11.3,0-20.7-0.8-28.3-2.4s-15.6-4.3-23.8-8.3V103c8.7,4.5,17.8,8,27.2,10.5c9.4,2.5,18,3.8,25.9,3.8
|
||||||
|
c6.8,0,11.8-1.2,14.9-3.5c3.1-2.3,4.7-5.4,4.7-9.1c0-2.3-0.6-4.3-1.9-6.1s-3.3-3.5-6.1-5.3c-2.8-1.8-10.3-5.4-22.5-10.9
|
||||||
|
c-11-5-19.3-9.9-24.8-14.6c-5.5-4.7-9.6-10.1-12.3-16.2c-2.7-6.1-4-13.3-4-21.6c0-15.6,5.7-27.7,17-36.4
|
||||||
|
c11.3-8.7,26.9-13.1,46.7-13.1c17.5,0,35.4,4,53.6,12.1l-14,35.3c-15.8-7.2-29.5-10.9-41-10.9c-5.9,0-10.3,1-13,3.1s-4,4.7-4,7.8
|
||||||
|
C520.4,31.2,522.1,34.2,525.5,36.8z M738.8,129.1c-15.5,15.3-37.4,22.9-65.5,22.9h-54.7V-17.1H677c27.2,0,48.1,6.9,62.9,20.8
|
||||||
|
c14.8,13.9,22.2,33.9,22.2,60.2C762.1,92.1,754.3,113.8,738.8,129.1z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2 KiB |
BIN
images/osd-bg-1.png
Normal file
BIN
images/osd-bg-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 303 KiB |
|
@ -20,6 +20,7 @@ var GUI_control = function () {
|
||||||
this.defaultAllowedTabsWhenConnected = [
|
this.defaultAllowedTabsWhenConnected = [
|
||||||
'failsafe',
|
'failsafe',
|
||||||
'transponder',
|
'transponder',
|
||||||
|
'osd',
|
||||||
'adjustments',
|
'adjustments',
|
||||||
'auxiliary',
|
'auxiliary',
|
||||||
'cli',
|
'cli',
|
||||||
|
@ -239,8 +240,7 @@ GUI_control.prototype.tab_switch_cleanup = function (callback) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
GUI_control.prototype.content_ready = function (callback) {
|
GUI_control.prototype.switchery = function() {
|
||||||
|
|
||||||
$('.togglesmall').each(function(index, elem) {
|
$('.togglesmall').each(function(index, elem) {
|
||||||
var switchery = new Switchery(elem, {
|
var switchery = new Switchery(elem, {
|
||||||
size: 'small',
|
size: 'small',
|
||||||
|
@ -275,6 +275,11 @@ GUI_control.prototype.content_ready = function (callback) {
|
||||||
});
|
});
|
||||||
$(elem).removeClass('togglemedium');
|
$(elem).removeClass('togglemedium');
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
GUI_control.prototype.content_ready = function (callback) {
|
||||||
|
|
||||||
|
this.switchery();
|
||||||
|
|
||||||
if (CONFIGURATOR.connectionValid) {
|
if (CONFIGURATOR.connectionValid) {
|
||||||
// Build link to in-use CF version documentation
|
// Build link to in-use CF version documentation
|
||||||
|
|
9
js/libraries/jquery.ba-throttle-debounce.min.js
vendored
Normal file
9
js/libraries/jquery.ba-throttle-debounce.min.js
vendored
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/*
|
||||||
|
* jQuery throttle / debounce - v1.1 - 3/7/2010
|
||||||
|
* http://benalman.com/projects/jquery-throttle-debounce-plugin/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* http://benalman.com/about/license/
|
||||||
|
*/
|
||||||
|
(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
|
21
js/msp.js
21
js/msp.js
|
@ -40,7 +40,12 @@ var MSP_codes = {
|
||||||
MSP_SET_BLACKBOX_CONFIG: 81,
|
MSP_SET_BLACKBOX_CONFIG: 81,
|
||||||
MSP_TRANSPONDER_CONFIG: 82,
|
MSP_TRANSPONDER_CONFIG: 82,
|
||||||
MSP_SET_TRANSPONDER_CONFIG: 83,
|
MSP_SET_TRANSPONDER_CONFIG: 83,
|
||||||
|
MSP_OSD_CONFIG: 84,
|
||||||
|
MSP_SET_OSD_CONFIG: 85,
|
||||||
|
MSP_OSD_CHAR_READ: 86,
|
||||||
|
MSP_OSD_CHAR_WRITE: 87,
|
||||||
|
MSP_VTX_CONFIG: 88,
|
||||||
|
MSP_SET_VTX_CONFIG: 89,
|
||||||
MSP_PID_ADVANCED_CONFIG: 90,
|
MSP_PID_ADVANCED_CONFIG: 90,
|
||||||
MSP_SET_PID_ADVANCED_CONFIG: 91,
|
MSP_SET_PID_ADVANCED_CONFIG: 91,
|
||||||
MSP_FILTER_CONFIG: 92,
|
MSP_FILTER_CONFIG: 92,
|
||||||
|
@ -1063,6 +1068,20 @@ var MSP = {
|
||||||
case MSP_codes.MSP_SET_FAILSAFE_CONFIG:
|
case MSP_codes.MSP_SET_FAILSAFE_CONFIG:
|
||||||
console.log('Failsafe config saved');
|
console.log('Failsafe config saved');
|
||||||
break;
|
break;
|
||||||
|
case MSP_codes.MSP_OSD_CONFIG:
|
||||||
|
break;
|
||||||
|
case MSP_codes.MSP_SET_OSD_CONFIG:
|
||||||
|
console.log('OSD config set');
|
||||||
|
break;
|
||||||
|
case MSP_codes.MSP_OSD_CHAR_READ:
|
||||||
|
break;
|
||||||
|
case MSP_codes.MSP_OSD_CHAR_WRITE:
|
||||||
|
console.log('OSD char uploaded');
|
||||||
|
break;
|
||||||
|
case MSP_codes.MSP_VTX_CONFIG:
|
||||||
|
break;
|
||||||
|
case MSP_codes.MSP_SET_VTX_CONFIG:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('Unknown code detected: ' + code);
|
console.log('Unknown code detected: ' + code);
|
||||||
} else {
|
} else {
|
||||||
|
|
31
main.css
31
main.css
|
@ -798,6 +798,14 @@ li.active .ic_transponder {
|
||||||
background-image: url(images/icons/cf_icon_transponder_white.svg);
|
background-image: url(images/icons/cf_icon_transponder_white.svg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ic_osd {
|
||||||
|
background-image: url(images/icons/icon_osd.svg);
|
||||||
|
background-position-y: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ic_osd:hover, li.active .ic_osd {
|
||||||
|
background-image: url(images/icons/icon_osd_white.svg);
|
||||||
|
}
|
||||||
|
|
||||||
/* SPARE Tab-Icons */
|
/* SPARE Tab-Icons */
|
||||||
.ic_failsafe {
|
.ic_failsafe {
|
||||||
|
@ -1439,7 +1447,7 @@ dialog {
|
||||||
/* fixing padding for all Tabs*/
|
/* fixing padding for all Tabs*/
|
||||||
.tab-setup, .tab-landing, .tab-adjustments, .tab-auxiliary, .tab-cli, .tab-configuration, .tab-failsafe, .tab-onboard_logging,
|
.tab-setup, .tab-landing, .tab-adjustments, .tab-auxiliary, .tab-cli, .tab-configuration, .tab-failsafe, .tab-onboard_logging,
|
||||||
.tab-firmware_flasher, .tab-gps, .tab-help, .tab-led-strip, .tab-logging, .tab-modes, .tab-motors, .tab-pid_tuning,
|
.tab-firmware_flasher, .tab-gps, .tab-help, .tab-led-strip, .tab-logging, .tab-modes, .tab-motors, .tab-pid_tuning,
|
||||||
.tab-ports, .tab-receiver, .tab-sensors, .tab-servos {
|
.tab-ports, .tab-receiver, .tab-sensors, .tab-servos, .tab-osd {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -1574,9 +1582,6 @@ dialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Battery element styling*/
|
/* Battery element styling*/
|
||||||
|
|
||||||
#quad-status_wrapper {
|
#quad-status_wrapper {
|
||||||
|
@ -1686,6 +1691,22 @@ dialog {
|
||||||
border-bottom-right-radius: 5px;
|
border-bottom-right-radius: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: .5em .75em;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #ccc;
|
||||||
|
color: #666;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
font-family: 'open_sanssemibold', Arial;
|
||||||
|
font-size: 10pt;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.active {
|
||||||
|
background-color: #ffbb00;
|
||||||
|
border: 1px solid #dba718;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
|
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
|
||||||
|
@ -1801,4 +1822,4 @@ input {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
<link type="text/css" rel="stylesheet" href="./tabs/adjustments.css" media="all" />
|
<link type="text/css" rel="stylesheet" href="./tabs/adjustments.css" media="all" />
|
||||||
<link type="text/css" rel="stylesheet" href="./tabs/auxiliary.css" media="all" />
|
<link type="text/css" rel="stylesheet" href="./tabs/auxiliary.css" media="all" />
|
||||||
<link type="text/css" rel="stylesheet" href="./tabs/failsafe.css" media="all" />
|
<link type="text/css" rel="stylesheet" href="./tabs/failsafe.css" media="all" />
|
||||||
|
<link type="text/css" rel="stylesheet" href="./tabs/osd.css" media="all" />
|
||||||
<link type="text/css" rel="stylesheet" href="./tabs/transponder.css" media="all" />
|
<link type="text/css" rel="stylesheet" href="./tabs/transponder.css" media="all" />
|
||||||
<link type="text/css" rel="stylesheet" href="./css/opensans_webfontkit/fonts.css" media="all" />
|
<link type="text/css" rel="stylesheet" href="./css/opensans_webfontkit/fonts.css" media="all" />
|
||||||
<link type="text/css" rel="stylesheet" href="./css/dropdown-lists/css/style_lists.css" media="all" />
|
<link type="text/css" rel="stylesheet" href="./css/dropdown-lists/css/style_lists.css" media="all" />
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
<script type="text/javascript" src="./js/libraries/jbox/jBox.min.js"></script>
|
<script type="text/javascript" src="./js/libraries/jbox/jBox.min.js"></script>
|
||||||
<script type="text/javascript" src="./js/libraries/switchery/switchery.js"></script>
|
<script type="text/javascript" src="./js/libraries/switchery/switchery.js"></script>
|
||||||
<script type="text/javascript" src="./js/libraries/bluebird.min.js"></script>
|
<script type="text/javascript" src="./js/libraries/bluebird.min.js"></script>
|
||||||
|
<script type="text/javascript" src="./js/libraries/jquery.ba-throttle-debounce.min.js"></script>
|
||||||
<script type="text/javascript" src="./js/injected_methods.js"></script>
|
<script type="text/javascript" src="./js/injected_methods.js"></script>
|
||||||
<script type="text/javascript" src="./js/port_handler.js"></script>
|
<script type="text/javascript" src="./js/port_handler.js"></script>
|
||||||
<script type="text/javascript" src="./js/port_usage.js"></script>
|
<script type="text/javascript" src="./js/port_usage.js"></script>
|
||||||
|
@ -81,6 +83,7 @@
|
||||||
<script type="text/javascript" src="./tabs/onboard_logging.js"></script>
|
<script type="text/javascript" src="./tabs/onboard_logging.js"></script>
|
||||||
<script type="text/javascript" src="./tabs/firmware_flasher.js"></script>
|
<script type="text/javascript" src="./tabs/firmware_flasher.js"></script>
|
||||||
<script type="text/javascript" src="./tabs/failsafe.js"></script>
|
<script type="text/javascript" src="./tabs/failsafe.js"></script>
|
||||||
|
<script type="text/javascript" src="./tabs/osd.js"></script>
|
||||||
<script type="text/javascript" src="./tabs/transponder.js"></script>
|
<script type="text/javascript" src="./tabs/transponder.js"></script>
|
||||||
<title></title>
|
<title></title>
|
||||||
</head>
|
</head>
|
||||||
|
@ -215,6 +218,7 @@
|
||||||
<li class="tab_servos"><a href="#" i18n="tabServos" class="tabicon ic_servo" title="Servos"></a></li>
|
<li class="tab_servos"><a href="#" i18n="tabServos" class="tabicon ic_servo" title="Servos"></a></li>
|
||||||
<li class="tab_gps"><a href="#" i18n="tabGPS" class="tabicon ic_gps" title="GPS"></a></li>
|
<li class="tab_gps"><a href="#" i18n="tabGPS" class="tabicon ic_gps" title="GPS"></a></li>
|
||||||
<li class="tab_motors"><a href="#" i18n="tabMotorTesting" class="tabicon ic_motor" title="Motors"></a></li>
|
<li class="tab_motors"><a href="#" i18n="tabMotorTesting" class="tabicon ic_motor" title="Motors"></a></li>
|
||||||
|
<li class="tab_osd"><a href="#" i18n="tabOsd" class="tabicon ic_osd" title="Osd"></a></li>
|
||||||
<li class="tab_transponder"><a href="#" i18n="tabTransponder" class="tabicon ic_transponder" title="Transponder"></a></li>
|
<li class="tab_transponder"><a href="#" i18n="tabTransponder" class="tabicon ic_transponder" title="Transponder"></a></li>
|
||||||
<li class="tab_led_strip"><a href="#" i18n="tabLedStrip" class="tabicon ic_led" title="LED Strip"></a></li>
|
<li class="tab_led_strip"><a href="#" i18n="tabLedStrip" class="tabicon ic_led" title="LED Strip"></a></li>
|
||||||
<li class="tab_sensors"><a href="#" i18n="tabRawSensorData" class="tabicon ic_sensors" title="Sensors"></a></li>
|
<li class="tab_sensors"><a href="#" i18n="tabRawSensorData" class="tabicon ic_sensors" title="Sensors"></a></li>
|
||||||
|
|
3
main.js
3
main.js
|
@ -126,6 +126,9 @@ $(document).ready(function () {
|
||||||
case 'transponder':
|
case 'transponder':
|
||||||
TABS.transponder.initialize(content_ready);
|
TABS.transponder.initialize(content_ready);
|
||||||
break;
|
break;
|
||||||
|
case 'osd':
|
||||||
|
TABS.osd.initialize(content_ready);
|
||||||
|
break;
|
||||||
case 'setup':
|
case 'setup':
|
||||||
TABS.setup.initialize(content_ready);
|
TABS.setup.initialize(content_ready);
|
||||||
break;
|
break;
|
||||||
|
|
16385
resources/osd/bold.mcm
Normal file
16385
resources/osd/bold.mcm
Normal file
File diff suppressed because it is too large
Load diff
16385
resources/osd/default.mcm
Normal file
16385
resources/osd/default.mcm
Normal file
File diff suppressed because it is too large
Load diff
16385
resources/osd/large.mcm
Normal file
16385
resources/osd/large.mcm
Normal file
File diff suppressed because it is too large
Load diff
344
tabs/osd.css
Normal file
344
tabs/osd.css
Normal file
|
@ -0,0 +1,344 @@
|
||||||
|
.tab-osd .info {
|
||||||
|
margin: 10px 0 0 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .info .progressLabel {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 26px;
|
||||||
|
top: 0px;
|
||||||
|
left: 0;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 24px;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
/* text-shadow: 1px 0px 2px rgba(0, 0, 0, 0.9);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
.darkgrey {
|
||||||
|
background-color: #575757;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .spacer_box_title {
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .info {
|
||||||
|
float: left;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progressLabel a {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progressLabel a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress {
|
||||||
|
width: 100%;
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 5px;
|
||||||
|
border: 1px solid silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress::-webkit-progress-bar {
|
||||||
|
background-color: #4f4f4f;
|
||||||
|
border-radius: 4px;
|
||||||
|
box-shadow: inset 0px 0px 5px #2f2f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress::-webkit-progress-value {
|
||||||
|
background-color: #F86008;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress.valid::-webkit-progress-bar {
|
||||||
|
background-color: #56ac1d;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress.valid::-webkit-progress-value {
|
||||||
|
background-color: #56ac1d;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress.invalid::-webkit-progress-bar {
|
||||||
|
background-color: #A62E32;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info .progress.invalid::-webkit-progress-value {
|
||||||
|
background-color: #A62E32;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd ul li {
|
||||||
|
list-style: initial;
|
||||||
|
list-style-type: circle;
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options {
|
||||||
|
position: relative;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
line-height: 18px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd td {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options label input {
|
||||||
|
float: left;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options label span {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options select {
|
||||||
|
width: 300px;
|
||||||
|
height: 20px;
|
||||||
|
border: 1px solid silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options .releases select {
|
||||||
|
width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .option.releases {
|
||||||
|
margin: 0 0 2px 0;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options .description {
|
||||||
|
position: relative;
|
||||||
|
left: 0px;
|
||||||
|
font-style: italic;
|
||||||
|
color: #818181;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .cf_table td:last-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options .flash_on_connect_wrapper {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .options .manual_baud_rate select {
|
||||||
|
width: 75px;
|
||||||
|
margin-left: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info .title {
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
border-bottom: 1px solid silver;
|
||||||
|
background-color: #3f4241;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info .target {
|
||||||
|
color: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info p {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info p a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info p a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .release_info .notes {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .git_info {
|
||||||
|
display: none;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border: 1px solid silver;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .git_info .title {
|
||||||
|
line-height: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
border-bottom: 1px solid silver;
|
||||||
|
background-color: #3f4241;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .git_info p {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .git_info p a {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .git_info p a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons {
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
margin-top: 10px;
|
||||||
|
bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons a {
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
padding: 0 15px 0 15px;
|
||||||
|
height: 28px;
|
||||||
|
line-height: 28px;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid silver;
|
||||||
|
background-color: #ececec;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons a:hover {
|
||||||
|
background-color: #dedcdc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons a.flash_font.locked {
|
||||||
|
background-color: #b8b8b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons a.flash_font.locked:hover {
|
||||||
|
cursor: default;
|
||||||
|
background-color: #b8b8b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons a.load_remote_file.locked {
|
||||||
|
background-color: #b8b8b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons a.load_remote_file.locked:hover {
|
||||||
|
cursor: default;
|
||||||
|
background-color: #b8b8b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .buttons .back {
|
||||||
|
float: right;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .btn .disabled {
|
||||||
|
cursor: default;
|
||||||
|
color: #fff;
|
||||||
|
background-color: #AFAFAF;
|
||||||
|
border: none;
|
||||||
|
pointer-events: none;
|
||||||
|
text-shadow: none;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .display-layout label {
|
||||||
|
margin: .25em .1em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .display-layout input {
|
||||||
|
margin: .1em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .display-layout input.position{
|
||||||
|
width: 5em;
|
||||||
|
border-bottom: 1px solid #ccc
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .hide {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .note {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .col {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .left {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .preview {
|
||||||
|
background: url(/images/osd-bg-1.png);
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .preview .char {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .preview .char[draggable="true"] {
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .preview .row {
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .content_wrapper {
|
||||||
|
height: calc(100% - 41px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .content_toolbar {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-osd .content_toolbar button {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
padding: 4px 10px !important;
|
||||||
|
font-family: 'open_sanssemibold', Arial;
|
||||||
|
font-size: 9pt !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fontbuttons {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
right: 1em;
|
||||||
|
top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 1055px) , only screen and (max-device-width: 1055px) {
|
||||||
|
.tab-osd .content_wrapper {
|
||||||
|
height: calc(100% - 30px);
|
||||||
|
}
|
||||||
|
}
|
43
tabs/osd.html
Executable file
43
tabs/osd.html
Executable file
|
@ -0,0 +1,43 @@
|
||||||
|
<div class="tab-osd toolbar_fixed_bottom">
|
||||||
|
<div class="content_wrapper">
|
||||||
|
<h1 class="tab_title">
|
||||||
|
OSD
|
||||||
|
<div class="fontbuttons supported hide">
|
||||||
|
<button data-font-file="default">Default</button>
|
||||||
|
<button data-font-file="bold">Bold</button>
|
||||||
|
<button data-font-file="large">Large</button>
|
||||||
|
<button class="load_font_file">Open Font File</button>
|
||||||
|
</div>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div class="unsupported hide">
|
||||||
|
<p class="note">Your flight controller isn't responding to OSD commands. This probably means that it does not have an integrated BetaFlight OSD.</p>
|
||||||
|
<p class="note">Note that some flight controllers have an onboard <a href="https://www.youtube.com/watch?v=ikKH_6SQ-Tk" target="_blank">MinimOSD</a> that can be flashed and configured with <a href="https://github.com/ShikOfTheRa/scarab-osd/releases/latest" target="_blank">scarab-osd</a>, however the MinimOSD cannot be configured through this interface.</p>
|
||||||
|
</div>
|
||||||
|
<div class="supported hide">
|
||||||
|
<div class="display-layout">
|
||||||
|
<div class="col left">
|
||||||
|
<div class="video-types">
|
||||||
|
</div>
|
||||||
|
<div class="display-fields">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col right">
|
||||||
|
<div class="preview">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="font-preview"></div>
|
||||||
|
|
||||||
|
<div class="info"><a name="progressbar"></a>
|
||||||
|
<progress class="progress" value="0" min="0" max="100"></progress>
|
||||||
|
<span class="progressLabel"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="content_toolbar supported hide">
|
||||||
|
<button class="save active">Save Layout</button>
|
||||||
|
<button class="flash_font active">Upload Font</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
660
tabs/osd.js
Executable file
660
tabs/osd.js
Executable file
|
@ -0,0 +1,660 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var SYM = SYM || {};
|
||||||
|
SYM.VOLT = 0x00;
|
||||||
|
SYM.RSSI = 0x01;
|
||||||
|
SYM.AH_RIGHT = 0x02;
|
||||||
|
SYM.AH_LEFT = 0x03;
|
||||||
|
SYM.THR = 0x04;
|
||||||
|
SYM.THR1 = 0x05;
|
||||||
|
SYM.FLY_M = 0x9C;
|
||||||
|
SYM.ON_M = 0x9B;
|
||||||
|
SYM.AH_CENTER_LINE = 0x26;
|
||||||
|
SYM.AH_CENTER_LINE_RIGHT = 0x27;
|
||||||
|
SYM.AH_CENTER = 0x7E;
|
||||||
|
SYM.AH_BAR9_0 = 0x80;
|
||||||
|
SYM.AH_DECORATION = 0x13;
|
||||||
|
SYM.LOGO = 0xA0;
|
||||||
|
|
||||||
|
var FONT = FONT || {};
|
||||||
|
|
||||||
|
FONT.initData = function() {
|
||||||
|
if (FONT.data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FONT.data = {
|
||||||
|
// default font file name
|
||||||
|
loaded_font_file: 'default',
|
||||||
|
// array of arry of image bytes ready to upload to fc
|
||||||
|
characters_bytes: [],
|
||||||
|
// array of array of image bits by character
|
||||||
|
characters: [],
|
||||||
|
// an array of base64 encoded image strings by character
|
||||||
|
character_image_urls: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FONT.constants = {
|
||||||
|
SIZES: {
|
||||||
|
/** NVM ram size for one font char, actual character bytes **/
|
||||||
|
MAX_NVM_FONT_CHAR_SIZE: 54,
|
||||||
|
/** NVM ram field size for one font char, last 10 bytes dont matter **/
|
||||||
|
MAX_NVM_FONT_CHAR_FIELD_SIZE: 64,
|
||||||
|
CHAR_HEIGHT: 18,
|
||||||
|
CHAR_WIDTH: 12,
|
||||||
|
LINE: 30
|
||||||
|
},
|
||||||
|
COLORS: {
|
||||||
|
// black
|
||||||
|
0: 'rgba(0, 0, 0, 1)',
|
||||||
|
// also the value 3, could yield transparent according to
|
||||||
|
// https://www.sparkfun.com/datasheets/BreakoutBoards/MAX7456.pdf
|
||||||
|
1: 'rgba(255, 255, 255, 0)',
|
||||||
|
// white
|
||||||
|
2: 'rgba(255,255,255, 1)'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each line is composed of 8 asci 1 or 0, representing 1 bit each for a total of 1 byte per line
|
||||||
|
*/
|
||||||
|
FONT.parseMCMFontFile = function(data) {
|
||||||
|
var data = data.split("\n");
|
||||||
|
// clear local data
|
||||||
|
FONT.data.characters.length = 0;
|
||||||
|
FONT.data.characters_bytes.length = 0;
|
||||||
|
FONT.data.character_image_urls.length = 0;
|
||||||
|
// make sure the font file is valid
|
||||||
|
if (data.shift().trim() != 'MAX7456') {
|
||||||
|
var msg = 'that font file doesnt have the MAX7456 header, giving up';
|
||||||
|
console.debug(msg);
|
||||||
|
Promise.reject(msg);
|
||||||
|
}
|
||||||
|
var character_bits = [];
|
||||||
|
var character_bytes = [];
|
||||||
|
// hexstring is for debugging
|
||||||
|
FONT.data.hexstring = [];
|
||||||
|
var pushChar = function() {
|
||||||
|
FONT.data.characters_bytes.push(character_bytes);
|
||||||
|
FONT.data.characters.push(character_bits);
|
||||||
|
FONT.draw(FONT.data.characters.length-1);
|
||||||
|
//$log.debug('parsed char ', i, ' as ', character);
|
||||||
|
character_bits = [];
|
||||||
|
character_bytes = [];
|
||||||
|
};
|
||||||
|
for (var i = 0; i < data.length; i++) {
|
||||||
|
var line = data[i];
|
||||||
|
// hexstring is for debugging
|
||||||
|
FONT.data.hexstring.push('0x' + parseInt(line, 2).toString(16));
|
||||||
|
// every 64 bytes (line) is a char, we're counting chars though, which are 2 bits
|
||||||
|
if (character_bits.length == FONT.constants.SIZES.MAX_NVM_FONT_CHAR_FIELD_SIZE * (8 / 2)) {
|
||||||
|
pushChar()
|
||||||
|
}
|
||||||
|
for (var y = 0; y < 8; y = y + 2) {
|
||||||
|
var v = parseInt(line.slice(y, y+2), 2);
|
||||||
|
character_bits.push(v);
|
||||||
|
}
|
||||||
|
character_bytes.push(parseInt(line, 2));
|
||||||
|
}
|
||||||
|
// push the last char
|
||||||
|
pushChar();
|
||||||
|
return FONT.data.characters;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
FONT.openFontFile = function($preview) {
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
chrome.fileSystem.chooseEntry({type: 'openFile', accepts: [{extensions: ['mcm']}]}, function (fileEntry) {
|
||||||
|
FONT.data.loaded_font_file = fileEntry.name;
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error(chrome.runtime.lastError.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileEntry.file(function (file) {
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onloadend = function(e) {
|
||||||
|
if (e.total != 0 && e.total == e.loaded) {
|
||||||
|
FONT.parseMCMFontFile(e.target.result);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error('could not load whole font file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a canvas image with the character on it
|
||||||
|
*/
|
||||||
|
var drawCanvas = function(charAddress) {
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
|
||||||
|
// TODO: do we want to be able to set pixel size? going to try letting the consumer scale the image.
|
||||||
|
var pixelSize = pixelSize || 1;
|
||||||
|
var width = pixelSize * FONT.constants.SIZES.CHAR_WIDTH;
|
||||||
|
var height = pixelSize * FONT.constants.SIZES.CHAR_HEIGHT;
|
||||||
|
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
for (var y = 0; y < height; y++) {
|
||||||
|
for (var x = 0; x < width; x++) {
|
||||||
|
if (!(charAddress in FONT.data.characters)) {
|
||||||
|
console.log('charAddress', charAddress, ' is not in ', FONT.data.characters.length);
|
||||||
|
}
|
||||||
|
var v = FONT.data.characters[charAddress][(y*width)+x];
|
||||||
|
ctx.fillStyle = FONT.constants.COLORS[v];
|
||||||
|
ctx.fillRect(x, y, pixelSize, pixelSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return canvas;
|
||||||
|
};
|
||||||
|
|
||||||
|
FONT.draw = function(charAddress) {
|
||||||
|
var cached = FONT.data.character_image_urls[charAddress];
|
||||||
|
if (!cached) {
|
||||||
|
cached = FONT.data.character_image_urls[charAddress] = drawCanvas(charAddress).toDataURL('image/png');
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
|
};
|
||||||
|
|
||||||
|
FONT.msp = {
|
||||||
|
encode: function(charAddress) {
|
||||||
|
return [charAddress].concat(FONT.data.characters_bytes[charAddress].slice(0,FONT.constants.SIZES.MAX_NVM_FONT_CHAR_SIZE));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FONT.upload = function($progress) {
|
||||||
|
return Promise.mapSeries(FONT.data.characters, function(data, i) {
|
||||||
|
$progress.val((i / FONT.data.characters.length) * 100);
|
||||||
|
return MSP.promise(MSP_codes.MSP_OSD_CHAR_WRITE, FONT.msp.encode(i));
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
return MSP.promise(MSP_codes.MSP_SET_REBOOT);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
FONT.preview = function($el) {
|
||||||
|
$el.empty()
|
||||||
|
for (var i = 0; i < SYM.LOGO; i++) {
|
||||||
|
var url = FONT.data.character_image_urls[i];
|
||||||
|
$el.append('<img src="'+url+'" title="0x'+i.toString(16)+'"></img>');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FONT.symbol = function(hexVal) {
|
||||||
|
return String.fromCharCode(hexVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
var OSD = OSD || {};
|
||||||
|
|
||||||
|
// parsed fc output and output to fc, used by to OSD.msp.encode
|
||||||
|
OSD.initData = function() {
|
||||||
|
OSD.data = {
|
||||||
|
video_system: null,
|
||||||
|
display_items: [],
|
||||||
|
last_positions: {},
|
||||||
|
preview: []
|
||||||
|
};
|
||||||
|
};
|
||||||
|
OSD.initData();
|
||||||
|
|
||||||
|
OSD.constants = {
|
||||||
|
VIDEO_TYPES: [
|
||||||
|
'AUTO',
|
||||||
|
'PAL',
|
||||||
|
'NTSC'
|
||||||
|
],
|
||||||
|
VIDEO_LINES: {
|
||||||
|
PAL: 16,
|
||||||
|
NTSC: 13
|
||||||
|
},
|
||||||
|
VIDEO_BUFFER_CHARS: {
|
||||||
|
PAL: 480,
|
||||||
|
NTSC: 390
|
||||||
|
},
|
||||||
|
AHISIDEBARWIDTHPOSITION: 7,
|
||||||
|
AHISIDEBARHEIGHTPOSITION: 3,
|
||||||
|
// order matters, so these are going in an array... pry could iterate the example map instead
|
||||||
|
DISPLAY_FIELDS: [
|
||||||
|
{
|
||||||
|
name: 'MAIN_BATT_VOLTAGE',
|
||||||
|
default_position: -29,
|
||||||
|
positionable: true,
|
||||||
|
preview: FONT.symbol(SYM.VOLT) + '16.8'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'RSSI_VALUE',
|
||||||
|
default_position: -59,
|
||||||
|
positionable: true,
|
||||||
|
preview: FONT.symbol(SYM.RSSI) + '99'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TIMER',
|
||||||
|
default_position: -39,
|
||||||
|
positionable: true,
|
||||||
|
preview: FONT.symbol(SYM.ON_M) + ' 11:11'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'THROTTLE_POS',
|
||||||
|
default_position: -9,
|
||||||
|
positionable: true,
|
||||||
|
preview: FONT.symbol(SYM.THR) + FONT.symbol(SYM.THR1) + ' 0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'CPU_LOAD',
|
||||||
|
default_position: 26,
|
||||||
|
positionable: true,
|
||||||
|
preview: '15'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VTX_CHANNEL',
|
||||||
|
default_position: 1,
|
||||||
|
positionable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'VOLTAGE_WARNING',
|
||||||
|
default_position: -80,
|
||||||
|
positionable: true,
|
||||||
|
preview: 'LOW VOLTAGE'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ARMED',
|
||||||
|
default_position: -107,
|
||||||
|
positionable: true,
|
||||||
|
preview: 'ARMED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'DISARMED',
|
||||||
|
default_position: -109,
|
||||||
|
positionable: true,
|
||||||
|
preview: 'DISARMED'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'ARTIFICIAL_HORIZON',
|
||||||
|
default_position: -1,
|
||||||
|
positionable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'HORIZON_SIDEBARS',
|
||||||
|
default_position: -1,
|
||||||
|
positionable: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
OSD.updateDisplaySize = function() {
|
||||||
|
var video_type = OSD.constants.VIDEO_TYPES[OSD.data.video_system];
|
||||||
|
if (video_type == 'AUTO') {
|
||||||
|
video_type = 'PAL';
|
||||||
|
}
|
||||||
|
// compute the size
|
||||||
|
OSD.data.display_size = {
|
||||||
|
x: 30,
|
||||||
|
y: OSD.constants.VIDEO_LINES[video_type],
|
||||||
|
total: null
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
OSD.msp = {
|
||||||
|
encodeOther: function() {
|
||||||
|
return [-1, OSD.data.video_system];
|
||||||
|
},
|
||||||
|
encode: function(display_item) {
|
||||||
|
return [
|
||||||
|
display_item.index,
|
||||||
|
specificByte(display_item.position, 0),
|
||||||
|
specificByte(display_item.position, 1)
|
||||||
|
];
|
||||||
|
},
|
||||||
|
// Currently only parses MSP_MAX_OSD responses, add a switch on payload.code if more codes are handled
|
||||||
|
decode: function(payload) {
|
||||||
|
var view = payload.data;
|
||||||
|
var d = OSD.data;
|
||||||
|
d.compiled_in = view.getUint8(0, 1);
|
||||||
|
d.video_system = view.getUint8(1, 1);
|
||||||
|
d.display_items = [];
|
||||||
|
// start at the offset from the other fields
|
||||||
|
for (var i = 2; i < view.byteLength; i = i + 2) {
|
||||||
|
var v = view.getInt16(i, 1)
|
||||||
|
var j = d.display_items.length;
|
||||||
|
var c = OSD.constants.DISPLAY_FIELDS[j];
|
||||||
|
d.display_items.push({
|
||||||
|
name: c.name,
|
||||||
|
index: j,
|
||||||
|
position: v,
|
||||||
|
positionable: c.positionable,
|
||||||
|
preview: c.preview
|
||||||
|
});
|
||||||
|
}
|
||||||
|
OSD.updateDisplaySize();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
OSD.GUI = {};
|
||||||
|
OSD.GUI.preview = {
|
||||||
|
onDragStart: function(e) {
|
||||||
|
var ev = e.originalEvent;
|
||||||
|
ev.dataTransfer.setData("text/plain", ev.target.id);
|
||||||
|
ev.dataTransfer.setDragImage($(this).data('field').preview_img, 6, 9);
|
||||||
|
},
|
||||||
|
onDragOver: function(e) {
|
||||||
|
var ev = e.originalEvent;
|
||||||
|
ev.preventDefault();
|
||||||
|
ev.dataTransfer.dropEffect = "move"
|
||||||
|
$(this).css({
|
||||||
|
background: 'rgba(0,0,0,.5)'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onDragLeave: function(e) {
|
||||||
|
// brute force unstyling on drag leave
|
||||||
|
$(this).removeAttr('style');
|
||||||
|
},
|
||||||
|
onDrop: function(e) {
|
||||||
|
var ev = e.originalEvent;
|
||||||
|
var position = $(this).removeAttr('style').data('position');
|
||||||
|
if (position > OSD.data.display_size.total/2) {
|
||||||
|
position = position - OSD.data.display_size.total;
|
||||||
|
}
|
||||||
|
var field_id = parseInt(ev.dataTransfer.getData('text').split('field-')[1])
|
||||||
|
$('input.'+field_id+'.position').val(position).change();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TABS.osd = {};
|
||||||
|
TABS.osd.initialize = function (callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (GUI.active_tab != 'osd') {
|
||||||
|
GUI.active_tab = 'osd';
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#content').load("./tabs/osd.html", function () {
|
||||||
|
// translate to user-selected language
|
||||||
|
localize();
|
||||||
|
|
||||||
|
// 2 way binding... sorta
|
||||||
|
function updateOsdView() {
|
||||||
|
// ask for the OSD config data
|
||||||
|
MSP.promise(MSP_codes.MSP_OSD_CONFIG)
|
||||||
|
.then(function(info) {
|
||||||
|
if (!info.length) {
|
||||||
|
$('.unsupported').fadeIn();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$('.supported').fadeIn();
|
||||||
|
OSD.msp.decode(info);
|
||||||
|
// video mode
|
||||||
|
var $videoTypes = $('.video-types').empty();
|
||||||
|
for (var i = 0; i < OSD.constants.VIDEO_TYPES.length; i++) {
|
||||||
|
var type = OSD.constants.VIDEO_TYPES[i];
|
||||||
|
var $checkbox = $('<label/>').append($('<input name="video_system" type="radio"/>'+type+'</label>')
|
||||||
|
.prop('checked', i === OSD.data.video_system)
|
||||||
|
.data('type', type)
|
||||||
|
.data('type', i)
|
||||||
|
);
|
||||||
|
$videoTypes.append($checkbox);
|
||||||
|
}
|
||||||
|
$videoTypes.find(':radio').click(function(e) {
|
||||||
|
OSD.data.video_system = $(this).data('type');
|
||||||
|
MSP.promise(MSP_codes.MSP_SET_OSD_CONFIG, OSD.msp.encodeOther())
|
||||||
|
.then(function() {
|
||||||
|
updateOsdView();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// display fields on/off and position
|
||||||
|
var $displayFields = $('.display-fields').empty();
|
||||||
|
for (let field of OSD.data.display_items) {
|
||||||
|
var checked = (-1 != field.position) ? 'checked' : '';
|
||||||
|
var $field = $('<div class="display-field"/>');
|
||||||
|
$field.append(
|
||||||
|
$('<input type="checkbox" name="'+field.name+'" class="togglesmall"></input>')
|
||||||
|
.data('field', field)
|
||||||
|
.attr('checked', field.position != -1)
|
||||||
|
.change(function(e) {
|
||||||
|
var field = $(this).data('field');
|
||||||
|
var $position = $(this).parent().find('.position.'+field.name);
|
||||||
|
if (field.position == -1) {
|
||||||
|
$position.show();
|
||||||
|
field.position = OSD.data.last_positions[field.name]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$position.hide();
|
||||||
|
OSD.data.last_positions[field.name] = field.position
|
||||||
|
field.position = -1
|
||||||
|
}
|
||||||
|
MSP.promise(MSP_codes.MSP_SET_OSD_CONFIG, OSD.msp.encode(field))
|
||||||
|
.then(function() {
|
||||||
|
updateOsdView();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
$field.append('<label for="'+field.name+'">'+field.name+'</label>');
|
||||||
|
if (field.positionable && field.position != -1) {
|
||||||
|
$field.append(
|
||||||
|
$('<input type="number" class="'+field.index+' position"></input>')
|
||||||
|
.data('field', field)
|
||||||
|
.val(field.position)
|
||||||
|
.change($.debounce(250, function(e) {
|
||||||
|
var field = $(this).data('field');
|
||||||
|
var position = parseInt($(this).val());
|
||||||
|
field.position = position;
|
||||||
|
MSP.promise(MSP_codes.MSP_SET_OSD_CONFIG, OSD.msp.encode(field))
|
||||||
|
.then(function() {
|
||||||
|
updateOsdView();
|
||||||
|
});
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$displayFields.append($field);
|
||||||
|
}
|
||||||
|
GUI.switchery();
|
||||||
|
// buffer the preview
|
||||||
|
OSD.data.preview = [];
|
||||||
|
OSD.data.display_size.total = OSD.data.display_size.x * OSD.data.display_size.y;
|
||||||
|
// clear the buffer
|
||||||
|
for(var i = 0; i < OSD.data.display_size.total; i++) {
|
||||||
|
OSD.data.preview.push([null, ' '.charCodeAt(0)]);
|
||||||
|
}
|
||||||
|
// draw all the displayed items and the drag and drop preview images
|
||||||
|
for(let field of OSD.data.display_items) {
|
||||||
|
if (!field.preview || field.position == -1) { continue; }
|
||||||
|
var j = (field.position >= 0) ? field.position : field.position + OSD.data.display_size.total;
|
||||||
|
// create the preview image
|
||||||
|
field.preview_img = new Image();
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
var ctx = canvas.getContext("2d");
|
||||||
|
// fill the screen buffer
|
||||||
|
for(var i = 0; i < field.preview.length; i++) {
|
||||||
|
var charCode = field.preview.charCodeAt(i);
|
||||||
|
OSD.data.preview[j++] = [field, charCode];
|
||||||
|
// draw the preview
|
||||||
|
var img = new Image();
|
||||||
|
img.src = FONT.draw(charCode);
|
||||||
|
ctx.drawImage(img, i*12, 0);
|
||||||
|
}
|
||||||
|
field.preview_img.src = canvas.toDataURL('image/png');
|
||||||
|
}
|
||||||
|
// logo
|
||||||
|
var x = 160;
|
||||||
|
for (var i = 1; i < 5; i++) {
|
||||||
|
for (var j = 3; j < 27; j++)
|
||||||
|
OSD.data.preview[i * 30 + j] = [{name: 'LOGO', positionable: false}, x++];
|
||||||
|
}
|
||||||
|
var centerishPosition = 194;
|
||||||
|
// artificial horizon
|
||||||
|
if ($('input[name="ARTIFICIAL_HORIZON"]').prop('checked')) {
|
||||||
|
for (var i = 0; i < 9; i++) {
|
||||||
|
OSD.data.preview[centerishPosition - 4 + i] = SYM.AH_BAR9_0 + 4;
|
||||||
|
}
|
||||||
|
OSD.data.preview[centerishPosition - 1] = SYM.AH_CENTER_LINE;
|
||||||
|
OSD.data.preview[centerishPosition + 1] = SYM.AH_CENTER_LINE_RIGHT;
|
||||||
|
OSD.data.preview[centerishPosition] = SYM.AH_CENTER;
|
||||||
|
}
|
||||||
|
// sidebars
|
||||||
|
if ($('input[name="HORIZON_SIDEBARS"]').prop('checked')) {
|
||||||
|
var hudwidth = OSD.constants.AHISIDEBARWIDTHPOSITION;
|
||||||
|
var hudheight = OSD.constants.AHISIDEBARHEIGHTPOSITION;
|
||||||
|
for (var i = -hudheight; i <= hudheight; i++) {
|
||||||
|
OSD.data.preview[centerishPosition - hudwidth + (i * FONT.constants.SIZES.LINE)] = SYM.AH_DECORATION;
|
||||||
|
OSD.data.preview[centerishPosition + hudwidth + (i * FONT.constants.SIZES.LINE)] = SYM.AH_DECORATION;
|
||||||
|
}
|
||||||
|
// AH level indicators
|
||||||
|
OSD.data.preview[centerishPosition-hudwidth+1] = SYM.AH_LEFT;
|
||||||
|
OSD.data.preview[centerishPosition+hudwidth-1] = SYM.AH_RIGHT;
|
||||||
|
}
|
||||||
|
// render
|
||||||
|
var $preview = $('.display-layout .preview').empty();
|
||||||
|
var $row = $('<div class="row"/>');
|
||||||
|
for(var i = 0; i < OSD.data.display_size.total;) {
|
||||||
|
var charCode = OSD.data.preview[i];
|
||||||
|
if (typeof charCode === 'object') {
|
||||||
|
var field = OSD.data.preview[i][0];
|
||||||
|
var charCode = OSD.data.preview[i][1];
|
||||||
|
}
|
||||||
|
var $img = $('<div class="char"><img src='+FONT.draw(charCode)+'></img></div>')
|
||||||
|
.on('dragover', OSD.GUI.preview.onDragOver)
|
||||||
|
.on('dragleave', OSD.GUI.preview.onDragLeave)
|
||||||
|
.on('drop', OSD.GUI.preview.onDrop)
|
||||||
|
.data('position', i);
|
||||||
|
if (field && field.positionable) {
|
||||||
|
$img
|
||||||
|
.attr('id', 'field-'+field.index)
|
||||||
|
.data('field', field)
|
||||||
|
.prop('draggable', true)
|
||||||
|
.on('dragstart', OSD.GUI.preview.onDragStart);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
}
|
||||||
|
$row.append($img);
|
||||||
|
if (++i % OSD.data.display_size.x == 0) {
|
||||||
|
$preview.append($row);
|
||||||
|
$row = $('<div class="row"/>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$('button.save').click(function() {
|
||||||
|
var self = this;
|
||||||
|
MSP.promise(MSP_codes.MSP_EEPROM_WRITE);
|
||||||
|
var oldText = $(this).text();
|
||||||
|
$(this).html("Saved");
|
||||||
|
setTimeout(function () {
|
||||||
|
$(self).html(oldText);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
// font preview window
|
||||||
|
var $preview = $('.font-preview');
|
||||||
|
|
||||||
|
// init structs once, also clears current font
|
||||||
|
FONT.initData();
|
||||||
|
|
||||||
|
var $fontPicker = $('.fontbuttons button');
|
||||||
|
$fontPicker.click(function(e) {
|
||||||
|
if (!$(this).data('font-file')) { return; }
|
||||||
|
$fontPicker.removeClass('active');
|
||||||
|
$(this).addClass('active');
|
||||||
|
$.get('/resources/osd/' + $(this).data('font-file') + '.mcm', function(data) {
|
||||||
|
FONT.parseMCMFontFile(data);
|
||||||
|
FONT.preview($preview);
|
||||||
|
updateOsdView();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// load the first font when we change tabs
|
||||||
|
$fontPicker.first().click();
|
||||||
|
|
||||||
|
$('button.load_font_file').click(function() {
|
||||||
|
$fontPicker.removeClass('active');
|
||||||
|
FONT.openFontFile().then(function() {
|
||||||
|
FONT.preview($preview);
|
||||||
|
updateOsdView();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// font upload
|
||||||
|
$('button.flash_font').click(function () {
|
||||||
|
if (!GUI.connect_lock) { // button disabled while flashing is in progress
|
||||||
|
$('.progressLabel').text('Uploading...');
|
||||||
|
FONT.upload($('.progress').val(0)).then(function() {
|
||||||
|
var msg = 'Uploaded all ' + FONT.data.characters.length + ' characters';
|
||||||
|
console.log(msg);
|
||||||
|
$('.progressLabel').text(msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', 'span.progressLabel a.save_font', function () {
|
||||||
|
chrome.fileSystem.chooseEntry({type: 'saveFile', suggestedName: 'baseflight', accepts: [{extensions: ['mcm']}]}, function (fileEntry) {
|
||||||
|
if (chrome.runtime.lastError) {
|
||||||
|
console.error(chrome.runtime.lastError.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chrome.fileSystem.getDisplayPath(fileEntry, function (path) {
|
||||||
|
console.log('Saving firmware to: ' + path);
|
||||||
|
|
||||||
|
// check if file is writable
|
||||||
|
chrome.fileSystem.isWritableEntry(fileEntry, function (isWritable) {
|
||||||
|
if (isWritable) {
|
||||||
|
var blob = new Blob([intel_hex], {type: 'text/plain'});
|
||||||
|
|
||||||
|
fileEntry.createWriter(function (writer) {
|
||||||
|
var truncated = false;
|
||||||
|
|
||||||
|
writer.onerror = function (e) {
|
||||||
|
console.error(e);
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.onwriteend = function() {
|
||||||
|
if (!truncated) {
|
||||||
|
// onwriteend will be fired again when truncation is finished
|
||||||
|
truncated = true;
|
||||||
|
writer.truncate(blob.size);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
writer.write(blob);
|
||||||
|
}, function (e) {
|
||||||
|
console.error(e);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('You don\'t have write permissions for this file, sorry.');
|
||||||
|
GUI.log('You don\'t have <span style="color: red">write permissions</span> for this file');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).keypress(function (e) {
|
||||||
|
if (e.which == 13) { // enter
|
||||||
|
// Trigger regular Flashing sequence
|
||||||
|
$('a.flash_font').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
GUI.content_ready(callback);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
TABS.osd.cleanup = function (callback) {
|
||||||
|
PortHandler.flush_callbacks();
|
||||||
|
|
||||||
|
// unbind "global" events
|
||||||
|
$(document).unbind('keypress');
|
||||||
|
$(document).off('click', 'span.progressLabel a');
|
||||||
|
|
||||||
|
if (callback) callback();
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue