You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
4.8 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# File: components/i2c/i2c_polling.py
from PyQt6.QtWidgets import QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout
from PyQt6.QtCore import QTimer, Qt
from PyQt6.QtGui import QPainter, QColor, QPen
from components.console.console_registry import log_main_console
from smbus2 import SMBus
import config.config as config
import math
AS5048_ADDR = 0x40
REG_ANGLE_MSB = 0xFE
REG_ANGLE_LSB = 0xFF
class AngleGauge(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setMinimumSize(200, 200)
self.angle = 0 # degrees
def set_angle(self, angle):
self.angle = max(0.0, min(90.0, angle))
self.update()
def paintEvent(self, event):
painter = QPainter(self)
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
rect = self.rect()
width = rect.width()
height = rect.height()
center_x = width // 2
bottom_y = height - 10
length = height - 40
# Draw vertical Y axis
pen = QPen(QColor(180, 180, 180), 2)
painter.setPen(pen)
painter.drawLine(center_x, bottom_y, center_x, bottom_y - length)
# Draw needle
radians = math.radians(self.angle)
end_x = int(center_x + length * math.sin(radians))
end_y = int(bottom_y - length * math.cos(radians))
pen = QPen(QColor(0, 100, 200), 4)
painter.setPen(pen)
painter.drawLine(center_x, bottom_y, end_x, end_y)
# Draw angle value
painter.setPen(Qt.GlobalColor.black)
painter.drawText(
rect,
int(Qt.AlignmentFlag.AlignTop | Qt.AlignmentFlag.AlignHCenter),
f"{self.angle:.1f}°",
)
class I2CPollingWidget(QWidget):
def __init__(self, bus_id=1, parent=None):
super().__init__(parent)
self.label = QLabel("AS5048A Angle Sensor")
self.gauge = AngleGauge()
self.zero_raw = None
self.start_button = QPushButton("Start")
self.stop_button = QPushButton("Stop")
self.set_zero_button = QPushButton("Set Zero")
self.start_button.clicked.connect(self.start_polling)
self.stop_button.clicked.connect(self.stop_polling)
self.set_zero_button.clicked.connect(self.set_zero)
button_layout = QHBoxLayout()
button_layout.addWidget(self.start_button)
button_layout.addWidget(self.stop_button)
button_layout.addWidget(self.set_zero_button)
layout = QVBoxLayout()
layout.addWidget(self.label)
layout.addWidget(self.gauge)
layout.addLayout(button_layout)
self.setLayout(layout)
self.bus_id = bus_id
self.bus = None
self.timer = QTimer(self)
self.timer.timeout.connect(self.read_angle)
if not config.DEBUG_MODE:
try:
self.bus = SMBus(self.bus_id)
log_main_console("success", f"AS5048A ready on bus {self.bus_id}")
except Exception as e:
log_main_console("error", f"Failed to open I2C bus {self.bus_id}: {e}")
else:
log_main_console("info", "DEBUG_MODE: fake polling setup complete")
def start_polling(self):
self.timer.start(50)
log_main_console("info", "Started I2C angle polling")
def stop_polling(self):
self.timer.stop()
log_main_console("info", "Stopped I2C angle polling")
def set_zero(self):
raw = self._read_raw_angle()
if raw is not None:
self.zero_raw = raw
log_main_console("info", f"Zero set to raw value: {self.zero_raw}")
def _read_raw_angle(self):
if config.DEBUG_MODE:
import random
return random.randint(0, 16383)
try:
msb = self.bus.read_byte_data(AS5048_ADDR, REG_ANGLE_MSB)
lsb = self.bus.read_byte_data(AS5048_ADDR, REG_ANGLE_LSB)
raw = ((msb << 6) | (lsb & 0x3F)) & 0x3FFF
return raw
except Exception as e:
log_main_console("error", f"I2C read failed: {e}")
return None
def read_angle(self):
current_raw = self._read_raw_angle()
if current_raw is None or self.zero_raw is None:
return
# Calculate signed delta
diff = (current_raw - self.zero_raw + 8192) % 16384 - 8192
angle_deg = -((diff * 360.0) / 16384.0)
# Clamp to 090° range for display
angle_clamped = max(0.0, min(90.0, angle_deg))
log_main_console(
"info",
f"Read raw: {current_raw}, Δ: {diff}, Degrees: {angle_deg:.2f}° → Gauge: {angle_clamped:.2f}°",
)
self.gauge.set_angle(angle_clamped)
def closeEvent(self, event):
self.timer.stop()
if self.bus:
self.bus.close()
super().closeEvent(event)