php-esp32 · v0.17.x
Docs · Recipes

Blink an LED

Drive a GPIO pin from PHP to blink an LED, using the setup()/loop() model with gpio_mode, gpio_write and delay — the smallest end-to-end program that proves PHP is moving real hardware.

This is the smallest program that does something physical: PHP toggles a GPIO pin twice a second and an LED follows. It uses the init-loop execution model — you write setup() and loop(), Arduino-style, and the C runtime calls them. setup() runs once, then loop($tick) repeats forever, driven from C so the FreeRTOS watchdog stays fed and memory housekeeping happens between passes.

What you need

  • A board flashed with php-esp32 (the examples target the ESP32-P4-Pico).
  • An LED and a series resistor of about 330 ohm.
  • Two jumper wires.

Wire the LED in series with the resistor between GPIO2 and GND: the pin drives the anode (through the resistor), the cathode goes to ground. Reset the board and the LED blinks.

Pick a red LED and use the resistor

The GPIO drives 3.3V, not 5V. A red LED with a ~330 ohm resistor is clearly visible; a blue or white LED has a higher forward voltage and can be too dim to see at 3.3V. The series resistor is not optional — it limits the current through the LED and the pin. Any free GPIO works; change the LED constant if GPIO2 is taken on your board.

The code

Put this in project-src/index.php. It defines the pin, makes it an output in setup(), and toggles it every 500 ms in loop() by writing $tick % 2 — high on odd ticks, low on even.

php project-src/index.php
<?php

define('LED', 2);

function setup(): void {
    gpio_mode(LED, GPIO_OUTPUT);
    echo "setup: blinking an LED on GPIO " . LED . " from PHP " . PHP_VERSION . "\n";
}

function loop(int $tick): void {
    gpio_write(LED, $tick % 2);        // on for odd ticks, off for even
    if ($tick % 10 === 0) {
        echo "tick $tick\n";
    }
    delay(500);
}
The LED on GPIO2 blinking on and off every 500 ms, driven from PHP.

Live on the board

Three built-in calls do all the hardware work, from the gpio extension:

  • gpio_mode(pin, GPIO_OUTPUT) — set the pin direction. The constants are GPIO_INPUT and GPIO_OUTPUT.
  • gpio_write(pin, level) — drive the pin high (non-zero) or low (0).
  • delay(ms) — sleep for ms milliseconds. It yields the core (it is not a busy-wait), so the watchdog stays happy through long pauses.

Config

A minimal init-loop project. The PHP source lives on the microSD; the entry file is index.php inside project-src.

toml php-esp32.config.toml
name = "led-blink"
storage_type = "microsd"   # where the PHP source lives
type = "init-loop"         # execution model

[board]
target = "esp32-p4-pico"
port   = ""                # empty = autodetect at flash time

[esp-idf]
path    = ""
version = ""

[php-esp32]
path    = ""
version = ""

[php]
src   = "project-src"      # PHP source folder (copied to the microSD / embedded)
entry = "index.php"        # entry file within src

Build & flash

Build the firmware, write it to the board, then open the serial monitor.

bash build, flash, monitor
phpflash build && phpflash flash && phpflash monitor

Iterate without reflashing

With storage_type = "microsd" the PHP source is read from the card at boot. To try a change, copy project-src/index.php to the card root as index.php and press reset — no rebuild or reflash needed.

What you'll see

The LED blinks at 1 Hz (500 ms on, 500 ms off) and the serial log prints the banner, the setup() line, then a tick every ten passes. The free heap stays flat tick after tick — the engine runs continuously without leaking.

text serial output (excerpt)
php-esp32: microSD mounted at /sdcard
php-esp32: php_embed_init()...
PHP 8.3.32 on ESP32-P4
--- /sdcard/index.php ---
setup: blinking an LED on GPIO 2 from PHP 8.3.32
php-esp32: entering loop()
tick 0
php-esp32: tick 0 -- heap free: 32694575 bytes
tick 10

Next

For real logic between blinks — a helper function and a foreach over a pattern array — see the blink-sos example, which spells "SOS" in Morse on the same pin. To read a pin instead of driving one, continue to Read a button.