Community project

Using A Waveshare Esp32-s3-eth Board I Want To

ESP32
Photo of Using A Waveshare Esp32-s3-eth Board I Want To
Generated with AI

TTB Automation

Published September 23, 2026

This guide builds a pulse and encoder monitoring system using the Waveshare ESP32-S3-ETH board with isolated gate-driver control. The project captures incoming pulse widths and encoder edges over Ethernet, making it suitable for industrial automation, motor control feedback, or precision timing applications where electrical isolation is critical.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for integrating the 24V buck converter, HCPL-3150 optocoupler isolation, and screw terminal connections. Firmware is included to handle real-time pulse capture, encoder counting, and TCP command processing on port 5000.

Wiring diagram

Wiring diagram for Using A Waveshare Esp32-s3-eth Board I Want To

Gather all the parts

QtyComponent
1

2-position screw terminal for pulse input

2-position, 3.5 mm

A small two-wire terminal where the external 3.3 V pulse source connects.

1

2-position screw terminal for encoder channel A

2-position, 3.5 mm

A small two-wire terminal where encoder channel A and its ground return connect.

1

2-position screw terminal for output pulse

2-position, 3.5 mm

A two-wire connector that carries the 3.3 V output pulse to the receiving equipment.

1

2-position screw terminal for DC supply input

2-position, 3.5 mm

A two-position terminal where the higher-voltage DC supply enters the LM2596 converter.

1

24v Buck Converter

adjust to 5.0 V

LM2596-based adjustable step-down buck converter module. Commonly used to regulate a higher battery rail, such as a 2S 18650 pack, down to 5V for Arduino logic. It is a regulator, not a charger or battery protection board.

1

HCPL-3150 isolated gate-driver optocoupler

DIP-8

An optocoupler gate driver that turns the GPIO41 pulse into an electrically isolated output pulse for external gate-drive equipment.

1

Resistor

180 Ω

Through-hole resistor (current-limiting in series with an LED)

1

2-position terminal for isolated 15 V gate-driver supply

2-position, 3.5 mm

A two-wire terminal that brings in the separate 15 V supply required by the HCPL-3150 output side.

1

0.1 µF ceramic bypass capacitor

0.1 µF, 50 V ceramic

A small capacitor fitted beside the HCPL-3150 to keep its 15 V gate-driver supply stable during output switching.

Assemble it in 9 steps

1. Keep everything unpowered while wiring

Place the Waveshare ESP32-S3-ETH board, LM2596 converter, HCPL-3150, 180 Ω resistor, and terminals where you can reach them. Leave USB and both DC supplies disconnected while making the connections.

  • Put the HCPL-3150 across the middle gap of a breadboard so its two sides are separated.
  • Do not move wires while power is connected; a loose wire can short power and damage the board or converter.

2. Connect the incoming pulse

Connect the external pulse source signal wire to the pulse-input terminal, then connect its SIG wire to GPIO40 (signal). Connect the source return wire to terminal GND and a board GND pin (ground) so both devices share the same electrical reference.

  • GPIO40 accepts a 3.3 V logic signal: low is near 0 V and high is near 3.3 V.
  • Do not connect a 5 V pulse directly to GPIO40 — it can damage the ESP32-S3. Use a level shifter or divider that makes the GPIO40 signal no higher than 3.3 V.

3. Connect encoder channel A

Connect the encoder channel-A signal wire to terminal A, then connect that terminal to GPIO38 (signal). Connect the encoder ground or signal-return wire to terminal GND and a board GND pin (ground).

  • This build counts rising edges on channel A only while the GPIO40 input pulse is high.
  • Do not connect a 5 V encoder output directly to GPIO38 — reduce it to a 3.3 V logic signal first, or use a 3.3 V encoder.

4. Wire the HCPL-3150 input side

Connect GPIO41 to one lead of the 180 Ω resistor (signal). Connect the resistor’s other lead to HCPL-3150 pin 1, the anode (signal). Connect HCPL-3150 pin 2, the cathode, to an ESP32 GND pin (ground).

  • The resistor limits the LED current inside the HCPL-3150. The dot or notch on the chip identifies pin 1.
  • Do not connect the HCPL-3150 input LED directly to GPIO41 — without the resistor the GPIO pin or optocoupler can be damaged.

5. Wire the isolated HCPL-3150 output side

Connect the isolated 15 V supply terminal +15V to HCPL-3150 pin 8, VCC (power). Connect the isolated supply 0V to pin 5, VEE (ground). Fit the 0.1 µF capacitor directly between pins 8 and 5 (power smoothing). Connect HCPL-3150 pin 6, VO, to output-terminal OUT (output signal), and connect output-terminal GND to pin 5, VEE (output return).

  • Keep the capacitor leads short and close to pins 8 and 5; it helps the driver make clean output pulses.
  • The HCPL-3150 output side requires a separate 15 V supply. Do not connect its VCC pin to the ESP32 3.3 V or 5 V rails. Keep the isolated 15 V 0V wire separate from ESP32 GND unless the external equipment design specifically requires them joined.

