From 4b471629630383feec5a888ae1a82e9429758093 Mon Sep 17 00:00:00 2001 From: Rui Pires Date: Mon, 6 Jan 2025 21:58:59 +0000 Subject: [PATCH 1/5] Add CANBusHandler class and QML integration; configure build tasks --- .vscode/launch.json | 26 ++++++++++++ .vscode/tasks.json | 29 +++++++++++++ CMakeLists.txt | 74 ++++++++++++--------------------- include/.keep | 0 include/CANBusHandler.hpp | 29 +++++++++++++ src/CANBusHandler.cpp | 87 +++++++++++++++++++++++++++++++++++++++ src/Main.qml | 46 +++++---------------- src/main.cpp | 26 ++++++++---- src/resources.qrc | 5 +++ 9 files changed, 232 insertions(+), 90 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json delete mode 100644 include/.keep create mode 100644 include/CANBusHandler.hpp create mode 100644 src/CANBusHandler.cpp create mode 100644 src/resources.qrc diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..fb5ba84 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Remote Debug on Raspberry Pi", + "type": "cppdbg", + "request": "launch", + "program": "/Users/ruipedropires/SEA:ME/QTonRaspberryPi/HelloQt6", // Be sure that this is the path where binary is exist on host + "stopAtEntry": false, + "cwd": "/Users/ruipedropires/SEA:ME/QTonRaspberryPi", // should be host path not target + "externalConsole": false, + "MIMode": "gdb", + "miDebuggerPath": "/usr/bin/gdb-multiarch", // gdb-multiacrh should be used not gdb + "miDebuggerServerAddress": "192.168.178.21:2000", // Replace with your Raspberry Pi's IP and the gdbserver port + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "", // Optional: Specify a task to run before debugging starts + "postDebugTask": "" // Optional: Specify a task to run after debugging ends + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..fb6e845 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,29 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build with Docker", + "type": "shell", + "command": "./helperTasks.sh build_docker_image", + "group": "build" + }, + { + "label": "Copy binary from tmp container", + "type": "shell", + "command": "./helperTasks.sh create_binary_and_copy", + "group": "build" + }, + { + "label": "Send binary to Rasp", + "type": "shell", + "command": "./helperTasks.sh send_binary_to_rasp", + "group": "build" + }, + { + "label": "Run gdbserver on Rasp", + "type": "shell", + "command": "./helperTasks.sh run_gdb_server_on_rasp", + "group": "build" + } + ] +} diff --git a/CMakeLists.txt b/CMakeLists.txt index 16845c5..4f428e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,47 +1,27 @@ -cmake_minimum_required(VERSION 3.16) - -project(Speedometer VERSION 0.1 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 6.5 REQUIRED COMPONENTS Quick) - -qt_standard_project_setup(REQUIRES 6.5) - -qt_add_executable(appSpeedometer - ./src/main.cpp -) - -qt_add_qml_module(appSpeedometer - URI Speedometer - VERSION 1.0 - QML_FILES - ./src/Main.qml -) - -# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1. -# If you are developing for iOS or macOS you should consider setting an -# explicit, fixed bundle identifier manually though. -set_target_properties(appSpeedometer PROPERTIES -# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appSpeedometer - MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} - MACOSX_BUNDLE TRUE - WIN32_EXECUTABLE TRUE -) - -target_link_libraries(appSpeedometer - PRIVATE - Qt6::Quick - Qt6::Core - Qt6::Gui - Qt6::Qml - Qt6::Network -) - -include(GNUInstallDirs) -install(TARGETS appSpeedometer - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) +cmake_minimum_required(VERSION 3.5) + +project(HelloQt6 LANGUAGES CXX) +message("CMAKE_SYSROOT " ${CMAKE_SYSROOT}) +message("CMAKE_LIBRARY_ARCHITECTURE " ${CMAKE_LIBRARY_ARCHITECTURE}) +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Core Quick DBus SerialBus REQUIRED) + +set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wl,-rpath-link, ${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} -L${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wl,-rpath-link,${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} -L${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}") +# Enable automatic MOC, UIC, and RCC processing +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +qt_add_resources(QT_RESOURCES ./src/resources.qrc) + +add_executable(speedometer ${QT_RESOURCES} + ./src/main.cpp + ./src/CANBusHandler.cpp + ./include/CANBusHandler.hpp +) + +target_link_libraries(speedometer -lm -ldl Qt6::Core Qt6::DBus Qt6::Quick Qt6::SerialBus) + diff --git a/include/.keep b/include/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/include/CANBusHandler.hpp b/include/CANBusHandler.hpp new file mode 100644 index 0000000..8d79bba --- /dev/null +++ b/include/CANBusHandler.hpp @@ -0,0 +1,29 @@ +#ifndef CANBUSHANDLER_HPP +#define CANBUSHANDLER_HPP + +#include + +class CANBusHandler : public QObject +{ + Q_OBJECT + Q_PROPERTY(int speed READ getSpeed WRITE setSpeed NOTIFY speedChanged) + +public: + explicit CANBusHandler(QObject *parent = nullptr); + ~CANBusHandler(); + + int getSpeed() const; + void setSpeed(int speed); + +signals: + void speedChanged(int speed); + +private slots: + void readFrames(); + +private: + int canSocket; // File descriptor for the CAN socket + int m_speed; // Member variable for the speed +}; + +#endif // CANBUSHANDLER_HPP diff --git a/src/CANBusHandler.cpp b/src/CANBusHandler.cpp new file mode 100644 index 0000000..af28be9 --- /dev/null +++ b/src/CANBusHandler.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../include/CANBusHandler.hpp" + +CANBusHandler::CANBusHandler(QObject *parent) + : QObject(parent), + canSocket(-1), + m_speed(0) +{ + struct ifreq ifr; + struct sockaddr_can addr; + + canSocket = socket(PF_CAN, SOCK_RAW, CAN_RAW); + 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_ifindex = ifr.ifr_ifindex; + if (bind(canSocket, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + qWarning() << "Cannot bind CAN socket!"; + close(canSocket); + return; + } + + qDebug() << "CAN socket bound to can0 interface successfully."; + + // Set up a timer to periodically read frames + 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; + } +} + +int CANBusHandler::getSpeed() const +{ + return m_speed; +} + +void CANBusHandler::setSpeed(int speed) +{ + if (m_speed != speed) { + m_speed = speed; + emit speedChanged(m_speed); + } +} + +void CANBusHandler::readFrames() +{ + struct can_frame frame; + int nbytes = read(canSocket, &frame, sizeof(struct can_frame)); + + if (nbytes < 0) { + qWarning() << "Error reading CAN frame!"; + return; + } + + 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; + } +} diff --git a/src/Main.qml b/src/Main.qml index 1f02e03..ea5ed84 100644 --- a/src/Main.qml +++ b/src/Main.qml @@ -1,15 +1,17 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 +import com.example.canbus 1.0 ApplicationWindow { visible: true - width: 1000 - height: 400 + width: Screen.width + height: Screen.height title: "Instrument Cluster with Smooth Transitions" - // Initial values declared here - property int speedValue: 0 - property int batteryValue: 284 + CANBusHandler { + id: canBusHandler + onSpeedChanged: speedGauge.requestPaint() + } Rectangle { anchors.fill: parent @@ -97,8 +99,6 @@ ApplicationWindow { } } - - // Left Speedometer Gauge Canvas { id: speedGauge @@ -125,7 +125,7 @@ ApplicationWindow { // Draw Speed Arc based on speed value var arcStartAngle = 0.75 * Math.PI; // Start at 270 degrees (left) - var arcEndAngle = arcStartAngle + (Math.PI * speedValue / 180); // Map speedValue to an arc from 0 to 180 degrees + 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"); @@ -139,7 +139,7 @@ ApplicationWindow { ctx.font = "36px Arial"; ctx.fillStyle = "white"; ctx.textAlign = "center"; - ctx.fillText(speedValue + " km/h", width / 2, height / 2); + ctx.fillText(canBusHandler.speed + " km/h", width / 2, height / 2); // Label ctx.font = "18px Arial"; @@ -153,7 +153,7 @@ ApplicationWindow { target: speedGauge property: "angle" from: speedGauge.angle - to: 0.75 * Math.PI + (Math.PI * speedValue / 180) + 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 @@ -226,31 +226,5 @@ ApplicationWindow { } } } - - // Dynamic Speed Timer - Timer { - id: speedTimer - interval: 500 // 500 milliseconds for more frequent updates - repeat: true - running: true - onTriggered: { - speedValue = Math.floor(Math.random() * 180); // Simulate random speed between 0 and 180 km/h - speedAnimation.stop(); // Stop any ongoing animation - speedAnimation.start(); // Start a new animation - } - } - - // Dynamic Battery Timer - Timer { - id: batteryTimer - interval: 1000 // 1 second for more frequent updates - repeat: true - running: true - onTriggered: { - batteryValue = Math.floor(Math.random() * 500); // Simulate random battery value between 0 and 500 km - batteryAnimation.stop(); // Stop any ongoing animation - batteryAnimation.start(); // Start a new animation - } - } } } diff --git a/src/main.cpp b/src/main.cpp index f98b0fa..bb5e44a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,18 +1,30 @@ #include #include +#include +#include +#include +#include "../include/CANBusHandler.hpp" int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); + // Set up the QML engine QQmlApplicationEngine engine; - QObject::connect( - &engine, - &QQmlApplicationEngine::objectCreationFailed, - &app, - []() { QCoreApplication::exit(-1); }, - Qt::QueuedConnection); - engine.loadFromModule("Speedometer", "Main"); + + qmlRegisterType("com.example.canbus", 1, 0, "CANBusHandler"); + // Create and expose the CAN bus handler to QML + CANBusHandler canBusHandler; + engine.rootContext()->setContextProperty("canBusHandler", &canBusHandler); + + // Load QML file + const QUrl url(QStringLiteral("qrc:/Main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); return app.exec(); } diff --git a/src/resources.qrc b/src/resources.qrc new file mode 100644 index 0000000..f907b18 --- /dev/null +++ b/src/resources.qrc @@ -0,0 +1,5 @@ + + + Main.qml + + From 90a7a774d0119323d3b0bdd40156287c8e32dc29 Mon Sep 17 00:00:00 2001 From: Rui Pires Date: Mon, 6 Jan 2025 22:06:11 +0000 Subject: [PATCH 2/5] Enable submodule checkout in clang-format-check workflow --- .github/workflows/clang-format-check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 9a2a6d8..3ca14f6 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,6 +16,8 @@ jobs: # Checkout the code - name: Checkout Code uses: actions/checkout@v4 + with: + submodules: true # Install clang-format - name: Install clang-format From 77e86761f1892045628f8eba5fc8d5ed684424fe Mon Sep 17 00:00:00 2001 From: Rui Pires Date: Mon, 6 Jan 2025 22:08:10 +0000 Subject: [PATCH 3/5] Remove submodule checkout from clang-format-check workflow --- .github/workflows/clang-format-check.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index 3ca14f6..9a2a6d8 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -16,8 +16,6 @@ jobs: # Checkout the code - name: Checkout Code uses: actions/checkout@v4 - with: - submodules: true # Install clang-format - name: Install clang-format From 151567809ba567d811e41e44026d65b7b2d15f0a Mon Sep 17 00:00:00 2001 From: Rui Pires Date: Mon, 6 Jan 2025 22:09:54 +0000 Subject: [PATCH 4/5] Add battery property and functionality to CANBusHandler; update QML integration --- include/CANBusHandler.hpp | 9 +++++-- src/CANBusHandler.cpp | 29 +++++++++++++++++++---- src/Main.qml | 50 ++++----------------------------------- 3 files changed, 36 insertions(+), 52 deletions(-) diff --git a/include/CANBusHandler.hpp b/include/CANBusHandler.hpp index 8d79bba..b46a8a2 100644 --- a/include/CANBusHandler.hpp +++ b/include/CANBusHandler.hpp @@ -7,6 +7,7 @@ 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); @@ -14,16 +15,20 @@ class CANBusHandler : public QObject int getSpeed() const; void setSpeed(int speed); + int getBattery() const; + void setBattery(int battery); signals: void speedChanged(int speed); + void batteryChanged(int battery); private slots: void readFrames(); private: - int canSocket; // File descriptor for the CAN socket - int m_speed; // Member variable for the speed + int canSocket; + int m_speed; + int m_battery; }; #endif // CANBUSHANDLER_HPP diff --git a/src/CANBusHandler.cpp b/src/CANBusHandler.cpp index af28be9..fd1bac5 100644 --- a/src/CANBusHandler.cpp +++ b/src/CANBusHandler.cpp @@ -12,7 +12,8 @@ CANBusHandler::CANBusHandler(QObject *parent) : QObject(parent), canSocket(-1), - m_speed(0) + m_speed(0), + m_battery(0) { struct ifreq ifr; struct sockaddr_can addr; @@ -46,7 +47,6 @@ CANBusHandler::~CANBusHandler() { if (canSocket >= 0) { close(canSocket); // Close the CAN socket - canSocket = -1; } } @@ -63,6 +63,19 @@ void CANBusHandler::setSpeed(int 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; @@ -78,10 +91,16 @@ void CANBusHandler::readFrames() 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); } } diff --git a/src/Main.qml b/src/Main.qml index ea5ed84..b38add3 100644 --- a/src/Main.qml +++ b/src/Main.qml @@ -11,12 +11,12 @@ ApplicationWindow { CANBusHandler { id: canBusHandler onSpeedChanged: speedGauge.requestPaint() + onBatteryChanged: batteryGauge.requestPaint() } Rectangle { anchors.fill: parent - // Background gradient Canvas { anchors.fill: parent onPaint: { @@ -59,46 +59,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 @@ -108,7 +68,7 @@ ApplicationWindow { anchors.left: parent.left anchors.leftMargin: 20 - property real angle: 0.75 * Math.PI // Initial angle for the arc + property real angle: 0.75 * Math.PI onPaint: { var ctx = getContext("2d"); @@ -189,7 +149,7 @@ ApplicationWindow { // 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 arcEndAngle = arcStartAngle + (Math.PI * canBusHandler.battery / 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"); @@ -203,7 +163,7 @@ ApplicationWindow { ctx.font = "36px Arial"; ctx.fillStyle = "white"; ctx.textAlign = "center"; - ctx.fillText(batteryValue + " km", width / 2, height / 2); + ctx.fillText(canBusHandler.battery + " km", width / 2, height / 2); // Label ctx.font = "18px Arial"; @@ -217,7 +177,7 @@ ApplicationWindow { target: batteryGauge property: "angle" from: batteryGauge.angle - to: 0.75 * Math.PI + (Math.PI * batteryValue / 500) + to: 0.75 * Math.PI + (Math.PI * canBusHandler.battery / 500) duration: 1000 // 1 second transition for smoother effect easing.type: Easing.InOutQuad // Smoother easing running: true From 6876fab5aa90d2bc3d39dd36ea0bb8d1d7f855f6 Mon Sep 17 00:00:00 2001 From: Rui Pires Date: Tue, 7 Jan 2025 10:13:43 +0000 Subject: [PATCH 5/5] Refactor CANBusHandler and QML integration; update clang-format path and improve formatting --- .vscode/settings.json | 2 +- include/CANBusHandler.hpp | 13 +++--- src/CANBusHandler.cpp | 38 +++++++++------- src/Main.qml | 93 ++++----------------------------------- src/main.cpp | 22 ++++----- 5 files changed, 51 insertions(+), 117 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 37cb614..f02b29a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -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" } \ No newline at end of file diff --git a/include/CANBusHandler.hpp b/include/CANBusHandler.hpp index b46a8a2..0fb3df9 100644 --- a/include/CANBusHandler.hpp +++ b/include/CANBusHandler.hpp @@ -7,10 +7,11 @@ 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) + 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; @@ -18,14 +19,14 @@ class CANBusHandler : public QObject int getBattery() const; void setBattery(int battery); -signals: + signals: void speedChanged(int speed); void batteryChanged(int battery); -private slots: + private slots: void readFrames(); -private: + private: int canSocket; int m_speed; int m_battery; diff --git a/src/CANBusHandler.cpp b/src/CANBusHandler.cpp index fd1bac5..23fb595 100644 --- a/src/CANBusHandler.cpp +++ b/src/CANBusHandler.cpp @@ -9,17 +9,15 @@ #include #include "../include/CANBusHandler.hpp" -CANBusHandler::CANBusHandler(QObject *parent) - : QObject(parent), - canSocket(-1), - m_speed(0), - m_battery(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; } @@ -27,9 +25,10 @@ CANBusHandler::CANBusHandler(QObject *parent) 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; @@ -38,15 +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 + if (canSocket >= 0) + { + close(canSocket); // Close the CAN socket } } @@ -57,7 +57,8 @@ int CANBusHandler::getSpeed() const void CANBusHandler::setSpeed(int speed) { - if (m_speed != speed) { + if (m_speed != speed) + { m_speed = speed; emit speedChanged(m_speed); } @@ -70,7 +71,8 @@ int CANBusHandler::getBattery() const void CANBusHandler::setBattery(int battery) { - if (m_battery != battery) { + if (m_battery != battery) + { m_battery = battery; emit batteryChanged(m_battery); } @@ -81,12 +83,14 @@ 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)); @@ -94,7 +98,9 @@ void CANBusHandler::readFrames() // qDebug() << "Speed:" << speed; // Print the speed value setSpeed(speed); - } else if (frame.can_id == 0x02) { + } + else if (frame.can_id == 0x02) + { int battery; memcpy(&battery, frame.data, sizeof(int)); diff --git a/src/Main.qml b/src/Main.qml index b38add3..e7f8c01 100644 --- a/src/Main.qml +++ b/src/Main.qml @@ -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 @@ -17,6 +17,7 @@ ApplicationWindow { Rectangle { anchors.fill: parent + // Background Gradient Canvas { anchors.fill: parent onPaint: { @@ -68,58 +69,20 @@ ApplicationWindow { anchors.left: parent.left anchors.leftMargin: 20 - property real angle: 0.75 * Math.PI - 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); } } @@ -132,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 * canBusHandler.battery / 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(canBusHandler.battery + " 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 * canBusHandler.battery / 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 - } - } } } } diff --git a/src/main.cpp b/src/main.cpp index bb5e44a..925258e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,29 +1,31 @@ #include #include #include -#include +#include #include #include "../include/CANBusHandler.hpp" -int main(int argc, char *argv[]) +int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); - // Set up the QML engine QQmlApplicationEngine engine; - qmlRegisterType("com.example.canbus", 1, 0, "CANBusHandler"); - // Create and expose the CAN bus handler to QML + qmlRegisterType("canbus", 1, 0, "CANBusHandler"); + CANBusHandler canBusHandler; engine.rootContext()->setContextProperty("canBusHandler", &canBusHandler); // Load QML file const QUrl url(QStringLiteral("qrc:/Main.qml")); - QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, - &app, [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, Qt::QueuedConnection); + QObject::connect( + &engine, &QQmlApplicationEngine::objectCreated, &app, + [url](QObject* obj, const QUrl& objUrl) + { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); engine.load(url); return app.exec();