IoT Sharing: Projects

Hot

Showing posts with label Projects. Show all posts
Showing posts with label Projects. Show all posts

Sunday, December 3, 2017

Demo 37: Display distance measured by ultrasonic sensor using module 7-segment-LED-N-Digits

3:43 AM 1
1. Introduction
Today I will show you how to use module 7-segment-LED-N-Digits to display distance which is measured by ultrasonic sensor.
2. Hardware and Software 
2.1 Ultrasonic sensor
I used HC - SR04.

Figure: Ultrasonic sensor HC - SR04 (Source: Internet) 
The sensor has 2 heads: one is emit the ultrasonic and one receives it when the ultrasonic is reflect by the obstacle. The range of this sensor is 2cm -  400cm non-contact.
Figure: the operation mechanism of ultrasonic sensor
The picture below is timing diagram of HC - SR04.
 
Figure: timing diagram of HC - SR04
The basic principle of work:
(1) Using IO trigger for at least 10us high level signal.
(2) The Module automatically sends eight 40 kHz and detect whether there is a ultrasonic signal back.
(3) IF the ultrasonic signal back, through high level, time of high output IO duration is the time from sending ultrasonic to returning.
Test distance = (High level time x velocity of sound (299 cm/us) / 2
2.2 7-segment-LED-8-digit module
I used
Figure: 7-segment-LED-8-digit module
This module used IC 74HC595. It is a 8 bits shift register. With this IC we can save more IO digital instead of using a lot of IO pins to trigger each segment of LED.
Figure: schematic application of IC 74HC595
In order to bring data to this LED module, first we shift data that will be displayed on LED then we shift the value that indicate which LED in order that will display the data. i created the library here. The library is easy to use. You create an instance of library  
EspLed7SegNDigit ledm(SCLK, RCLK, DIO, 8);
where SCLK, RCLK, DIO is pins that connect between ESP and LED module. 8 is number of LEDs in module.
We can use ledm.setCharAt(8, 'd'); to set the value (here is character 'd') that will be displayed at specific LED (here is LED number 8).
In order to human can see the LED clearly, the LED scan time will be default 400ms (25 frames/second). You can set it using setRefreshTime(ms). The callback function updateDisplayCb() will be invoked after every refreshing time to update new value for LED displaying. You should call "ledm.clearDisplay()" to clear LED module before updating new display. You can use ledm.displayNum(num, 3); to display the float number with 3 digits behind the dot character. The function "ledm.loop();" will run continuously to update the display on LED.
Pins connection:
LED module
+SCLK with ESP32 GPIO14+RCLK with ESP32 GPIO27
+DIO with ESP32 GPIO12
Ultrasonic sensor
+TRIG with ESP32 GPIO25
+ECHO with ESP32 GPIO33
2.3 Full software
Our application has 2 FreeRTOS tasks: 1 for ultrasonic measurement and 1 for LED updating.
This version do not use FreeRTOS
 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
#include "EspLed7SegNDigit.h"

//LED pins
int SCLK = 14; //pulse
int RCLK = 27; //latch
int DIO = 12;  //data

//utrasonic pins
int TRIG_PIN = 25;
int ECHO_PIN = 33;

unsigned long startMeasure = 0;
unsigned long endMeasure = 0;
unsigned long measureTime = 0;
unsigned long distance = 0;

EspLed7SegNDigit ledm(SCLK, RCLK, DIO, 8);

void updateDisplayCb(void){
  ledm.clearDisplay();
  ledm.setCharAt(8, 'd');
  ledm.setCharAt(7, 's');
  ledm.setCharAt(6, 't');
  ledm.displayNum(distance, 3);
}

void initUltra(){
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void ultraTask(){

  //trigger sensor with pulse LOW-HIGH-LOW
  digitalWrite(TRIG_PIN, LOW);
  //wait 2 us
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  //wait 10 us
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  
  //at the beginning ECHO pin will be pull LOW until finishing transmitting ultrasonic signal
  while (digitalRead(ECHO_PIN) == 0){
    startMeasure = micros();
  }
  //ECHO pin will be pulled HIGH until get response
  while (digitalRead(ECHO_PIN) == 1){
    endMeasure = micros();
  }
  
  //response time will be calculated by
  measureTime = endMeasure - startMeasure;

  //convert to cm
  distance = (measureTime)/29/2;
  Serial.printf("distance = %d\n", distance);
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  initUltra();
  //this callback will be invoked for updating new ultrasonic value
  ledm.setUpdateCb(updateDisplayCb);

  ledm.setCharAt(8, 'd');
  ledm.setCharAt(7, 's');
  ledm.setCharAt(6, 't');
  //in loop it take time to do ultraTask so we decrease refresh time
  ledm.setRefreshTime(100);
}
long tick = 0;
void loop() {
  // put your main code here, to run repeatedly:
  ledm.loop();
  long now = millis();
  if (now - tick > 1000) {
    tick = now;
    ultraTask();
  }
}
This version uses FreeRTOS

 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
#include "EspLed7SegNDigit.h"

//utrasonic pins
int TRIG_PIN = 25;
int ECHO_PIN = 33;

//LED pins
int SCLK = 14; //pulse
int RCLK = 27; //latch
int DIO = 12;  //data

unsigned long measureTime = 0;
unsigned long distance = 0;
EspLed7SegNDigit ledm(SCLK, RCLK, DIO, 8);

void initUltra(){
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
}

void updateDisplayCb(void){
  ledm.clearDisplay();
  ledm.setCharAt(8, 'd');
  ledm.setCharAt(7, 's');
  ledm.setCharAt(6, 't');
  ledm.displayNum(distance, 3);
}

void setup() {
  Serial.begin(112500);
  initUltra();
  //this callback will be invoked for updating new ultrasonic value
  ledm.setUpdateCb(updateDisplayCb);
  ledm.setCharAt(8, 'd');
  ledm.setCharAt(7, 's');
  ledm.setCharAt(6, 't');
  ledm.setRefreshTime(100);
  /* we create a new task here */
  xTaskCreate(
      ultraTask,                /* Task function. */
      "ultrasonic Task",        /* name of task. */
      10000,                    /* Stack size of task */
      NULL,                     /* parameter of the task */
      3,                        /* priority of the task */
      NULL);                    /* Task handle to keep track of created task */
}

/* the forever loop() function is invoked by Arduino ESP32 loopTask */
void loop() {
  ledm.loop();
  delay(1);
}
/* this function will be invoked when ultraTask was created */
void ultraTask( void * parameter )
{
  for(;;){

    //trigger sensor with pulse LOW-HIGH-LOW
    digitalWrite(TRIG_PIN, LOW);
    //wait 2 us
    delayMicroseconds(2);
    digitalWrite(TRIG_PIN, HIGH);
    //wait 10 us
    delayMicroseconds(10);
    digitalWrite(TRIG_PIN, LOW);
    measureTime = pulseIn(ECHO_PIN, HIGH);//read measurement time for HIGH level from Echo
    //convert to cm
    distance = (measureTime)/29/2;
    Serial.printf("distance = %d\n", distance);
    delay(1000);
  }
  vTaskDelete( NULL );
}
3. Result
Read More

Saturday, November 25, 2017

Demo 36: Firmware update OTA via ESP Http Web Server

7:26 PM 31
1. Introduction
In Demo 34: firmware update OTA for ESP32 using HTTP and sdcard and Demo 35: firmware update OTA for ESP32 directly using HTTP, I showed ways to update firmware OTA. In this demo, I will show you another way. That is updating firmware OTA for ESP via ESP Http Web server. With this demo, ESP will act as a web server and user will access the web server and upload the firmware file to ESP via web browser.
Figure: Web interface of the demo
User choose Browse button, navigate to firmware file and press Update button. The updating progress will be shown.
2. Hardware
Using Demo 1 to connect ESP to LED.
3. Software
- First, we will create a simple LED blink application, export the binary file for updating. In order to export the .bin file from Arduino IDE Menu, we choose Sketch -> Export compiled Binary. After finishing we choose Sketch -> Show Sketch Folder. You will see the .bin file there, rename it as led.bin. Below is the LED blinky code.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int ledPin = 14; 

void setup(){
  pinMode(ledPin, OUTPUT);
}

void loop(){
  digitalWrite(ledPin, HIGH);
  delay(1000);
  digitalWrite(ledPin, LOW);
  delay(1000);
} 
- Second, we will re-use the Web Server library in Demo 12: How to turn the Arduino ESP32 into a Web Server. Beside that, i also used the jquery library to create uploading Http POST request. MDNS (Demo 9: How to use mDNS to resolve host names to Arduino ESP32 IP addresses) was used to resolve host name for our web server instead of using IP address directly. The code will be explained below.
  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
#include <WiFi.h>
#include <WiFiClient.h>
#include <ESP32WebServer.h>
#include <ESPmDNS.h>
#include <Update.h>
#include "esp_wps.h"
const char* host = "esp32webupdate";
const char* ssid = "dd-wrt";
const char* password = "0000000000";

ESP32WebServer server(80);
const char* serverIndex = "<script src='https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js'></script>"
"<form method='POST' action='#' enctype='multipart/form-data' id='upload_form'>"
    "<input type='file' name='update'>"
    "<input type='submit' value='Update'>"
"</form>"
"<div id='prg'>progress: 0%</div>"
"<script>"
"$('form').submit(function(e){"
    "e.preventDefault();"
      "var form = $('#upload_form')[0];"
      "var data = new FormData(form);"
      " $.ajax({"
            "url: '/update',"
            "type: 'POST',"               
            "data: data,"
            "contentType: false,"                  
            "processData:false,"  
            "xhr: function() {"
                "var xhr = new window.XMLHttpRequest();"
                "xhr.upload.addEventListener('progress', function(evt) {"
                    "if (evt.lengthComputable) {"
                        "var per = evt.loaded / evt.total;"
                        "$('#prg').html('progress: ' + Math.round(per*100) + '%');"
                    "}"
               "}, false);"
               "return xhr;"
            "},"                                
            "success:function(d, s) {"    
                "console.log('success!')"
           "},"
            "error: function (a, b, c) {"
            "}"
          "});"
"});"
"</script>";

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

    // Connect to WiFi network
    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());

    /*use mdns for host name resolution*/
    if (!MDNS.begin(host)) {
        Serial.println("Error setting up MDNS responder!");
        while(1) {
            delay(1000);
        }
    }
    Serial.println("mDNS responder started");
    /*return index page which is stored in serverIndex */
    server.on("/", HTTP_GET, [](){
      server.sendHeader("Connection", "close");
      server.send(200, "text/html", serverIndex);
    });
    /*handling uploading firmware file */
    server.on("/update", HTTP_POST, [](){
      server.sendHeader("Connection", "close");
      server.send(200, "text/plain", (Update.hasError())?"FAIL":"OK");
      esp_wifi_wps_disable(); ESP.restart();
    },[](){
      HTTPUpload& upload = server.upload();
      if(upload.status == UPLOAD_FILE_START){
        Serial.printf("Update: %s\n", upload.filename.c_str());
        if(!Update.begin(UPDATE_SIZE_UNKNOWN)){//start with max available size
          Update.printError(Serial);
        }
      } else if(upload.status == UPLOAD_FILE_WRITE){
        /* flashing firmware to ESP*/
        if(Update.write(upload.buf, upload.currentSize) != upload.currentSize){
          Update.printError(Serial);
        }
      } else if(upload.status == UPLOAD_FILE_END){
        if(Update.end(true)){ //true to set the size to the current progress
          Serial.printf("Update Success: %u\nRebooting...\n", upload.totalSize);
        } else {
          Update.printError(Serial);
        }
      }
    });
    server.begin();
}

void loop(void){
  server.handleClient();
  delay(1);
}
In the code, the variable serverIndex holds the index page which is return to the web browser firstly. We will use "$.ajax" to create asynchronous uploading request. This request will be handled by "/update" action at web server. We also use "var xhr = new window.XMLHttpRequest()" to handle the progress of uploading.
The code "MDNS.begin(host)" will use MDNS to resolve "http://esp32webupdate.local" to our web server IP address.
The code "server.on("/", HTTP_GET, []()" will handle the first HTTP GET request from web browser and return the http status code 200 and the web page content in serverIndex variable.
The code "server.on("/update", HTTP_POST, []()" will handle the uploading firmware file process via HTTP POST. We handle the order of process via "upload.status" and use Update for flashing firmware. After finishing, we call "ESP.restart();" to restart ESP to get effect.
4. Result
Open web browser and Browse to the file "led.bin" and press Update button.

Read More

Sunday, July 9, 2017

Demo 26: How to use Arduino ESP32 I2S (external DAC and built-in DAC) to play wav music file from sdcard

3:51 AM 23
1. Introduction
- ESP32 has two I2S peripherals. They can be configured to input and output sample data. They also supports DMA to stream sample data without needing CPU operations. I2S output can also be routed directly to the Digital to Analog Converter output (GPIO25 and GPIO26) without needing external I2S codec.
- In this demo I will show you how to use Arduino ESP32 I2S to play wav music file from sdcard. I chose wav file because it is not compressed like mp3 file. So we need not to de-compress it.
- There are 2 demos for this post:
    1. I used external I2S codec, 2 speakers and 1 module micro sdcard.
    2. I Used internal DAC, 2 speakers and 1 module micro sdcard.
Figure: I used external I2S codec for this demo, 2 speakers and 1 module micro sdcard
2. Hardware
Connect hardware like below:
[ESP32 IO32 – CS MICROSD]
[ESP32 IO14 – MOSI MICROSD]
[ESP32 IO13 – MISO MICROSD]
[ESP32 IO27 – SCK MICROSD]
[ESP32 IO26 – I2S codec BCK]
[ESP32 IO22 – I2S codec DATA]
[ESP32 IO25 – I2S codec LRCK]
[ESP32 GND – I2S codec GND]
[ESP32 GND – GND MICROSD]
[5V – VCC MICROSD]
[5V – I2S codec]
3. Software
- We will re-use Demo 7 for sdcard reading and I2S driver here. You can download the document about the wav file format here.
Note: You can down full project including wav file sample : https://github.com/nhatuan84/esp32-i2s-sdcard-wav-player
3.1 The code with external DAC 
  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
#include <mySD.h>
#include "driver/i2s.h"
#include "freertos/queue.h"

#define CCCC(c1, c2, c3, c4)    ((c4 << 24) | (c3 << 16) | (c2 << 8) | c1)

/* these are data structures to process wav file */
typedef enum headerState_e {
    HEADER_RIFF, HEADER_FMT, HEADER_DATA, DATA
} headerState_t;

typedef struct wavRiff_s {
    uint32_t chunkID;
    uint32_t chunkSize;
    uint32_t format;
} wavRiff_t;

typedef struct wavProperties_s {
    uint32_t chunkID;
    uint32_t chunkSize;
    uint16_t audioFormat;
    uint16_t numChannels;
    uint32_t sampleRate;
    uint32_t byteRate;
    uint16_t blockAlign;
    uint16_t bitsPerSample;
} wavProperties_t;
/* variables hold file, state of process wav file and wav file properties */    
File root;
headerState_t state = HEADER_RIFF;
wavProperties_t wavProps;

//i2s configuration 
int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
     .sample_rate = 36000,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB),
     .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, // high interrupt priority
     .dma_buf_count = 8,
     .dma_buf_len = 64   //Interrupt level 1
    };
    
i2s_pin_config_t pin_config = {
    .bck_io_num = 26, //this is BCK pin
    .ws_io_num = 25, // this is LRCK pin
    .data_out_num = 22, // this is DATA output pin
    .data_in_num = -1   //Not used
};
//
void debug(uint8_t *buf, int len){
  for(int i=0;i<len;i++){
    Serial.print(buf[i], HEX);
    Serial.print("\t");
  }
  Serial.println();
}
/* write sample data to I2S */
int i2s_write_sample_nb(uint32_t sample){
  return i2s_write_bytes((i2s_port_t)i2s_num, (const char *)&sample, sizeof(uint32_t), 100);
}
/* read 4 bytes of data from wav file */
int read4bytes(File file, uint32_t *chunkId){
  int n = file.read((uint8_t *)chunkId, sizeof(uint32_t));
  return n;
}

/* these are function to process wav file */
int readRiff(File file, wavRiff_t *wavRiff){
  int n = file.read((uint8_t *)wavRiff, sizeof(wavRiff_t));
  return n;
}
int readProps(File file, wavProperties_t *wavProps){
  int n = file.read((uint8_t *)wavProps, sizeof(wavProperties_t));
  return n;
}

void setup()
{
  Serial.begin(115200);
  Serial.print("Initializing SD card...");
  if (!SD.begin(32, 14, 13, 27)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  delay(1000);
  /* open wav file and process it */
  root = SD.open("T.WAV");
  if (root) {    
    int c = 0;
    int n;
    while (root.available()) {
      switch(state){
        case HEADER_RIFF:
        wavRiff_t wavRiff;
        n = readRiff(root, &wavRiff);
        if(n == sizeof(wavRiff_t)){
          if(wavRiff.chunkID == CCCC('R', 'I', 'F', 'F') && wavRiff.format == CCCC('W', 'A', 'V', 'E')){
            state = HEADER_FMT;
            Serial.println("HEADER_RIFF");
          }
        }
        break;
        case HEADER_FMT:
        n = readProps(root, &wavProps);
        if(n == sizeof(wavProperties_t)){
          state = HEADER_DATA;
        }
        break;
        case HEADER_DATA:
        uint32_t chunkId, chunkSize;
        n = read4bytes(root, &chunkId);
        if(n == 4){
          if(chunkId == CCCC('d', 'a', 't', 'a')){
            Serial.println("HEADER_DATA");
          }
        }
        n = read4bytes(root, &chunkSize);
        if(n == 4){
          Serial.println("prepare data");
          state = DATA;
        }
        //initialize i2s with configurations above
        i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
        i2s_set_pin((i2s_port_t)i2s_num, &pin_config);
        //set sample rates of i2s to sample rate of wav file
        i2s_set_sample_rates((i2s_port_t)i2s_num, wavProps.sampleRate); 
        break; 
        /* after processing wav file, it is time to process music data */
        case DATA:
        uint32_t data; 
        n = read4bytes(root, &data);
        i2s_write_sample_nb(data); 
        break;
      }
    }
    root.close();
  } else {
    Serial.println("error opening test.txt");
  }
  i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver 
  Serial.println("done!");
}

void loop()
{
}
3.2 The code with built-in DAC
  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
#include <mySD.h>
#include "driver/i2s.h"
#include "freertos/queue.h"

#define CCCC(c1, c2, c3, c4)    ((c4 << 24) | (c3 << 16) | (c2 << 8) | c1)

/* these are data structures to process wav file */
typedef enum headerState_e {
    HEADER_RIFF, HEADER_FMT, HEADER_DATA, DATA
} headerState_t;

typedef struct wavRiff_s {
    uint32_t chunkID;
    uint32_t chunkSize;
    uint32_t format;
} wavRiff_t;

typedef struct wavProperties_s {
    uint32_t chunkID;
    uint32_t chunkSize;
    uint16_t audioFormat;
    uint16_t numChannels;
    uint32_t sampleRate;
    uint32_t byteRate;
    uint16_t blockAlign;
    uint16_t bitsPerSample;
} wavProperties_t;
/* variables hold file, state of process wav file and wav file properties */    
File root;
headerState_t state = HEADER_RIFF;
wavProperties_t wavProps;

//i2s configuration 
int i2s_num = 0; // i2s port number
i2s_config_t i2s_config = {
     .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN),
     .sample_rate = 44100,
     .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, /* the DAC module will only take the 8bits from MSB */
     .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
     .communication_format = (i2s_comm_format_t)I2S_COMM_FORMAT_I2S_MSB,
     .intr_alloc_flags = 0, // default interrupt priority
     .dma_buf_count = 8,
     .dma_buf_len = 64,
     .use_apll = 0
    };
//
void debug(uint8_t *buf, int len){
  for(int i=0;i<len;i++){
    Serial.print(buf[i], HEX);
    Serial.print("\t");
  }
  Serial.println();
}
/* write sample data to I2S */
int i2s_write_sample_nb(uint8_t sample){
  return i2s_write_bytes((i2s_port_t)i2s_num, (const char *)&sample, sizeof(uint8_t), 100);
}
/* read 4 bytes of data from wav file */
int read4bytes(File file, uint32_t *chunkId){
  int n = file.read((uint8_t *)chunkId, sizeof(uint32_t));
  return n;
}

int readbyte(File file, uint8_t *chunkId){
  int n = file.read((uint8_t *)chunkId, sizeof(uint8_t));
  return n;
}

/* these are function to process wav file */
int readRiff(File file, wavRiff_t *wavRiff){
  int n = file.read((uint8_t *)wavRiff, sizeof(wavRiff_t));
  return n;
}
int readProps(File file, wavProperties_t *wavProps){
  int n = file.read((uint8_t *)wavProps, sizeof(wavProperties_t));
  return n;
}

void setup()
{
  Serial.begin(115200);
  Serial.print("Initializing SD card...");
  if (!SD.begin(32, 14, 13, 27)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  delay(1000);
  /* open wav file and process it */
  root = SD.open("T.WAV");
  if (root) {    
    int c = 0;
    int n;
    while (root.available()) {
      switch(state){
        case HEADER_RIFF:
        wavRiff_t wavRiff;
        n = readRiff(root, &wavRiff);
        if(n == sizeof(wavRiff_t)){
          if(wavRiff.chunkID == CCCC('R', 'I', 'F', 'F') && wavRiff.format == CCCC('W', 'A', 'V', 'E')){
            state = HEADER_FMT;
            Serial.println("HEADER_RIFF");
          }
        }
        break;
        case HEADER_FMT:
        n = readProps(root, &wavProps);
        if(n == sizeof(wavProperties_t)){
          state = HEADER_DATA;
        }
        break;
        case HEADER_DATA:
        uint32_t chunkId, chunkSize;
        n = read4bytes(root, &chunkId);
        if(n == 4){
          if(chunkId == CCCC('d', 'a', 't', 'a')){
            Serial.println("HEADER_DATA");
          }
        }
        n = read4bytes(root, &chunkSize);
        if(n == 4){
          Serial.println("prepare data");
          state = DATA;
        }
        //initialize i2s with configurations above
        i2s_driver_install((i2s_port_t)i2s_num, &i2s_config, 0, NULL);
        i2s_set_pin((i2s_port_t)i2s_num, NULL);
        //set sample rates of i2s to sample rate of wav file
        i2s_set_sample_rates((i2s_port_t)i2s_num, wavProps.sampleRate); 
        break; 
        /* after processing wav file, it is time to process music data */
        case DATA:
        uint8_t data; 
        n = readbyte(root, &data);
        i2s_write_sample_nb(data); 
        break;
      }
    }
    root.close();
  } else {
    Serial.println("error opening test.txt");
  }
  i2s_driver_uninstall((i2s_port_t)i2s_num); //stop & destroy i2s driver 
  Serial.println("done!");
}

void loop()
{
}
4. Result
Read More

Thursday, June 15, 2017

Demo 21: How to use interrupt in Arduino ESP32

9:16 AM 3
1. Introduction
- When interrupt occurs, micro-controller will stop current task immediately to process the event that cause the interrupt. If we do not use interrupt, we have to use loop control statement to always listen on events that we are interested in. In case our program has many tasks to do, we may miss the interested event because micro-controller is busy to process another task.
- The demo for this tutorial is quite simple. We use a button to connect to an interrupt pin. Every time the user press the button, it will cause an interrupt on ESP32 and ESP32 process this interrupt by toggle a LED.
2. Hardware
Figure: ESP32 attach external interrupt
Here we set interrupt pin as INPUT_PULLUP, it means there is a resister connect the input pin to Vcc so our circuit is more simple.
Figure: ESP32 connect to button (input_pullup) and LED
Connections:
[ESP32 GIO12 - BUTTON - GND]
[ESP32 GIO14 - LED - GND]
3. Software
- In order to use interrupt, we will use the 2 functions: attachInterrupt(digitalPinToInterrupt(pin), ISR, mode)
+ pin: is the pin to attach interrupt.
+ ISR: the function to be invoked when interrupt occurs.
+ mode: which event trigger interrupt (CHANGE: trigger when the pin change value, RISING: when the pin go from LOW to HIGH, ...)
- Create an Arduino project 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
/* LED pin */
byte ledPin = 14;
/* pin that is attached to interrupt */
byte interruptPin = 12;
/* hold the state of LED when toggling */
volatile byte state = LOW;

void setup() {
  pinMode(ledPin, OUTPUT);
  /* set the interrupt pin as input pullup*/
  pinMode(interruptPin, INPUT_PULLUP);
  /* attach interrupt to the pin
  function blink will be invoked when interrupt occurs
  interrupt occurs whenever the pin change value */
  attachInterrupt(digitalPinToInterrupt(interruptPin), blink, CHANGE);
}

void loop() {
}

/* interrupt function toggle the LED */
void blink() {
  state = !state;
  digitalWrite(ledPin, state);
}
4. Result
Read More

Saturday, June 10, 2017

Demo 20: How to control a Servo via Arduino ESP32 Web Server

1:41 AM 3
1. Introduction
- In this tutorial, I will show you how to control a Servo motor via Arduino ESP32 Web Server.
Figure: Servo for this demo
- This servo:
+ Has 3 wires with colors: PWM, Vcc, Ground
+ Can rotate 180 degrees (90 degrees for each direction).
Figure: Pulse wave of trigger
- In order to control the rotation angle of servo we need to trigger pulses to Servo PWM pin. The pulses has pulse width between 0.6–2.4 ms and PWM signal period is 20 ms (50Hz). For example, if we trigger pulse with width is 0.6 ms  then servo is at 0 degrees angle, 1.5 ms  is 90 degrees angle and 2.4 ms is 180 degrees angle.
- We can use the map(angleDegrees, 0,180,600,2400) function to map the rotation angle to pulse width. This function will return the width of pulse in microseconds. Here 0 is minimum angle with pulse width 600 microseconds, 180 is maximum angle with pulse width 2400 microseconds.
2. Hardware
We connect:
[Servo PWM - ESP32 GPIO 12]
[Server VCC - ESP32 5V]
[Server GND - ESP32 GND]

Figure: Servo connect to ESP32
3. Software
3.1 Web server 
3.2 Servo
Create an Arduino project 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
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
#include <WiFiClient.h>
#include <ESP32WebServer.h>
#include <WiFi.h>
#include <ESPmDNS.h>

/* change your ssid and password here */
const char* ssid = "Coffee Amy";
const char* password = "amy12345";

ESP32WebServer server(80);

const int servoPin = 12;
int oldAngle = 0; 
int PWM_WIDTH = 20000;

/* this array contains the web will be responded to client
it uses jquery for making GET request and processing slider UI control */
char res[900]=
"<!DOCTYPE html>\
<html>\
<head>\
<meta charset='utf-8'>\
<H1>iotsharing.com Servo</H1>\
<link href='https://code.jquery.com/ui/1.10.4/themes/ui-lightness/jquery-ui.css' rel='stylesheet'>\
<script src='https://code.jquery.com/jquery-1.10.2.js'></script>\
<script src='https://code.jquery.com/ui/1.10.4/jquery-ui.js'></script>\
<script>\
$(function() {\
$('#sliVal').html('Angle: 0');\
$('#slider').slider({\
    orientation:'vertical',value:0,min: 0,max: 180,step: 5\
});\
$('#slider').slider().bind({\
slidestop: function(e,ui){\
    $('#res').css('background','red');\
    $('#sliVal').html('Angle: '+ui.value);\
    $.get('/ang?val=' + ui.value, function(d, s){\
        $('#res').css('background','green');\
        $('#res').html(s);\
    }); \
}\
});\
});\
</script>\
</head>\
<body>\
<div id='slider'></div></br>\
<div id='sliVal'></div>\
<div id='res'></div>\
</body>\
</html>";

void handleRoot() {
  server.send(200, "text/html", res);
}
/* this function map from angle to pulse width */
int servoPulse(int angleDegrees)
{
  int pulseWidth = map(angleDegrees, 0,180,600,2400);
  return pulseWidth;
}
/* this function check the rotation angle
and trigger pulse accordingly*/
void servoGo(int oldAngle, int newAngle)
{
  int pulseWidth;
  if(oldAngle == newAngle){
    return;
  }else if(oldAngle < newAngle){
    /* clockwise processing */
    for (int i=oldAngle; i<=newAngle; i++){
      /* convert angle to pulse width us*/
      pulseWidth = servoPulse(i);
      /* trigger HIGH pulse */  
      digitalWrite(servoPin, HIGH);
      /* use delayMicroseconds to delay for pulseWidth */
      delayMicroseconds(pulseWidth); 
      /* trigger LOW pulse */                          
      digitalWrite(servoPin, LOW);
      /* use delayMicroseconds to delay 
      for rest time (20000 - pulseWidth) */ 
      delayMicroseconds(PWM_WIDTH - pulseWidth);   
    }
  }else if(oldAngle > newAngle){
    /* anti-clockwise processing */
    for (int i=oldAngle; i>=newAngle; i--){
      pulseWidth = servoPulse(i);
      digitalWrite(servoPin, HIGH);
      delayMicroseconds(pulseWidth);                           
      digitalWrite(servoPin, LOW);
      delayMicroseconds(PWM_WIDTH - pulseWidth);
    }
  }
}
/* this callback will be invoked when get servo rotation request */
void handleServo() {
  //Serial.println(server.argName(0));
  int newAngle = server.arg(0).toInt();
  servoGo(oldAngle, newAngle);
  oldAngle = newAngle;
  server.send(200, "text/html", "ok");
}

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");
  }
  
  server.on("/", handleRoot);
  server.on("/ang", handleServo);

  server.onNotFound(handleNotFound);

  server.begin();
  pinMode(servoPin, OUTPUT);
  Serial.println("HTTP server started");
}

void loop(void){
  server.handleClient();
}
4. Result
Figure: ESP32 web server control servo
Read More

Friday, June 2, 2017

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

8:42 AM 1
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
Read More
Thường mất vài phút để quảng cáo xuất hiện trên trang nhưng thỉnh thoảng, việc này có thể mất đến 1 giờ. Hãy xem hướng dẫn triển khai mã của chúng tôi để biết thêm chi tiết. Ðã xong