-
Notifications
You must be signed in to change notification settings - Fork 51
Support ODROID-GO #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,253 @@ | ||
| print("odroid_go.py initialization") | ||
|
|
||
| # Hardware initialization for Hardkernel ODROID-Go | ||
| # https://github.com/hardkernel/ODROID-GO/ | ||
| # https://wiki.odroid.com/odroid_go/odroid_go | ||
|
|
||
| import time | ||
|
|
||
| import ili9341 | ||
| import lcd_bus | ||
| import lvgl as lv | ||
| import machine | ||
| import mpos.ui | ||
| from machine import ADC, Pin | ||
| from micropython import const | ||
| from mpos import InputManager | ||
|
|
||
| # Display settings: | ||
| SPI_HOST = const(1) | ||
| SPI_FREQ = const(40000000) | ||
|
|
||
| LCD_SCLK = const(18) | ||
| LCD_MOSI = const(23) | ||
| LCD_DC = const(21) | ||
| LCD_CS = const(5) | ||
| LCD_BL = const(32) | ||
| LCD_RST = const(33) | ||
| LCD_TYPE = const(2) # ILI9341 type 2 | ||
|
|
||
| TFT_VER_RES = const(320) | ||
| TFT_HOR_RES = const(240) | ||
|
|
||
|
|
||
| # Button settings: | ||
| BUTTON_MENU = const(13) | ||
| BUTTON_VOLUME = const(0) | ||
| BUTTON_SELECT = const(27) | ||
| BUTTON_START = const(39) | ||
|
|
||
| BUTTON_B = const(33) | ||
| BUTTON_A = const(32) | ||
|
|
||
| # The crossbar pin numbers: | ||
| CROSSBAR_X = const(34) | ||
| CROSSBAR_Y = const(35) | ||
|
|
||
|
|
||
| # Misc settings: | ||
| LED_BLUE = const(2) | ||
| BATTERY_PIN = const(36) | ||
| BATTERY_RESISTANCE_NUM = const(2) | ||
| SPEAKER_ENABLE_PIN = const(25) | ||
| SPEAKER_PIN = const(26) | ||
|
|
||
|
|
||
| print("odroid_go.py turn on blue LED") | ||
| blue_led = machine.Pin(LED_BLUE, machine.Pin.OUT) | ||
| blue_led.on() | ||
|
|
||
|
|
||
| print("odroid_go.py machine.SPI.Bus() initialization") | ||
| try: | ||
| spi_bus = machine.SPI.Bus(host=SPI_HOST, mosi=LCD_MOSI, sck=LCD_SCLK) | ||
| except Exception as e: | ||
| print(f"Error initializing SPI bus: {e}") | ||
| print("Attempting hard reset in 3sec...") | ||
| time.sleep(3) | ||
| machine.reset() | ||
|
|
||
| print("odroid_go.py lcd_bus.SPIBus() initialization") | ||
| display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, freq=SPI_FREQ, dc=LCD_DC, cs=LCD_CS) | ||
|
|
||
| print("odroid_go.py ili9341.ILI9341() initialization") | ||
| try: | ||
| mpos.ui.main_display = ili9341.ILI9341( | ||
| data_bus=display_bus, | ||
| display_width=TFT_HOR_RES, | ||
| display_height=TFT_VER_RES, | ||
| color_space=lv.COLOR_FORMAT.RGB565, | ||
| color_byte_order=ili9341.BYTE_ORDER_BGR, | ||
| rgb565_byte_swap=True, | ||
| reset_pin=LCD_RST, | ||
| reset_state=ili9341.STATE_LOW, | ||
| backlight_pin=LCD_BL, | ||
| backlight_on_state=ili9341.STATE_PWM, | ||
| ) | ||
| except Exception as e: | ||
| print(f"Error initializing ILI9341: {e}") | ||
| print("Attempting hard reset in 3sec...") | ||
| time.sleep(3) | ||
| machine.reset() | ||
|
|
||
| print("odroid_go.py display.init()") | ||
| mpos.ui.main_display.init(type=LCD_TYPE) | ||
| mpos.ui.main_display.set_rotation(lv.DISPLAY_ROTATION._270) | ||
| mpos.ui.main_display.set_power(True) | ||
| mpos.ui.main_display.set_color_inversion(False) | ||
| mpos.ui.main_display.set_backlight(25) | ||
|
|
||
| print("odroid_go.py lv.init() initialization") | ||
| lv.init() | ||
|
|
||
|
|
||
| print("odroid_go.py Battery initialization...") | ||
| from mpos import BatteryManager | ||
|
|
||
|
|
||
| def adc_to_voltage(adc_value): | ||
| return adc_value * BATTERY_RESISTANCE_NUM | ||
|
|
||
|
|
||
| BatteryManager.init_adc(BATTERY_PIN, adc_to_voltage) | ||
|
|
||
|
|
||
| print("odroid_go.py button initialization...") | ||
|
|
||
| button_menu = Pin(BUTTON_MENU, Pin.IN, Pin.PULL_UP) | ||
| button_volume = Pin(BUTTON_VOLUME, Pin.IN, Pin.PULL_UP) | ||
| button_select = Pin(BUTTON_SELECT, Pin.IN, Pin.PULL_UP) | ||
| button_start = Pin(BUTTON_START, Pin.IN, Pin.PULL_UP) # -> ENTER | ||
|
|
||
| # PREV <- B | A -> NEXT | ||
| button_b = Pin(BUTTON_B, Pin.IN, Pin.PULL_UP) | ||
| button_a = Pin(BUTTON_A, Pin.IN, Pin.PULL_UP) | ||
|
|
||
|
|
||
| class CrossbarHandler: | ||
| # ADC values are around low: ~236 and high ~511 | ||
| # So the mid value is around (236+511)/2 = 373.5 | ||
| CROSSBAR_MIN_ADC_LOW = const(100) | ||
| CROSSBAR_MIN_ADC_MID = const(370) | ||
|
|
||
| def __init__(self, pin, high_key, low_key): | ||
| self.adc = ADC(Pin(pin, mode=Pin.IN)) | ||
| self.adc.width(ADC.WIDTH_9BIT) | ||
| self.adc.atten(ADC.ATTN_11DB) | ||
|
|
||
| self.high_key = high_key | ||
| self.low_key = low_key | ||
|
|
||
| def poll(self): | ||
| value = self.adc.read() | ||
| if value > self.CROSSBAR_MIN_ADC_LOW: | ||
| if value > self.CROSSBAR_MIN_ADC_MID: | ||
| return self.high_key | ||
| elif value < self.CROSSBAR_MIN_ADC_MID: | ||
| return self.low_key | ||
|
|
||
|
|
||
| class Crossbar: | ||
| def __init__(self, *, up, down, left, right): | ||
| self.joy_x = CrossbarHandler(CROSSBAR_X, high_key=left, low_key=right) | ||
| self.joy_y = CrossbarHandler(CROSSBAR_Y, high_key=up, low_key=down) | ||
|
|
||
| def poll(self): | ||
| crossbar_pressed = self.joy_x.poll() or self.joy_y.poll() | ||
| return crossbar_pressed | ||
|
|
||
|
|
||
| # see: internal_filesystem/lib/mpos/indev/mpos_sdl_keyboard.py | ||
| # lv.KEY.UP | ||
| # lv.KEY.LEFT - lv.KEY.RIGHT | ||
| # lv.KEY.DOWN | ||
| # | ||
| crossbar = Crossbar( | ||
| up=lv.KEY.UP, down=lv.KEY.DOWN, left=lv.KEY.LEFT, right=lv.KEY.RIGHT | ||
| ) | ||
|
|
||
| REPEAT_INITIAL_DELAY_MS = 300 # Delay before first repeat | ||
| REPEAT_RATE_MS = 100 # Interval between repeats | ||
| next_repeat = None # Used for auto-repeat key handling | ||
|
|
||
|
|
||
| def input_callback(indev, data): | ||
| global next_repeat | ||
|
|
||
| current_key = None | ||
|
|
||
| if crossbar_pressed := crossbar.poll(): | ||
| current_key = crossbar_pressed | ||
|
|
||
| elif button_menu.value() == 0: | ||
| current_key = lv.KEY.ESC | ||
| elif button_volume.value() == 0: | ||
| print("Volume button pressed -> reset") | ||
| machine.reset() | ||
| elif button_select.value() == 0: | ||
| current_key = lv.KEY.BACKSPACE | ||
| elif button_start.value() == 0: | ||
| current_key = lv.KEY.ENTER | ||
|
|
||
| elif button_b.value() == 0: | ||
| current_key = lv.KEY.PREV | ||
| elif button_a.value() == 0: | ||
| current_key = lv.KEY.NEXT | ||
| else: | ||
| # No crossbar/buttons pressed | ||
| if data.key: # A key was previously pressed and now released | ||
| # print(f"Key {data.key=} released") | ||
| data.key = 0 | ||
| data.state = lv.INDEV_STATE.RELEASED | ||
| next_repeat = None | ||
| blue_led.off() | ||
| return | ||
|
|
||
| # A key is currently pressed | ||
|
|
||
| blue_led.on() # Blink on key press and auto repeat for feedback | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice :-D |
||
|
|
||
| current_time = time.ticks_ms() | ||
| repeat = current_time > next_repeat if next_repeat else False # Auto repeat? | ||
| if repeat or current_key != data.key: | ||
| print(f"Key {current_key} pressed {repeat=}") | ||
|
|
||
| data.key = current_key | ||
| data.state = lv.INDEV_STATE.PRESSED | ||
|
|
||
| if current_key == lv.KEY.ESC: # Handle ESC for back navigation | ||
| mpos.ui.back_screen() | ||
| elif current_key == lv.KEY.RIGHT: | ||
| mpos.ui.focus_direction.move_focus_direction(90) | ||
| elif current_key == lv.KEY.LEFT: | ||
| mpos.ui.focus_direction.move_focus_direction(270) | ||
| elif current_key == lv.KEY.UP: | ||
| mpos.ui.focus_direction.move_focus_direction(0) | ||
| elif current_key == lv.KEY.DOWN: | ||
| mpos.ui.focus_direction.move_focus_direction(180) | ||
|
|
||
| if not repeat: | ||
| # Initial press: Delay before first repeat | ||
| next_repeat = current_time + REPEAT_INITIAL_DELAY_MS | ||
| else: | ||
| # Faster auto repeat after initial press | ||
| next_repeat = current_time + REPEAT_RATE_MS | ||
| blue_led.off() # Blink the LED, too | ||
|
|
||
|
|
||
| group = lv.group_create() | ||
| group.set_default() | ||
|
|
||
| # Create and set up the input device | ||
| indev = lv.indev_create() | ||
| indev.set_type(lv.INDEV_TYPE.KEYPAD) | ||
| indev.set_read_cb(input_callback) | ||
| indev.set_group( | ||
| group | ||
| ) # is this needed? maybe better to move the default group creation to main.py so it's available everywhere... | ||
| disp = lv.display_get_default() # NOQA | ||
| indev.set_display(disp) # different from display | ||
| indev.enable(True) # NOQA | ||
| InputManager.register_indev(indev) | ||
|
|
||
| print("odroid_go.py finished") | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,38 +39,85 @@ def single_address_i2c_scan(i2c_bus, address): | |
| Returns: | ||
| True if a device responds at the specified address, False otherwise | ||
| """ | ||
| print(f"Attempt to write a single byte to I2C bus address 0x{address:02x}...") | ||
| try: | ||
| # Attempt to write a single byte to the address | ||
| # This will raise an exception if no device responds | ||
| i2c_bus.writeto(address, b'') | ||
| i2c_bus.writeto(address, b"") | ||
| print("Write test successful") | ||
| return True | ||
| except OSError: | ||
| # No device at this address | ||
| except OSError as e: | ||
| print(f"No device at this address: {e}") | ||
| return False | ||
| except Exception as e: | ||
| # Handle any other exceptions gracefully | ||
| print(f"single_address_i2c_scan: error scanning address 0x{address:02x}: {e}") | ||
| print(f"scan error: {e}") | ||
| return False | ||
|
|
||
|
|
||
| def fail_save_i2c(sda, scl): | ||
| from machine import I2C, Pin | ||
|
|
||
| print(f"Try to I2C initialized on {sda=} {scl=}") | ||
| try: | ||
| i2c0 = I2C(0, sda=Pin(sda), scl=Pin(scl)) | ||
| except Exception as e: | ||
| print(f"Failed: {e}") | ||
| return None | ||
| else: | ||
| print("OK") | ||
| return i2c0 | ||
|
|
||
|
|
||
| def check_pins(*pins): | ||
| from machine import Pin | ||
|
|
||
| print(f"Test {pins=}...") | ||
| for pin in pins: | ||
| try: | ||
| Pin(pin) | ||
| except Exception as e: | ||
| print(f"Failed to initialize {pin=}: {e}") | ||
| return True | ||
| print("All pins initialized successfully") | ||
| return True | ||
|
|
||
|
|
||
| def detect_board(): | ||
| import sys | ||
| if sys.platform == "linux" or sys.platform == "darwin": # linux and macOS | ||
| return "linux" | ||
| elif sys.platform == "esp32": | ||
| from machine import Pin, I2C | ||
|
|
||
| i2c0 = I2C(0, sda=Pin(39), scl=Pin(38)) | ||
| if single_address_i2c_scan(i2c0, 0x14) or single_address_i2c_scan(i2c0, 0x5D): # "ghost" or real GT911 touch screen | ||
| return "matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660" | ||
|
|
||
| i2c0 = I2C(0, sda=Pin(48), scl=Pin(47)) # IO48 is floating on matouch and therefore, using that for I2C will find many devices, so do this after matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 | ||
| if single_address_i2c_scan(i2c0, 0x15) and single_address_i2c_scan(i2c0, 0x6B): # CST816S touch screen and IMU | ||
| return "waveshare_esp32_s3_touch_lcd_2" | ||
|
|
||
| i2c0 = I2C(0, sda=Pin(9), scl=Pin(18)) | ||
| if single_address_i2c_scan(i2c0, 0x6B): # IMU (plus possibly the Communicator's LANA TNY at 0x38) | ||
| return "fri3d_2024" | ||
|
|
||
| print("Detecting ESP32 board by scanning I2C addresses...") | ||
|
|
||
| print("matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 ?") | ||
| if i2c0 := fail_save_i2c(sda=39, scl=38): | ||
| if single_address_i2c_scan(i2c0, 0x14) or single_address_i2c_scan( | ||
| i2c0, 0x5D | ||
| ): | ||
| # "ghost" or real GT911 touch screen | ||
| return "matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660" | ||
|
|
||
| print("waveshare_esp32_s3_touch_lcd_2 ?") | ||
| if i2c0 := fail_save_i2c(sda=48, scl=47): | ||
| # IO48 is floating on matouch and therefore, using that for I2C will find many devices, so do this after matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 | ||
| if single_address_i2c_scan(i2c0, 0x15) and single_address_i2c_scan( | ||
| i2c0, 0x6B | ||
| ): | ||
| # CST816S touch screen and IMU | ||
| return "waveshare_esp32_s3_touch_lcd_2" | ||
|
|
||
| print("odroid_go ?") | ||
| if check_pins(0, 13, 27, 39): | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| return "odroid_go" | ||
|
|
||
| print("fri3d_2024 ?") | ||
| if i2c0 := fail_save_i2c(sda=9, scl=18): | ||
| # IMU (plus possibly the Communicator's LANA TNY at 0x38) | ||
| if single_address_i2c_scan(i2c0, 0x6B): | ||
| return "fri3d_2024" | ||
|
|
||
| print("Fallback to fri3d_2026") | ||
| # default: if single_address_i2c_scan(i2c0, 0x6A): # IMU but currently not installed | ||
| return "fri3d_2026" | ||
|
|
||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This changes can also be move to a separate PR. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| [format] | ||
| quote-style = "double" | ||
ThomasFarstrike marked this conversation as resolved.
Show resolved
Hide resolved
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get these values (while connected via USB):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after a while running:
So i assume that 100% is wrong ;)