You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Merge branch 'main' of https://github.com/MicroPythonOS/MicroPythonOS
This commit is contained in:
@@ -42,6 +42,7 @@ I want application that will show big time (hour, minutes), with smaller seconds
|
||||
import lvgl as lv
|
||||
import mpos.time
|
||||
from mpos import Activity, BatteryManager
|
||||
from mpos.battery_manager import MAX_VOLTAGE, MIN_VOLTAGE
|
||||
|
||||
HISTORY_LEN = 60
|
||||
|
||||
@@ -52,71 +53,60 @@ class ShowBattery(Activity):
|
||||
|
||||
refresh_timer = None
|
||||
|
||||
# Widgets
|
||||
lbl_time = None
|
||||
lbl_sec = None
|
||||
lbl_text = None
|
||||
|
||||
bat_outline = None
|
||||
bat_fill = None
|
||||
|
||||
clear_cache_checkbox = None # Add reference to checkbox
|
||||
|
||||
history_v = []
|
||||
history_p = []
|
||||
|
||||
def onCreate(self):
|
||||
scr = lv.obj()
|
||||
main_content = lv.obj()
|
||||
main_content.set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||
main_content.set_style_pad_all(0, 0)
|
||||
main_content.set_size(lv.pct(100), lv.pct(100))
|
||||
|
||||
# --- TIME ---
|
||||
self.lbl_time = lv.label(scr)
|
||||
self.lbl_time.set_style_text_font(lv.font_montserrat_40, 0)
|
||||
self.lbl_time.align(lv.ALIGN.TOP_LEFT, 5, 5)
|
||||
# --- TOP FLEX BOX: INFORMATION ---
|
||||
|
||||
self.lbl_sec = lv.label(scr)
|
||||
self.lbl_sec.set_style_text_font(lv.font_montserrat_24, 0)
|
||||
self.lbl_sec.align_to(self.lbl_time, lv.ALIGN.OUT_RIGHT_BOTTOM, 24, -4)
|
||||
info_column = lv.obj(main_content)
|
||||
info_column.set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||
info_column.set_style_pad_all(1, 1)
|
||||
info_column.set_size(lv.pct(100), lv.SIZE_CONTENT)
|
||||
|
||||
# --- CHECKBOX ---
|
||||
self.clear_cache_checkbox = lv.checkbox(scr)
|
||||
self.lbl_datetime = lv.label(info_column)
|
||||
self.lbl_datetime.set_style_text_font(lv.font_montserrat_16, 0)
|
||||
|
||||
self.lbl_battery = lv.label(info_column)
|
||||
self.lbl_battery.set_style_text_font(lv.font_montserrat_24, 0)
|
||||
|
||||
self.lbl_battery_raw = lv.label(info_column)
|
||||
self.lbl_battery_raw.set_style_text_font(lv.font_montserrat_14, 0)
|
||||
|
||||
self.clear_cache_checkbox = lv.checkbox(info_column)
|
||||
self.clear_cache_checkbox.set_text("Real-time values")
|
||||
self.clear_cache_checkbox.align(lv.ALIGN.TOP_LEFT, 5, 50)
|
||||
|
||||
self.lbl_text = lv.label(scr)
|
||||
self.lbl_text.set_style_text_font(lv.font_montserrat_16, 0)
|
||||
self.lbl_text.align(lv.ALIGN.TOP_LEFT, 5, 80)
|
||||
# --- BOTTOM FLEX BOX: GRAPH ---
|
||||
|
||||
# --- BATTERY ICON ---
|
||||
self.bat_outline = lv.obj(scr)
|
||||
self.bat_size = 225
|
||||
self.bat_outline.set_size(80, self.bat_size)
|
||||
self.bat_outline.align(lv.ALIGN.TOP_RIGHT, -10, 10)
|
||||
self.bat_outline.set_style_border_width(2, 0)
|
||||
self.bat_outline.set_style_radius(4, 0)
|
||||
self.canvas_width = main_content.get_width()
|
||||
self.canvas_height = 100
|
||||
|
||||
self.bat_fill = lv.obj(self.bat_outline)
|
||||
self.bat_fill.align(lv.ALIGN.BOTTOM_MID, 0, -2)
|
||||
self.bat_fill.set_width(52)
|
||||
self.bat_fill.set_style_radius(2, 0)
|
||||
canvas_column = lv.obj(main_content)
|
||||
canvas_column.set_flex_flow(lv.FLEX_FLOW.COLUMN)
|
||||
canvas_column.set_style_pad_all(0, 0)
|
||||
canvas_column.set_size(self.canvas_width, self.canvas_height)
|
||||
|
||||
self.canvas = lv.canvas(canvas_column)
|
||||
self.canvas.set_size(self.canvas_width, self.canvas_height)
|
||||
buffer = bytearray(self.canvas_width * self.canvas_height * 4)
|
||||
self.canvas.set_buffer(
|
||||
buffer, self.canvas_width, self.canvas_height, lv.COLOR_FORMAT.NATIVE
|
||||
)
|
||||
|
||||
# --- CANVAS ---
|
||||
self.canvas = lv.canvas(scr)
|
||||
self.canvas.set_size(220, 100)
|
||||
self.canvas.align(lv.ALIGN.BOTTOM_LEFT, 5, -5)
|
||||
self.canvas.set_style_border_width(1, 0)
|
||||
self.canvas.set_style_bg_color(lv.color_white(), lv.PART.MAIN)
|
||||
buffer = bytearray(220 * 100 * 4)
|
||||
self.canvas.set_buffer(buffer, 220, 100, lv.COLOR_FORMAT.NATIVE)
|
||||
self.layer = lv.layer_t()
|
||||
self.canvas.init_layer(self.layer)
|
||||
|
||||
self.setContentView(scr)
|
||||
self.setContentView(main_content)
|
||||
|
||||
def draw_line(self, color, x1, y1, x2, y2):
|
||||
dsc = lv.draw_line_dsc_t()
|
||||
lv.draw_line_dsc_t.init(dsc)
|
||||
dsc.color = color
|
||||
dsc.width = 4
|
||||
dsc.width = 2
|
||||
dsc.round_end = 1
|
||||
dsc.round_start = 1
|
||||
dsc.p1 = lv.point_precise_t()
|
||||
@@ -125,17 +115,47 @@ class ShowBattery(Activity):
|
||||
dsc.p2 = lv.point_precise_t()
|
||||
dsc.p2.x = x2
|
||||
dsc.p2.y = y2
|
||||
lv.draw_line(self.layer,dsc)
|
||||
lv.draw_line(self.layer, dsc)
|
||||
self.canvas.finish_layer(self.layer)
|
||||
|
||||
def draw_graph(self):
|
||||
self.canvas.fill_bg(lv.color_white(), lv.OPA.COVER)
|
||||
self.canvas.clean()
|
||||
|
||||
w = self.canvas_width
|
||||
h = self.canvas_height
|
||||
|
||||
if len(self.history_v) < 2:
|
||||
return
|
||||
|
||||
v_range = max(MAX_VOLTAGE - MIN_VOLTAGE, 0.01)
|
||||
|
||||
for i in range(1, len(self.history_v)):
|
||||
x1 = int((i - 1) * w / HISTORY_LEN)
|
||||
x2 = int(i * w / HISTORY_LEN)
|
||||
|
||||
yv1 = h - int((self.history_v[i - 1] - MIN_VOLTAGE) / v_range * h)
|
||||
yv2 = h - int((self.history_v[i] - MIN_VOLTAGE) / v_range * h)
|
||||
|
||||
yp1 = h - int(self.history_p[i - 1] / 100 * h)
|
||||
yp2 = h - int(self.history_p[i] / 100 * h)
|
||||
|
||||
self.draw_line(DARKPINK, x1, yv1, x2, yv2)
|
||||
self.draw_line(BLACK, x1, yp1, x2, yp2)
|
||||
|
||||
def onResume(self, screen):
|
||||
super().onResume(screen)
|
||||
|
||||
def update(timer):
|
||||
# --- DATE+TIME ---
|
||||
now = mpos.time.localtime()
|
||||
|
||||
year, month, day = now[0], now[1], now[2]
|
||||
hour, minute, second = now[3], now[4], now[5]
|
||||
date = f"{now[0]}-{now[1]:02}-{now[2]:02}"
|
||||
self.lbl_datetime.set_text(
|
||||
f"{year}-{month:02}-{day:02} {hour:02}:{minute:02}:{second:02}"
|
||||
)
|
||||
|
||||
# --- BATTERY VALUES ---
|
||||
|
||||
if self.clear_cache_checkbox.get_state() & lv.STATE.CHECKED:
|
||||
# Get "real-time" values by clearing the cache before reading
|
||||
@@ -144,25 +164,27 @@ class ShowBattery(Activity):
|
||||
voltage = BatteryManager.read_battery_voltage()
|
||||
percent = BatteryManager.get_battery_percentage()
|
||||
|
||||
# --- TIME ---
|
||||
self.lbl_time.set_text(f"{hour:02}:{minute:02}")
|
||||
self.lbl_sec.set_text(f":{second:02}")
|
||||
|
||||
# --- BATTERY VALUES ---
|
||||
date += f"\n{voltage:.2f}V {percent:.0f}%"
|
||||
date += f"\nRaw ADC: {BatteryManager.read_raw_adc()}"
|
||||
self.lbl_text.set_text(date)
|
||||
|
||||
# --- BATTERY ICON ---
|
||||
fill_h = int((percent / 100) * (self.bat_size * 0.9))
|
||||
self.bat_fill.set_height(fill_h)
|
||||
|
||||
if percent >= 30:
|
||||
self.bat_fill.set_style_bg_color(lv.palette_main(lv.PALETTE.GREEN), 0)
|
||||
if percent > 80:
|
||||
symbol = lv.SYMBOL.BATTERY_FULL
|
||||
elif percent > 60:
|
||||
symbol = lv.SYMBOL.BATTERY_3
|
||||
elif percent > 40:
|
||||
symbol = lv.SYMBOL.BATTERY_2
|
||||
elif percent > 20:
|
||||
symbol = lv.SYMBOL.BATTERY_1
|
||||
else:
|
||||
self.bat_fill.set_style_bg_color(lv.palette_main(lv.PALETTE.RED), 0)
|
||||
symbol = lv.SYMBOL.BATTERY_EMPTY
|
||||
|
||||
# --- HISTORY ---
|
||||
self.lbl_battery.set_text(f"{symbol} {voltage:.2f}V {percent:.0f}%")
|
||||
if percent >= 30:
|
||||
bg_color = lv.PALETTE.GREEN
|
||||
else:
|
||||
bg_color = lv.PALETTE.RED
|
||||
self.lbl_battery.set_style_text_color(lv.palette_main(bg_color), 0)
|
||||
|
||||
self.lbl_battery_raw.set_text(f"Raw ADC: {BatteryManager.read_raw_adc()}")
|
||||
|
||||
# --- HISTORY GRAPH ---
|
||||
self.history_v.append(voltage)
|
||||
self.history_p.append(percent)
|
||||
|
||||
@@ -174,33 +196,6 @@ class ShowBattery(Activity):
|
||||
|
||||
self.refresh_timer = lv.timer_create(update, 1000, None)
|
||||
|
||||
def draw_graph(self):
|
||||
self.canvas.fill_bg(lv.color_white(), lv.OPA.COVER)
|
||||
self.canvas.clean()
|
||||
|
||||
w = self.canvas.get_width()
|
||||
h = self.canvas.get_height()
|
||||
|
||||
if len(self.history_v) < 2:
|
||||
return
|
||||
|
||||
v_min = 3.3
|
||||
v_max = 4.2
|
||||
v_range = max(v_max - v_min, 0.01)
|
||||
|
||||
for i in range(1, len(self.history_v)):
|
||||
x1 = int((i - 1) * w / HISTORY_LEN)
|
||||
x2 = int(i * w / HISTORY_LEN)
|
||||
|
||||
yv1 = h - int((self.history_v[i - 1] - v_min) / v_range * h)
|
||||
yv2 = h - int((self.history_v[i] - v_min) / v_range * h)
|
||||
|
||||
yp1 = h - int(self.history_p[i - 1] / 100 * h)
|
||||
yp2 = h - int(self.history_p[i] / 100 * h)
|
||||
|
||||
self.draw_line(DARKPINK, x1, yv1, x2, yv2)
|
||||
self.draw_line(BLACK, x1, yp1, x2, yp2)
|
||||
|
||||
def onPause(self, screen):
|
||||
super().onPause(screen)
|
||||
if self.refresh_timer:
|
||||
|
||||
@@ -2,42 +2,66 @@
|
||||
# Manufacturer's website at https://https://docs.m5stack.com/en/core/fire_v2.7
|
||||
# Original author: https://github.com/ancebfer
|
||||
|
||||
import time
|
||||
|
||||
import drivers.display.ili9341 as ili9341
|
||||
import lcd_bus
|
||||
import machine
|
||||
|
||||
import lvgl as lv
|
||||
import task_handler
|
||||
|
||||
import machine
|
||||
import mpos.ui
|
||||
import mpos.ui.focus_direction
|
||||
from mpos import InputManager
|
||||
from machine import PWM, Pin
|
||||
from micropython import const
|
||||
from mpos import AudioManager, InputManager
|
||||
|
||||
# Pin configuration
|
||||
SPI_BUS = 1 # SPI2
|
||||
SPI_FREQ = 40000000
|
||||
LCD_SCLK = 18
|
||||
LCD_MOSI = 23
|
||||
LCD_DC = 27
|
||||
LCD_CS = 14
|
||||
LCD_BL = 32
|
||||
LCD_RST = 33
|
||||
LCD_TYPE = 2 # ILI9341 type 2
|
||||
# Display settings:
|
||||
SPI_BUS = const(1) # SPI2
|
||||
SPI_FREQ = const(40000000)
|
||||
|
||||
TFT_HOR_RES=320
|
||||
TFT_VER_RES=240
|
||||
LCD_SCLK = const(18)
|
||||
LCD_MOSI = const(23)
|
||||
LCD_DC = const(27)
|
||||
LCD_CS = const(14)
|
||||
LCD_BL = const(32)
|
||||
LCD_RST = const(33)
|
||||
LCD_TYPE = const(2) # ILI9341 type 2
|
||||
|
||||
TFT_HOR_RES = const(320)
|
||||
TFT_VER_RES = const(240)
|
||||
|
||||
# Button settings:
|
||||
BUTTON_A = const(39) # A
|
||||
BUTTON_B = const(38) # B
|
||||
BUTTON_C = const(37) # C
|
||||
|
||||
# Misc settings:
|
||||
BATTERY_PIN = const(35)
|
||||
|
||||
# Buzzer
|
||||
BUZZER_PIN = const(25)
|
||||
|
||||
|
||||
print("m5stack_fire.py init buzzer")
|
||||
buzzer = PWM(Pin(BUZZER_PIN, Pin.OUT, value=1), duty=5)
|
||||
AudioManager(i2s_pins=None, buzzer_instance=buzzer)
|
||||
AudioManager.set_volume(40)
|
||||
AudioManager.play_rtttl("Star Trek:o=4,d=20,b=200:8f.,a#,4d#6.,8d6,a#.,g.,c6.,4f6")
|
||||
while AudioManager.is_playing():
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
print("m5stack_fire.py machine.SPI.Bus() initialization")
|
||||
try:
|
||||
spi_bus = machine.SPI.Bus(host=SPI_BUS, 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()
|
||||
|
||||
|
||||
display_bus = lcd_bus.SPIBus(spi_bus=spi_bus, freq=SPI_FREQ, dc=LCD_DC, cs=LCD_CS)
|
||||
|
||||
spi_bus = machine.SPI.Bus(
|
||||
host=SPI_BUS,
|
||||
mosi=LCD_MOSI,
|
||||
sck=LCD_SCLK
|
||||
)
|
||||
display_bus = lcd_bus.SPIBus(
|
||||
spi_bus=spi_bus,
|
||||
freq=SPI_FREQ,
|
||||
dc=LCD_DC,
|
||||
cs=LCD_CS
|
||||
)
|
||||
|
||||
# M5Stack-Fire ILI9342 uses ILI9341 type 2 with a modified orientation table.
|
||||
class ILI9341(ili9341.ILI9341):
|
||||
@@ -45,9 +69,10 @@ class ILI9341(ili9341.ILI9341):
|
||||
0x00,
|
||||
0x40 | 0x20, # _MADCTL_MX | _MADCTL_MV
|
||||
0x80 | 0x40, # _MADCTL_MY | _MADCTL_MX
|
||||
0x80 | 0x20 # _MADCTL_MY | _MADCTL_MV
|
||||
0x80 | 0x20, # _MADCTL_MY | _MADCTL_MV
|
||||
)
|
||||
|
||||
|
||||
mpos.ui.main_display = ILI9341(
|
||||
data_bus=display_bus,
|
||||
display_width=TFT_HOR_RES,
|
||||
@@ -58,7 +83,7 @@ mpos.ui.main_display = ILI9341(
|
||||
reset_pin=LCD_RST,
|
||||
reset_state=ili9341.STATE_LOW,
|
||||
backlight_pin=LCD_BL,
|
||||
backlight_on_state=ili9341.STATE_PWM
|
||||
backlight_on_state=ili9341.STATE_PWM,
|
||||
)
|
||||
mpos.ui.main_display.init(LCD_TYPE)
|
||||
mpos.ui.main_display.set_power(True)
|
||||
@@ -68,12 +93,9 @@ mpos.ui.main_display.set_backlight(25)
|
||||
lv.init()
|
||||
|
||||
# Button handling code:
|
||||
from machine import Pin
|
||||
import time
|
||||
|
||||
btn_a = Pin(39, Pin.IN, Pin.PULL_UP) # A
|
||||
btn_b = Pin(38, Pin.IN, Pin.PULL_UP) # B
|
||||
btn_c = Pin(37, Pin.IN, Pin.PULL_UP) # C
|
||||
btn_a = Pin(BUTTON_A, Pin.IN, Pin.PULL_UP) # A
|
||||
btn_b = Pin(BUTTON_B, Pin.IN, Pin.PULL_UP) # B
|
||||
btn_c = Pin(BUTTON_C, Pin.IN, Pin.PULL_UP) # C
|
||||
|
||||
# Key repeat configuration
|
||||
# This whole debounce logic is only necessary because LVGL 9.2.2 seems to have an issue where
|
||||
|
||||
@@ -12,9 +12,9 @@ import lcd_bus
|
||||
import lvgl as lv
|
||||
import machine
|
||||
import mpos.ui
|
||||
from machine import ADC, Pin
|
||||
from machine import ADC, PWM, Pin
|
||||
from micropython import const
|
||||
from mpos import InputManager
|
||||
from mpos import AudioManager, BatteryManager, InputManager
|
||||
|
||||
# Display settings:
|
||||
SPI_HOST = const(1)
|
||||
@@ -49,14 +49,26 @@ CROSSBAR_Y = const(35)
|
||||
# Misc settings:
|
||||
LED_BLUE = const(2)
|
||||
BATTERY_PIN = const(36)
|
||||
SPEAKER_ENABLE_PIN = const(25)
|
||||
SPEAKER_PIN = const(26)
|
||||
|
||||
# Buzzer
|
||||
BUZZER_PIN = const(26)
|
||||
BUZZER_DAC_PIN = const(25)
|
||||
BUZZER_TONE_CHANNEL = const(0)
|
||||
|
||||
|
||||
print("odroid_go.py turn on blue LED")
|
||||
blue_led = machine.Pin(LED_BLUE, machine.Pin.OUT)
|
||||
blue_led.on()
|
||||
|
||||
print("odroid_go.py init buzzer")
|
||||
buzzer = PWM(Pin(BUZZER_PIN, Pin.OUT, value=1), duty=5)
|
||||
dac_pin = Pin(BUZZER_DAC_PIN, Pin.OUT, value=1)
|
||||
dac_pin.value(1) # Unmute
|
||||
AudioManager(i2s_pins=None, buzzer_instance=buzzer)
|
||||
AudioManager.set_volume(40)
|
||||
AudioManager.play_rtttl("Star Trek:o=4,d=20,b=200:8f.,a#,4d#6.,8d6,a#.,g.,c6.,4f6")
|
||||
while AudioManager.is_playing():
|
||||
time.sleep(0.1)
|
||||
|
||||
print("odroid_go.py machine.SPI.Bus() initialization")
|
||||
try:
|
||||
@@ -102,24 +114,23 @@ lv.init()
|
||||
|
||||
|
||||
print("odroid_go.py Battery initialization...")
|
||||
from mpos import BatteryManager
|
||||
|
||||
|
||||
def adc_to_voltage(raw_adc_value):
|
||||
"""
|
||||
The percentage calculation uses MIN_VOLTAGE = 3.15 and MAX_VOLTAGE = 4.15
|
||||
0% at 3.15V -> raw_adc_value = 270
|
||||
0% at 3.15V -> raw_adc_value = 210
|
||||
100% at 4.15V -> raw_adc_value = 310
|
||||
|
||||
4.15 - 3.15 = 1V
|
||||
310 - 270 = 40 raw ADC steps
|
||||
310 - 210 = 100 raw ADC steps
|
||||
|
||||
So each raw ADC step is 1V / 40 = 0.025V
|
||||
So each raw ADC step is 1V / 100 = 0.01V
|
||||
Offset calculation:
|
||||
270 * 0.025 = 6.75V. but we want it to be 3.15V
|
||||
So the offset is 3.15V - 6.75V = -3.6V
|
||||
210 * 0.01 = 2.1V. but we want it to be 3.15V
|
||||
So the offset is 3.15V - 2.1V = 1.05V
|
||||
"""
|
||||
voltage = raw_adc_value * 0.025 - 3.6
|
||||
voltage = raw_adc_value * 0.01 + 1.05
|
||||
return voltage
|
||||
|
||||
|
||||
@@ -198,6 +209,13 @@ def input_callback(indev, data):
|
||||
elif button_volume.value() == 0:
|
||||
print("Volume button pressed -> reset")
|
||||
blue_led.on()
|
||||
AudioManager.play_rtttl(
|
||||
"Outro:o=5,d=32,b=160,b=160:c6,b,a,g,f,e,d,c",
|
||||
stream_type=AudioManager.STREAM_ALARM,
|
||||
volume=40,
|
||||
)
|
||||
while AudioManager.is_playing():
|
||||
time.sleep(0.1)
|
||||
machine.reset()
|
||||
elif button_select.value() == 0:
|
||||
current_key = lv.KEY.BACKSPACE
|
||||
|
||||
@@ -104,20 +104,28 @@ def detect_board():
|
||||
if i2c0 := fail_save_i2c(sda=21, scl=22):
|
||||
if single_address_i2c_scan(i2c0, 0x68): # IMU (MPU6886)
|
||||
return "m5stack_fire"
|
||||
|
||||
|
||||
import machine
|
||||
unique_id_prefix = machine.unique_id()[0]
|
||||
|
||||
print("odroid_go ?")
|
||||
if unique_id_prefix == 0x30:
|
||||
return "odroid_go"
|
||||
|
||||
print("fri3d_2024 ?")
|
||||
if i2c0 := fail_save_i2c(sda=9, scl=18):
|
||||
if single_address_i2c_scan(i2c0, 0x6B): # IMU (plus possibly the Communicator's LANA TNY at 0x38)
|
||||
return "fri3d_2024"
|
||||
|
||||
import machine
|
||||
if machine.unique_id()[0] == 0xdc: # prototype board had: dc:b4:d9:0b:7d:80
|
||||
print("fri3d_2026 ?")
|
||||
if unique_id_prefix == 0xDC: # prototype board had: dc:b4:d9:0b:7d:80
|
||||
# or: if single_address_i2c_scan(i2c0, 0x6A): # IMU currently not installed on prototype board
|
||||
return "fri3d_2026"
|
||||
|
||||
print("odroid_go ?")
|
||||
#if check_pins(0, 13, 27, 39): # not good because it matches other boards (like fri3d_2024 and fri3d_2026)
|
||||
return "odroid_go"
|
||||
raise Exception(
|
||||
"Unknown ESP32-S3 board: couldn't detect known I2C devices or unique_id prefix"
|
||||
)
|
||||
|
||||
|
||||
# EXECUTION STARTS HERE
|
||||
|
||||
|
||||
Reference in New Issue
Block a user