Friday, June 2, 2017

Demo 17: Arduino ESP32/ESP8266 WebGPIO - control GPIO from web

1. Introduction
In this tutorial, I will show you how to make an application like Raspberry WebGPIO.
The GUI of Web look like below:
Figure: ESP32 WebGPIO interface
- From this GUI you can select which GPIO, direction (Input/Output), the value (HIGH/LOW) need to be written to GPIO in output mode or read the value of GPIO in input mode. The status or read value will be showed in Status column.
- I will create 2 version of this demo:
+ Version 1: without using sdcard to store html code.
+ Version 2: Using sdcard to store html code.
- In order to make this demo, I will reuse 2 previous demos:
Demo 7: How to use Arduino ESP32 to store data to sdcard
Demo 12: How to turn the Arduino ESP32 into a Web Server
2. Hardware
We connect ESP32 to microSD module like below:

Figure: ESP32 connect microSD module
Here we connect:
[ESP32 IO26 – CS MICROSD]
[ESP32 IO14 – MOSI MICROSD]
[ESP32 IO12 – MISO MICROSD]
[ESP32 IO27 – SCK MICROSD]
3. Software
I created 2 versions: with and without sdcard
3.1 Without sdcard
To test it from web browser go to http://ip_address_of_esp32
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#include <WiFiClient.h>
#include <ESP32WebServer.h>
#include <WiFi.h>

/* thay doi ssid va password */
const char* ssid = "dd-wrt";
const char* password = "0000000000";

ESP32WebServer server(80);

/* day la phan code giao dien, dung jquery va boostrap */
char res[3000] = 
"<!DOCTYPE html>\n\
<html>\n\
<head>\n\
<meta name='viewport' content='width=device-width, initial-scale=1'>\n\
<link rel='stylesheet' href='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css'>\n\
<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>\n\
<script src='https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'></script>\n\
</head>\n\
<body>\n\
\n\
<div class='container'>\n\
<h2>iotsharing.com - ESP32 WEBGPIO</h2>\n\
</br>  \n\
<table class='table'>\n\
<thead>\n\
<tr>\n\
<th>Status</th>\n\
<th>GPIO</th>\n\
<th>In/Out</th>\n\
<th>Write HIGH</th>\n\
<th>Write LOW</th>\n\
<th>Read</th>\n\
</tr>\n\
</thead>\n\
<tbody>\n\
<tr>\n\
<td>  \n\
<button class='btn btn-default dropdown-toggle' type='status' id='status' >#status</button>\n\
</td>\n\
<td> \n\
<select id = 'gpio' class='btn btn-default dropdown-toggle'>\n\
<option>GPIO0</option>\n\
<option>GPIO2</option>\n\
<option>GPIO4</option>\n\
</select>\n\
</td>\n\
<td>  \n\
<select id = 'dir' class='btn btn-default dropdown-toggle'>\n\
<option>Out</option>\n\
<option>In</option>\n\
</select>\n\
</td>\n\
<td>  \n\
<button class='btn btn-default dropdown-toggle' type='button' id='write1' >Write HIGH</button>\n\
</td>\n\
<td>  \n\
<button class='btn btn-default dropdown-toggle' type='button' id='write0' >Write LOW</button>\n\
</td>\n\
<td>  \n\
<button class='btn btn-default dropdown-toggle' type='button' id='read' >Read</button>\n\
</td>\n\
</tr>\n\
</tbody>\n\
</table>                                        \n\
</div>\n\
<script>\n\
function ml(gpio, dir, value) {\n\
var io = gpio.replace('GPIO', '');\n\
var d = (dir == 'In') ? 'i' : 'o';\n\
if(value != null){\n\
var v = (value == 'Write LOW') ? '0' : '1';\n\
return (io+d+v);    \n\
} else {\n\
return (io+d);  \n\
}\n\
}\n\
$(function() {\n\
$('#status').css('background','grey');\n\
$('#write1').on('click', function (e) {\n\
var dir = $('#dir').val();\n\
$('#status').css('background','red');\n\
$('#status').html('#status');\n\
if(dir == 'In'){\n\
alert('direction is In');\n\
} else {\n\
var gpio = $('#gpio').val();\n\
var value = $('#write1').text();\n\
$.get('/req?out=' + ml(gpio, dir, value), function(d, s){\n\
$('#status').css('background','green');\n\
$('#status').html(s);\n\
});      \n\
}\n\
})\n\
$('#write0').on('click', function (e) {\n\
var dir = $('#dir').val();\n\
$('#status').css('background','red');\n\
$('#status').html('#status');\n\
if(dir == 'In'){\n\
alert('direction is In');\n\
} else {\n\
var gpio = $('#gpio').val();\n\
var value = $('#write0').text();\n\
$.get('/req?out=' + ml(gpio, dir, value), function(d, s){\n\
$('#status').css('background','green');\n\
$('#status').html(s);\n\
});        \n\
}\n\
})\n\
$('#read').on('click', function (e) {\n\
var dir = $('#dir').val();\n\
$('#status').css('background','red');\n\
$('#status').html('#value');\n\
if(dir == 'Out'){\n\
alert('direction is Out');\n\
} else {\n\
var gpio = $('#gpio').val();\n\
$.get('/req?in=' + ml(gpio, dir, null), function(d, s){\n\
if(s == 'success'){\n\
$('#status').css('background','yellow');\n\
$('#status').html(d);\n\
}else{\n\
$('#status').html(s);\n\
}               \n\
});     \n\
}\n\
})\n\
});\n\
</script>\n\
</body>\n\
</html>";

