Skip to content
This repository was archived by the owner on Oct 6, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"editor.defaultFormatter": "xaver.clang-format",
"editor.formatOnSave": true
},
"clang-format.executable": "C:\\Program Files\\LLVM\\bin\\clang-format.exe",
"clang-format.executable": "/opt/homebrew/bin/clang-format",
"C_Cpp.codeAnalysis.clangTidy.enabled": true,
"C_Cpp.codeAnalysis.clangTidy.path": "C:\\Program Files\\LLVM\\bin\\clang-tidy.exe"
}
20 changes: 13 additions & 7 deletions include/CANBusHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,29 @@ class CANBusHandler : public QObject
{
Q_OBJECT
Q_PROPERTY(int speed READ getSpeed WRITE setSpeed NOTIFY speedChanged)
Q_PROPERTY(
int battery READ getBattery WRITE setBattery NOTIFY batteryChanged)

public:
explicit CANBusHandler(QObject *parent = nullptr);
public:
explicit CANBusHandler(QObject* parent = nullptr);
~CANBusHandler();

int getSpeed() const;
void setSpeed(int speed);
int getBattery() const;
void setBattery(int battery);

signals:
signals:
void speedChanged(int speed);
void batteryChanged(int battery);

private slots:
private slots:
void readFrames();

private:
int canSocket; // File descriptor for the CAN socket
int m_speed; // Member variable for the speed
private:
int canSocket;
int m_speed;
int m_battery;
};

#endif // CANBUSHANDLER_HPP
59 changes: 42 additions & 17 deletions src/CANBusHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,26 @@
#include <arpa/inet.h>
#include "../include/CANBusHandler.hpp"

