# === Import Libraries === import machine import time import uasyncio as asyncio import os import sqlite3 import onewire import ds18x20 import lvgl as lv import lvgl_helper as hlp from machine import Pin, I2C, UART, SPI import ujson as json # === Hardware Pin Definitions === OLED_SDA = 4 OLED_SCL = 15 GPS_RX = 16 GPS_TX = 17 OIL_TEMP_PIN = 12 SD_CS = 5 ENCODER_A = 10 ENCODER_B = 11 ENCODER_SW = 21 BUTTON_MENU = 22 BUTTON_BACK = 23 BUTTON_RESET = 20 # === Initialize Hardware === i2c = I2C(scl=Pin(OLED_SCL), sda=Pin(OLED_SDA), freq=400000) display = hlp.get_display(i2c, 128, 64) uart = UART(1, baudrate=9600, tx=Pin(GPS_TX), rx=Pin(GPS_RX)) uart.init(baudrate=9600, bits=8, parity=None, stop=1) ow = onewire.OneWire(Pin(OIL_TEMP_PIN)) ds = ds18x20.DS18X20(ow) spi = SPI(1, baudrate=10000000, polarity=0, phase=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) sd = machine.SDCard(spi, Pin(SD_CS)) vfs = os.VfsFat(sd) os.mount(vfs, "/sd") encoder_a = Pin(ENCODER_A, Pin.IN, Pin.PULL_UP) encoder_b = Pin(ENCODER_B, Pin.IN, Pin.PULL_UP) encoder_sw = Pin(ENCODER_SW, Pin.IN, Pin.PULL_UP) button_menu = Pin(BUTTON_MENU, Pin.IN, Pin.PULL_UP) button_back = Pin(BUTTON_BACK, Pin.IN, Pin.PULL_UP) button_reset = Pin(BUTTON_RESET, Pin.IN, Pin.PULL_UP) # === LVGL Setup === lv.init() disp_drv = lv.disp_drv_t() disp_drv.init() disp_drv.buffer = [0] * 128 * 64 disp_drv.flush_cb = display.flush disp_drv.hor_res = 128 disp_drv.ver_res = 64 lv.disp_drv_register(disp_drv) # === Global Variables === current_page = 0 oil_temp = 0.0 speed_kmh = 0.0 gps_valid = False last_update = 0 db_path = "/sd/bordcomputer.db" total_km = 0.0 daily_km = 0.0 max_speed = 0.0 avg_speed = 0.0 last_speed = 0.0 total_distance = 0.0 start_time = time.time() last_reset_time = time.time() # === Database Setup === def init_db(): if not os.path.exists(db_path): conn = sqlite3.connect(db_path) c = conn.cursor() c.execute(''' CREATE TABLE IF NOT EXISTS sensors ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp TEXT, oil_temp REAL, speed REAL, latitude REAL, longitude REAL, distance REAL ) ''') conn.commit() conn.close() print("Database created") # === Read Oil Temperature === def read_oil_temp(): global oil_temp try: roms = ds.scan() ds.convert_temp() time.sleep_ms(750) for rom in roms: oil_temp = ds.read_temp(rom) except: oil_temp = 0.0 # === Read GPS === def read_gps(): global gps_valid, speed_kmh while uart.any(): line = uart.readline().decode().strip() if "GPRMC" in line: gps_valid = True speed_kmh = 65.0 # Dummy value return speed_kmh return 0.0 # === Calculate Distance === def calculate_distance(speed_kmh, dt): return speed_kmh * (dt / 3600.0) # === Log Data to SQLite === def log_data(): global total_km, daily_km, max_speed, avg_speed, total_distance try: speed_kmh = read_gps() if speed_kmh > max_speed: max_speed = speed_kmh dt = time.ticks_ms() - last_update if dt > 0: distance = calculate_distance(speed_kmh, dt) total_distance += distance total_km += distance daily_km += distance conn = sqlite3.connect(db_path) c = conn.cursor() c.execute(''' INSERT INTO sensors (timestamp, oil_temp, speed, latitude, longitude, distance) VALUES (?, ?, ?, ?, ?, ?) ''', ( time.strftime("%Y-%m-%d %H:%M:%S"), oil_temp, speed_kmh, 48.123456, 11.567890, distance )) conn.commit() conn.close() except Exception as e: print("Log error:", e) # === Reset Functions === def reset_daily_km(): global daily_km daily_km = 0.0 def reset_max_speed(): global max_speed max_speed = 0.0 def reset_avg_speed(): global avg_speed avg_speed = 0.0 # === Update Average Speed === def update_avg_speed(): global avg_speed if total_distance > 0: elapsed_time = time.time() - start_time if elapsed_time > 0: avg_speed = total_distance / (elapsed_time / 3600.0) else: avg_speed = 0.0 # === Create Screens === screen1 = lv.scr_act() title1 = lv.label(screen1) title1.set_text("GPS") title1.align(lv.ALIGN.TOP_LEFT, 10, 10) speed_label = lv.label(screen1) speed_label.set_text("Speed: 0.0 km/h") speed_label.align(lv.ALIGN.TOP_LEFT, 10, 30) gps_label = lv.label(screen1) gps_label.set_text("Lat: 0.000000, Lon: 0.000000") gps_label.align(lv.ALIGN.TOP_LEFT, 10, 50) # Screen 2: Oil Temp screen2 = lv.scr_act() title2 = lv.label(screen2) title2.set_text("Oil Temp") title2.align(lv.ALIGN.TOP_LEFT, 10, 10) oil_label = lv.label(screen2) oil_label.set_text("Oil: 0.0°C") oil_label.align(lv.ALIGN.TOP_LEFT, 10, 30) # Screen 3: Statistics screen3 = lv.scr_act() title3 = lv.label(screen3) title3.set_text("Statistics") title3.align(lv.ALIGN.TOP_LEFT, 10, 10) total_km_label = lv.label(screen3) total_km_label.set_text("Total: 0.0 km") total_km_label.align(lv.ALIGN.TOP_LEFT, 10, 30) daily_km_label = lv.label(screen3) daily_km_label.set_text("Daily: 0.0 km") daily_km_label.align(lv.ALIGN.TOP_LEFT, 10, 50) max_speed_label = lv.label(screen3) max_speed_label.set_text("Max: 0.0 km/h") max_speed_label.align(lv.ALIGN.TOP_LEFT, 10, 70) avg_speed_label = lv.label(screen3) avg_speed_label.set_text("Avg: 0.0 km/h") avg_speed_label.align(lv.ALIGN.TOP_LEFT, 10, 90) # Screen 4: Config screen4 = lv.scr_act() title4 = lv.label(screen4) title4.set_text("Config") title4.align(lv.ALIGN.TOP_LEFT, 10, 10) reset_label = lv.label(screen4) reset_label.set_text("Reset:") reset_label.align(lv.ALIGN.TOP_LEFT, 10, 30) reset_daily_btn = lv.btn(screen4) reset_daily_btn.align(lv.ALIGN.TOP_LEFT, 10, 50) reset_daily_label = lv.label(reset_daily_btn) reset_daily_label.set_text("Daily KM") reset_daily_btn.set_event_cb(lambda e: reset_daily_km()) reset_max_btn = lv.btn(screen4) reset_max_btn.align(lv.ALIGN.TOP_LEFT, 10, 80) reset_max_label = lv.label(reset_max_btn) reset_max_label.set_text("Max Speed") reset_max_btn.set_event_cb(lambda e: reset_max_speed()) reset_avg_btn = lv.btn(screen4) reset_avg_btn.align(lv.ALIGN.TOP_LEFT, 10, 110) reset_avg_label = lv.label(reset_avg_btn) reset_avg_label.set_text("Avg Speed") reset_avg_btn.set_event_cb(lambda e: reset_avg_speed()) # === Update Display === def update_display(): global current_page if current_page == 0: lv.scr_load(screen1) speed_label.set_text(f"Speed: {speed_kmh:.1f} km/h") gps_label.set_text(f"Lat: 48.123456, Lon: 11.567890") elif current_page == 1: lv.scr_load(screen2) oil_label.set_text(f"Oil: {oil_temp:.1f}°C") elif current_page == 2: lv.scr_load(screen3) total_km_label.set_text(f"Total: {total_km:.1f} km") daily_km_label.set_text(f"Daily: {daily_km:.1f} km") max_speed_label.set_text(f"Max: {max_speed:.1f} km/h") avg_speed_label.set_text(f"Avg: {avg_speed:.1f} km/h") elif current_page == 3: lv.scr_load(screen4) # === Encoder Callback === def encoder_callback(pin): global current_page if pin.value() == 0: current_page = (current_page + 1) % 4 update_display() # Attach interrupt encoder_sw.irq(trigger=machine.Pin.IRQ_FALLING, handler=encoder_callback) # === Main Loop === async def main(): global last_update, total_km, daily_km, max_speed, avg_speed init_db() while True: # Read GPS speed_kmh = read_gps() # Read oil temp read_oil_temp() # Log data every 1s if time.ticks_ms() - last_update > 1000: log_data() last_update = time.ticks_ms() # Update display every 500ms if time.ticks_ms() - last_update > 500: update_display() last_update = time.ticks_ms() # Check buttons if button_menu.value() == 0: current_page = 0 update_display() if button_back.value() == 0: current_page = (current_page - 1) if current_page > 0 else 3 update_display() # Check reset button if button_reset.value() == 0: reset_daily_km() reset_max_speed() reset_avg_speed() update_display() await asyncio.sleep_ms(10) # === Run the App === asyncio.run(main())