From: Thomas M. Philip
Subject: Communicating with serial/parallel ports
Date: 
Message-ID: <Pine.SOL.4.21.0002132236040.8321-100000@conquest.OCF.Berkeley.EDU>
Anyone have any information about using Lisp to communicate
with a serial and/or parallel port on a PC and Mac?

Thanks in advance.
From: David McClain
Subject: Re: Communicating with serial/parallel ports
Date: 
Message-ID: <sagikh1kivs10@corp.supernews.com>
Check this out...

;;---------------------------------------------------
;; commports.lisp -- General Purpose COM Port Facility
;;
;; DM/HMSC 12/97
;; -------------------------------------------------------------

(defpackage "COMMPORTS"
  (:use "USEFUL-MACROS" "COMMON-LISP")
  (:nicknames "SIO")
  (:export
   "<COMMPORT>"
   "PORT-OPEN"
   "PORT-CLOSE"
   "PORT-READ-BYTES"
   "PORT-READ"
   "PORT-READ-UNTIL"
   "PORT-DRAIN-INPUT"
   "PORT-WRITE-BYTES"
   "PORT-WRITE"
   "PORT-FLUSH"
   "PORT-SETUP"
   "PORT-GET-SETUP"
   "PORT-SET-BUFFERS"
   "PORT-GET-TIMEOUTS"
   "PORT-SET-TIMEOUTS"
   "PORT-OPEN?"
   "$COM1"
   "$COM2"
   "$COM3"
   "$COM4"))

(in-package "COMMPORTS")

;; abstract base class
(defclass <commport> ()
    ())

;; private instantiable class
(defclass <pc-commport> (<commport>)
    ((id
      :accessor port-id
      :initarg  :id)
     (handle
      :accessor port-handle
      :initform nil)
     (scrap
      :accessor port-scrap
      :initform nil)))

(defmethod print-object ((port <pc-commport>) stream)
   (format stream "#<PC-COMMPORT COM~A ~A>"
     (port-id port)
     (if (port-handle port)
        "OPEN"
        "CLOSED")))

