The Internet of Things (IoT) has created a tremendous opportunity for developers. A giant network of connected “things,” which includes people, is a tremendous platform to develop applications that capitalize on the relationships between People and Things, People and People and Things and Things. In this post, you’ll learn how to enable a mobile device to monitor and control a prototype that automates gardening. 

It is estimated that by 2020, there will be 26 billion connected devices. Bear in mind, this is a conservative estimate as some experts predict that there will be over 100 billion IoT devices within that timeframe. In the past decade, we have witnessed the IoT phenomenon evolve from the smartphone to the smart home. The application of IoT strategies on the power grid and transportation networks prove that we are in the midst of the next step of this evolution, the smart city. The concept of connecting any electrical device and component to the Internet has the potential to fundamentally reshape society.

In my first three posts “Mobile Development for Arduino Part 1, Part 2, and Part 3,” I provided an introduction to IoT software development using Arduino as a platform. Arduino is an open-source hardware solution that gives developers an opportunity to cultivate a thorough understanding of electronics and learn how to integrate them using software. In “Mobile Development for Arduino Part 4,” we developed a prototype that automated gardening by acquiring data regarding soil moisture, temperature, humidity, sunlight and rainfall. In Part 5, we enabled our prototype to communicate remotely with an actuator via RF communication. In Part 6, we connected our prototype to the cloud via MQTT to visualize the data readings from our garden. In Part 7, we expanded the cloud connectivity of our prototype by enabling it to send push notifications to a mobile device. In this post, we will enable our mobile device to monitor and control our prototype.

We are going to switch things up a bit and replace our Arduino Yun with a NodeMCU ESP8266 board. The NodeMCU board offers several advantages over the Arduino Yun and has usurped the Yun as the premier board for prototyping. The primary advantage is cost. A NodeMCU can be acquired for under $10, which certainly beats the Yun’s $75 price tag. Other advantages include a faster 80 Mhz processor and the capability to be used with Lua as well as the Arduino IDE for programming.

1. Setup NodeMCU ESP8266 Board

The first thing we need to do is install the drivers for the NodeMCU, which can be downloaded here. Next, we need to enable support for the NodeMCU within the Arduino IDE, by navigating to Preferences and entering https://arduino.esp8266.com/package_esp8266com_index.json in the dialog for the field Additional Board Manager URL.

Apply this change and then navigate to Tools -> Board -> Board Manager. Once the Board Manager appears, search for ESP8266 and an option item should appear allowing you to install the board set.

After you have installed the board set, navigate to Tools -> Board and select NodeMCU 1.0 (ESP-12E Module).

Now, connect your NodeMCU board via USB and verify that you can compile and run a sample sketch.

2. Connect NodeMCU to Prototype

The NodeMCU has a slightly different pin layout than the Arduino Yun. This requires us to reference the chart below to ensure we are making the appropriate connections.

First, connect the 3V3 pin to the power strip of the breadboard and connect the GND pin to the ground strip of the breadboard. Then, connect the power and ground strips of the breadboard containing the NodeMCU to the power and ground strips of the breadboard containing the LEDs and Receiver module.

Now, connect the D7 pin of the NodeMCU to the Receiver, connect the D2 pin to the green LED, connect the D1 pin to the red LED, and connect the D0 pin to the relay.

The final result should look like this.

3. Program NodeMCU To Receive Commands From Mobile Device

First, we need to include the necessary libraries.

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>

The ESP8266WiFi library will allow us to establish network connectivity. The ESP8266WebServer library will allow us to use our NodeMCU as a server so we can listen for the client, in this case, our mobile device. The ArduinoJson library will enable us to create nicely formatted JSON objects containing the state of the prototype for our mobile client to consume.

Next, we need to connect to a WiFi network and give our server a port. We also will create two booleans that will track the state of the prototype.

#define WLAN_SSID     "your_network_name_here"
#define WLAN_PASSWORD "your_network_password_here"
ESP8266WebServer server(80);

boolean isOn;
boolean isWaterPumpActive;

Now, within our setup method we need to call begin on Serial with a much higher baud rate of 115200 to account for our new server functionality. We also want to connect to our WiFi network within this method. Lastly, we initialize our web server and provide the two endpoints that should either trigger the water pump or provide information regarding the current status of the prototype, using the server.on method.

void setup() {
   Serial.begin(115200);
   delay(10);

//some code

    isOn = false;
    isWaterPumpActive = false;

    WiFi.mode(WIFI_STA);
    WiFi.begin(WLAN_SSID, WLAN_PASSWORD);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println();

    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());

    server.on("/toggleWaterPump", [](){
        if (isOn) {
          isOn = false;
          turnWaterPumpOff();
        } else {
          isOn = true;
          turnWaterPumpOn();
        }
        createJsonResponse();
    });

    server.on("/waterPumpStatus", [](){
        createJsonResponse();
    });

    server.begin();
    Serial.println("HTTP server started! Waiting for clients!");
}

Next, within our loop method, we call the handleClientmethod on our web server. This instructs our web server to constantly listen for either of the endpoints we specified when we initialized our server in the setup method and behave accordingly. We also add a conditional statement to keep the water pump in sync in the case it is manually triggered by the soil moisture sensor.

void loop() {

  //some code

  server.handleClient();

  if (isOn) {
     turnWaterPumpOn();
  } else {
     turnWaterPumpOff();
  }
}

Last, we create methods to toggle the state of the water pump and create a JSON object containing the status of our prototype.

void turnWaterPumpOn() {
  digitalWrite(greenLEDPin, HIGH);
     digitalWrite(redLEDPin, LOW);

     if(!isWaterPumpActive) {
        isWaterPumpActive = true;
        digitalWrite(waterPumpPin, HIGH); //Turns on Pump
     }
     Serial.println("ON");
}

void turnWaterPumpOff() {
  Serial.println("OFF");
     if(isWaterPumpActive) {
        isWaterPumpActive = false;
        digitalWrite(waterPumpPin, LOW); //Turns off Pump
     }

     digitalWrite(greenLEDPin, LOW);
     digitalWrite(redLEDPin, HIGH);
}

void createJsonResponse() {
  StaticJsonBuffer<500> jsonBuffer;
        JsonObject& root = jsonBuffer.createObject();
        if (isOn) {
          root["isWaterPumpOn"] = true;
        } else {
          root["isWaterPumpOn"] = false;
        }

        String jsonString;
        root.printTo(jsonString);

        Serial.println(jsonString);
        server.send(200, "application/json", jsonString);
}

4. Program Android Client to Interact with Prototype

All we need to do is create a simple UI that displays the current status of the prototype and provide a button to change the state of the prototype. To accomplish this, we will add a TextView and Button to the layout we created in our previous post.

<LinearLayout
   xmlns:android="https://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:orientation="vertical">

   //some code

   <TextView
       android:id="@+id/status_textView"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"/>

   <Button
       android:id="@+id/water_pump_button"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="@string/turn_water_pump_on"/>

</LinearLayout>

Next, we need to create two GET endpoints in our WebService class that match the endpoints we instructed our prototype web server to listen for. In our case, "/toggleWaterPump"
and "/waterPumpStatus"respectively. Don’t forget to add corresponding method calls in the WebManager class as well.

public interface WebServices {

  //some code

   @GET("/toggleWaterPump")
   Observable<WaterPumpResponse> toggleWaterPump();

   @GET("/waterPumpStatus")
   Observable<WaterPumpResponse> getWaterPumpStatus();
}
public class WebManager {

  //some code

   public Observable<WaterPumpResponse> toggleWaterPump() {
       return webService.toggleWaterPump();
   }

   public Observable<WaterPumpResponse> getWaterPumpStatus() {
       return webService.getWaterPumpStatus();
   }
}

Now, in our ArduinoCloudFragment class, within the onCreateView method, we need to create an instance of our WebManager with the IP address of our prototype. The IP address can be retrieved by viewing the output of the Arduino IDE Serial Monitor when Serial.println(WiFi.localIP()) is called in the setup method or by downloading an IP scanning app such as Fing, which is easier. Once the webManager is initialized, we call getWaterPumpStatus() and update the UI accordingly.

webManager = new WebManager(WebServices.Factory.create(PROTOTYPE_IP));
webManager.getWaterPumpStatus()
       .observeOn(AndroidSchedulers.mainThread())
       .subscribe(r -> {
           if (r.isWaterPumpOn()) {
               statusTextView.setText("Water Pump Is On");
               waterPumpButton.setText("Turn OFF");
           } else {
               statusTextView.setText("Water Pump Is Off");
               waterPumpButton.setText("Turn ON");
           }
       }, e -> Log.e("ESP8266", e.getMessage()));

Last, when our button is clicked, we call `toggleWaterPump()` to toggle the water pump state and update the UI when the request completes.

@OnClick(R.id.water_pump_button) 
public void toggleWaterPump() { 
   webManager.toggleWaterPump() 
           .observeOn(AndroidSchedulers.mainThread()) 
           .subscribe(r -> { 
               if (r.isWaterPumpOn()) { 
                   statusTextView.setText("Water Pump Is On"); 
                   waterPumpButton.setText("Turn OFF"); 
               } else { 
                   statusTextView.setText("Water Pump Is Off"); 
                   waterPumpButton.setText("Turn ON"); } 
           }, e -> Log.esp8266", e.getMessage())); 
}

Congratulations, we have successfully completed an Internet of Things prototype! You can now monitor and control the water pump of the prototype remotely. Our next step is to move our prototype closer to product by learning how to create Printed Circuit Boards (PCBs).

Leave a Reply

Your email address will not be published. Required fields are marked *