CANBusHandler::CANBusHandler(QObject *parent)
: QObject(parent),
canSocket(-1),
m_speed(0)
CANBusHandler::CANBusHandler(QObject* parent)
: QObject(parent), canSocket(-1), m_speed(0), m_battery(0)
{
struct ifreq ifr;
struct sockaddr_can addr;

canSocket = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (canSocket < 0) {
if (canSocket < 0)
{
qWarning() << "Cannot create CAN socket!";
return;
}

strcpy(ifr.ifr_name, "can0");
ioctl(canSocket, SIOCGIFINDEX, &ifr);

addr.can_family = AF_CAN;
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(canSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (bind(canSocket, (struct sockaddr*)&addr, sizeof(addr)) < 0)
{
qWarning() << "Cannot bind CAN socket!";
close(canSocket);
return;
Expand All @@ -37,16 +37,16 @@ CANBusHandler::CANBusHandler(QObject *parent)
qDebug() << "CAN socket bound to can0 interface successfully.";

// Set up a timer to periodically read frames
QTimer *timer = new QTimer(this);
QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this]() { readFrames(); });
timer->start(10); // Adjust the interval as needed
}

CANBusHandler::~CANBusHandler()
{
if (canSocket >= 0) {
close(canSocket); // Close the CAN socket
canSocket = -1;
if (canSocket >= 0)
{
close(canSocket); // Close the CAN socket
}
}

Expand All @@ -57,31 +57,56 @@ int CANBusHandler::getSpeed() const

void CANBusHandler::setSpeed(int speed)
{
if (m_speed != speed) {
if (m_speed != speed)
{
m_speed = speed;
emit speedChanged(m_speed);
}
}

int CANBusHandler::getBattery() const
{
return m_battery;
}

void CANBusHandler::setBattery(int battery)
{
if (m_battery != battery)
{
m_battery = battery;
emit batteryChanged(m_battery);
}
}

void CANBusHandler::readFrames()
{
struct can_frame frame;
int nbytes = read(canSocket, &frame, sizeof(struct can_frame));

if (nbytes < 0) {
if (nbytes < 0)
{
qWarning() << "Error reading CAN frame!";
return;
}

if (frame.can_id == 0x01) {
if (frame.can_id == 0x01)
{
int speed;
memcpy(&speed, frame.data, sizeof(int));

speed = ntohl(speed);

// qDebug() << "Speed:" << speed; // Print the speed value
setSpeed(speed);
} else {
qDebug() << "Received CAN frame with ID:" << frame.can_id;
}
else if (frame.can_id == 0x02)
{
int battery;
memcpy(&battery, frame.data, sizeof(int));

battery = ntohl(battery);

// qDebug() << "Battery:" << battery; // Print the speed value
setBattery(battery);
}
}
135 changes: 10 additions & 125 deletions src/Main.qml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import com.example.canbus 1.0
import canbus 1.0

ApplicationWindow {
visible: true
Expand All @@ -11,12 +11,13 @@ ApplicationWindow {
CANBusHandler {
id: canBusHandler
onSpeedChanged: speedGauge.requestPaint()
onBatteryChanged: batteryGauge.requestPaint()
}

Rectangle {
anchors.fill: parent

// Background gradient
// Background Gradient
Canvas {
anchors.fill: parent
onPaint: {
Expand Down Expand Up @@ -59,46 +60,6 @@ ApplicationWindow {
}
}

Canvas {
id: roadCanvas
anchors.fill: parent

onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);

var centerX = width / 2;
var roadWidth = 200;
var roadHeight = height;

// Perspective variables
var topRoadWidth = 50; // Simulated narrow top width for 3D effect
var bottomRoadWidth = roadWidth;

// Road boundaries
var leftTop = centerX - topRoadWidth / 2;
var rightTop = centerX + topRoadWidth / 2;
var leftBottom = centerX - bottomRoadWidth / 2;
var rightBottom = centerX + bottomRoadWidth / 2;

// Draw road edges
ctx.strokeStyle = "white";
ctx.lineWidth = 3;

// Left edge
ctx.beginPath();
ctx.moveTo(leftTop, 0);
ctx.lineTo(leftBottom, roadHeight);
ctx.stroke();

// Right edge
ctx.beginPath();
ctx.moveTo(rightTop, 0);
ctx.lineTo(rightBottom, roadHeight);
ctx.stroke();
}
}

// Left Speedometer Gauge
Canvas {
id: speedGauge
Expand All @@ -108,58 +69,20 @@ ApplicationWindow {
anchors.left: parent.left
anchors.leftMargin: 20

property real angle: 0.75 * Math.PI // Initial angle for the arc

onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);

// Background gradient for the circular gauge
var bgGradient = ctx.createRadialGradient(width / 2, height / 2, 50, width / 2, height / 2, 120);
bgGradient.addColorStop(0, "#001848");
bgGradient.addColorStop(1, "#002060");
ctx.fillStyle = bgGradient;
ctx.beginPath();
ctx.arc(width / 2, height / 2, 120, 0, 2 * Math.PI);
ctx.fill();

// Draw Speed Arc based on speed value
var arcStartAngle = 0.75 * Math.PI; // Start at 270 degrees (left)
var arcEndAngle = arcStartAngle + (Math.PI * canBusHandler.speed / 180); // Map speedValue to an arc from 0 to 180 degrees
var arcGradient = ctx.createLinearGradient(0, 0, width, height);
arcGradient.addColorStop(0, "#00ff00");
arcGradient.addColorStop(1, "#004000");
ctx.lineWidth = 15;
ctx.strokeStyle = arcGradient;
ctx.beginPath();
ctx.arc(width / 2, height / 2, 100, arcStartAngle, arcEndAngle);
ctx.stroke();

// Speed Value
ctx.font = "36px Arial";
ctx.font = "36px sans-serif";
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText(canBusHandler.speed + " km/h", width / 2, height / 2);
ctx.fillText(canBusHandler.speed, width / 2, height / 2);

// Label
ctx.font = "18px Arial";
ctx.font = "18px sans-serif";
ctx.fillStyle = "lightgray";
ctx.fillText("km/h", width / 2, height / 2 + 40);
}

// Speed angle animation with smoother transition
NumberAnimation {
id: speedAnimation
target: speedGauge
property: "angle"
from: speedGauge.angle
to: 0.75 * Math.PI + (Math.PI * canBusHandler.speed / 180)
duration: 1000 // 1 second transition for smoother effect
easing.type: Easing.InOutQuad // Smoother easing
running: true
onRunningChanged: {
speedGauge.requestPaint(); // Trigger repaint when animation is running
}
ctx.fillText("Speed", width / 2, height / 2 + 40);
}
}

Expand All @@ -172,59 +95,21 @@ ApplicationWindow {
anchors.right: parent.right
anchors.rightMargin: 20

property real angle: 0.75 * Math.PI // Initial angle for the arc

onPaint: {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);

// Background gradient for the circular gauge
var bgGradient = ctx.createRadialGradient(width / 2, height / 2, 50, width / 2, height / 2, 120);
bgGradient.addColorStop(0, "#001848");
bgGradient.addColorStop(1, "#002060");
ctx.fillStyle = bgGradient;
ctx.beginPath();
ctx.arc(width / 2, height / 2, 120, 0, 2 * Math.PI);
ctx.fill();

// Draw Battery Arc based on battery value
var arcStartAngle = 0.75 * Math.PI; // Start at 270 degrees (left)
var arcEndAngle = arcStartAngle + (Math.PI * batteryValue / 500); // Map batteryValue to an arc from 0 to 180 degrees
var arcGradient = ctx.createLinearGradient(0, 0, width, height);
arcGradient.addColorStop(0, "#ffcc00"); // Yellow arc for battery status
arcGradient.addColorStop(1, "#e0b100");
ctx.lineWidth = 15;
ctx.strokeStyle = arcGradient;
ctx.beginPath();
ctx.arc(width / 2, height / 2, 100, arcStartAngle, arcEndAngle);
ctx.stroke();

// Battery Value
ctx.font = "36px Arial";
ctx.font = "36px sans-serif";
ctx.fillStyle = "white";
ctx.textAlign = "center";
ctx.fillText(batteryValue + " km", width / 2, height / 2);
ctx.fillText(canBusHandler.battery + " %", width / 2, height / 2);

// Label
ctx.font = "18px Arial";
ctx.font = "18px sans-serif";
ctx.fillStyle = "lightgray";
ctx.fillText("Battery", width / 2, height / 2 + 40);
}

// Battery angle animation with smoother transition
NumberAnimation {
id: batteryAnimation
target: batteryGauge
property: "angle"
from: batteryGauge.angle
to: 0.75 * Math.PI + (Math.PI * batteryValue / 500)
duration: 1000 // 1 second transition for smoother effect
easing.type: Easing.InOutQuad // Smoother easing
running: true
onRunningChanged: {
batteryGauge.requestPaint(); // Trigger repaint when animation is running
}
}
}
}
}
Loading
Loading