6. Wire the LM2596 input

Connect the external DC supply positive wire to the DC-supply terminal VIN+ (power), then connect that terminal to LM2596 VIN+ (power). Connect the external supply negative wire to terminal GND (ground), then connect it to LM2596 VIN- (ground).

  • The supply voltage must be higher than 5 V for this step-down converter to regulate correctly.
  • Observe positive and negative markings carefully — reversing the input supply can destroy the LM2596 module and board. Do not connect mains electricity directly to this terminal.

7. Set the converter to exactly 5 V

Power only the LM2596 input from the external DC supply. Use a multimeter across LM2596 VOUT+ and VOUT- and turn the adjustment screw until it reads 5.0 V. Disconnect the external DC supply again after adjustment.

  • Set and measure the output before connecting it to the ESP32 board.
  • Do not connect the LM2596 output to VSYS until the meter shows 5.0 V. A higher setting can damage the board.

8. Feed the board from the adjusted converter

Connect LM2596 VOUT+ to the board header pin labelled VSYS, physical pin 39 (5 V power). Connect LM2596 VOUT- to any board GND pin (ground).

  • The LM2596 output ground also provides the shared ground for the board and the external input signal equipment.
  • Do not connect USB power and the LM2596 VSYS supply at the same time unless your exact board revision documentation explicitly permits it.

9. Attach Ethernet and power the two supplies

Plug a network cable into the RJ45 socket. Apply the adjusted board supply and the separate isolated 15 V HCPL-3150 supply. The board requests a network address automatically and listens for TCP commands on port 5000.

  • Send LIMITS 100 2000 as an example to allow pulses from 100 to 2,000 microseconds; replace those numbers with your allowed range.
  • Make sure every external signal connected directly to GPIO40 or GPIO38 shares ESP32 GND. The HCPL-3150 output-side 0V is deliberately separate for isolation.

Review all connections

1. Connections between "pulse_input_terminal" and "ESP32"

Functionpulse_input_terminalESP32
digitalSIGGPIO 40
groundGNDGND

2. Connections between "encoder_input_terminal" and "ESP32"

Functionencoder_input_terminalESP32
digitalAGPIO 38
groundGNDGND

3. Connections between "vsys_power_terminal" and "ESP32"

Functionvsys_power_terminalESP32
powerVIN+24v Buck Converter VIN+EXT
groundGND24v Buck Converter VIN-EXT

4. Connections between "lm2596_converter" and "ESP32"

Functionlm2596_converterESP32
powerVOUT+VIN
groundVOUT-GND

5. Connections between "output_terminal" and "ESP32"

Functionoutput_terminalESP32
digitalOUTHCPL-3150 isolated gate-driver optocoupler VOEXT
groundGNDHCPL-3150 isolated gate-driver optocoupler VEEEXT

6. Connections between "hcpl3150_input_resistor" and "ESP32"

Functionhcpl3150_input_resistorESP32
digitalP1GPIO 41
digitalP2HCPL-3150 isolated gate-driver optocoupler ANODEEXT

7. Connections between "hcpl3150_driver" and "ESP32"

Functionhcpl3150_driverESP32
groundCATHODEGND

8. Connections between "hcpl3150_supply_terminal" and "ESP32"

Functionhcpl3150_supply_terminalESP32
power+15VHCPL-3150 isolated gate-driver optocoupler VCCEXT
ground0VHCPL-3150 isolated gate-driver optocoupler VEEEXT

9. Connections between "hcpl3150_bypass_capacitor" and "ESP32"

Functionhcpl3150_bypass_capacitorESP32
powerP1HCPL-3150 isolated gate-driver optocoupler VCCEXT
groundP2HCPL-3150 isolated gate-driver optocoupler VEEEXT

Deploy the firmware

#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>

void IRAM_ATTR onEncoderARising();
void IRAM_ATTR onPulseChange();
void sendLine(EthernetClient &client, const String &line);
void processCommand(String command, EthernetClient &client);
void serviceTcp();
void reportCapturedPulse();

constexpr uint8_t PULSE_INPUT_PIN = 40;
constexpr uint8_t ENCODER_A_PIN = 38;
constexpr uint8_t OUTPUT_PULSE_PIN = 41;
constexpr uint8_t ETH_MISO_PIN = 12;
constexpr uint8_t ETH_MOSI_PIN = 11;
constexpr uint8_t ETH_SCK_PIN = 13;
constexpr uint8_t ETH_CS_PIN = 14;
constexpr uint16_t TCP_PORT = 5000;
constexpr uint32_t OUTPUT_PULSE_WIDTH_US = 1000;

byte macAddress[] = {0x02, 0x53, 0x45, 0x54, 0x48, 0x01};
EthernetServer server(TCP_PORT);

