diff --git a/app/www/static/js/intval.pwa.js b/app/www/static/js/intval.pwa.js
new file mode 100644
index 0000000..4ff1866
--- /dev/null
+++ b/app/www/static/js/intval.pwa.js
@@ -0,0 +1,804 @@
+/* jshint esversion:6, strict:true, browser:true*/
+/* global console, alert */
+
+
+'use strict';
+var mobile = {};
+
+mobile.wble = {
+ BLENO_DEVICE_NAME : 'intval3',
+ DEVICE_ID : 'intval3',
+ 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 : [],
+ device : {},
+ connected : false,
+ active : false
+};
+
+mobile.wifi = {
+ current : 'null',
+ available : [],
+ ip : null
+};
+
+async function delay (ms) {
+ return new Promise((resolve, reject) => {
+ return setTimeout(resolve, ms);
+ })
+}
+
+mobile.wble.scan = async function () {
+ let device;
+ UI.spinner.show('Scanning for INTVAL3...');
+ UI.overlay.show();
+ try {
+ device = await navigator.bluetooth.requestDevice({
+ filters: [{ services: [ mobile.wble.SERVICE_ID ] }],
+ //optionalServices: optionalServices
+ });
+ mobile.wble.onDiscover(device);
+ } catch (err) {
+ mobile.wble.onError(err);
+ }
+ //ble.scan([], 5, mobile.wble.onDiscover, mobile.wble.onError);
+ mobile.wble.devices = [];
+
+ await delay(5000);
+
+ UI.spinner.hide();
+ UI.overlay.hide();
+
+ if (!mobile.wble.connected) {
+ mobile.alert('No devices found.')
+ settingsPage();
+ }
+};
+
+mobile.wble.onDiscover = function (device) {
+ if (device && device.name && device.name.indexOf('intval3') !== -1) {
+ console.log('BLE - Discovered INTVAL3');
+ console.dir(device);
+ mobile.wble.devices.push(device);
+ if (!mobile.wble.connected) {
+ mobile.wble.connect(device);
+ }
+ } else {
+ //console.log(`BLE - Discovered Other ${device.id}`);
+ }
+};
+
+mobile.wble.connect = async function (device) {
+ console.log(`BLE - Connecting to ${device.id}`);
+ try {
+ await device.gatt.connect()
+ } catch (err) {
+ mobile.wble.onError(err);
+ }
+ ble.connect(device.id, (peripheral) => {
+ mobile.wble.onConnect(peripheral, device);
+ }, mobile.wble.onError);
+};
+
+mobile.wble.onConnect = function (peripheral, device) {
+ const elem = document.getElementById('bluetooth');
+ const option = document.createElement('option');
+ const disconnect = document.getElementById('disconnect');
+ const scan = document.getElementById('scan');
+
+ UI.spinner.hide();
+ UI.overlay.hide();
+ console.log(`BLE - Connected to ${device.id}`);
+ console.log(peripheral);
+ console.dir(device);
+
+ mobile.wble.device = device;
+ mobile.wble.connected = true;
+
+ elem.innerHTML = '';
+ option.text = device.name;
+ option.value = device.id;
+ elem.add(option);
+
+ disconnect.classList.add('active');
+ scan.classList.remove('active');
+
+ getState();
+ mobile.getWifi();
+};
+
+mobile.wble.disconnect = function () {
+ const elem = document.getElementById('bluetooth');
+ const option = document.createElement('option');
+ const disconnect = document.getElementById('disconnect');
+ const scan = document.getElementById('scan');
+ let device;
+ if (!mobile.wble.connected) {
+ console.warn('Not connected to any device');
+ return false;
+ }
+ device = mobile.wble.device;
+ console.log(`BLE - Disconnecting from ${device.id}`);
+ ble.disconnect(device.id, mobile.wble.onDisconnect, mobile.wble.onDisconnect);
+
+ elem.innerHTML = '';
+ option.text = 'N/A';
+ elem.add(option);
+
+ disconnect.classList.remove('active');
+ scan.classList.add('active');
+ UI.spinner.hide();
+ UI.overlay.hide();
+};
+
+mobile.wble.onDisconnect = function (res) {
+ console.log(`BLE - Disconnected from ${res}`);
+ mobile.wble.connected = false;
+ mobile.wble.device = {};
+};
+
+mobile.wble.onError = function (err) {
+ if (err.errorMessage && err.errorMessage === 'Peripheral Disconnected') {
+ console.log('Device disconnected');
+ mobile.wble.disconnect()
+ } else {
+ mobile.alert(JSON.stringify(err));
+ }
+ /*
+ Object
+ errorDescription: "The specified device has disconnected from us."
+ errorMessage: "Peripheral Disconnected"
+ id: "E8EF4B8B-0B5E-4E96-B337-E878DB1E3C4B"
+ name: "intval3_b827ebc7461d"
+ */
+};
+
+mobile.init = function () {
+ const bleInputs = document.querySelectorAll('.ble');
+ const bolIso = document.querySelector('.iso');
+ const bolF = document.querySelector('.fstop');
+
+ document.querySelector('body').classList.add('mobile');
+
+ window.frame = mobile.frame;
+ window.getState = mobile.getState;
+ window.setDir = mobile.setDir;
+ window.setExposure = mobile.setExposure;
+ window.setDelay = mobile.setDelay;
+ window.setCounter = mobile.setCounter;
+ window.sequence = mobile.sequence;
+ window.reset = mobile.reset;
+ window.restart = mobile.restart;
+ window.update = mobile.update;
+
+ //show ble-specific fields in settings
+ for (let i of bleInputs) {
+ i.classList.add('active');
+ }
+ UI.spinner.init()
+ mobile.wble.scan();
+ mobile.cameraValues();
+
+};
+
+mobile.getState = function () {
+ if (!mobile.wble.connected) {
+ //returning here will prevent error alert
+ }
+ ble.read(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ mobile.stateSuccess,
+ mobile.wble.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.wble.connected) {
+ return mobile.alert('Not connected to an INTVAL3 device.');
+ }
+ if (mobile.wble.active) {
+ return false;
+ }
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.frameSuccess,
+ mobile.wble.onError);
+ document.getElementById('frame').classList.add('focus');
+ mobile.wble.active = true;
+};
+
+
+mobile.frameSuccess = function () {
+ if (STATE.exposure < 5000) {
+ console.log('Frame finished, getting state.');
+ mobile.wble.active = false;
+ document.getElementById('frame').classList.remove('focus');
+ mobile.getState();
+ } else {
+ setTimeout(() => {
+ console.log('Frame finished, getting state.');
+ mobile.wble.active = false;
+ document.getElementById('frame').classList.remove('focus');
+ mobile.getState();
+ }, STATE.exposure + 500)
+ }
+}
+mobile.setDir = function () {
+ const opts = {
+ type : 'dir',
+ dir : !document.getElementById('dir').checked
+ };
+
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.dirSuccess,
+ mobile.wble.onError);
+};
+mobile.dirSuccess = function () {
+ console.log('Set direction');
+ mobile.getState();
+ setTimeout(() => {
+ setDirLabel(STATE.dir);
+ }, 50);
+};
+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.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.exposureSuccess,
+ mobile.wble.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.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.delaySuccess,
+ mobile.wble.onError);
+}
+
+mobile.delaySuccess = function () {
+ console.log('Set delay');
+ mobile.getState();
+};
+
+mobile.setCounter = function () {
+ let opts = {
+ type : 'counter',
+ counter : null
+ };
+ const counter = document.getElementById('counter').value;
+ function counterPrompt (results) {
+ let change = results.input1
+ if (results.buttonIndex === 1) {
+ if (change === null || !isNumeric(change)) return false;
+ opts.counter = change;
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.counterSuccess,
+ mobile.wble.onError);
+ }
+ }
+ navigator.notification.prompt(
+ `Change counter value?`,
+ counterPrompt,
+ 'INTVAL3',
+ ['Okay', 'Cancel'],
+ counter);
+};
+
+mobile.counterSuccess = function () {
+ console.log('Set counter');
+ mobile.getState();
+};
+
+mobile.sequence = function () {
+ const opts = {
+ type : 'sequence'
+ };
+ const elem = document.getElementById('seq');
+ if (!mobile.wble.connected) {
+ return mobile.alert('Not connected to an INTVAL3 device.');
+ }
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)), //check length?
+ mobile.sequenceSuccess,
+ mobile.wble.onError);
+
+ if (!elem.classList.contains('focus')) {
+ elem.classList.add('focus');
+ }
+
+ mobile.wble.active = true;
+};
+
+mobile.sequenceSuccess = function () {
+ console.log('Sequence state changed');
+ mobile.getState();
+ setTimeout(() => {
+ if (STATE.sequence) {
+ mobile.wble.active = true;
+ seqState(true);
+ } else {
+ mobile.wble.active = false;
+ seqState(false);
+ }
+ }, 20);
+};
+
+
+//retreive object with list of available Wifi APs,
+//and state of current connection, if available
+mobile.getWifi = function () {
+ UI.spinner.show('Refreshing WIFI...');
+ UI.overlay.show();
+
+ ble.read(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.WIFI_ID,
+ mobile.getWifiSuccess,
+ mobile.wble.onError);
+};
+
+mobile.getWifiSuccess = function (data) {
+ const elem = document.getElementById('available');
+ const wifi = document.getElementById('wifi');
+ const password = document.getElementById('password');
+ const ip = document.getElementById('ip');
+ let option = document.createElement('option');
+ let str = bytesToString(data);
+ let res = JSON.parse(str);
+
+ UI.spinner.hide();
+ UI.overlay.hide();
+ elem.innerHTML = ''
+ if (!res.available || res.available.length === 0) {
+ if (elem.classList.contains('active')) {
+ elem.classList.remove('active');
+ }
+ option.text = 'N/A'
+ elem.add(option);
+ elem.value = '';
+ } else {
+ for (let ap of res.available) {
+ option = document.createElement('option');
+ option.text = ap;
+ option.value = ap;
+ elem.add(option);
+ }
+ if (res.current && res.available.indexOf(res.current) !== -1) {
+ elem.value = res.current
+ if (!elem.classList.contains('active')) {
+ elem.classList.add('active');
+ }
+ if (wifi.classList.contains('active')) {
+ wifi.classList.remove('active');
+ }
+ if (password.classList.contains('active')) {
+ password.classList.remove('active');
+ }
+ } else {
+ if (!wifi.classList.contains('active')) {
+ wifi.classList.add('active');
+ }
+ if (!password.classList.contains('active')) {
+ password.classList.add('active');
+ }
+ }
+ }
+ if (typeof res.ip !== 'undefined' && res.ip != null ) {
+ ip.innerHTML = `Local IP: ${res.ip}`
+ if (!ip.classList.contains('active')) {
+ ip.classList.add('active');
+ }
+ } else {
+ ip.innerHTML = 'Local IP: null'
+ if (ip.classList.contains('active')) {
+ ip.classList.remove('active');
+ }
+ }
+ mobile.wifi.current = res.current;
+ mobile.wifi.available = res.available;
+ mobile.wifi.ip = res.ip;
+};
+
+mobile.editWifi = function () {
+ const available = document.getElementById('available');
+ const wifi = document.getElementById('wifi');
+ const password = document.getElementById('password');
+ if (!wifi.classList.contains('active')) {
+ wifi.classList.add('active');
+ }
+ if (!password.classList.contains('active')) {
+ password.classList.add('active');
+ }
+ password.focus();
+ if (available.value !== mobile.wifi.current && available.classList.contains('active')) {
+ available.classList.remove('active');
+ }
+};
+
+mobile.setWifi = function () {
+ const ssid = document.getElementById('available').value;
+ const pwd = document.getElementById('password').value;
+ const opts = {
+ ssid : ssid,
+ pwd : pwd
+ };
+ UI.spinner.show('Setting WIFI...');
+ UI.overlay.show();
+
+ if (ssid === '' || ssid === null || ssid === undefined) {
+ return mobile.alert('Cannot set wireless credentials with a blank SSID');
+ }
+ if (pwd === '' || pwd === null || pwd === undefined) {
+ return mobile.alert('Cannot set wireless credentials with a blank passphrase');
+ }
+ if (pwd.length < 8 || pwd.length > 63) {
+ return mobile.alert('Passphrase must be 8..63 characters');
+ }
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.WIFI_ID,
+ stringToBytes(JSON.stringify(opts)),
+ mobile.setWifiSuccess,
+ mobile.wble.onError);
+};
+
+mobile.setWifiSuccess = function () {
+ UI.spinner.hide();
+ UI.overlay.hide();
+ console.log('Set new wifi credentials');
+ setTimeout(mobile.getWifi, 100);
+};
+mobile.exif = {}
+
+mobile.getCamera = function () {
+ const opts = {
+ quality: 30,
+ sourceType: Camera.PictureSourceType.CAMERA,
+ destinationType: Camera.DestinationType.FILE_URI
+ };
+ navigator.camera.getPicture(mobile.cameraSuccess, mobile.cameraError, opts);
+};
+mobile.cameraSuccess = function (result) {
+ const thisResult = JSON.parse(result);
+ const metadata = JSON.parse(thisResult.json_metadata);
+
+ mobile.cameraExposure(metadata.Exif);
+};
+mobile.cameraError = function (err) {
+ console.error(err);
+ mobile.alert(JSON.stringify(err));
+};
+
+mobile.cameraExposure = function (exif) {
+ const cam_exp = document.getElementById('cam_exp');
+ const cam_f = document.getElementById('cam_f');
+ const cam_iso = document.getElementById('cam_iso');
+ const bol_exp = document.getElementById('bol_exp');
+ const bol_f = document.getElementById('bol_f');
+ const bol_iso = document.getElementById('bol_iso');
+ const bol_f_diff = document.getElementById('bol_f_diff');
+ const bol_iso_diff = document.getElementById('bol_iso_diff');
+ const bol_exp_diff = document.getElementById('bol_exp_diff');
+
+ const fstop = BOLEX.fstop || 5.6;
+ const iso = BOLEX.iso || 100;
+ const prism = BOLEX.prism || 0.8;
+
+ const cFstop = exif.ApertureValue || exif.FNumber;
+ const cExposure = exif.ExposureTime * 1000;
+ const cIso = exif.ISOSpeedRatings[0];
+
+ //convert fstop to "fnumber", an absolute scale where stops are scaled to 1.0
+ const f = fnumber(cFstop);
+ const target = fnumber(fstop); //bolex
+
+ let exposure = cExposure;
+ let isoStops = 0;
+ let fStops = 0;
+ let expDiff;
+
+ let scale_elem;
+ let exposure_elem;
+
+ let proceed;
+ let e1;
+ let e2;
+
+ mobile.exif = exif;
+
+ //Determine if fstop of phone camera "f"
+ if (target !== f) {
+ fStops = f - target;
+ exposure = exposure / Math.pow(2, fStops);
+ }
+
+ if (cIso != iso) {
+ isoStops = (Math.log(cIso) / Math.log(2)) - (Math.log(iso) / Math.log(2));
+ }
+
+ //Double or halve exposure based on the differences in ISO stops
+ exposure = exposure * Math.pow(2, isoStops);
+
+ //Compensate for Bolex prism
+ exposure = exposure * Math.pow(2, prism);
+
+ exposure = Math.round(exposure) //round to nearest millisecond
+
+ bol_f.value = fstop;
+ bol_iso.value = iso;
+ bol_exp.value = exposure;
+
+ //Total difference in exposure from phone camera to Bolex
+ expDiff = (Math.log(exposure) / Math.log(2)) - (Math.log(cExposure) / Math.log(2));
+
+ bol_exp_diff.innerHTML = floatDisplay(expDiff);
+ bol_iso_diff.innerHTML = floatDisplay(isoStops);
+ bol_f_diff.innerHTML = floatDisplay(-fStops);
+
+ cam_exp.value = cExposure;
+ cam_f.value = cFstop;
+ cam_iso.value = cIso;
+
+ function exposureConfirm (index) {
+ if (index === 1) {
+ e1 = new Event('change');
+ e2 = new Event('change');
+
+ scale_elem = document.getElementById('scale');
+ exposure_elem = document.getElementById('exposure');
+
+ scale_elem.value = 'ms';
+ scale_elem.dispatchEvent(e1);
+
+ exposure_elem.value = exposure;
+ exposure_elem.dispatchEvent(e2);
+ }
+ }
+
+ if (exposure > 500) {
+ navigator.notification.confirm(
+ `Set camera exposure to ${exposure}ms to match photo?`,
+ exposureConfirm,
+ 'INTVAL3',
+ ['Okay', 'Cancel']
+ );
+ }
+
+ /*
+{
+ "Exif": {
+ "DateTimeOriginal": "2018:02:02 16:59:13",
+ "ExposureBiasValue": 0,
+ "SensingMethod": 2,
+ "BrightnessValue": -0.9969016228800144,
+ "LensMake": "Apple",
+ "FNumber": 1.8,
+ "FocalLength": 3.99,
+ "ShutterSpeedValue": 2.049355412374274,
+ "SceneType": 1,
+ "ApertureValue": 1.6959938131099002,
+ "SubjectArea": [
+ 2015,
+ 1511,
+ 2217,
+ 1330
+ ],
+ "ColorSpace": 65535,
+ "LensSpecification": [
+ 3.99,
+ 3.99,
+ 1.8,
+ 1.8
+ ],
+ "PixelYDimension": 3024,
+ "WhiteBalance": 0,
+ "DateTimeDigitized": "2018:02:02 16:59:13",
+ "ExposureMode": 0,
+ "ISOSpeedRatings": [
+ 100
+ ],
+ "PixelXDimension": 4032,
+ "LensModel": "iPhone 8 back camera 3.99mm f/1.8",
+ "ExposureTime": 0.25,
+ "Flash": 24,
+ "SubsecTimeDigitized": "209",
+ "SubsecTimeOriginal": "209",
+ "ExposureProgram": 2,
+ "FocalLenIn35mmFilm": 28,
+ "MeteringMode": 5
+ }
+}
+ */
+};
+
+mobile.refreshExposure = function () {
+ if (typeof mobile.exif.ExposureTime !== 'undefined') {
+ mobile.cameraExposure(mobile.exif);
+ }
+};
+
+mobile.EV = function (fstop, shutter) {
+ const sec = shutter / 1000; //shutter in ms => seconds
+ const square = Math.pow(fstop, 2);
+ return Math.log(square / sec);
+};
+
+mobile.reset = function () {
+ let opts = {
+ type : 'reset'
+ };
+ function resetConfirm (index) {
+ if (index === 1) {
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)),
+ mobile.resetSuccess,
+ mobile.wble.onError);
+ }
+ }
+ navigator.notification.confirm(
+ `Reset INTVAL3 to default settings and clear counter?`,
+ resetConfirm,
+ 'INTVAL3',
+ ['Okay', 'Cancel']
+ );
+};
+
+mobile.resetSuccess = function () {
+ console.log('Reset to default settings');
+ setTimeout(() => {
+ mobile.getState();
+ }, 100)
+};
+
+mobile.update = function () {
+ let opts = {
+ type : 'update'
+ };
+ function updateConfirm (index) {
+ if (index === 1) {
+ UI.spinner.show('Updating INTVAL3...');
+ UI.overlay.show();
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)),
+ mobile.updateSuccess,
+ mobile.wble.onError);
+ }
+ }
+ navigator.notification.confirm(
+ `Check for updates? You will be disconnected from the INTVAL3 during this process.`,
+ updateConfirm,
+ 'INTVAL3',
+ ['Okay', 'Cancel']
+ );
+};
+
+mobile.updateSuccess = function () {
+ console.log('Finished updating firmware, restarting...');
+};
+
+mobile.restart = function () {
+ let opts = {
+ type : 'restart'
+ };
+ function restartConfirm (index) {
+ if (index === 1) {
+ UI.spinner.show('Restarting INTVAL3...');
+ UI.overlay.show();
+ ble.write(mobile.wble.device.id,
+ mobile.wble.SERVICE_ID,
+ mobile.wble.CHAR_ID,
+ stringToBytes(JSON.stringify(opts)),
+ mobile.restartSuccess,
+ mobile.wble.onError);
+ }
+ }
+ navigator.notification.confirm(
+ `Restart the INTVAL3? You will be disconnected from it during this process.`,
+ restartConfirm,
+ 'INTVAL3',
+ ['Okay', 'Cancel']
+ );
+};
+mobile.restartSuccess = function () {
+ console.log('Restarting... ');
+}
+
+mobile.alert = function (msg) {
+ if (navigator && navigator.notification) {
+ navigator.notification.alert(
+ msg,
+ () => {},
+ 'INTVAL3',
+ 'Okay'
+ );
+ } else {
+ alert(msg);
+ }
+};
+
+/**
+ * Mobile helper functions
+ */
+
+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;
+}
+
+function fnumber (fstop) {
+ return Math.log(fstop) / Math.log(Math.sqrt(2));
+}
+
+function floatDisplay (value) {
+ let str = value + '';
+ const period = str.indexOf('.');
+ if (period === -1) {
+ str = str + '.0';
+ } else {
+ str = roundTenth(value) + '';
+ }
+ if (value < 0) {
+ str = `${(str + '')}`;
+ } else if (value > 0) {
+ str = `+${(str + '')}`;
+ }
+ return str;
+}
+
+function roundTenth (value) {
+ return Math.round((value * 10) / 10)
+}
+
diff --git a/hardware/case.scad b/hardware/case.scad
index 48387fc..4185115 100644
--- a/hardware/case.scad
+++ b/hardware/case.scad
@@ -444,6 +444,18 @@ module motor_cap_120 (HALF = false) {
}
}
+module bolt_guide () {
+ $fn = 80;
+ difference () {
+ union () {
+ cylinder(r = 10 /2, h = 38, center = true);
+ translate([0, 0, -(38 / 2) + 1.5]) cylinder(r = 16 /2, h = 3, center = true);
+ }
+ cylinder(r = 7.5 / 2, h = 40, center = true);
+ translate([0, -13, -(38 / 2) + 1.5]) cube([16, 16, 10], center = true);
+ }
+}
+
module bearing_calibrate (val = 0) {
mat = 25.4/8;
difference () {
diff --git a/hardware/intval_3_prototype.fzz b/hardware/intval_3_prototype.fzz
index 5e325f4..2d5f960 100644
Binary files a/hardware/intval_3_prototype.fzz and b/hardware/intval_3_prototype.fzz differ
diff --git a/lib/log/index.js b/lib/log/index.js
index 8527559..eaa2673 100644
--- a/lib/log/index.js
+++ b/lib/log/index.js
@@ -14,7 +14,7 @@ function createLog (label, filename = null) {
if (filename !== null) {
transports.push( new (winston.transports.File)({ label : label, filename : filename }) )
}
- return new (winston.Logger)({
+ return new (winston.createLogger)({
transports: transports
})
}
diff --git a/scripts/intval3-deps.sh b/scripts/intval3-deps.sh
index c280d64..027abb9 100644
--- a/scripts/intval3-deps.sh
+++ b/scripts/intval3-deps.sh
@@ -9,9 +9,9 @@ sudo apt install nodejs npm -y
sudo npm install -g n
sudo n latest
sudo npm install -g npm@latest
-sudo npm install -g pm2 node-gyp
+sudo npm install -g pm2 node-gyp node-pre-gyp
echo "Installing bluetooth dependencies..."
sudo apt install bluetooth bluez libbluetooth-dev libudev-dev -y
-echo "Finished installing intval3 dependencies"
\ No newline at end of file
+echo "Finished installing intval3 dependencies"
diff --git a/scripts/intval3-install.sh b/scripts/intval3-install.sh
index 2c6ab53..066e7e0 100644
--- a/scripts/intval3-install.sh
+++ b/scripts/intval3-install.sh
@@ -10,7 +10,7 @@ sudo apt install nodejs npm -y
sudo npm install -g n
sudo n 9.1.0
sudo npm install -g npm@latest
-sudo npm install -g pm2 node-gyp
+sudo npm install -g pm2 node-gyp node-pre-gyp
echo "Installing bluetooth dependencies..."
sudo apt install bluetooth bluez libbluetooth-dev libudev-dev -y
@@ -46,4 +46,4 @@ sudo pm2 start process.json
sudo pm2 save
sudo pm2 startup
-echo "Finished installing intval3"
\ No newline at end of file
+echo "Finished installing intval3"