-
Notifications
You must be signed in to change notification settings - Fork 3
/
pan-tilt-hat.js
222 lines (191 loc) · 6.06 KB
/
pan-tilt-hat.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//
// Node Wrapper for Pimoroni Pan-Tilt HAT
// Copyright (c) Roger Hardiman 2017
// Copyright (c) 2018 Casper Meijn
//
// PIMORONI API (ABSOLUTE POSITIONING)
// The APIs implemented are
// servo_one(angle), // angle between -90 and 90
// pan(angle), // angle between -90 and 90
// servo_two(angle), // angle between -90 and 90
// tilt(angle) // angle between -90 and 90
//
// CONTINUOUS MOVE API (START AND STOP COMMANDS)
// The class also implements a 'continuous move' API where the Pan/Tilt HAT is
// told to move (direction and speed provided) and the Pan/Tilt HAT continues
// moving until a Stop command (or zero speed command) is issued.
// This mirrors the type of functions on old analogue CCTV cameras.
// pan_left(speed) // speed from 0 to 15. 0 means stop
// pan_right(speed) // speed from 0 to 15. 0 means stop
// tilt_up(speed) // speed from 0 to 15. 0 means stop
// tilt_down(speed) // speed from 0 to 15. 0 means stop
// stop() // stop the pan and the tilt
//
// DEPENDENCIES (PYTHON AND PIMORONI PYTHON LIBARY)
// To control the Pan-Tilt HAT this Node class spawns a Python program
// and then uses a very simple ASCII messaging passing protocol to
// send messages to the Python program which uses Pimoroni's python library
// So you must have Python installed and in your PATH
var spawn = require('child_process').spawn;
var path = require('path');
class PanTiltHAT {
constructor() {
this.python = null;
this.parent = this;
// current position (used for continuous move API)
this.pan_position = 0;
this.tilt_position = 0;
// current speed (used for continuous move API)
this.pan_speed = 0;
this.tilt_speed = 0;
// start timer, used for Continuous Move API
this.timer = setInterval(this.calculate_angles.bind(this),100);
}
light_type(lightType)
{
if( lightType==="RGB" ||
lightType==="GRB" ||
lightType=="RGBW" ||
lightType==="GRBW")
{
this.python_command('light_type ' + lightType + '\n');
}
}
light_mode(lightMode)
{
if( lightMode==="PWM" ||
lightMode==="WS2812" )
{
this.python_command('light_mode ' + lightMode + '\n');
}
}
brightness(brightness)
{
if( brightness>=0 && brightness<=255)
{
this.python_command('light_mode ' + brightness);
}
}
set_all(r, g, b, w)
{
r=parseInt(r);
g=parseInt(g);
b=parseInt(b);
if( !isNaN(r) && !isNaN(g) && !isNaN(b))
{
let command = 'set_all ' + r + ' ' + g + ' ' + b;
w = parseInt(w);
if( !isNaN(w))
{
command += ' ' + w;
}
this.python_command(command + '\n');
}
}
set_pixel(index, r, g, b, w)
{
index = parseInt(index);
r=parseInt(r);
g=parseInt(g);
b=parseInt(b);
if( !isNaN(index) && !isNaN(r) && !isNaN(g) && !isNaN(b))
{
let command = 'set_all ' + index + ' ' + r + ' ' + g + ' ' + b;
w = parseInt(w);
if( !isNaN(w))
{
command += ' ' + w;
}
this.python_command(command + '\n');
}
}
show()
{
this.python_command('show\n');
}
// Move Servo One to 'angle'
servo_one(angle) {
this.pan_position = angle;
if (this.pan_position > 90) this.pan_position = 90;
if (this.pan_position < -90) this.pan_position = -90;
this.pan_speed = 0;
this.python_command('pan '+ Math.round(this.pan_position) +'\n');
}
// Move Servo Two to 'angle'
servo_two(angle) {
this.tilt_position = angle;
if (this.tilt_position > 80) this.tilt_position = 80;
if (this.tilt_position < -80) this.tilt_position = -80;
this.tilt_speed = 0;
this.python_command('tilt '+ Math.round(this.tilt_position) +'\n');
}
// Alias for Servo One and Servo Two
pan(angle) { this.servo_one(angle); };
tilt(angle) { this.servo_two(angle); };
goto_home() { this.pan(0); this.tilt(0); };
python_command(command_string) {
if (this.python === null) {
this.python = spawn('python', [path.join(__dirname, '/bin/node-pantilthat.py')]);
}
if (this.python != null) {
// console.log(command_string);
this.python.stdin.write(command_string);
}
}
// calculate new angle for continuous move API using current position
// and current speed
calculate_angles() {
if (this.pan_speed === 0 && this.tilt_speed === 0) return;
var new_pan_position = this.pan_position + (this.pan_speed/10);
var new_tilt_position = this.tilt_position + (this.tilt_speed/10);
// range check
if (new_pan_position > 90) new_pan_position = 90;
if (new_pan_position < -90) new_pan_position = -90;
if (new_tilt_position > 80) new_tilt_position = 80;
if (new_tilt_position < -80) new_tilt_position = -80;
// console.log('new pan=' + new_pan_position + ' new tilt=' + new_tilt_position);
// call the python code which uses the Pimoroni Python Library
// stop any continuous move
if (new_pan_position != this.pan_position) {
this.pan_position = new_pan_position;
this.python_command('pan '+ Math.round(this.pan_position)+'\n');
}
if (new_tilt_position != this.tilt_position) {
this.tilt_position = new_tilt_position;
this.python_command('tilt '+ Math.round(this.tilt_position)+'\n');
}
}
pan_left(speed) {
if (speed > 15) speed = 15;
if (speed < 0) speed = 0;
this.pan_speed = speed;
}
pan_right(speed) {
if (speed > 15) speed = 15;
if (speed < 0) speed = 0;
this.pan_speed = -speed;
}
tilt_up(speed) {
if (speed > 15) speed = 15;
if (speed < 0) speed = 0;
this.tilt_speed = -speed;
}
tilt_down(speed) {
if (speed > 15) speed = 15;
if (speed < 0) speed = 0;
this.tilt_speed = speed;
}
stop() {
this.pan_speed = 0;
this.tilt_speed = 0;
}
close() {
clearTimeout(this.timer);
this.timer = null;
if (this.python != null) {
this.python_command('exit\n');
this.python = null;
}
}
}
module.exports = PanTiltHAT;