Skip to main content

Working with ESP-IDF

This chapter includes the following sections. Please read as needed:

ESP-IDF Getting Started​

New to ESP32 ESP-IDF development and looking to get started quickly? We have prepared a general Getting Started Tutorial for you.

Please Note: This tutorial uses the ESP32-S3-Zero as a teaching example, and all hardware code is based on its pinout. Before you start, it is recommended that you check the pinout of your development board to ensure the pin configuration is correct.

Set Up the ESP-IDF Development Environment​

Framework Version​

ESP-IDF 5.5 stable is recommended, with the build target being esp32s3. ESP-IDF 6.2 is currently used for compatibility verification with a fixed development-version SDK and is not recommended as an official release.

Install ESP-IDF and VS Code​

Install ESP-IDF and VS Code according to the following illustrated instructions, and select 5.5 stable for the SDK version.

note

The following guide uses Windows as an example, demonstrating development using VS Code + the ESP-IDF extension. macOS and Linux users should refer to the official documentation.

Version Selection

The screenshots in this section use ESP-IDF V5.5.2 as an example. When installing, please select the ESP-IDF version that matches your board's example.

Install the ESP-IDF Development Environment​

  1. Download the installation manager from the ESP-IDF Installation Manager page. This is Espressif's latest cross-platform installer. The following steps demonstrate how to use its offline installation feature.

    Click the Offline Installer tab on the page, then select Windows as the operating system and the ESP-IDF version you need (the version shown in the screenshot is for reference only — choose the version that fits your actual needs).

    Download EIM and offline package

    After confirming your selection, click the download button. The browser will automatically download two files: the ESP-IDF Offline Package (.zst) and the ESP-IDF Installer (.exe).

    Download EIM and offline package 2

    Please wait for both files to finish downloading.

  2. Once the download is complete, double-click to run the ESP-IDF Installer (eim-gui-windows-x64.exe).

    The installer will automatically detect if the offline package exists in the same directory. Click Install from archive.

    Auto-detect offline package

    Next, select the installation path. We recommend using the default path. If you need to customize it, ensure the path does not contain Chinese characters or spaces. Click Start installation to proceed.

    Select installation path
  3. When you see the following screen, the ESP-IDF installation is successful.

    Installation successful
  4. We recommend installing the drivers as well. Click Finish installation, then select Install driver.

    Install drivers via ESP-IDF Installation Manager

Install Visual Studio Code and the ESP-IDF Extension​

  1. Download and install Visual Studio Code.

  2. During installation, it is recommended to check Add "Open with Code" action to Windows Explorer file context menu to facilitate opening project folders quickly.

  3. In VS Code, click the Extensions icon Extensions Icon in the Activity Bar on the side (or use the shortcut Ctrl + Shift + X) to open the Extensions view.

  4. Enter ESP-IDF in the search box, locate the ESP-IDF extension, and click Install.

    Search and install ESP-IDF extension in VS Code

  5. For ESP-IDF extension versions ≥ 2.0, the extension will automatically detect and recognize the ESP-IDF environment installed in the previous steps, requiring no manual configuration.

Build and Flash​

Examples Using Board Manager​

Obtain the project from ESP32-S3-LR1121-ePaper-2.9 Example Programs, and open the desired example directory in VS Code. The following commands are executed in the VS Code ESP-IDF terminal (Windows PowerShell), using example 03 as an example.

All 13 examples require generating board-level code through Board Manager. The board type is provided by the waveshare/waveshare_boards component. The example repository does not include a local boards directory, generated code, or build artifacts.

  1. Install the generation assistant tool in the current SDK's Python environment:

    python -m pip install esp-bmgr-assist==0.8.3
  2. List the available board types:

    idf.py bmgr -l

    Confirm that the list contains esp32_s3_lr1121_epaper_2_9. If this board type is not present, prepare the corresponding definition according to the Board Type Missing Instructions before continuing.

  3. Select the board type for this product and generate the board-level code:

    idf.py bmgr -b esp32_s3_lr1121_epaper_2_9

    Listing the board types does not generate code; you must run the selection command. The generator may replace sdkconfig; back up any locally modified configurations before regenerating.

  4. Set the build target; if you need to modify the example options, run idf.py menuconfig and build after saving:

    idf.py set-target esp32s3
    idf.py build
  5. Confirm that the build succeeds, connect the Type-C cable, and replace the actual port:

    idf.py -p COM3 flash monitor

    If automatic download fails, follow the Download Mode Instructions. Press Ctrl+] to exit the serial monitor.

When switching to other examples, repeat the board type selection and build steps in the corresponding directory. For normal cleanup, use idf.py fullclean; retain or regenerate the board-level source code as needed.

For board type resources, see the Waveshare Board Manager Board Repository.

Example Programs​

The following describes the operation steps and expected source code behavior for the 13 examples. The actual running results need to be verified against the corresponding logs and hardware behavior.

DirectoryFunctionAdditional Conditions
01_ws2812_ledRGB LED single-color and breathing testObserve the LED
02_i2c_scanOnboard I2C detectionSerial port
03_epaper_2_9E-paper display and screen clearingConfirm the screen configuration
04_bat_axp2602Fuel gauge and interruptBattery and correct sampling configuration
05_rtc_pcf85063RTC and 1 Hz interruptSet the time as needed
06_lr1121_pingpongLoRa PING/PONGAntenna and peer with the same parameters
07_sd_cardStorage card read/write verification and e-paper status displayFAT32 card
08_deep_sleepTimed wake-up, BOOT wake-up, and power shutdownObserve the wake-up and measurement conditions
09_comprehensive_demoStartup self-test, wireless communication, and sleep recoveryPrepare matching antenna, LoRa peer, and FAT32 TF card as needed
10_lr1121_lorawanLoRaWAN Class A OTAAGateway, server, credentials
11_LoRaWAN_ABPLoRaWAN Class A ABPMatching session and counters
12_LoRaWAN_Class_CContinuous reception after OTAAServer Class C configuration
13_LoRaWAN_MulticastStatic Class C multicastMatching multicast parameters

01_ws2812_led: RGB LED​

Example Description

Board Manager enables GPIO38 power, obtains the single GRB LED on GPIO21, and uses the RMT driver. Red, green, and blue are each displayed for 1.2 seconds, followed by continuous color breathing, with the channel value limited to 48/255.

Operation Steps and Running Effect

After generating the board-level code, compiling, and downloading, verify that the single-color phase in the log matches the actual color, then observe the breathing effect. A successful log cannot replace the actual check of the LED color and power timing.

02_i2c_scan: I2C Scan​

Example Description

Checks the idle levels of GPIO13/SDA and GPIO14/SCL, and scans 0x03-0x77; each round is spaced 3 seconds apart.

Operation Steps and Running Effect

Download and open the monitor, and check found 0x51 PCF85063A RTC, found 0x62 AXP2602 fuel gauge, and the device count. If the bus remains low, the program prompts you to check the power supply, pull-ups, and shared pins. This example does not refresh the screen.

03_epaper_2_9: E-Paper Display​

Example Description

In the default functional mode, it displays the status and update count, updating every 30 seconds. The first frame is a full refresh, and a full refresh is inserted after a maximum of 10 successful full-screen fast refreshes.

Operation Steps

  1. Generate the board-level code and run idf.py menuconfig.
  2. Go to "03 e-Paper display" > "Display mode" and select the functional mode or clear screen mode in the table below.
  3. Save, recompile, and download, then check the behavior against the table below.
ModeExpected Behavior
Functional (customer default)Periodically updates the functional interface
Clear screen (full white, then sleep)Fully refreshes to pure white, then puts the screen controller to sleep, retaining the blank screen

The success log for clearing the screen is screen cleared to white; panel sleeping; periodic refresh disabled. To restore normal use, reselect Functional and compile and download again; the mode is saved in sdkconfig, and a reset will not restore the default.

04_bat_axp2602: Fuel Gauge and IRQ​

Example Description

This example reads the AXP2602 battery voltage, current, SOC, chip temperature, and remaining time, and uses GPIO48 to handle fuel gauge interrupts. The default sampling interval is 30 seconds and can be modified in "Component config" > "AXP2602 Battery Gauge Example".

Operation Steps and Running Effect

Connect the battery according to the Battery Instructions, then download. The example is already configured for the onboard 10 mΩ sampling resistor. Observe consecutive samples and the screen information; after an IRQ event occurs, the status should be recorded and cleared. Charger plug/unplug is not an independent AXP2602 IRQ source, so you cannot require GPIO48 to trigger on every plug/unplug.

The example does not write a new model adapted to the connected battery, and the SOC and remaining time are estimates. When reading individual fields fails, old values may be retained; judge based on consecutive samples. GPIO47 is an independent charging status signal.

05_rtc_pcf85063: RTC and Interrupt​

Example Description

Reads the PCF85063A time, enables the 1 Hz timer interrupt, and records the interrupt count via GPIO7. When the time is invalid or the low-voltage flag is set, writes the default time 2026-01-01 12:00:00.

Operation Steps and Running Effect

Modify the initial time in main/main.c as needed, compile and download, and check the date, time, VL, INT level, and interrupt count. The screen updates according to the display period; you cannot require every serial log to synchronously refresh the screen.

An existing valid time is retained. The default time is not network time synchronization, nor is it the accurate time at the moment of flashing.

06_lr1121_pingpong: LoRa Point-to-Point​

Example Description

Sends PING at random intervals and replies with PONG after receiving a peer's PING; displays the transmit/receive counts, RSSI, SNR, and the latest payload. The regular screen refresh is rate-limited to 30 seconds, and the serial port and screen do not need to update every packet synchronously.

Operation Steps

  1. Prepare two boards and matching antennas, and first check the Wireless Parameters and Regional Requirements.
  2. Check the default configuration and initialization parameters in include/board_radio.h of the component supported by this example. The default is 868 MHz, 125 kHz, SF7, coding rate 4/5, 22 dBm. Both ends need to be consistent and comply with the actual usage restrictions.
  3. Build and download them separately, and check PING/PONG and the incrementing TX/RX counts.

Result Judgment

Without a peer, a receive timeout cannot be judged as hardware damage on its own. A successful initialization alone also cannot be judged as the wireless link passing.

07_sd_card: TF Test​

Example Description

Initializes the e-paper and shared power supply through Board Manager, mounts a FAT32 card using the native SDMMC/FATFS, lists up to 16 root directory entries, and then completes the write, sync, byte-by-byte read-back verification, cleanup, and unmount of /ws_sd/test.txt.

The e-paper displays the card capacity, mount, read/write, and unmount status, and the title is updated from SD TEST to SD PASS or SD FAIL. After unmounting is complete, refresh the final result, then let the screen sleep and turn off the shared power supply; the result screen is still retained.

The display and TF test use independent 16 KiB task stacks, with no need to manually increase the main task stack. If task creation fails, an error is reported, and the display is not switched to the main task.

Operation Steps and Running Effect

  1. Power off, insert a FAT32 card, and read the Test File Impact.
  2. Follow Build and Flash to generate the board-level code, compile, and download.
  3. Check the various statuses and SD PASS / SD FAIL on the screen, and verify the write/read verification passed, filesystem synchronized and unmounted, and FINAL results in the serial port.

The program does not format or overwrite existing test files. If the display fails, the TF test still continues; the serial port DISPLAY separately reports the display result, and FINAL indicates the TF test result; if a failure occurs, check the preceding error logs.

08_deep_sleep: Deep Sleep​

Example Description

Displays a full-refresh sleep prompt once, and configures 600-second timed wake-up and BOOT/GPIO0 wake-up. Before sleeping, it sends a sleep command to the LR1121, puts the AXP2602 and e-paper to sleep, then isolates the wireless pins and turns off and holds off the GPIO38 shared power supply.

Operation Steps and Running Effect

After downloading, check LR1121 Sleep accepted before power off, the AXP2602 sleep status, and start deep sleep. Release BOOT, wait for the timed wake-up, or briefly press BOOT, and check the timed/button wake-up cause. To modify the period, adjust WAKEUP_TIME_SEC in main/main.cpp.

The USB monitor may disconnect during deep sleep. Power consumption needs to be measured at a clear measurement point and cannot be inferred from sleep API return values.

09_comprehensive_demo: Comprehensive Example​

Example Description

After power-on or wake-up, it performs a single self-test of RAM/PSRAM, Flash, encryption, and peripherals, then runs Wi-Fi APSTA, BLE passive scanning, LoRa reception, battery monitoring, and button services. The full-capacity Flash check is read-only, and the TF check is read-only; no Flash erase/write or card file writing is performed.

After the self-test ends, the states proceed in sequence: ACTIVE_READY → LORA_RX. The RAM/PSRAM read/write verification is performed only during the startup self-test; no continuous dual-core stress test is performed during normal operation. The serial port retains the wireless status, LoRa transmit/receive counts, and free memory.

Operation Steps

  1. Configure the project according to Build and Flash, and generate the board-level code for this product.
  2. Modify the STA, SoftAP, and LoRa configurations in main/app_config.h, and check the antenna and transmit parameters. If STA is not set, it does not affect SoftAP startup.
  3. Build and download, and observe SELF TEST, BOARD TEST, and the runtime page in sequence, confirming that the serial port status changes from ACTIVE_READY to LORA_RX.
  4. Each stable press of KEY/GPIO1 sends one frame; check the peer, TX/RX, and RSSI/SNR.
  5. Double-press BOOT/GPIO0, and check the orderly stop and entering deep sleep; during deep sleep, single-press BOOT, and check the wake-up cause and the complete self-test rebuild.

