Using libgpiod and C++, how to wait for multiple line changes and get the changed value?

167 Views Asked by At

Using C++ on a Raspberry Pi 4, I'm building a single routine called monitor to watch my GPIO lines and log changes for all lines. My simple code using libgpiod:

void monitor()
{
    const std::string chipname = "gpiochip0";         
    const std::vector<int> offsets = {4, 5}; // Lines 4 and 5 for testing
    try
    {
        gpiod::chip chip(chipname);
        gpiod::line_bulk lines;

        for (int offset : offsets)
        {
            auto line = chip.get_line(offset);
            line.request({"monitor", gpiod::line_request::EVENT_BOTH_EDGES, 0});
            lines.append(line);
        }

        while (true)
        {
            // Infinite waiting on all lines
            auto signals = lines.event_wait(std::chrono::nanoseconds(0)); 

            // STUCK HERE - How can I find out which line has changed and get its value and timestamp.
        }
    }
    catch (const std::exception &e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}

For the missing code, is there a way to find out which line has changed and get its current value?

If not, should I iterate between all lines? How can I find out which line has changed and its value?

Version 1 Code:

void monitor()
{
    const std::string chipname = "gpiochip0";         
    const std::vector<int> offsets = {4, 5}; // Lines 4 and 5 for testing
    try
    {
        gpiod::chip chip(chipname);
        gpiod::line_bulk lines;

        for (int offset : offsets)
        {
            auto line = chip.get_line(offset);
            line.request({"monitor", gpiod::line_request::EVENT_BOTH_EDGES, 0});
            lines.append(line);
        }

        while (true)
        {
            // Infinite waiting on all lines
            auto signals = lines.event_wait(std::chrono::nanoseconds(0)); 

            for (unsigned int i = 0; i < lines.size(); ++i) 
            {
                if (lines[i].event_wait(std::chrono::seconds(0))) 
                {
                    std::cout << "Change detected on GPIO line " << offsets[i] << std::endl;
                }
            }        
        }
    }
    catch (const std::exception &e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
    }
}
1

There are 1 best solutions below

5
dimich On

In libgpiodcxx versions 1.x line_bulk::event_wait() returns bulk of lines on which events occurred. line_bulk behaves like a collection, you can iterate it.

    auto bulk = chip("gpiochip0", chip::OPEN_BY_NAME).get_lines({ 5, 6 });

    bulk.request({ "monitor",
        line_request::EVENT_BOTH_EDGES,
        line_request::FLAG_BIAS_PULL_UP });

    while (true) {
        // wait for events and get bulk of lines on which events occured
        auto changed = bulk.event_wait(std::chrono::seconds::max());
        // iterate lines in bulk
        for (const auto &line : changed) {
            std::cout << "Change detected on GPIO line " << line.offset() << std::endl;
            // read events occured on a line
            auto events = line.event_read_multiple();
            // iterate events
            for (const auto &event : events) {
                std::cout << "\ttype: " << event.event_type << std::endl;
            }
        }
    }

Disclaimer: I'm not sure event_wait(std::chrono::seconds::max()) is a good way for "infinite" blocking wait, but i haven't found proper documented method.