diff --git a/README.md b/README.md index b7b3d6e9e..b273d587c 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,9 @@ poetry run python simulator.py --device maixpy_amigo_tft # Run simulator with sd enabled and the small button-only m5stick, then use keyboard (arrow keys UP or DOWN and ENTER) poetry run python simulator.py --device maixpy_m5stickv --sd + +# Run simulator with the rotary encoder device dock, then use keyboard (arrow keys UP or DOWN and ENTER) +poetry run python simulator.py --device maixpy_dock ``` To be able to emulate a SD card, first create a folder called `sd` inside `simulator` folder. diff --git a/i18n/translations/de-DE.json b/i18n/translations/de-DE.json index 75c8d2491..9dd9c2fbb 100644 --- a/i18n/translations/de-DE.json +++ b/i18n/translations/de-DE.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Geräte-Flash-Speicher nicht erkannt.", "Done?": "Fertig?", "Driver": "Driver", + "Encoder": "Encoder", + "Encoder Debounce": "Encoder-Entprellung", "Encrypt Mnemonic": "Verschlüssel Mnemonic", "Encrypted QR Code": "Verschlüsselter QR-Code", "Encrypted mnemonic was not stored": "Verschlüsselte Mnemonic wurde nicht gespeichert", diff --git a/i18n/translations/en-US.json b/i18n/translations/en-US.json index e878fbceb..36f6853e7 100644 --- a/i18n/translations/en-US.json +++ b/i18n/translations/en-US.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Device flash storage not detected.", "Done?": "Done?", "Driver": "Driver", + "Encoder": "Encoder", + "Encoder Debounce": "Encoder Debounce", "Encrypt Mnemonic": "Encrypt Mnemonic", "Encrypted QR Code": "Encrypted QR Code", "Encrypted mnemonic was not stored": "Encrypted mnemonic was not stored", diff --git a/i18n/translations/es-MX.json b/i18n/translations/es-MX.json index db544d593..6c18f1aab 100644 --- a/i18n/translations/es-MX.json +++ b/i18n/translations/es-MX.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Almacenamiento flash del dispositivo no detectado.", "Done?": "¿Listo?", "Driver": "Operador", + "Encoder": "Codificador", + "Encoder Debounce": "Antirrebote del codificador", "Encrypt Mnemonic": "Cifrado mnemónico", "Encrypted QR Code": "Código QR cifrado", "Encrypted mnemonic was not stored": "Mnemonic cifrado no se almacenó", diff --git a/i18n/translations/fr-FR.json b/i18n/translations/fr-FR.json index e8631700b..80a915d22 100644 --- a/i18n/translations/fr-FR.json +++ b/i18n/translations/fr-FR.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Stockage flash de l'appareil non détecté.", "Done?": "Terminé?", "Driver": "Conducteur", + "Encoder": "Encodeur", + "Encoder Debounce": "Anti-rebond de l'encodeur", "Encrypt Mnemonic": "Crypter mnémonique", "Encrypted QR Code": "Code QR crypté", "Encrypted mnemonic was not stored": "Le mnémonique crypté n'a pas été stocké", diff --git a/i18n/translations/nl-NL.json b/i18n/translations/nl-NL.json index 08cee6de1..1af16695b 100644 --- a/i18n/translations/nl-NL.json +++ b/i18n/translations/nl-NL.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Opslag op apparaat is niet gedetecteerd.", "Done?": "Klaar?", "Driver": "Driver", + "Encoder": "Encoder", + "Encoder Debounce": "Encoder-debounce", "Encrypt Mnemonic": "Geheugensteun versleutelen", "Encrypted QR Code": "Versleutelde QR code", "Encrypted mnemonic was not stored": "Versleutelde geheugensteun was niet opgeslagen", diff --git a/i18n/translations/pt-BR.json b/i18n/translations/pt-BR.json index 0061f2435..23a8bb83c 100644 --- a/i18n/translations/pt-BR.json +++ b/i18n/translations/pt-BR.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Armazenamento flash do dispositivo não detectado.", "Done?": "Feito?", "Driver": "Driver", + "Encoder": "Codificador", + "Encoder Debounce": "Espera do codificador", "Encrypt Mnemonic": "Criptografar mnemônico", "Encrypted QR Code": "Código QR criptografado", "Encrypted mnemonic was not stored": "Mnemonic criptografado não foi armazenado", diff --git a/i18n/translations/vi-VN.json b/i18n/translations/vi-VN.json index 7f9432cac..cf8ceab72 100644 --- a/i18n/translations/vi-VN.json +++ b/i18n/translations/vi-VN.json @@ -54,6 +54,8 @@ "Device flash storage not detected.": "Không phát hiện bộ lưu trữ flash của thiết bị.", "Done?": "Hoàn tất?", "Driver": "Người cầm lái", + "Encoder": "Mã hoá", + "Encoder Debounce": "Gỡ lỗi bộ mã hóa", "Encrypt Mnemonic": "Mã hóa Mnemonic", "Encrypted QR Code": "Mã QR được mã hóa", "Encrypted mnemonic was not stored": "MNemon được mã hóa không được lưu trữ", diff --git a/simulator/kruxsim/devices.py b/simulator/kruxsim/devices.py index fc2cdbe30..2a4f5d826 100644 --- a/simulator/kruxsim/devices.py +++ b/simulator/kruxsim/devices.py @@ -26,12 +26,14 @@ AMIGO_IPS = "maixpy_amigo_ips" AMIGO_TFT = "maixpy_amigo_tft" PC = "maixpy_pc" +DOCK = "maixpy_dock" WINDOW_SIZES = { M5STICKV: (320, 640), AMIGO_IPS: (480, 768), AMIGO_TFT: (480, 768), PC: (480, 640), + DOCK: (440, 640), } @@ -46,6 +48,8 @@ def load_image(device): device = with_prefix(device) if device == PC: return None + if device == DOCK: + return None if device not in images: images[device] = pg.image.load( os.path.join("assets", "%s.png" % device) @@ -63,6 +67,10 @@ def load_font(device): fonts[device] = pg.freetype.Font( os.path.join("..", "firmware", "font", "ter-u14n.bdf") ) + elif device == DOCK: + fonts[device] = pg.freetype.Font( + os.path.join("..", "firmware", "font", "ter-u16n.bdf") + ) else: fonts[device] = pg.freetype.Font( os.path.join("..", "firmware", "font", "ter-u24b.bdf") diff --git a/simulator/kruxsim/mocks/board.py b/simulator/kruxsim/mocks/board.py index d01a5edfa..170fb6fe6 100644 --- a/simulator/kruxsim/mocks/board.py +++ b/simulator/kruxsim/mocks/board.py @@ -57,6 +57,9 @@ def register_device(device): sys.modules["board"] = mock.MagicMock(config=BOARD_CONFIG) BUTTON_A = BOARD_CONFIG["krux"]["pins"]["BUTTON_A"] + if "ENCODER" in BOARD_CONFIG["krux"]["pins"]: + del BOARD_CONFIG["krux"]["pins"]["ENCODER"] + BOARD_CONFIG["krux"]["pins"]["BUTTON_B"] = 37 BUTTON_B = BOARD_CONFIG["krux"]["pins"]["BUTTON_B"] if "BUTTON_C" in BOARD_CONFIG["krux"]["pins"]: BUTTON_C = BOARD_CONFIG["krux"]["pins"]["BUTTON_C"] diff --git a/simulator/kruxsim/mocks/rotary.py b/simulator/kruxsim/mocks/rotary.py new file mode 100644 index 000000000..f83ed88f9 --- /dev/null +++ b/simulator/kruxsim/mocks/rotary.py @@ -0,0 +1,36 @@ +# The MIT License (MIT) + +# Copyright (c) 2021-2022 Krux contributors + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +import sys +from unittest import mock + +class RotaryEncoder: + def __init__(self): + self.debounce = 0 + pass + + +encoder = RotaryEncoder() # Singleton + +if "krux.rotary" not in sys.modules: + sys.modules["krux.rotary"] = mock.MagicMock( + RotaryEncoder=RotaryEncoder, + ) diff --git a/simulator/simulator.py b/simulator/simulator.py index 43fef3815..c5c34ffc4 100644 --- a/simulator/simulator.py +++ b/simulator/simulator.py @@ -96,6 +96,8 @@ from kruxsim.mocks import ft6x36 from kruxsim.sequence import SequenceExecutor +from kruxsim.mocks import rotary + sequence_executor = None if args.sequence: sequence_executor = SequenceExecutor(args.sequence) @@ -126,6 +128,10 @@ def run_krux(): device_image = devices.load_image(args.device) +if(args.device == devices.PC): + from kruxsim.mocks.board import BOARD_CONFIG + BOARD_CONFIG["type"] = "amigo_type" + t.start() diff --git a/src/boot.py b/src/boot.py index 325542436..ccf17c68c 100644 --- a/src/boot.py +++ b/src/boot.py @@ -30,7 +30,7 @@ from krux.power import power_manager -MIN_SPLASH_TIME = 1000 +MIN_SPLASH_WAIT_TIME = 1000 def splash(): @@ -124,8 +124,8 @@ def home(ctx_home): # If importing happened too fast, sleep the difference so the logo # will be shown -if preimport_ticks + MIN_SPLASH_TIME > postimport_ticks: - time.sleep_ms(preimport_ticks + MIN_SPLASH_TIME - postimport_ticks) +if preimport_ticks + MIN_SPLASH_WAIT_TIME > postimport_ticks: + time.sleep_ms(preimport_ticks + MIN_SPLASH_WAIT_TIME - postimport_ticks) login(ctx) gc.collect() diff --git a/src/krux/firmware.py b/src/krux/firmware.py index 5d2061682..677b66f63 100644 --- a/src/krux/firmware.py +++ b/src/krux/firmware.py @@ -40,6 +40,8 @@ MAIN_BOOT_CONFIG_SECTOR_ADDRESS = 0x00004000 BACKUP_BOOT_CONFIG_SECTOR_ADDRESS = 0x00005000 +FLASH_IO_WAIT_TIME = 100 + def find_active_firmware(sector): """Returns a tuple of the active firmware's configuration""" @@ -128,12 +130,12 @@ def write_data( cur_address = i * chunk_size + address flash.erase(cur_address, chunk_size) - time.sleep_ms(100) + time.sleep_ms(FLASH_IO_WAIT_TIME) if header and i == 0: flash.write(cur_address, b"\x00" + data_size.to_bytes(4, "little")) - time.sleep_ms(100) + time.sleep_ms(FLASH_IO_WAIT_TIME) flash.write(cur_address + header_offset, buffer[:chunk_size_after_header]) - time.sleep_ms(100) + time.sleep_ms(FLASH_IO_WAIT_TIME) i += 1 num_read = 0 chunk_read = 0 diff --git a/src/krux/input.py b/src/krux/input.py index 14b227a41..19d95eb1b 100644 --- a/src/krux/input.py +++ b/src/krux/input.py @@ -41,6 +41,8 @@ PRESSED = 0 RELEASED = 1 +BUTTON_WAIT_PRESS_DELAY = 10 + class Input: """Input is a singleton interface for interacting with the device's buttons""" @@ -167,7 +169,7 @@ def wait_for_press(self, block=True, wait_duration=QR_ANIM_PERIOD): wdt.feed() # here is where krux spends most of its time if not block and time.ticks_ms() > start_time + wait_duration: return None - time.sleep_ms(10) + time.sleep_ms(BUTTON_WAIT_PRESS_DELAY) def wait_for_button(self, block=True): """Waits for any button to release, optionally blocking if block=True. diff --git a/src/krux/krux_settings.py b/src/krux/krux_settings.py index c6a388323..8c895e1b5 100644 --- a/src/krux/krux_settings.py +++ b/src/krux/krux_settings.py @@ -247,6 +247,19 @@ def label(self, attr): }[attr] +class EncoderSettings(SettingsNamespace): + """Encoder debounce settings""" + + namespace = "settings.encoder" + debounce = NumberSetting(int, "debounce", 50, [25, 250]) + + def label(self, attr): + """Returns a label for UI when given a setting name or namespace""" + return { + "debounce": t("Encoder Debounce"), + }[attr] + + class TouchSettings(SettingsNamespace): """Touch sensitivity settings""" @@ -333,6 +346,8 @@ def __init__(self): self.appearance = ThemeSettings() if board.config["type"].startswith("amigo"): self.touch = TouchSettings() + if board.config["type"] == "dock": + self.encoder = EncoderSettings() def label(self, attr): """Returns a label for UI when given a setting name or namespace""" @@ -347,4 +362,6 @@ def label(self, attr): } if board.config["type"].startswith("amigo"): main_menu["touchscreen"] = t("Touchscreen") + if board.config["type"] == "dock": + main_menu["encoder"] = t("Encoder") return main_menu[attr] diff --git a/src/krux/pages/__init__.py b/src/krux/pages/__init__.py index fc3ef10e9..a93179b73 100644 --- a/src/krux/pages/__init__.py +++ b/src/krux/pages/__init__.py @@ -49,6 +49,7 @@ ANTI_GLARE_WAIT_TIME = 500 QR_CODE_STEP_TIME = 100 CAMERA_INIT_TIME = 1000 +SHUTDOWN_WAIT_TIME = 300 TOGGLE_BRIGHTNESS = (BUTTON_PAGE, BUTTON_PAGE_PREV) PROCEED = (BUTTON_ENTER, BUTTON_TOUCH) @@ -468,7 +469,7 @@ def shutdown(self): if self.prompt(t("Are you sure?"), self.ctx.display.height() // 2): self.ctx.display.clear() self.ctx.display.draw_centered_text(t("Shutting down..")) - time.sleep(0.3) + time.sleep_ms(SHUTDOWN_WAIT_TIME) return MENU_SHUTDOWN return MENU_CONTINUE diff --git a/src/krux/pages/settings_page.py b/src/krux/pages/settings_page.py index 2cc47f573..9639fd2e3 100644 --- a/src/krux/pages/settings_page.py +++ b/src/krux/pages/settings_page.py @@ -21,7 +21,6 @@ # THE SOFTWARE. # pylint: disable=C2801 -import time from ..themes import theme, RED, GREEN, ORANGE, MAGENTA from ..settings import ( CategorySetting, @@ -30,7 +29,13 @@ FLASH_PATH, Store, ) -from ..krux_settings import Settings, LoggingSettings, BitcoinSettings, TouchSettings +from ..krux_settings import ( + Settings, + LoggingSettings, + BitcoinSettings, + TouchSettings, + EncoderSettings, +) from ..input import BUTTON_ENTER, BUTTON_PAGE, BUTTON_PAGE_PREV, BUTTON_TOUCH from ..krux_settings import t from ..sd_card import SDHandler @@ -115,8 +120,6 @@ def _display_centered_text( """Display a text for duration ms or until you press a button""" self.ctx.display.clear() self.ctx.display.draw_centered_text(message, color, bg_color) - # this sleep protect form a double input at the same time (ENTER + PAGE on a rotary encoder) - time.sleep_ms(WAIT_TO_CHECK_INPUT) self.ctx.input.wait_for_press(block=False, wait_duration=duration) self.ctx.display.clear() @@ -237,18 +240,27 @@ def handler(): self.number_setting(settings_namespace, setting) if settings_namespace.namespace == TouchSettings.namespace: self._touch_threshold_exit_check() + elif settings_namespace.namespace == EncoderSettings.namespace: + self._encoder_threshold_exit_check() return MENU_CONTINUE return handler def _touch_threshold_exit_check(self): - """Handler for the 'Back' on settings screen""" + """Handler for the 'Back' on touch settings screen""" # Update touch detection threshold if self.ctx.input.touch is not None: self.ctx.input.touch.touch_driver.threshold(Settings().touch.threshold) + def _encoder_threshold_exit_check(self): + """Handler for the 'Back' on encoder settings screen""" + from ..rotary import encoder + + # Update rotary encoder debounce time + encoder.debounce = Settings().encoder.debounce + def category_setting(self, settings_namespace, setting): """Handler for viewing and editing a CategorySetting""" categories = setting.categories diff --git a/src/krux/pages/tiny_seed.py b/src/krux/pages/tiny_seed.py index a3502e674..778dc4adb 100644 --- a/src/krux/pages/tiny_seed.py +++ b/src/krux/pages/tiny_seed.py @@ -967,7 +967,7 @@ def scanner(self, w24=False): self._set_camera_sensitivity() full_screen = self._run_camera() postcamera_ticks = time.ticks_ms() - # check how much ms camera took to retain message on the screen + # check how much time camera took to retain message on the screen if precamera_ticks + FLASH_MSG_TIME > postcamera_ticks: time.sleep_ms(precamera_ticks + FLASH_MSG_TIME - postcamera_ticks) del message, precamera_ticks, postcamera_ticks diff --git a/src/krux/printers/thermal.py b/src/krux/printers/thermal.py index 9aa7f6343..3c8fc4e1f 100644 --- a/src/krux/printers/thermal.py +++ b/src/krux/printers/thermal.py @@ -46,6 +46,8 @@ from ..wdt import wdt from . import Printer +INITIALIZE_WAIT_TIME = 500 + class AdafruitPrinter(Printer): """AdafruitPrinter is a minimal wrapper around a serial connection to @@ -78,7 +80,7 @@ def setup(self): # upon power up -- it needs a moment to cold boot # and initialize. Allow at least 1/2 sec of uptime # before printer can receive data. - time.sleep_ms(500) + time.sleep_ms(INITIALIZE_WAIT_TIME) # Wake up the printer to get ready for printing self.write_bytes(255) diff --git a/src/krux/rotary.py b/src/krux/rotary.py index 9d528356c..e8b50d35d 100644 --- a/src/krux/rotary.py +++ b/src/krux/rotary.py @@ -24,11 +24,11 @@ from Maix import GPIO from fpioa_manager import fm import time +from .krux_settings import Settings from .logging import logger as log RIGHT = 1 LEFT = 0 -DEBOUNCE = 50 # milliseconds def __handler__(pin_num=None): @@ -55,6 +55,8 @@ def __init__(self): self.value = 0 self.time_frame = 0 + self.debounce = Settings().encoder.debounce + log.info("Encoder Initiated Pins: %d and %d" % (pins[0], pins[1])) def process(self, new_state): @@ -62,14 +64,14 @@ def process(self, new_state): def _right(): if self.direction: - if time.ticks_ms() > self.time_frame + DEBOUNCE: + if time.ticks_ms() > self.time_frame + self.debounce: self.value += 1 self.time_frame = time.ticks_ms() self.direction = RIGHT def _left(): if not self.direction: - if time.ticks_ms() > self.time_frame + DEBOUNCE: + if time.ticks_ms() > self.time_frame + self.debounce: self.value -= 1 self.time_frame = time.ticks_ms() self.direction = LEFT diff --git a/src/krux/translations.py b/src/krux/translations.py index 26f91ea7e..79a545b0c 100644 --- a/src/krux/translations.py +++ b/src/krux/translations.py @@ -77,6 +77,8 @@ 1230133196: "Geräte-Flash-Speicher nicht erkannt.", 3836852788: "Fertig?", 382368239: "Driver", + 3978947916: "Encoder", + 4090746898: "Encoder-Entprellung", 374684711: "Verschlüssel Mnemonic", 1244124409: "Verschlüsselter QR-Code", 2968548114: "Verschlüsselte Mnemonic wurde nicht gespeichert", @@ -320,6 +322,8 @@ 1230133196: "Device flash storage not detected.", 3836852788: "Done?", 382368239: "Driver", + 3978947916: "Encoder", + 4090746898: "Encoder Debounce", 374684711: "Encrypt Mnemonic", 1244124409: "Encrypted QR Code", 2968548114: "Encrypted mnemonic was not stored", @@ -563,6 +567,8 @@ 1230133196: "Almacenamiento flash del dispositivo no detectado.", 3836852788: "¿Listo?", 382368239: "Operador", + 3978947916: "Codificador", + 4090746898: "Antirrebote del codificador", 374684711: "Cifrado mnemónico", 1244124409: "Código QR cifrado", 2968548114: "Mnemonic cifrado no se almacenó", @@ -806,6 +812,8 @@ 1230133196: "Stockage flash de l'appareil non détecté.", 3836852788: "Terminé?", 382368239: "Conducteur", + 3978947916: "Encodeur", + 4090746898: "Anti-rebond de l'encodeur", 374684711: "Crypter mnémonique", 1244124409: "Code QR crypté", 2968548114: "Le mnémonique crypté n'a pas été stocké", @@ -1049,6 +1057,8 @@ 1230133196: "Opslag op apparaat is niet gedetecteerd.", 3836852788: "Klaar?", 382368239: "Driver", + 3978947916: "Encoder", + 4090746898: "Encoder-debounce", 374684711: "Geheugensteun versleutelen", 1244124409: "Versleutelde QR code", 2968548114: "Versleutelde geheugensteun was niet opgeslagen", @@ -1292,6 +1302,8 @@ 1230133196: "Armazenamento flash do dispositivo não detectado.", 3836852788: "Feito?", 382368239: "Driver", + 3978947916: "Codificador", + 4090746898: "Espera do codificador", 374684711: "Criptografar mnemônico", 1244124409: "Código QR criptografado", 2968548114: "Mnemonic criptografado não foi armazenado", @@ -1535,6 +1547,8 @@ 1230133196: "Không phát hiện bộ lưu trữ flash của thiết bị.", 3836852788: "Hoàn tất?", 382368239: "Người cầm lái", + 3978947916: "Mã hoá", + 4090746898: "Gỡ lỗi bộ mã hóa", 374684711: "Mã hóa Mnemonic", 1244124409: "Mã QR được mã hóa", 2968548114: "MNemon được mã hóa không được lưu trữ",