roomba-iot – Blame information for rev 2
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
2 | office | 1 | #!/usr/bin/env nodejs |
2 | /////////////////////////////////////////////////////////////////////////// |
||
3 | // Copyright (C) Wizardry and Steamworks 2018 - License: GNU GPLv3 // |
||
4 | // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // |
||
5 | // rights of fair usage, the disclaimer and warranty conditions. // |
||
6 | /////////////////////////////////////////////////////////////////////////// |
||
7 | |||
8 | const SENSOR_PACKET_SIZE = 80; |
||
9 | |||
10 | const PACKET_DECODE_MASKS = { |
||
11 | BUMP_WHEELDROP: { |
||
12 | BUMP_RIGHT: 0x01, |
||
13 | BUMP_LEFT: 0x02, |
||
14 | WHEELDROP_RIGHT: 0x04, |
||
15 | WHEELDROP_LEFT: 0x08 |
||
16 | }, |
||
17 | WHEEL_OVERCURRENT: { |
||
18 | SIDEBRUSH: 0x01, |
||
19 | RESERVED: 0x02, |
||
20 | MAINBRUSH: 0x04, |
||
21 | RIGHT_WHEEL: 0x08, |
||
22 | LEFT_WHEEL: 0x10 |
||
23 | }, |
||
24 | BUTTONS: { |
||
25 | CLEAN: 0x01, |
||
26 | SPOT: 0x02, |
||
27 | DOCK: 0x03, |
||
28 | MINUTE: 0x04, |
||
29 | HOUR: 0x05, |
||
30 | DAY: 0x06, |
||
31 | SCHEDULE: 0x07, |
||
32 | CLOCK: 0x08 |
||
33 | }, |
||
34 | LIGHT_BUMPER: { |
||
35 | LIGHT_BUMPER_LEFT: 0x01, |
||
36 | LIGHT_BUMPER_FRONT_LEFT: 0x02, |
||
37 | LIGHT_BUMPER_CENTER_LEFT: 0x03, |
||
38 | LIGHT_BUMPER_CENTER_RIGHT: 0x04, |
||
39 | LIGHT_BUMPER_FRONT_RIGHT: 0x05, |
||
40 | LIGHT_BUMPER_RIGHT: 0x06 |
||
41 | }, |
||
42 | CHARGING_SOURCES: { |
||
43 | INTERNAL_CHARGER: 0x01, |
||
44 | HOME_BASE: 0x02 |
||
45 | } |
||
46 | } |
||
47 | |||
48 | /* |
||
49 | * Decode sensor data for packet group 100 (all sensors). |
||
50 | */ |
||
51 | const decode_all_sensors = function(packetBuffer, callback) { |
||
52 | if (packetBuffer.length != SENSOR_PACKET_SIZE) { |
||
53 | callback(null, 0, 'Invalid packet size') |
||
54 | return |
||
55 | } |
||
56 | |||
57 | var packet = {} |
||
58 | var pos = 0 |
||
59 | var byte = 0x00 |
||
60 | var mask = {} |
||
61 | |||
62 | // Bumps and Wheel Drops (Packet ID: 7, Data Bytes: 1, unsigned) |
||
63 | byte = packetBuffer.readUInt8(pos++) |
||
64 | mask = PACKET_DECODE_MASKS.BUMP_WHEELDROP |
||
65 | |||
66 | packet.bump = {} |
||
67 | packet.bump.right = byte & mask.BUMP_RIGHT |
||
68 | packet.bump.left = byte & mask.BUMP_LEFT |
||
69 | packet.wheeldrop = {} |
||
70 | packet.wheeldrop.right = byte & mask.WHEELDROP_RIGHT |
||
71 | packet.wheeldrop.left = byte & mask.WHEELDROP_LEFT |
||
72 | |||
73 | // Wall (Packet ID: 8, Data Bytes: 1, unsigned) |
||
74 | packet.wall = packetBuffer.readUInt8(pos++) == 1 |
||
75 | |||
76 | // Cliff Sensors (Packet ID: 9 - 12) |
||
77 | packet.cliff = {} |
||
78 | packet.cliff.left = packetBuffer.readUInt8(pos++) == 1 |
||
79 | packet.cliff.front_left = packetBuffer.readUInt8(pos++) == 1 |
||
80 | packet.cliff.front_right = packetBuffer.readUInt8(pos++) == 1 |
||
81 | packet.cliff.right = packetBuffer.readUInt8(pos++) == 1 |
||
82 | |||
83 | // Virtual Wall (Packet ID: 13, Data Bytes: 1, unsigned) |
||
84 | packet.virtual_wall = packetBuffer.readUInt8(pos++) == 1 |
||
85 | |||
86 | // Wheel Overcurrents (Packet ID: 14, Data Bytes: 1, unsigned) |
||
87 | byte = packetBuffer.readUInt8(pos++) |
||
88 | mask = PACKET_DECODE_MASKS.WHEEL_OVERCURRENT |
||
89 | packet.wheel_overcurrent = {} |
||
90 | packet.wheel_overcurrent.side_brush = byte & mask.SIDEBRUSH |
||
91 | packet.wheel_overcurrent.main_brush = byte & mask.MAINBRUSH |
||
92 | packet.wheel_overcurrent.right_wheel = byte & mask.RIGHT_WHEEL |
||
93 | packet.wheel_overcurrent.left_wheel = byte & mask.LEFT_WHEEL |
||
94 | |||
95 | // Dirt Detect (Packet ID: 15, Data Bytes: 1) |
||
96 | packet.dirt_level = packetBuffer.readInt8(pos++) |
||
97 | |||
98 | // For Packet IDs: 0 1 or 6, 1 unused byte (Packet ID: 16, Data Bytes: 1) |
||
99 | // Apparently also for 100 |
||
100 | ++pos |
||
101 | |||
102 | // Infrared Character Omni (Packet ID: 17, Data Bytes: 1, unsigned) |
||
103 | packet.remote = packetBuffer.readUInt8(pos++) |
||
104 | |||
105 | // Buttons (Packet ID: 18, Data Bytes: 1, unsigned) |
||
106 | // These toggle only when the button is held down! |
||
107 | byte = packetBuffer.readUInt8(pos++) |
||
108 | mask = PACKET_DECODE_MASKS.BUTTONS |
||
109 | packet.buttons = {} |
||
110 | packet.buttons.clean = byte & mask.CLEAN |
||
111 | packet.buttons.spot = byte & mask.SPOT |
||
112 | packet.buttons.dock = byte & mask.DOCK |
||
113 | packet.buttons.minute = byte & mask.MINUTE |
||
114 | packet.buttons.hour = byte & mask.HOUR |
||
115 | packet.buttons.day = byte & mask.DAY |
||
116 | packet.buttons.schedule = byte & mask.SCHEDULE |
||
117 | packet.buttons.clock = byte & mask.CLOCK |
||
118 | |||
119 | // Distance (Packet ID: 19, Data Bytes: 2, signed) |
||
120 | packet.distance = packetBuffer.readInt16BE(pos) |
||
121 | pos += 2 |
||
122 | |||
123 | // Angle (Packet ID: 20, Data Bytes: 2, signed) |
||
124 | packet.angle = packetBuffer.readInt16BE(pos) |
||
125 | pos += 2 |
||
126 | |||
127 | // Charging State (Packet ID: 21, Data Bytes: 1, unsigned) |
||
128 | packet.battery = {} |
||
129 | packet.battery.charging_state = packetBuffer.readUInt8(pos++) |
||
130 | |||
131 | // Voltage (Packet ID: 22, Data Bytes: 2, unsigned) |
||
132 | packet.battery.voltage = packetBuffer.readUInt16BE(pos) |
||
133 | pos += 2 |
||
134 | |||
135 | // Current (Packet ID: 23, Data Bytes: 2, signed) |
||
136 | packet.battery.current = packetBuffer.readInt16BE(pos) |
||
137 | pos += 2 |
||
138 | |||
139 | // Temperature (Packet ID: 24, Data Bytes: 1, signed) |
||
140 | packet.battery.temp = packetBuffer.readInt8(pos++) |
||
141 | |||
142 | // Battery Charge (Packet ID: 25, Data Bytes: 2, unsigned) |
||
143 | packet.battery.charge = packetBuffer.readUInt16BE(pos) |
||
144 | pos += 2 |
||
145 | |||
146 | // Battery Capacity (Packet ID: 26, Data Bytes: 2, unsigned) |
||
147 | packet.battery.capacity = packetBuffer.readUInt16BE(pos) |
||
148 | pos += 2 |
||
149 | |||
150 | // Wall Signal (Packet ID: 27, Data Bytes: 2, unsigned) |
||
151 | packet.wall_signal = packetBuffer.readUInt16BE(pos) |
||
152 | pos += 2 |
||
153 | |||
154 | // cliff signals (Packet IDs: 28-31, Data Bytes: 2, unsigned) |
||
155 | packet.cliff_signal = {} |
||
156 | packet.cliff_signal.left = packetBuffer.readUInt16BE(pos) |
||
157 | pos += 2 |
||
158 | packet.cliff_signal.front_left = packetBuffer.readUInt16BE(pos) |
||
159 | pos += 2 |
||
160 | packet.cliff_signal.front_right = packetBuffer.readUInt16BE(pos) |
||
161 | pos += 2 |
||
162 | packet.cliff_signal.right = packetBuffer.readUInt16BE(pos) |
||
163 | pos += 2 |
||
164 | |||
165 | // Unused (Packet IDs: 32 - 33, Data Bytes: 3) |
||
166 | pos += 3 |
||
167 | |||
168 | // Charging Sources Available (Packet ID: 34, Data Bytes: 1, unsigned) |
||
169 | byte = packetBuffer.readUInt8(pos++) |
||
170 | mask = PACKET_DECODE_MASKS.CHARGING_SOURCES |
||
171 | packet.charging_source = {} |
||
172 | packet.charging_source.internal_charger = byte & mask.INTERNAL_CHARGER |
||
173 | packet.charging_source.home_base = byte & mask.HOME_BASE |
||
174 | |||
175 | // OI Mode (Packet ID: 35, Data Bytes 1, unsigned) |
||
176 | packet.oi_mode = packetBuffer.readUInt8(pos++) |
||
177 | |||
178 | // Song Number (Packet ID: 36, Data Bytes: 1, unsigned) |
||
179 | packet.song = {} |
||
180 | packet.song.number = packetBuffer.readUInt8(pos++) |
||
181 | |||
182 | // Song Playing (Packet ID: 37, Data Bytes: 1, unsigned) |
||
183 | packet.song.playing = packetBuffer.readUInt8(pos++) |
||
184 | |||
185 | // Number of Stream Packets (Packet ID: 38, Data Bytes: 1, unsigned) |
||
186 | packet.stream_packets = packetBuffer.readUInt8(pos++) |
||
187 | |||
188 | // Requested Velocity (Packet ID: 39, Data Bytes: 2, signed) |
||
189 | packet.last_requested_velocity = {} |
||
190 | packet.last_requested_velocity.general = packetBuffer.readInt16BE(pos) |
||
191 | pos += 2 |
||
192 | |||
193 | // Requested Radius (Packet ID: 40, Data Bytes: 2, signed) |
||
194 | packet.requested_radius = packetBuffer.readInt16BE(pos) |
||
195 | pos += 2 |
||
196 | |||
197 | // Requested Right Velocity (Packet ID: 41, Data Bytes: 2, signed) |
||
198 | packet.last_requested_velocity.right = packetBuffer.readInt16BE(pos) |
||
199 | pos += 2 |
||
200 | |||
201 | // Requested Left Velocity (Packet ID: 42, Data Bytes: 2, signed) |
||
202 | packet.last_requested_velocity.left = packetBuffer.readInt16BE(pos) |
||
203 | pos += 2 |
||
204 | |||
205 | // Left Encoder Counts (Packet ID: 43, Data Bytes: 2, signed) |
||
206 | packet.encoder = {} |
||
207 | packet.encoder.left = packetBuffer.readInt16BE(pos) |
||
208 | pos += 2 |
||
209 | |||
210 | // Right Encoder Counts (Packet ID: 44, Data Bytes: 2, signed) |
||
211 | packet.encoder.right = packetBuffer.readInt16BE(pos) |
||
212 | pos += 2 |
||
213 | |||
214 | // Light Bumper (Packet ID: 45, Data Bytes: 1, unsigned) |
||
215 | byte = packetBuffer.readUInt8(pos++) |
||
216 | mask = PACKET_DECODE_MASKS.LIGHT_BUMPER |
||
217 | packet.light_bumper = {} |
||
218 | packet.light_bumper.left = byte & mask.LIGHT_BUMPER_LEFT |
||
219 | packet.light_bumper.front_left = byte & mask.LIGHT_BUMPER_FRONT_LEFT |
||
220 | packet.light_bumper.center_left = byte & mask.LIGHT_BUMPER_CENTER_LEFT |
||
221 | packet.light_bumper.center_right = byte & mask.LIGHT_BUMPER_CENTER_RIGHT |
||
222 | packet.light_bumper.front_right = byte & mask.LIGHT_BUMPER_FRONT_RIGHT |
||
223 | packet.light_bumper.right = byte & mask.LIGHT_BUMPER_RIGHT |
||
224 | |||
225 | // Light Bump Left Signal (Packet ID: 46, Data Bytes: 2, unsigned) |
||
226 | packet.light_bump_signal = {} |
||
227 | packet.light_bump_signal.left = packetBuffer.readUInt16BE(pos) |
||
228 | pos += 2 |
||
229 | |||
230 | // Light Bump Front Left Signal (Packet ID: 47, Data Bytes: 2, unsigned) |
||
231 | packet.light_bump_signal.front_left = packetBuffer.readUInt16BE(pos) |
||
232 | pos += 2 |
||
233 | |||
234 | // Light Bump Center Left Signal (Packet ID: 48, Data Bytes: 2, unsigned) |
||
235 | packet.light_bump_signal.center_left = packetBuffer.readUInt16BE(pos) |
||
236 | pos += 2 |
||
237 | |||
238 | // Light Bump Center Right Signal (Packet ID: 49, Data Bytes: 2, unsigned) |
||
239 | packet.light_bump_signal.center_right = packetBuffer.readUInt16BE(pos) |
||
240 | pos += 2 |
||
241 | |||
242 | // Light Bump Front Right Signal (Packet ID: 50, Data Bytes: 2, unsigned) |
||
243 | packet.light_bump_signal.front_right = packetBuffer.readUInt16BE(pos) |
||
244 | pos += 2 |
||
245 | |||
246 | // Light Bump Right Signal (Packet ID: 51, Data Bytes: 2, unsigned) |
||
247 | packet.light_bump_signal.right = packetBuffer.readUInt16BE(pos) |
||
248 | pos += 2 |
||
249 | |||
250 | // Infrared Character Left (Packet ID: 52, Data Bytes: 1, unsigned) |
||
251 | packet.infrared = {} |
||
252 | packet.infrared.character = {} |
||
253 | packet.infrared.character.left = packetBuffer.readUInt8(pos++) |
||
254 | |||
255 | // Infrared Character Left (Packet ID: 53, Data Bytes: 1, unsigned) |
||
256 | packet.infrared.character.right = packetBuffer.readUInt8(pos++) |
||
257 | |||
258 | // Left Motor Current (Packet IDs: 54 - 57, Data Bytes: 2, signed) |
||
259 | packet.motor_current = {} |
||
260 | packet.motor_current.left = packetBuffer.readInt16BE(pos) |
||
261 | pos += 2 |
||
262 | packet.motor_current.right = packetBuffer.readInt16BE(pos) |
||
263 | pos += 2 |
||
264 | packet.motor_current.main_brush = packetBuffer.readInt16BE(pos) |
||
265 | pos += 2 |
||
266 | packet.motor_current.side_brush = packetBuffer.readInt16BE(pos) |
||
267 | pos += 2 |
||
268 | |||
269 | // Stasis (Packet ID: 58, Data Bytes: 1) |
||
270 | packet.stasis = packetBuffer.readUInt8(pos++) |
||
271 | |||
272 | // Check whether the complete packet has been consumed. |
||
273 | if (pos != SENSOR_PACKET_SIZE) { |
||
274 | callback(null, pos, 'Insuficient data to assemble a complete packet') |
||
275 | return |
||
276 | } |
||
277 | |||
278 | // Return the data |
||
279 | callback(packet, pos, null) |
||
280 | } |
||
281 | |||
282 | module.exports = { |
||
283 | PACKET_DECODE_MASKS: PACKET_DECODE_MASKS, |
||
284 | SENSOR_PACKET_SIZE: SENSOR_PACKET_SIZE, |
||
285 | decode_all_sensors: decode_all_sensors |
||
286 | } |