Skip to content

talking usb-serial to my arduino from lisp (sbcl) on linux

I got an arduino microcontroller a little while ago, and have played with it a little but found it’s C/C++ development environment annoying.  I wanted to control it from lisp, and that meant serial IO.  Many other languages have special serial libraries you can use, where you instatiate a Serial object with configuration like baud, parity, etc.  John Wiseman wrote arduino_serial.py that shows this pattern.

I searched around for lisp options, and came up with a few options:

  1. open /dev/ttyUSB0 directly (from a comp.lang.lisp thread)
  2. use a FFI wrapper around libusb (from a comp.lang.lisp thread)
  3. use sb-ext:run-program to call out to python/C/whatever to deal with the serial port (we do something similar at work to render trac wiki markup to HTML in lisp)
  4. write a small C program and FFI to that (was tempting for the experience)

After much trial and error and some advice from the helpful folks on #lisp, I got method #1 working tonight.  I was able to read from arduino pretty easily, but I needed to issue this magic stty command before I could write:

stty -F /dev/ttyUSB0 9600 raw -parenb -parodd cs8 -hupcl -cstopb clocal

I had been curious how lisp (or my underlying linux) would know what baud, parity, etc to use, and it makes perfect sense that I need to set these first.  After that, the lisp side ends up pretty simple.  It took a little tweaking to find the right :direction, :if-exists, and :external-format arguments.

(with-open-file (stream "/dev/ttyUSB0"
			:direction :io
			:if-exists :overwrite
			:external-format :ascii)
  (format stream "hello")
  (read-line stream))

Disorganized source is available at http://github.com/ryepup/arduino-experiments.  I have a few servos laying around, maybe this weekend I’ll have time to get lisp moving around the real world.

My dream goal is to have lisp controlling motors that are spinning mirrors to reflect a laser in very particular patterns.  I’d use this on halloween decorations for starters, combining with fog machine/dry ice to create nifty patterns and make people wonder how the hell I did it.  Maybe, if I have the willpower to see that through, then I’ll also hook up a USB camera (using cl-v4l2) and get lisp to track and hightlight objects, augmented-reality style.  That’d be great for table-top games, being able to overlay terrain or effects on a grid mat.