volatile uint32_t encoderEdgeCount = 0;
volatile uint32_t pulseStartUs = 0;
volatile uint32_t pulseStartEncoderCount = 0;
volatile uint32_t capturedPulseWidthUs = 0;
volatile uint32_t capturedEncoderEdges = 0;
volatile bool pulseCaptured = false;
volatile bool pulseHigh = false;

uint32_t minWidthUs = 0;
uint32_t maxWidthUs = 0;
bool limitsConfigured = false;
bool outputPulseActive = false;
uint32_t outputPulseStartedUs = 0;

void IRAM_ATTR onEncoderARising() { encoderEdgeCount++; }

void IRAM_ATTR onPulseChange() {
  const uint32_t now = micros();
  if (digitalRead(PULSE_INPUT_PIN) == HIGH) {
    pulseStartUs = now;
    pulseStartEncoderCount = encoderEdgeCount;
    pulseHigh = true;
  } else if (pulseHigh) {
    capturedPulseWidthUs = now - pulseStartUs;
    capturedEncoderEdges = encoderEdgeCount - pulseStartEncoderCount;
    pulseHigh = false;
    pulseCaptured = true;
  }
}

void sendLine(EthernetClient &client, const String &line) {
  client.print(line);
  client.print("\r\n");
}

void processCommand(String command, EthernetClient &client) {
  command.trim();
  command.toUpperCase();
  if (command == "GET LIMITS") {
    sendLine(client, limitsConfigured ? "LIMITS " + String(minWidthUs) + " " + String(maxWidthUs) : "LIMITS NOT_SET");
    return;
  }
  if (command == "GET STATUS") {
    sendLine(client, String("STATUS LIMITS=") + (limitsConfigured ? "SET" : "NOT_SET") + " MIN_US=" + String(minWidthUs) + " MAX_US=" + String(maxWidthUs));
    return;
  }
  unsigned long requestedMin = 0;
  unsigned long requestedMax = 0;
  if (sscanf(command.c_str(), "LIMITS %lu %lu", &requestedMin, &requestedMax) == 2) {
    if (requestedMin > 0 && requestedMin <= requestedMax) {
      minWidthUs = requestedMin;
      maxWidthUs = requestedMax;
      limitsConfigured = true;
      sendLine(client, "OK LIMITS " + String(minWidthUs) + " " + String(maxWidthUs));
    } else {
      sendLine(client, "ERROR limits must be positive and MIN_US must not exceed MAX_US");
    }
    return;
  }
  sendLine(client, "ERROR commands: LIMITS <min_us> <max_us>, GET LIMITS, GET STATUS");
}

void serviceTcp() {
  EthernetClient client = server.available();
  if (!client) return;
  client.setTimeout(1000);
  sendLine(client, "PULSE_GATE READY");
  processCommand(client.readStringUntil('\n'), client);
  delay(2);
  client.stop();
}

void reportCapturedPulse() {
  uint32_t widthUs;
  uint32_t encoderEdges;
  noInterrupts();
  widthUs = capturedPulseWidthUs;
  encoderEdges = capturedEncoderEdges;
  pulseCaptured = false;
  interrupts();
  const bool accepted = limitsConfigured && widthUs >= minWidthUs && widthUs <= maxWidthUs;
  if (accepted && !outputPulseActive) {
    digitalWrite(OUTPUT_PULSE_PIN, HIGH);
    outputPulseStartedUs = micros();
    outputPulseActive = true;
  }
  Serial.printf("PULSE width_us=%lu encoder_a_edges=%lu result=%s\n", static_cast<unsigned long>(widthUs), static_cast<unsigned long>(encoderEdges), accepted ? "ACCEPTED" : "REJECTED");
}

void setup() {
  Serial.begin(115200);
  pinMode(PULSE_INPUT_PIN, INPUT_PULLDOWN);
  pinMode(ENCODER_A_PIN, INPUT_PULLDOWN);
  pinMode(OUTPUT_PULSE_PIN, OUTPUT);
  digitalWrite(OUTPUT_PULSE_PIN, LOW);
  attachInterrupt(digitalPinToInterrupt(ENCODER_A_PIN), onEncoderARising, RISING);
  attachInterrupt(digitalPinToInterrupt(PULSE_INPUT_PIN), onPulseChange, CHANGE);
  SPI.begin(ETH_SCK_PIN, ETH_MISO_PIN, ETH_MOSI_PIN, ETH_CS_PIN);
  Ethernet.init(ETH_CS_PIN);
  Ethernet.begin(macAddress);
  server.begin();
  Serial.print("TCP pulse gate listening on port ");
  Serial.println(TCP_PORT);
  Serial.print("Ethernet IP: ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  serviceTcp();
  if (pulseCaptured) reportCapturedPulse();
  if (outputPulseActive && static_cast<uint32_t>(micros() - outputPulseStartedUs) >= OUTPUT_PULSE_WIDTH_US) {
    digitalWrite(OUTPUT_PULSE_PIN, LOW);
    outputPulseActive = false;
  }
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik