[DevTip] Windows Octave에서 Serial Port 사용하기

준비?

Windows 쪽은 빌드 환경과 바이너리 버전이 안 맞아서 작업하기 상당히 까다롭다.
차라리 http://wiki.octave.org/Octave_for_Windows에 쓰인 것 처럼, 비록 unofficial 버전이지만 MXE 빌드를 받아 설치한다. http://mxeoctave.osuv.de/
3.8.2-5

instrument-contorl 패키지 설치

pkg list로 설치된 패키지를 확인해 본다.
설치한 MXE 빌드에는 아무런 패키지도 설치되어 있지 않아 다음 명령으로 필요한 패키지를 설치한다.
pkg install -forge general instrument-control
설치된 버전은 0.2.1.
news instrument-control 명령으로 새 소식을 살펴보면, 기존 SERIAL 관련 명령들을 더 이상 사용하지 못한단다 (deprecated). fopen, fclose, get, set 을 사용하라는데...
http://octave.sourceforge.net/instrument-control/index.html에서 function reference를 보면 조금 헷갈린다. 뭐가 deprecated 된 것이고 무슨 함수를 써야 되는 것인지...
일단은 그냥 고~

샘플 받아 수정

http://shop.dwengo.org/node/486에서 srl_plot.m 샘플을 받아서 내용을 살펴본다.
맘에 안 드는 것들이 좀 있다.
PC 환경이라서 serial port의 주소(COM3)를 변경해 줘야 하고, 기본 Baudrate도 수정해 줬다.
Serial port로부터 연속적으로 uint16 형식의 데이터를 받아서 int16으로 표시해 줘야 하는데, 이것도 구현해 봤다. 처음에 '\r' 전송하는 부분데 막아주고, deserializeInt() 함수도 안 쓰는 걸로. (코드는 그냥 전부 살려 놨다.)
plotmet 인자가 data 인 경우만 유효한 코드는 다음과 같다.

## -*- texinfo -*-
## @deftypefn {Function File} {@var{h} =} srl_plot ()
## @deftypefnx {Function File} {@var{h} =} srl_plot (@var{property},@var{value})
## Plots data read from the serial port.
##
## It takes the following properties from the command line.
## @strong{Properties}
## @table @asis
##  @item "Figure"
##  Handle to the figure to plot into. Default @code{gcf()}.
##  @item "QeueSize"
##  Size of the array to be plotted. Default 1e3.
##  @item "BufferSize"
##  Size of the array to be read form the serial port. Default 1e2.
##  @item "Baudrate"
##  Baud rate of the communication. Default 9600
##  @item "Timeout"
##  Time out for reading from the serial port. Default 1e3 ms.
##  @item "Axis"
##  What to do with the y-axis after the plot is updated. It can be a 1-by-2 array
##  [miny maxy] indicating the limits of the axis. Default @asis{"tight"}.
##  @item "PlotMethod"
##  Indicates the method used ot update the plot. Either via "source" using callback
##  or directly updating the "data" of the plot. Default @asis{"data"}.
##  @item "Deserializer"
##  Handle to a function executed exactly after the data gas been read from the serial
##  port. It must take a vector input (the data) and return an array of double.
##  Default @code{"@double"}.
## @end table
##
## @seealso{srl_read, srl_write}
## @end deftypefn
## Author: Juan Pablo Carbajal <ajuanpi+dev@gmail.com>
function h = srl_plot (varargin)
  % Parse argumnets %%%%%%%%%%%%
  parser = inputParser ();
  parser.FunctionName = "srl_plot";
  parser = addParamValue (parser, "Figure", gcf (), @isindex);
  parser = addParamValue (parser, "QeueSize", 5e2, @(x) x > 0 );
  parser = addParamValue (parser, "BufferSize", 1e2, @(x) x > 0 );
  parser = addParamValue (parser, "ComPath", "COM3");
  parser = addParamValue (parser, "Baudrate", 38400, @isbaud);
  parser = addParamValue (parser, "Timeout", 1e3, @(x) x > 0 ); % in ms
  parser = addParamValue (parser, "Axis", "tight", @isaxis);
  parser = addParamValue (parser, "PlotMethod", "data");
  parser = addParamValue (parser, "Deserializer", @double, @is_function_handle);
  parser = parse (parser, varargin{:});
  fig     = parser.Results.Figure;
  qsize   = parser.Results.QeueSize;
  bsize   = parser.Results.BufferSize;
  compath = parser.Results.ComPath;
  baud    = parser.Results.Baudrate;
  timeo   = parser.Results.Timeout/1e3*10; %to ds
  axistyp = parser.Results.Axis;
  if isnumeric (axistyp)
    axistyp = [1 qsize axistyp(:)'];
  endif
  plotmet = parser.Results.PlotMethod;
  desrl   = parser.Results.Deserializer;
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % Open serial port
  s = serial (compath, baud, timeo);
#  srl_baudrate (s,baud);
#  srl_timeout (s,timeo);
  srl_flush (s); 
-- 중간 생략 -- 
  switch plotmet
    case "source"
      set (h,"ydatasource","y");
      while true
        tic
        [data n]    = srl_read (s, bsize);
        data        = desrl (data);
        n           = length (data);
        [y idx]     = updatedata (y,data,idx,n,qsize);
        refreshdata (fig, "caller");
        set (ht, "string", t_txt(toc));
        sleep (0.008);
      endwhile
    case "data"
      lsb_msb_flag = 1; # 1: msb
      data_aligned = zeros(0, 'uint16');
      plotdata = uint16([]);
      while (true)
        [data n] = srl_read (s, 1);
        if (1 == n)
          if (1 == lsb_msb_flag)
            data_aligned = 256 * uint16(data);
            lsb_msb_flag = 0;
          else
            tic;
            data_aligned = data_aligned + uint16(data);
            lsb_msb_flag = 1;
            plotdata = [plotdata data_aligned];
            if (bsize == length(plotdata))
              plotdata = typecast(plotdata, 'int16');
              [y idx] = updatedata(y, plotdata, idx, bsize, qsize);
              set (h, "ydata", y);
              axis (axistyp);
              set (ht, "string", t_txt(toc));
              sleep (0.008);
              plotdata = uint16([]);
            endif
          endif
        endif
      endwhile
  endswitch
뭐 대중 이런 식으로 하니 얼추 동작한다.
이래저래 맘에 안 드는 부분이 있긴 하지만,
데이터 동기화(MSB/LSB 등), 통신 프로토콜 등의 추가나, 그래프 화면을 입맛에 맞게 수정하는 등의 작업들은 조금씩 수정해 나가면 될 것 같다.
그래도 쉽게 쓸 수 있으니 그게 어디야..

댓글

이 블로그의 인기 게시물

환경개선부담금

[DevTip] Windows에서 tail 쓰기...