This tutorial created by Sudhu Tewari 2025
- from materials originally created by: Michael Shiloh and Judy Castro for Teach Me To Make
The tutorial will focus on getting you up and running quickly, so that you will understand the basic procedures for working with your ESP32 (specifically the Adafruit ESP32 Feather v2) and the Arduino IDE and can explore further on your own.
We will cover how to install Arduino on your laptop; how to understand, modify, and write Arduino programs; how to connect input devices and sensors to your ESP32 and read them from a program; and how to connect actuators (LEDs, motors, speakers) and control them from a program. Other topics will be covered as interest dictates and time permits.
What is Arduino anyway?
- Read about Arduino: https://www.arduino.cc/en/Guide/Introduction
What is ESP32?
- read up on ESP32 on Wikipedia
- Check out the ESP32 datasheet from espressif
-
Read: Adafruit's ESP32 Feather V2 Overview and Get Started Tutorial
- download as PDF
-
Install Arduino IDE
- Arduino software (IDE) runs on Windows, Mac OSX, and Linux.
- Please download and install the (free) Arduino software prior to the workshop from http://arduino.cc/en/Main/Software.
-
Install ESP32 drivers and Add ESP32 to Arduino IDE Boards Manager
- if you are not sure which USB-to-serial driver chip you have, install both!!
-
Connect ESP32 via USB cable
- Windows? Might see “New Hardware Discovered” and later might see “New Hardware Ready for Use”.
- Mac OS X? Might see “New Network Interface Found”. Click “Network Preferences…”, click “Apply”, and when it finishes, click “Close”. It doesn’t matter if the configuration fails.
- Linux? Nothing to do here
-
Open Arduino software (IDE)
-
Select Tools -> Board
- You have a Adafruit ESP32 Feather.
-
Select Tools -> Serial Port
- Windows? Chose the largest COM number
- No COMs? Raise your hand for help or visit troubleshooting
- Mac OS X? Choose dev/cu.usbserial******* or /dev/cu.SLAB_USDtoUART
- No dev/cu? Raise your hand for help or visit troubleshooting
- Linux? There is only one choice
- Windows? Chose the largest COM number
-
Open File->Examples->Basics->Blink
- Click “Upload”
- Look for errors in the bottom window of the program
- Errors? Raise your hand for help or visit troubleshooting
- Look for an amber LED blinking rapidly and a red LED blinking slowly on the other side of the USB connector
- No blinking? Raise your hand for help or visit troubleshooting
Copy the code below into a new Arduino sketch or download and open this example sketch: HelloWorld.ino
/*
Hello World
A "Hello, World!" program generally is a computer program that
outputs or displays the message "Hello, World!".
Such a program is very simple in most programming languages,
and is often used to illustrate the basic syntax of a programming language.
It is often the first program written by people learning to code
*/
void setup() {
//initialize serial communications at 9600 baud rate
Serial.begin(9600);
}
void loop(){
//send 'Hello, world!' over the serial port
Serial.println("Hello, world!");
//wait 100 milliseconds so we don't drive ourselves crazy
delay(1000);
}
The Serial commands allow Arduino to send a message to your laptop. In order to see this message you need to open the Serial Monitor by clicking on the magnifying glass near the top right corner.
a little code anatomy:
-
the setup() function is called when a sketch starts.
- Use it to initialize variables, pin modes, start using libraries, etc.
- The setup() function will only run once, after each powerup or reset of the Arduino board.
-
the loop() function does precisely what its name suggests, and loops consecutively through your list of instructions to control the Arduino.
- Arduino only executes one instruction at a time
More on specific functions and variables soon! Let's make something happen in the real world first.
How does the program (sketch) do this? (all described in the Blink tutorial)
/*
Blink
Turns an LED on for one second, then off for one second, repeatedly.
Most Arduino boards have an on-board LED you can control.
On the ESP32 Feather V2 it is attached to digital pin 13
This example code is modified from.
https://www.arduino.cc/en/Tutorial/BuiltInExamples/Blink
*/
int led = 13; // define a variable to hold the pin number of the internal LED
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin "led" as an output.
pinMode(led, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
Where is the LED that we're turning on and off???
some more code anatomy - info about an Arduino Sketch
- combine HelloWorld and Blink to make a program that shows it's working with physical and digital output.
In order to connect inputs or outputs to your microcontroller you need to know where the GPIO (general-purpose input/output) pins are!
Using a solderless Breadboard to connect your microcontroller to other things (LEDs, motors, speakers, sensors, etc.)
The Solderless Breadboard
Use the breadboard to add an external LED.
-
LEDs must always be used with resistors so they don’t burn out.
-
The resistor value can be anywhere from 100 ohm to 1k ohm.
- The lower the resistance, the brighter the light.
- Evil Mad Scientist explains it well here
-
Resistor Color Code!
-
-
LEDs are polarized
Here’s a picture showing how to connect the LED and resistor on the breadboard:
Here is another view of this circuit:
Use the Blink sketch: File -> Examples -> Basics -> Blink
Does the LED on the breadboard blink? (think about why)
Move LED to a different pin (e.g. pin 12). See if you can figure out how to do this on your own
Now the LED won’t blink until you change the program, since the program is only turning pin 13 on and off. Change the program to control pin 8.
If you changed the program to control only pin 8, then the built-in LED on pin 13 is no longer blinking. Can you change the program to make them both blink?
Are we limited to LEDs? No; we could replace the LED (and its resistor) with any other suitable device, with some considerations. We’ll learn more about this later.
-
Switches
-
digitalRead()
- Arduino->File->Examples->Basics->DigitalReadSerial
- Digital Read Serial tutorial
/* DigitalReadSerial Reads a digital input on pin A0, prints the result to the Serial Monitor This example code is in the public domain. http://www.arduino.cc/en/Tutorial/DigitalReadSerial */ // A0 has the pushbutton attached to it. Give it a name: int pushButton = A0; // 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(pushButton, INPUT); } // the loop routine runs over and over again forever: void loop() { // read the input pin: int buttonState = digitalRead(pushButton); // print out the state of the button: Serial.println(buttonState); delay(1); // delay in between reads for stability }
- Write an IF statement to turn the LED on when the button is pushed.
- Write an IF statement to toggle the LED when the button is pushed a certain number of times.
So far we’ve only used Arduino as an output device, to control something in the physical world (the LED). The other way of interfacing to the physical world is as an input device, using a sensor to get information about the physical world. We’ll start with a photoresistor, also called a light dependent resistor or LDR. It’s a resistor whose resistance depends on the light: the more light, the lower the resistance. (The resistor we used above with the LED is a fixed resistor.) The LDR indicates the amount of light by changing its resistance, but Arduino can not measure resistance. But, Arduino can measure voltage! Fortunately, we can easily convert a varying resistance to a varying voltage using a fixed resistor to create a voltage divider. This time the fixed resistor needs a larger resistance, so select a 10k ohm resistor and build the circuit below. You don’t need to remove the LED circuit as there should be room on your breadboard for both, and we’ll use the LED again later.
Open and upload this sketch: File->Examples->Basics->AnalogReadSerial
How do you know if anything is working? Arduino might be reading the sensor, but is it telling you anything?
Arduino is connected to your computer, so they can communicate - just like we did earlier with Hello World, but now your Ardunio is sending sensor DATA!
- this line:
cpp Serial.println(sensorValue);
allows Arduino to send a message to your laptop. In order to see this message you need to open the Serial Monitor by clicking on the magnifying glass near the top right corner. Read the Arduino AnalogRead tutorial to find out more. Also see File->Examples->Communication for more examples of other types of Serial communication).
Now that we've got sensor data coming in (as a range of values) what can we do with the data?
- ESP32 can measure varying voltage levels between 0 V and 3.3 V.
- The voltage measured is assigned to a value between 0 and 4095
- 0V corresponds to 0, and 3.3V corresponds to 4095.
- Any voltage between 0 V and 3.3 V will be given the corresponding value.
- We could do some math to calculate the voltage we're measuring:
File->Examples->Basics->AnalogReadVoltage
- we'll need to do some different math to re-configure the stock Arduino example for ESP32
- Arduino inputs range from 0-1024
- we'll need to do some different math to re-configure the stock Arduino example for ESP32
- The voltage measured is assigned to a value between 0 and 4095
That's nice, but what if we want to use the sensor data to control some kind of physical reaction (light, heat, motion) to the data?
Use an IF statement to turn your LED on and off according to the data coming from the LDR.
What other kinds of sensors are there?
IMPORTANT NOTE:
- When looking for sensors to use
- be aware that ESP32 is a 3.3V system.
- Many microcontrollers and sensors are made to run on 5v
- 5V WILL KILL YOUR ESP32!
- It is possible to convert 5V sensor outputs to 3.3v
- It is even easier to use 3.3V compatible sensors
- Try searching on Adafruit for “3.3v sensor”
Let's shift our focus, now, for a moment, to outputting a range of voltages. Then we'll put the input and output together to get real world input to control real world output.
- see some additional Sensor Resources here: https://learn.adafruit.com/pir-passive-infrared-proximity-motion-sensor/using-a-pir-w-arduino
- useful Arduino functions
-
If digitalWrite() can turn an LED on and off, and analogRead() can read a range of values, what would you guess analogWrite() might do?
- You guessed it!
-
analogWrite outputs PWM
- PWM = pulse width modulation
- this allows us, effectively, to output any voltage between minimum
- minimum = 0 volts = 0 in code
- maximum = 3.3 volts (ESP32) = 255 (@8bit resolution)
-
Arduino: analogWrite() only works on certain pins.
- see the Arduino Uno Board Pins reference for more info
-
ESP32 can output PWM on ANY pin.
-
ESP32 uses different functions to call PWM output
ledcAttachPin(GPIO, channel)
ledcWrite(channel, dutycycle)
-
16 PWM channels (0-15)
- variable PWM frequency (5000 is plenty for LEDs)
- variable duty cycle
- 8 bits = 0-255
- this is how you control the intensity of the output
/*
* PWM example code
*/
// the number of the LED pin
const int ledPin = 12; // 12 corresponds to GPIO16
// setting PWM properties
// variable PWM frequency (5000 is plenty for LEDs)
const int freq = 5000;
// 16 PWM channels available (0-15)
const int ledChannel = 0;
// 8 bits = 0-255
const int resolution = 8;
void setup(){
// configure LED PWM functionalitites
ledcSetup(ledChannel, freq, resolution);
// attach the channel to the GPIO to be controlled
ledcAttachPin(ledPin, ledChannel);
}
void loop(){
// increase the LED brightness
for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
// decrease the LED brightness
for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
// changing the LED brightness with PWM
ledcWrite(ledChannel, dutyCycle);
delay(15);
}
}
- Fade your LED according to the data from an LDR.
- This handy library allows us to use the analogWrite() function
provides an analogWrite polyfill for ESP32 using the LEDC functions
- https://github.com/ERROPiX/ESP32_AnalogWrite
- How To:
Installing Additional Arduino Libraries
- download
- locate .zip
- Sketch > Include Library > Add .ZIP Library
- Sketch > Include Library > ESP32 Analog Write
- download
- Fade an LED-
- Fade your LED with data from an LDR.
RGB LEDs are really handy for non-text, non-serial debug and they make really pretty colors!
-
Generally we use a slightly larger resistor (150 ohm) for the RED component and the same slightly smaller resistor values (100 ohm) for the GREEN and BLUE components.
-
for our circuit let's use:
- 470 ohm for RED
- color bands-> yellow, purple, black, black, brown
- 430 ohm for GREEN and BLUE
- color bands-> yellow, orange, black, black, brown
- We're using common CATHODE RGB LEDs
- 470 ohm for RED
-
RGB LED code example using analogWrite()
- RGB LED code example using ledcWrite()
Some other online information about RGB LEDs
- https://randomnerdtutorials.com/electronics-basics-how-do-rgb-leds-work/
- https://learn.adafruit.com/adafruit-arduino-lesson-3-rgb-leds/breadboard-layout
- https://howtomechatronics.com/tutorials/arduino/how-to-use-a-rgb-led-with-arduino/
-
Using delay() to control timing is probably one of the very first things you learned when experimenting with the Arduino. Timing with delay() is simple and straightforward, but it does cause problems down the road when you want to add additional functionality. The problem is that delay() is a "busy wait" that monopolizes the processor.
-
During a delay() call, you can’t respond to inputs, you can't process any data and you can’t change any outputs. Delay() ties up 100% of the processor. So, if any part of your code uses a delay(), everything else is dead in the water for the duration.
-
Blink Without Delay Tutorial on the official Arduino website
-
Another explanation of Blink Without Delay
-
In order to understand Blink Without Delay, it is helpful to first understand millis()
-
Excellent Adafruit Multitasking Tutorial (highly recommended)
-
A detailed explanation of Blink Without Delay.
-
Very detailed line-by-line explanation of Blink Without Delay, with links to explanations of related concepts every step of the way.
- PWM also works well to control the speed of a motor.
- However now we need to consider whether our motor is compatible with our GPIO output "levels".
- When used as outputs, two things must be considered: the voltage and the current. Our ESP32 can deliver 3.3v, and at most 12 or 28mA (250mA maximum for all channels - according to Adafruit).
- The voltage is determined by the source, but the current is determined by whatever we’re trying to control. In the case of LEDs, they only need 20 mA or less. The motor we have might take more than 40 mA. In the worst case, when it’s stalled, it might want a 200 mA.
- The important thing to realize is that the microcontroller does not have the ability to limit this current. It will try to deliver whatever is asked of it, even if it overheats and damages itself.
- If we want to control a device that might take more than 40 mA, we have to use an intermediary.
Can we hook it up like this, with the motor wired directly to the microcontroller??
- Yes!!
-
Will it probably damage the microcontroller?
- YES! aka 爆炸!
-
Should we use an intermediary to control the high current?
- YES!!!!!
The transistor is like a bicycle gear: you control it with a small amount of current, and it in turn can control a lot more current. The transistor also allows us to use a higher voltage than the 3.3V the ESP32 can deliver.
Use a transistor to control a higher current for a motor.
- There are hundreds of transisors that will work for this application.
- never assume the pinout of a transistor or IC.
- ALWAYS look up the pinout before applying power.
- or else 爆炸
- ALWAYS look up the pinout before applying power.
Here's the pinout for a TIP 120 Darlington transistor, rated for up to 60V and 5A
You can test this with any of the code above for driving an LED:
- just send an analogWrite to pin 12.
It's important to note that we are now using USB as the power source for the motor.
- For small motors this is probably okay.
- For larger motors that require more current (>500ma), your computer will probably complain about a device using too much power and will disconnect the USB.
- It is a much better practice to get in the habit of using a separate power source for the motor.
- The H-Bridge is a circuit that allows us to control the direction of a motor as well as its speed.
- The L298N is a popular H-Bridge motor driver that can be used with the Arduino.
- The L298N has 2 H-Bridges, which means it can control 2 motors.
- The L298N can handle up to 2A per channel, and up to 35V.
notes:
-
on many L298N modules, the power input is labeled 12v
- but it can handle up to 35v
- and it can run as low as 4.5v
-
since motors take a significant amount of current, we'll use a separate power source for the motor(s)
- this external source can be a power supply or a battery pack (or USB for SMALL motors)
- the microcontroller will send data to the Hbridge to switch on/off the larger voltage/current
-
We can power the motor(s) with an external power supply
- match the power supply voltage to the motor voltage
- you'll also need to consider the motor's current draw and provide sufficient amperage
- the manufacturer's specification should provide this information
When using 5V or less for motor voltage (muscles) or less we must also provide 5V to the L298 IC (brains).
- we can get 5v from the USB pin on our ESP32V2
- note: this only works if you have USB connected to a USB power source (computer or charging brick)
- When using more than 7V the L298N can get the 5v it needs to operate from an onboard voltage regulator
- most voltage regulators need about 2v more than their output voltage
- in this special case we can --probably-- use the L298's onboard voltage regulator to provide 5V for our ESP32
- most voltage regulators need about 2v more than their output voltage
ESP32V2_L298N_Simple
Hbridge example code:
- FIRST -> install the L298N library by Andrea Lombardo
- ESP32V2_L298N_Simple.ino)
NeoPixels are individually addressable (WS2812B) RGB LEDs that can be chained together to create a string of lights. They are very bright and can be used to create a wide range of colors. They are available in a variety of form factors, including strips, rings, and matrices. They are typically controlled by a single data line and can be powered by 5V. That said, our Arduino can only supply a limited amount of current, so we need to be careful when powering neoPixels.
If you want to understand addressable LEDs (aka neoPixels), read this guide!!
We can start with a simple test to get familiar with the NeoPixel library. When we're ready to control larger numbers of NeoPixels, we'll need to consider power requirements and how to power them safely.
NeoPixel example code:
- FIRST -> install the Adafruit NeoPixel library
- 12a_NeoPixel_simpleTest.ino
If you are using more than 8 neopixels you must power them with an external power supply!
- or else 爆炸
Also, it is very worthwhile to follow the Best Practices given in the Adafruit NeoPixel Uberguide
Servo motors are a simple and easy way to add motion to your Arduino project.
- They 'know' their own position which makes them easy to control
- they are not that powerful (although larger, more power servos do exist)
Servo motors have three wires: power, ground, and signal. The power wire is typically red, the ground wire is typically black or brown, and the signal wire is typically yellow, orange or white.
Since the ESP32 can supply limited current at only 3.3V, and servos draw considerable power, we will connect servo power to the USB pin of the ESP32 to use 5v from our computer.
We -could- connect servo power to the BAT (battery) pin of the ESP32, but this is ONLY appropriate for SMALL servos and will draw down the battery power quickly.
We can also connect servo power to a separate external power source (as long as we connect all of the grounds (ESP32, servo, and external power). In this example, we just connect ESP32 ground to servo ground.
The servo signal pin connects to any available GPIO pins on the ESP32 (in this example, we use pin 14).
The 'standard' Arduino servo library doesn't work with ESP32 so we'll need to install another library: https://docs.arduino.cc/libraries/esp32servo/
- Use library manager in Arduino IDE
- or download the library .zip file and unzip into your Arduino/libraries folder
Servo example code:
- ESP32_Servo_Sweep.ino - same as Examples -> Servo -> Sweep
Note: Min and Max angles Different servos require different pulse widths to vary servo angle, but the range is an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos sweep 180 degrees, so the lowest number in the published range for a particular servo represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top of the range represents 180 degrees. So for example, if the range is 1000us to 2000us, 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 1800 degrees. If you find that your servo does not move from 0 to 180 degrees, adjust the min and max values in the code to match your needs. In the example above this adjustment has been made to match the micro servos in your kits.
Let's Go Crazy (Let's Get Nuts)*
Let's combine circuits and code for the DC motor, servo, and Neopixel!! In this example EVERYTHING is running on 5V from from power supply and we're using the L298 terminal block as a way to connect ALL the power and grounds leads to one another.
example code:
https://github.com/roopa-ramanujam/ESP32-web-api-example
- Arduino Language Reference