Merge pull request #57 from nathantsoi/feature/osd

Feature/osd
10.3.x-maintenance
Nathan 2016-06-29 20:53:06 -07:00 committed by GitHub
commit 50b46c3c97
16 changed files with 50324 additions and 8 deletions

View File

@ -93,6 +93,9 @@
"tabTransponder": {
"message": "Race Transponder"
},
"tabOsd": {
"message": "OSD"
},
"tabGPS": {
"message": "GPS"
},

25
images/icons/icon_osd.svg Normal file
View 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.0 KiB

View 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.0 KiB

BIN
images/osd-bg-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

View File

@ -20,6 +20,7 @@ var GUI_control = function () {
this.defaultAllowedTabsWhenConnected = [
'failsafe',
'transponder',
'osd',
'adjustments',
'auxiliary',
'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) {
var switchery = new Switchery(elem, {
size: 'small',
@ -275,6 +275,11 @@ GUI_control.prototype.content_ready = function (callback) {
});
$(elem).removeClass('togglemedium');
});
};
GUI_control.prototype.content_ready = function (callback) {
this.switchery();
if (CONFIGURATOR.connectionValid) {
// Build link to in-use CF version documentation

View 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);

View File

@ -40,7 +40,12 @@ var MSP_codes = {
MSP_SET_BLACKBOX_CONFIG: 81,
MSP_TRANSPONDER_CONFIG: 82,
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_SET_PID_ADVANCED_CONFIG: 91,
MSP_FILTER_CONFIG: 92,
@ -1063,6 +1068,20 @@ var MSP = {
case MSP_codes.MSP_SET_FAILSAFE_CONFIG:
console.log('Failsafe config saved');
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:
console.log('Unknown code detected: ' + code);
} else {

View File

@ -798,6 +798,14 @@ li.active .ic_transponder {
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 */
.ic_failsafe {
@ -1439,7 +1447,7 @@ dialog {
/* fixing padding for all Tabs*/
.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-ports, .tab-receiver, .tab-sensors, .tab-servos {
.tab-ports, .tab-receiver, .tab-sensors, .tab-servos, .tab-osd {
height: 100%;
position: relative;
}
@ -1574,9 +1582,6 @@ dialog {
}
/* Battery element styling*/
#quad-status_wrapper {
@ -1686,6 +1691,22 @@ dialog {
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) {
@ -1801,4 +1822,4 @@ input {
overflow-y: auto;
}
}
}

View File

@ -26,6 +26,7 @@
<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/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="./css/opensans_webfontkit/fonts.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/switchery/switchery.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/port_handler.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/firmware_flasher.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>
<title></title>
</head>
@ -215,6 +218,7 @@
<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_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_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>

View File

@ -126,6 +126,9 @@ $(document).ready(function () {
case 'transponder':
TABS.transponder.initialize(content_ready);
break;
case 'osd':
TABS.osd.initialize(content_ready);
break;
case 'setup':
TABS.setup.initialize(content_ready);
break;

16385
resources/osd/bold.mcm Normal file

File diff suppressed because it is too large Load Diff

16385
resources/osd/default.mcm Normal file

File diff suppressed because it is too large Load Diff

16385
resources/osd/large.mcm Normal file

File diff suppressed because it is too large Load Diff

344
tabs/osd.css Normal file
View 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
View 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
View 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();
};