roomba-iot – Blame information for rev 2

Subversion Repositories:
Rev:
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 }