Result Judgment

This example does not enable timed wake-up. PASS indicates that this item obtained a verifiable result, FAIL indicates a specific operation failed, and NOT_TESTED indicates that a card, peer, or other condition is missing. SLEEP_CANCELLED does not mean deep sleep has been entered, and a crash or USB reset cannot be regarded as a normal button wake-up.

In the RAM self-test log, requested is the target size, and actual and RAM=PASS bytes=... are the actual test range. If allocation fails, it retries with a smaller range; a single self-test cannot be regarded as verification of the entire RAM. If allocation is impossible, it displays RAM=NOT_TESTED bytes=0, and the system self-test is still judged as failed.

When the fuel gauge temperature is invalid or reading fails, it displays temp=UNAVAILABLE and does not substitute an old value or zero; a valid 0°C is still a normal reading. Chip temperature is not the same as ambient temperature.

The regular serial status period is 30 seconds, and the screen is 5 minutes; transmit/receive events can trigger an earlier refresh. Frequency offset displays NOT_AVAILABLE; this example has no network traffic generator, so Wi-Fi throughput still needs to be measured separately. After adding features, the application partition space needs to be rechecked.

10_lr1121_lorawan: Class A OTAA​

Example Description

Default LoRaWAN 1.1, EU868 standard multi-channel, Class A. After completing OTAA or session restoration, it sends periodic uplinks and handles RX1/RX2 downlinks and status saving.

Operation Steps

  1. Copy main/lorawan_secrets.h.example to a local main/lorawan_secrets.h, and fill in the JoinEUI, DevEUI, AppKey, and NwkKey assigned by the server.
  2. Modify the region, MAC version, power, and transmission policy in main/lorawan_config.h, and use matching configurations on the server.
  3. Compile and download, and check the activation result, FCntUp, receive windows, and server uplink/downlink records.

Result Judgment

The default application period is 60 seconds, FPort 1, unconfirmed uplink, with ADR disabled; actual transmission is also subject to protocol scheduling. The initialization showing 868.1 MHz does not mean all uplinks are fixed to that frequency; in multi-channel mode, RadioLib selects the valid channel. If the placeholder credentials are not replaced, no Join Request is transmitted.

11_LoRaWAN_ABP: Class A ABP​

Example Description

Preconfigures the DevAddr and session keys, directly activates or restores a session, and does not perform OTAA. The current default is LoRaWAN 1.0.x, EU868 single-channel 868.1 MHz / DR3.

Operation Steps and Running Effect

Copy main/abp_credentials.h.example to main/abp_credentials.h, fill in the DevAddr, AppSKey, and NwkSKey, and verify the MAC version and channel in main/abp_config.h. After compiling and downloading, check session restoration, uplink, and RX1/RX2.

When selecting 1.1, use the independent network keys required by the configuration header and sync the server version. Single-channel testing requires a matching gateway; you cannot simply change the frequency and then consider it adapted to a standard multi-channel network. The frame counters need to be consistent between the device and the server.

12_LoRaWAN_Class_C: Continuous Reception​

Example Description

Uses LoRaWAN 1.1 OTAA, entering Class C after activation or restoration. The default is EU868 multi-channel; after transmission and the necessary Class A receive windows end, it returns to the continuous reception state.

Operation Steps and Running Effect

Create a local main/lorawan_secrets.h, modify main/lorawan_config.h, and enable matching Class C and RX2/RXC parameters on the server. After downloading, check the activation and Class C status, then actually send downlinks, recording the downlink port, count, and payload.

Keep the power supply connected during operation, and do not perform deep sleep that stops reception. The current adaptation source uses a TCXO of 3.0 V; verify the actual module specifications before modifying.

13_LoRaWAN_Multicast: Static Class C Multicast​

Example Description

Retains the device's own OTAA unicast session and adds a static Class C multicast slot. The default follows the EU868 single-channel test configuration. The multicast counter is persisted, and normal uplinks still go through the device's own unicast session.

Operation Steps and Running Effect

Create a local main/lorawan_secrets.h, and fill in the device identity as well as the multicast address and group keys; verify the frequency, DR, port, and period in main/multicast_config.h. Keep the same group parameters consistent on the server. After downloading, send unicast and multicast downlinks separately, and check the UNICAST / MULTICAST classification, group ID, and incrementing counters.

The current example only enables slot 0 and does not provide Class B, remote multicast configuration, clock synchronization, or full FUOTA. Multicast configuration errors cannot be fixed by repeatedly rejoining the network; verify the parameters and persisted state first.