void handleRoot() {
    server.send(200, "text/html", res);
}
/* ham nay de xu lu yeu cau cua nguoi dung
req?out=0o1 -> user request GPIO0 output gia tri HIGH 
req?in=0i -> user request GPIO0 input */
void handleGPIO() {
  Serial.println("got request");
  if(server.args() > 0){
    String req = server.argName(0);
    if(req == "out"){
      server.send(200, "text/plain", "OK");
      /* parse req */
      String param = server.arg(0);
      int p = param.indexOf('o');
      int pin = param.substring(0,p).toInt();
      int value = param.substring(p+1).toInt();
      /* cai dat pin va ghi gia tri */
      pinMode(pin, OUTPUT);
      digitalWrite(pin, value);
    }else if(req == "in"){
      String param = server.arg(0);
      /* parse req */
      int p = param.indexOf('i');
      int pin = param.substring(0,p).toInt();
      /* cai dat pin va doc input */
      pinMode(pin, INPUT);
      int value = digitalRead(pin);
      char str[3] = {0,0,0};
      sprintf(str, "%d", value);
      /* respond cho nguoi dung */
      server.send(200, "text/plain", str);
    }
  }
}
/* cannot handle request so return 404 */
void handleNotFound(){
  String message = "File Not Found\n\n\n\n";
  server.send(404, "text/plain", message);
}

void setup(void){
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");

  /*cho wifi ket not */
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  /* nhung ham nay se duoc goi khi co user request va respond */
  server.on("/", handleRoot);
  /* this callback handle GPIO request and respond*/
  server.on("/req", handleGPIO);

  server.onNotFound(handleNotFound);
  server.begin();

  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
}
3.2 With sdcard
- Create a index.html file in memory card at root "/" with content:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
<!DOCTYPE html>
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
   
<div class="container">
    <h2>iotsharing.com - ESP32 WEBGPIO</h2>
    </br>  
    <table class="table">
    <thead>
      <tr>
        <th>Status</th>
        <th>GPIO</th>
        <th>In/Out</th>
        <th>Write HIGH</th>
        <th>Write LOW</th>
        <th>Read</th>
        
      </tr>
    </thead>
    <tbody>
        <tr>
            <td>  
                <button class="btn btn-default dropdown-toggle" type="status" id="status" >#status</button>
            </td>
            <td> 
                <select id = "gpio" class="btn btn-default dropdown-toggle">
                    <option>GPIO0</option>
                    <option>GPIO2</option>
                    <option>GPIO4</option>
                </select>
            </td>
            <td>  
                <select id = "dir" class="btn btn-default dropdown-toggle">
                    <option>Out</option>
                    <option>In</option>
                </select>
            </td>
            <td>  
                <button class="btn btn-default dropdown-toggle" type="button" id="write1" >Write HIGH</button>
            </td>
            <td>  
                <button class="btn btn-default dropdown-toggle" type="button" id="write0" >Write LOW</button>
            </td>
            <td>  
                <button class="btn btn-default dropdown-toggle" type="button" id="read" >Read</button>
            </td>
      </tr>
    </tbody>
    </table>                                        
