Accessing Gpio's



  • Hello Everybody!

    I tried to get a simple project to work for almost a week now, searching google up and down and trying different approaches.
    My situation is the following:
    I have a Raspberry Pi 1 and compiled Qt 5.10 for its OS (Raspbian Jessie currently running in CLI-mode, but its still the GUI version) this has to be transferred to a Pi Zero afterwards. Also I have a Host Laptop running Ubuntu 18.04 connecting to the Pi via SSH.
    I want to access its GPIOs for simple Interrupts if the edge is rising or falling or even if a high turns to a low signal. (at this point I am just frustrated and dont even care anymore as long as SOMETHING is happening) :D

    I have tried multiple suggestions given online in different forums including QSocketNotifier and QFileSystemWatcher and also tried to modify various examples code snippeds given online to fulfill the purpose I need. The FileSystemWatcher just gives me the name of the file. And in my QSocketNotifier trials I cant even open the gpio-files.

    What do you think is the best method to learn accessing the GPIO's as a Qt-newbie? Should I just use QFile and poll with a Timer or something? Although I never even got the Slot I connected to timeout() to work.

    Here is my QSocketNotifier approach which currently not even opens the files:

    #ifndef GPIOTRACKER_H
    #define GPIOTRACKER_H
    
    #include <QObject>
    #include <QFile>
    #include <QSocketNotifier>
    #include <QDebug>
    #include <QTimer>
    
    typedef enum
    {
        high = 1,
        low = 0
    }gpiovalue_t;
    
    class GpioTracker
          : public QObject
    {
        Q_OBJECT
    
    public:
        QString path = "/sys/class/gpio/gpio";
    
    //___________Full-GPIO-Paths____________________
    //    QString gpio4  = "/sys/class/gpio/gpio4";
    //    QString gpio17 = "/sys/class/gpio/gpio17";
    //    QString gpio22 = "/sys/class/gpio/gpio22";
    //    QString gpio27 = "/sys/class/gpio/gpio27";
    //______________________________________________
    
        QSocketNotifier *notifier;
        QFile file;
        int gpio;
    
        explicit GpioTracker(int gpio_nr, QObject *parent = nullptr);
        void connectGpio(int gpio_nr);
        ~GpioTracker();
    
        int lastValue;
        gpiovalue_t gpio4, gpio17, gpio22, gpio27;
    
    signals:
        void gpioChanged(int value, int gpio);
    
    public slots:
        void handle_readNotification(int socket);
    
    private slots:
        void updateDebouncing();
    
    private:
        int originalValue;
        int fd; //filedescriptor
        QTimer interval;
    };
    
    #endif // GPIOTRACKER_H
    
    #include "gpiotracker.h"
    
    #include <fcntl.h>
    #include <unistd.h>
    
    GpioTracker::GpioTracker(int gpio_nr, QObject *parent)
       : QObject(parent)
    {
        gpio = gpio_nr;
        QString filename = "/sys/class/gpio/gpio" + QString::number(gpio) + "/";
        QFile exportFile(filename +  "export");
        exportFile.open(QIODevice::WriteOnly);
        QString gpioNumber = QString::number(gpio);
        exportFile.write(gpioNumber.toUtf8());
        //mit diesem Ansatz habe ich die Gpios vor Ausf├╝hrung der Executable/Debug mit einem Script initialisiert:
        QFile *file = new QFile();
        auto pathname = QStringLiteral("%1%2/value").arg(path).arg(gpio);
        qDebug() << pathname;
        file->setFileName(pathname);
    //    QFile test;
    //    test.setFileName("/sys/class/gpio/gpio4");
    //    test.open(QIODevice::ReadOnly | QIODevice::Text);
    //    qDebug() << test.readAll();
        //check if GPIO exists_____________
        if( !file->exists() )
        {
            qDebug() << pathname << " file does not exist";
            return;
        }
        qDebug() << file->readAll();
        //try opening GPIO_________________
        fd = open(pathname.toUtf8().data(), O_RDONLY|O_NONBLOCK);
        if( fd==-1 ){
            qDebug() << "can not open " << pathname;
            return;
        }
            notifier = new QSocketNotifier( fd,
                                            QSocketNotifier::Read,
                                            this );
            connect( notifier,
                     &QSocketNotifier::activated,
                     this,
                     &GpioTracker::handle_readNotification );
            qDebug() << "Notifiier connected!";
    
            QTimer *interval = new QTimer(this);
            connect(interval, SIGNAL(timeout()), this, SLOT(updateDebouncing()));
            //interval->start(3);
    
        //or this instead of the above approach -> also didnt work out so?!
    //    QString filename = "/sys/class/gpio/gpio" + QString::number(gpio) + "/";
    
    //    QFile exportFile(filename +  "export");
    //    exportFile.open(QIODevice::WriteOnly);
    //    QString gpioNumber = QString::number(gpio);
    //    exportFile.write(gpioNumber.toUtf8());
    
    //    QFile directionFile(filename + "direction");
    //    directionFile.open(QIODevice::WriteOnly);
    //    directionFile.write("in");
    
    //    QFile edgeFile(filename + "edge");
    //    edgeFile.open(QIODevice::WriteOnly);
    //    edgeFile.write("both");
    
    //    QFile* file = new QFile(filename + "value");
    //    QSocketNotifier notifier(file->handle(), QSocketNotifier::Read);
    //    connect(&notifier, &QSocketNotifier::activated, this, &GpioTracker::handle_readNotification);
    }
    
    GpioTracker::~GpioTracker()
    {
        if( fd>=0 )
        {
            close(fd);
        }
        this->deleteLater();
        qDebug() << "gpio closed";
    }
    
    void GpioTracker::updateDebouncing()
    {
        auto pathname = QStringLiteral("%1%2/value").arg(path).arg(gpio);
        if(file.fileName() != pathname)
        file.setFileName(pathname);
        int currentValue = file.readAll().toInt();
        if (lastValue == currentValue)
        {
            emit gpioChanged(currentValue, gpio);
        }
    }
    
    void GpioTracker::handle_readNotification(int /*socket*/)
    {
        auto pathname = QStringLiteral("%1%2/value").arg(path).arg(gpio);
        if(file.fileName() != pathname)
        file.setFileName(pathname);
        int currentValue = file.readAll().toInt();
        originalValue = file.readAll().toInt();
    //    static int failureCntr;
        if(file.readAll().toInt() != originalValue)
        {
    //_________________Result=HIGH____________________________
            if(file.readAll() == "1")
            {
                currentValue = file.readAll().toInt();
                if (currentValue != lastValue) {
                    interval.start(3);
                }
                lastValue = currentValue;
            }
    //_________________Result=LOW____________________________
            if(file.readAll() == "0")
            {
                currentValue = file.readAll().toInt();
                if (currentValue != lastValue) {
                    interval.start(3);
                }
                lastValue = currentValue;
            }
        }
    }
    
    #include "mainwindow.h"
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
    
        return a.exec();
    }
    
    #include "mainwindow.h"
    #include "ui_mainwindow.h"
    #include "gpiotracker.h"
    #include <QFileSystemWatcher>
    #include <QFile>
    #include <QObject>
    
    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        GpioTracker* enter_key = new GpioTracker(4, this);
        GpioTracker* back_key = new GpioTracker(17, this);
        connectGpio(enter_key);
        connectGpio(back_key);
        qDebug() << "keys connected!";
    }
    
    MainWindow::~MainWindow()
    {
        delete ui;
    }
    
    void MainWindow::connectGpio(GpioTracker* gpiotracker)
    {
        QObject::connect(
            gpiotracker,
            SIGNAL(gpioChanged(int, int)),
            this,
            SLOT(handle_GpioValue(int, int))
        );
        qDebug() << "gpioChanged connected";
    }
    
    void MainWindow::handle_GpioValue(int value, int gpio)
    {
        auto temp = gpio;
        auto printable = QStringLiteral("%1 is %2").arg(temp).arg(value);
        ui->label->setText(printable);
        qDebug() << "gpio value changed!";
    }
    

    QFile refuses to open like this:

    QIODevice::read (QFile, "/sys/class/gpio/gpio4/value"): device not open
    

    I am root through Terminal parameters added to QtCreator and it also is the right full path.
    I'll add a picture of the gpiotest-tool to show the pins are input.
    configured as input, edge -> both and low-active through hardware

    Thanks for every bit of help. Maybe I am just overlooking something cause I tried to hard for like a week and just dont realize what I am doing wrong anymore myself.
    0_1533031621283_gpio-test_from_2018-07-31_10-18-02.png


  • Qt Champions 2017

    @ParkerSnable

    I can only recommend you to start from scratch. your program contains too much of commented code, its hard to understand therefore.

    Regarding the QFile: the simplest approach of reading a file is:

    QByteArray contents;
    QFile f("/path/to/file");
    if (f.open(QIODevice::ReadOnly)) {
      contents = f.readAll();
      f.close();
    }
    

    Please note, a second readAll() is useless, if the file has not grown (which these GPIO files don't do, they just change) in the meantime. You would at least need to rewind it.

    If that works, than try writing the export file from your program. Verify you can do this correctly.

    Now the last step: the watching. Depending on how often you need the values, you can really just use a QTimer. QFileSystemWatcher uses polling on some sytems too (on Linux it uses Kernel interrupts, AFAIK).

    Although I never even got the Slot I connected to timeout() to work.

    Then best is to try in a separate example (e.g. on your computer). Should be fairly simple.



  • Hi,
    maybe a stupid question, but why don't you use something like wiringpi that has c/c++ api it is shipped with the OS(at least with the newer ones) and easy to include in your qt/c++ project.

    Works fine for me.



  • @ParkerSnable said in Accessing Gpio's:

    What do you think is the best method to learn accessing the GPIO's as a Qt-newbie?

    I agree with @J-Hilk reply, it looks like using wiringpi is the best option.
    If you don't need to run Qt's GUI in the Raspberry board itself, you may even use GPIOs remotely via pigpiod



  • @aha_1980

    Thank you! I was just frustrated cause I already started from scratch three times, but I did as you told and started again using your example and the other information and got the QFile stuff to work like a charm and sticked with the Watcher cause I have to react as fast as possible. Thinking about starting a new Thread() for the GPIO stuff.

    Thanks for your advise, it helped a lot. :)

    @J-Hilk
    I thought about it and it actually seems really simple in my half an hour test project, but chances are that I have to do some work with a BeagleBone in the near future so I thought its best to just learn it from scratch and use this for the following projects also no matter what board is the target. But if its neccessary I will switch to the wiringpi stuff. :)

    @Pablo-J-Rogina
    This is also a nice option to have, didn't no about it before. Thank you!
    For now I have to use the GUI later on, but I will remember this for the future.


  • Qt Champions 2017

    @ParkerSnable Glad you got it working!

    So please mark this topic as SOLVED now (button Topic Tools). Thanks!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.