Skip to content

esp32s3_hmi43v3_factory_app_input

The esp32s3_hmi43v3_factory_app_input module provides the complete human-input layer for the ESP32-S3 HMI 4.3” V3 factory application. It aggregates four independent hardware input drivers — physical buttons, a quadrature rotary encoder, a capacitive touchscreen, and a feedback buzzer — into a cohesive polling-based subsystem consumed by the factory app’s main loop.

This module is a sub-module of esp32s3_hmi43v3_factory_app, which is the standalone diagnostic and provisioning firmware flashed alongside the main pendant firmware. It is architecturally identical to the _factory_app_input modules found in sibling boards (e.g., esp32s3_8048s070c_factory_app_input, pibot_pendant_v1_0_factory_app_input) — each board carries its own copy, wired to board-specific hw_config.h constants.


reads buttons / encoder / touch

triggers buzzer_beep_short()

esp32s3_hmi43v3_factory_app

esp32s3_hmi43v3_factory_app_main

(main.c - menu, OTA, SD, snapshots)

esp32s3_hmi43v3_factory_app_display

(gfx.c, rm68120.c)

esp32s3_hmi43v3_factory_app_input

▶ buttons.c encoder.c touch.c buzzer.c ◀

esp32s3_hmi43v3_factory_app_storage

(sdcard.c)

esp32s3_hmi43v3_factory_app_tools

(flash_all.py, generate_font.py …)

The input module has no internal dependencies on other factory-app sub-modules. It is consumed unidirectionally by main.c in the esp32s3_hmi43v3_factory_app_main sub-module.


FileRole
boards/esp32s3_hmi43v3/Factory/main/buttons.cPhysical button driver (up to 3 buttons, GPIO, debounce)
boards/esp32s3_hmi43v3/Factory/main/encoder.cQuadrature rotary encoder driver (ESP-IDF PCNT)
boards/esp32s3_hmi43v3/Factory/main/touch.cCapacitive touch driver (FT5x06, I2C, coordinate scaling)
boards/esp32s3_hmi43v3/Factory/main/touch.hPublic API and touch_point_t type
boards/esp32s3_hmi43v3/Factory/main/buzzer.cBit-banged square-wave buzzer (tactile feedback only)

Board Configuration

Shared Hardware Drivers

ESP-IDF Drivers

esp32s3_hmi43v3_factory_app_input

gpio_config / gpio_get_level

pcnt_new_unit / pcnt_unit_get_count

gpio_set_level / esp_rom_delay_us

bus_i2c_init

touch_ft5x06_configure / read

buttons.c

GPIO polling + debounce

BTN_1 / BTN_2 / BTN_3

encoder.c

PCNT quadrature decoder

clicks-since-last-read

touch.c / touch.h

FT5x06 via I2C

screen-pixel coordinates

buzzer.c

Bit-bang square wave

2700 Hz / 40 ms beep

driver/gpio

driver/pulse_cnt

bus_i2c (shared component)

touch_ft5x06

(hardware/common/drivers)

hw_config.h

BUTTON_n_PIN, ENCODER_A/B_PIN

BUZZER_PIN, TOUCH_* constants

BUTTON_n_PIN

ENCODER_A/B_PIN

TOUCH_I2C_* / TOUCH_RST_PIN

BUZZER_PIN

Physical Buttons 1 to 3

(active-LOW GPIO)

Rotary Encoder

(quadrature A+B)

FT5x06 Touch IC

(I2C)

Piezo Buzzer

(GPIO)

buttons.c

encoder.c

touch.c

buzzer.c

main.c

(factory app loop)


Provides debounced polling for up to three physical push-buttons (BTN_1, BTN_2, BTN_3). All buttons are configured as active-LOW GPIO inputs with internal pull-ups.

DecisionDetail
Polled, not interrupt-drivenSimplifies factory app: no queues, no ISRs
GPIO_NUM_NC guard via pin_bit()Boards without a given button define its pin as GPIO_NUM_NC; the computed bitmask drops to zero and gpio_config() is skipped entirely
50 ms debouncePress confirmed after 50 ms re-check; release also waits 50 ms before returning
button_wait_press() blocksIntentional — factory menu is sequential, not concurrent
FunctionSignatureDescription
buttons_initvoid buttons_init(void)Configures GPIO for all valid button pins as pull-up inputs. No-op if all pins are GPIO_NUM_NC.
button_is_pressedbool button_is_pressed(button_id_t btn)Returns true if the button’s GPIO reads LOW. Guards against invalid btn IDs and GPIO_NUM_NC pins.
button_wait_pressbutton_id_t button_wait_press(uint32_t timeout_ms)Blocks until a button is pressed and released, or the timeout expires. Pass 0 for infinite wait. Returns BTN_NONE on timeout.
pin_bit (static)uint64_t pin_bit(gpio_num_t pin)Returns (1ULL << pin) for valid GPIO numbers, 0ULL for GPIO_NUM_NC, avoiding compile-time negative-shift warnings.

No

Yes

No

Yes

No

Yes

button_wait_press called

timeout_ms == 0

or elapsed < timeout?

return BTN_NONE

any button LOW?

wait 20 ms

elapsed += 20

wait 50 ms debounce

still LOW?

wait for button release

20 ms polls

wait 50 ms release debounce

return button_id_t


Implements a hardware quadrature decoder using the ESP32-S3’s PCNT (Pulse Counter) peripheral. The counter accumulates raw edge counts; encoder_read() converts them to signed detent clicks and clears the counter.

DecisionDetail
PCNT, not GPIO-IRQCounts quadrature edges in hardware; zero CPU load during rotation
4 pulses per detentTypical for 100-detent encoders; adjust PULSES_PER_DETENT if steps feel wrong
±1000 count limitsPrevents counter wrap-around between polling intervals
1000 ns glitch filterMatches the main firmware’s BSP setting; suppresses contact bounce
Internal pull-upsNo external pull-up resistors on this board
Polled, no callbacksencoder_read() called from the main loop; sub-detent remainder pulses are discarded
GPIO_NUM_NC guardIf either encoder pin is invalid, s_initialized stays false and encoder_read() always returns 0
FunctionSignatureDescription
encoder_initesp_err_t encoder_init(void)Creates PCNT unit with two quadrature channels (A-edge-on-B, B-edge-on-A). Returns ESP_OK without configuring PCNT if encoder pins are GPIO_NUM_NC.
encoder_readint encoder_read(void)Returns signed click count since last call (positive = clockwise, negative = counter-clockwise). Clears counter on each non-zero read. Returns 0 if not initialized.

Channel B (edge=B, level=A)

Channel A (edge=A, level=B)

B=LOW: INCREASE

B=HIGH: DECREASE

B=LOW: DECREASE

B=HIGH: INCREASE

A=LOW: INCREASE

A=HIGH: DECREASE

A=LOW: DECREASE

A=HIGH: INCREASE

divide by PULSES_PER_DETENT (4)

Rising edge on A

PCNT count

Falling edge on A

Rising edge on B

PCNT count

Falling edge on B

Accumulated count

(cleared on read)

signed clicks


touch.c / touch.h — Capacitive Touch Driver

Section titled “touch.c / touch.h — Capacitive Touch Driver”

A thin wrapper over the shared touch_ft5x06 hardware driver component. It handles I2C bus initialisation (idempotent — safe even if main.c already started the bus for the TCA9554 I/O expander), configures the FT5x06 touch controller with board-specific geometry, and rescales raw touch coordinates to screen-pixel space.

DecisionDetail
Fixed x_max / y_maxThe FT5x06 auto-detect returns bogus values on this board; TOUCH_X_MAX / TOUCH_Y_MAX from hw_config.h are used instead
Coordinate rescalingraw_x * SCREEN_WIDTH / x_max — same formula used by all other boards’ touch wrappers
Idempotent bus_i2c_initReturns ESP_OK if the I2C port is already installed; no double-init crash
Polling-onlyNo interrupt pin processing; touch_read() is called from the main loop
[[maybe_unused]] TAGLog tag suppressed in release builds where FACTORY_LOGD compiles to nothing
typedef struct {
bool pressed; // true if the screen is currently being touched
int16_t x; // screen X coordinate in pixels, -1 if not pressed
int16_t y; // screen Y coordinate in pixels, -1 if not pressed
} touch_point_t;
FunctionSignatureDescription
touch_initvoid touch_init(void)Initialises I2C bus (idempotent) and configures FT5x06 with board-specific geometry. Sets internal s_initialized flag on success.
touch_readtouch_point_t touch_read(void)Polls FT5x06 and returns coordinates scaled to screen pixels. Returns {false, -1, -1} when not pressed or not initialised.

I2C poll

data.x / data.y

FT5x06 IC

raw X and Y

(0 to TOUCH_X/Y_MAX)

touch_ft5x06_read()

Rescale

x = raw_x x SCREEN_WIDTH / x_max

y = raw_y x SCREEN_HEIGHT / y_max

touch_point_t

{ pressed, x, y }

main.c

touch_hint_hit_test()


A minimal bit-bang square-wave generator that produces a short tactile feedback beep. No PWM hardware or LEDC timer is used — the GPIO is toggled in a tight loop using esp_rom_delay_us().

DecisionDetail
Bit-bang, not LEDCFactory app is not performance-sensitive; avoids LEDC timer conflicts with the main firmware
2700 Hz / 40 msAudible, short, and distinct — same frequency/duration as the bootloader’s beep_short() in hooks.c
GPIO_NUM_NC guardBoards without a buzzer define BUZZER_PIN as GPIO_NUM_NC; both init and beep functions return immediately
pin_bit() helperSame pattern as buttons.c — avoids compile-time negative-shift warnings on invalid pin values
FunctionSignatureDescription
buzzer_initvoid buzzer_init(void)Configures BUZZER_PIN as a GPIO output, initialised LOW. No-op if pin is GPIO_NUM_NC.
buzzer_beep_shortvoid buzzer_beep_short(void)Emits a 2700 Hz square wave for 40 ms (108 cycles). Blocks caller for approximately 40 ms. No-op if pin is GPIO_NUM_NC.
pin_bit (static)uint64_t pin_bit(gpio_num_t pin)Safe GPIO bitmask helper — identical to the one in buttons.c.
BEEP_FREQ_HZ = 2700
BEEP_DURATION = 40 ms
cycles = 2700 x 40 / 1000 = 108 square-wave cycles
half_period_us = 1 000 000 / (2 x 2700) = 185 us (approx)
Per cycle: GPIO HIGH -> delay 185 us -> GPIO LOW -> delay 185 us
Total time: 108 x 370 us = 40 ms (blocks the calling task)

Shared Components

ESP-IDF

Board-specific

esp32s3_hmi43v3_factory_app_input

gpio_config / gpio_get_level

vTaskDelay (debounce)

BUTTON_n_PIN

pcnt_new_unit / pcnt_unit_get_count

ENCODER_A/B_PIN

FACTORY_LOGD

bus_i2c_init

touch_ft5x06_configure / read

TOUCH_* constants

FACTORY_LOGD

gpio_config / gpio_set_level

esp_rom_delay_us

BUZZER_PIN

buttons.c

encoder.c

touch.c

buzzer.c

hw_config.h

Pin assignments and display geometry

factory_log.h

FACTORY_LOGD macro

driver/gpio.h

driver/pulse_cnt.h

esp_rom_sys.h

esp_rom_delay_us

freertos/task.h

vTaskDelay

touch_ft5x06

hardware/common/drivers/touch_ft5x06

bus_i2c

hardware/common/drivers/bus_i2c


All four drivers are initialised once from app_main() in esp32s3_hmi43v3_factory_app_main before the menu loop starts:

ESP-IDF / HWtouch.cencoder.cbuttons.cbuzzer.cmain.c (app_main)ESP-IDF / HWtouch.cencoder.cbuttons.cbuzzer.cmain.c (app_main)alt[pin_mask != 0]alt[ENCODER_A/B_PIN valid]Startup confirmation beepbuzzer_init()gpio_config(BUZZER_PIN, OUTPUT)buttons_init()pin_bit() x3 -> pin_maskgpio_config(pin_mask, INPUT, PULLUP)encoder_init()pcnt_new_unit() + channels A+Bpcnt_unit_enable / clear / starttouch_init()bus_i2c_init(TOUCH_I2C_PORT_IDX, ...)touch_ft5x06_configure(&config)buzzer_beep_short()

Inside the factory app menu loop, main.c polls all input drivers and dispatches actions:

buzzer.ctouch.cencoder.cbuttons.cmain.c (menu loop)buzzer.ctouch.cencoder.cbuttons.cmain.c (menu loop)alt[button pressed and debounced][timeout]alt[clicks != 0]alt[pressed]loop[Every ~20 ms (driven by button_wait_press poll interval)]button_wait_press(timeout_ms)button_id_t (BTN_1/2/3)buzzer_beep_short()dispatch_button(btn)BTN_NONEencoder_read()int clicks (positive CW / negative CCW / 0)menu_move(clicks)touch_read()touch_point_ttouch_hint_hit_test(pt)buzzer_beep_short()

All four drivers share a defensive initialisation pattern that makes the module safe on boards that lack certain peripherals. This is a cross-board convention across the entire factory app codebase:

Runtime - driver init

Build-time (hw_config.h)

invalid

valid

Pin defined as GPIO_NUM_NC

when peripheral is absent

GPIO_IS_VALID_GPIO?

or pin_mask == 0?

Return immediately

no ESP-IDF call made

Configure GPIO / PCNT / I2C

normally

This ensures the same factory binary can be compiled for any board variant by changing only hw_config.h, without scattering #ifdef guards through driver code.


The following constants from hw_config.h govern all runtime behaviour of this module. They are not defined in the input module itself:

ConstantUsed byPurpose
BUTTON_1_PIN, BUTTON_2_PIN, BUTTON_3_PINbuttons.cGPIO numbers for physical push-buttons
ENCODER_A_PIN, ENCODER_B_PINencoder.cGPIO numbers for quadrature encoder channels
BUZZER_PINbuzzer.cGPIO number for buzzer output
TOUCH_I2C_ADDR_LISTtouch.cFT5x06 I2C device address(es)
TOUCH_I2C_PORT_IDXtouch.cESP-IDF I2C port number
TOUCH_I2C_SDA_PIN, TOUCH_I2C_SCL_PINtouch.cI2C bus GPIO pins
TOUCH_I2C_FREQ_HZtouch.cI2C clock frequency
TOUCH_RST_PIN, TOUCH_IRQ_PINtouch.cFT5x06 reset and interrupt pins
TOUCH_SWAP_XY_FLAG, TOUCH_MIRROR_X_FLAG, TOUCH_MIRROR_Y_FLAGtouch.cOrientation correction flags
TOUCH_X_MAX, TOUCH_Y_MAXtouch.cFixed touch resolution (overrides FT5x06 auto-detect)
SCREEN_WIDTH, SCREEN_HEIGHTtouch.cDisplay resolution for coordinate rescaling

Every board in the BSP that carries a factory app has its own _factory_app_input module. They share the same API surface (buttons_init, button_is_pressed, button_wait_press, encoder_init, encoder_read, touch_init, touch_read, buzzer_init, buzzer_beep_short) and differ only in the touch IC selected and hw_config.h pin assignments.

Board input moduleTouch ICDisplay interfaceNotes
pibot_pendant_v1_0_factory_app_inputResistive (custom)SPI (ILI9341)Different touch wrapper
esp32s3_8048s070c_factory_app_inputGT911 (I2C)RGB parallel7” panel
esp32s3_bzm_tft35_gt911_factory_app_inputGT911 (I2C)SPI (ST7796)3.5” panel
esp32s3_hmi43v3_factory_app_input (this module)FT5x06 (I2C)I80 (RM68120)Fixed x/y max; shared I2C bus

For shared hardware driver details see touch_drivers, physical_input, and buzzer in the Hardware Peripheral Drivers documentation.