Read Serial Port Linux

51 views
Skip to first unread message

Robert O.

unread,
Feb 3, 2017, 3:53:19 PM2/3/17
to Extempore
Heyho,

I have an arduino based interface which sends strings of data to my computer and i want to use it in extempore on linux.

What is the most easy way to read the serial port from extempore?


greetings,

Robert

Ben Swift

unread,
Feb 5, 2017, 12:03:08 AM2/5/17
to extemp...@googlegroups.com
Hi Robert

A while ago I hacked together a fit-for-purpose bindings file for
librs232, there's an example in
https://github.com/digego/extempore/blob/master/examples/contrib/librs232.xtm

You'll need to build librs232 as a shared library, I've got a fork with
CMake support to do just that up here:

https://github.com/extemporelang/rs232

Hope that's helpful.

Cheers,
Ben

Fabien

unread,
Jan 30, 2018, 8:14:37 PM1/30/18
to Extempore
Hi,

I am in a very similar situation. I would like to use extempore to send commands and receive sensor information from an arduino. I used the C library and bindings provided by Ben.

Right now, for testing, I have arduino sending a 4 digit strings that changes with a potentiometer. I open and read the serial port at a certain rate, and print the value. However, if I read "slowly", like below 500 times per second, the value doesn't change, even if I know the arduino is sending a new value. The value is updated only if I call (open) again. However, if I read faster (1000+ times per second), the value changes, but with a delay.

My hypothesis is that the reading rate should be the same as the baudrate, with another function for reading the buffer, but I don't know how to call a function faster than the sampling rate allows. I didn't find how to use (clock:clock) with callback.

(bind-func open
  (lambda (chan baudrate)
    (comOpen chan baudrate)))

(open 0 115200)

(bind-val buffer i8* 6)

(bind-func read_and_print
  (lambda ()
    (begin
      (comRead 0 buffer 6)
      (printf "%s" buffer))))

(define read-print-loop
  (lambda (time rate)
    (let ((dur (/ *second* rate)))
      (read_and_print)
      (callback (+ time dur) 'read-print-loop (+ time dur) rate))))

;;this always prints the first value after (open), and changes only after an other call to (open)
(read-print-loop (now) 10)

;;this one works, but with a delay
(read-print-loop (now) 1000)

Toby Gifford

unread,
Jan 31, 2018, 1:47:27 AM1/31/18
to extemp...@googlegroups.com
Hi Fabien, here is some code I made for serial port stuff. I've got it hooked up to an Arduino / potentiometer combo like you describe, and it is working great, with no delay.
I was having similar problems to you, the solution for me was to put the comOpen call in the polling loop (but with a flag so it only gets called once).
My guess is that the operating system is particular about which thread calls read(), so if you comOpen from the top environment, then call read() from a callback,
then it is not happy (since they are on different threads - I think). This is just a guess however. Also, in the polling loop, I use
(callback (+ (now) 10) serial_port_polling ...) instead of passing in a time parameter. The reason for this is that according to the Posix spec
the behaviour of read() when called from different threads is undefined. I'm not entirely sure whether each new callback is on it's own thread, but at least
I can be sure that the previous read() call has returned before the next callback is scheduled this way.

I could be completely wrong about how this threading is working. Also, I'm on OSX so might not be the same as Linux


(sys:load "libs/core/audio_dsp.xtm")
(sys:load "libs/contrib/librs232.xtm")


(bind-func dsp
  (let ((osc1 (osc_c 0.0))
        (freq1:SAMPLE 200.0:float))
    (lambda (in:SAMPLE time:i64 channel:i64 data:SAMPLE*)
      (cond
       ((= channel 0)
        (osc1 0.1 freq1))
       (else 0.0)))))

(dsp:set! dsp)


(bind-func list-ports:[i64]*
  (lambda ()
    (let ((n:i64 (i32toi64 (comEnumerate)))
          (loop:[i64,i64]*
           (lambda (i:i64)
             (cond
              ((< i n)
               (printf "Port %d:\t%s\n" i (comGetPortName (i64toi32 i)))
               (loop (+ i 1)))
              (else n)))))
      (loop 0))))


(bind-func serial_port_id:[i64,i8*]*
  (lambda (port_name)
    (let* ((n:i64 (i32toi64 (comEnumerate)))
           (loop:[i64,i64]*
            (lambda (i:i64)
              (cond
               ((and (<= 0 i) (< i n))
                (let* ((name:i8* (comGetPortName (i64toi32 i)))
                       (a (strlen name)) (b (strlen port_name)) (c (if (< a b) a b)))
                  (cond
                   ((= 0:i32 (strncmp name port_name c)) i)
                   (else
                    (loop (+ i 1))))))
               (else
                (printf "No serial port found matching name %s\n" port_name)
                -1)))))
        (loop 0))))


(bind-func readSerialPort:[i64,i64]*
  (let ((buffer:i8* (alloc 32)))
    (lambda (port:i64)
      (let ((loop:[i64]*
             (let ((count:i64 0))
               (lambda ()
                 (let ((nr:i32 (comRead (i64toi32 port) buffer 32)) (i:i32 0:i32))
                   (cond
                    ((< 0:i32 nr)
                     (dotimes (i nr)
                       (set! count (+ count 1))
                       (dsp.freq1 (* 10.0:float (i8tof (pref buffer i)))))
                     (loop))
                    (else ;(println "Read" count "bytes")
                     count)))))))
      (loop)))))


(bind-func serial_port_polling:[void,i64]*
  (let ((isConnected:i1 #f))
    (lambda (port:i64) 
      (if (not isConnected)
          (begin
            (comOpen (i64toi32 port) (i64toi32 115200))
            (set! isConnected #t)))
      (readSerialPort port)
      ;;(printf "\n--- serial data read complete ---\n")
      (callback (+ (now) 10) serial_port_polling port)
    void)))

;; Replace "tty.usbmodem1412" with the appropriate string for your COM port
(serial_port_polling (serial_port_id "tty.usbmodem1412"))


The Arduino code I am using is

unsigned int value;
unsigned int oldValue;

void setup() {
  Serial.begin(115200);
  value = 0;
  oldValue = 0;
}

void loop() {
  value = analogRead(A0) / 8;
  if (value != oldValue) {
    byte v = byte(value);
    Serial.write(v);
    oldValue = value;
  }
}






--
You received this message because you are subscribed to the Google Groups "Extempore" group.
To unsubscribe from this group and stop receiving emails from it, send an email to extemporelang+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages