Scott Beasley
Published © CC BY-NC-SA

Dustbunny - A Particle.io Controlled Rover

Dustbunny - A simple Particle.io Web/Wifi controlled rover.

IntermediateFull instructions provided12 hours2,054
Dustbunny - A Particle.io Controlled Rover

Things used in this project

Hardware components

Photon
Particle Photon
×1
L9110 H-Bridge
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
Used for looks now (not needed for web control. So optional.)
×1
Little Yellow Gear Motor (Right-angle)
×2
Wheel and hub
Make sure they are rubber tires and NOT nylon. The nylons are shiny black and the rubber dull black.
×2
4xAA battery holder
4xAA battery holder
Get one that is 2x2 and holds 4. eBay has them listed.
×1
20mm M2 screws with nuts
×4
Female/Female Jumper Wires
Female/Female Jumper Wires
10cm long. You will need at least 10
×1
Velcro
A roll of stick-on Velcro
×1
Breadboard (generic)
Breadboard (generic)
See note above about breadboard choice.
×1

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Double sided tape

Story

Read more

Custom parts and enclosures

Face

Top

Back

Left

Right

Chassis

Castor leg

Castor glide

Flair back lid

Optional castor leg

Optional castor glide

Dustbunny Example

Support braces

Photon mount

Stl files home

Schematics

dustbunny.fzz

Flair back

Note the thermal exhaust port on the bottom rear :)

dustbunny_bb.png

Code

Photon driver code

C/C++
// This #include statement was automatically added by the Particle IDE.
#include "L9110Driver/L9110Driver.h"

// Various time delays used for driving autonomously
#define TURNDELAY 475
#define BACKUPDELAY 400
#define MAXFORWARDSPEED 225

int rovMotors (String command);
int rovSpeed (String speed_in);

// Globals
L9110_Motor motor_l (D0, D2);
L9110_Motor motor_r (D1, D3);
static bool avoid_it = false;
static int speed = 200;

/* This function is called once at start up ----------------------------------*/
void setup ( )
{
    //Setup the Tinker application here

    //Register the rover functions
    Particle.function ("motors", rovMotors);
    Particle.function ("speed", rovSpeed);

	  rovMotors ("0,0"); // Make sure the motors are off at startup
}

/* This function loops forever --------------------------------------------*/
void loop ( )
{

}

int rovMotors (String command)
{
	//convert ascii to integer
	int mcmd = command.charAt(0) - '0';
	//Sanity check to see if the direction numbers are within limits
	if (mcmd < 0 || mcmd > 5)
	    return -1;

    if (mcmd == 0) // Stop
    {
       	motor_l.run (BRAKE);
        motor_r.run (BRAKE);
    } 
    
    else if (mcmd == 1) // Forward
    {
       	motor_l.run (FORWARD | RELEASE);
        motor_r.run (FORWARD | RELEASE);
    }
    
    else if (mcmd == 2) // Backward
    {
       	motor_l.run (BACKWARD | RELEASE);
        motor_r.run (BACKWARD | RELEASE);
    }
    
    else if (mcmd == 3) // Left turn
    {
       	motor_l.run (BACKWARD | RELEASE);
        motor_r.run (FORWARD | RELEASE);
    }

    else if (mcmd == 4) // Right turn
    {
       	motor_l.run (FORWARD | RELEASE);
        motor_r.run (BACKWARD | RELEASE);
    }

    if (mcmd != 0)
    {
        String value = command.substring(2);
        motor_l.setSpeed (speed);
        motor_r.setSpeed (speed);
        int delay_time = value.toInt();
        if (delay_time != 0) 
        {
           delay (delay_time);
           motor_l.run (BRAKE);
           motor_r.run (BRAKE);
        }
    }

    return (0);
}

int rovSpeed (String speed_in)
{
    speed = speed_in.toInt ( );

//        motor_l.run (BRAKE);
//        motor_r.run (BRAKE);

    return (0);
}

Rover html/js driver page for local network use

HTML
Driver for playing with the bot from your local network.
<!--
   Photon based rover driver page
   Written by Scott Beasley - 2016
   Free to use or modify. Enjoy!
   SEE BELOW FOR USAGE WARNING.
