roomba-iot – Diff between revs 2 and 3

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 2 Rev 3
1 #!/usr/bin/env nodejs 1 #!/usr/bin/env nodejs
2 /////////////////////////////////////////////////////////////////////////// 2 ///////////////////////////////////////////////////////////////////////////
3 // Copyright (C) Wizardry and Steamworks 2018 - License: GNU GPLv3 // 3 // Copyright (C) Wizardry and Steamworks 2018 - License: GNU GPLv3 //
4 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, // 4 // Please see: http://www.gnu.org/licenses/gpl.html for legal details, //
5 // rights of fair usage, the disclaimer and warranty conditions. // 5 // rights of fair usage, the disclaimer and warranty conditions. //
6 /////////////////////////////////////////////////////////////////////////// 6 ///////////////////////////////////////////////////////////////////////////
7   7  
8 // Global package definitions. 8 // Global package definitions.
9 const Gpio = require('onoff').Gpio 9 const Gpio = require('onoff').Gpio
10 const mqtt = require('mqtt') 10 const mqtt = require('mqtt')
11 const YAML = require('yamljs') 11 const YAML = require('yamljs')
12 const winston = require('winston') 12 const winston = require('winston')
13 const SerialPort = require('serialport') 13 const SerialPort = require('serialport')
14   14  
15 // Local package definitions. 15 // Local package definitions.
16 const decoder = require('./lib/decoder.js') 16 const decoder = require('./lib/decoder.js')
17   17  
18 // Load configuration file. 18 // Load configuration file.
19 const config = YAML.load('config.yml') 19 const config = YAML.load('config.yml')
20   20  
21 // Set up logger. 21 // Set up logger.
22 winston.add(winston.transports.File, { 22 winston.add(winston.transports.File, {
23 filename: config.log 23 filename: config.log
24 }) 24 })
25   25  
26 // Initiate connection to MQTT. 26 // Initiate connection to MQTT.
27 const mqttClient = mqtt.connect(config.mqtt.url) 27 const mqttClient = mqtt.connect(config.mqtt.url)
28   28  
29 // Set up serial port. 29 // Set up serial port.
30 var port = new SerialPort(config.serial_device, { 30 var port = new SerialPort(config.serial_device, {
31 // Roomba 4xx and Dirt Dog: 57600 31 // Roomba 4xx and Dirt Dog: 57600
32 // Roomba 5xx and 7xx: 115200 32 // Roomba 5xx and 7xx: 115200
33 baudRate: 115200, 33 baudRate: 115200,
34 dataBits: 8, 34 dataBits: 8,
35 parity: 'none', 35 parity: 'none',
36 stopBits: 1, 36 stopBits: 1,
37 flowControl: false 37 flowControl: false
38 }) 38 })
39   39  
40 // Set up sensor packet assembler. 40 // Set up sensor packet assembler.
41 port.on('data', AssembleSensorPacket); 41 port.on('data', AssembleSensorPacket);
42   42  
43 // Set up GPIO wake pin. 43 // Set up GPIO wake pin.
44 const wakeGPIO = new Gpio(config.wake, 'out') 44 const wakeGPIO = new Gpio(config.wake, 'out')
45 // Wake up Roomba. 45 // Wake up Roomba.
46 RoombaWake(wakeGPIO, function () { 46 RoombaWake(wakeGPIO, function () {
47 // Switch to safe mode. 47 // Switch to safe mode.
48 RoombaSafe(function () { 48 RoombaSafe(function () {
49 // Poll sensors periodically. 49 // Poll sensors periodically.
50 setInterval(function () { 50 setInterval(function () {
51 port.write(new Buffer(COMMAND_SEQUENCES['query_all_sensors'])) 51 port.write(new Buffer(COMMAND_SEQUENCES['query_all_sensors']))
52 }, config.sensor_poll_interval) 52 }, config.sensor_poll_interval)
53 }) 53 })
54 }) 54 })
55   55  
56 // Subscribe to configured topic when client connects and start polling sensors. 56 // Subscribe to configured topic when client connects and start polling sensors.
57 mqttClient.on('connect', function () { 57 mqttClient.on('connect', function () {
58 winston.info('Connected to MQTT server') 58 winston.info('Connected to MQTT server')
59 mqttClient.subscribe(config.mqtt.topic, function (error) { 59 mqttClient.subscribe(config.mqtt.topic, function (error) {
60 if (error) { 60 if (error) {
61 winston.error('Unable to subscribe to MQTT topic') 61 winston.error('Unable to subscribe to MQTT topic')
62 } 62 }
63 winston.info("Subscribed to MQTT topic " + config.mqtt.topic) 63 winston.info("Subscribed to MQTT topic " + config.mqtt.topic)
64 }) 64 })
65 }) 65 })
66   66  
67 // Execute command when message is received on configured topic. 67 // Execute command when message is received on configured topic.
68 mqttClient.on('message', function (topic, message) { 68 mqttClient.on('message', function (topic, message) {
69 if (message.length === 0) 69 if (message.length === 0)
70 return 70 return
71   71  
72 message = message.toString() 72 message = message.toString()
73   73  
74 winston.debug('Received message: ' + message) 74 winston.debug('Received message: ' + message)
75   75  
76 // Skip commands that do not figure in the command sequence table. 76 // Skip commands that do not figure in the command sequence table.
77 if (!(message in COMMAND_SEQUENCES)) 77 if (!(message in COMMAND_SEQUENCES))
78 return 78 return
79   79  
80 winston.info('Executing Roomba command: ' + message) 80 winston.info('Executing Roomba command: ' + message)
81   81  
82 // Execute Roomba command in safe mode. 82 // Execute Roomba command in safe mode.
83 RoombaSafe(function () { 83 RoombaSafe(function () {
84 port.write(new Buffer(COMMAND_SEQUENCES[message])) 84 port.write(new Buffer(COMMAND_SEQUENCES[message]))
85 }) 85 })
86 }) 86 })
87   87  
88 /* 88 /*
89 * Wake Roomba device. 89 * Wake Roomba device.
90 * Sequence: 90 * Sequence:
91 * 1. Set DD to HIGH 91 * 1. Set DD to HIGH
92 * 2. Wait 100ms 92 * 2. Wait 100ms
93 * 3. Set DD to LOW 93 * 3. Set DD to LOW
94 * 4. Wait 500ms 94 * 4. Wait 500ms
95 * 5. Set DD to HIGH 95 * 5. Set DD to HIGH
96 */ 96 */
97 function RoombaWake(gpio, callback) { 97 function RoombaWake(gpio, callback) {
98 gpio.write(1, err => { 98 gpio.write(1, err => {
99 if (err) { 99 if (err) {
100 winston.error('Could not wake Roomba!') 100 winston.error('Could not wake Roomba!')
101 return 101 return
102 } 102 }
103   103  
104 setTimeout(function () { 104 setTimeout(function () {
105 gpio.write(0, function (err) { 105 gpio.write(0, function (err) {
106 if (err) { 106 if (err) {
107 winston.error('Could not wake Roomba!') 107 winston.error('Could not wake Roomba!')
108 return 108 return
109 } 109 }
110 setTimeout(function () { 110 setTimeout(function () {
111 gpio.write(1, function (err) { 111 gpio.write(1, function (err) {
112 if (err) { 112 if (err) {
113 winston.error('Could not wake Roomba!') 113 winston.error('Could not wake Roomba!')
114 return 114 return
115 } 115 }
116 callback() 116 callback()
117 }) 117 })
118 }, 500) 118 }, 500)
119 }) 119 })
120 }, 100) 120 }, 100)
121 }) 121 })
122 } 122 }
123   123  
124 /* 124 /*
125 * Put Roomba in safe mode. 125 * Put Roomba in safe mode.
126 */ 126 */
127 function RoombaSafe(callback) { 127 function RoombaSafe(callback) {
128 setTimeout(function () { 128 setTimeout(function () {
129 port.write(new Buffer(COMMAND_SEQUENCES['start'])) 129 port.write(new Buffer(COMMAND_SEQUENCES['start']))
130 setTimeout(function () { 130 setTimeout(function () {
131 port.write(new Buffer(COMMAND_SEQUENCES['safe'])) 131 port.write(new Buffer(COMMAND_SEQUENCES['safe']))
132 setTimeout(callback, 500) 132 setTimeout(callback, 500)
133 }, 30) 133 }, 100)
134 }, 30) 134 }, 100)
135 } 135 }
136   136  
137 /* 137 /*
138 * Build the sensor buffer as the data is read from the serial port. 138 * Build the sensor buffer as the data is read from the serial port.
139 */ 139 */
140 var packetBuffer = null 140 var packetBuffer = null
141   141  
142 function AssembleSensorPacket(data) { 142 function AssembleSensorPacket(data) {
143 if (packetBuffer) { 143 if (packetBuffer) {
144 data = Buffer.concat( 144 data = Buffer.concat(
145 [packetBuffer, data], packetBuffer.length + data.length) 145 [packetBuffer, data], packetBuffer.length + data.length)
146 } 146 }
147   147  
148 if (data.length >= decoder.SENSOR_PACKET_SIZE) { 148 if (data.length >= decoder.SENSOR_PACKET_SIZE) {
149 var packet = data.slice(0, decoder.SENSOR_PACKET_SIZE) 149 var packet = data.slice(0, decoder.SENSOR_PACKET_SIZE)
150   150  
151 decoder.decode_all_sensors(packet, function (result, position, error) { 151 decoder.decode_all_sensors(packet, function (result, position, error) {
152 if (error) { 152 if (error) {
153 winston.error('Packet assembly failed with error:' + error) 153 winston.error('Packet assembly failed with error:' + error)
154 return 154 return
155 } 155 }
156   156  
157 // Push the packet onto MQTT. 157 // Push the packet onto MQTT.
158 mqttClient.publish(config.mqtt.topic, JSON.stringify(result)) 158 mqttClient.publish(config.mqtt.topic, JSON.stringify(result))
159 }) 159 })
160   160  
161 data = data.slice(decoder.SENSOR_PACKET_SIZE) 161 data = data.slice(decoder.SENSOR_PACKET_SIZE)
162 } 162 }
163   163  
164 if (data.length > 0) { 164 if (data.length > 0) {
165 packetBuffer = data 165 packetBuffer = data
166 return 166 return
167 } 167 }
168   168  
169 packetBuffer = null 169 packetBuffer = null
170 } 170 }
171   171  
172 const COMMAND_SEQUENCES = { 172 const COMMAND_SEQUENCES = {
173 safe: [131], 173 safe: [131],
174 start: [128], 174 start: [128],
175 spot: [134], 175 spot: [134],
176 clean: [135], 176 clean: [135],
177 dock: [143], 177 dock: [143],
178 query_all_sensors: [142, 100], 178 query_all_sensors: [142, 100],
179 stream_all_sensors: [148, 1, 100], 179 stream_all_sensors: [148, 1, 100],
180 stream_sensors_off: [150, 0], 180 stream_sensors_off: [150, 0],
181 stream_sensors_on: [150, 1] 181 stream_sensors_on: [150, 1]
182 } 182 }
183   183  
184
Generated by GNU Enscript 1.6.5.90.
184
Generated by GNU Enscript 1.6.5.90.
185   185  
186   186  
187   187