roomba-iot – Blame information for rev 3

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