Windowsのシリアル通信でBREAK信号を送受信する
https://msdn.microsoft.com/ja-jp/library/cc429842.aspx
https://github.com/bamchoh/go-serial/commit/f74298f65a6834a06357510f1d208ff8df764c11
SetCommMask で EV_BREAK を設定しておくこと。そうしないと、エラーになる。
あと、立ち上がりは検出できるけど、立下りは検出できなさそう。
一応、BREAK信号を送信するのと、受信(?)するののサンプルは以下のような感じで
package main import ( "bufio" "flag" "fmt" "log" "os" "time" "github.com/bamchoh/go-serial" ) type SIO struct { port serial.Port txq chan []byte rxq chan []byte } func (sio *SIO) Start() { go sio.Write() go sio.Read() } func (sio *SIO) Write() { for { q := <-sio.txq log.Printf("send: %q", q) sio.port.Write(q) } } func (sio *SIO) Read() { err := sio.port.SetCommMask(0x0041) if err != nil { fmt.Println(err) } for { buf := make([]byte, 128) n, err := sio.port.Read(buf) if err != nil { fmt.Println(err) time.Sleep(1 * time.Second) continue } sio.rxq <- buf[:n] } } func (s *SIO) SetBreak() { s.port.SetBreak() } func (s *SIO) ClearBreak() { s.port.ClearBreak() } var port = flag.String("p", "COM3", "port name") var baud = flag.Int("b", 9600, "baud rate") func main() { flag.Parse() c := &serial.Mode{BaudRate: *baud} s, err := serial.Open(*port, c) if err != nil { log.Fatal(err) } txq := make(chan []byte, 5) rxq := make(chan []byte, 5) sio := SIO{ s, txq, rxq, } go sio.Start() go func() { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { text := scanner.Text() s.SetBreak() time.Sleep(10 * time.Millisecond) s.ClearBreak() txq <- []byte(text) } }() for { q := <-rxq if err != nil { log.Fatal(err) } log.Printf("recv: %v", q) } }
余談だけど、LinuxだとBREAK信号の受信が\FF\00\00という3バイトに変換されてしまうみたい。 バイナリデータの送受信だと、このバイト列が来るようなプロトコルを使うようなものだと BREAK信号を送信できないという制限がかかってしまう。回避策ってないのかな?
https://stackoverflow.com/questions/14803434/receive-read-break-condition-on-linux-serial-port
追記(2018/7/17) Linux の BREAK信号の受信方法がなんとなくわかった。
Armadillo460シリアルとTFTP | アットマークテクノ ユーザーズサイト
ここによると、ioctlのTIOCGICOUNTを使うとできるらしい。
ラズパイで、確かにBREAK信号を受信するとカウントアップしていることが確認できた。
カウントが上がってることと、\xFF\x00\x00 のバイト列が来てることをダブルチェックして
BREAK信号を認識できるかもしれない。O_NONBLOCK
なので、ループがすごい回ってしまうのが
どうにかしたいところ。
(追記2018/7/18) 非カノニカルモードにすることでブロッキングしていても、指定のバイト数を受信すれば 処理が戻るようにできた。後、残ってる課題は カウンタのクリアの仕方かなぁ
The Linux Serial Programming HOWTO: $B%W%m%0%i%`Nc(B
以下にサンプルを例示しておく。
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <linux/serial.h> #define SERIAL_PORT "/dev/ttyAMA0" int main(int argc, char *argv[]) { unsigned char msg[] = "serial port open...\n"; unsigned char buf[255]; int fd; struct termios tio; int baudRate = B9600; int i; int len; fd = open(SERIAL_PORT, O_RDWR|O_NOCTTY); if (fd < 0) { printf("open error\n"); return -1; } tio.c_cflag += CREAD; tio.c_cflag += CLOCAL; tio.c_cflag += CS8; tio.c_cflag += 0; tio.c_cflag += 0; cfsetispeed(&tio, baudRate); cfsetospeed(&tio, baudRate); // non canonical mode setting tio.c_lflag = 0; tio.c_cc[VTIME] = 0; tio.c_cc[VMIN] = 1; ioctl(fd, TCSETS, &tio); // send break signal for 100 ms(?) tcsendbreak(fd, 100); tcdrain(fd); // write 2 bytes buf[0] = 'A'; buf[1] = 'B'; len = 2; write(fd, buf, len); tcdrain(fd); // waiting for receive any data while (1) { len = read(fd, buf, sizeof(buf)); if (0 < len) { // check break detection count struct serial_icounter_struct icount; ioctl(fd, TIOCGICOUNT, &icount); printf("brk: %d", icount.brk); // print received data for(i = 0; i < len; i++) { if(i != 0) { if((i % 8) == 0) { printf("\n"); } else { printf(":"); } } printf("%02X", buf[i]); } printf("\n"); } } close(fd); return 0; }