roomba-iot

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ HEAD  →  ?path2? @ 1
/main.js
@@ -5,95 +5,62 @@
// rights of fair usage, the disclaimer and warranty conditions. //
///////////////////////////////////////////////////////////////////////////
 
// Global package definitions.
const Gpio = require('onoff').Gpio
const mqtt = require('mqtt')
const YAML = require('yamljs')
const winston = require('winston')
const SerialPort = require('serialport')
 
// Local package definitions.
const decoder = require('./lib/decoder.js')
var serialport = require("serialport")
var SerialPort = serialport.SerialPort
 
var SerialPort = require('serialport');
var port = new SerialPort('/dev/serial0', {
baudRate: 115200
});
 
// Load configuration file.
const config = YAML.load('config.yml')
 
// Set up logger.
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File({ filename: config.log })
]
});
winston.add(winston.transports.File, {filename: config.log})
 
// Initiate connection to MQTT.
const mqttClient = mqtt.connect(config.mqtt.url)
const client = mqtt.connect(config.mqtt.url, {queueQoSZero: false})
 
// Set up serial port.
var port = new SerialPort(config.serial_device, {
// Roomba 4xx and Dirt Dog: 57600
// Roomba 5xx and 7xx: 115200
baudRate: 115200,
dataBits: 8,
parity: 'none',
stopBits: 1,
flowControl: false
})
 
// Set up sensor packet assembler.
port.on('data', PublishSensorPacket);
 
// Set up GPIO wake pin.
const wakeGPIO = new Gpio(config.wake, 'out')
// Wake up Roomba.
RoombaWake(wakeGPIO, function () {
// Switch to safe mode.
RoombaSafe(function () {
// Poll sensors periodically.
setInterval(function () {
port.write(new Buffer.from(COMMAND_SEQUENCES['query_all_sensors']))
}, config.sensor_poll_interval)
})
})
 
// Subscribe to configured topic when client connects and start polling sensors.
mqttClient.on('connect', function () {
logger.info('Connected to MQTT server')
mqttClient.subscribe(config.mqtt.topic, function (error) {
if (error) {
logger.error('Unable to subscribe to MQTT topic')
}
logger.info("Subscribed to MQTT topic " + config.mqtt.topic)
})
client.on('connect', function () {
winston.info('Connected to MQTT server')
client.subscribe(config.mqtt.topic)
})
 
// Execute command when message is received on configured topic.
mqttClient.on('message', function (topic, message) {
if (message.length === 0)
return
client.on('message', function (topic, message) {
if(message.length === 0)
return;
 
// Remove any retained message.
client.publish(topic, "", {retain: true})
 
message = message.toString()
 
logger.debug('Received message: ' + message)
winston.debug('Received message: ' + message)
 
// Skip commands that do not figure in the command sequence table.
if (!(message in COMMAND_SEQUENCES))
return
if(!(message in config.ROOMBA)) {
winston.warn('Unknown Roomba command: ' + message)
return;
}
 
logger.info('Executing Roomba command: ' + message)
winston.info('Executing Roomba command: ' + message)
 
// Execute Roomba command in safe mode.
RoombaSafe(function () {
port.write(new Buffer.from(COMMAND_SEQUENCES[message]))
})
})
// Wake up Roomba.
RoombaWake(wakeGPIO)
 
mqttClient.on('reconnect', () => {
logger.info('Reconnecting to MQTT server...')
// Execute Roomba command.
RoombaExecute(message)
})
 
/*
* Wake Roomba device.
* Sequence:
* 1. Set DD to HIGH
* 2. Wait 100ms
@@ -101,89 +68,41 @@
* 4. Wait 500ms
* 5. Set DD to HIGH
*/
function RoombaWake(gpio, callback) {
function RoombaWake(gpio) {
gpio.write(1, err => {
if (err) {
logger.error('Could not wake Roomba!')
return
if(err) {
winston.error('Could not wake Roomba!')
return;
}
 
setTimeout(function () {
gpio.write(0, function (err) {
if (err) {
logger.error('Could not wake Roomba!')
return
}
setTimeout(function () {
gpio.write(1, function (err) {
if (err) {
logger.error('Could not wake Roomba!')
return
}
callback()
})
}, 500)
setTimeout(function() {
gpio.write(0, function(err) {
if(err) {
winston.error('Could not wake Roomba!')
return;
}
setTimeout(function() {
gpio.write(1, function(err) {
if(err) {
winston.error('Could not wake Roomba!')
return;
}
})
}, 500)
})
}, 100)
})
}
 
/*
* Put Roomba in safe mode.
*/
function RoombaSafe(callback) {
function RoombaExecute(message) {
setTimeout(function () {
port.write(new Buffer.from(COMMAND_SEQUENCES['start']))
port.write(new Buffer(config.ROOMBA['start']));
setTimeout(function () {
port.write(new Buffer.from(COMMAND_SEQUENCES['safe']))
setTimeout(callback, 500)
}, 100)
}, 100)
port.write(new Buffer(config.ROOMBA['safe']));
setTimeout(function () {
port.write(new Buffer(config.ROOMBA[message]));
}, 500)
}, 30)
}, 30)
}
 
/*
* Build the sensor buffer as the data is read from the serial port.
*/
var packetBuffer = null
 
function PublishSensorPacket(data) {
if (packetBuffer) {
data = Buffer.concat(
[packetBuffer, data], packetBuffer.length + data.length)
}
 
if (data.length >= decoder.SENSOR_PACKET_SIZE) {
var packet = data.slice(0, decoder.SENSOR_PACKET_SIZE)
 
decoder.decode_all_sensors(packet, function (result, position, error) {
if (error) {
logger.error('Packet assembly failed with error:' + error)
return
}
 
// Push the packet onto MQTT.
mqttClient.publish(config.mqtt.topic, JSON.stringify(result))
})
 
data = data.slice(decoder.SENSOR_PACKET_SIZE)
}
 
if (data.length > 0) {
packetBuffer = data
return
}
 
packetBuffer = null
}
 
const COMMAND_SEQUENCES = {
safe: [131],
start: [128],
spot: [134],
clean: [135],
dock: [143],
query_all_sensors: [142, 100],
stream_all_sensors: [148, 1, 100],
stream_sensors_off: [150, 0],
stream_sensors_on: [150, 1]
}