LoginSignup
1
1

More than 3 years have passed since last update.

Arduino IDEを仮想COMポートに繋いでWindowsアプリから通信を吸い出す実験

Posted at

※ Qiitaの下書きが10件までという異常な少なさなので追い出し...

これを置いている間にqemuが正式版でAVRをサポートしたのでArduino IDEを使わない方向で作戦を大巾に修正して、結局用意していたArduinoエミュレータは捨てることにした。


以前の内容

Arduinoエミュレータが欲しいので用意することにした。AVRエミュレータは simavr とか Simulavr や qemu が既にあるので、これとArduino IDEを仮想COMポート com0com で接続するのが良いはず。

... simavrを使ったArduinoエミュレータとしては simulIDE が既に有るけど、自前のデバイスを足す方法がよくわからず。。GUIも要らないし。。

※ 今回は仮想COMポートを経由するところまでで、実際のArduinoエミュレータの組み立ては次回。

やること / できないこと

目的は「Arduino IDEとエミュレータを仮想COMポート経由で直結し、Arduino実機と同じUIで書き込み等行いたい」と言える。

image.png

(DTR信号は直結ではなくコンデンサを通している。これはDTRがローになる瞬間をリセット"パルス"にするため

Arduino の回路図を確認すると、RXD(受信データ)、TXD(送信データ)、DTR(端末レディ)の3線が接続されていることがわかる。このため、仮想COMポートを経由して、この3線をエミュレータに入力する必要がある。

DTR信号は正確には処理できない

実際の文字列データをやりとりするRXD/TXDと違って、DTRのような制御信号は1ビットのI/Oポートとして使える(DTRは出力専用)ため、PCのプログラム(= Arduino IDE)から任意のタイミングでOn/Offできる。この性質から、 ↑ の回路図にあるように、DTR信号はリセット信号として使われている。

Wikipediaの ヌルモデム のページにあるように、PC側のDTRは、com0comの他端から見るとDSR(とDCD)に接続されている。LinuxやWindowsのようなPC側のAPIでは、これらの信号が変化したタイミングは取得できるが、 信号の状態が変化するのを待つAPIしか存在しない つまり 取りこぼしした際の救済措置は一切存在しない ため、注意する必要がある。

(言い換えると、Linuxの TIOCMIWAIT や、Win32の WaitCommEvent0110両方 に反応し、どちらか片方に限定することができないため、Wait後に何か処理している内にビット状態が変化してしまうと見逃しが起こる。

このため、今回の目的では100 %正確に動作することは期待できない。これらのOSの通信APIは、モデムとかを操作する側(いわゆるDTE)のために設計されているので、モデム(いわゆるDCE)になりきるためにAPIが完備されているとは限らないと言える。)

仮想COMポート com0com の準備

Windows上の仮想COMポートデバイスとしてはcom0comが著名で、今の com0com はちゃんと64bit版が提供されている。インストール後、適当にポートを追加してからデバイスマネージャを開いてビックリマークが付いていないか確認する。

image.png

ビックリマークが付いている場合は、プロパティ → ドライバーの更新 からWindows Update経由でドライバを入手することになる。

Arduino IDEは(デバイス名先頭に \\.\ を付けないので) COMで始まる名前のポートしか使えない ようだ。なので、仮想COMポートの片方を "use Ports class" して適当なCOMポート番号を振るのが良い。

image.png

逆端はこちらで用意するプログラムからopenするので名前は適当でもちゃんと指定すれば動作する。

Win32 APIを使ったシリアルポートの監視

今回のプログラムもCygwinで実装している。実はCygwinには /dev/ttyS0 等でシリアルポートを直接扱う機能が付いているが、 TIOCMIWAIT のような必要なIOCTLが実装されていないようなのでWin32 APIを使っている。

一般的なPOSIX系OSと同様に、Win32でもシリアルポートはファイルのように扱うことができる。おおまかな処理の流れは、

  1. CreateFile APIでシリアルポートのデバイスファイルをopenする(今回の場合は \\.\VIRT0)
  2. SetCommState , SetCommMask APIでシリアルポートのボーレート等を設定する
  3. (文字受信スレッド) ReadFile で受信データを読み取る
  4. (DSR監視スレッド) WaitCommEvent で変化を待ち、GetCommModemStatusでDSRの状態を取得する

のようになる。 ReadFile および WaitCommEvent の各APIはブロッキングAPIであるため、これらのAPIを呼び出すためにスレッドを2つ用意している。

image.png

Arduino IDEを起動して適当に書き込みボタンを押すと、 0x300x20 が送られてきていることがわかる。Arduinoのブートローダであるoptiboot( https://github.com/Optiboot/optiboot )は、 STK500 Communication Protocolのサブセットを実装していて これらのバイト列の意味はSTK500 Communication Protocolで定義されている。

0x30 0x20STK_GET_SYNCCRC_EOP を表わしていて、AVR側からは 0x14 0x10 (STK_IN_SYNC STK_OK) で応答することが期待されている。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1