[Asio] Need help making a custom service
Hello everyone,
First off, I would like to say that I have been using Boost for the past
few years, and have found it very useful. Thanks for all your hard work!
Second, I have a question about making a custom service. I would like to
make a service that triggers a callback when a GPIO pin changes state
and triggers an interrupt (I'm using a Raspberry Pi.) I have (or can
obtain) a file descriptor that can be poll()'d, select()'d, or epoll'd
and which omits high-priority events (POLLPRI, EPOLLPRI, etc.). This
entire use case is pretty Linux-specific, so it doesn't have to be very
portable. What I would like to be able to do is something like this:
// CODE STARTS HERE
#include <iostream>
#include
On 03/09/2015 06:49 PM, Kyle Edwards wrote:
Second, I have a question about making a custom service. I would like to make a service that triggers a callback when a GPIO pin changes state and triggers an interrupt (I'm using a Raspberry Pi.) I have (or can obtain) a file descriptor that can be poll()'d, select()'d, or epoll'd and which omits high-priority events (POLLPRI, EPOLLPRI, etc.). This
Then you can wrap your file descriptor in a posix::stream_descriptor [1] and wait on events using async_read_some() with null_buffers() [2]. An example can be found in [3]. The example assumes that the wrapper does not take ownership of the file descriptor (if it does, then you can omit the non_closing_service.) [1] http://www.boost.org/doc/html/boost_asio/overview/posix/stream_descriptor.ht... [2] http://www.boost.org/doc/html/boost_asio/overview/core/reactor.html [3] https://github.com/breese/aware/blob/master/include/aware/detail/native_sock...
Hi Bjorn, Thanks for the quick reply. On Mon, 2015-03-09 at 21:21 +0100, Bjorn Reese wrote:
Then you can wrap your file descriptor in a posix::stream_descriptor [1] and wait on events using async_read_some() with null_buffers() [2].
An example can be found in [3]. The example assumes that the wrapper does not take ownership of the file descriptor (if it does, then you can omit the non_closing_service.)
Thanks for the suggestion. Unfortunately, though, I don't think async_read_some() is going to work for me. Here is an excerpt from boost/asio/detail/reactive_descriptor_service.hpp: template <typename Handler> void async_read_some(implementation_type& impl, const null_buffers&, Handler handler) { // ... start_op(impl, reactor::read_op, p.p, false, false); // ... } Notice the use of reactor::read_op. I've done a little more digging through the Boost.Asio code, and it looks like, for this application, I need to use reactor::except_op instead. Is there a simple class/function that does this? Or will I have to implement it myself? Kyle
On Mon, 2015-03-09 at 22:25 +0100, Bjorn Reese wrote:
On 03/09/2015 09:34 PM, Kyle Edwards wrote:
Thanks for the suggestion. Unfortunately, though, I don't think async_read_some() is going to work for me. Here is an excerpt from
How do you obtain the file descriptor?
It's just a native POSIX file descriptor created with open(), like this: int open_pin(unsigned int number) { std::ostringstream filename; filename << "/sys/class/gpio/gpio" << number << "/value"; return open(filename.str().c_str(), O_RDONLY | O_NONBLOCK); }
On 03/09/2015 09:34 PM, Kyle Edwards wrote:
Notice the use of reactor::read_op. I've done a little more digging through the Boost.Asio code, and it looks like, for this application, I need to use reactor::except_op instead. Is there a simple class/function that does this? Or will I have to implement it myself?
A possibility could be to wrap the file descriptor in a udp::socket (or possibly your own derived from basic_datagram_socket) and use async_receive(null_buffers) with the message_out_of_band flag set. Never tried it myself though.
On Tue, 2015-03-10 at 14:53 +0100, Bjorn Reese wrote:
On 03/09/2015 09:34 PM, Kyle Edwards wrote:
Notice the use of reactor::read_op. I've done a little more digging through the Boost.Asio code, and it looks like, for this application, I need to use reactor::except_op instead. Is there a simple class/function that does this? Or will I have to implement it myself?
A possibility could be to wrap the file descriptor in a udp::socket (or possibly your own derived from basic_datagram_socket) and use async_receive(null_buffers) with the message_out_of_band flag set. Never tried it myself though.
Alright, I've written a custom service that just wraps the relevant UDP classes (service, implementation) and does async_receive() with message_out_of_band. It's hairy, but it seems to work. Thank you!
If you got it working by wrapping the file descriptor - post it and I'll
give it a shot on tidying it up.
2015-03-10 20:05 GMT+01:00 Kyle Edwards
On Tue, 2015-03-10 at 14:53 +0100, Bjorn Reese wrote:
On 03/09/2015 09:34 PM, Kyle Edwards wrote:
Notice the use of reactor::read_op. I've done a little more digging through the Boost.Asio code, and it looks like, for this application, I need to use reactor::except_op instead. Is there a simple class/function that does this? Or will I have to implement it myself?
A possibility could be to wrap the file descriptor in a udp::socket (or possibly your own derived from basic_datagram_socket) and use async_receive(null_buffers) with the message_out_of_band flag set. Never tried it myself though.
Alright, I've written a custom service that just wraps the relevant UDP classes (service, implementation) and does async_receive() with message_out_of_band. It's hairy, but it seems to work. Thank you! _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Tue, 2015-03-10 at 20:35 +0100, svante karlsson wrote:
If you got it working by wrapping the file descriptor - post it and I'll give it a shot on tidying it up.
Here's what I've got. It works pretty well so far.
/////////////////////////
// gpio_interrupt.hpp: //
/////////////////////////
#ifndef GPIO_INTERRUPT_HPP
#define GPIO_INTERRUPT_HPP
#include
* svante karlsson
If you got it working by wrapping the file descriptor - post it and I'll give it a shot on tidying it up.
I *think* I have a vested interest in this as well. An age or two ago, I started an inotify interface on a query engine (when some data file changes on disk, it re-reads). I didn't know what I was doing and ended up with something that tightloops. https://github.com/ericprud/SWObjects/blob/sparql11/lib/SimpleServer.hpp#L11... Since inotify works on fds, what you're doing might teach me my next steps. (I'd of course love to see inotify/fsevent/FileSystemWatcher widgets in Asio and am happy to donate whatever I have of value).
2015-03-10 20:05 GMT+01:00 Kyle Edwards
: On Tue, 2015-03-10 at 14:53 +0100, Bjorn Reese wrote:
On 03/09/2015 09:34 PM, Kyle Edwards wrote:
Notice the use of reactor::read_op. I've done a little more digging through the Boost.Asio code, and it looks like, for this application, I need to use reactor::except_op instead. Is there a simple class/function that does this? Or will I have to implement it myself?
A possibility could be to wrap the file descriptor in a udp::socket (or possibly your own derived from basic_datagram_socket) and use async_receive(null_buffers) with the message_out_of_band flag set. Never tried it myself though.
Alright, I've written a custom service that just wraps the relevant UDP classes (service, implementation) and does async_receive() with message_out_of_band. It's hairy, but it seems to work. Thank you! _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-- -ericP office: +1.617.599.3509 mobile: +33.6.80.80.35.59 (eric@w3.org) Feel free to forward this message to any list for any purpose other than email address distribution. There are subtle nuances encoded in font variation and clever layout which can only be seen by printing this message on high-clay paper.
On 11 Mar 2015 at 7:25, Eric Prud'hommeaux wrote:
* svante karlsson
[2015-03-10 20:35+0100] If you got it working by wrapping the file descriptor - post it and I'll give it a shot on tidying it up.
I *think* I have a vested interest in this as well. An age or two ago, I started an inotify interface on a query engine (when some data file changes on disk, it re-reads). I didn't know what I was doing and ended up with something that tightloops. https://github.com/ericprud/SWObjects/blob/sparql11/lib/SimpleServer.hpp#L11...
Since inotify works on fds, what you're doing might teach me my next steps. (I'd of course love to see inotify/fsevent/FileSystemWatcher widgets in Asio and am happy to donate whatever I have of value).
The v1.4 release of AFIO should have some very limited support for file watching, mainly for portably avoiding polling of advisory lock state changes. It should land before the end of 2015. A proper file system monitor is probably a >= 2016 item. They are surprisingly hard to write correctly. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
Sorry for not replying earlier but I have not yet gotten any time to
actually try it out on a raspberry.
Hopefully tonight.... anyway - if the original code worked then the
following "could" there is probably mistakes since I compiled this on
windows and the ::open call in not working there...
#include
#include <iostream>
#include
#include
#include
#include
class gpio_interrupt
{
public:
gpio_interrupt(boost::asio::io_service &ios, unsigned int number) :
_socket(ios)
{
open(number);
}
~gpio_interrupt()
{
close();
}
void open(unsigned int number)
{
boost::system::error_code ec;
open(number, ec);
boost::asio::detail::throw_error(ec, "open");
}
void open(unsigned int number, boost::system::error_code& ec)
{
std::ostringstream filename;
filename << "/sys/class/gpio/gpio" << number << "/value";
int fd = ::open(filename.str().c_str(), O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
ec = boost::system::error_code(errno,
boost::asio::error::get_system_category());
return;
}
_socket.assign(boost::asio::ip::udp::v4(), fd, ec);
}
void close()
{
boost::system::error_code ec;
cancel(ec);
boost::asio::detail::throw_error(ec, "close");
}
void close(boost::system::error_code& ec)
{
cancel(ec);
}
void cancel()
{
boost::system::error_code ec;
_socket.cancel(ec);
boost::asio::detail::throw_error(ec, "close");
}
void cancel(boost::system::error_code& ec)
{
_socket.cancel(ec);
}
void async_wait(boost::function
cb) {
_socket.async_receive(boost::asio::null_buffers(), [cb](const
boost::system::error_code& ec, std::size_t bytes_transferred)
{
cb(ec);
});
}
private:
boost::asio::ip::udp::socket _socket;
};
int main(int argc, char **argv)
{
boost::asio::io_service ios;
boost::asio::io_service::work work(ios);
boost::thread thread(boost::bind(&boost::asio::io_service::run, &ios));
gpio_interrupt interrupt(ios, 21);
interrupt.async_wait([](const boost::system::error_code& ec)
{
if (ec)
{
std::cout << ec.message() << std::endl;
return;
}
std::cout << "Pin changed state" << std::endl;
});
while (true)
{
boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
// just dummy - do something else here...
}
ios.stop();
thread.join();
return 0;
}
Haven't tested it, but at first glance it looks correct. So you don't think there's a need to implement service and all that, like I did in my code? Kyle On Mon, 2015-03-16 at 12:27 +0100, svante karlsson wrote:
Sorry for not replying earlier but I have not yet gotten any time to actually try it out on a raspberry.
Hopefully tonight.... anyway - if the original code worked then the following "could" there is probably mistakes since I compiled this on windows and the ::open call in not working there...
#include
#include <iostream> #include #include #include #include class gpio_interrupt { public: gpio_interrupt(boost::asio::io_service &ios, unsigned int number) : _socket(ios) { open(number); }
~gpio_interrupt() { close(); }
void open(unsigned int number) { boost::system::error_code ec; open(number, ec); boost::asio::detail::throw_error(ec, "open"); }
void open(unsigned int number, boost::system::error_code& ec) { std::ostringstream filename; filename << "/sys/class/gpio/gpio" << number << "/value";
int fd = ::open(filename.str().c_str(), O_RDONLY | O_NONBLOCK); if (fd < 0) { ec = boost::system::error_code(errno, boost::asio::error::get_system_category()); return; } _socket.assign(boost::asio::ip::udp::v4(), fd, ec); }
void close() { boost::system::error_code ec; cancel(ec); boost::asio::detail::throw_error(ec, "close"); }
void close(boost::system::error_code& ec) { cancel(ec); }
void cancel() { boost::system::error_code ec; _socket.cancel(ec); boost::asio::detail::throw_error(ec, "close"); }
void cancel(boost::system::error_code& ec) { _socket.cancel(ec); }
void async_wait(boost::function
cb) { _socket.async_receive(boost::asio::null_buffers(), [cb](const boost::system::error_code& ec, std::size_t bytes_transferred) { cb(ec); }); }
private: boost::asio::ip::udp::socket _socket; };
int main(int argc, char **argv) { boost::asio::io_service ios; boost::asio::io_service::work work(ios); boost::thread thread(boost::bind(&boost::asio::io_service::run, &ios));
gpio_interrupt interrupt(ios, 21); interrupt.async_wait([](const boost::system::error_code& ec) { if (ec) { std::cout << ec.message() << std::endl; return; }
std::cout << "Pin changed state" << std::endl; });
while (true) {
boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); // just dummy - do something else here... }
ios.stop(); thread.join(); return 0; }
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
I think it should be similar to networking code - and there is no need to
implement that there.
I'm still curious as to why we would get an interrupt because state changes
on IO pins (but that has nothing to do with boost). But as I said I have
been to busy the last week to try it out on a raspberry.
Also note that I have not thought the destruction faze through. So you
might crash there..
2015-03-16 14:18 GMT+01:00 Kyle Edwards
Haven't tested it, but at first glance it looks correct.
So you don't think there's a need to implement service and all that, like I did in my code?
Kyle
On Mon, 2015-03-16 at 12:27 +0100, svante karlsson wrote:
Sorry for not replying earlier but I have not yet gotten any time to actually try it out on a raspberry.
Hopefully tonight.... anyway - if the original code worked then the following "could" there is probably mistakes since I compiled this on windows and the ::open call in not working there...
#include
#include <iostream> #include #include #include #include class gpio_interrupt { public: gpio_interrupt(boost::asio::io_service &ios, unsigned int number) : _socket(ios) { open(number); }
~gpio_interrupt() { close(); }
void open(unsigned int number) { boost::system::error_code ec; open(number, ec); boost::asio::detail::throw_error(ec, "open"); }
void open(unsigned int number, boost::system::error_code& ec) { std::ostringstream filename; filename << "/sys/class/gpio/gpio" << number << "/value";
int fd = ::open(filename.str().c_str(), O_RDONLY | O_NONBLOCK); if (fd < 0) { ec = boost::system::error_code(errno, boost::asio::error::get_system_category()); return; } _socket.assign(boost::asio::ip::udp::v4(), fd, ec); }
void close() { boost::system::error_code ec; cancel(ec); boost::asio::detail::throw_error(ec, "close"); }
void close(boost::system::error_code& ec) { cancel(ec); }
void cancel() { boost::system::error_code ec; _socket.cancel(ec); boost::asio::detail::throw_error(ec, "close"); }
void cancel(boost::system::error_code& ec) { _socket.cancel(ec); }
void async_wait(boost::function
cb) { _socket.async_receive(boost::asio::null_buffers(), [cb](const boost::system::error_code& ec, std::size_t bytes_transferred) { cb(ec); }); }
private: boost::asio::ip::udp::socket _socket; };
int main(int argc, char **argv) { boost::asio::io_service ios; boost::asio::io_service::work work(ios); boost::thread thread(boost::bind(&boost::asio::io_service::run, &ios));
gpio_interrupt interrupt(ios, 21); interrupt.async_wait([](const boost::system::error_code& ec) { if (ec) { std::cout << ec.message() << std::endl; return; }
std::cout << "Pin changed state" << std::endl; });
while (true) {
boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); // just dummy - do something else here...
}
ios.stop(); thread.join(); return 0; }
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
On Mon, 2015-03-16 at 14:32 +0100, svante karlsson wrote:
I'm still curious as to why we would get an interrupt because state changes on IO pins (but that has nothing to do with boost). But as I said I have been to busy the last week to try it out on a raspberry.
It's hardware-specific. Some (not all) processors have GPIO pins that double as interrupt pins. If the processor supports it (Raspberry Pi's BCM2835/2836 does) and it's implemented in the kernel, then you can wait on interrupts in userspace, like we're doing here. Kyle
participants (5)
-
Bjorn Reese
-
Eric Prud'hommeaux
-
Kyle Edwards
-
Niall Douglas
-
svante karlsson