How2Computing

How to use serial port on Mac OS X

http://siio.jp/gyazo/20140726120153.png http://siio.jp/gyazo/20140726120104.png http://siio.jp/gyazo/20140726115840.png

MacOS Xで、RS232C型シリアルポートを使うためのメモ書きです。 実際には、すでにMacintoshには何年も前からシリアルポートが付いていません。 レガシー(遺産)な技術、すなわち失われた太古の技術と言ってもよいです。 とはいえ、RS232C風のシリアル通信は、簡単で便利なので、世の中ではあちこちで使われています。 それをMacintoshで読む場合には、USBシリアル変換器のようなものを使います。 また、現行のArduinoでは、USBシリアル変換器を内蔵していて、USB経由で、シリアル信号を送ってきます。

USBシリアル変換器やArduinoでは、FTDI社(http://www.ftdichip.com/) の チップが使われていることが多く、ここのVirtual COM port (VCP)ドライバというのをインストールすると、 /dev以下に旧来のシリアルポートと互換のスペシャルファイルが出来上がります。 また、一部のUSBシリアル変換器 ( http://www.sun-denshi.co.jp/scc/products/mobile/vs60r/vs60r.htm ) や、最新のArduino (Arduino Uno, Arduino MEGA 2560)は、USBの標準のCDCクラス (Communications Device Class) として動作するので、 ドライバーをインストールしなくても、/dev/cu.usbmodemXXXXというようなスペシャルファイルが現れます。

ここでは、このようにUSBを経由して送られてきたシリアル信号を、Mac OS Xで読んでプログラムする方法を紹介します。

まずは、ネットでググった一般的なシリアルポートの情報をメモしておきます。

Arduinoでシリアル通信(送信)するプログラム

Arduinoで以下のプログラムを作ったとします。

int outpin=13;
int inpin=2;

void setup() {                
  // initialize the digital pin as an output.
  // Pin 13 has an LED connected on most Arduino boards:
  pinMode(outpin, OUTPUT);     
  pinMode(inpin, INPUT);
  Serial.begin(9600);
}

void loop() {
  if ( digitalRead(inpin) == HIGH) {
     digitalWrite(outpin, HIGH);   // set the LED on
     Serial.print("H");
  } else {
     digitalWrite(outpin, LOW);    // set the LED off
     Serial.print("L");
  }
  delay(1000);              // wait for a second
}

写真のように、2番ピンに押しボタンスイッチを付けると、 これを押すことで13番のLEDが消えます。

http://gyazo.com/63b7dd8b1468470eba4d5b5451114024.png

また、このプログラムは、9600baudでボタンの状態を知らせます。 ボタンが押されていないと'H'の文字が、押されると'L'の文字がシリアルポートに流れます。

Arduinoからのシリアル情報をC言語で読み取る

これをC言語で読むには、以下のようにします。 (逆スラッシュが化けているかもしれないです。コピペするときはお気をつけください)

// #include <sys/types.h>
// #include <sys/stat.h>
#include <unistd.h> //for open() close() read() 
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <strings.h>
#include <signal.h> //to handle ctrl-c

#define BAUDRATE B9600
#define MODEMDEVICE "/dev/cu.usbmodem621"
#define _POSIX_SOURCE 1 /* POSIX compliant source */
#define FALSE 0
#define TRUE 1

int fd;
struct termios oldtio, newtio;
int isrunning = TRUE;

int serialOpen() {
	fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); 
	if (fd <0) {
		printf("open error: %s\n",MODEMDEVICE); 
		return -1; 
	}
	tcgetattr(fd,&oldtio); /* save current port settings */	
	bzero(&newtio,  sizeof(newtio));
	newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD;
	newtio.c_iflag = IGNPAR;
	newtio.c_oflag = 0;
	/* set input mode (non-canonical, no echo,...) */
	newtio.c_lflag = 0;
	newtio.c_cc[VTIME]    = 0;   /* inter-character timer unused */
	newtio.c_cc[VMIN]     = 1;   /* blocking read until 1 chars received */
	tcflush(fd, TCIFLUSH);
	tcsetattr(fd,TCSANOW,&newtio);
	return 0;
}

void serialClose() {
	tcsetattr(fd,TCSANOW,&oldtio); //restore previous port setting
	close(fd);
}

unsigned char serialReadChar() {
	unsigned char result;
	int length=0;
	tcflush(fd, TCIFLUSH); //clear the receiving buffer
	length = read(fd,&result,1); //blocked until reading 1 byte.
	return result;
}

void interrupt(int sig) { isrunning = FALSE; } //ctrl-c handler

int  main() {
	int c, res;
	unsigned char result;
	if(serialOpen() != 0) return -1;
	signal(SIGINT, interrupt); //set ctrl-c handler

	while (isrunning) {   /*endless loop until ctrl-c is typed */
          printf("value is %c\n", serialReadChar());
	}
 
	serialClose();
	return 0;
}

これを、gcc test.cなどしてコンパイルし、実行すると、 以下のように、読めていることがわかります。 ArduinoのIDEで /dev/tty.usb..を使っている場合は、上記のように、 /dev/cu.usbl... で動きます.

ちなみに、このプログラムはLinuxなどでも動くと思われます。

http://gyazo.com/fa5ef8361553618306c70d46523ea1a7.png

Arduinoにシリアル情報をC言語で書き込む(2015.05.29追加)

上記のプログラムに下記の関数を追加する事でC言語でArduinoへ数字を1文字送信出来るようになります。

void serialWriteChar(int num){
	unsigned char result;
	int length = 0;
	write(fd,&num,1);
} 

Arduinoからのシリアル情報をPythonで読み取る

簡単です。

pyserialをインストールする

  1. pyserialを入手する
  2. pyserialをインストールする
    ダウンロードした圧縮ファイルを開いて、 setup.pyを
    sudo python setup.py install
    として動かします。

シリアルポートからデータを取得する

つぎに、適当にこんなプログラムを書きます。

import serial

ser=serial.Serial('/dev/cu.usbmodem3a21',9600,timeout=2)

data = ser.read(10)

if len(data) != 0 :
    print data

ser.close()

10個のデータを取得して、約10秒後に表示してくれます(Arduinoから毎秒1個のデータが来るため)。

http://gyazo.com/ca59528f454106a9df41b1cceaf55dbe.png

Arduinoからのシリアル情報をProcessingで読み取る

こちらも簡単です。解説はこちらをみてください。 http://www.processing.org/reference/libraries/serial/

import processing.serial.*;
Serial myPort;

void setup(){
    myPort = new Serial(this,"/dev/tty.usbmodem3a11",9600);
}

void draw(){
  background(0);
  while(myPort.available() > 0 )
    println("value is " + myPort.readChar());
}

http://siio.jp/gyazo/20140726121616.png

Arduinoからのシリアル情報をJavaで読み取る

Windowsなどではシリアル通信するクラスが配布されていたと思います。 Macなどには無かったように思います(最近はあるのかもしれない)。 ここでは面倒ですが、 Cでダイナミックライブラリを作って、JNAで読む方法を紹介します。 これができればCで作ったもののすべてがJavaから使えるので嬉しい場面があるかと思います。

ダイナミックライブラリとJNAについては以下をみてください。

上記のCプログラムをダイナミックライブラリにします。 このプログラムの名前が、libserread.cだったとしたら、以下のようにコンパイルします。

cc -dynamiclib -o libserread.dylib libserread.c

このあと、JNAをインストールしたJavaの環境で、以下のプログラムをjavacでコンパイルしてjavaで動かせばokです。(確実にクローズするために60個データを表示してcloseするようにしました。)

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;

public class TestSerial {
  public interface CLibrary extends Library {
       CLibrary INSTANCE = (CLibrary) Native.loadLibrary("serread", CLibrary.class);
	int serialOpen();
	void serialClose();
	byte serialReadChar();
  }

  public static void main(String[] args) {
      CLibrary arduino = CLibrary.INSTANCE;

	arduino.serialOpen();
      for(int i=0;i<60;i++) System.out.println((char)arduino.serialReadChar());
      arduino.serialClose();
  }
}

ArduinoとJavaでシリアル通信をやり取りするためのクラス(2015.05.29更新)

上記の方法でCのダイナミックライブラリを作り、JavaとArduinoでシリアル通信(読み書き)できるようにするためのクラスです。 これは2014年に研究室有志メンバーで学祭に出店したリジョワーツ魔法魔術学校のアトラクション(薬草学・マンドラゴラ)の制御の為に作ったので、Classの名前がそれ仕様になっています。他で使う際には クラス名とコンストラクタの部分の「SerialFromMNDR」を任意の名称に変更して使って下さい。 また、Arduinoから情報を読み込むread()はChar型1文字分を読み込みますが、Arduinoへ書き込むwrite()の方は1桁の数字を送るようになっています。それぞれ需要にあわせて、Char型やint型はカスタマイズ氏て下さい。

import java.io.UnsupportedEncodingException;
import com.sun.jna.Library;
import com.sun.jna.Native;
/*
 * JavaでArduinoとシリアル通信をする 
 */
public class SerialFromMNDR {	 
	
	  public interface CLibrary extends Library {
	    CLibrary INSTANCE = (CLibrary) Native.loadLibrary("serread", CLibrary.class);
		int serialOpen();
		void serialClose();
		byte serialReadChar();
		void serialWriteChar(int num);
		
	  }
	    
	  CLibrary arduino;
	  
	  public SerialFromMNDR(){
		  arduino = CLibrary.INSTANCE;
	  }
	
	  /* シリアル通信開始 */
	  void open(){
		  arduino.serialOpen();
	  }
	    
	  /* シリアル通信終了 */
	  void close(){
		  arduino.serialClose();
	  }
	
	  /* Arduinoからの信号を読み取る */
	  String read(){
		  byte[] data= new byte[1];
		  data[0] = arduino.serialReadChar();
		  String strAscii = "";
		try {
			strAscii = new String(data, "US-ASCII");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		  return strAscii;
	  }
	  
	  /* Arduinoに一文字送る */
	  void write(int i){
				arduino.serialWriteChar(i);
	  }
	  
}

このクラスを呼び出して使う際には、まず

SerialFromMNDR.open();

でシリアル通信を開始します。その後はread()やwrite()で自由にArduinoと通信できるようになります。


トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2015-05-29 (金) 18:35:29 (908d)