From 4b471629630383feec5a888ae1a82e9429758093 Mon Sep 17 00:00:00 2001 From: Rui Pires Date: Mon, 6 Jan 2025 21:58:59 +0000 Subject: [PATCH 1/3] 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/3] 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/3] 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