ITR90 ESP32-S3 vacuum gauge controller
Arduino firmware for a Leybold/Pfeiffer ITR90 vacuum gauge, using the Waveshare ESP32-S3-Touch-LCD-2, SKU 29667, with a 240 × 320 touch display.
The controller displays pressure and gauge status, provides touchscreen and web controls, and supports degas through either RS-232 or the carrier board's direct +24 V control circuit. It connects to an existing router and advertises a local browser address, with a password-protected standalone Wi-Fi access point as a fallback. The webpage and its API have no username/password login.
Reproduce the versions, board settings, BSP files and LVGL configuration below before changing the firmware. A successful compilation alone does not prove that the display or PSRAM is configured correctly.
Quick start
- Obtain the complete working sketch folder, including its Waveshare BSP source/header files. The application
.inoalone is insufficient. - Install the pinned board package and libraries.
- Install the working
lv_conf.hand enable the required fonts. - Select ESP32S3 Dev Module and match every setting in the Tools table.
- Set the router credentials and per-board names in the application.
- Open the application, click Verify, then Upload. Keep the original BSP files in the same project.
- Open Serial Monitor at 115200 baud, reset the board, and follow first-boot checks.
If anything fails, start with troubleshooting rather than upgrading libraries or changing the control logic.
Pinned board package and libraries
This is the project's recorded working combination:
| Component | Working version | Installation / role |
|---|---|---|
| esp32 by Espressif Systems | 3.3.5 | Arduino Boards Manager; compiler, ESP32 APIs and USB/Wi-Fi support |
| GFX Library for Arduino / Arduino_GFX | 1.5.0 | Library Manager or the matching vendor archive; display BSP dependency |
| lvgl | 8.4.0 | Library Manager; GUI library |
| FastIMU | 1.2.6 | Library Manager or the matching vendor archive; dependency of the supplied Waveshare BSP where used |
| Waveshare BSP/example files | Exact working copy for ESP32-S3-Touch-LCD-2 | Keep the complete working source set; do not substitute a similarly named board's BSP |
WiFi.h, WebServer.h, ESPmDNS.h |
Bundled with ESP32 core 3.3.5 | No separate library installation |
Use Arduino IDE 2.x for the instructions below. The exact IDE version of the original working PC was not recorded; record the version used for future releases.
Do not substitute LVGL 9.x, ESP32 core 3.3.11, or GFX 1.6.x when reproducing this build. These are not the recorded working versions. Newer versions may be usable after a deliberate port and validation, but are not drop-in assumptions for this BSP.
Install the ESP32 board package
-
Open File → Preferences.
-
Add the stable Espressif package index to Additional Boards Manager URLs:
https://espressif.github.io/arduino-esp32/package_esp32_index.json -
Open Boards Manager, search for esp32 by Espressif Systems, select 3.3.5, and install it.
-
Select Tools → Board → ESP32 Arduino → ESP32S3 Dev Module.
ESP32 is a board platform, not a Library Manager entry. If it does not appear in Library Manager, that does not mean it was deleted. Check Boards Manager instead.
Install the libraries
Install the exact versions in the table using Library Manager's version selector. If using the working PC's supplied library archives, preserve those copies and any vendor modifications.
Enable verbose compilation output in Preferences. Check the actual library folders and versions selected by the compiler, especially if Arduino reports “Multiple libraries were found”. A folder name such as lvgl does not by itself establish its version; check its library.properties.
Avoid keeping several active copies of the same library under names such as lvgl-master, lvgl-old or GFX_Library_for_Arduino_backup. Move backups outside the sketchbook's libraries directory.
Arduino IDE Tools settings
These settings were recorded from the working project:
| Tools item | Value |
|---|---|
| Board | ESP32S3 Dev Module |
| PSRAM | OPI PSRAM |
| Flash Size | 16MB (128Mb) |
| Flash Mode | QIO 80MHz |
| Partition Scheme | 16M Flash (3MB APP/9.9MB FATFS) |
| Upload Mode | USB-OTG CDC (TinyUSB) |
| USB Mode | Hardware CDC and JTAG |
| USB CDC On Boot | Enabled |
| CPU Frequency | 240MHz (WiFi) |
| Upload Speed | 921600 |
| Port | The connected board's actual COM/serial port; this varies by PC |
Serial Monitor uses 115200 baud. The gauge UART uses 9600 baud. Neither value is the upload speed.
Menu labels and available choices depend on the board package and selected board. If these choices do not match, first check ESP32 core 3.3.5 and ESP32S3 Dev Module. Keep the recorded USB selections; do not silently replace them with a different example's defaults.
Copying a sketch to another PC does not reliably carry Arduino IDE Tools selections with it. Check this table on every new PC and after switching boards or reinstalling the board package. Configuration comments in the .ino document the settings; they do not apply them.
Project files and Waveshare BSP
The application must stay with the board-specific LCD, touch, I²C and SPI implementation it was tested with.
| File / location | What it should contain |
|---|---|
README.md |
This setup and troubleshooting guide |
ITR90/ITR90.ino |
Main application, if the sketch is named ITR90 |
ITR90/bsp_*.h and ITR90/bsp_*.cpp |
Complete working BSP source/header set, including touch-driver dependencies |
| Other files from the working Waveshare example | Any additional sources/configuration required by that BSP |
config/lv_conf.h, recommended repository copy |
The exact working LVGL configuration, with installation instructions below |
| Dependency/version record | Exact versions above, plus vendor archive provenance or checksums where available |
The repository layout above is a packaging recommendation, not a claim that every listed file is already supplied. If BSP files or lv_conf.h are absent, recover them from the working project or matching Waveshare package before attempting a reproducible build.
Arduino's main sketch filename must match its enclosing folder: ITR90/ITR90.ino. If a downloaded file is named ITR90(1).ino, rename it to ITR90.ino when placing it in ITR90. Keep only one application's setup() and loop() in that sketch folder; remove the original example application after replacing it.
If reconstructing the project, start with a copy of the matching Waveshare example package, such as the factory or LVGL brightness example. Preserve its complete BSP and replace its application with this firmware. Do not mix files from different Waveshare board sizes or revisions.
The application uses these BSP functions:
bsp_i2c_init();
bsp_lv_port_init();
bsp_spi_init();
bsp_lv_port_run();
lvgl_lock(-1);
lvgl_unlock();
The current application includes bsp_i2c.h, bsp_lv_port.h and bsp_spi.h. The BSP may include additional headers such as bsp_cst816.h. Those dependencies must be supplied too. If a vendor package uses different header organization, reconcile the real declarations and implementations; creating an empty header does not supply a driver.
LVGL configuration and fonts
Use LVGL 8.4.0 and the working project's lv_conf.h. This configuration affects LVGL's own C source files as well as the application.
For the standard Arduino LVGL 8.4 layout, put lv_conf.h next to the lvgl folder, in the active sketchbook's libraries directory:
| Windows path example | Contents |
|---|---|
C:\Users\<user>\Documents\Arduino\libraries\lvgl\ |
Installed LVGL 8.4.0 library |
C:\Users\<user>\Documents\Arduino\libraries\lv_conf.h |
Active LVGL configuration |
Use the actual Sketchbook location shown in Arduino Preferences; it may differ from this example. Putting the configuration only beside ITR90.ino does not ensure that LVGL's library sources will find it. If the BSP explicitly configures LV_CONF_PATH or LV_CONF_INCLUDE_SIMPLE, follow that existing include setup instead.
If starting from lv_conf_template.h, copy the template from LVGL 8.4.0, rename it to lv_conf.h and change its opening #if 0 to #if 1. Restore the working BSP's color, memory, tick and byte-order settings; an otherwise default template is not a substitute for the working configuration. This display path uses RGB565 / 16-bit color.
The two pressure labels require these options in the active configuration:
#define LV_FONT_MONTSERRAT_28 1
#define LV_FONT_MONTSERRAT_48 1
Keep the working configuration's default font and other enabled fonts as well. These two settings are edits within the complete configuration, not a complete lv_conf.h.
The application has conditional checks around the 28- and 48-pixel fonts. If those fonts are disabled, it can compile while using the default font, making the pressure labels smaller than intended.
After replacing lv_conf.h, rebuild and confirm that the compiler is using the intended library/configuration. If font linking still fails, follow the clean reinstall procedure under troubleshooting.
See the LVGL 8.4 Arduino configuration guide and configuration/include-path guide.
Wi-Fi and multiple boards
Configure these values near the top of the application before uploading:
const char *WIFI_SSID = "YOUR_ROUTER_SSID";
const char *WIFI_PASS = "YOUR_ROUTER_PASSWORD";
const char *MDNS_HOSTNAME = "itr90-1";
const bool ENABLE_FALLBACK_AP = true;
const char *FALLBACK_AP_SSID = "ITR90-Controller-1";
const char *FALLBACK_AP_PASS = "YOUR_AP_PASSWORD"; // 8–63 characters
The sample credentials above are placeholders; replace them. Enter only the hostname in MDNS_HOSTNAME, without http:// or .local. Use letters, digits and hyphens.
Router connection
Connect the board to a 2.4 GHz Wi-Fi network and use a browser on the same LAN:
| Board | MDNS_HOSTNAME |
Browser address |
|---|---|---|
| 1 | "itr90-1" |
http://itr90-1.local/ |
| 2 | "itr90-2" |
http://itr90-2.local/ |
Use a distinct hostname for every board on that network. Two boards advertising itr90.local do not create a device-selection page.
The firmware prints the router-assigned IP address in Serial Monitor. If .local resolution is blocked or unsupported on the client/network, open http://<that-IP>/ instead. A DHCP hostname alone is not the same as working mDNS resolution.
Standalone fallback
- With no router SSID configured—an empty string or
"CHANGE_ME"—the fallback AP starts immediately. - With an SSID configured, the firmware attempts the router connection and retries approximately every 10 seconds.
- After 30 seconds without a router connection, it starts the fallback AP while continuing router attempts.
- If the router connection returns, it stops the fallback AP and restores the router browser address.
- A later router outage starts a new 30-second fallback window.
Join the board's FALLBACK_AP_SSID using FALLBACK_AP_PASS, then open http://192.168.1.1/. Type the explicit http:// address if the browser tries HTTPS. A “no internet” warning on this standalone Wi-Fi network is expected; keep the connection.
Give every board a different AP name, such as ITR90-Controller-1 and ITR90-Controller-2. Their separate fallback networks can share the same AP password and 192.168.1.1 address. Select the intended board's Wi-Fi network before opening that address.
There is no webpage login in either router or fallback mode. The fallback password protects joining its Wi-Fi network; it is not a dashboard password. Router credentials are still needed when joining a password-protected router.
If the router LAN also uses 192.168.1.x/24, the identical fallback subnet can complicate routing while AP and station mode run together. Diagnose this separately from mDNS; use a distinct AP subnet for such a deployment and document its revised fallback URL.
Keep real Wi-Fi credentials out of public Git commits. The current firmware stores them in source; use a private configured build copy or implement a separately ignored local configuration file before publishing real credentials.
Upload and first-boot checks
- Use a USB data cable and the board's appropriate USB connection. Select its actual serial port.
- Click Verify first. Save the full compiler output if it fails; the first specific error is more useful than
exit status 1. - Recheck the Tools settings, then click Upload.
- If upload stalls at connection, try the board's BOOT/RESET recovery procedure: hold BOOT, press/release RESET, then retry upload and release BOOT once the downloader connects. Re-select the port if it changes.
- Open Serial Monitor at 115200 baud and reset the board. Native USB can enumerate on a different port after boot.
- Confirm the log begins with
ITR90 controller booting...and progresses toController ready; waiting for valid ITR90 frames. - Confirm the application pressure screen appears. Any BSP RGB startup test should finish; repeated RGB cycling requires investigation.
- Confirm touch input works. A screen that later turns off after five minutes of inactivity is intentional: touch wakes it, while gauge monitoring and networking continue.
- Test the router URL or fallback URL. Open
/api/statusto inspect live JSON without any login. - Connect the correctly powered gauge and confirm fresh RS-232 pressure/status data before testing control functions.
If 921600 upload speed is unreliable, temporarily lower Upload Speed, for example to 115200, and retry. This helps transfer reliability; it does not repair a runtime PSRAM/flash configuration problem.
Hardware and gauge interface
The ESP32 controller's USB connection does not replace the ITR90's required 24 V supply. Use the carrier board's intended power and interface wiring.
| ESP32 GPIO | Function | Gauge/interface connection |
|---|---|---|
| 17 | Gauge UART TX | MAX3232 T1IN → RS-232 output → ITR90 pin 14, RxD |
| 18 | Gauge UART RX | ITR90 pin 13, TxD → MAX3232 RS-232 input → R1OUT |
| 4 | Analog pressure diagnostic | ITR90 analog output through the carrier's 33 kΩ / 12 kΩ divider and 100 nF filtering |
| 10 | Direct degas request, active HIGH | Carrier's AQY212EH / +24 V switching circuit → ITR90 pin 7 |
| 1 | LCD backlight | Reserved for the Waveshare board |
Gauge connector essentials: pin 8 = +24 V supply, pin 5 = supply/serial ground, pin 2 = analog output, pin 12 = analog signal ground. Use the actual connector's pin numbering/view; the solder side mirrors the mating view.
Use the MAX3232 for RS-232 level conversion. The gauge's RS-232 signals are not ESP32 3.3 V UART signals. Likewise, gauge analog output can reach 10 V and must pass through the divider; the direct-degas +24 V circuit must not connect directly to a GPIO.
The older Oerlikon Leybold ITR90 12090 and the red Pfeiffer ITR90 use the same documented RS-232 frame/command format:
| Protocol item | Value |
|---|---|
| UART | 9600 baud, 8 data bits, no parity, 1 stop bit |
| Measurements | 9-byte binary frames, transmitted automatically approximately every 20 ms |
| Frame checks used by this firmware | Length byte 7, page 5, sensor type 10, checksum = low byte of the sum of bytes 1–7 |
| Commands | 5-byte binary frames, including the same degas ON/OFF commands |
No serial protocol change is required solely because of that older ITR90 housing. Confirm that the label actually identifies an ITR90 when substituting gauges.
Control behavior relevant to debugging
- Fresh RS-232 data are required for degas. Data older than 500 ms are treated as stale.
- Degas requires valid pressure below 7.2 × 10⁻⁶ mbar, no reported warning/error, and the gauge's 5 mA emission state.
- The controller expects degas acknowledgement within approximately 2 seconds and stops a request that is not acknowledged.
- Degas is limited to 3 minutes. STOP removes the direct GPIO request and also sends the serial OFF command.
- The analog pressure estimate can support the displayed pressure when serial data are unavailable, but does not authorize degas.
- Touchscreen start uses a confirmation screen and a 2-second hold. Web start uses confirmation and the same gauge prerequisite checks.
- Controls return to the pressure view after approximately 2 minutes of inactivity; the LCD backlight turns off after 5 minutes. The ESP32 stays awake.
An inhibited degas request with a clear reason is expected behavior when those prerequisites are not met. Diagnose the missing prerequisite rather than bypassing it to test communication.
Troubleshooting
bsp_cst816.h: No such file or directory
The touch-driver header or its BSP source set is missing/mismatched. Restore the complete working Waveshare sketch/BSP folder, including the driver implementation and transitive headers. Do not fix this by merely deleting the include or creating an empty file. The same approach applies to missing bsp_i2c.h, bsp_spi.h or bsp_lv_port.h.
spiFrequencyToClockDiv: “too few arguments” / invalid conversion to spi_t*
The graphics library and ESP32 core disagree about the SPI API. This project encountered that failure with a compiler path showing ESP32 core 3.3.11. Restore core 3.3.5 and GFX Library for Arduino 1.5.0 together, and verify the versions/paths in verbose compiler output.
Treat a different core/GFX combination as a port requiring verification; do not patch one failing library call and assume the rest of the BSP is compatible.
RED, GREEN or BLUE “was not declared in this scope”
The BSP's RGB startup test uses color names that are not provided by the selected graphics headers. First restore the pinned GFX 1.5.0 library and matching BSP includes. A change to GFX 1.6.x was one of the project's compatibility problems.
If deliberately adapting the BSP to a different graphics version, use that version's documented color constants. For an explicitly RGB565 fillScreen argument, the numerical colors are 0xF800 (red), 0x07E0 (green), and 0x001F (blue). This optional source adaptation does not replace checking the rest of the graphics API.
lv_conf.h cannot be found / LVGL configuration appears ignored
Check the active sketchbook path, the configuration's location beside lvgl, and the opening #if 1. Check for duplicate configurations or an explicitly configured include path. Renaming a file to lv_conf.h.txt on Windows will not work; enable visible filename extensions.
Fonts are too small, or undefined reference to lv_font_montserrat_48 / lv_font_montserrat_28
Enable both LV_FONT_MONTSERRAT_48 and LV_FONT_MONTSERRAT_28 in the configuration used by LVGL's compiled sources, not just in ITR90.ino.
The undefined-reference error is a linker error: a font was referenced, but its compiled definition was unavailable. A declaration such as LV_FONT_DECLARE(...) alone cannot supply that definition.
If the same copied lv_conf.h works on one PC but not another:
- Check the actual LVGL library path/version and duplicate copies using verbose output.
- Back up the working
lv_conf.h. - Uninstall the active LVGL library using Library Manager. Move any leftover competing copies outside the library search path.
- Reinstall lvgl 8.4.0, restore the working configuration, restart the IDE and rebuild.
- If necessary, force a clean rebuild rather than reusing old compiled objects. For an existing Arduino CLI build,
arduino-cli compile --cleansupports this; target only the relevant temporary build cache when cleaning manually.
Reinstalling LVGL 8.4.0 resolved this exact font-link failure during development. That outcome does not establish whether the original cause was a stale build, an incomplete installation or conflicting library/configuration copies; check all three.
Upload succeeds, but the display repeatedly cycles red/green/blue
First distinguish an uninterrupted RGB test from a reboot loop using Serial Monitor. Repeated boot messages, watchdog/panic traces, PSRAM errors or brownout messages identify different causes.
For this project, matching the working PC's Upload Mode, PSRAM, Partition Scheme and Flash Size resolved the startup problem. Recheck the entire Tools table, particularly OPI PSRAM, 16 MB flash and the exact partition selection. Upload success only means firmware was transferred; it does not validate those runtime settings.
Also confirm that the intended application replaced the factory example, that the BSP matches this board, and that power is stable. If settings match and cycling continues, keep the full boot log for diagnosis.
No serial port / upload cannot connect / Serial Monitor is blank
Check the USB data cable, selected port, USB CDC settings and upload mode. Try BOOT/RESET recovery, re-select the port after enumeration, and close programs that hold the port open. Select 115200 for Serial Monitor; enabling CDC on boot matters when using the native USB console.
Router IP works but itr90-1.local does not
Use the exact hostname configured for that board and the same LAN. Guest/client isolation, blocked multicast, VPN routing or client mDNS support can prevent name resolution. Test the printed IP as an alternative. If the log says mDNS startup failed, the firmware falls back to the IP while retrying.
Fallback AP cannot be joined / 192.168.1.1 does not open
Check ENABLE_FALLBACK_AP, the 30-second connection window, the actual AP name and an 8–63 character AP password. Join that AP, stay connected despite “no internet”, and enter http://192.168.1.1/. Check Serial Monitor for AP startup/configuration failures and subnet overlap. A browser trying HTTPS is not using this HTTP server.
Browser still prompts for a username/password
Confirm that the no-web-login firmware was uploaded to the board you are opening. Check the hostname/AP name and verify that the source has no server.authenticate(...), server.requestAuthentication() or requireWebAuth() checks. Test /api/status as well as the root page.
An older firmware or another board at the address can still have HTTP authentication. A login prompt when joining Wi-Fi is the separate AP/network password and is expected.
Pressure is absent/stale, but the display and webpage work
Check gauge power, common ground, the MAX3232 level conversion and TX/RX directions in the wiring table. Confirm 9600 baud / 8N1. Gauge output is binary, so readable text is not expected from a terminal.
Open /api/status and inspect pressureSource, frameAge, gauge, firmware and message. “ADC fallback” does not establish working RS-232 communication. When instrumenting the parser, the internal goodFrames and rejectedWindows counters help distinguish valid frames from misalignment/noise; they are not currently exported by the API.
Degas is refused or stops shortly after starting
Read the on-screen/web message or Serial Monitor action log. Check fresh serial frames, gauge errors, pressure and 5 mA emission state. A direct-pin request still needs serial feedback. Missing acknowledgement after approximately 2 seconds is an intentional stop condition.
Moving to another PC and maintaining a reproducible repository
Include the complete working application/BSP source, the exact lv_conf.h, this README, and the dependency versions. Record any modifications inside vendor libraries; simply naming a released version will not reproduce an unpublished patch.
For every release, record:
- Git commit/release identifier.
- Arduino IDE and ESP32 core versions.
- Library versions and the selected library paths.
- The complete Tools settings, ideally a screenshot plus this text table.
- The LVGL configuration and any vendor/BSP archive version or checksum.
- The per-board hostname/AP naming convention, with credentials redacted.
- A successful compiler log and a first-boot log.
On the receiving PC, install the pinned versions, restore the configuration into its active location, select the matching Tools settings and perform the same first-boot checks. Copying only the .ino or only lv_conf.h is insufficient.
Optional Arduino CLI build profile
Arduino CLI supports a sketch.yaml build profile that records the full board FQBN/options and pins core/library versions. Generate a profile from a successful CLI build using --dump-profile, keep it with the sketch, and name the reviewed profile verified. Include any locally configured/vendor libraries and the LVGL configuration/include setup.
Use the complete FQBN from the working configuration, including its menu options. The base esp32:esp32:esp32s3 name alone does not encode the flash, PSRAM, partition or USB choices.
Once that profile exists, from the repository root:
arduino-cli compile --profile verified ./ITR90
No generated/validated CLI profile is supplied by this README. A profile using a fresh stock LVGL library also needs the working lv_conf.h available to that library's build; do not assume it will inherit an unrelated IDE library installation. IDE users should still verify their Tools settings.
See Arduino's build-profile documentation and compile command.
What to include in a bug report
Provide the board/SKU, Git revision, exact core/library versions, all Tools settings, the chosen library/configuration paths, the first meaningful compiler error or full boot log, and whether the failure occurs in router or fallback mode. Redact router/AP passwords.
Also report whether the problem reproduces on the original working PC with the same project folder and settings. This distinguishes a source regression from an environment mismatch.
References
- Waveshare ESP32-S3-Touch-LCD-2 board and example downloads
- Espressif Arduino-ESP32 installation
- Arduino_GFX project
- LVGL 8.4 Arduino setup
- LVGL 8.4 configuration paths
- Leybold ITR90 operating manual, GA09420_002_C0
- Pfeiffer ITR90 operating instructions, BG 5040 BEN
The pinned versions and Tools table above come from this project's recorded working setup. General vendor documentation may describe different example versions or defaults.