Guide to creating a simple signal generator with ESP32

Introduction

Signal generator with ESP32. A signal generator is a fundamental instrument in electronics, used to produce waveforms of various frequencies and amplitudes. These devices are crucial for the testing, calibration and development of electronic circuits and systems. They can generate different types of waves, each with specific practical applications. It produces electronic signals with specific waveforms, such as sine, square and triangular. These signals are used to test and calibrate electronic circuits, verify the frequency response of amplifiers and filters, and develop audio and communications devices.

Utilities and applications

  • Sine wave:
    • Description: a continuous and smooth waveform, characterized by a periodic and symmetrical variation.
    • Applications: testing of audio circuits, analysis of the frequency response of amplifiers and filters, generation of carrier signals in telecommunications systems.
  • Square wave:
    • Description: a waveform with rapid transitions between two voltage levels, creating a rectangular profile.
    • Applications: digital circuit testing, clock signal generation, stepper motor control, and digital signal simulation.
  • Triangular wave:
    • Description: a linear waveform that varies symmetrically between two voltage values, creating a sawtooth profile.
    • Applications: pulse width modulation (PWM), linear circuit testing, generation of scan signals for oscilloscopes and measuring instruments.

In addition to the three basic waveforms, more advanced generators can also produce more complex waveforms, such as pulses, sawtooth waves, and user-defined arbitrary signals.

Functionality and use

Waveform generators typically offer several features to control and modulate the output signal:

  • Frequency: determines the number of complete cycles of the waveform per second, measured in Hertz (Hz).
  • Amplitude: establishes the maximum or peak-to-peak height of the signal, measured in Volts (V).
  • Duty cycle: in the case of a square wave, it represents the percentage of the period in which the signal is at the high level.
  • Phase shift: allows you to synchronize or delay the output of one generator with respect to another.

Using a waveform generator is relatively simple. Typically, the user selects the desired waveform, sets the parameters of frequency, amplitude and other characteristics, and connects the generator output to the device or circuit to be tested or analyzed.

In this article we will create a simple signal generator using an ESP32.

As usual we will use the PlatformIO IDE to write the code.

The DacTone library for generating sine waves

The DacTone library is designed to facilitate the generation of audio signals using the digital-to-analog converters (DACs) built into the ESP32. This library allows you to create sine waveforms, simplifying the signal setup and production process. With DacTone, you can specify the signal frequency and start sine wave generation with just a few lines of code, making it ideal for audio applications and circuit testing.

Main features

  • Ease of use: simple API for generating sine waves.
  • Frequency configuration: possibility to set the desired frequency for the signal.
  • Integration with ESP32: takes advantage of the ESP32’s built-in DACs to produce high-quality analog signals.

The library limits the maximum frequency generated to 5 kHz so the generated sine wave can reach a maximum of 5 kHz.

Example of use

Here is an example of how to use the DacTone library to generate a sine wave:

#include "DacTone.h"

DacTone audio;

void setup() {
  Serial.begin(115200);
  audio.tone(1000); // Generates a 1000 Hz sine wave
}

void loop() {
  // The signal continues to be generated in the background
}

Square wave generation via LEDC on the ESP32

The ESP32 uses the LEDC (LED Control) module to generate square waves. The LEDC module is designed to control LEDs but is versatile and can be used to generate PWM (Pulse Width Modulation) signals. To create a square wave, the LEDC module rapidly alternates between two voltage levels, creating crisp, smooth transitions.

Configuration and code

  1. LEDC channel configuration:
    • Setup: configure the LEDC channel with a specific frequency and resolution. The resolution determines the accuracy of the duty cycle.
    • Pin attachment: assigns a GPIO pin to the LEDC channel output.
  2. Square wave generation:
    • Duty cycle: sets the duty cycle to 50% for a symmetrical square wave.

An example code can be seen below:

#define SQUARE_PIN 26 // Pin for square wave

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

  const int freq = 1000; // Frequency in Hz
  const int ledChannel = 0;
  const int resolution = 8;

  // Configure the LEDC channel
  ledcSetup(ledChannel, freq, resolution);
  // Attach the LEDC channel to the GPIO pin
  ledcAttachPin(SQUARE_PIN, ledChannel);
  // Set duty cycle to 50%
  ledcWrite(ledChannel, 128); // 50% duty cycle (risoluzione 8-bit: 128/255)
}

