5

I am looking to use boost::asio to read from a 12 digit keypad. I currently can do it without boost, this way:

fd = open ("/dev/input/event0", 0_NONBLOCK);
read (fd, &ev, sizeof ev);

Do you know how I could do this with boost::asio? I am using Linux and c++. This post and this post are useful. I would not use serial port port (io, "/dev/usb/hiddev0") because its not serial, right?

Thank you.

Community
  • 1
  • 1
xinthose
  • 3,213
  • 3
  • 40
  • 59
  • I could open the device, get a file descriptor, then use that in asio I think – xinthose Jan 07 '15 at 17:08
  • 2
    `Boost.Asio` provides modern C++ interfaces, but in its implementation it uses plain C (or OS) functions. So, first of all, it's worth understanding why the above `read` doesn't work. What behavior do you observe? – Igor R. Jan 07 '15 at 19:28
  • 1
    Highly relevant: consider using a library instead of going to the raw device: http://stackoverflow.com/a/25559167/85371 – sehe Jan 07 '15 at 19:34
  • @IgorR. I cannot open the device this way: serial_port port (io, "/dev/usb/hiddev0"); I think I should use stream_descriptor – xinthose Jan 07 '15 at 19:38

1 Answers1

5

On my system, event2 represents the mouse, and the following simple readloop program works like a charm.

Run as root:

#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/bind.hpp>

#include <iostream>      // for debug output
#include <iomanip>

#include <linux/input.h> // for input_event
#include <boost/range/adaptor/sliced.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace boost::asio;

struct input {
    using error_code = boost::system::error_code;
    using sliced = boost::adaptors::sliced;

    input(const char* devspec) : svc(), sd(svc, open(devspec, O_RDONLY)) {
        readloop(); // post work
    }

    void run() {
        svc.run();
    }

  private:
    io_service svc;
    posix::stream_descriptor sd;

    std::vector<input_event> events;

    void handle_input(error_code ec, size_t bytes_transferred) {
        if (!ec) {
            auto const n = bytes_transferred / sizeof(input_event);

            for (auto& ev : events | sliced(0,n)) {
                using namespace boost::posix_time;

                ptime ts({1970,1,1}, seconds(ev.time.tv_sec) + microsec(ev.time.tv_usec));

                std::cout << std::dec << ts.time_of_day() << "\t" << std::hex 
                          << std::hex << ev.type << " " << ev.code  << " " << ev.value << "\n";
            }

            std::cout << "\n";

            readloop();
        } else {
            std::cerr << ec.message() << "\n";
        }
    }

    void readloop() {
        events.resize(32);
        sd.async_read_some(buffer(events), boost::bind(&input::handle_input, this, placeholders::error, placeholders::bytes_transferred));
    }

};

int main()
{
    input monitor("/dev/input/event2");
    monitor.run();
}

Typical output:

22:33:09.705346 2 0 ffffffff
22:33:09.705346 2 1 1
22:33:09.705346 0 0 0

22:33:09.713412 2 0 ffffffff
22:33:09.713412 2 1 1
22:33:09.713412 0 0 0

22:33:09.721308 2 0 ffffffff
22:33:09.721308 0 0 0

22:33:09.729328 2 0 ffffffff
22:33:09.729328 0 0 0

22:33:09.737346 2 1 1
22:33:09.737346 0 0 0

22:33:09.745328 2 0 ffffffff
22:33:09.745328 2 1 1
22:33:09.745328 0 0 0

22:33:11.897301 4 4 90001
22:33:11.897301 1 110 1
22:33:11.897301 0 0 0

22:33:12.065294 4 4 90001
22:33:12.065294 1 110 0
22:33:12.065294 0 0 0
sehe
  • 374,641
  • 47
  • 450
  • 633
  • 1
    You should probably not accept it as "the solution" until you tried it :) – sehe Jan 07 '15 at 20:45
  • 1
    I've updated to actually decode the events, printing friendly µs timestamps and event batches. _(This is a poster-child for how Asio transparently uses a vector of POD structs as a write buffer.)_ – sehe Jan 07 '15 at 22:35
  • 1
    Possibly date_time. (You could contrast with the [first version](http://stackoverflow.com/revisions/27827955/1) perhaps) – sehe Jan 08 '15 at 14:57
  • 1
    Looks like all the include dirs are redundant `/usr/include/` is on it. So `g++ -x c++ -std=c++11 -g -Wall -m32 -c asio_event_reader.cc -o asio_event_reader.o` makes more sense typically. – sehe Jan 08 '15 at 15:13
  • hello sir; I have gotten your code to work; can you explain how your `handle_input` function works please? – xinthose May 31 '16 at 02:58
  • 1
    @xinthose The loop just loops through the `events` vector. The `events | sliced(0,n)` expression takes the first `n` elements (namely, the number of events transferred). You could write this as the more traditional for loop `for (size_t i = 0; i < n; ++i) { auto& ev = events[i]; }` – sehe May 31 '16 at 16:19
  • oh I see; and the `auto` gives `ev` the `input_event` type; thank you – xinthose Jun 02 '16 at 02:03
  • why do you resize the input event vector to 32? – xinthose Jul 06 '16 at 19:55
  • 1
    @xinthose just seemed like a reasonable batch of events to receive in one go, at most – sehe Jul 06 '16 at 19:56
  • how do I convert `ev.code` to ASCII? the codes for me do not match the ASCII table – xinthose Jul 06 '16 at 20:28
  • 1
    @xinthose The hardware scancodes are mapped to ASCII by what is known as "keyboard layout". Look at the source of evtest to find out how you could do it. Here's output showing me pressing-releasing `abc`: http://paste.ubuntu.com/18657502/ – sehe Jul 06 '16 at 20:42
  • Hello again. I am trying to use `boost::asio::async_read_until` with a delimiter of `\x0D` (new line). But I get lots of binary returns that don't make much sense. How do you recommend I proceed? I can post my code in a new question if you like. – xinthose Dec 21 '16 at 23:23