/
desk-machine.js
144 lines (102 loc) · 2.95 KB
/
desk-machine.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
'use strict'
const delay = require('delay')
const finiteStateMachine = require('./finite-state-machine.js')
const fs = require('fs')
const gpio = require('rpi-gpio')
const MAX_HEIGHT = 18
const HEIGHT_FILE = __dirname + '/height'
const HISTORY_FILE = __dirname + '/history'
const SPEED = 0.56 // how fast the desk extends/retracts (inches per second)
const gpiop = gpio.promise
const pins = [ 7, 3, 11, 5 ]
const legsDownPin = 11
const legsUpPin = 5
let currentHeight = 0
function saveHeight (height) {
fs.writeFileSync(HEIGHT_FILE, height, 'utf8')
fs.appendFileSync(HISTORY_FILE, `${Date.now()},${height}\n`)
}
function loadHeight () {
return fs.existsSync(HEIGHT_FILE) ? parseFloat(fs.readFileSync(HEIGHT_FILE, 'utf8')) : 0
}
async function setHeight (oldHeight, newHeight) {
if (newHeight < 0)
newHeight = 0
if (newHeight > MAX_HEIGHT)
newHeight = MAX_HEIGHT
const travelDistance = Math.abs(oldHeight - newHeight)
const timeToSleep = Math.floor(travelDistance / SPEED * 1000)
if (timeToSleep < 50)
return
if (newHeight > oldHeight)
await extend()
else
await retract()
// periodically update the desk state (maybe like every 0.5 seconds?)
const start = Date.now()
const interval = setInterval(function () {
const delta = (Date.now() - start) / 1000
const direction = (newHeight > oldHeight) ? 1 : -1
currentHeight = oldHeight + SPEED * delta * direction
}, 100)
await delay(timeToSleep)
clearInterval(interval)
await stop()
currentHeight = newHeight
saveHeight(newHeight)
}
async function extend () {
await gpiop.write(legsDownPin, true)
await gpiop.write(legsUpPin, false)
}
async function retract () {
await gpiop.write(legsUpPin, true)
await gpiop.write(legsDownPin, false)
}
async function stop () {
await gpiop.write(legsUpPin, true)
await gpiop.write(legsDownPin, true)
}
function noop () { }
module.exports = function () {
const fsm = finiteStateMachine()
let _setHeight = noop
fsm.addState('initializing', {
enter: async function () {
currentHeight = loadHeight()
console.log('current height:', currentHeight)
for (const pin of pins)
await gpiop.setup(pin, gpio.DIR_OUT)
await stop()
console.log('initialization complete, pins setup\n')
fsm.setState('ready')
}
})
fsm.addState('ready', {
enter: function () {
console.log('entering ready state')
_setHeight = function (height) {
fsm.setState('moving', height)
}
},
exit: function () {
_setHeight = noop
}
})
fsm.addState('moving', {
enter: async function (newHeight) {
console.log('moving to new height:', newHeight)
await setHeight(currentHeight, newHeight)
fsm.setState('ready')
}
})
fsm.setState('initializing')
return {
setHeight: (height) => _setHeight(height),
getState: function () {
return {
state: fsm.getCurrentState(), height: currentHeight
}
}
}
}