Hardware components | ||||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 2 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 | ||||
| × | 1 |
The Particle Sumo Bot is a totally rad 3D printed Wi-Fi enabled Internet of Things robot platform! Whew. For this project I kept it simple and used a micro breadboard with a Spark Core and a Pololu motor controller, but you could go nuts with a larger half size breadboard and add some sensors. I've also kept it lean with a 4 AAA battery holder, although the chassis supports 4 AA.
Below you can see Pebble and Particle.io make it super easy to control via your Pebble smartwatch, of if you prefer to use Node.js and your keyboard there's code here for that as well!
Ideas: for a true Sumo Bot experience with two bots, the watch could send a START command, after which the bot completely ran on it's own.
Ok, enough talk let's watch the first video:
Particle Sumo Chassis (MOD3)
TB6612 Motor Driver Pinout
There is no need to use multiple GNDs, just the bottom right GND is fine.
VCC goes to 3V3 output on the Spark Core.
VMOT goes to BATT+ from the Battery Pack.
BATT+ also goes up to VIN on the Spark Core.
Spark Core GND is connected to BATT- and GND of the Motor Driver.
AO0/AO1 goes to the left motor, BO0/BO1 goes to the right motor.
PWMA on the Motor Driver will align with A6 and PWMB will align with A0.
Particle Sumo Bot Firmware
C/C++/*
A0 - PWMB (speed right)
A1 - BIN2 (direction bits)
A2 - BIN1
A3 - /STBY (motor enable)
A4 - AIN1 (direction bits)
A5 - AIN2
A6 - PMWA (speed left)
*/
#define STBY A3
#define MOTOR_L 0
#define MOTOR_R 1
#define FWD 0
#define RVS 1
#define BIN1 A2
#define BIN2 A1
#define AIN1 A4
#define AIN2 A5
#define PWM_L A6
#define PWM_R A0
int _speed = 0;
int _duration = 0;
bool _new_command = false;
void setup() {
Serial.begin(9600);
int pin;
for (pin=A0; pin<=A6; pin++) {
pinMode(pin, OUTPUT);
}
for (pin=A0; pin<=A6; pin++) {
digitalWrite(pin, LOW);
}
enableMotors(true);
Spark.function("cmd",cmdRobot);
}
void loop() {
if (_new_command == true) {
setSpeed(MOTOR_L, _speed);
setSpeed(MOTOR_R, _speed);
delay(_duration);
setSpeed(MOTOR_L, 0);
setSpeed(MOTOR_R, 0);
_new_command = false;
}
}
int cmdRobot(String c) {
// c format: x,yyy,zzz,
// x = direction (f/b/l/r)
// y = speed in percentage
// z = duration in milliseconds
//
// fblr, forward, backward, left turn, right turn
int start = 0;
int end = 0;
int speed = 0;
int duration = 0;
if (c.charAt(0) == 'f') {
setDirection(MOTOR_L, FWD);
setDirection(MOTOR_R, FWD);
Serial.println("F");
}
else if (c.charAt(0) == 'b') {
setDirection(MOTOR_L, RVS);
setDirection(MOTOR_R, RVS);
Serial.println("B");
}
else if (c.charAt(0) == 'l') {
setDirection(MOTOR_L, RVS);
setDirection(MOTOR_R, FWD);
Serial.println("L");
}
else if (c.charAt(0) == 'r') {
setDirection(MOTOR_L, FWD);
setDirection(MOTOR_R, RVS);
Serial.println("R");
}
start = c.indexOf(',');
if (start != -1) {
end = c.indexOf(',', start+1);
if (end == -1) {
return 2;
}
}
else {
return 1;
}
speed = c.substring(start+1,end).toInt();
start = end;
end = c.indexOf(',', start+1);
if (end == -1) {
return 3;
}
duration = c.substring(start+1,end).toInt();
Serial.print("Speed: ");
Serial.println(speed);
Serial.print("Duration: ");
Serial.println(duration);
_speed = speed;
_duration = duration;
_new_command = true;
return _duration;
}
void enableMotors(bool enable) {
if (enable) digitalWrite(STBY, HIGH); // enable motor output
else digitalWrite(STBY, LOW); // disable motor output
}
void setDirection(bool motor, bool dir) {
if (motor == MOTOR_L) {
if (dir == RVS) {
digitalWrite(AIN1, LOW);
digitalWrite(AIN2, HIGH);
}
else if (dir == FWD) {
digitalWrite(AIN2, LOW);
digitalWrite(AIN1, HIGH);
}
}
else if (motor == MOTOR_R) {
if (dir == FWD) {
digitalWrite(BIN1, LOW);
digitalWrite(BIN2, HIGH);
}
else if (dir == RVS) {
digitalWrite(BIN2, LOW);
digitalWrite(BIN1, HIGH);
}
}
else {
digitalWrite(AIN1, LOW); // disable all motors
digitalWrite(AIN2, LOW);
digitalWrite(BIN1, LOW);
digitalWrite(BIN2, LOW);
}
}
void setSpeed(bool motor, int speed) {
if (motor == MOTOR_L) {
analogWrite(PWM_L, map(speed,0,100,0,255) );
}
else if (motor == MOTOR_R) {
analogWrite(PWM_R, map(speed,0,100,0,255) );
}
else {
analogWrite(PWM_L, 0); // stop all motors
analogWrite(PWM_R, 0);
}
}
Pebble Watch Simply.js App
JavaScriptvar DEVICE_ID = 'YOUR_DEVICE_ID';
var ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN';
simply.on('singleClick', function(e) {
console.log(util2.format('single clicked $button!', e));
var command = '';
if ('up' == e.button) {
command = 'f,100,500,';
simply.subtitle('FORWARD!');
} else if ('select' == e.button) {
command = 'l,100,100,';
simply.subtitle('LEFT!');
}
else if ('down' == e.button) {
command = 'b,100,500,';
simply.subtitle('REVERSE!');
}
else {
return;
}
ajax(
{
url: 'https://api.particle.io/v1/devices/' + DEVICE_ID + '/cmd?access_token=' + ACCESS_TOKEN,
method: 'post',
type: 'json',
data: { a: command }
},
function(e){ console.log('ajax success: ' + e); },
function(e){ console.log('ajax failure: ' + e); }
);
});
simply.on('longClick', function(e) {
console.log(util2.format('long clicked $button!', e));
var command = '';
if ('select' == e.button) {
command = 'r,100,100,';
simply.subtitle('RIGHT!');
}
else {
return;
}
ajax(
{
url: 'https://api.particle.io/v1/devices/' + DEVICE_ID + '/cmd?access_token=' + ACCESS_TOKEN,
method: 'post',
type: 'json',
data: { a: command }
},
function(e){ console.log('ajax success: ' + e); },
function(e){ console.log('ajax failure: ' + e); }
);
});
simply.setText({
title: 'Particle bot',
body: 'Press buttons to control the bot!',
}, true);
Node.js WASD Key Driving Script
JavaScriptSave the script as bot.js in some directory on your computer.
Make sure Node.js is installed!
Run `npm install sparkjs` in the directory you saved bot.js in.
Run `npm install prompt` in the same directory.
Run the script with `node bot.js`
Login to your Particle account, and then enter the device name of the Spark Core you used on your Particle Sumo Bot. There's no need to enter the long DEVICE ID. Keep in mind this name is case sensitive. If you forgot what your device name is, you can use https://build.spark.io to find out.
Have fun!
"use strict";
var spark = require('spark');
var prompt = require('prompt');
var logged_in = false;
var DEVICE_NAME = "";
spark.on('login', function() {
var stdin = process.stdin;
stdin.setRawMode( true );
stdin.resume();
stdin.setEncoding( 'utf8' );
stdin.on( 'data', function( key ){
// ctrl-c ( end of text )
if ( key === '\u0003' ) {
console.log("EXITING NOW!");
setTimeout(function(){
process.exit();
},1000);
}
if (logged_in) {
if ( key === 'w') {
console.log("Forward 500");
spark.callFunction(DEVICE_NAME, 'cmd', 'f,100,500,');
}
else if ( key === 'a') {
console.log("Left 100");
spark.callFunction(DEVICE_NAME, 'cmd', 'l,100,100,');
}
else if ( key === 's') {
console.log("Reverse 500");
spark.callFunction(DEVICE_NAME, 'cmd', 'b,100,500,');
}
else if ( key === 'd') {
console.log("Right 100");
spark.callFunction(DEVICE_NAME, 'cmd', 'r,100,100,');
}
}
// echo the keypress, if desired
//process.stdout.write( key );
});
});
//
// Setting these properties customizes the prompt.
//
prompt.message = "SPARK LOGIN: ".cyan;
prompt.delimiter = "".cyan;
//
// Start the prompt
//
prompt.start();
//
// Get three properties from the user: username, password and device name (no need to type in that long ID)
//
prompt.get({
properties: {
username: {
description: "What's your username?".cyan
},
password: {
hidden: true,
description: "Now gimme da password!".red
},
device_name: {
description: "What's the DEVICE NAME?".green
}
}
}, function (err, result) {
//
// Log the results.
//
// console.log('Command-line input received:');
// console.log(' username: ' + result.username);
// console.log(' password: ' + result.password);
// console.log(' device_name: ' + result.device_name);
spark.login({ username: result.username, password: result.password }, function(err, body) {
logged_in = true;
console.log('API call login completed on callback:', body);
});
DEVICE_NAME = result.device_name;
});
Comments