void loop() {
  // The square wave continues to be generated in the background
}

In this example, the LEDC channel is configured to generate a 1000 Hz square wave with 8-bit resolution. The duty cycle is set to 50%, ensuring that the square wave is symmetrical. This method allows you to create high frequency and precision signals with the ESP32.

Triangular wave generation with ESP32

The triangle wave is generated using the DAC built into the ESP32. The generateTriangleWave(int frequency) function creates a triangle wave by continuously adjusting the DAC’s output voltage.

The ESP32’s generateTriangleWave(int frequency) function is designed to generate a triangle wave using the built-in digital-to-analog converter (DAC). Start by disabling the LEDC module to ensure the DAC is working properly. The DAC on channel 1 is then enabled. The function calculates the number of samples needed per cycle to obtain good wave resolution.

During execution, the DAC voltage value is linearly increased and decreased to form the sawtooth profile characteristic of the triangular wave. This value varies between 0 and 255, creating a linear increasing and decreasing trend. A delay calculated based on the desired frequency ensures that the wave is generated at the correct speed. The result is a smooth and precise triangular wave, ideal for various electronic applications.

An example of a function can be seen below:

void generateTriangleWave(int frequency) {
  ledcWrite(0, 0); // Turn off LEDC for DAC operation
  dac_output_enable(DAC_CHANNEL_1); // Enable DAC

  const int samples = 500; // Increase the number of samples per cycle to improve resolution
  int value = 0;
  int increment = 256 / (samples / 2);

  for (int i = 0; i < samples; ++i) {
    dac_output_voltage(DAC_CHANNEL_1, value);
    value += increment;
    if (value >= 256) {
      increment = -increment;
      value = 256 + increment;
    } else if (value <= 0) {
      increment = -increment;
      value = 0 + increment;
    }

    delayMicroseconds((1000000 / frequency) / samples);
  }
}

What components do we need for our signal generator with ESP32?

The list of components is not particularly long:

  • a breadboard to connect the ESP32 NodeMCU to other components
  • some DuPont wires (male – male, male – female, female – female)
  • (optional) a digital frequency meter to precisely measure the frequency of the signal generated by the ESP32. This tool can help you verify that your generator is working properly.
  • (optional) an oscilloscope, essential for viewing the generated wave.
  • and, of course, an ESP32 NodeMCU!

The ESP32 model chosen for this project is that of the AZ-Delivery company.

Project implementation

The electrical diagram

Before creating the actual circuit let’s take a look at the pinout of the board:

Pinout of the ESP32
Pinout of the ESP32

As you can see from the following image, the electrical diagram (made with Fritzing) is truly elementary:

Wiring diagram of the signal generator with ESP32
Wiring diagram of the signal generator with ESP32

Let’s create the PlatformIO project

We have already seen the procedure for creating a PlatformIO project in the article How to create a project for NodeMCU ESP8266 with PlatformIO.

Although it refers to the ESP8266 board, the procedure is similar.
Simply, when choosing the platform, you will have to choose the AZ-Delivery ESP-32 Dev Kit C V4.

Do not install any of the libraries mentioned in the article.

Now edit the platformio.ini file to add these two lines:

monitor_speed = 115200
upload_speed = 921600

so that the file looks like this:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600

and add the DacTone library so the file looks like this:

[env:az-delivery-devkit-v4]
platform = espressif32
board = az-delivery-devkit-v4
framework = arduino
monitor_speed = 115200
upload_speed = 921600
lib_deps = https://github.com/bhall66/DacTone

You can download the project from the following link:

unzip it, take the main.cpp file and replace it with the one you have in the previously created project.

Now let’s see how the sketch works.

The sketch begins with including the necessary libraries:

#include <Arduino.h>
#include "DacTone.h"           // this is the DAC audio library

The GPIO 26 for the square wave, the DacTone type audio object, the frequency and the resolution of the PWM are then defined:

#define SQUARE_PIN 26 // Pin for square wave


//  triangular  GPIO25
//  sinusoidal  GPIO26
//  square      GPIO26


DacTone audio;                 // create audio object

const int freq = 1000; // Frequency in Hz
const int resolution = 8; // PWM resolution

