Gambit Scheme FFI Notes
This is yet another post for personal reference, I've been playing with Gambit Scheme on the BeagleBoard, following is a collection of snippets that wraps OpenCV and POSIX Serial API using Gambit Scheme's FFI.
(c-declare "#include \"cv.h\"") (c-declare "#include \"highgui.h\"") (define CV_WINDOW_AUTOSIZE 1) (define capture-from-cam (c-lambda (int) (pointer "CvCapture") "cvCaptureFromCAM")) (define named-window (c-lambda (char-string int) int "cvNamedWindow")) (define destroy-window (c-lambda (char-string) void "cvDestroyWindow")) (define query-frame (c-lambda ((pointer "CvCapture")) (pointer "IplImage") "cvQueryFrame")) (define show-image (c-lambda (char-string (pointer "IplImage")) void "cvShowImage")) (define release-capture (c-lambda ((pointer "CvCapture")) void "CvCapture* c = ___arg1; cvReleaseCapture(&c);")) (define wait-key (c-lambda (int) int "cvWaitKey"))
Calling native functions is straight forward,
(define (display-webcam) (let ((camera (capture-from-cam 0)) (window (named-window "cam" CV_WINDOW_AUTOSIZE))) (let loop ((n 1)) (if (not (= n 113)) (begin (show-image "cam" (query-frame camera)) (loop (wait-key 1))))) (destroy-window "cam"))) (display-webcam)
/opt/local/bin/gambit-gsc -cc-options "-I/opt/local/include/opencv/" \ -ld-options "-L/opt/local/lib -lopencv_core -lopencv_highgui" \ -exe -o opencv opencv.scm
Returning scheme objects from a c-lambda is a bit more cumbersome, following will convert an IplImage to jpeg and return a list of bytes. (Just a reminder, cvEncodeImage is an internal call don't rely on it.)
(define ipl-jpeg (c-lambda ((pointer "IplImage")) scheme-object #<<c-lambda-end int jpeg_params[] = { CV_IMWRITE_JPEG_QUALITY, 80, 0 }; // buf is now a single-row matrix of type CV_8UC1 that contains the encoded image CvMat* buf = cvEncodeImage(".jpeg", ___arg1, jpeg_params); ___SCMOBJ list,item,tmp; list = ___NUL; int col = 0; for(col = buf->cols - 1; col >= 0; col-- ) { uchar* ptr = (uchar*)(buf->data.ptr + col); ___EXT(___U8_to_SCMOBJ) (ptr[0], &item, ___STILL); tmp = ___EXT(___make_pair) (item,list, ___STILL); ___EXT(___release_scmobj) (list); list = tmp; } ___EXT(___release_scmobj) (item); ___EXT(___release_scmobj) (list); cvReleaseMat(&buf); ___result = list; c-lambda-end ))
And finally a servo only Firmata implementation,
(c-declare "#include \"termios.h\"") (c-declare "#include \"fcntl.h\"") (define serial-init (c-lambda (char-string) int #<<c-lambda-end struct termios toptions; int fd; fd = open(___arg1, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1){ perror("init_serialport: Unable to open port"); ___result = -1; }else{ if (tcgetattr(fd, &toptions) < 0) { perror("init_serialport: Couldn't get term attributes"); ___result = -1; }else{ cfsetispeed(&toptions, B57600); cfsetospeed(&toptions, B57600); // 8N1 toptions.c_cflag &= ~PARENB; toptions.c_cflag &= ~CSTOPB; toptions.c_cflag &= ~CSIZE; toptions.c_cflag |= CS8; // no flow control toptions.c_cflag &= ~CRTSCTS; toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw toptions.c_oflag &= ~OPOST; // make raw toptions.c_cc[VMIN] = 0; toptions.c_cc[VTIME] = 20; if( tcsetattr(fd, TCSANOW, &toptions) < 0) { perror("init_serialport: Couldn't set term attributes"); ___result = -1; }else ___result = fd; } } c-lambda-end )) (define serial-write (c-lambda (int int) void #<<c-lambda-end char b[1]; b[0] = (char)___arg2; write(___arg1, b, 1); c-lambda-end ))
(define SET-PIN-MODE #xF4) ;;set a pin to INPUT/OUTPUT/PWM/etc (define ANALOG-MESSAGE #xE0) ;;send data for an analog pin (or PWM) (define SERVO 4) ;; attach servo to pin (define (arduino port) (let ((conn (serial-init port))) (thread-sleep! 5) conn)) (define (set-pin-mode conn pin mode) (serial-write conn SET-PIN-MODE) (serial-write conn pin) (serial-write conn mode)) (define (analog-write conn pin val) (serial-write conn (bitwise-ior ANALOG-MESSAGE (bitwise-and pin #x0F))) (serial-write conn (bitwise-and val #x7F)) (serial-write conn (arithmetic-shift val -7))) (define *servo-pin* 11) (define *arduino* (arduino "/dev/tty.usbserial-A600aeCj")) (set-pin-mode *arduino* *servo-pin* SERVO) (analog-write *arduino* *servo-pin* 90) (display "Arduino ready..")(newline) (let loop () (let ((line (read-line))) (analog-write *arduino* *servo-pin* (string->number line)) (display (string-append "Angle: " line))(newline) (loop)))
/opt/local/bin/gambit-gsc -exe -o servo servo.scm && ./servo