Course Content - Nabilphysics/esp32_IoT_short_course_CRUX GitHub Wiki

What is IoT ?

https://en.wikipedia.org/wiki/Internet_of_things https://internetofthingsagenda.techtarget.com/definition/Internet-of-Things-IoT

The Internet of Things (IoT) is the network of physical devices, vehicles, home appliances, and other items embedded with electronics, software, sensors, actuators, and connectivity which enables these things to connect and exchange data,creating opportunities for more direct integration of the physical world into computer-based systems, resulting in efficiency improvements, economic benefits, and reduced human exertions. The number of IoT devices increased 31% year-over-year to 8.4 billion in 2017 and it is estimated that there will be 30 billion devices by 2020. The global market value of IoT is projected to reach $7.1 trillion by 2020. IoT involves extending internet connectivity beyond standard devices, such as desktops, laptops, smartphones and tablets, to any range of traditionally dumb or non-internet-enabled physical devices and everyday objects. Embedded with technology, these devices can communicate and interact over the internet, and they can be remotely monitored and controlled.

ESP32 :

ESP32 is a series of low-cost, low-power system on a chip microcontrollers with integrated Wi-Fi and dual-mode Bluetooth. The ESP32 series employs a Tensilica Xtensa LX6 microprocessor in both dual-core and single-core variations and includes in-built antenna switches, RF balun, power amplifier, low-noise receive amplifier, filters, and power-management modules. ESP32 is created and developed by Espressif Systems, a Shanghai-based Chinese company, and is manufactured by TSMC using their 40 nm process. It is a successor to the ESP8266 microcontroller.

ESP32 Block Diagram

Features Features of the ESP32 include the following:
Processors:
CPU: Xtensa dual-core (or single-core) 32-bit LX6 microprocessor, operating at 160 or 240 MHz and performing at up to 600 DMIPS
Ultra low power (ULP) co-processor
Memory: 520 KiB SRAM
Wireless connectivity:
Wi-Fi: 802.11 b/g/n
Bluetooth: v4.2 BR/EDR and BLE
Peripheral interfaces:
12-bit SAR ADC up to 18 channels
2 × 8-bit DACs
10 × touch sensors (capacitive sensing GPIOs)
Temperature sensor
4 × SPI
2 × I²S interfaces
2 × I²C interfaces
3 × UART
SD/SDIO/CE-ATA/MMC/eMMC host controller
SDIO/SPI slave controller
Ethernet MAC interface with dedicated DMA and IEEE 1588 Precision Time Protocol support
CAN bus 2.0
Infrared remote controller (TX/RX, up to 8 channels)
Motor PWM
LED PWM (up to 16 channels)
Hall effect sensor
Ultra low power analog pre-amplifier
Security:
IEEE 802.11 standard security features all supported, including WFA, WPA/WPA2 and WAPI
Secure boot
Flash encryption
1024-bit OTP, up to 768-bit for customers
Cryptographic hardware acceleration: AES, SHA-2, RSA, elliptic curve cryptography (ECC), random number generator (RNG)
Power management:
Internal low-dropout regulator
Individual power domain for RTC
5uA deep sleep current
Wake up from GPIO interrupt, timer, ADC measurements, capacitive touch sensor interrupt

### ESP 32 Module :

Espressif makes several single and dual core ESP32 module. Using those modules Various company produce various ESP-32 based board for easy prototyping. Those development boards includes USB communication, Power management, Display ,etc depends on manufacturer.

Module that we are using :

http://s.click.aliexpress.com/e/cZmbc3tQ

Installation :

  1. Install Arduino IDE from this link https://www.arduino.cc/en/Main/Donate

  2. Install CP210X Driver if needed
    https://www.silabs.com/products/development-tools/software/usb-to-uart-bridge-vcp-drivers

  3. Open Arduino IDE > File > Preferences > Additional Board Manager URLs > Copy and Paste below link https://dl.espressif.com/dl/package_esp32_index.json

if you have installed ESP8266 previously use separate above link with comma.
4) Tools > Boards > Boards Manager > “Type ESP32” > Install
Another Alternative is VS Code with Platform IO
http://docs.platformio.org/en/latest/ide/vscode.html

Test With LED Blinking Code : Attach LED with a 1K resistor . If your module have built-in LED than make sure you enter the right GPIO(General Purpose Input Output) Pin. Now copy and paste code and upload to board.

int LED_PIN = 2;
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_PIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_PIN, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);                   	
  digitalWrite(LED_PIN, LOW);	// turn the LED off by making the voltage LOW
  delay(500);                
}

Programming Options (Functionality) of ESP32:

ESP32 has lots of functionality. Such as > GPIO Input,GPIO Output, PWM, Interrupt, WiFi AP, WiFi Scan, WiFi Connect, Web server, Bluetooth Normal, Bluetooth Low Energ (BLE), OTP, SD Card, HTTP Connection, File Server, SPI, SPIFFS, ESP NOW, DNS Server , SPI,I2C, Smart Config etc

ESP32 support AT Command.

GPIO Output :
General Purpose Input Output (GPIO) . When HIGH output is 3.3V and 0 when LOW

In setup. Usually setup function contains code those are necessary for configure ESP32 Board.
Step 1 : pinMode(GPIO , OUTPUT);
Step 2: digitalWrite(GPIO , HIGH);

if we need delay than call delay(ms) e.g. delay(1000) means wait 1 seconds or 1000 milliseconds
we can use array to control multiple GPIO to create pattern output. e.g. running light

GPIO Input :
We can use button input to call any event.
Circuit : A button has to connect with GPIO pin and GND. Input pullup should be activated. Teacher will explain about circuit and what is input pullup and pulldown.

pinMode(4, INPUT_PULLUP); 
// GPIO 4 pin as input and also internally connected with HIGH to avoid noise
uint8_t pinState = digitalRead(0);
//uint8_t means 8 bit integer . digitalRead will read pin state(HIGH or LOW) of GPIO pin.

Toggle LED Example :

//ESP32 Toggle LED Example
// Circuit: Connect and Push Button between GPIO 13 and GND
//Edited by
//Syed Razwanul Haque Nabil , CRUX 
int ledPin =2;
int buttonPin =13;
int buttonState = 0;
int ledState = 0;

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

void loop() {
  int reading = digitalRead(buttonPin);
  if(reading != buttonState ){
    delay(30); //debounce 
    buttonState = reading;
   if(buttonState == LOW){ 
    ledState = !ledState;
   }
   digitalWrite(ledPin, ledState);
  }
}

LED Output and Button Input with Serial communication :

Push Button 1 is attached with GPIO 4 and GND. Input Pullup is activated that means Input pin is connected with HIGH internally. When we press push button ESP32 will detect press as LOW One Led is connected with GPIO 2 or If your ESP32 module contains Builtn in LED you dont need to connect externally. (Teacher will explain what is Serial Communication and its baud rate) https://www.arduino.cc/reference/en/language/functions/communication/serial/
Difference Between Serial.Write() and Serial.Print()

/*
Push Button 1 is attached with GPIO 4 and GND.
Input Pullup is activated that means Input pin is connected with HIGH internally.
When we press push button ESP32 will detect press as LOW
One Led is connected with GPIO 2 or If your ESP32 module contains Builtn in LED   
*/

int pushButton1 = 4;
int ledPin1 = 2;
// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  // make the pushbutton's pin an input:
  pinMode(pushButton1, INPUT_PULLUP);
  pinMode(ledPin1, OUTPUT);
  
}
void loop() {
  // read the input pin:
  int buttonState1 = digitalRead(pushButton1);
  // print out the state of the button:
  Serial.println(buttonState1);
  delay(100);        // delay in between reads for stability
  if(buttonState1 == 0)
  {
    digitalWrite(ledPin1, HIGH);
  }
  else{
    digitalWrite(ledPin1, LOW);
  }
  
}

Button Debounce:

http://www.labbookpages.co.uk/electronics/debounce.html
Switch debouncing is one of those things you generally have to live with when playing with switches and digital circuits. If you want to input a manual switch signal into a digital circuit you'll need to debounce the signal so a single press doesn't appear like multiple presses.

Code From Arduino Example

/*
  Debounce

  Each time the input pin goes from LOW to HIGH (e.g. because of a push-button
  press), the output pin is toggled from LOW to HIGH or HIGH to LOW. There's a
  minimum delay between toggles to debounce the circuit (i.e. to ignore noise).

  The circuit:
  - LED attached from pin 13 to ground
  - pushbutton attached from pin 2 to GND
  - Internally Pullup (This pin is already internally HIGH)

  created 21 Nov 2006
  by David A. Mellis
  modified 30 Aug 2011
  by Limor Fried
  modified 28 Dec 2012
  by Mike Walters
  modified 30 Aug 2016
  by Arturo Guadalupi
  modified 16 Sep 2018
  by Syed Razwanul Haque (Nabil) , CRUX

  This example code is in the public domain.
  http://www.arduino.cc/en/Tutorial/Debounce
*/

// constants won't change. They're used here to set pin numbers:
const int buttonPin = 2;    // the number of the pushbutton pin
const int ledPin = 13;      // the number of the LED pin

// Variables will change:
int ledState = HIGH;         // the current state of the output pin
int buttonState;             // the current reading from the input pin
int lastButtonState = LOW;   // the previous reading from the input pin

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  // set initial LED state
  digitalWrite(ledPin, ledState);
}

void loop() {
  // read the state of the switch into a local variable:
  int reading = digitalRead(buttonPin);

  // check to see if you just pressed the button
  // (i.e. the input went from LOW to HIGH), and you've waited long enough
  // since the last press to ignore any noise:

  // If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;

      // only toggle the LED if the new button state is LOW
      if (buttonState == LOW) {
        ledState = !ledState;
      }
    }
  }

  // set the LED:
  digitalWrite(ledPin, ledState);

  // save the reading. Next time through the loop, it'll be the lastButtonState:
  lastButtonState = reading;
}

ESP32 Built-in Touch Sensor:

ESP32 has 10 built-in capacitive touch sensor(Teacher will explain what is capacitive touch) . We can use those sensors for input detection and implement in our various projects. In Arduino IDE we will define Touch sensors as T0, T1 , T2 and so on. Please see the block diagram of your ESP32 board to see respective GPIO pin . e.g. T0 means Touch Button 1 which is GPIO pin 4 touchRead(TOUTCH_PIN); function will return the value of Touch Pin. Usually when we touch this button value will decrease below 30 so ESP32 detects which pin has been touched. Some noise reduction technique is needed to reduce noise(Teacher will explain) http://www.esp32learning.com/code/esp32-capacitive-touch-example.php

/***************************************************** 
* ESP32 Touch Test and LED Ctrl
* Touch pin ==> Touch0 is T0 which is on GPIO 4 (D4).
* LED pin   ==> D2

*****************************************************/

#define TOUTCH_PIN T0 // ESP32 Pin D4
#define LED_PIN 2
int touch_value = 100;

void setup()
{
  Serial.begin(115200);
  delay(100); // give me time to bring up serial monitor
  Serial.println("ESP32 Touch Test");
  pinMode(LED_PIN, OUTPUT);
  digitalWrite (LED_PIN, LOW);
}

void loop()
{
  touch_value = touchRead(TOUTCH_PIN);
  Serial.println(touch_value);  // get value using T0 
  if (touch_value < 50)
  {
    digitalWrite (LED_PIN, HIGH);
  }
  else
  {
    digitalWrite (LED_PIN, LOW);
  }
  delay(100);
}

Control LED with Serial :

Now we ll control LED with serial monitor. When we will enter H or h led will HIGH and when we will enter L or l led will be LOW.
(Teacher Will Explain)

uint8_t ledPin = 2; //GPIO 2
char state;
void setup() {
 pinMode(ledPin, OUTPUT);
 Serial.begin(9600);
}
void loop() {
 if (Serial.available() > 0) {
   state = Serial.read();
   if (state == 'H' || state == 'h') {
     digitalWrite(ledPin, HIGH);
   }
   if (state == 'L' || state == 'l') {
     digitalWrite(ledPin, LOW);
   }
 }
 Serial.println(state);
 delay(50);
}

OLED

Step 1 : Download Library from Library manager Step 2 : Upload the code

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2018 by ThingPulse, Daniel Eichhorn
 * Copyright (c) 2018 by Fabrice Weinberg
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * ThingPulse invests considerable time and money to develop these open source libraries.
 * Please support us by buying our products (and not the clones) from
 * https://thingpulse.com
 *
 */
// Include the correct display library
// For a connection via I2C using Wire include
#include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
#include "SSD1306Wire.h" // legacy include: `#include "SSD1306.h"`
// or #include "SH1106Wire.h", legacy include: `#include "SH1106.h"`
// For a connection via I2C using brzo_i2c (must be installed) include
// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Brzo.h"
// #include "SH1106Brzo.h"
// For a connection via SPI include
// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
// #include "SSD1306Spi.h"
// #include "SH1106SPi.h"


// Initialize the OLED display using SPI
// D5 -> CLK
// D7 -> MOSI (DOUT)
// D0 -> RES
// D2 -> DC
// D8 -> CS
// SSD1306Spi        display(D0, D2, D8);
// or
// SH1106Spi         display(D0, D2);

// Initialize the OLED display using brzo_i2c
// D3 -> SDA
// D5 -> SCL
// SSD1306Brzo display(0x3c, D3, D5);
// or
// SH1106Brzo  display(0x3c, D3, D5);

// Initialize the OLED display using Wire library
SSD1306Wire  display(0x3c, 5, 4);
// SH1106 display(0x3c, D3, D5);

#define DEMO_DURATION 3000
typedef void (*Demo)(void);


void setup() {
 
// Initialising the UI will init the display too.
 display.init();
 display.flipScreenVertically(); //Rotate Display
//  display.setFont(ArialMT_Plain_10);
}
void drawFontFaceDemo() {
    // Font Demo1
    // create more fonts at http://oleddisplay.squix.ch/
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10); //font and font size
    display.drawString(0, 0, "WWW.CRUXBD.COM");// (x-axis,y-axis, text)
    display.setFont(ArialMT_Plain_16);
    //Previous font size 10 so we start "CRUX" after 10 so display.drawString(0, 10, "CRUX"); 
    display.drawString(0, 10, "CRUX"); 
    display.setFont(ArialMT_Plain_24);
    //Previous font size 10 than 16. Total=26 so we start "CRUX" after 26 so display.drawString(0, 10, "CRUX"); 
    display.drawString(0, 26, "CRUX");
}

void drawTextFlowDemo() {
    display.setFont(ArialMT_Plain_10);
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.drawStringMaxWidth(0, 0, 128,
      "Welcome to the IoT World of CRUX. Thank you very much" );
}

void loop() {
  // clear the display
  display.clear();
  drawFontFaceDemo();
  display.display();
  delay(1000);
  display.clear();
  drawTextFlowDemo();
  display.display();
  delay(1000);
}

WiFi Scan & connect

From ESP32 Example you will find basic demo code for WiFi scan and connect to a SSID.
First it will disconnect from WiFi network if connected than it will scan . WiFi.scanNetworks() will return number of SSID available . WiFi.SSID() will return SSID and WiFi.RSSI() will return RF strength of this particular SSID.

WiFi Smartconfig(not in this course but its a topics of our long course) can be used which is a modern and smart system to send wifi ssid and password to client device so we dont need to hard codded wifi ssid and password. See this link for details http://www.iotsharing.com/2017/05/how-to-use-smartconfig-on-esp32.html

/*
 *  This sketch demonstrates how to scan WiFi networks.
 *  The API is almost the same as with the WiFi Shield library,
 *  the most obvious difference being the different file you need to include:
 */
#include "WiFi.h"

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

    // Set WiFi to station mode and disconnect from an AP if it was previously connected
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);

    Serial.println("Setup done");
}

void loop()
{
    Serial.println("scan start");

    // WiFi.scanNetworks will return the number of networks found
    int n = WiFi.scanNetworks();
    Serial.println("scan done");
    if (n == 0) {
        Serial.println("no networks found");
    } else {
        Serial.print(n);
        Serial.println(" networks found");
        for (int i = 0; i < n; ++i) {
            // Print SSID and RSSI for each network found
            Serial.print(i + 1);
            Serial.print(": ");
            Serial.print(WiFi.SSID(i));
            Serial.print(" (");
            Serial.print(WiFi.RSSI(i));
            Serial.print(")");
            Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
            delay(10);
        }
    }
    Serial.println("");

    // Wait a bit before scanning again
    delay(5000);
}

**Connect to a Specific SSID : **
If you want to give multiple SSID and Password and let ESP32 to choose which to connect based on WiFI Signal strength than you have to implement WiFi Multi. It will be covered in 30 Hours course or see the example in Arduino IDE

/*
 This example is written for a network using WPA(Wi-Fi Protected Access) encryption. For
 WEP(Wired Equivalent Privacy) or WPA, change the Wifi.begin() call accordingly.
 created for arduino 25 Nov 2012
 by Tom Igoe
 Edited by Syed Razwanul Haque (Nabil)
 */

#include <WiFi.h>

const char* ssid     = "yourssid";
const char* password = "yourpasswd";

void setup()
{
    Serial.begin(115200);
    delay(10);
    // We start by connecting to a WiFi network
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);
    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }

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

 void loop(){
   }

WiFI Station, Wifi Web Server , AP, Captive Portal

Types of Wireless Network
Access Points(AP):
also known as AP or hotspots
To Create a WiFi Hotspot using ESP32

#include <WiFi.h>
 
const char *ssid = "MyESP32AP";// Hotspoot ID
const char *password = "testpassword"; // Password Require to Join
 
void setup() {
 
  Serial.begin(115200);
WiFi.softAP(ssid, password);
 
  Serial.println();
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());
 }
 
void loop() {}

In long course We ll implement more advanced features.
Now we ll create a WiFi Hotspot and Webserver to Control LED. So no wifi router connection will be needed. Copy from https://randomnerdtutorials.com/esp32-access-point-ap-web-server/

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com  
*********/

// Load Wi-Fi library
#include <WiFi.h>

// Replace with your network credentials
const char* ssid     = "ESP32-Access-Point";
const char* password = "123456789";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// Auxiliar variables to store the current output state
String output26State = "off";
String output27State = "off";

// Assign output variables to GPIO pins
const int output26 = 2;
const int output27 = 27;

