Getting Started with the ESP32

What are Microcontrollers?

A “normal” computer (a laptop or desktop computer) typically runs an operating system (typically Windows, Mac OS, or a derivative of Linux) that can then run various programs to accomplish any number of tasks (web browsing, video editing, programming, etc.).

A cell phone is generally sort of a hybrid between a desktop computer and a microcontroller. It has aspects of both; it’s smaller and often less powerful than desktop systems, but it still runs an operating system and can perform a large variety of tasks (texting, watching videos, taking pictures, browsing social media sites, and – yes – can even make phone calls. They use less power and are smaller to run off a battery for a while, and fit comfortably into your pocket.

Microcontrollers are essentially small, bare-bones computers that were explicitly designed to replace hardware logic to accomplish a focused series of tasks. They can use a fraction of the power that a cell phone or a desktop computer uses and are physically very small by comparison. However, their operating system is far more limited and specialized to specific tasks, and they generally have far less processing power, memory, and storage space than their larger brothers.

Like desktop computers, phones, and other types of computing mechanisms, there are many different brands and types of microcontrollers – all with different features, processor speeds, capabilities, and costs.

Examples of Common Microcontroller Brands

  • Espressif (ESP32 and variations)
  • Arduino (Uno, Nano)
  • Raspberry Pi (Pico)
  • Teensy
  • Etc.

Why Use a Microcontroller?

Ease of Use

Originally, circuits had to be designed using hardware logic. This meant learning the ins and outs of hardware components, designing complicated circuit schematics, soldering, and desoldering components until your circuit worked as you wanted it to. If you wanted to make a change, you had to rework the logic of the circuit. This created a massive barrier to entry for newcomers, and even if you were an expert, it was a pain to change anything about an existing circuit.

Using computers, you can abstract all that hardware logic into a few wires connected to a small computer. The computer reads the information and decides what to do with that information via a few lines of code, then outputs the proper commands to the effector. This is a far more straightforward process to follow, and if you need to change the logic, you change a few characters in the code.

Cost Effective

Computers and phones aren’t cheap, but microcontrollers are! You can purchase some microcontrollers for less than a cup of coffee!

Power Usage and Size

Microcontrollers require very little energy by comparison. So they are great for battery-operating devices. They are also incredibly small and can be embedded in various small designs for portability.

Firmware and Coding Languages

What is firmware?

Firmware is basically a simplified operating system that handles the low-level functions of the microcontroller and the software’s interaction with them. Firmware is less robust than desktop or phone operating systems and is generally reasonably specific. In the case of microcontrollers, there are usually several different options for the various brands and models.

To “speak” to a computer to provide it with instructions, we need to write our instructions in languages a computer can understand. There are quite a few of these coding languages, which generally include two different types: compiled and interpreted.

Compiled Languages

Writing code in a compiled language is generally more complicated and harder to learn, but that code will run faster than an interpreted language (often hundreds of times faster). However, computers are already relatively fast, and this speed increase often provides no real benefit as an interpreted language can already run fast enough for most applications. So, the added complexity of using a compiled language can be a hassle.

Examples include C, C++, Assembly, FORTRAN, and RUST

Interpreted Languages

Interpreted languages are more modern. They are more easily read and written by humans but are read and run more slowly by computers than their compiled counterparts.

Examples include MicroPython, Lua, Javascript, BASIC, and Ruby

Firmware Selection

Depending on which type of language you select, you must deploy compatible firmware to the microcontroller to use that language. This step only generally needs to be done once unless you decide to change languages or you encounter a serious error with the controller and need to start over from scratch.

Deploying and Running Code

Once the appropriate firmware has been deployed to the microcontroller, you may begin to send code to the controller to make it do something useful.

The general workflow from this point on is as follows:

  1. Connect the microcontroller to a desktop computer or laptop using a USB cable.
  2. Write or modify the desired code using a desktop or laptop computer editor.
  3. Run the appropriate commands to deploy the code to the microcontroller.
  4. Reboot the microcontroller to force it to run the new code.

Once your code has been deployed to the microcontroller, you may leave it connected to the computer or disconnect it from it and power it via another source (an appropriate battery or a power outlet, for example). As soon as the board receives the appropriate power, it will turn on and immediately run your code – only stopping when your code stops or when it loses power.

Interacting with Sensors and Outputs

To make the microcontroller do something useful, we will likely want to connect it to one or more sensors and one or more outputs. This will allow the microcontroller to detect a change in what you want it to and then respond to it as you’ve instructed.

We must utilize the appropriate pins to connect sensors and outputs to the controller. This process is quite similar for almost all sensors and outputs, and the workflow is repeatable:

  1. Determine which sensors and outputs are required for your project.
  2. Connect the sensors to the appropriate data pins on your controller.
  3. Configure the connected pins in your code.
  4. Read and write values from the configured pins to detect and affect.

Python Code to make the onboard LED Flash

# Import the time library
import time

# Import the Pin object from the machine library
from machine import Pin

# Configure the pin for the onboard LED light and set it up as an output
led = Pin(2, Pin.OUT)

# Begin running an infinite loop of code
while True:

    # Turn the LED on by activating GPIO pin 2

    # Sleep for 1 second

    # Turn the LED off by deactivating GPIO pin 2

    # Sleep for 1 second

Python code to make the LED fade

When you surround text with 3 quotation marks on each side, it becomes a long comment that can span
 multiple lines. You can type whatever you want within this block of text, but the program will ignore
 it when it runs.

The following program will fade the onboard LED on, then fade it back to off - repeating forever.

# Import the time library
import time

# Import the Pin and PWM objects from the machine library
from machine import Pin, PWM

# Configure the pin for the onboard LED light for pulse-width modulation
led = PWM(Pin(2), duty=0)

# Set up a variable for sleep duration
sleep_duration = 1000

# Start an infinite loop of code
while True:

    # Create a loop that will run 256 times, starting from 0 and incrementing the "duty" variable by one
    #  every time the loop runs its code. This "for loop" will fade the light on
    for duty in range(1, 1023):

        # Set the duty cycle for the LED light to the current duty variable's value
        # Pause the program for the number of microseconds specified in the "sleep_duration" variable

    # Create a loop that will run 256 times, starting from -256 and incrementing the "duty" variable by one
    #  every time the loop runs its code. This "for loop" will fade the light off
    for duty in range(-1023, 1):

        # Set the duty cycle for the LED light to the absolute value of the current duty variable's value

        # Pause the program for the number of microseconds specified in the "sleep_duration" variable
    # Sleep for one second before repeating the process
    #   Note that the sleep function here is different from 'time.sleep_us()'
    #   The 'time.sleep()' function specifies a sleep time in seconds instead of microseconds.