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[offsetffset + 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...