Smooth Web Browser Motor Control
by david0429 in Circuits > Remote Control
11783 Views, 34 Favorites, 0 Comments
Smooth Web Browser Motor Control
Background
There are a million ways to control something remotely, but there are very few that are as accessible as an internet browser. Internet browsers are on almost every device, which makes them the prime interface for many projects. For another project I am working on, I need smooth/adjustable control of motors and servos, not just on/off, and I want easy control without a dedicated device, so this Instructable covers how I accomplished that.
Parts
- Adafruit DC and Stepper Motor HAT
- Raspberry Pi 3
- Whatever you want to control (ie. Motor)
- Pi and motor power supplies
*Some of this information will be a repeat of the Adafruit tutorial for this hat (which can be found HERE). If you already have your Pi and motor hat setup, skip to step 4, Software
Wiring
The wiring for this project is fairly basic as most of the project is software. Either way, I'll cover the general idea:
Setup Motor Hat
Solder up the motor hat from Adafruit and attach it to the top of the Raspberry Pi. It should just plug onto the GPIO header. I recommend stand-offs between the two boards, but that isn't strictly necessary. Adafruit has this step covered fairly completely HERE
Power
The next step is to hook-up the motor power supply. This can be plugged into a wall supply, or a battery, whichever is easier. Keep in mind that the power supply you are going to use must match the motor you are using. If it won't supply enough power, the board can't fix that. There will be two headers for power in. All of your motors must run off the same voltage.
Motor
This board can handle 2 steppers, or 4 DC motors. In this case, I just want to use a single DC motor. Take the motor and attach the wires to the terminals labeled with an M#. For example, my motor will be the third one, so the wires need to go into the two terminals in front of the M3 label on the board.
Configuration
I2C
To use the motor hat, I2C must be enabled on the Pi. You can do this with the GUI version, or the Terminal:
sudo raspi-config
In the options that come up, select Interfacing Options > I2C > Yes
Network
To access the Pi from a web browser, it needs to be on the network! You can use Wireless as there is minimal bandwidth used, or ethernet. How all of this is set up depends heavily on your network infrastructure.
Software Install
There are a few different pieces of software framework to make this work:
Motor Hat
These are the libraries needed to "talk" to the motor hat.
Install dependencies
apt-get install python-smbus git python-dev
Download the motor hat library
git clone https://github.com/adafruit/Adafruit-Motor-HAT-Python-Library.git
cd Adafruit-Motor-HAT-Python-Library
sudo python setup.py install
Flask
Flask is the main "magic" here. It is a python web server that makes it simple for python to handle web requests
Install Pip (used to download and install python libraries)
sudo apt-get install python-pip
Have Pip install Flask
pip install Flask
Finally, to the Interface!
This section is the real purpose of this Instructable. No matter what you want to control, this is the part that will control it all.
There are 2 parts that make this work, the interface and the web server. The interface is the set of nice sliders and feedback that you bring up on your phone or computer to control the remote motors. The server is the part that is living on the Pi, accepting the commands from the interface, moving the motors, and giving feedback.
Example
Download the example from the link below. I'll walk through how the code works
git clone https://github.com/david0429/web-motor-control.git
Interface (web_interface.html)
This is the main file that is presented to your browser when you connect to the Pi. It will have the sliders, buttons, drop-downs, etc. You could fancy it up with css, but that is mostly cosmetic and outside of this Instructable
The block of HTML code below contains 2 parts:
- Input - Slider
- It can also be a button or drop-down, etc.
- It has a minimum, maximum, default value, and an ID/name that can be referred to elsewhere in the code
- Span - Text
- This will display the value that you are setting with the slider, or could display feedback from the server
- This is completely optional and not necessary for control
This is mostly the visual part of the code
<div id="slidecontainer"> <input type="range" min="-255" max="255" value="0" id="range1"> <p>Value: <span id="range1_value"></span></p> </div>
The next block of code is javascript. It is the "workhorse" of this webpage. It seems like a lot of code, but it really only does 1 basic task; send the value of the slider to python.
This first chunk of code just gets a handle on the 2 html elements we need and saves them in variables
var slider = document.getElementById("range1"); var output = document.getElementById("range1_value");
The next chink is the "connection" to python. This code sets an event to trigger whenever the sliders is moved because of the "slider.oninput" line. Every little slide of the slider (in this case, the input called "range1"), it will make a request to a remote server.
slider.oninput = function() { output.innerHTML = slider.value; var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (this.readyState == 4 && this.status == 200) { } }; xhttp.open("GET", "http://192.168.2.103:5000/set_speed?speed=" + slider.value, true); xhttp.send(); }
The open() function is the key here.
- http://192.168.2.103:5000/ < This is the address of the Pi. It may be the hostname too depending on your network configuration. By default, the server will use port 5000
- set_speed < This tells the server which function to call. More on this later
- ?speed=" + slider.value This is a variable that you are passing to the Pi. In this case, it's the current position of the slider. If you want to pass multiple variables, separate them with a &. Doing this would let you program some more complex behaviours on the server-side
You can have as many controls/sliders as you want, just duplicate those two blocks of code above, change the variable and id names, and change which "function" you're calling and you're done!
Python - Flask (motor_control.py)
This Flask server is the actual controller that accepts the requests from the web interface. This is the brain of you robot/machine/device,
This first chunk of code is a basic python function. The name is fairly unimportant, as long as it is unique. Having the @app.route("/") just before it tells Flask that this function should be called whenever someone requests the root page of the web server.
When this function is run, it will open the web_interface.html file (discussed previously) and return everything in it back to the user. This way, you can store your web interface in another file so it is much cleaner. Having the flask server return the interface also helps avoid cross-site security warnings
@app.route("/") def web_interface(): html = open("web_interface.html") response = html.read().replace('\n', '') html.close() myMotor.setSpeed(0) return response
This next chunk is the portion that works with the slider and the motor. Whenever someone requests the "set_speed" page/resource, Flask calls this function. Like above, the @app.route("/set_speed") line tells Flask to do this.This is the "open" line in the section above. Every tick of the slider, this page/function/resource is called.
Once in the function, the "speed" variable is pulled out of the request. If there were multiple variables passed in this request, just use this line once for each variable.
After that, it's basic motor commands, setting speed and direction and sending them to the motor hat.
@app.route("/set_speed") def set_speed(): speed = request.args.get("speed") print "Received " + str(speed) direction = Adafruit_MotorHAT.FORWARD if int(speed) < 0: direction = Adafruit_MotorHAT.BACKWARD myMotor.run(direction) myMotor.setSpeed(abs(int(speed)))
This last line starts the Flask server. If it didn't have anything between the brackets, you could only access the webpage from on the Pi, but with the '0.0.0.0', you can access the page from anywhere within the network.
app.run(host= '0.0.0.0')
Like the interface, you can have as many functions as your want to make your application more complicated/flexible. Just make a new function with a unique name and a unique route above it and you're good
Usage
Once both of these have been set up, run the web server by typing the following command:
python motor_control.py
Now that this is running, from any browser in your network, visit the Flask webpage:
http://raspberry-pi-ip:5000
Note: replace "raspberry-pi-ip" above with either the IP or the hostname of your Pi.
If all goes well, you should see your basic web page! You should also see a message in the terminal where the web server is running indicating that it served a page.
Now, try moving the slider. Again, if all is working, you should see your motor start moving! The slider will control speed and direction.
Troubleshooting
- If the page doesn't appear and you get an error 500, it means there is a mistake in your python. Head back to the terminal to see the error listed.
- If a page loads but it looks weird, there is an issue in your html page.
- If you move the slider and you don't see messages coming up in the server terminal, make sure the IP and page are correct in your "open" function in the web interface and open your Javascript console in your browser and fix any errors
- If you move the slider and you see messages coming up in the server terminal, but you don't see your motor moving, make sure your motor hat has power, the motor is properly connected to the right port, and your python code is driving the right motor
Final Thoughts
*Note - This instructable doesn't take security into account at all. Use this on a public network or allow direct access from the internet at your own risk.
With some very minor tweaks you could control servos, steppers, LEDs, even full interfaces. With some fancy html and CSS, you could make a cross-platform controller that is really sleek and even adds to current projects!