質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

Q&A

解決済

3回答

3608閲覧

Arduinoで4つのSerial通信を受信する方法

kekke59

総合スコア2

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

シリアルポート

シリアルポートは一度に一ビットごと移行される物理的なインターフェイスです。一般的には、9ピンのd-subコネクタであるRS-232を指します。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Arduino

Arduinoは、AVRマイコン、単純なI/O(入出力)ポートを備えた基板、C言語を元としたArduinoのプログラム言語と、それを実装した統合開発環境から構成されたシステムです。

0グッド

0クリップ

投稿2021/05/28 13:32

編集2021/05/28 13:50

前提・実現したいこと

一つの受信用Arduino(親Arduino)に対して,4つの送信用Arduino(子Arduino)を接続します.
このとき,いつどんな順番で送信されるかわからない子ArduinoからのSerial通信を親Arduinoで受信したいと考えています.
ただし,複数の子Arduinoからの通信は同時に来ることはなく,同時に送信される通信は一つだけです.

最初の方法とその結果

まず行ったのは,下のサンプルコードを実行することです.
Two Port Receive | Arduino
このサンプルでは2つのSoftwareSerialポートを定義し,2つのSerial通信を受信しようとしています.
実験に使った送信側・受信側のソースコードは以下のとおりです.

受信側ソースコード
送信側ソースコード

実験なのでArduinoの数を減らし,送信側・受信側それぞれ一つずつのArduinoで2ポートのSerial通信を試みました.
ほぼサンプルを変更していませんが,うまく受信できていないようです.
ほとんどの場合は,送信側のボタンを押して送信したにも関わらず,受信側のシリアルモニタには何も表示されません.
たまに受信に成功し表示されることはありますが,割合としては20回に1回くらいだと思います.

考えた方法とその結果

そこで,いくつかの解決策を考えて実験してみました.

1. Arduinoの外部にAND回路を追加
前提に書いたように同時に複数の通信が来ることはないので,親Arduinoに接続する4本の線をダイオードで作成したAND回路に接続し,4つの電圧のANDをとった結果を親Arduinoに接続する方法です.実際にこの方法で実験をしてみると正しく受信することができました.しかし,Arduinoの外部にAND回路を作る必要があるという欠点があります.

2. MultiUARTライブラリの利用
このライブラリは,SoftwareSerialでできなかった複数ポートからの同時受信を,最大4ポートまで可能にしてくれます.確かにこのライブラリを利用することで目的は達成されるのですが,下の注意書きが少し心配です.同時に受信するわけでないならもっとシンプルな方法があるのではないかと考えました.

調歩同期のための受信割込処理は非常に重く、16Mhz駆動時でも40%以上の CPUタイムを常に消費する。 16Mhz未満の、あるいは内蔵RC発振器を使用している場合、多くは期待通りに動作しない。 この点で SoftwareSerial の割り切った実装は理に適っている。

3. SoftwareSerialを書き換え
そこで考えたのがSoftwareSerialの受信用ピンを複数設定可能な仕様に変更する方法です.改変したソースコードとそれを使った受信用のソースコードを下に示します.

受信ピンを複数設定可能なSoftwareSerialのソースコード

主な変更点は以下のとおりです.

  1. #define PIN_NUM 4のように,設定するピンの数を示すPIN_NUMを定義
  2. uint8_t _receivePin[PIN_NUM]のように,メンバ変数の_receivePinなどを配列に変更
  3. SoftwareSerial(uint8_t *receivePins, bool inverse_logic = false);のように,コンストラクタでは受信に使うピンを配列で渡し,送信はしないため送信用ピンは渡さない
  4. メソッドrx_pin_read()では,各ピンのANDをとった結果を返すように変更

このような変更を加え,まずPIN_NUMを4に設定して実験を行いました.
利用した送信用のソースコードを下に示します.
送信用ソースコード

PIN_NUMが4だと受信に失敗しました.
下のように,ポート1とポート2から送信されたデータは正しく受信できていますが,ポート3とポート4では失敗しています.
どうやら,ポート3,4では最上位ビットに1が入ってしまうようです.

PIN_NUMが4のときの受信結果 1 10 10000011 10000100 求めている結果 1 10 11 100

ここで言っているポート1やポート2というのは送信用のポートのことですが,今回のソースコードではSoftwareSerialを初期化するときに与えたピンの配列と対応しています.つまりこの場合は,ピン16とピン14で受信した場合は最上位ビットに1が入って,受信に失敗してしまうということです.
ちなみに,この16,14というピン番号が問題なのかと考え,配列内でピンの順番を入れ替えてもpinsの2番目3番目に設定したピンで受信すると失敗します.おそらく,コンストラクタ内でsetRXが呼ばれる順番が関係しているのだと思います.

Arduino

1uint8_t pins[] = {8, 10, 16, 14}; 2SoftwareSerial portOne(pins);

次にPIN_NUMを3に変更して実験を行いました.
PIN_NUMが3の場合は,正しく通信することができました.

質問内容

  1. Two Port Receive | Arduino このサンプルがうまく動作しないのはなぜか?
  2. PIN_NUMが4だとダメで,3だとうまくいくのはなぜか?
  3. 目的を達成するために一番良いのはどの方法か?(ここにない方法があれば教えて下さい.)

補足情報(FW/ツールのバージョンなど)

Arduino IDEのバージョン:1.8.15
利用しているArduino:Pro Micro

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

Two Port Receive | Arduino このサンプルがうまく動作しないのはなぜか?

実験してないですけれど、
Softwareシリアルはlistenしている間しかデータを取れないから、
じゃないですか? データを送るタイミングを制御していないですよね? データを送ったときにたまたま受信側がlistenしていないと取りこぼしになるでしょう。

「どこまでできるか」に挑戦するのはそれはそれで楽しいですけれど、シリアルポートをたくさん持っているボードを使っちゃったほうが話が簡単だったり...
Nucleo-L152あたりUART5系統持ってます。Arduino環境もあるし。

投稿2021/05/28 22:52

thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

thkana

2021/05/28 22:55

外付け回路が必要になるけど、RS485...なんてのもこの機会に調べておくと役に立つ日が来るかも。
guest

0

Two Port Receive | Arduino このサンプルがうまく動作しないのはなぜか?

分かりません。

PIN_NUMが4だとダメで,3だとうまくいくのはなぜか?

後の方に読む3,4番のみが、最後に読む最上位bitだけ違っているところからして、
サンプリングに時間が掛かる分だけ1bitあたりの時間が増えてサンプリング位置が後ろにズレているのでしょう。
1bitあたりの待ち時間を微妙に加減してみて、0x55あたりを受信してみて1番でも4番でも正しく読めるような値を探してください。
もしくは入力ピンを全て同じポートにまとめて1回で全て同時に読めるようにする手もあります。入力ピンの配置の自由度が減りますが。

目的を達成するために一番良いのはどの方法か?(ここにない方法があれば教えて下さい.)

SoftwareSerial改造はわりと良い方法だと思います。

確かにこのライブラリを利用することで目的は達成されるのですが,下の注意書きが少し心配です.
同時に受信するわけでないならもっとシンプルな方法があるのではないかと考えました.

「受信割込処理は非常に重く、16Mhz駆動時でも40%以上の CPUタイムを常に消費する。」が心配なのであれば、SoftwareSerialは受信中にCPUを100%消費するので心配には及びません。
ただ、最高速が遅い点、同時に受信するわけでないのに不要に複雑な点を考えれば、状況によってはSoftwareSerial改造の方が向いていることもあるでしょう。

投稿2021/05/28 16:35

ikadzuchi

総合スコア3047

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

ダイオード4本ならそんなに面倒だとおもわないのだけど・・・

必ず1つしか送信しない(&全てソフトシリアル)、という条件なら送信側を

beginした後に、TXをINPUT_PULLUPにして、

実際の送信の箇所を

TXをOUTPUTに変更
write?で送信
TXをINPUT_PULLUPに変更

っていう感じに、それぞれ送信時のみOUTPUTにする仕様にした方が簡単じゃないかな。

投稿2021/05/28 15:57

nac_tnk

総合スコア494

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kekke59

2021/05/28 16:33

なるほど!!! そのとおりやってみたらできました! 今まで自分がやっていた努力がなんだったのかと思うほど簡単でした. 素早いご回答本当にありがとうございました.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問