In this tutorial we are going to show how to use the ESP8266-12 module to control a Zumo robot with an iOS device. If you haven’t already checked out the other three tutorials on the ESP module, we recommend you review those first:

Getting started with the ESP8266 ESP-01
Using ESP-01 and Arduino UNO
ESP8266 as a Microcontroller

Materials

Unlike the previous three tutorials, we are going to use ESP-12 instead of ESP-01. Even though both modules work the same way, they differ in a few things. The ESP-12 comes with nine GPIOs instead of two. In order to control the robot’s direction we require four GPIOs (two per motor). Also, before downloading a code to the ESP-12 module we need to ground GPIO0 and GPIO15, and set GPIO2 high.

Wiring

 

Zumo Robot Complete Circuit

ESP8266 -12 Pinout

 

H-Bridge Pinout

 

The H-Bridge allows us to control four DC motors in one direction, or two DC motors in both directions. Pins 1 and 9 are used to enable the motors. Pins 2 and 7 change the direction of one motor, while pins 10 and 15 change the direction of the other motor. These pins need to be connected to a PWM output in order to control the speed of the motors. Luckily the ESP-12’s GPIOs can produce PWM signals so you can choose any four GPIOs from the module. We decided to use GPIOs 0, 2, 4 and 15. Pins 3 and 6 connect to one of the motors, while pins 11 and 14 connect to the other. Pins 4, 5, 12, and 13 are grounded. Pin 8 is used to power the motors with an external power supply. This pin is connected to the positive terminal of the 4-AA batteries, which supply ~6V. You can use a maximum of 36 V. Pin 16 is used for the IC logic power.  

Arduino Code

In order to download the code to our module successfully, we first need to ground GPIOs 0 and 15, and set GPIO2 high with 3.3V. Once the setup is ready, we select “Generic ESP8266 Module” as our board and click on the “upload” button.   

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
 
const char *ssid = "Zumo Robot";
const char *password = "12345678";
 
ESP8266WebServer server(80);
 
const int motor1 = 2;
const int motor2 = 15;
const int motorA = 0;
const int motorB = 4;
 
int motor_speed;
 
void setup() {
  delay(1000);
  Serial.begin(115200);
  pinMode(motor1, OUTPUT);>
  pinMode(motor2, OUTPUT);
  pinMode(motorA, OUTPUT);
  pinMode(motorB, OUTPUT);
  WiFi.softAP(ssid, password);
 
  server.on("/", []() {
    
  String url = server.arg("pin");
  String state = url.substring(0,3);
  String value = url.substring(3);
  motor_speed = value.toInt();
 
  if (state == "ONA"){
    analogWrite(motor1, 0);
    analogWrite(motor2, motor_speed);
    analogWrite(motorA, 0);
    analogWrite(motorB, motor_speed);
  }
  else if (url == "OFFA"){
    analogWrite(motor1, 0);
    analogWrite(motor2, 0);
    analogWrite(motorA, 0);
    analogWrite(motorB, 0);
  }
  else if (state == "ONB"){
    analogWrite(motor1, motor_speed);
    analogWrite(motor2, 0);
    analogWrite(motorA, motor_speed);
    analogWrite(motorB, 0);
  }
  else if (url == "OFFB"){
    analogWrite(motor1, 0);
    analogWrite(motor2, 0);
    analogWrite(motorA, 0);
    analogWrite(motorB, 0);
  }
  else if (state == "ONC"){
    analogWrite(motor1, 0);
    analogWrite(motor2, motor_speed);
    analogWrite(motorA, motor_speed);
    analogWrite(motorB, 0);
  }
  else if (url == "OFFC"){
    analogWrite(motor1, 0);
    analogWrite(motor2, 0);
    analogWrite(motorA, 0);
    analogWrite(motorB, 0);
  }
  else if (state == "OND"){
    analogWrite(motor1, motor_speed);
    analogWrite(motor2, 0);
    analogWrite(motorA, 0  );
    analogWrite(motorB, motor_speed);
  }
  else if (url == "OFFD"){
    analogWrite(motor1, 0);
    analogWrite(motor2, 0);
    analogWrite(motorA, 0);
    analogWrite(motorB, 0);
  }
});
 
  server.begin();
}
 
void loop() {
   server.handleClient();
}
 

Code Explanation

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
 
const char *ssid = "Zumo Robot";
const char *password = "12345678";
 
ESP8266WebServer server(80);
 
const int motor1 = 2;
const int motor2 = 15;
const int motorA = 0;
const int motorB = 4;
 
int motor_speed;
 

In the first part of the code we include the libraries required to initialize the ESP module in AP mode and create a web server. Then we set the name and password of our access point and create a server object that listens to port 80. Finally, we assign a name to each pin that connects to a motor and create a variable that holds the value of the motors’ speed.

void setup() {
  Serial.begin(115200);
  pinMode(motor1, OUTPUT);
  pinMode(motor2, OUTPUT);
  pinMode(motorA, OUTPUT);
  pinMode(motorB, OUTPUT);
  WiFi.softAP(ssid, password);
 
  server.on("/", []() {

Here we start serial communication and select the baud rate. We connect the pins to the motors outputs, initialize the ESP module as an access point, and start the server.

Before we get to the following section of code, we are going to talk about URLs. A URL is a string that contains the address of a file that is accessible on the internet. Let’s look at a URL example from this tutorial:

192.168.4.1/?pin=ONA500

The first part is the IP address of our ESP module in AP mode. The question mark is used to indicate the starts of a query string that is used to retrieve variable values from a web server. In this example, a variable named “pin” is created with the value “ONA500.” 

String url = server.arg("pin"); String state = url.substring(0,3); String value = url.substring(3); motor_speed = value.toInt();  

Here we create several variables to hold different values. The first variable, “url,” stores the value of “pin” from the query string. The second variable, “state,” takes the same value and stores only the first three characters. The third variable also takes the same value and stores only the last characters after the third one. The last variable, “motor_speed,” takes the value from “value” and converts it into an integer. So we are basically just splitting the information into different parts to know what button was pressed in our iOS App and what speed was selected.

 
if (state == "ONA"){
    analogWrite(motor1, 0);
    analogWrite(motor2, motor_speed);
    analogWrite(motorA, 0);
    analogWrite(motorB, motor_speed);
 

In this section of the code we use the values obtained from the query string to control the robot’s wheels. In this example, we check if the button “UP,” which is labeled as “ONA” in the query string, is pressed on our iOS device. If the case is true, we use the “analogWrite” function to send the speed value, also obtained from the query string and set in the iOS App, to the motors. So “ONA” will make both motors move forward at a specific speed. The same process is used for the other possible directions. The code compares each case and makes the motors move in the corresponding direction.

 
void loop() {
   server.handleClient();
}
 

In the loop section we constantly check if a client initiates connection with our server and handles its requests.

ESP8266 App

Now that we have the ESP8266-12 running as a server, we just need to create the iOS App that will control the Zumo robot. For a better understanding on how to start developing iOS Apps you can refer to the following site:

https://developer.apple.com/library/ios/referencelibrary/GettingStarted/DevelopiOSAppsSwift/

Open Xcode, create a new single view application and select swift as the coding language.

Go to “Main.storyboard” and create this user interface:

“Up,” “Down,” “Left,” and “Right” are buttons, while “0” and “Set Speed” are labels. We also include a Slider and a Web View. Don’t forget to add the missing constraints so that the elements fit properly on your iOS device screen.

Open the Assistant Editor and connect the Web View, Slider, and Label with “0” to the ViewController.swift window. Hold the “control” key and drag each element to the ViewController.swift window to make the connections. Select the connections to be “Outlet,” choose a name for each element, and leave the storage as “Weak.”  You should get the following lines of code:

    @IBOutlet weak var web: UIWebView!
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var slider: UISlider!
 

Now create the following variables:

 
    var onURLPath = "http://192.168.4.1/?pin=ONA"
    var offURLPath = "http://192.168.4.1/?pin=OFFA"
    
    var on1URLPath = "http://192.168.4.1/?pin=ONB"
    var off1URLPath = "http://192.168.4.1/?pin=OFFB"
    
    var on2URLPath = "http://192.168.4.1/?pin=ONC"
    var off2URLPath = "http://192.168.4.1/?pin=OFFC"
    
    var on3URLPath = "http://192.168.4.1/?pin=OND"
    var off3URLPath = "http://192.168.4.1/?pin=OFFD"
 

These variables represent the possible URLs that the client can request depending on the button pressed.

Use the same method to connect the Slider to the ViewController.swift window. Select the connection to be an action and change the type to UISlider. Inside the function created type the following lines of code:

    @IBAction func sliderValueChanged(sender: UISlider) {
        var currentValue = Int(sender.value)
        label.text = "\(currentValue)"
    }
 

This function reads the value from the slider, changes it to an integer type, and displays the value on the label that initially shows “0.” We make the slider have a range from 0 to 1023, which is the maximum speed, or PWM, that the ESP8266 can handle. 

Each button will have two functions. One that executes when the button is being pressed and one that executes when the button is released. Connect one of the buttons to ViewController.swift, select the connection to be an action, give the button a name, make the type UIButton, and select the event to be Touch Down. Inside the function created type the following lines of code:

    @IBAction func buttonTouched(sender: UIButton) {
        let requestURL = NSURL(string: onURLPath+"\(label.text!)")
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
 

This function executes when the button is pressed. It selects the URL assigned to it, which in this case is:

http://192.168.4.1/?pin=ONA

Then it adds to the URL the value of the label, which holds the value of the slider. So if the slider has a value of “125,” when this button is pressed the following URL is created:

http://192.168.4.1/?pin=ONA125

Finally we use the Web View to make the URL request to the server when this button is pressed.

Now we take the same button and connect it to ViewController.swift, but this time we select the event to be Touch Up Inside. Type the following code inside the function:

       @IBAction func buttonReleased(sender: UIButton) {
        let requestURL = NSURL(string: offURLPath)
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }

This function executes when the button is released. It does the same thing as the previous function, but we change the URL, and we don’t add the value of the slider to it since we want the speed to always be zero when any button is released.

We repeat the same process for the other three buttons. We assign an URL to each one of them so that every time one is pressed, or released, the client makes the corresponding URL request to our web server.

The final code should like this:

 
import UIKit
 
class ViewController: UIViewController {
 
    @IBOutlet weak var web: UIWebView!
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var slider: UISlider!
    
    var onURLPath = "http://192.168.4.1/?pin=ONA"
    var offURLPath = "http://192.168.4.1/?pin=OFFA"
    
    var on1URLPath = "http://192.168.4.1/?pin=ONB"
    var off1URLPath = "http://192.168.4.1/?pin=OFFB"
    
    var on2URLPath = "http://192.168.4.1/?pin=ONC"
    var off2URLPath = "http://192.168.4.1/?pin=OFFC"
    
    var on3URLPath = "http://192.168.4.1/?pin=OND"
    var off3URLPath = "http://192.168.4.1/?pin=OFFD"
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
    }
 
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
 
//UP
    @IBAction func buttonTouched(sender: UIButton) {
        let requestURL = NSURL(string: onURLPath+"\(label.text!)")
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
    @IBAction func buttonReleased(sender: UIButton) {
        let requestURL = NSURL(string: offURLPath)
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
//Down
    
    @IBAction func buttonTouched1(sender: UIButton) {
        let requestURL = NSURL(string: on1URLPath+"\(label.text!)")
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
    @IBAction func buttonReleased1(sender: UIButton) {
        let requestURL = NSURL(string: off1URLPath)
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
//Left
    
    @IBAction func buttonTouchedLeft(sender: UIButton) {
        let requestURL = NSURL(string: on2URLPath+"\(label.text!)")
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
    @IBAction func buttonReleasedLeft(sender: UIButton) {
        let requestURL = NSURL(string: off2URLPath)
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
//Right
    
    @IBAction func buttonTouched3(sender: UIButton) {
        let requestURL = NSURL(string: on3URLPath+"\(label.text!)")
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
    @IBAction func buttonReleased3(sender: UIButton) {
        let requestURL = NSURL(string: off3URLPath)
        let request = NSURLRequest(URL: requestURL!)
        web.loadRequest(request)
        print(requestURL!)
    }
    
//Slider
    
    @IBAction func sliderValueChanged(sender: UISlider) {
        var currentValue = Int(sender.value)
        label.text = "\(currentValue)"
    }
}

Results

Once the code has been uploaded to the ESP module we need to connect to the Wi-Fi module from our iOS device. Open Settings -> Wi-Fi -> Select “Zumo Robot” -> Write password.

Once we are connected to the module we just need to open the ESP826 App. We can now control the direction and speed of the Zumo robot from our iOS device.

If you have any questions about this tutorial, do not hesitate to post a comment, shoot us an email, or post it in our forum.