'use strict' const log = require('../log')('intval') let Gpio try { Gpio = require('onoff').Gpio } catch (e) { console.warn('Failed including Gpio, using sim') Gpio = require('../../lib/onoffsim').Gpio } const PINS = { fwd : { pin : 13, dir : 'out' }, bwd : { pin : 19, dir : 'out' }, micro : { pin : 6, dir : 'in', edge : 'both' }, release : { pin : 5, dir : 'in', edge : 'both' } } /** Class representing the intval3 features */ class Intval { constructor () { this._pin = {} this._state = { dir : true, //forward frame : { start : 0, //time frame started, timestamp active : false, //should frame be running time : 0, //length of frame, in ms delay : 0, //delay before start of frame, in ms expected : 1000 //expected length of frame, in ms }, release : { time: 0, active : false //is pressed }, micro : { time : 0, primed : false //is ready to stop frame } } this._releaseMin = 50 this._releaseSequence = 1000 this._microDelay = 10 // delay after stop signal before stopping motors this._declarePins() process.on('SIGINT', this._undeclarePins) } /** * (internal function) Declares all Gpio pins that will be used * */ _declarePins () { let pin for (let p in PINS) { pin = PINS[p] if (pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir, pin.edge) if (!pin.edge) this._pin[p] = Gpio(pin.pin, pin.dir) } console.dir(this._pin) this._pin.release.watch(this._watchRelease) } /** * (internal function) Undeclares all Gpio in event of uncaught error * that interupts the node process * */ _undeclarePins () { this._pin.fwd.unexport() this._pin.bwd.unexport() this._pin.micro.unexport() this._pin.release.unexport() } /** * Start motor in forward direction by setting correct pins in h-bridge * */ _startFwd () { this._pin.fwd.writeSync(1) this._pin.bwd.writeSync(0) //start high-cpu watch } /** * Start motor in backward direction by setting correct pins in h-bridge * */ _startBwd () { this._pin.fwd.writeSync(0) this._pin.bwd.writeSync(1) } /** * Stop motor by setting both motor pins to 0 (LOW) * */ _stop () { this._pin.fwd.writeSync(0) this._pin.bwd.writeSync(0) let len = (+new Date()) - this._state.frame.start console.log(`Frame stopped ${len}ms`) this._pin.micro.unwatch() this._state.frame.active = false this._state.frame.start = 0 } /** * Callback for watching relese switch state changes. * Using GPIO 06 on Raspberry Pi Zero W. * * 1) If closed AND frame active, start timer, set state primed to `true`. * 1) If opened AND frame active, stop frame * * Microswitch + 10K ohm resistor * * 1 === open * * 0 === closed * * * @param {object} err Error object present if problem reading pin * @param {integer} val Current value of the pin * */ _watchMicro (err, val) { const NOW = +new Date() if (err) { console.error(err) } //determine when to stop if (val === 0 && this._state.frame.active) { if (!this._state.micro.primed) { this._state.micro.primed = true this._state.micro.time = NOW console.log('Mircoswitch primed to stop motor') } } else if (val === 1 && this._state.frame.active) { if (this._state.micro.primed) { this._state.micro.primed = false this._state.micro.time = 0 setTimeout( () => { console.log(`Stopped frame after ${NOW - this._state.micro.time}ms`) }, this._microDelay) } } } /** * Callback for watching relese switch state changes. * Using GPIO 05 on Raspberry Pi Zero W. * * 1) If closed, start timer. * 2) If opened, check timer AND * 3) If `press` (`NOW - this._state.release.time`) greater than minimum and less than `this._releaseSequence`, start frame * 4) If `press` greater than `this._releaseSequence`, start sequence * * Button + 10K ohm resistor * * 1 === open * * 0 === closed * * @param {object} err Error object present if problem reading pin * @param {integer} val Current value of the pin * */ _watchRelease (err, val) { const NOW = +new Date() let press = 0 if (err) { return console.error(err) } console.log(`Release switch val: ${val}`) if (val === 0) { //closed if ((!this._state.release.active && this._state.release.time === 0) || (this._state.release.active && (NOW - this._state.release.time) > (this._releaseSequence * 10)) ) { this._state.release.time = NOW this._state.release.active = true //maybe unncecessary } } else if (val === 1) { //opened if (this._state.release.active) { press = NOW - this._state.release.time if (press > this._releaseMin && press < this._releaseSequence) { this.frame() } else if (press >= this._releaseSequence) { this.sequence() } console.log(`Release closed for ${press}ms`) this._state.release.time = 0 this._state.release.active = false } } } /** * Set the default direction of the camera. * * forward = true * * backward = false * * @param {boolean} [dir=true] Direction of the camera * */ setDir (val = true) { if (typeof val !== 'boolean') { return console.warn('Direction must be represented as either true or false') } this._state.dir = val } /** * Begin a single frame with set variables or defaults * * @param {?boolean} [dir="null"] (optional) Direction of the frame * @param {?integer} [time="null"] (optional) Exposure time, 0 = minimum * @param {?integer} [delay="null"] (optional) Delay after frame before another can be started * */ frame (dir = null, time = null, delay = null) { //may be overloaded, delay is suspect if (dir === true || (dir === null && this._state.dir === true) ) { dir = true } else { dir = false } if (time === null && this._state.time !== 0) { time = this._state.time } else { time = 0 } if (delay === null && this._state.delay !== 0) { delay = this._state.delay } else { delay = 0 } this._state.frame.start = +new Date() this._state.frame.active = true this._pin.micro.watch(this._watchMicro) if (delay !== 0) { setTimeout(function () { if (dir) { this._startFwd() } else { this._startBwd() } }, delay) } else { if (dir) { this._startFwd() } else { this._startBwd() } } } /** * Start a sequence of frames, using defaults or explicit instructions * */ sequence () { console.log(`Started sequence`) } status () { return this._state } } module.exports = new Intval()