void setup() {
  Serial.begin(115200);
  // Initialize the output variables as outputs
  pinMode(output26, OUTPUT);
  pinMode(output27, OUTPUT);
  // Set outputs to LOW
  digitalWrite(output26, LOW);
  digitalWrite(output27, LOW);

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Setting AP (Access Point)…");
  // Remove the password parameter, if you want the AP (Access Point) to be open
  WiFi.softAP(ssid, password);

  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);
  
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   // Listen for incoming clients

  if (client) {                             // If a new client connects,
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
            
            // turns the GPIOs on and off
            if (header.indexOf("GET /26/on") >= 0) {
              Serial.println("GPIO 26 on");
              output26State = "on";
              digitalWrite(output26, HIGH);
            } else if (header.indexOf("GET /26/off") >= 0) {
              Serial.println("GPIO 26 off");
              output26State = "off";
              digitalWrite(output26, LOW);
            } else if (header.indexOf("GET /27/on") >= 0) {
              Serial.println("GPIO 27 on");
              output27State = "on";
              digitalWrite(output27, HIGH);
            } else if (header.indexOf("GET /27/off") >= 0) {
              Serial.println("GPIO 27 off");
              output27State = "off";
              digitalWrite(output27, LOW);
            }
            
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");
            
            // Web Page Heading
            client.println("<body><h1>ESP32 Web Server</h1>");
            
            // Display current state, and ON/OFF buttons for GPIO 26  
            client.println("<p>GPIO 26 - State " + output26State + "</p>");
            // If the output26State is off, it displays the ON button       
            if (output26State=="off") {
              client.println("<p><a href=\"/26/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/26/off\"><button class=\"button button2\">OFF</button></a></p>");
            } 
               
            // Display current state, and ON/OFF buttons for GPIO 27  
            client.println("<p>GPIO 27 - State " + output27State + "</p>");
            // If the output27State is off, it displays the ON button       
            if (output27State=="off") {
              client.println("<p><a href=\"/27/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/27/off\"><button class=\"button button2\">OFF</button></a></p>");
            }
            client.println("</body></html>");
            
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

Wifi Station :
WiFi Station In IEEE 802.11 (Wi-Fi) terminology, a station (abbreviated as STA) is a device that has the capability to use the 802.11 protocol. For example, a station may be a laptop, a desktop PC, PDA, access point or Wi-Fi phone.

WiFi Web Server:
Using the WiFi library, your device will be able to answer a HTTP request. After opening a browser and navigating to your WiFi shield's IP address, your ESP32 will respond with HTML for a browser to display data.

File > Example > WiFi Web Server (Teacher will explain the process)

/*
 WiFi Web Server LED Blink

 A simple web server that lets you blink an LED via the web.
 This sketch will print the IP address of your WiFi Shield (once connected)
 to the Serial monitor. From there, you can open that address in a web browser
 to turn on and off the LED on pin 5.

 If the IP address of your shield is yourAddress:
 http://yourAddress/H turns the LED on
 http://yourAddress/L turns it off

 This example is written for a network using WPA encryption. For
 WEP or WPA, change the Wifi.begin() call accordingly.

 Circuit:
 * WiFi shield attached
 * LED attached to pin 5

 created for arduino 25 Nov 2012
 by Tom Igoe

ported for sparkfun esp32 
31.01.2017 by Jan Hendrik Berlin
 
 */

#include <WiFi.h>

const char* ssid     = "yourssid";
const char* password = "yourpasswd";

WiFiServer server(80);

void setup()
{
    Serial.begin(115200);
    pinMode(5, OUTPUT);      // set the LED pin mode

    delay(10);

    // We start by connecting to a WiFi network

    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, 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.begin();

}

int value = 0;

void loop(){
 WiFiClient client = server.available();   // listen for incoming clients

  if (client) {                             // if you get a client,
    Serial.println("New Client.");           // print a message out the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected()) {            // loop while the client's connected
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        if (c == '\n') {                    // if the byte is a newline character

          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();

            // the content of the HTTP response follows the header:
            client.print("Click <a href=\"/H\">here</a> to turn the LED on pin 5 on.<br>");
            client.print("Click <a href=\"/L\">here</a> to turn the LED on pin 5 off.<br>");

            // The HTTP response ends with another blank line:
            client.println();
            // break out of the while loop:
            break;
          } else {    // if you got a newline, then clear currentLine:
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }

        // Check to see if the client request was "GET /H" or "GET /L":
        if (currentLine.endsWith("GET /H")) {
          digitalWrite(5, HIGH);               // GET /H turns the LED on
        }
        if (currentLine.endsWith("GET /L")) {
          digitalWrite(5, LOW);                // GET /L turns the LED off
        }
      }
    }
    // close the connection:
    client.stop();
    Serial.println("Client Disconnected.");
  }
}

Captive Portal :
Captive Portal Explanation Video A captive portal is a web page which is displayed to newly connected users before they are granted broader access network resources.
(Teacher will explain about DNS Server and other terms)
Open File > Example > DNSServer

#include <WiFi.h>
#include <DNSServer.h>

const byte DNS_PORT = 53;
IPAddress apIP(192, 168, 1, 1);
DNSServer dnsServer;
WiFiServer server(80);

String responseHTML = ""
  "<!DOCTYPE html><html><head><title>CaptivePortal</title></head><body>"
  "<h1>Hello World!</h1><p>This is a captive portal example. All requests will "
  "be redirected here.</p></body></html>";

void setup() {
  WiFi.mode(WIFI_AP);
  WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0));
  WiFi.softAP("DNSServer CaptivePortal example");

  // if DNSServer is started with "*" for domain name, it will reply with
  // provided IP to all DNS request
  dnsServer.start(DNS_PORT, "*", apIP);

  server.begin();
}

void loop() {
  dnsServer.processNextRequest();
  WiFiClient client = server.available();   // listen for incoming clients

  if (client) {
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n') {
          if (currentLine.length() == 0) {
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();
            client.print(responseHTML);
            break;
          } else {
            currentLine = "";
          }
        } else if (c != '\r') {
          currentLine += c;
        }
      }
    }
    client.stop();
  }
}

WiFI Manager & Smart Config :

In this course we hard coded wifi credentials in our code. But this is not useful in production condition. We can solve this problem by using WiFi Manager library or Smart Config. This topics will be discussed in our long course(30 Hours). Stay tuned in my github I ll publish that course ASAP.

Analog to Digital Converter (ADC) :

Useful Links :
https://github.com/espressif/esp-idf/blob/v2.0/docs/api/peripherals/adc.rst
https://whatis.techtarget.com/definition/analog-to-digital-conversion-ADC
https://learn.sparkfun.com/tutorials/analog-to-digital-conversion
https://www.youtube.com/watch?v=RlKMJknsNpo
https://github.com/G6EJD/ESP32-ADC-Accuracy-Improvement-function Analog to Digital Converter (ADC) is a feature that converts an analog voltage on a pin to a digital number. By converting from the analog world to the digital world, we can begin to use electronics to interface to the analog world around us. ESP32 integrates two 12-bit SAR ("Successive Approximation Register") ADCs (Analog to Digital Converters) and supports measurements on 18 channels (analog enabled pins). Some of these pins can be used to build a programmable gain amplifier which is used for the measurement of small analog signals. The ADC on the ESP32 is a 12-bit meaning it has the ability to detect 4096 (2^12) discrete analog levels. We can use voltage divider with ADC to read analog voltage within a range. Higher the resolution more precisely it can read analog signal or we can say more small change it can detect. The ADC driver API currently only supports ADC1 (9 channels, attached to GPIOs 32-39). Taking an ADC reading involves configuring the ADC with the desired precision and attentuation settings, and then calling adc1_get_voltage() to read the channel. It is also possible to read the internal hall effect sensor via ADC1.
But before you apply any voltage to your ADC pin, you need to make sure that you’re using a device that only outputs 3.3V. Otherwise you might need to use a voltage divider.
Voltage Divider Equation is :

Some ADC functions for ESP32

analogReadResolution(12);// Sets the sample bits and read resolution, default is 12-bit (0 - 4095), range is 9 - 12 bits
analogSetWidth(12);// Sets the sample bits and read resolution, default is 12-bit (0 - 4095), range is 9 - 12 bits
                                        //  9-bit gives an ADC range of 0-511
                                        // 10-bit gives an ADC range of 0-1023
                                        // 11-bit gives an ADC range of 0-2047
                                        // 12-bit gives an ADC range of 0-4095
analogSetCycles(8);// Set number of cycles per sample, default is 8 and provides an optimal result, range is 1 - 255
analogSetSamples(1);// Set number of samples in the range, default is 1, it has an effect on sensitivity has been multiplied
analogSetClockDiv(1);// Set the divider for the ADC clock, default is 1, range is 1 - 255
analogSetAttenuation(ADC_11db);// Sets the input attenuation for ALL ADC inputs, default is ADC_11db, range is ADC_0db, ADC_2_5db, ADC_6db, ADC_11db
analogSetPinAttenuation(VP,ADC_11db);// Sets the input attenuation, default is ADC_11db, range is ADC_0db, ADC_2_5db, ADC_6db, ADC_11db
                                        // ADC_0db provides no attenuation so IN/OUT = 1 / 1 an input of 3 volts remains at 3 volts before ADC measurement
                                        // ADC_2_5db provides an attenuation so that IN/OUT = 1 / 1.34 an input of 3 volts is reduced to 2.238 volts before ADC measurement
                                        // ADC_6db provides an attenuation so that IN/OUT = 1 / 2 an input of 3 volts is reduced to 1.500 volts before ADC measurement
                                        // ADC_11db provides an attenuation so that IN/OUT = 1 / 3.6 an input of 3 volts is reduced to 0.833 volts before ADC measurement
  adcAttachPin(VP);                     // Attach a pin to ADC (also clears any other analog mode that could be on), returns TRUE/FALSE result 
  adcStart(VP);                         // Starts an ADC conversion on attached pin's bus
  adcBusy(VP);                          // Check if conversion on the pin's ADC bus is currently running, returns TRUE/FALSE result 
  adcEnd(VP);                           // Get the result of the conversion (will wait if it have not finished), returns 16-bit integer result
  */

Simple ADC read :

// This code will read ADC value and also measure time taken for conversion. 
void setup() {
  Serial.begin(115200);
}
void loop() {
   int timer  = micros();
   Serial.print(analogRead(13)); //GPIO 13
   Serial.print("  ");
   Serial.println(micros() - timer);
}

Read Voltage (Teacher will explain about code and Circuit):

/*
  
  ReadAnalogVoltage with ESP32
  Edited by Syed Razwanul Haque Nabil, www.github.com/Nabilphysics
  Original Code from Arduino Example
  Reads an analog input on pin 0, converts it to voltage, and prints the result to the Serial Monitor.
  Graphical representation is available using Serial Plotter (Tools > Serial Plotter menu).
  Attach the center pin of a potentiometer to pin A0, and the outside pins to +3.3V and ground.
  This example code is in the public domain.
 */

// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 115200 bits per second:
  Serial.begin(115200);
}

// the loop routine runs over and over again forever:
void loop() {
  int sensorValue = analogRead(13); //GPIO 13
  // Convert the analog reading (which goes from 0 - 4095) to a voltage (0 - 3.3V):
  float voltage = sensorValue * (3.3 / 4095.0);
  // print out the value you read:
  Serial.println(voltage);
}

Reading Weather Info Using openweather API :

API means Application Programming Interface. openweather provide its weather data using API call.
(Course Teacher Will Explain) See the instruction in this code
https://github.com/Nabilphysics/esp32_weather

MQTT :

See Details Here : http://mqtt.org/faq
MQTT stands for MQ Telemetry Transport. It is a publish/subscribe, extremely simple and lightweight messaging protocol, designed for constrained devices and low-bandwidth, high-latency or unreliable networks. The design principles are to minimise network bandwidth and device resource requirements whilst also attempting to ensure reliability and some degree of assurance of delivery. These principles also turn out to make the protocol ideal of the emerging “machine-to-machine” (M2M) or “Internet of Things” world of connected devices, and for mobile applications where bandwidth and battery power are at a premium.

Some important topics must need to understand about MQTT is MQTT Broker, Publish, Subscribe. Teacher will explain about this topics briefly. Some Links :
https://www.youtube.com/watch?v=2aHV2Fn0I60
https://www.youtube.com/watch?v=iRUI3y3all4

Project :

Control 3 LED Using Smartphone App using Adafruit IoT Platform
Step 1: Install Adafruit_mqtt Library in Arduino
Step 2: Create a free account in io.adafruit.com
Step 3: Copy and Paste Code to Arduino IDE

/***************************************************
  Adafruit MQTT Library ESP32 Example
  Edited by Syed Razwanul Haque (Nabil)
  CEO and Founder, www.cruxbd.com
  www.youtube.com/Nabilphysics
  This is a very basic code. I upload it just for an IDEA
  Three or more LED can be controlled by this code using adafruit IoT
  Works great with Adafruit's Huzzah ESP board & Feather
  ----> https://www.adafruit.com/product/2471
  ----> https://www.adafruit.com/products/2821
  Adafruit invests time and resources providing this open source code,
  please support Adafruit and open-source hardware by purchasing
  products from Adafruit!
  Written by Tony DiCola for Adafruit Industries.
  MIT license, all text above must be included in any redistribution
 ****************************************************/
#include "WiFi.h"
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"

// the on off button feed turns this LED on/off
#define LED1 1  //GPIO 1
#define LED2 2  //GPIO 2
#define LED3 3  //GPIO 3

/************************* WiFi Access Point *********************************/
#define WLAN_SSID   	"yourSSID"
#define WLAN_PASS   	"yourPassword"

/************************* Adafruit.io Setup *********************************/

#define AIO_SERVER  	"io.adafruit.com"
#define AIO_SERVERPORT  1883               	// use 8883 for SSL
// You have to replace it with your own User name and AIO key
#define AIO_USERNAME	"crux"
#define AIO_KEY     	"2dacfc5913343434b3ac09718cafe124"

/************ Global State (you don't need to change this!) ******************/

// Create an ESP8266 WiFiClient class to connect to the MQTT server.
WiFiClient client;
// or... use WiFiFlientSecure for SSL
//WiFiClientSecure client;

// Setup the MQTT client class by passing in the WiFi client and MQTT server and login details.
Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY);

/****************************** Feeds ***************************************/

// Notice MQTT paths for AIO follow the form: <username>/feeds/<feedname>
Adafruit_MQTT_Subscribe onoffbutton1 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff1");
Adafruit_MQTT_Subscribe onoffbutton2 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff2");
Adafruit_MQTT_Subscribe onoffbutton3 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff3");

/*************************** Sketch Code ************************************/

void MQTT_connect();

void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  digitalWrite(LED1,LOW);
  digitalWrite(LED2,LOW);
  digitalWrite(LED3,LOW);
 
  Serial.begin(115200);
  Serial.println("\n Starting");
  delay(10);
  Serial.println(F("MQTT Switch demo"));
  Serial.println(WiFi.SSID());
  // Connect to WiFi access point.
  Serial.println(); Serial.println();
  Serial.print("Connecting to ");
  Serial.println(WLAN_SSID);

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

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

  // Setup MQTT subscription for onoff & slider feed.
  mqtt.subscribe(&onoffbutton1);
  mqtt.subscribe(&onoffbutton2);
  mqtt.subscribe(&onoffbutton3);
}

uint32_t x=0;

void loop() {
 
  // Ensure the connection to the MQTT server is alive (this will make the first
  // connection and automatically reconnect when disconnected).  See the MQTT_connect
  // function definition further below.
  MQTT_connect();
 
  // this is our 'wait for incoming subscription packets' busy subloop
  // try to spend your time here

  Adafruit_MQTT_Subscribe *subscription;
  while ((subscription = mqtt.readSubscription(10000))) {
	// Check if its the onoff button feed
	if (subscription == &onoffbutton1) {
  	Serial.print(F("On-Off button: "));
  	Serial.println((char *)onoffbutton1.lastread);
 	 
  	if (strcmp((char *)onoffbutton1.lastread, "ON") == 0) {
    	digitalWrite(LED1, HIGH);
  	}
  	if (strcmp((char *)onoffbutton1.lastread, "OFF") == 0) {
    	digitalWrite(LED1, LOW);
  	}
	}

	if (subscription == &onoffbutton2) {
  	Serial.print(F("On-Off button 2: "));
  	Serial.println((char *)onoffbutton2.lastread);
 	 
  	if (strcmp((char *)onoffbutton2.lastread, "ON") == 0) {
    	digitalWrite(LED2, HIGH);
  	}
  	if (strcmp((char *)onoffbutton2.lastread, "OFF") == 0) {
    	digitalWrite(LED2, LOW);
  	}
	}
 	if (subscription == &onoffbutton3) {
  	Serial.print(F("On-Off button 3: "));
  	Serial.println((char *)onoffbutton3.lastread);
 	 
  	if (strcmp((char *)onoffbutton3.lastread, "ON") == 0) {
    	digitalWrite(LED3, HIGH);
  	}
  	if (strcmp((char *)onoffbutton3.lastread, "OFF") == 0) {
    	digitalWrite(LED3, LOW);
  	}
	}
    
//	// check if its the slider feed
//	if (subscription == &slider) {
//  	Serial.print(F("Slider: "));
//  	Serial.println((char *)slider.lastread);
//  	uint16_t sliderval = atoi((char *)slider.lastread);  // convert to a number
//  	analogWrite(PWMOUT, sliderval);
//	}
  }

  // ping the server to keep the mqtt connection alive
  if(! mqtt.ping()) {
	mqtt.disconnect();
  }

}

// Function to connect and reconnect as necessary to the MQTT server.
// Should be called in the loop function and it will take care if connecting.
void MQTT_connect() {
  int8_t ret;
  Serial.print("Connecting to MQTT... ");

  uint8_t retries = 3;
  while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected
   	Serial.println(mqtt.connectErrorString(ret));
   	Serial.println("Retrying MQTT connection in 5 seconds...");
	 
   	mqtt.disconnect();
   	delay(5000);  // wait 5 seconds
   	retries--;
   	if (retries == 0) {
     	// basically die and wait for WDT to reset me
     	while (1);
   	}
  }
  Serial.println("MQTT Connected!");
  }

Step 4: From [Adafruit_MQTT_Subscribe onoffbutton1 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff1");](Adafruit_MQTT_Subscribe onoffbutton1 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff1");) copy AIO Key and paste in this code
Step 5: Click Dashboard > Actions > New Dashboard and give it a name(e.g. IoT_Switch)
Step 6: Click + button > Toggle Switch and than create three switch (onoff1, onoff2, onoff3 ) make sure this name matches your code. e.g.

Adafruit_MQTT_Subscribe onoffbutton1 = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff1");

Step 7: Upload code to ESP32 and open Serial Monitor and check
Step 8: Attach 3 Resistor Series with 3 LED in GPIO Pin 1,2,3 and check its working when you changing Toggle Button in adafruit website.
Step 9: Download MQTT Dashboard app in your smartphone
Step 10: Give necessary info same as io.adafruit.com and check everything working properly.

ESP32 Bluetooth Example with Simple Project :

Built-in bluetooth is a great feature of ESP32. ESP32 support both Classic and BLE (Bluetooth Low Energy)
From ESP32 Datasheet we can see :
Bluetooth Link Controller
The link controller operates in three major states: standby, connection and sniff. It enables multiple connections, and other operations, such as inquiry, page, and secure simple-pairing, and therefore enables Piconet and Scatternet. Below are the features:

• Classic Bluetooth
– Device Discovery (inquiry, and inquiry scan)
– Connection establishment (page, and page scan)
– Multi-connections
– Asynchronous data reception and transmission
– Synchronous links (SCO/eSCO)
– Master/Slave Switch
– Adaptive Frequency Hopping and Channel assessment
– Broadcast encryption
– Authentication and encryption
– Secure Simple-Pairing
– Multi-point and scatternet management
– Sniff mode
– Connectionless Slave Broadcast (transmitter and receiver)
– Enhanced power control
– Ping
• Bluetooth Low Energy

– Advertising
– Scanning
– Simultaneous advertising and scanning
– Multiple connections
– Asynchronous data reception and transmission
– Adaptive Frequency Hopping and Channel assessment
– Connection parameter update
– Data Length Extension
– Link Layer Encryption
– LE Ping

Example Code : From Arduino IDE Open and Upload > File > Examples > BluetoothSerial > SerialToSerialBT
Teacher will explain code and above stuff.

//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Evandro Copercini - 2018
//
//This example creates a bridge between Serial and Classical Bluetooth (SPP)
//and also demonstrate that SerialBT have the same functionalities of a normal Serial

#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32test"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");
}

void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    Serial.write(SerialBT.read());
  }
  delay(20);
}

Bluetooth Project : Bluetooth Serial LED Control

Step 1 : Upload this code to ESP32

//This example code is in the Public Domain (or CC0 licensed, at your option.)
//By Evandro Copercini - 2018
//
//This example creates a bridge between Serial and Classical Bluetooth (SPP)
//and also demonstrate that SerialBT have the same functionalities of a normal Serial
// Edited for LED Control by Syed Razwanul Haque Nabil
// Founder & CEO, www.cruxbd.com , [email protected]
#include "BluetoothSerial.h"

#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif

BluetoothSerial SerialBT;
uint8_t ledPin = 2; //GPIO 2
char state;
void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32test by CRUX"); //Bluetooth device name
  Serial.println("The device started, now you can pair it with bluetooth!");
  pinMode(ledPin, OUTPUT);
}

void loop() {
 if (SerialBT.available() > 0) {
   state = SerialBT.read();
   if (state == 'H' || state == 'h') {
     digitalWrite(ledPin, HIGH);
   }
   if (state == 'L' || state == 'l') {
     digitalWrite(ledPin, LOW);
   }
 }
 Serial.println(state);
 delay(50);
}

Step 2: Download Android App Bluetooth Terminal Android App
Step 2: Open App than Scan and Connect with ESP32 . Now Send H or h for LED HIGH and l or L for LED LOW.
Step 3: Make some cool project like ESP32 Bluetooth Controlled Car/Robot

BLE will be discussed in Long Course

Motor Driver with ESP32 :

https://randomnerdtutorials.com/esp32-dc-motor-l298n-motor-driver-control-speed-direction/
Bangla Tutorial For Motor Control :
https://youtu.be/5ortz6qBApQ

⚠️ **GitHub.com Fallback** ⚠️