diff --git a/app/config.xml b/app/config.xml
index cb85e75..18ccb9e 100644
--- a/app/config.xml
+++ b/app/config.xml
@@ -22,8 +22,10 @@
+
+
+
-
diff --git a/app/package-lock.json b/app/package-lock.json
index fb5edb4..59fe73e 100644
--- a/app/package-lock.json
+++ b/app/package-lock.json
@@ -4,15 +4,10 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
- "android-versions": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/android-versions/-/android-versions-1.2.1.tgz",
- "integrity": "sha512-k6zlrtWbJ3tx1ZsyyJ0Bo3r6cqPA3JUnFGv7pnIaLr1XVxSi2Tcem2lg3kBebFp27v/A40tZqdlouPyakpyKrw=="
- },
"cordova-android": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/cordova-android/-/cordova-android-6.3.0.tgz",
- "integrity": "sha1-2lQYQz0lx1pZd7QoJEu+Q30BKNI=",
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/cordova-android/-/cordova-android-6.4.0.tgz",
+ "integrity": "sha1-VK6NpXKKjX5e/MYXLT3MoXvH/n0=",
"requires": {
"android-versions": "1.2.1",
"cordova-common": "2.1.0",
@@ -27,6 +22,10 @@
"version": "1.1.0",
"bundled": true
},
+ "android-versions": {
+ "version": "1.2.1",
+ "bundled": true
+ },
"ansi": {
"version": "0.3.1",
"bundled": true
diff --git a/app/package.json b/app/package.json
index b98c52b..b896cc6 100644
--- a/app/package.json
+++ b/app/package.json
@@ -10,7 +10,7 @@
"author": "M McWilliams",
"license": "MIT",
"dependencies": {
- "cordova-android": "^6.3.0",
+ "cordova-android": "^6.4.0",
"cordova-ios": "^4.5.4",
"cordova-plugin-ble-central": "^1.1.4",
"cordova-plugin-compat": "^1.2.0",
@@ -30,8 +30,8 @@
}
},
"platforms": [
- "ios",
- "android"
+ "android",
+ "ios"
]
}
}
\ No newline at end of file
diff --git a/app/www/index.html b/app/www/index.html
index 7a63684..7298e13 100644
--- a/app/www/index.html
+++ b/app/www/index.html
@@ -4,12 +4,15 @@
-
+
+
BLUETOOTH
+
-
@@ -92,6 +99,7 @@
+
diff --git a/app/www/static/css/index.css b/app/www/static/css/index.css
index 730c2c5..d64fc43 100644
--- a/app/www/static/css/index.css
+++ b/app/www/static/css/index.css
@@ -46,6 +46,12 @@ body{
.page.selected{
display: block;
}
+.ble{
+ display: none;
+}
+.ble.active{
+ display: block;
+}
h2{
font-size: 18px;
text-align: center;
@@ -144,7 +150,8 @@ button{
padding: 5px 0;
text-align: center;
}
-button:focus{
+button:focus,
+button.focus{
background-color: #20ce45;
border-color: #20ce45;
color: #212121;
@@ -162,7 +169,7 @@ button:focus{
right: 10%;
}
.label{
- text-align: center;
+ /*text-align: center;*/
color: #666;
margin-top: 6px;
margin-bottom: 9px;
@@ -244,3 +251,26 @@ footer > div.selected{
#compile{
margin-top: 20px;
}
+
+#seq{
+ margin-top: 40px;
+}
+
+#overlay{
+ position: fixed;
+ z-index: 2001;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.5);
+ display: none;
+}
+#overlay.active{
+ display: block;
+}
+#spinner{
+ margin-top: 200px;
+}
diff --git a/app/www/static/js/index.js b/app/www/static/js/index.js
index d7c73a0..1c8de02 100644
--- a/app/www/static/js/index.js
+++ b/app/www/static/js/index.js
@@ -35,7 +35,6 @@ var app = {
// 'pause', 'resume', etc.
onDeviceReady: function() {
mobile.init();
- getState();
},
onDeviceResume : function () {
getState();
diff --git a/app/www/static/js/intval.core.js b/app/www/static/js/intval.core.js
index c6335bc..81dee9c 100644
--- a/app/www/static/js/intval.core.js
+++ b/app/www/static/js/intval.core.js
@@ -12,6 +12,7 @@ const STATE = {
delayScale : 'ms',
counter : 0
};
+
//functions
window.frame = null;
window.getState = null;
@@ -93,7 +94,7 @@ var setExposureScale = function () {
};
var setDelayScale = function () {
- const scale = document.getElementById('scale').value;
+ const scale = document.getElementById('delayScale').value;
const elem = document.getElementById('delay');
if (scale === 'ms') {
elem.value = STATE.delay;
@@ -138,6 +139,36 @@ var unsetPages = function () {
}
};
+
+var setState = function (res) {
+ let exposure;
+ let exposureScale;
+ let delayScale;
+
+ if (res.frame.dir !== true) {
+ document.getElementById('dir').checked = true;
+ STATE.dir = res.frame.dir;
+ setDirLabel(false);
+ }
+ document.getElementById('counter').value = res.counter;
+ STATE.counter = res.counter;
+ //Exposure
+ if (res.frame.exposure === 0) {
+ res.frame.exposure = BOLEX.expected;
+ }
+ STATE.exposure = res.frame.exposure;
+ exposure = shutter(STATE.exposure);
+ exposureScale = scaleAuto(STATE.exposure);
+
+ document.getElementById('str').value = exposure.str;
+ document.getElementById('scale').value = exposureScale;
+ setExposureScale();
+
+ STATE.delay = res.frame.delay;
+ delayScale = scaleAuto(STATE.delay);
+ document.getElementById('delayScale').value = delayScale;
+ setDelayScale();
+};
var appPage = function () {
unsetPages();
document.getElementById('app').classList.add('selected');
@@ -154,6 +185,44 @@ var mscriptPage = function () {
document.getElementById('mscriptIcon').classList.add('selected');
editor.cm.refresh();
};
+var spinnerInit = function () {
+ const spinnerOpts = {
+ lines: 13 // The number of lines to draw
+ , length: 33 // The length of each line
+ , width: 11 // The line thickness
+ , radius: 30 // The radius of the inner circle
+ , scale: 0.5 // Scales overall size of the spinner
+ , corners: 1 // Corner roundness (0..1)
+ , color: '#fff' // #rgb or #rrggbb or array of colors
+ , opacity: 0.25 // Opacity of the lines
+ , rotate: 0 // The rotation offset
+ , direction: 1 // 1: clockwise, -1: counterclockwise
+ , speed: 1 // Rounds per second
+ , trail: 60 // Afterglow percentage
+ , fps: 20 // Frames per second when using setTimeout() as a fallback for CSS
+ , zIndex: 2e9 // The z-index (defaults to 2000000000)
+ , className: 'spinner' // The CSS class to assign to the spinner
+ , top: '50%' // Top position relative to parent
+ , left: '50%' // Left position relative to parent
+ , shadow: true // Whether to render a shadow
+ , hwaccel: true // Whether to use hardware acceleration
+ , position: 'relative' // Element positioning
+ };
+ const target = document.getElementById('spinner');
+ const spinner = new Spinner(spinnerOpts).spin(target);
+};
+var spinnerShow = function () {
+ const elem = document.getElementById('overlay');
+ if (!elem.classList.contains('active')) {
+ elem.classList.add('active');
+ }
+};
+var spinnerHide = function () {
+ const elem = document.getElementById('overlay');
+ if (elem.classList.contains('active')) {
+ elem.classList.remove('active');
+ }
+}
var isNumeric = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
\ No newline at end of file
diff --git a/app/www/static/js/intval.mobile.js b/app/www/static/js/intval.mobile.js
index a067145..46669ce 100644
--- a/app/www/static/js/intval.mobile.js
+++ b/app/www/static/js/intval.mobile.js
@@ -7,27 +7,72 @@ mobile.ble = {
SERVICE_ID : '149582bd-d49d-4b5c-acd1-1ae503d09e7a',
CHAR_ID : '47bf69fb-f62f-4ef8-9be8-eb727a54fae4', //general data
WIFI_ID : '3fe7d9cf-7bd2-4ff0-97c5-ebe87288c2cc', //wifi only
- DEVICES : []
+ devices : [],
+ device : {},
+ connected : false,
+ active : false
};
mobile.ble.scan = function () {
- ble.scan([], 5, mobile.ble.onDiscover, BLE.onError);
+ spinnerShow();
+ ble.scan([], 5, mobile.ble.onDiscover, mobile.ble.onError);
+ mobile.ble.devices = [];
+ setTimeout(() => {
+ if (!mobile.ble.connected) {
+ ble.stopScan(() => {}, mobile.ble.onError);
+ spinnerHide();
+ alert('No INTVAL devices found.');
+ }
+ }, 5000)
};
mobile.ble.onDiscover = function (device) {
- console.dir(device);
- mobile.ble.connect(device.id);
+ if (device && device.name && device.name === 'intval3') {
+ console.log('BLE - Discovered INTVAL');
+ console.dir(device);
+ mobile.ble.devices.push(device);
+ if (!mobile.ble.connected) {
+ mobile.ble.connect(device);
+ }
+ } else {
+ //console.log(`BLE - Discovered Other ${device.id}`);
+ }
}
-mobile.ble.connect = function (deviceId) {
- ble.connect(deviceId, function (peripheral) {
- mobile.ble.onConnect(peripheral, deviceId);
+mobile.ble.connect = function (device) {
+ console.log(`BLE - Connecting to ${device.id}`)
+ ble.connect(device.id, (peripheral) => {
+ mobile.ble.onConnect(peripheral, device);
}, mobile.ble.onError);
};
-mobile.ble.onConnect = function (peripheral, deviceId) {
+mobile.ble.onConnect = function (peripheral, device) {
+ spinnerHide();
+ ble.stopScan(() => {}, moble.ble.onError);
+ console.log(`BLE - Connected to ${device.id}`);
console.log(peripheral);
- console.log(deviceId);
+ console.dir(device);
+ mobile.ble.device = device;
+ mobile.ble.connected = true;
+
+ getState();
+};
+
+mobile.ble.disconnect = function () {
+ let device
+ if (!mobile.ble.connected) {
+ console.warn('Not connected to any device')
+ return false
+ }
+ device = mobile.ble.device
+ console.log(`BLE - Disconnecting from ${device.id}`)
+ ble.disconnect(device.id, mobile.ble.onDisconnect, mobile.ble.onDisconnect);
+};
+
+mobile.ble.onDisconnect = function (res) {
+ console.log(`BLE - Disconnected from ${res}`);
+ mobile.ble.connected = false;
+ mobile.ble.device = {};
};
mobile.ble.onError = function (err) {
@@ -35,15 +80,154 @@ mobile.ble.onError = function (err) {
};
mobile.init = function () {
- frame = mobile.frame;
- getState = mobile.getState;
- setDir = mobile.setDir;
- setExposure = mobile.setExposure;
- setCounter = mobile.setCounter;
+ const bleInputs = document.querySelectorAll('.ble')
+
+ window.frame = mobile.frame;
+ window.getState = mobile.getState;
+ window.setDir = mobile.setDir;
+ window.setExposure = mobile.setExposure;
+ window.setDelay = mobile.setDelay;
+ window.setCounter = mobile.setCounter;
+
+ //show ble-specific fields in settings
+ for (let i of bleInputs) {
+ i.classList.add('active');
+ }
+ spinnerInit();
+ mobile.ble.scan();
};
-mobile.frame = function () {};
-mobile.getState = function () {};
-mobile.setDir = function () {};
-mobile.setExposure = function () {};
-mobile.setCounter = function () {};
\ No newline at end of file
+mobile.getState = function () {
+ if (!mobile.ble.connected) {
+ //
+ }
+ ble.read(mobile.ble.device.id,
+ mobile.ble.SERVICE_ID,
+ mobile.ble.CHAR_ID,
+ mobile.stateSuccess,
+ mobile.ble.onError);
+};
+mobile.stateSuccess = function (data) {
+ let str = bytesToString(data);
+ let res = JSON.parse(str);
+ setState(res)
+};
+
+mobile.frame = function () {
+ const opts = {
+ type : 'frame'
+ };
+ if (!mobile.ble.connected) {
+ return alert('Not connected to an INTVAL device.');
+ }
+ if (mobile.ble.active) {
+ return false;
+ }
+ ble.write(mobile.ble.device.id,
+ mobile.ble.SERVICE_ID,
+ mobile.ble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.frameSuccess,
+ mobile.ble.onError);
+ document.getElementById('frame').classList.add('focus');
+ mobile.ble.active = true;
+};
+
+
+mobile.frameSuccess = function () {
+ console.log('Frame finished, getting state.');
+ mobile.ble.active = false;
+ document.getElementById('frame').classList.remove('focus');
+ mobile.getState();
+}
+mobile.setDir = function () {
+ const opts = {
+ type : 'dir',
+ dir : !document.getElementById('dir').checked
+ };
+
+ ble.write(mobile.ble.device.id,
+ mobile.ble.SERVICE_ID,
+ mobile.ble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.dirSuccess,
+ mobile.ble.onError);
+};
+mobile.dirSuccess = function () {
+ console.log('Set direction');
+ mobile.getState();
+};
+mobile.setExposure = function () {
+ let exposure = document.getElementById('exposure').value;
+ let scaledExposure;
+ let opts = {
+ type : 'exposure'
+ };
+ if (exposure === '' || exposure === null) {
+ exposure = 0;
+ }
+ scaledExposure = scaleTime(exposure, STATE.scale);
+ opts.exposure = scaledExposure;
+ ble.write(mobile.ble.device.id,
+ mobile.ble.SERVICE_ID,
+ mobile.ble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.exposureSuccess,
+ mobile.ble.onError);
+};
+mobile.exposureSuccess = function () {
+ console.log('Set exposure');
+ mobile.getState();
+};
+
+mobile.setDelay = function () {
+ const delay = document.getElementById('delay').value;
+ const scaledDelay = scaleTime(delay, STATE.delayScale)
+ let opts = {
+ type : 'delay',
+ delay : scaledDelay
+ };
+ ble.write(mobile.ble.device.id,
+ mobile.ble.SERVICE_ID,
+ mobile.ble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.delaySuccess,
+ mobile.ble.onError);
+}
+
+mobile.delaySuccess = function () {
+ console.log('Set delay')
+ mobile.getState();
+};
+
+mobile.setCounter = function () {
+ const counter = document.getElementById('counter').value;
+ const change = prompt(`Change counter value?`, counter);
+ if (change === null || !isNumeric(change)) return false;
+ let opts = {
+ type : 'counter',
+ counter : change
+ };
+ ble.write(mobile.ble.device.id,
+ mobile.ble.SERVICE_ID,
+ mobile.ble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.counterSuccess,
+ mobile.ble.onError);
+};
+
+mobile.counterSuccess = function () {
+ console.log('Set counter');
+ mobile.getState();
+};
+
+function bytesToString (buffer) {
+ return String.fromCharCode.apply(null, new Uint8Array(buffer));
+};
+function stringToBytes(string) {
+ var array = new Uint8Array(string.length);
+ for (var i = 0, l = string.length; i < l; i++) {
+ array[i] = string.charCodeAt(i);
+ }
+ return array.buffer;
+};
\ No newline at end of file
diff --git a/app/www/static/js/intval.web.js b/app/www/static/js/intval.web.js
index c1851d4..66b9850 100644
--- a/app/www/static/js/intval.web.js
+++ b/app/www/static/js/intval.web.js
@@ -55,36 +55,12 @@ web.getState = function () {
.then(res => {
return res.json();
})
- .then(web.getStateSuccess)
+ .then(setState)
.catch(err => {
console.error('Error getting state');
console.error(err);
});
};
-web.getStateSuccess = function (res) {
- let exposure;
- let scale;
- if (res.frame.dir !== true) {
- document.getElementById('dir').checked = true;
- STATE.dir = res.frame.dir;
- setDirLabel(false);
- }
- document.getElementById('counter').value = res.counter;
- STATE.counter = res.counter;
- //Exposure
- if (res.frame.exposure === 0) {
- res.frame.exposure = BOLEX.expected;
- }
- STATE.exposure = res.frame.exposure;
- exposure = shutter(STATE.exposure);
- scale = scaleAuto(STATE.exposure);
- document.getElementById('str').value = exposure.str;
- document.getElementById('scale').value = scale;
- setExposureScale();
-
- document.getElementById('delay').value = res.frame.delay;
- STATE.delay = res.frame.delay;
-};
web.setExposure = function () {
let exposure = document.getElementById('exposure').value;
let scaledExposure;
@@ -165,6 +141,7 @@ web.init = function () {
window.frame = web.frame;
window.getState = web.getState;
window.setDir = web.setDir;
+ window.setDelay = web.setDelay;
window.setExposure = web.setExposure;
window.setCounter = web.setCounter;
console.log('started web')
diff --git a/app/www/static/js/spin.min.js b/app/www/static/js/spin.min.js
new file mode 100644
index 0000000..bd3ae4f
--- /dev/null
+++ b/app/www/static/js/spin.min.js
@@ -0,0 +1,2 @@
+// http://spin.js.org/#v2.3.2
+!function(a,b){"object"==typeof module&&module.exports?module.exports=b():"function"==typeof define&&define.amd?define(b):a.Spinner=b()}(this,function(){"use strict";function a(a,b){var c,d=document.createElement(a||"div");for(c in b)d[c]=b[c];return d}function b(a){for(var b=1,c=arguments.length;c>b;b++)a.appendChild(arguments[b]);return a}function c(a,b,c,d){var e=["opacity",b,~~(100*a),c,d].join("-"),f=.01+c/d*100,g=Math.max(1-(1-a)/b*(100-f),a),h=j.substring(0,j.indexOf("Animation")).toLowerCase(),i=h&&"-"+h+"-"||"";return m[e]||(k.insertRule("@"+i+"keyframes "+e+"{0%{opacity:"+g+"}"+f+"%{opacity:"+a+"}"+(f+.01)+"%{opacity:1}"+(f+b)%100+"%{opacity:"+a+"}100%{opacity:"+g+"}}",k.cssRules.length),m[e]=1),e}function d(a,b){var c,d,e=a.style;if(b=b.charAt(0).toUpperCase()+b.slice(1),void 0!==e[b])return b;for(d=0;d
',c)}k.addRule(".spin-vml","behavior:url(#default#VML)"),h.prototype.lines=function(a,d){function f(){return e(c("group",{coordsize:k+" "+k,coordorigin:-j+" "+-j}),{width:k,height:k})}function h(a,h,i){b(m,b(e(f(),{rotation:360/d.lines*a+"deg",left:~~h}),b(e(c("roundrect",{arcsize:d.corners}),{width:j,height:d.scale*d.width,left:d.scale*d.radius,top:-d.scale*d.width>>1,filter:i}),c("fill",{color:g(d.color,a),opacity:d.opacity}),c("stroke",{opacity:0}))))}var i,j=d.scale*(d.length+d.width),k=2*d.scale*j,l=-(d.width+d.length)*d.scale*2+"px",m=e(f(),{position:"absolute",top:l,left:l});if(d.shadow)for(i=1;i<=d.lines;i++)h(i,-2,"progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)");for(i=1;i<=d.lines;i++)h(i);return b(a,m)},h.prototype.opacity=function(a,b,c,d){var e=a.firstChild;d=d.shadow&&d.lines||0,e&&b+d>1)+"px"})}for(var i,k=0,l=(f.lines-1)*(1-f.direction)/2;k