|
| 1 | +# Hardware initialization for ESP32 M5Stack-Fire board |
| 2 | +# Manufacturer's website at https://https://docs.m5stack.com/en/core/fire_v2.7 |
| 3 | +import ili9341 |
| 4 | +import lcd_bus |
| 5 | +import machine |
| 6 | + |
| 7 | +import lvgl as lv |
| 8 | +import task_handler |
| 9 | + |
| 10 | +import mpos.ui |
| 11 | +import mpos.ui.focus_direction |
| 12 | +from mpos import InputManager |
| 13 | + |
| 14 | +# Pin configuration |
| 15 | +SPI_BUS = 1 # SPI2 |
| 16 | +SPI_FREQ = 40000000 |
| 17 | +LCD_SCLK = 18 |
| 18 | +LCD_MOSI = 23 |
| 19 | +LCD_DC = 27 |
| 20 | +LCD_CS = 14 |
| 21 | +LCD_BL = 32 |
| 22 | +LCD_RST = 33 |
| 23 | +LCD_TYPE = 2 # ILI9341 type 2 |
| 24 | + |
| 25 | +TFT_HOR_RES=320 |
| 26 | +TFT_VER_RES=240 |
| 27 | + |
| 28 | +spi_bus = machine.SPI.Bus( |
| 29 | + host=SPI_BUS, |
| 30 | + mosi=LCD_MOSI, |
| 31 | + sck=LCD_SCLK |
| 32 | +) |
| 33 | +display_bus = lcd_bus.SPIBus( |
| 34 | + spi_bus=spi_bus, |
| 35 | + freq=SPI_FREQ, |
| 36 | + dc=LCD_DC, |
| 37 | + cs=LCD_CS |
| 38 | +) |
| 39 | + |
| 40 | +# M5Stack-Fire ILI9342 uses ILI9341 type 2 with a modified orientation table. |
| 41 | +class ILI9341(ili9341.ILI9341): |
| 42 | + _ORIENTATION_TABLE = ( |
| 43 | + 0x00, |
| 44 | + 0x40 | 0x20, # _MADCTL_MX | _MADCTL_MV |
| 45 | + 0x80 | 0x40, # _MADCTL_MY | _MADCTL_MX |
| 46 | + 0x80 | 0x20 # _MADCTL_MY | _MADCTL_MV |
| 47 | + ) |
| 48 | + |
| 49 | +mpos.ui.main_display = ILI9341( |
| 50 | + data_bus=display_bus, |
| 51 | + display_width=TFT_HOR_RES, |
| 52 | + display_height=TFT_VER_RES, |
| 53 | + color_space=lv.COLOR_FORMAT.RGB565, |
| 54 | + color_byte_order=ili9341.BYTE_ORDER_BGR, |
| 55 | + rgb565_byte_swap=True, |
| 56 | + reset_pin=LCD_RST, |
| 57 | + reset_state=ili9341.STATE_LOW, |
| 58 | + backlight_pin=LCD_BL, |
| 59 | + backlight_on_state=ili9341.STATE_PWM |
| 60 | +) |
| 61 | +mpos.ui.main_display.init(LCD_TYPE) |
| 62 | +mpos.ui.main_display.set_power(True) |
| 63 | +mpos.ui.main_display.set_color_inversion(True) |
| 64 | +mpos.ui.main_display.set_backlight(25) |
| 65 | + |
| 66 | +lv.init() |
| 67 | + |
| 68 | +# Button handling code: |
| 69 | +from machine import Pin |
| 70 | +import time |
| 71 | + |
| 72 | +btn_a = Pin(39, Pin.IN, Pin.PULL_UP) # A |
| 73 | +btn_b = Pin(38, Pin.IN, Pin.PULL_UP) # B |
| 74 | +btn_c = Pin(37, Pin.IN, Pin.PULL_UP) # C |
| 75 | + |
| 76 | +# Key repeat configuration |
| 77 | +# This whole debounce logic is only necessary because LVGL 9.2.2 seems to have an issue where |
| 78 | +# the lv_keyboard widget doesn't handle PRESSING (long presses) properly, it loses focus. |
| 79 | +REPEAT_INITIAL_DELAY_MS = 300 # Delay before first repeat |
| 80 | +REPEAT_RATE_MS = 100 # Interval between repeats |
| 81 | +last_key = None |
| 82 | +last_state = lv.INDEV_STATE.RELEASED |
| 83 | +key_press_start = 0 # Time when key was first pressed |
| 84 | +last_repeat_time = 0 # Time of last repeat event |
| 85 | + |
| 86 | +# Read callback |
| 87 | +# Warning: This gets called several times per second, and if it outputs continuous debugging on the serial line, |
| 88 | +# that will break tools like mpremote from working properly to upload new files over the serial line, thus needing a reflash. |
| 89 | +def keypad_read_cb(indev, data): |
| 90 | + global last_key, last_state, key_press_start, last_repeat_time |
| 91 | + since_last_repeat = 0 |
| 92 | + |
| 93 | + # Check buttons |
| 94 | + current_key = None |
| 95 | + current_time = time.ticks_ms() |
| 96 | + if btn_a.value() == 0: |
| 97 | + current_key = lv.KEY.PREV |
| 98 | + elif btn_b.value() == 0: |
| 99 | + current_key = lv.KEY.ENTER |
| 100 | + elif btn_c.value() == 0: |
| 101 | + current_key = lv.KEY.NEXT |
| 102 | + |
| 103 | + if (btn_a.value() == 0) and (btn_c.value() == 0): |
| 104 | + current_key = lv.KEY.ESC |
| 105 | + |
| 106 | + # Key repeat logic |
| 107 | + if current_key: |
| 108 | + if current_key != last_key: |
| 109 | + # New key press |
| 110 | + data.key = current_key |
| 111 | + data.state = lv.INDEV_STATE.PRESSED |
| 112 | + last_key = current_key |
| 113 | + last_state = lv.INDEV_STATE.PRESSED |
| 114 | + key_press_start = current_time |
| 115 | + last_repeat_time = current_time |
| 116 | + else: # same key |
| 117 | + # Key held: Check for repeat |
| 118 | + elapsed = time.ticks_diff(current_time, key_press_start) |
| 119 | + since_last_repeat = time.ticks_diff(current_time, last_repeat_time) |
| 120 | + if elapsed >= REPEAT_INITIAL_DELAY_MS and since_last_repeat >= REPEAT_RATE_MS: |
| 121 | + # Send a new PRESSED/RELEASED pair for repeat |
| 122 | + data.key = current_key |
| 123 | + data.state = lv.INDEV_STATE.PRESSED if last_state == lv.INDEV_STATE.RELEASED else lv.INDEV_STATE.RELEASED |
| 124 | + last_state = data.state |
| 125 | + last_repeat_time = current_time |
| 126 | + else: |
| 127 | + # No repeat yet, send RELEASED to avoid PRESSING |
| 128 | + data.state = lv.INDEV_STATE.RELEASED |
| 129 | + last_state = lv.INDEV_STATE.RELEASED |
| 130 | + else: |
| 131 | + # No key pressed |
| 132 | + data.key = last_key if last_key else lv.KEY.ENTER |
| 133 | + data.state = lv.INDEV_STATE.RELEASED |
| 134 | + last_key = None |
| 135 | + last_state = lv.INDEV_STATE.RELEASED |
| 136 | + key_press_start = 0 |
| 137 | + last_repeat_time = 0 |
| 138 | + |
| 139 | + # Handle ESC for back navigation (only on initial PRESSED) |
| 140 | + if last_state == lv.INDEV_STATE.PRESSED: |
| 141 | + if current_key == lv.KEY.ESC and since_last_repeat == 0: |
| 142 | + mpos.ui.back_screen() |
| 143 | + |
| 144 | +group = lv.group_create() |
| 145 | +group.set_default() |
| 146 | + |
| 147 | +# Create and set up the input device |
| 148 | +indev = lv.indev_create() |
| 149 | +indev.set_type(lv.INDEV_TYPE.KEYPAD) |
| 150 | +indev.set_read_cb(keypad_read_cb) |
| 151 | +indev.set_group(group) # is this needed? maybe better to move the default group creation to main.py so it's available everywhere... |
| 152 | +disp = lv.display_get_default() # NOQA |
| 153 | +indev.set_display(disp) # different from display |
| 154 | +indev.enable(True) # NOQA |
| 155 | +InputManager.register_indev(indev) |
| 156 | + |
| 157 | +print("m5stack_fire.py finished") |
0 commit comments