(defvar $COM1
  (make-instance '<pc-commport> :id 1))

(defvar $COM2
  (make-instance '<pc-commport> :id 2))

(defvar $COM3
  (make-instance '<pc-commport> :id 3))

(defvar $COM4
  (make-instance '<pc-commport> :id 4))

(defconstant $IDLE-READ-SIZE  80)

(defmethod require-port-handle ((port <pc-commport>))
  (or (port-handle port)
      (error "SIO port COM~A not open." (port-id port))))

;; --- DLL Interface ----------------------------
(defvar **commlib** "commport.dll")

(fli:define-foreign-function (_Open ···············@4")
    ((which :long))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Close ················@4")
    ((handle :long))
  :module **commlib**
  :result-type :void
  :calling-convention :stdcall)

(fli:define-foreign-function (_Read ···············@12")
    ((handle :long)
     (buffer (:pointer (:unsigned :char)))
     (buflen :long))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Write ················@12")
    ((handle :long)
     (buffer (:pointer (:unsigned :char)))
     (buflen :long))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Setup ················@8")
    ((handle :long)
     (settings (:pointer (:unsigned :char))))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Get-Setup ···················@12")
    ((handle :long)
     (buf    (:pointer (:unsigned :char)))
     (buflen :long))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Set-Buffers ·················@12")
    ((handle :long)
     (rxlen  :long)
     (txlen  :long))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Set-Timeouts ······················@8")
    ((handle :long)
     (tmouts (:pointer :long)))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Get-Timeouts ······················@8")
    ((handle  :long)
     (tmouts  (:pointer :long)))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

(fli:define-foreign-function (_Flush ················@4")
    ((handle :long))
  :module **commlib**
  :result-type :long
  :calling-convention :stdcall)

;; -----------------------------------------------------------------
(defmethod Port-Open ((port <pc-commport>))
  ;;
  ;; Precondition: valid port number
  ;; Precondition: port available
  ;;
  (unless (port-handle port) ;; already open?
    (let ((handle (_Open (port-id port))))
      (if (eql handle -1)
          (error "Can't open serial port COM~A" (port-id port))
        (setf (port-handle port) handle)))
    ))

(defmethod Port-Open? ((port <pc-commport>))
   (port-handle port))

(defmethod Port-Close ((port <pc-commport>))
  ;;
  ;; Returns nil on failure, else non-nil
  ;;
  (_Close (require-port-handle port))
  (setf (port-handle port) nil
        (port-scrap port) nil)
  t)

(defmethod Port-Set-Buffers ((port <pc-commport>) rxlen txlen)
  (_Set-Buffers (require-port-handle port) rxlen txlen))

(defmethod Port-Setup ((port <pc-commport>) setup-string)
  ;;
  ;; Precondition: valid handle
  ;; Precondition: (and (stringp setup-string)
  ;;                    (valid setup-string))
  ;;
  (fli:with-dynamic-foreign-objects ()
    (let ((settings
           (fli:convert-to-dynamic-foreign-string setup-string)))
      (_Setup (require-port-handle port) settings))))

(defmethod Port-Get-Setup ((port <pc-commport>))
  ;;
  ;; Precondition: valid handle
  ;;
  (fli:with-dynamic-foreign-objects ()
    (let ((str (fli:allocate-dynamic-foreign-object
                :type '(:unsigned :char)
                :nelems 512)))
      (if (plusp (_Get-Setup (require-port-handle port) str 512))
          (fli:convert-from-foreign-string str)))))

(defmethod Port-Get-Timeouts ((port <pc-commport>))
  ;;
  ;; Precondition: valid handle
  ;;
  (fli:with-dynamic-foreign-objects ()
    (let ((tmouts (fli:allocate-dynamic-foreign-object
                   :type :long
                   :nelems 5)))
      (if (plusp (_Get-Timeouts (require-port-handle port) tmouts))
          (let ((rslt (make-array 5)))
            (dotimes (i 5)
              (setf (aref rslt i)
                    (fli:dereference tmouts :index i)))
            rslt))
      )))

(defmethod Port-Set-Timeouts ((port <pc-commport>)
                          &key RI RM RK TM TK)
  ;;
  ;; Precondition: valid handle
  ;; Precondition: (every (mapcar (or (null item)
  ;;                                  (and (integerp item)
  ;;                                       (plusp item)))
  ;;                              (list RI RM RK TM TK)))
  ;;
  (let ((handle (require-port-handle port)))
    (fli:with-dynamic-foreign-objects ()
      (let ((tmouts (fli:allocate-dynamic-foreign-object
                     :type :long
                     :nelems 5)))
        (when (plusp (_Get-Timeouts handle tmouts))
          (if RI
              (setf (fli:dereference tmouts :index 0) RI))
          (if RM
              (setf (fli:dereference tmouts :index 1) RM))
          (if RK
              (setf (fli:dereference tmouts :index 2) RK))
          (if TM
              (setf (fli:dereference tmouts :index 3) TM))
          (if TK
              (setf (fli:dereference tmouts :index 4) TK))
          (_Set-Timeouts handle tmouts))
        ))))

(defmethod Port-Read-Bytes ((port <pc-commport>)
                            &optional (rdlen 4096))
  ;;
  ;; Be careful with byte I/O -- make sure you're not using Xon/Xoff!
  ;;
  ;; Return a vector of integer char codes
  ;;
  (let ((scrap (port-scrap port))
        (rslt  #()))
    (when scrap
      (if (>= rdlen (length scrap))
          (progn
            (decf rdlen (length scrap))
            (setf rslt scrap
                  (port-scrap port) nil))
        (progn
          (setf rdlen 0
                rslt  (subseq scrap 0 rdlen)
                (port-scrap port) (subseq scrap rdlen)))))
    (if (plusp rdlen)
        (concatenate 'vector rslt
           (fli:with-dynamic-foreign-objects ()
             (let* ((buf (fli:allocate-dynamic-foreign-object
                          :type    '(:unsigned :char)
                          :nelems  rdlen))
                    (nb (_Read (require-port-handle port) buf rdlen))
                    (rslt (make-array nb)))
               (fli:with-coerced-pointer (ptr) buf
                 (dotimes (ix nb)
                   (setf (aref rslt ix) (fli:dereference ptr))
                   (fli:incf-pointer ptr)))
               rslt)))
      rslt)))

(defmethod Port-Read ((port <pc-commport>)
                      &optional (rdlen 4096))
  ;;
  ;; Return a string of characters. Uses the Port-Read-Bytes function
  ;; so that all possible character codes are accepted.
  ;;
  (map 'string 'code-char (Port-Read-Bytes port rdlen)))

(defmethod Port-Read-Until ((port <pc-commport>)
                            (delim character))
  ;;
  ;; Accumulate read until delimiter is found.
  ;; Returns two values:
  ;;   1. The accumulated input string
  ;;   2. nil if delimiter not found by timeout period,
  ;;      or else non-nil if delimiter is found.
  ;;
  ;; Delimiter is not returned as part of the returned string.
  ;;
  (labels ((read ()
                 (port-read port $IDLE-READ-SIZE)))
    (do* ((str (read) (read))
          (pos (position delim str) (position delim str))
          (rslt ""))
         ((or (zerop (length str))
              pos)
          (values
           (if pos
               (progn
                 (setf (port-scrap port) (subseq str (1+ pos)))
                 (concatenate 'string rslt (subseq str 0 pos)))
             rslt)
           pos))
      (setf rslt (concatenate 'string rslt str)))
    ))

(defmethod Port-Drain-Input ((port <pc-commport>))
  ;;
  ;; Keep reading and discarding input until the port times-out waiting.
  ;;
  (setf (port-scrap port) nil)
  (labels ((read ()
                 (port-read-bytes port $IDLE-READ-SIZE)))
    (do ((str (read) (read)))
        ((zerop (length str)))
      )))

(defmethod Port-Write-Bytes ((port <pc-commport>) (seq sequence))
  ;;
  ;; Be careful with byte I/O -- make sure you're not using Xon/Xoff!
  ;;
  (fli:with-dynamic-foreign-objects ()
    (let* ((nelems (length seq))
           (buf (fli:allocate-dynamic-foreign-object
                 :type    '(:unsigned :char)
                 :nelems  nelems
                 :initial-contents (coerce seq 'list))))
      (_Write (require-port-handle port) buf nelems))
    ))

(defmethod Port-Write ((port <pc-commport>) (str string))
  (Port-Write-Bytes port (map 'list 'char-code str)))

(defmethod Port-Flush ((port <pc-commport>))
  (_Flush (require-port-handle port)))


;; -- end of commports.lisp -- ;;

David McClain, Sr. Scientist
Raytheon Systems Co.
Tucson, AZ

Thomas M. Philip <········@ocf.Berkeley.EDU> wrote in message
············································@conquest.OCF.Berkeley.EDU...
> Anyone have any information about using Lisp to communicate
> with a serial and/or parallel port on a PC and Mac?
>
> Thanks in advance.
>
>