</div>
<script>
function ml(gpio, dir, value) {
    var io = gpio.replace("GPIO", "");
    var d = (dir == "In") ? "i" : "o";
    if(value != null){
        var v = (value == "Write LOW") ? "0" : "1";
        return (io+d+v);    
    } else {
        return (io+d);  
    }
}
$(function() {
    $("#status").css("background","grey");
    $('#write1').on('click', function (e) {
        var dir = $('#dir').val();
        $("#status").css("background","red");
        $("#status").html("#status");
        if(dir == "In"){
            alert("direction is In");
        } else {
            var gpio = $('#gpio').val();
            var value = $('#write1').text();
            $.get("/req?out=" + ml(gpio, dir, value), function(d, s){
                $("#status").css("background","green");
                $("#status").html(s);
            });      
        }
    })
    $('#write0').on('click', function (e) {
        var dir = $('#dir').val();
        $("#status").css("background","red");
        $("#status").html("#status");
        if(dir == "In"){
            alert("direction is In");
        } else {
            var gpio = $('#gpio').val();
            var value = $('#write0').text();
            $.get("/req?out=" + ml(gpio, dir, value), function(d, s){
                $("#status").css("background","green");
                $("#status").html(s);
            });        
        }
    })
    $('#read').on('click', function (e) {
        var dir = $('#dir').val();
        $("#status").css("background","red");
        $("#status").html("#value");
        if(dir == "Out"){
            alert("direction is Out");
        } else {
            var gpio = $('#gpio').val();
            $.get("/req?in=" + ml(gpio, dir, null), function(d, s){
                if(s == "success"){
                    $("#status").css("background","yellow");
                    $("#status").html(d);
                }else{
                    $("#status").html(s);
                }               
            });     
        }
    })
});
</script>
</body>
</html>
- Here we use boostrap and jquery library. Bootstrap for creating GUI and jquery to handle user click action and create GET request to ESP32 web server.
- Create an Arduino project , save as esp32webgpio with code:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
#include <WiFiClient.h>
#include <ESP32WebServer.h>
#include <WiFi.h>
#include <ESPmDNS.h>
#include <SPI.h>
#include <mySD.h>

const char* ssid = "dd-wrt";
const char* password = "0000000000";

ESP32WebServer server(80);

void handleRoot() {
  /* we load the chart.html from microSD */
  File myFile = SD.open("INDEX~1.HTM");
  if (myFile) {  
    /* respond the content of file to client by calling streamFile()*/
    size_t sent = server.streamFile(myFile, "text/html");
    /* close the file */
    myFile.close();
  } else {
    Serial.println("error opening test.txt");
  }
}
/* this callback is to handle user request
req?out=0o1 -> user request GPIO0 output value HIGH 
req?in=0i -> user request GPIO0 input */
void handleGPIO() {
  
  if(server.args() > 0){
    String req = server.argName(0);
    if(req == "out"){
      server.send(200, "text/plain", "OK");
      /* this code is to parse req */
      String param = server.arg(0);
      int p = param.indexOf('o');
      int pin = param.substring(0,p).toInt();
      int value = param.substring(p+1).toInt();
      /* set pin and output value */
      pinMode(pin, OUTPUT);
      digitalWrite(pin, value);
      
    }else if(req == "in"){
      String param = server.arg(0);
      /* this code is to parse req */
      int p = param.indexOf('i');
      int pin = param.substring(0,p).toInt();
      /*set pin and read input */
      pinMode(pin, INPUT);
      int value = digitalRead(pin);
      char str[3] = {0,0,0};
      sprintf(str, "%d", value);
      /* respond the read value */
      server.send(200, "text/plain", str);
    }
  }
}
/* cannot handle request so return 404 */
void handleNotFound(){
  String message = "File Not Found\n\n";
  server.send(404, "text/plain", message);
}

void setup(void){
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  Serial.println("");

  // Wait for connection
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected to ");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("esp32")) {
    Serial.println("MDNS responder started");
  }
  /* register the callbacks to process client request */
  /* root request we will read the memory card to get 
  the content of chrt.html and respond that content to client */
  server.on("/", handleRoot);
  /* this callback handle GPIO request and respond*/
  server.on("/req", handleGPIO);

  server.onNotFound(handleNotFound);
  server.begin();

  Serial.println("HTTP server started");
  Serial.print("Initializing SD card...");
  /* initialize microSD */
  if (!SD.begin(26, 14, 12, 27)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
}

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

Figure: ESP32 WebGPIO demo

0 comments: