roomba-iot – Blame information for rev 3
?pathlinks?
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 | } |