diff --git a/.gitignore b/.gitignore index a708aeb..7601cdf 100644 --- a/.gitignore +++ b/.gitignore @@ -87,7 +87,6 @@ dist/ downloads/ eggs/ .eggs/ -lib/ lib64/ parts/ sdist/ diff --git a/pin_control/lib/pin_control.c b/pin_control/lib/pin_control.c new file mode 100644 index 0000000..0ad62b0 --- /dev/null +++ b/pin_control/lib/pin_control.c @@ -0,0 +1,156 @@ +#include +#include + +#include + +// https://docs.kernel.org/driver-api/gpio/using-gpio.html + +#define GPIO_CHIP "gpiochip0" + +struct gpiod_chip *chip; + +int init_gpio() { + cleanup_gpio(); + + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_chip_open_by_name.html + chip = gpiod_chip_open_by_name(GPIO_CHIP); + if (!chip) { + perror("[pin_control] gpiod_chip_open_by_name(\"" GPIO_CHIP "\")"); + return -1; + } + + return 0; +} + +void cleanup_gpio() { + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_chip_close.html + if (chip) { + gpiod_chip_close(chip); + chip = NULL; + } +} + +static int set_pin_state(int pin, int state) { + int ret = -1; + if (!chip) { + perror("[pin_control] attempt to set GPIO line for invalid chip"); + goto exit; + } + + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_chip_get_line.html + struct gpiod_line *line = gpiod_chip_get_line(chip, pin); // this may need an offset + if (!line) { + perror("[pin_control] gpiod_chip_get_line()"); + goto exit; + } + + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_line_request_output.html + if (gpiod_line_request_output(line, "house-leds", state) < 0) { + perror("[pin_control] gpiod_line_request_output()"); + goto exit_line; + } + + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_line_set_value.html + if (gpiod_line_set_value(line, state) < 0) { + perror("[pin_control] gpiod_line_set_value()"); + goto exit_line; + } + + ret = 0; + +exit_line: + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_line_release.html + gpiod_line_release(line); +exit: + return ret; +} + +int turn_on_pin(int pin) { + return set_pin_state(pin, 1); +} + +int turn_off_pin(int pin) { + return set_pin_state(pin, 0); +} + +int probe_pin(int pin) { + int ret = -1; + if (!chip) { + perror("[pin_control] attempt to read GPIO line for invalid chip"); + goto exit; + } + + struct gpiod_line *line = gpiod_chip_get_line(chip, pin); + if (!line) { + perror("[pin_control] gpiod_chip_get_line()"); + goto exit; + } else if (gpiod_line_request_input(line, "house-leds") < 0) { + perror("[pin_control] gpiod_line_request_input()"); + goto exit_line; + } + + ret = gpiod_line_get_value(line); + if (ret < 0) { + perror("[pin_control] gpiod_line_get_value()"); + goto exit_line; + } + +exit_line: + // https://libgpiod-dlang.dpldocs.info/gpiod.gpiod_line_release.html + gpiod_line_release(line); +exit: + return ret; +} + +int turn_on_all_pins() { + for (int pin = 0; pin <= 5; pin++) + if (turn_on_pin(pin) < 0) + return -1; + + return 0; +} + +int turn_off_all_pins() { + for (int pin = 0; pin <= 5; pin++) { + if (turn_off_pin(pin) < 0) + return -1; + } + return 0; +} + +static int button_presssed(int event, unsigned int pin, const struct timespec * timestamp, void *data) { + (void) event; + (void) timestamp; + * (int *) data = (int) pin; + return GPIOD_CTXLESS_EVENT_POLL_RET_STOP; +} + +int wait_for_button_press(const unsigned pins[static 5]) { + // https://www.lane-fu.com/linuxmirror/libgpiod/doc/html/group______high__level____.html#ga3ac28eb59bbd31b8b2298f76047d377d + + int pressed_pin = -1; + + struct timespec ts = { + .tv_sec = 3600, + .tv_nsec = 0, + }; + + int ret = gpiod_ctxless_event_monitor_multiple( + GPIO_CHIP, + GPIOD_CTXLESS_EVENT_FALLING_EDGE, + pins, + 5, + true, + "house-leds", + &ts, // big timout + NULL, + button_presssed, + &pressed_pin); + + if (ret < 0) { + perror("[pin_control] gpiod_ctxless_event_monitor_multiple()"); + return -1; + } + + return pressed_pin; +} diff --git a/pin_control/lib/v4l2.c b/pin_control/lib/v4l2.c new file mode 100644 index 0000000..dca554d --- /dev/null +++ b/pin_control/lib/v4l2.c @@ -0,0 +1,226 @@ +// Derivado de https://gist.github.com/jayrambhia/5866483 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +static int xioctl(int fd, int request, void *arg) +{ + int r; + + do r = ioctl (fd, request, arg); + while (-1 == r && EINTR == errno); + + return r; +} + +static int print_caps(int fd) +{ + struct v4l2_capability caps = {}; + if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &caps)) + { + perror("Querying Capabilities"); + return 1; + } + + printf( "Driver Caps:\n" + " Driver: \"%s\"\n" + " Card: \"%s\"\n" + " Bus: \"%s\"\n" + " Version: %d.%d\n" + " Capabilities: %08x\n", + caps.driver, + caps.card, + caps.bus_info, + (caps.version>>16)&&0xff, + (caps.version>>24)&&0xff, + caps.capabilities); + + + struct v4l2_cropcap cropcap = {0}; + cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (-1 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) + { + perror("Querying Cropping Capabilities"); + return 1; + } + + printf( "Camera Cropping:\n" + " Bounds: %dx%d+%d+%d\n" + " Default: %dx%d+%d+%d\n" + " Aspect: %d/%d\n", + cropcap.bounds.width, cropcap.bounds.height, cropcap.bounds.left, cropcap.bounds.top, + cropcap.defrect.width, cropcap.defrect.height, cropcap.defrect.left, cropcap.defrect.top, + cropcap.pixelaspect.numerator, cropcap.pixelaspect.denominator); + + struct v4l2_fmtdesc fmtdesc = {0}; + fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + char fourcc[5] = {0}; + char c, e; + printf(" FMT : CE Desc\n--------------------\n"); + while (0 == xioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc)) + { + strncpy(fourcc, (char *)&fmtdesc.pixelformat, 4); + c = fmtdesc.flags & 1? 'C' : ' '; + e = fmtdesc.flags & 2? 'E' : ' '; + printf(" %s: %c%c %s\n", fourcc, c, e, fmtdesc.description); + fmtdesc.index++; + } + + struct v4l2_format fmt = {0}; + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = 640; + fmt.fmt.pix.height = 480; + //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_BGR24; + //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; + fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG; + fmt.fmt.pix.field = V4L2_FIELD_NONE; + + if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt)) + { + perror("Setting Pixel Format"); + return 1; + } + + strncpy(fourcc, (char *)&fmt.fmt.pix.pixelformat, 4); + printf( "Selected Camera Mode:\n" + " Width: %d\n" + " Height: %d\n" + " PixFmt: %s\n" + " Field: %d\n", + fmt.fmt.pix.width, + fmt.fmt.pix.height, + fourcc, + fmt.fmt.pix.field); + return 0; +} + +static int init_mmap(int fd, uint8_t **buffer, size_t *buffer_len) +{ + struct v4l2_requestbuffers req = {0}; + req.count = 1; + req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req.memory = V4L2_MEMORY_MMAP; + + if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) + { + perror("Requesting Buffer"); + return 1; + } + + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = 0; + if(-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf)) + { + perror("Querying Buffer"); + return 1; + } + + *buffer = mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); + *buffer_len = buf.length; + + printf("Length: %d\nAddress: %p\n", buf.length, *buffer); + printf("Image Length: %d\n", buf.bytesused); + + return 0; +} + +static int capture_frame(int fd) +{ + struct v4l2_buffer buf = {0}; + buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf.memory = V4L2_MEMORY_MMAP; + buf.index = 0; + if(-1 == xioctl(fd, VIDIOC_QBUF, &buf)) + { + perror("Query Buffer"); + return 1; + } + + if(-1 == xioctl(fd, VIDIOC_STREAMON, &buf.type)) + { + perror("Start Capture"); + return 1; + } + + fd_set fds; + FD_ZERO(&fds); + FD_SET(fd, &fds); + struct timeval tv = {0}; + tv.tv_sec = 2; + int r = select(fd+1, &fds, NULL, NULL, &tv); + if(-1 == r) + { + perror("Waiting for Frame"); + return 1; + } + + if(-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) + { + perror("Retrieving Frame"); + return 1; + } + + return 0; +} + +int save_image(const char *out, uint8_t *buffer, size_t buffer_len) +{ + printf ("saving image\n"); + + int fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + fprintf(stderr, "open(\"%s\"): %m\n", out); + return -1; + } + + int ret = 0; + while (buffer_len) { + ssize_t written = write(fd, buffer, buffer_len); + if (written < 0) { + perror("write()"); + ret = -1; + break; + } + + buffer += written; + buffer_len -= written; + } + + close(fd); + return ret; +} + +int capture_image(const char *out) +{ + int fd = open("/dev/video0", O_RDWR); + if (fd < 0) { + perror("Opening video device"); + return -1; + } + + uint8_t *buffer = NULL; + size_t buffer_len; + + int ret = print_caps(fd) + || init_mmap(fd, &buffer, &buffer_len) + || capture_frame(fd) + || save_image(out, buffer, buffer_len); + + close(fd); + if (buffer) + munmap(buffer, buffer_len); + + return ret ? -1 : 0; +}