We then encounter an enum that contains the possible waveforms that can be generated, the definition of the waveType variable with which we set the type of wave to be generated and the prototype of the generateTriangleWave function implemented below:

enum WaveType {
  SINE_WAVE,                   // max 5 kHz
  SQUARE_WAVE,            // max 300 kHz
  TRIANGLE_WAVE           // max 300 Hz
};

WaveType waveType = TRIANGLE_WAVE; // SINE_WAVE, SQUARE_WAVE, TRIANGLE_WAVE


void generateTriangleWave(int freq);

The setup function follows which activates the serial port and, if the selected wave is square or triangular, activates the block that manages the LEDC module:

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

  if(waveType != SINE_WAVE) {
    // Square wave configuration
    ledcSetup(0, freq, resolution);
    ledcAttachPin(SQUARE_PIN, 0);
    ledcWrite(0, 128); // 50% duty cycle
  }
}

The loop function manages the generation of the selected wave via the waveType variable:

void loop() {
    switch (waveType) {
      case SINE_WAVE:
        audio.tone(freq);
        break;
      case SQUARE_WAVE:
        break;
      case TRIANGLE_WAVE:
        generateTriangleWave(freq);
        break;
  }
}

Finally, the generateTriangleWave function generates the triangular wave as already explained in the paragraph “Triangular wave generation with ESP32”:

void generateTriangleWave(int frequency) {
  ledcWrite(0, 0); // Turn off LEDC for DAC operation
  dac_output_enable(DAC_CHANNEL_1); // Enable DAC

  const int samples = 500; // Increase the number of samples per cycle to improve resolution
  int value = 0;
  int increment = 256 / (samples / 2);

  for (int i = 0; i < samples; ++i) {
    dac_output_voltage(DAC_CHANNEL_1, value);
    value += increment;
    if (value >= 256) {
      increment = -increment;
      value = 256 + increment;
    } else if (value <= 0) {
      increment = -increment;
      value = 0 + increment;
    }

    delayMicroseconds((1000000 / frequency) / samples);
  }
}

Final thoughts

The signal generator project with ESP32 demonstrated how it is possible to create a versatile and powerful device for generating sine, square and triangle waves. Using the integrated DAC and LEDC module, we have created a generator that can meet various electronic testing and development needs. However, it is important to note some limitations inherent to this system.

Frequency limits

  • Sine wave: the maximum frequency achievable for the sine wave is 5 kHz. This limit is imposed by the DacTone library which does not generate higher frequency sinusoids.
  • Square wave: the ESP32 is capable of generating square waves up to a maximum frequency of 300 kHz. This capability takes advantage of the LEDC module, which allows you to create rapid transitions between two voltage levels, ideal for simulating digital and clock signals.
  • Triangular wave: the maximum frequency for the triangle wave is limited to 300 Hz. This limit is determined by how quickly the DAC can change voltage values, maintaining a stable, distortion-free waveform.

Limitations and improvements

For applications requiring significantly higher frequencies or greater precision, a DDS (Direct Digital Synthesis) signal generator driven by the ESP32 represents an advanced solution. DDS generators offer better bandwidth and precision, allowing you to generate signals at much higher frequencies and with greater stability. A DDS generator can be driven by the ESP32 to achieve higher output frequencies and greater signal fidelity. This approach combines the ease of programming of the ESP32 with the advanced signal generation capabilities of the DDS, ideal for professional and research applications.

Testing with the oscilloscope

Below are the tests done with the oscilloscope on the three types of waves:

Square wave
Square wave

Sine wave
Sine wave

Triangular wave
Triangular wave

In conclusion

Despite these limitations, the ESP32 signal generator is a great starting point for hobbyists, students and professionals looking to explore and experiment with electronic signals. The simplicity of setup and accessibility of the ESP32 make this design ideal for a wide range of educational and experimental applications.

With this project, you now have the tools necessary to create and test basic signals, understanding the limitations and potential of this microcontroller. If you want to expand the capabilities of your signal generator, consider integrating with additional hardware or using more advanced tools. Happy experimenting!

Newsletter

If you want to be informed about the release of new articles, subscribe to the newsletter. Before subscribing to the newsletter read the page Privacy Policy (UE)

If you want to unsubscribe from the newsletter, click on the link that you will find in the newsletter email.

Enter your name
Enter your email
0 0 votes
Article Rating
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
Scroll to Top