[Python] OLED keyboard with rotary encoder input

Discussão em 'Python' iniciado por Stack, Maio 5, 2025.

  1. Stack

    Stack Membro Participativo

    Im using an SSD1306 128x64 OLED, and a rotary encoder, I'm trying to set up a menu system that initially shows some basic details. there is then a menu to go back to details or enter setup mode, setup mode then displays available wifi SSIDs. once you select a SSID I want to enter a keyboard layout to enter and store a WIFI password. I'm still relatively new to python programming, but I am running struggling to fix this issue. getting the encoder to actually enter the correct menu is hit or miss, sometimes goes back to the detail screen, sometimes goes to the next screen, when you manage to get to the keyboard screen it will currently cut off the bottom layer, before it was incorrectly spacing the bottom layer. code for OLED/encoder/details below

    # === WiFi Menu Options ===
    wifi_menu_items = ["Setup", "Details"]
    wifi_menu_index = 0

    # === WiFi_Menu_Function
    def display_wifi_menu(index):
    oled.fill(0)
    oled.text("WiFi Menu", 0, 0)

    # Ensure the index is within bounds
    index = max(0, min(index, len(wifi_menu_items) - 1))

    for i, item in enumerate(wifi_menu_items):
    prefix = ">" if i == index else " "
    oled.text(prefix + item, 0, 10 + i * 10)

    oled.show()


    keyboard_pages = {
    'abc': [
    ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"],
    ["k", "l", "m", "n", "o", "p", "q", "r", "s", "t"],
    ["u", "v", "w", "x", "y", "z", "0", "1", "2", "3"],
    ["4", "5", "6", "7", "8", "9", "-", "_", ".", ","],
    ["[ABC]", "[SYM]", "[SPACE]", "[<]", "[ENT]"]
    ],
    'ABC': [
    ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"],
    ["K", "L", "M", "N", "O", "P", "Q", "R", "S", "T"],
    ["U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3"],
    ["4", "5", "6", "7", "8", "9", "-", "_", ".", ","],
    ["[abc]", "[SYM]", "[SPACE]", "[<]", "[ENT]"]
    ],
    'sym': [
    ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"],
    ["=", "+", "[", "]", "{", "}", "|", "\\", "/", "?"],
    ["<", ">", "~", "`", ":", ";", "'", '"', ",", "."],
    ["-", "_", "0", "1", "2", "3", "4", "5", "6", "7"],
    ["[abc]", "[ABC]", "[SPACE]", "[<]", "[ENT]"]
    ]
    }
    # === Updated Password Input Function ===
    def prompt_for_password():
    mode = 'abc'
    password = ""
    cursor_index = 0

    rows_per_page = 5 # Number of rows to display at once
    max_columns = 10 # Maximum number of columns we can fit
    offset = 0 # The starting row for the current screen display

    while True:
    # Get the current keyboard layout
    grid = keyboard_pages[mode]
    flat_grid = [(char, r, c) for r, row in enumerate(grid) for c, char in enumerate(row)]

    max_index = len(flat_grid) - 1 # Maximum index in the keyboard layout

    # Debugging prints to track the error
    print(f"max_index: {max_index}")
    print(f"cursor_index: {cursor_index}")
    print(f"len(flat_grid): {len(flat_grid)}")

    # Ensure cursor_index is within valid bounds
    if cursor_index > max_index:
    cursor_index = max_index # Ensure the cursor doesn't exceed the bounds
    print(f"Reset cursor_index to {cursor_index}")

    # Ensure available rows is not exceeded
    visible_rows = flat_grid[offset:eek:ffset + rows_per_page * max_columns]
    if len(visible_rows) > rows_per_page * max_columns:
    visible_rows = visible_rows[:rows_per_page * max_columns] # Truncate if out of bounds

    print(f"visible_rows length: {len(visible_rows)}")

    encoder.set(value=cursor_index, min_val=0, max_val=max_index)
    cursor_index = encoder.value()
    selected_char, sel_row, sel_col = flat_grid[cursor_index]

    oled.fill(0)

    # Show password label and password on the same row (1st row)
    oled.text(f"PW: {password[-16:]}", 0, 0) # Limit the displayed password to the last 16 chars

    # Display the visible rows (rest of the rows)
    for idx, (char, r, c) in enumerate(visible_rows):
    row_idx = idx // max_columns
    col_idx = idx % max_columns
    x = col_idx * 12 # Adjust for width of each character
    y = 12 + row_idx * 8 # Adjust for the row spacing (start below the password row)

    if r == sel_row and c == sel_col:
    oled.fill_rect(x - 1, y - 1, 12, 8, 1) # Highlight selected character
    oled.text(char, x, y, 0)
    else:
    oled.text(char, x, y, 1)

    oled.show()

    # Handle button press
    if button.value() == 0:
    if selected_char == "[<]":
    password = password[:-1]
    elif selected_char == "[SPACE]":
    password += " "
    elif selected_char == "[ENT]":
    break
    elif selected_char == "[abc]":
    mode = 'abc'
    cursor_index = 0
    elif selected_char == "[ABC]":
    mode = 'ABC'
    cursor_index = 0
    elif selected_char == "[SYM]":
    mode = 'sym'
    cursor_index = 0
    else:
    password += selected_char
    time.sleep(0.3)

    # Scrolling functionality for rows (optional)
    if encoder.value() == len(flat_grid) - 1: # If encoder reaches the last item
    offset += rows_per_page
    if offset > len(flat_grid) - rows_per_page:
    offset = 0 # Reset to top if we've reached the end
    time.sleep(0.3)

    return password


    from main, parts that handle OLED and ENCODER

    if button.value() == 0:
    menu_mode = not menu_mode
    submenu_mode = False
    time.sleep(0.25)

    if menu_mode:
    if not submenu_mode:
    # Display the Wi-Fi menu
    display_wifi_menu(wifi_menu_index)

    if button.value() == 0:
    selected = wifi_menu_index
    if wifi_menu_items[selected] == "Setup":
    wifi_ssids = scan_wifi()
    encoder.set(value=0, min_val=0, max_val=len(wifi_ssids) - 1)
    submenu_mode = True
    time.sleep(0.25)
    elif wifi_menu_items[selected] == "Details":
    # Handle the "Details" option
    print("Displaying Wi-Fi Details...")
    time.sleep(0.25)

    else:
    # Handle Wi-Fi SSID selection (submenu)
    display_wifi_selection(wifi_ssids, wifi_ssid_index)
    if button.value() == 0:
    selected_ssid = wifi_ssids[encoder.value()]
    if selected_ssid == "[Hidden SSID]":
    selected_ssid = prompt_for_password() # reuse to enter SSID
    password = prompt_for_password()
    print("Selected SSID:", selected_ssid)
    print("Entered Password:", password)
    time.sleep(0.5)
    else:
    # Handle other modes (if applicable)
    pass

    # Handle the encoder for menu navigation
    if encoder.value() == 0: # Move up
    wifi_menu_index = max(0, wifi_menu_index - 1)
    elif encoder.value() == len(wifi_menu_items) - 1: # Move down
    wifi_menu_index = min(len(wifi_menu_items) - 1, wifi_menu_index + 1)
    time.sleep(0.1)

    # Show TOTP periodically
    if time.ticks_ms() - oled_timer > 250:
    oled_timer = time.ticks_ms()
    oled.fill(0)
    oled.text("TOTP:", 0, 0)
    oled.text(current_code, 0, 10)
    oled.text("WiFi OK", 0, 20)
    oled.text(f"Dial: {encoder.value()}", 0, 30)
    oled.show()

    time.sleep(0.02)

    if __name__ == "__main__":
    main()

    Continue reading...

Compartilhe esta Página