-->
<html>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"
          type="text/javascript" charset="utf-8">
  </script>
<head>
<title>Dustbunny Local Control page</title>
<script type="text/javascript">
document.onkeypress = keypressed;
document.onload = initBot;
// WARNING!!!!! DO NOT RUN IN THE DMZ!
// This script should only be run locally on your computer due to the device
// and access token being used to talk to the robot. Again, this is only for
// local use!
var deviceID    = "ADD YOUR ID";
var accessToken = "ADD YOUR TOKEN";
// !!!!!WARNING END
var last_action = "";
var start_dt = new Date ( );
var last_mills = start_dt.getTime ( );
var run_time = "750"
var turn_tm = 166
var auto_state = false

function sentBotCommand (action) {
   var run_dt = new Date ( );
   var mills = run_dt.getTime ( );
   // If a dup, ignore if newer than 500ms
   if (action == last_action && (mills - last_mills) <= 500)
      return;

  console.log ("Action = " + action)
   last_mills = mills;
   last_action = action;
   if (action == 'stop') {
      motors ("0", "0")
      run_time = "750"
   }
   else if (action == 'forward')
      motors ("1", run_time)
   else if (action == 'backward')
      motors ("2", run_time)
   else if (action == 'left')
      motors ("3", 500 - (turn_tm / 2))
   else if (action == 'right')
      motors ("4", 500 - (turn_tm / 2))
   else if (action == 'ltforward')
      motors ("3", 250 - (turn_tm / 2))
   else if (action == 'rtforward')
      motors ("4", 250 - (turn_tm / 2))
   else if (action == 'speed_low') {
      setSpeed ("150")
      turn_tm = 83
   }
   else if (action == 'speed_mid') {
      setSpeed ("200")
      turn_tm = 166
   }
   else if (action == 'speed_hi') {
      setSpeed ("255")
      turn_tm = 332
   }
   else if (action == 'short_time') {
      run_time = "300"
   }
   else if (action == 'mid_run') {
      run_time = "750"
   }
   else if (action == 'continuous') {
      run_time = "0"
   }
   else if (action == 'auto_off') {
      setAuto ("off")
      motors ("0", "0");
   }
   else if (action == 'auto_on') {
      setAuto ("on")
      motors ("1", "0");
   }
}
function keypressed (event) {
   var keyCd = ('charCode' in event) ? event.charCode : event.keyCode;
   if (keyCd == 113 || keyCd == 81)  // q
      sentBotCommand ('ltforward')
   if (keyCd == 119 || keyCd == 87)  // w
      sentBotCommand ('forward')
   if (keyCd == 122 || keyCd == 90)  // z
      sentBotCommand ('backward')
   if (keyCd == 101 || keyCd == 69)  // e
      sentBotCommand ('rtforward')
   if (keyCd == 97 || keyCd == 65)   // a
      sentBotCommand ('left')
   if (keyCd == 104 || keyCd == 72)  // h
      sentBotCommand ('stop')
   if (keyCd == 115 || keyCd == 83)  // s
      sentBotCommand ('right')
}
function initBot (event) {
   setSpeed ("200");
   motors ("0", "0");
}
function motors (direction, run_time) {
   var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/motors";
   $.post( requestURL, { params: direction+","+run_time, access_token: accessToken });
}
function setSpeed (speed) {
   var requestURL = "https://api.spark.io/v1/devices/" + deviceID + "/speed";
   $.post( requestURL, { params: speed, access_token: accessToken });
}
</script>
</head>
<center>
   Dustbunny Local Control page
   <br><br>
   <Font face="Arial">
   <table name="Table" border="0" cellpadding="6">
      <tr>
         <td align="center">
            <input type="radio" name="run_time" value="42" id="short_time"
            onclick="sentBotCommand ('short_time');">Short run
         </td>
         <td align="center">
            <input type="radio" name="run_time" value="84" id="mid_run"
            onclick="sentBotCommand ('mid_run');" checked>Med run
         </td>
         <td align="center">
            <input type="radio" name="run_time" value="126" id="continuous"
            onclick="sentBotCommand ('continuous');">Continuous
         </td>
      </tr>
      <tr>
         <td align="center">
            Left-Forward
            <br>
            <input type="hidden" name="ltforward" value="0" id="ltforward" />
            <input type="image" src="images/arrow_up_left.png"
            id="imgLtforward" onclick="sentBotCommand ('ltforward');"/>
         </td>
         <td align="center">
            Forward
            <br>
            <input type="hidden" name="forward" value="0" id="forward" />
            <input type="image" src="images/forward.png" id="imgforward"
            onclick="sentBotCommand ('forward');"/>
         </td>
         <td align="center">
            Right-Forward
            <br>
            <input type="hidden" name="rtforward" value="0" id="rtforward" />
            <input type="image" src="images/arrow_up_right.png"
            id="imgRtforward" onclick="sentBotCommand ('rtforward');"/>
         </td>
      </tr>
      <tr>
         <td align="center">
            Left
            <br>
            <input type="hidden" name="left" value="0" id="left" />
            <input type="image" src="images/left.png" id="imgleft"
            onclick="sentBotCommand ('left');"/>
         </td>
         <td align="center">
            Stop
            <br>
            <input type="hidden" name="stop" value="0" id="stop" />
            <input type="image" src="images/stop.png" width="50" id="imgstop"
            onclick="sentBotCommand ('stop');"/>
         </td>
         <td align="center">
            Right
            <br>
            <input type="hidden" name="right" value="0" id="right" />
            <input type="image" src="images/right.png" id="imgright"
            onclick="sentBotCommand ('right');"/>
         </td>
      </tr>
      <tr>
         <td align="center">
            Left-Backward
            <br>
            <input type="hidden" name="ltforward" value="0" id="ltforward" />
            <input type="image" src="images/arrow_down_left.png"
            id="imgltback" onclick="sentBotCommand ('ltforward');"/>
         </td>
         <td align="center">
            Backward
            <br>
            <input type="hidden" name="backward" value="0" id="backward" />
            <input type="image" src="images/backward.png" id="imgbackward"
            onclick="sentBotCommand ('backward');"/>
         </td>
         <td align="center">
            Right-Backward
            <br>
            <input type="hidden" name="rtforward" value="0" id="rtforward" />
            <input type="image" src="images/arrow_down_right.png"
            id="imgrtback" onclick="sentBotCommand ('rtforward');"/>
         </td>
      </tr>
      <tr>
         <td align="center">
            <input type="radio" name="speed" value="42" id="speed_low"
            onclick="sentBotCommand ('speed_low');">Low speed
         </td>
         <td align="center">
            <input type="radio" name="speed" value="84" id="speed_mid"
            onclick="sentBotCommand ('speed_mid');" checked>Mid speed
         </td>
         <td align="center">
            <input type="radio" name="speed" value="126" id="speed_hi"
            onclick="sentBotCommand ('speed_hi');">High speed
         </td>
      </tr>
   </table>
   </font>
