Compare commits

...

3 Commits

Author SHA1 Message Date
Roberto Viola
a9cfe24358 Update homeform.h 2025-07-11 12:09:08 +02:00
Roberto Viola
8cc1982dd4 Update homeform.cpp 2025-07-11 09:39:32 +02:00
Roberto Viola
f82e099795 Add GPIO gear control support for Raspberry Pi
Introduces GPIO-based gear shifting functionality using GPIO 17 (up) and GPIO 27 (down) for Raspberry Pi. Adds a worker thread to monitor GPIO pins, emits signals for gear changes, and integrates with the homeform class. Includes new settings key and UI option to enable or disable this feature.
2025-07-11 08:58:47 +02:00
5 changed files with 212 additions and 5 deletions

View File

@@ -781,9 +781,35 @@ homeform::homeform(QQmlApplicationEngine *engine, bluetooth *bl) {
QtAndroid::androidContext().object());
#endif
// Initialize GPIO gear worker if enabled
gpioGearsEnabled = settings.value(QZSettings::gpio_gears_enabled, QZSettings::default_gpio_gears_enabled).toBool();
if (gpioGearsEnabled) {
gpioGearWorker = new GPIOGearWorkerThread(this);
connect(gpioGearWorker, &GPIOGearWorkerThread::gearUpPressed, this, &homeform::onGPIOGearUpPressed);
connect(gpioGearWorker, &GPIOGearWorkerThread::gearDownPressed, this, &homeform::onGPIOGearDownPressed);
gpioGearWorker->start();
qDebug() << "GPIO Gear Worker initialized for Raspberry Pi";
}
bluetoothManager->homeformLoaded = true;
}
homeform::~homeform() {
gpx_save_clicked();
fit_save_clicked();
// Cleanup GPIO gear worker
if (gpioGearWorker) {
gpioGearWorker->stop();
gpioGearWorker->quit();
gpioGearWorker->wait(1000);
delete gpioGearWorker;
gpioGearWorker = nullptr;
qDebug() << "GPIO Gear Worker cleaned up";
}
}
#ifdef Q_OS_ANDROID
extern "C" {
JNIEXPORT void JNICALL
@@ -1141,11 +1167,6 @@ void homeform::refresh_bluetooth_devices_clicked() {
bluetoothManager->restart();
}
homeform::~homeform() {
gpx_save_clicked();
fit_save_clicked();
}
void homeform::aboutToQuit() {
qDebug() << "homeform::aboutToQuit()";
@@ -8568,3 +8589,99 @@ extern "C" {
}
}
#endif
// GPIO Gear Worker Thread Implementation
GPIOGearWorkerThread::GPIOGearWorkerThread(QObject *parent)
: QThread(parent), m_running(false) {
#if __has_include(<wiringPi.h>)
if (wiringPiSetup() == -1) {
qDebug() << "wiringPiSetup failed for GPIO Gear Worker";
return;
}
// Setup GPIO pins as inputs with pull-up resistors
pinMode(GPIO_GEAR_UP, INPUT);
pinMode(GPIO_GEAR_DOWN, INPUT);
pullUpDnControl(GPIO_GEAR_UP, PUD_UP);
pullUpDnControl(GPIO_GEAR_DOWN, PUD_UP);
qDebug() << "GPIO Gear Worker: Pins" << GPIO_GEAR_UP << "and" << GPIO_GEAR_DOWN << "initialized";
#endif
}
GPIOGearWorkerThread::~GPIOGearWorkerThread() {
stop();
if (isRunning()) {
quit();
wait(1000);
}
}
void GPIOGearWorkerThread::run() {
m_running = true;
m_lastUpState = HIGH;
m_lastDownState = HIGH;
m_lastTriggerTime = QDateTime::currentDateTime().addMSecs(-GPIO_DEBOUNCE_MS);
qDebug() << "GPIO Gear Worker thread started";
while (m_running) {
#if __has_include(<wiringPi.h>)
int upState = digitalRead(GPIO_GEAR_UP);
int downState = digitalRead(GPIO_GEAR_DOWN);
QDateTime currentTime = QDateTime::currentDateTime();
// Check for gear up (GPIO 17) - falling edge (button pressed)
if (m_lastUpState == HIGH && upState == LOW) {
if (m_lastTriggerTime.msecsTo(currentTime) >= GPIO_DEBOUNCE_MS) {
emit gearUpPressed();
m_lastTriggerTime = currentTime;
qDebug() << "GPIO Gear Up pressed";
}
}
// Check for gear down (GPIO 27) - falling edge (button pressed)
if (m_lastDownState == HIGH && downState == LOW) {
if (m_lastTriggerTime.msecsTo(currentTime) >= GPIO_DEBOUNCE_MS) {
emit gearDownPressed();
m_lastTriggerTime = currentTime;
qDebug() << "GPIO Gear Down pressed";
}
}
m_lastUpState = upState;
m_lastDownState = downState;
#endif
msleep(GPIO_POLL_MS);
}
qDebug() << "GPIO Gear Worker thread stopped";
}
void GPIOGearWorkerThread::stop() {
m_running = false;
}
// GPIO Gear Control Methods in homeform
void homeform::onGPIOGearUpPressed() {
QDateTime currentTime = QDateTime::currentDateTime();
if (lastGearGpioTime.msecsTo(currentTime) < 100) {
return; // Additional debouncing protection
}
lastGearGpioTime = currentTime;
qDebug() << "GPIO Gear Up processed";
Plus(QStringLiteral("gears"));
}
void homeform::onGPIOGearDownPressed() {
QDateTime currentTime = QDateTime::currentDateTime();
if (lastGearGpioTime.msecsTo(currentTime) < 100) {
return; // Additional debouncing protection
}
lastGearGpioTime = currentTime;
qDebug() << "GPIO Gear Down processed";
Minus(QStringLiteral("gears"));
}

View File

@@ -24,6 +24,21 @@
#include <QQuickItem>
#include <QQuickItemGrabResult>
#include <QTextToSpeech>
#include <QThread>
#include <QDateTime>
#if __has_include(<wiringPi.h>)
#include <wiringPi.h>
#else
#define INPUT 0
#define HIGH 1
#define LOW 0
#define PUD_UP 2
inline int digitalRead(int pin) { Q_UNUSED(pin); return HIGH; }
inline void pinMode(int pin, int mode) { Q_UNUSED(pin); Q_UNUSED(mode); }
inline void pullUpDnControl(int pin, int pud) { Q_UNUSED(pin); Q_UNUSED(pud); }
inline int wiringPiSetup() { return 0; }
#endif
#ifdef Q_OS_ANDROID
@@ -42,6 +57,31 @@
#endif
#endif
class GPIOGearWorkerThread : public QThread {
Q_OBJECT
public:
explicit GPIOGearWorkerThread(QObject *parent = nullptr);
~GPIOGearWorkerThread();
void run() override;
void stop();
signals:
void gearUpPressed();
void gearDownPressed();
private:
static const uint8_t GPIO_GEAR_UP = 17; // GPIO 17 (Physical pin 11)
static const uint8_t GPIO_GEAR_DOWN = 27; // GPIO 27 (Physical pin 13)
static const uint16_t GPIO_DEBOUNCE_MS = 100;
static const uint16_t GPIO_POLL_MS = 50;
bool m_running = false;
int m_lastUpState = HIGH;
int m_lastDownState = HIGH;
QDateTime m_lastTriggerTime;
};
class DataObject : public QObject {
Q_OBJECT
@@ -709,6 +749,9 @@ class homeform : public QObject {
static homeform *m_singleton;
TemplateInfoSenderBuilder *userTemplateManager = nullptr;
TemplateInfoSenderBuilder *innerTemplateManager = nullptr;
GPIOGearWorkerThread *gpioGearWorker = nullptr;
QDateTime lastGearGpioTime;
bool gpioGearsEnabled = false;
QList<QObject *> dataList;
QList<SessionLine> Session;
QQmlApplicationEngine *engine;
@@ -888,6 +931,8 @@ class homeform : public QObject {
void sortTilesTimeout();
void gearUp();
void gearDown();
void onGPIOGearUpPressed();
void onGPIOGearDownPressed();
void changeTimestamp(QTime source, QTime actual);
void pelotonOffset_Plus();
void pelotonOffset_Minus();

View File

@@ -943,6 +943,7 @@ const QString QZSettings::inclinationResistancePoints = QStringLiteral("inclinat
const QString QZSettings::default_inclinationResistancePoints = QStringLiteral("");
const QString QZSettings::floatingwindow_type = QStringLiteral("floatingwindow_type");
const QString QZSettings::gpio_gears_enabled = QStringLiteral("gpio_gears_enabled");
const uint32_t allSettingsCount = 772;
@@ -1737,6 +1738,7 @@ QVariant allSettings[allSettingsCount][2] = {
{QZSettings::nordictrack_elite_800, QZSettings::default_nordictrack_elite_800},
{QZSettings::inclinationResistancePoints, QZSettings::default_inclinationResistancePoints},
{QZSettings::floatingwindow_type, QZSettings::default_floatingwindow_type},
{QZSettings::gpio_gears_enabled, QZSettings::default_gpio_gears_enabled},
{QZSettings::rogue_echo_bike, QZSettings::default_rogue_echo_bike},
};

View File

@@ -2516,6 +2516,9 @@ class QZSettings {
static const QString floatingwindow_type;
static constexpr int default_floatingwindow_type = 0;
static const QString gpio_gears_enabled;
static constexpr bool default_gpio_gears_enabled = false;
/**
* @brief Write the QSettings values using the constants from this namespace.
* @param showDefaults Optionally indicates if the default should be shown with the key.

View File

@@ -1167,6 +1167,7 @@ import Qt.labs.platform 1.1
// 2.19.2
property bool tile_hr_time_in_zone_individual_mode: false
property bool wahoo_without_wheel_diameter: false
property bool gpio_gears_enabled: false
}
function paddingZeros(text, limit) {
@@ -12059,6 +12060,45 @@ import Qt.labs.platform 1.1
}
}
}
AccordionElement {
id: hardwareControlAccordion
title: qsTr("Hardware Control")
indicatRectColor: Material.color(Material.Grey)
textColor: Material.color(Material.Yellow)
color: Material.backgroundColor
accordionContent: ColumnLayout {
spacing: 0
IndicatorOnlySwitch {
id: gpioGearsEnabledDelegate
text: qsTr("GPIO Gears Control")
spacing: 0
bottomPadding: 0
topPadding: 0
rightPadding: 0
leftPadding: 0
clip: false
checked: settings.gpio_gears_enabled
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
onClicked: { settings.gpio_gears_enabled = checked; window.settings_restart_to_apply = true; }
}
Label {
text: qsTr("Enable GPIO-based gear shifting on Raspberry Pi (GPIO 17 for up, GPIO 27 for down)")
font.bold: true
font.italic: true
font.pixelSize: Qt.application.font.pixelSize - 2
textFormat: Text.PlainText
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.fillWidth: true
color: Material.color(Material.Lime)
}
}
}
}
/*##^##
Designer {