14 Comments

  1. JDBoyd wrote:

    If you want to take this a little further, you could replace the external use of stty with somehow making ioctls in your lisp. I’m sure SBCL has a way to do this. I don’t know precisely what it is, but I would imagine it is contained in sb-posix.

    Tuesday, September 29, 2009 at 12:18 am | Permalink
  2. TY Chew wrote:

    When I wanted an interactive “REPL” on an Atmel, I wrote a Forth to run on the chip instead. It’s really cool because it’s all self hosting, so I only need a serial connection to talk to it. All user defined programs (Forth words) sit in SRAM memory, so they are volatile. I haven’t got around to flashing the program memory or the internal EEPROM, but that hasn’t been a great issue: I just wanted to prototype things, and learn about the chip’s capabilities.

    It’s not cool because being self hosting means it uses up a lot of memory. I’m easily out of memory!

    When I get around to it, I would probably investigate how to couple Lisp nicely into this system. Would you be keen to try this out? Fire me an email.

    Tuesday, September 29, 2009 at 3:42 am | Permalink
  3. Kevin Raison wrote:

    I use the following code in SBCL to speak to bar code scanners. Perhaps it would be helpful to you?

    (defun get-posix-baud (baud)
    (when (stringp baud)
    (setf baud (parse-integer baud)))
    (case baud
    (110 sb-posix:b110)
    (300 sb-posix:b300)
    (1200 sb-posix:b1200)
    (2400 sb-posix:b2400)
    (4800 sb-posix:b4800)
    (9600 sb-posix:b9600)
    (19200 sb-posix:b19200)
    (38400 sb-posix:b38400)
    (57600 sb-posix:b57600)
    (115200 sb-posix:b115200)
    (230400 sb-posix:b230400)
    (otherwise sb-posix:b19200)))

    (defun open-serial (tty &optional (baud 9600))
    (handler-case
    (let* ((fd (sb-posix:open
    tty
    (boole boole-ior
    (boole boole-ior sb-posix:O-RDWR sb-posix:O-NOCTTY)
    sb-posix:O-NDELAY)))
    (options (sb-posix:tcgetattr fd))
    (serial-stream nil)
    (posix-baud (get-posix-baud baud)))
    (sb-posix:cfsetispeed posix-baud options)
    (sb-posix:cfsetospeed posix-baud options)
    (setf (sb-posix:termios-cflag options)
    (boole boole-ior sb-posix:CLOCAL sb-posix:CREAD))
    (setf (sb-posix:termios-cflag options)
    (boole boole-and
    (sb-posix:termios-cflag options) (boole boole-c1 sb-posix:PARENB 0)))
    (setf (sb-posix:termios-cflag options)
    (boole boole-and
    (sb-posix:termios-cflag options) (boole boole-c1 sb-posix:CSTOPB 0)))
    (setf (sb-posix:termios-cflag options)
    (boole boole-and
    (sb-posix:termios-cflag options) (boole boole-c1 sb-posix:CSIZE 0)))
    (setf (sb-posix:termios-cflag options)
    (boole boole-ior (sb-posix:termios-cflag options) sb-posix:CS8))
    (sb-posix:tcsetattr fd sb-posix:TCSANOW options)
    (setf serial-stream
    (sb-sys:make-fd-stream fd :input t :output t :element-type ‘unsigned-byte :buffering :full))
    (values serial-stream fd))
    (error (condition)
    (format t “Problem opening serial port ~A: ~A” tty condition))))

    (defun close-serial (fd)
    (sb-posix:fcntl fd sb-posix:F-SETFL 0)
    (sb-posix:close fd))

    (let ((bcode nil))
    (defun data-received-handler (stream)
    (logger :info “DATA-RECEIVED-HANDLER called on ~A” stream)
    (handler-case
    (let ((c nil))
    (loop
    (setf c (read-byte stream))
    (logger :info “SERIAL GOT: ~A / ~A” c (code-char c))
    (cond ((eql c 13)
    (logger :info “ENQUEUEING ~A” (reverse bcode))
    (sb-queue:enqueue (format nil “~{~a~}” (reverse bcode)) *barcode-reader-queue*)
    (setf bcode nil))
    ((eql c 10) (return))
    ((eql c 2) (setf bcode nil))
    (t (push (code-char c) bcode)))))
    (end-of-file (condition)
    (declare (ignore condition)))
    (error (condition)
    (logger :err “DATA-RECEIVED-HANDLER GOT ERROR: ~A” condition)))))

    (defun create-input-handler (stream fd)
    (handler-case
    (sb-sys:add-fd-handler
    fd :input
    #'(lambda (fd)
    (declare (ignore fd))
    (data-received-handler stream)))
    (error (condition)
    (logger :err “Problem initiating fd handler: ~A” condition)
    (close-serial fd))))

    (defun activate-scanner (tty)
    (setf *barcode-reader-thread-off* nil)
    (setf *barcode-reader-queue* (sb-queue:make-queue))
    (setf *barcode-reader-thread*
    (sb-thread:make-thread
    #'(lambda ()
    (multiple-value-bind (stream fd) (open-serial tty)
    (create-input-handler stream fd)
    (loop
    (if *barcode-reader-thread-off*
    (ignore-errors
    (close-serial fd)
    (return nil))
    (handler-case
    (sb-sys:serve-all-events 0.5)
    (error (condition)
    (ignore-errors (close-serial fd))
    (logger :err “Unahndled error in barcode reader thread: ~A” condition)
    (return nil)))))))
    :name “scanner-thread”)))

    (defun deactivate-scanner ()
    (setf *barcode-reader-thread-off* t)
    (sb-thread:join-thread *barcode-reader-thread*)
    (setf *barcode-reader-thread* nil))

    Tuesday, September 29, 2009 at 11:59 am | Permalink
  4. marsijanin wrote:

    I’m using with-* licke macro for RS-422/485/232 devices
    http://paste.lisp.org/display/87952

    Wednesday, September 30, 2009 at 11:23 am | Permalink
  5. fortunatus wrote:

    So you will use the Arduino for a serial-to-I/O interface, which is a great architecture for leaving the smarts on the PC running Lisp. I’ve done lots of factory automation with that approach.

    But you are still left programming the Arduino with the C/C++. And, if you want the Arduino to exercise application smarts – for either embedding stand alone, or for offloading the PC – you still must program those in the C/C++.

    So the question is whether to go further into breaking away from that environment. Write a Lisp system to download executable image (called a vector) from PC to Arduino. Then use Lisp to implement a DSL for coding the application logic. Lisp would create the executable vector from the DSL input.

    The DSL could be a collection of functions that generate the vector as they are called.

    Or, like the FORTH guy did, write a small as possible interpreter running on the Arduino for the DSL, which in his case was FORTH. Or a byte-code interpreter could run on the Arduino, etc, etc.

    Wednesday, September 30, 2009 at 5:34 pm | Permalink
  6. ryan wrote:

    I didn’t find time for it this weekend, but I definetly plan to revisit this. You folks are awesome for volunteering all this code.

    @fortunatus: I had thought about that a lot, I think I’m going to start with a very simple pass-through interface, where lisp asks for the value of a pin, or sets the value of a pin, etc. I’m basically going to make a big switch statement on the arduino and pass serial input in.

    I had considered jumping down several rabbit holes:
    1. making an interpreter to run on the arduino, with a big array to store the program, then a way to re-program it from lisp, essentially recreating my current C/avr-gcc/avrdude toolchain in lisp and C
    1. making a DSL in lisp that compiles down to C/C++, then use the standard toolchain from there

    I’d be surprised if I made it down any of those holes, they seem like more work than I’m usually willing to give my hobby projects.

    Monday, October 5, 2009 at 6:08 pm | Permalink
  7. BBq Fir Pit wrote:

    It’s actually a nice and helpful piece of info. I am glad that you shared this helpful info with us. Please keep us up to date like this. Thanks for sharing.

    Sunday, November 13, 2011 at 9:49 pm | Permalink
  8. markus wrote:

    Audio started playing as soon as I opened up this site, so irritating!

    Tuesday, January 3, 2012 at 7:41 pm | Permalink
  9. You could definitely see your expertise within the work you write. The world hopes for even more passionate writers such as you who aren’t afraid to say how they believe. Always go after your heart. “In order to preserve your self-respect, it is sometimes necessary to lie and cheat.” by Robert Byrne.

    Friday, January 13, 2012 at 10:19 pm | Permalink
  10. Vender Ouro wrote:

    Good post. I study one thing more challenging on different blogs everyday. It’ll all the time be stimulating to read content material from other writers and practice just a little one thing from their store. I’d favor to use some with the content on my weblog whether you don’t mind. Natually I’ll give you a link in your internet blog. Thanks for sharing.

    Monday, February 13, 2012 at 12:44 pm | Permalink
  11. Alma Macphee wrote:

    Thanks a lot, I’ve recently been searching for information concerning this issue forever and your blog is the best I’ve seen so far.

    Wednesday, April 11, 2012 at 12:15 pm | Permalink
  12. Hi there, I would like to subscribe for this website to obtain hottest updates, so where can i do it please help.

    Saturday, July 21, 2012 at 9:36 am | Permalink
  13. We are a group of volunteers and opening a new scheme in our community.
    Your web site offered us with valuable information
    to work on. You’ve done a formidable job and our whole
    community will be thankful to you.

    Saturday, March 8, 2014 at 4:02 am | Permalink
  14. This guide certainly makes sure that you are getting the best possible kitchen
    design advice and the kitchen of your dreams! Please also send a photo of a household
    kitchen design or design item you would like in your new kitchen–kitchen layouts, appliances,
    countertops that fit your lifestyle, and your budget. Corridor PlanThe corridor plan is
    a version of the one walled design is very to use if you want to combine the kitchen and dining rooms are combined with each other.

    Thursday, March 27, 2014 at 8:37 pm | Permalink

2 Trackbacks/Pingbacks

  1. links for 2009-09-29 « Blarney Fellow on Tuesday, September 29, 2009 at 9:14 pm

    […] Ryan’s Tech Blog » talking usb-serial to my arduino from lisp (sbcl) on linux (tags: lisp hardware linux arduino) […]

  2. […] http://ryepup.unwashedmeme.com/blog/2009/09/28/talking-usb-serial-to-my-arduino-from-lisp-sbcl-on-l… a few seconds ago from choqoK […]