</center>
</html>

Python/Flask driver code for WEB driving the bot.

Python
Use this to drive the bot from the WEB.
#!/usr/bin/env python

#   Photon based rover driver Web app
#   Written by Scott Beasley - 2016
#   Free to use or modify. Enjoy!

from flask import Flask, render_template # request
import time
import requests
import meinheld

DEVICE_ID = '<YOUR DEV ID>'
ACCESS_TOKEN = '<YOUR ACCESS TOKEN>'

API_URL = 'https://api.spark.io/v1/devices'
DEVICE_URL = API_URL+'/'+DEVICE_ID

# Speed and drive control variables
last_direction = -1
speed_offset = 84
turn_tm = 166
run_time = 750

application = Flask (__name__, static_url_path = "", static_folder = "images")

@application.route("/")
def index ( ):
   return render_template ('index.html', name=None)

@application.route ("/forward")
def forward ( ):
   global run_time

   print "Forward"
   motors ("1", run_time)

   # sleep one second
   time.sleep (1)
   return "ok" # keep flask happy with a return.

@application.route ("/backward")
def backward ( ):
   global run_time

   print "Backward"
   motors ("2", run_time)

   # sleep one second
   time.sleep (1)
   return "ok"

@application.route ("/left")
def left ( ):
   global speed_offset, last_direction, turn_tm

   print "Left"
   motors ("3", 500 - (turn_tm / 2))

   # sleep one second
   time.sleep (1)
   return "ok"

@application.route ("/right")
def right ( ):
   global speed_offset, last_direction, turn_tm

   print "Right"
   motors ("4", 500 - (turn_tm / 2))

   # sleep one second
   time.sleep (1)
   return "ok"

@application.route ("/ltforward")
def ltforward ( ):
   global speed_offset, last_direction, turn_tm

   print "Left 45deg turn"
   motors ("3", 250 - (turn_tm / 2))

   # sleep one second

   time.sleep (1)
   return "ok"

@application.route ("/rtforward")
def rtforward ( ):
   global speed_offset, last_direction, turn_tm

   print "Right 45deg turn"
   motors ("4", 250 - (turn_tm / 2))

   # sleep one second
   time.sleep (1)
   return "ok"

@application.route ("/stop")
def stop ( ):
   print "Stop"
   motors ("0", "0")

   # sleep one second
   time.sleep (1)
   return "ok"

@application.route ("/speed_low")
def speed_low ( ):
   global speed_offset, last_direction, turn_tm

   speed_offset = 42
   turn_tm = 83

   setspeed (150)
   # sleep 1 second
   time.sleep (01)
   return "ok"

@application.route ("/speed_mid")
def speed_mid ( ):
   global speed_offset, last_direction, turn_tm

   speed_offset = 84
   turn_tm = 166

   setspeed (195)
   # sleep 1 second
   time.sleep (1)
   return "ok"

@application.route ("/speed_hi")
def speed_hi ( ):
   global speed_offset, last_direction, turn_tm

   speed_offset = 126
   turn_tm = 332

   setspeed (250)
   # sleep 1 second
   time.sleep (1)
   return "ok"

@application.route ("/continuous")
def continuous ( ):
   global run_time

   print "Continuous run"
   run_time = 0

   # sleep 1 second
   time.sleep (1)
   return "ok"

@application.route ("/mid_run")
def mid_run ( ):
   global run_time

   print "Mid run"
   run_time = 750

   # sleep 1 second
   time.sleep (1)
   return "ok"

@application.route ("/short_time")
def short_time ( ):
   global run_time

   print "Short run"
   run_time = 300

   # sleep 1 second
   time.sleep (1)
   return "ok"

def motors (direction, run_time):
    r = requests.post(DEVICE_URL+'/motors',
        data={'access_token':ACCESS_TOKEN, 'args':direction+','+str(run_time)})
    ret_json = r.json ( )
    return_val = ret_json['return_value']
    return int(return_val)

def setspeed (speed):
    r = requests.post(DEVICE_URL+'/motors',
        data={'access_token':ACCESS_TOKEN, 'args':'5,'+str(speed)})
    ret_json = r.json ( )
    return_val = ret_json['return_value']
    return int(return_val)

def digitalWrite (pin, state):
    r = requests.post(DEVICE_URL+'/digitalwrite',
        data={'access_token':ACCESS_TOKEN, 'args':pin+','+state})
    ret_json = r.json ( )
    return_val = ret_json['return_value']
    return int(return_val)

def digitalRead (pin):
    r = requests.post(DEVICE_URL+'/digitalread',
        data={'access_token':ACCESS_TOKEN, 'args':pin})
    ret_json = r.json ( )
    return_val = ret_json['return_value']
    return int(return_val)

if __name__ == "__main__" :
   #application.run (host='0.0.0.0', port=8080, debug=False)
   #application.run (host='0.0.0.0')
   meinheld.listen(("0.0.0.0"))
   meinheld.run(app)

L9110 driver code for Particle boards

L9110 driver code for use with Particle boards and environment.

Dustbunny rover code

Code for the rover with web pages and apps.

Credits

Scott Beasley

Scott Beasley

9 projects • 61 followers
Well odd

Comments