← All projects
Basics Intermediate

Logic Analyzer: See What Really Happens on the Pins

A cheap 8-channel analyzer shows GPIO signals over time. We compare a software square wave made with digitalWrite and hardware PWM, and learn to measure period and duty.

Difficulty
Intermediate
Time
30 min
Category
Basics
Updated

About the project

A multimeter shows the average voltage, and when a signal changes thousands of times a second it is helpless. A logic analyzer records the state of several digital lines over time and draws them as a timing diagram. It is the best way to understand why I2C does not work, how long your delay(1) really takes, or whether PWM is configured correctly.

A popular cheap option is an 8-channel, 24 MHz Saleae clone that works with the free PulseView software (the sigrok project).

What you need

  • ESP32 DevKit
  • 8-channel / 24 MHz USB logic analyzer
  • PulseView

Wiring

Analyzer channel ESP32 What we see
D0 GPIO25 a software square wave
D1 GPIO26 a slower pattern
D2 GPIO27 hardware PWM, 1 kHz, 25 %
GND GND a common ground is mandatory

Without the GND connection the analyzer shows noise. Cheap analyzer inputs are rated for 0–5 V, so the ESP32's 3.3 V is safe for them.

What the sketch generates

  • D0: every pass of loop() toggles the pin and then calls delay(1). That gives a square wave with a ~2 ms period, but it "breathes": each pass takes a little longer than 1 ms.
  • D1: bit 2 of the counter n, a signal four times slower than D0.
  • D2: the LEDC hardware produces 1 kHz at 64/256 = 25 % duty. It does not depend on loop() and stays perfectly steady even while the program is busy with something else.

Code

// Three signals for a logic analyzer
void setup() {
  pinMode(25, OUTPUT);
  pinMode(26, OUTPUT);
  ledcAttach(27, 1000, 8);   // hardware PWM: 1 kHz
  ledcWrite(27, 64);         // 25 % duty
}

void loop() {
  static uint8_t n = 0;
  digitalWrite(25, !digitalRead(25));   // toggles every ~1 ms → 2 ms period
  digitalWrite(26, (n >> 2) & 1);       // 4× slower
  n++;
  delay(1);
}

Reading the diagram

  • Period is the time between two rising edges. Frequency = 1 / period.
  • Duty is the share of the period the line spends HIGH.
  • In PulseView, add a protocol decoder (I2C, UART, SPI, 1-Wire): it labels the bytes right above the signal.
  • The sample rate should be at least 4–10 times the signal frequency. 400 kHz I2C needs 4 MHz or more.

Things to try

  • Change delay(1) to delayMicroseconds(100) and watch D0 squeeze together.
  • Change ledcWrite(27, 64) to 192: the duty becomes 75 %.
  • In the simulator, change the "Time window" in the analyzer properties (2 ms … 5 s) to zoom in on the signal or see a long stretch.