[[How2Computing]] #author("2022-01-13T19:14:49+09:00","","") *How to use serial port on Mac OS X [#y0fdbfb6] //http://gyazo.com/0060e9c32c4f010456ec13e205e77764.png http://is.ocha.ac.jp/~gradthesis/gyazo/20140726120153.png http://is.ocha.ac.jp/~gradthesis/gyazo/20140726120104.png http://is.ocha.ac.jp/~gradthesis/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で読んでプログラムする方法を紹介します。 まずは、ネットでググった一般的なシリアルポートの情報をメモしておきます。 -http://linuxjf.sourceforge.jp/JFdocs/Serial-HOWTO.html -http://0n0.fc2web.com/paso/serial/serial01.html *Arduinoでシリアル通信(送信)するプログラム [#q0344560] 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 //http://gyazo.com/25e22833c7482de19c4580f9e0e85a3e.png また、このプログラムは、9600baudでボタンの状態を知らせます。 ボタンが押されていないと'H'の文字が、押されると'L'の文字がシリアルポートに流れます。 *Arduinoからのシリアル情報をC言語で読み取る [#q062972d] これを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追加) [#q062972d] 上記のプログラムに下記の関数を追加する事でC言語でArduinoへ数字を1文字送信出来るようになります。 void serialWriteChar(int num){ unsigned char result; int length = 0; write(fd,&num,1); } *Arduinoからのシリアル情報をPythonで読み取る [#e96c175f] 簡単です。 **pyserialをインストールする [#cbdb2cd7] +pyserialを入手する&br; --http://pypi.python.org/pypi/pyserial からpyserialを入手します。 今回は、 pyserial-2.5.tar.gz を使いました。 +pyserialをインストールする&br; ダウンロードした圧縮ファイルを開いて、 setup.pyを sudo python setup.py install として動かします。 **シリアルポートからデータを取得する [#u54bbc90] つぎに、適当にこんなプログラムを書きます。 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で読み取る [#g5db712a] こちらも簡単です。解説はこちらをみてください。 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://is.ocha.ac.jp/~gradthesis/gyazo/20140726121616.png *Arduinoからのシリアル情報をJavaで読み取る [#t78d8014] Windowsなどではシリアル通信するクラスが配布されていたと思います。 Macなどには無かったように思います(最近はあるのかもしれない)。 ここでは面倒ですが、 Cでダイナミックライブラリを作って、JNAで読む方法を紹介します。 これができればCで作ったもののすべてがJavaから使えるので嬉しい場面があるかと思います。 ダイナミックライブラリとJNAについては以下をみてください。 -http://is.ocha.ac.jp/~gradthesis/index.php?How2JNA 上記の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更新) [#t78d8014] 上記の方法で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と通信できるようになります。