loading

Building and playing with robots is my main guilty pleasure in life. Others play golf or ski, but I build robots (since I can't play golf or ski :-). I find it relaxing and fun! To make most of my bots, I use chassis kits. Using kits helps me do what I like doing more, the software and electronics and also makes for a better chassis for my all-thumbs self.

In this Instructable, we will look in what it takes to make a simple but robust Wifi/web controlled rover. The chassis used is the Actobotics Gooseneck. I chose it for it's size, expand-ability and cost but you can use any other chassis of your own choosing.

For a project like this, we will need a good solid single board computer and for this bot I chose to use the Raspberry Pi (RPI) a Linux based computer. The RPI (and Linux) gives us lots of coding options and Python will be used for the coding side. For the web interface I use Flask, a lightweight web framework for Python.

To drive the motors, I chose a RoboClaw 2x5a. It allows for simple serial communication for commanding it and works well with the RPI and the motors on the Gooseneck.

Finally, it has a webcam for POV type video feedback for driving it remotely. I will cover each topic in more detail later.

Step 1: Hardware Needed

  • Actobotics Gooesneck chassis or a suitable replacement of your choice
  • Raspberry Pi of your choice (or clone) - An RPI model B is used on this bot, but any with at least two USB ports will work
  • Standard Servo Plate B x1
  • 90° Single Angle Channel Bracket x1
  • RoboClaw 2x5a motor driver
  • S3003 or similar standard size servo
  • Small breadboard or Mini breadboard
  • Female to Female jumper wires
  • Male to Female jumper wires
  • Web cam (optional) - I use a Logitech C110, and here is a list of supported cams for the RPI
  • 5v-6v power source for servo power
  • 7.2v-11.1v battery for drive motor powering
  • 5v 2600mah (or higher) USB power bank for the RPI
  • USB Wifi adapter

On my bot, I use 4" wheels to make it a little more All-Terrain-Indoor. For this option you will need:

Step 2: Assembling the Chassis

First assemble the chassis following the instructions included with the chassis or video. After finishing you should have something like the image. NOTE: When assembling the Neck part, just leave the mounting bracket off.

On my bot, I chose to replace the wheels that the chassis came with for 4" heavy duty wheels. This is optional and not needed unless you want to do the same.

Step 3: Mounting the Electronics

The Gooseneck has a lot of room and options for mounting your electronics. I give you these pictures as a guide line, but you can choose how you would like to lay it all out. You can use stand-offs, double-sided tape, Velcro or servo-tape to mount the board and batteries.

Step 4: Adding the Webcam

Take the 90 degree bracket, lightweight servo hub and four (4) of the .3125" screws for this step:

  • Take the servo hub and place it on one side of the bracket and secure them together with the .2125" screws like pictured
  • Next mount the servo into the servo bracket
  • Attach the 90 degree bracket with the servo horn to the servos spine and use the horn screw that came with the servo to connect them together
  • Now mount the Servo in bracket onto the top of the goose-neck with the remaining screws
  • Mount camera with zip-ties or double sided tape on to the 90 degree bracket

Use the pictures for guides if needed.

Step 5: Wiring It All Up

The wiring is fairly strait forward for this robot.

The Motors:

  • Solder leads on both motors if you have not done so already

With the robots front (the end with the goose-neck) facing away from you:

  • Connect the motor wires on the left motor to the channel M1A and M1B
  • Connect the motor wires on the right motor to the channel M2A and M2B

Ground (GND) connections:

  • Connect one ground pin on the RoboClaw to the ground jumper board. The ground pin line on the RoboClaw is closest to the center (See pic)
  • Connect PIN 6 on the RPI to the jumper board. See the RPI header pic for pin assignments.
  • Connect the GND from the servo battery pack to one of the pins on the jumper board.
  • Run a jumper wire from the jumper board to the servos GND wire.

RPI to RoboClaw:

  • Connect the RPI GPIO14 TXD pin to RoboClaw S1 pin

Power:

  • Connect the POS wire from the servo battery to the servos POS lead
  • Connect the POS wire from the motor battery to POS (+) of the RoboClaw motor power input terminal. We will leave the GND terminal disconnected for now.

Step 6: Setting Up the RPI

I assume the user here knows some about Linux and the RPI. I do not cover how to setup or connect to one. If you need help with that then use the pages below.

To get your RPI setup, have a look at the following pages:

For general jump-off pages, The RPI main page and the eLinux pages are great places to start.

See this link for RPI general Wifi setup.

If you plan on using some sort of camera or web cam on the bot, have a look at these pages to get the basic needed files.

Streaming video:

There are a few ways to get video streaming working on a RPI, but the method I prefer is using Motion.

To install it on your RPI run this: sudo apt-get install motion

This instrucatable goes over setting it up for streaming as well.

Step 7: Configuring the RPI Serial Port

We will need to disable the Linux console mode for using the RX and TX as we want to talk to the RoboClaw motor controller from this port. To do this, you can use this method or this tool. The choice is yours on the method as they both do the same thing in the end.

Step 8: Installing the Python Modules

You will need python installed on the RPI as well as the python package installer pip.

To install pip do:

  1. sudo apt-get install python-setuptools
  2. sudo easy_install pip

Then:

  1. sudo pip install flask
  2. sudo pip install pyserial
  3. sudo pip install RPIO

This will be all the modules needed for the code to run.

Step 9: Setting Up the RoboClaw

I have the robot code talking to the RoboClaw in Standard Serial Mode at 19200 baud.

To set the RoboClaw up for this do:

  1. Hit the "MODE" button on the RoboClaw
  2. Hit the set button until the LED flashes 5 (five) times between the delays
  3. Hit the "LIPO" button to store
  4. Next hit the "SET" button until the LED flashes 3 (three) times between the delays
  5. Hit the LIPO button to store

That's it for setting up the motor controller. See the pdf linked above for more info if needed.

Step 10: Installing the Rover Program/files

Download and copy the rover.zip file to your RPI in your pi user directory.

If you are running Linux or a Mac, you can use 'scp' to do it:

scp ~/location/of/the/file/rover.zip pi@your_rpi_ip:/~

For Windows, you can download and use pscp and then do:

pscp /location/of/the/file/rover.zip pi@your_rpi_ip:/~

Once the zipfile is copied over to the RPI, log into it as the pi user.

Now run:

unzip rover.zip

This will unzip the files to a folder named 'rover' and have the following under that folder:

  • restrover.py (The python code for the robot)
  • static (holds the image files for the buttons on the control page)
  • templates (holds the index.htlm file, the control web page)

If you are using a web cam, modify line near the bottom of the index.html file in the template folder. Change the URL in the IFRAME line to match the src URL for your video stream.

Step 11: Starting the Bot Up

Connect the USB power to the RPI.

To start the bot code up, log in as the pi user and run:

  • cd rover
  • sudo python restrover.py

If all was OK, you should see a screen similar to the image in this step

If you see any errors or issues, you will have to fix them before going forward.

Now, connect the the GND (-) wire to the NEG (-) terminal on the RoboClaw motor power input.

Step 12: Accessing the Bot Control Page

After the robot's python script is running, power up the RoboClaw and then navigate to your RPI's ip like:

http://your_rpi_ip

You should see the Web control page pop up like in the images. If not, check your RPI output terminal and look for any errors and correct them.

Once on the page, you are ready to control the bot.

The robot will start in the "Med run" setting and at the Medium speed.

The bot can be controlled via the buttons on the page or by keys on the keyboard.

The keys are:

  • w - forward
  • z - reverse/backward
  • a - long left turn
  • s - long right turn
  • q - short left turn
  • e - short right turn
  • 1 - pan camera left
  • 2 - pan camera right
  • 3 - pan full left
  • 4 - pan full right
  • / - home/center camera
  • h - halt/stop robot

There is an half second delay buffer between commands sent. I did this to eliminate unwanted repeated commands. You can of course remove this from the code if you like (in index.html)

The rest of the controls and controlling it should be self explanatory.

Step 13: The Python/Flask Code

This bot uses Python and the Flask web framework. You can learn more about Flask here if you are interested.

The big difference from a Flask app and normal Python script is @app.route class/method used to do the URI handling. Other than that it's pretty much normal Python for the most part.

#!/usr/bin/env python

#
# Wifi/Web driven Rover
#
# Written by Scott Beasley - 2015
#
# Uses RPIO, pyserial and Flask
#

import time
import serial
from RPIO import PWM
from flask import Flask, render_template, request

app = Flask (__name__, static_url_path = '')

# Connect to the comm port to talk to the Roboclaw motor controller
try:
   # Change the baud rate here if different than 19200
   roboclaw = serial.Serial ('/dev/ttyAMA0', 19200)
except IOError:
   print ("Comm port not found")
   sys.exit (0)

# Speed and drive control variables
last_direction = -1
speed_offset = 84
turn_tm_offset = 0.166
run_time = 0.750

# Servo neutral position (home)
servo_pos = 1250
servo = PWM.Servo ( )
servo.set_servo (18, servo_pos)

# A little dwell for settling down time
time.sleep (3)

#
# URI handlers - all the bot page actions are done here
#

# Send out the bots control page (home page)
@app.route ("/")
def index ( ):
   return render_template ('index.html', name = None)

@app.route ("/forward")
def forward ( ):
   global last_direction, run_time

   print "Forward"
   go_forward ( )
   last_direction = 0

   # sleep 100ms + run_time
   time.sleep (0.100 + run_time)

   # If not continuous, then halt after delay
   if run_time > 0:
      last_direction = -1
      halt ( )

   return "ok"

@app.route ("/backward")
def backward ( ):
   global last_direction, run_time

   print "Backward"
   go_backward ( )
   last_direction = 1

   # sleep 100ms + run_time
   time.sleep (0.100 + run_time)

   # If not continuous, then halt after delay
   if run_time > 0:
      last_direction = -1
      halt ( )

   return "ok"

@app.route ("/left")
def left ( ):
   global last_direction, turn_tm_offset

   print "Left"
   go_left ( )
   last_direction = -1

   # sleep @1/2 second
   time.sleep (0.500 - turn_tm_offset)

   # stop
   halt ( )
   time.sleep (0.100)
   return "ok"

@app.route ("/right")
def right ( ):
   global last_direction, turn_tm_offset

   print "Right"
   go_right ( )

   # sleep @1/2 second
   time.sleep (0.500 - turn_tm_offset)
   last_direction = -1

   # stop
   halt ( )
   time.sleep (0.100)
   return "ok"

@app.route ("/ltforward")
def ltforward ( ):
   global last_direction, turn_tm_offset

   print "Left forward turn"
   go_left ( )

   # sleep @1/8 second
   time.sleep (0.250 - (turn_tm_offset / 2))
   last_direction = -1

   # stop
   halt ( )
   time.sleep (0.100)
   return "ok"

@app.route ("/rtforward")
def rtforward ( ):
   global last_direction, turn_tm_offset

   print "Right forward turn"
   go_right ( )

   # sleep @1/8 second
   time.sleep (0.250 - (turn_tm_offset / 2))
   last_direction = -1

   # stop
   halt ( )
   time.sleep (0.100)
   return "ok"

@app.route ("/stop")
def stop ( ):
   global last_direction

   print "Stop"
   halt ( )
   last_direction = -1

   # sleep 100ms
   time.sleep (0.100)
   return "ok"

@app.route ("/panlt")
def panlf ( ):
   global servo_pos

   print "Panlt"
   servo_pos -= 100
   if servo_pos < 500:
      servo_pos = 500

   servo.set_servo (18, servo_pos)

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/panrt")
def panrt ( ):
   global servo_pos

   print "Panrt"
   servo_pos += 100
   if servo_pos > 2500:
      servo_pos = 2500

   servo.set_servo (18, servo_pos)

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/home")
def home ( ):
   global servo_pos

   print "Home"
   servo_pos = 1250

   servo.set_servo (18, servo_pos)

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/panfull_lt")
def panfull_lt ( ):
   global servo_pos

   print "Pan full left"
   servo_pos = 500

   servo.set_servo (18, servo_pos)

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/panfull_rt")
def panfull_rt ( ):
   global servo_pos

   print "Pan full right"
   servo_pos = 2500

   servo.set_servo (18, servo_pos)

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/speed_low")
def speed_low ( ):
   global speed_offset, last_direction, turn_tm_offset

   speed_offset = 42
   turn_tm_offset = 0.001

   # Update current direction to get new speed
   if last_direction == 0:
       go_forward ( )
   if last_direction == 1:
       go_backward ( )

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/speed_mid")
def speed_mid ( ):
   global speed_offset, last_direction, turn_tm_offset

   speed_offset = 84
   turn_tm_offset = 0.166

   # Update current direction to get new speed
   if last_direction == 0:
       go_forward ( )
   if last_direction == 1:
       go_backward ( )

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

@app.route ("/speed_hi")
def speed_hi ( ):
   global speed_offset, last_direction, turn_tm_offset

   speed_offset = 126
   turn_tm_offset = 0.332

   # Update current direction to get new speed
   if last_direction == 0:
       go_forward ( )
   if last_direction == 1:
       go_backward ( )

   # sleep 150ms
   time.sleep (0.150)
   return "ok"

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

   print "Continuous run"
   run_time = 0

   # sleep 100ms
   time.sleep (0.100)
   return "ok"

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

   print "Mid run"
   run_time = 0.750
   halt ( )

   # sleep 100ms
   time.sleep (0.100)
   return "ok"

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

   print "Short run"
   run_time = 0.300
   halt ( )

   # sleep 100ms
   time.sleep (0.100)
   return "ok"

#
# Motor drive functions
#
def go_forward ( ):
    global speed_offset

    if speed_offset != 42:
        roboclaw.write (chr (1 + speed_offset))
        roboclaw.write (chr (128 + speed_offset))
    else:
        roboclaw.write (chr (127 - speed_offset))
        roboclaw.write (chr (255 - speed_offset))

def go_backward ( ):
    global speed_offset

    if speed_offset != 42:
        roboclaw.write (chr (127 - speed_offset))
        roboclaw.write (chr (255 - speed_offset))
    else:
        roboclaw.write (chr (1 + speed_offset))
        roboclaw.write (chr (128 + speed_offset))

def go_left ( ):
    global speed_offset

    if speed_offset != 42:
        roboclaw.write (chr (127 - speed_offset))
        roboclaw.write (chr (128 + speed_offset))
    else:
        roboclaw.write (chr (1 + speed_offset))
        roboclaw.write (chr (255 - speed_offset))

def go_right ( ):
    global speed_offset

    if speed_offset != 42:
        roboclaw.write (chr (1 + speed_offset))
        roboclaw.write (chr (255 - speed_offset))
    else:
        roboclaw.write (chr (127 - speed_offset))
        roboclaw.write (chr (128 + speed_offset))

def halt ( ):
    roboclaw.write (chr (0))

if __name__ == "__main__" :
   app.run (host = '0.0.0.0', port = 80, debug = True)

If you do not want or need debug information from Flask, set debug to 'false' on the app.run line.

if __name__ == "__main__" :

app.run (host = '0.0.0.0', port = 80, debug = False)

You can also change the port that the Flask http server listens on here as well.

Step 14: Using Other Hardware

If you want to use other hardware, like another type of SBC (Single Board Computer) you should have little issues getting Python and Flask running on other boards like the Beagle Bone, PCDuino etc... You will have to change the code to match the GPIO layout and use the servo driving capabilities of the new board.

To use another type motor driver, you just need to modify the go_forward, go_backward, go_left, go_right and halt functions to do what ever the replacement motor driver needs to make the motor do that particular function.

<p>i did the tutorial, swapped SD card to test both in my zero, pi-B and my pi3 and im getting the same error on em all, i did run the Fix described in comments, but nothing helped, any suggestions??:<br><br><strong>pi@sPIrit</strong>:<strong>~/rover $</strong> sudo ./restrover.py</p><p>Using hardware: PWM</p><p>PW increments: 10us</p><p>Initializing channel 0...</p><p>Traceback (most recent call last):</p><p> File &quot;./restrover.py&quot;, line 35, in &lt;module&gt;</p><p> servo.set_servo (18, servo_pos)</p><p> File &quot;build/bdist.linux-armv6l/egg/RPIO/PWM/__init__.py&quot;, line 212, in set_servo</p><p> File &quot;build/bdist.linux-armv6l/egg/RPIO/PWM/__init__.py&quot;, line 97, in init_channel</p><p>RuntimeError: rpio-pwm: Page 0 not present (pfn 0xa10000000002608c)<br></p>
<p>Is there a way to make the script start when the RasPi boots up? </p>
You need to add it to init.d.<br><br>Set up a script added to init.d like the in the following link: http://raspberrypi.stackexchange.com/questions/13415/using-init-d-script-to-start-my-python-program-on-startup or http://www.pietervanos.net/knowledge/start-python-script-from-init-d/<br><br>
<p>Ok just a few tips I wanted to add in case anyone is planning on trying this themselves.</p><p>1.) It's a great project, you should totally do it.</p><p>2.) The instructions above aren't perfect which lead to alot of frustration in my house and vows to rewrite the instructions. But after I got the robot working and slept on it I realized that the instructions were perfect just the way they are. They're perfect because they aren't perfect which led me to learn alot more about the ecosystem.</p><p>3.) I've managed to get everything working except for the servo (which isn't in the parts list) I bought the HS-311 standard servo. I'm hesitant to recommend it as I haven't got it working yet so buy at your own risk.</p><p>4.) I started with a 30' ethernet cable I had hanging around the house but quickly outgrew that. Go buy yourself a wifi adapter for this project, you won't regret it.</p><p>5.) First thing you should do after installing raspian is to install xrdp. You'll find instructions all over the net to install it. I just found it more convenient to remote into the Pi box and use my normal computer setup to do all of the coding and such.</p><p>6.) Use Wheezy. If you don't know what this is, go look it up. Don't use Jessie, there are alot of software incompatibilities at this point.</p><p>7.) When you get stuck, and you probably will... Just ask for help. A fellow nerd will come running to the rescue.</p><p>8.) This is a great dad/kid project. I had my kids help me assemble the robot and in the process they all want to be robotics engineers now when they grow up. haha.</p><p>Thanks again for a great project, and if anyone has any tips on how to get the servo going please drop me a line.</p><p>Thanks,</p><p>DeeDub</p>
<p>Hi DeeDub,</p><p>Thanks for report! I am glad to see someone use it (with pictures!) </p><p>I totally missed the Servo and will add it, thanks.</p><p>What is you servo issue? The HS-311 is a good servo for the job. Let me know and we can try and work though the problems.</p><p>As for my write-up... In the words of Dr. Leonard McCoy &quot;Darn it Jim, I'm a programmer not a writer!&quot; ;)</p><p>My talents are in the code more-so than the writing and I know that. I also know as a old Unix/Linux guy, I forget that others do not know those OS's as well. I do forget that sometimes. </p><p>One thing I know I should say (and will) is the get a good USB WiFi device and not a super cheap one. The router on board is a good option for this bot though, since it can haul it.<br></p><p>Some of the RPI stuff I did skip, due to it being covered a lot other places, but I know some I should have covered better. I will look for touch-up places.</p><p>Thanks again,</p><p>Scott</p>
<p>Hi guys, great project! I've been having fun trying to put it together. I have most of it working now with the exception of the web camera interface. Is there any thing special I need to do to get it going with the usb camera rather than the raspi camera module?</p><p>Thanks!</p>
<p>Sorry, the link was broken that I had. I have it fixed now. Follow it here: <a href="https://www.instructables.com/id/Raspberry-Pi-remote-webcam/">https://www.instructables.com/id/Raspberry-Pi-remot...</a></p><p>This shows how to get a WC running with Motion on a RPI. There are others on the web as well. Just change the link in the rovers index.html to point to your RPI streaming setup. </p>
<p>How do I change the webserver port off the 80 it is currently set at? I have another RPi that is controlling my Garage Door and it is using Apache I was able to use this info to change the port http://www.noveldevices.co.uk/rp-webserverport . this does not work on this project. </p>
<p>The port is set here:</p><p>if __name__ == &quot;__main__&quot; : <br> app.run (host = '0.0.0.0', port = 80, debug = True)</p>
<p>I was able to change the port using WinSCP, Clicking the restrover.py and looking for the information you posted above. </p><p>This did change and worked on internal IP . However after configuring port forwarding through my router, i still can not access with my DDNS. Any suggestions? </p>
<p>Sorry. I can be no help with you router :)</p>
<p>Great Project however I am having an issue. Im getting this when installing step &quot;sudo pip install RPIO&quot; This is error i get:</p><p> copying source/RPIO/__init__.py -&gt; build/lib.linux-armv7l-2.7/RPIO</p><p> copying source/RPIO/Exceptions.py -&gt; build/lib.linux-armv7l-2.7/RPIO</p><p> creating build/lib.linux-armv7l-2.7/RPIO/PWM</p><p> copying source/RPIO/PWM/__init__.py -&gt; build/lib.linux-armv7l-2.7/RPIO/PWM</p><p> running build_ext</p><p> building 'RPIO._GPIO' extension</p><p> creating build/temp.linux-armv7l-2.7</p><p> creating build/temp.linux-armv7l-2.7/source</p><p> creating build/temp.linux-armv7l-2.7/source/c_gpio</p><p> gcc -pthread -fno-strict-aliasing -DNDEBUG -g -fwrapv -O2 -Wall -Wstrict-prototypes -fPIC -I/usr/include/python2.7 -c source/c_gpio/py_gpio.c -o build/temp.linux-armv7l-2.7/source/c_gpio/py_gpio.o</p><p> source/c_gpio/py_gpio.c:28:20: fatal error: Python.h: No such file or directory</p><p> compilation terminated.</p><p> error: command 'gcc' failed with exit status 1</p><p> ----------------------------------------</p><p>Command &quot;/usr/bin/python -c &quot;import setuptools, tokenize;__file__='/tmp/pip-build-v1_9ya/RPIO/setup.py';exec(compile(getattr(tokenize, 'open', open)(__file__).read().replace('\r\n', '\n'), __file__, 'exec'))&quot; install --record /tmp/pip-U99v_f-record/install-record.txt --single-version-externally-managed --compile&quot; failed with error code 1 in /tmp/pip-build-v1_9ya/RPIO</p><p>pi@raspberrypi ~ $</p><p>The result is causing this error when running script: &quot; sudo python restrover.py&quot; i think. Here is the error message :</p><p>pi@raspberrypi ~/rover $ sudo python restrover.py</p><p>Traceback (most recent call last):</p><p> File &quot;restrover.py&quot;, line 13, in &lt;module&gt;</p><p> from RPIO import PWM</p><p>ImportError: No module named RPIO</p><p>pi@raspberrypi ~/rover $</p><p>Any idea what i did wrong? </p><p>Im using the newest Rpi. </p>
Hi, <br><br>That would cause the issue. <br><br>Try this one to install the Python gpio:<br><br>sudo apt-get install python-dev python-rpi.gpio<br><br>
<p>Wait, I see now I think.</p><p>Do this sudo apt-get install python-dev</p><p>and then this: sudo pip install RPIO</p><p>See: <a href="https://pythonhosted.org/RPIO/" rel="nofollow" style="">https://pythonhosted.org/RPIO/</a> for more information.</p>
<p>Thanks for the reply. I really want to get this working. Performing the Sudo apt-get install python-dev did work. However now Im getting this error.</p><p>pi@raspberrypi ~/rover $ sudo python restrover.py</p><p>Traceback (most recent call last):</p><p> File &quot;restrover.py&quot;, line 13, in &lt;module&gt;</p><p> from RPIO import PWM</p><p> File &quot;/usr/local/lib/python2.7/dist-packages/RPIO/__init__.py&quot;, line 115, in &lt;module&gt;</p><p> import RPIO._GPIO as _GPIO</p><p>SystemError: This module can only be run on a Raspberry Pi!</p><p>pi@raspberrypi ~/rover $</p>
<p>Have a look at this thread: <a href="https://github.com/metachris/RPIO/issues/53" rel="nofollow">https://github.com/metachris/RPIO/issues/53</a></p><p>Looks like a RPI 2 issue. I only have older Pi's. The page does talk of how to fix it.</p>
<p>THAT WORKED!!! Thank you for your help! </p>
<p>Good work Jscottb</p>
<p>Thanks!</p>
<p>awesome ,great job ?</p>
<p>Thank you very much!</p>

About This Instructable

13,902views

244favorites

More by jscottb:ESP8266 HTTP IO Server Boom and Gripper bot Web controlled rover 
Add instructable to: