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

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

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

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

Q&A

解決済

3回答

9923閲覧

C言語 Arduino サーボモータの停止指令プログラム

himuka55

総合スコア1

Arduino

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

0グッド

0クリップ

投稿2020/10/20 12:56

前提・実現したいこと

Arduinoでサーボモータ制御の学習をしています。
リモコンスイッチで動作指令しています。連続作動中にリモコンスイッチ
例えば4番を押すことで途中停止させようとプログラム作りに挑戦していますが
うまくいきません。
今のプログラムは以下の通りです。回転速度を調整できるプログラムです。
リモコン作動のプログラムはネット上から拝借したものです。
プログラム後半の他のスイッチを押すとfor(;;)で連続動作するようにしてます。
いまのプログラムでリモコンスイッチを押すことによるサーボモータ動作は
問題ありません。
for(;;)で連続回転している途中で例えば4番スイッチを押すと
途中停止させるプログラムが分かりません。
いろいろbreak とか digitalWrite(...LOW)とか試しましたが
うまくいきません。 なお
サーボモータはTower Pro SG92Rです。
教えてください。よろしくお願い致します。

#include <VarSpeedServo.h> //2servo speed control rimokon
//1番SWONで緑点灯、2個順次サーボ動作連続
//2SWで赤LED点灯消え、3SWで緑LED点灯消え、その他swで赤LED点灯2番サーボ連続動作 OK 2020.10.18
VarSpeedServo myservo1; // インスタンス作成
VarSpeedServo myservo2;
#define INFRARED 11 // 赤外線センサー
#define RECEIVE_OK 3 // 緑LED
#define RECEIVE_ERROR 4 // 赤LED
void setup() {
Serial.begin(9600);
// 赤外線
pinMode(INFRARED, INPUT);
pinMode(RECEIVE_OK,OUTPUT);
pinMode(RECEIVE_ERROR,OUTPUT);
myservo1.attach(9); //servo1 D9ピンをサーボ1の信号線
myservo2.attach(10); //servo2 D10ピンをサーボ2の信号線
}
void loop() {
unsigned long dword = 0, pls;
// リーダーコードの開始(8T = 9ms)
while(digitalRead(INFRARED) == HIGH){
// none
}
// リーダーコードの終了(4T = 4.5ms)
while(digitalRead(INFRARED) == LOW){
// none
}
// NECフォーマットの読み込み(32bit)
// (16bitのカスタマーコード(メーカー識別コード) + 8bitのデータコード + 8bitの反転データコード)
for(int i = 0; i < 32; i++) {
// パルスの検出(パルスの長さ)
// ※戻り値はマイクロ秒(μs)
pls = pulseIn(INFRARED, HIGH);
// パルス検出のタイムアウト(1秒)
if(pls >= 1000000) {
Serial.println("timeout");
return;
}
// ビットが1の場合にデータを加算する
// ※ビット0= 1125μs ビット1 = 2250μs
if(pls >= 1126){
// Serial.println(pls);
dword |= 1UL << i;
}
}
// 待機状態のエラーを回避
// ※このスケッチ固有のエラーです。
if(dword == 0){return;}
// リピートコード(9ms + 2.25ms)
pulseIn(INFRARED, HIGH);
// ストップビット(0.56ms)
while(digitalRead(INFRARED) == HIGH){
// none
}
// データコードが正しく受信できているかを確認
byte bit8_data1 = (dword >> 16) & 0xFF; // データコード
byte bit8_data2 = 255 - ((dword >> 24) & 0xFF); // データコードの反転
if(bit8_data1 == bit8_data2){
// 送信波形を反転する
dword = (dword >> 24) & 0xFF |
((dword >> 16) & 0xFF) << 8 |
((dword >> 8) & 0xFF) << 16 |
(dword & 0xFF) << 24;
// デバッグ用
//リモコンキー1番
Serial.println(dword ,HEX);
if(dword == 0xFF0CF3){
digitalWrite(RECEIVE_OK,HIGH);//sw1押すと緑LED点灯してサーボ1動いて次に2が動いて停止 緑led off
delay(300);
myservo1.write(180,100,true);
myservo1.write(0,100,true);
delay(2000);
myservo2.write(180,100,true);
myservo2.write(0,100,true);
delay(2000);
digitalWrite(RECEIVE_OK,LOW);
delay(2000); //緑LED消える
}
else if(dword == 0xFF18E7) //2のスイッチ押すと赤1LED点灯してすぐ消える
{
digitalWrite(RECEIVE_ERROR,HIGH);
delay(1000);
digitalWrite(RECEIVE_ERROR,LOW);
}
else if(dword == 0xFF5EA1)//3のスイッチ押すと緑LED点灯してすぐ消える
{
digitalWrite(RECEIVE_OK,HIGH);
delay(1000);
digitalWrite(RECEIVE_OK,LOW);
}
else { //他のsw押すと赤LED点灯2サーボが連続動作
digitalWrite(RECEIVE_ERROR,HIGH);
delay(300);
for(;;)
{
myservo2.write(180,100,true);
delay(1000);
myservo2.write(0,100,true);
delay(1000);
//if (dword == 0xFF0CF3) 連続作動中に例えばsw1で停止させる方法が分からない
// digitalWrite(9,LOW);
}
}
}
}

ここに質問の内容を詳しく書いてください。
(例)PHP(CakePHP)で●●なシステムを作っています。
■■な機能を実装中に以下のエラーメッセージが発生しました。

発生している問題・エラーメッセージ

エラーメッセージ

該当のソースコード

ソースコード

試したこと

ここに問題に対して試したことを記載してください。

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

y_waiwai

2020/10/20 23:20

このままではコードが読みづらいので、質問を編集し、<code>ボタンを押し、出てくる’’’の枠の中にコードを貼り付けてください
guest

回答3

0

ヘルプ等を参考にソースコードを掲載して下さい。特に#記号は特別な意味を持っているので悲惨なことになっています。

で。今のプログラムは

Arduino

1void loop(){ 2 //リモコンコードの受信 3 //受信したコードをdword変数の設定 4 //受信したコードによる動作 5 for(;;){ 6 //サーボの操作 7 } 8}

という構造になっていますから、一度for(;;)のループに入ってしまうとリモコンの受信も、コードの更新も行われないのでリモコンの操作で脱出は不可能です。
しかも、リモコンコードの受信部分は

Arduino

1 // リーダーコードの開始(8T = 9ms) 2 while (digitalRead(INFRARED) == HIGH) { 3 // none 4 } 5 // リーダーコードの終了(4T = 4.5ms) 6 while (digitalRead(INFRARED) == LOW) { 7 // none 8 }

となっているため、リモコン信号を受信するまでは先に進まない構造です。
つまり、小手先で

いろいろbreak とか digitalWrite(...LOW)とか試し

てもダメです。根本的に構造を考え直さないと。例えば

Arduino

1void loop(){ 2 if( digitalRead(INFRARED) == HIGH){//何か受信している? 3 //リモコンコードの受信 4 }else{//受信していない 5 switch(直前に受信したリモコンコード){ 6 case キー1: 7 //キー1を受信したときの処理。ただしここに滞留してはいけない。 8 //そのためdelayを使用しないで、loop()が繰り返し実行される 9 //ことを前提にmillis()等を用いて時間を測って動作を切り替える 10 break; 11 case キー2: 12 //キー1と同様 13 break; 14 case キー3: 15 //同様 16 break; 17 default: //他のキー 18 //同様 19 } 20}

のようなことにすることでなんとかならないでしょうか。

loop()の繰り返しを前提に時間を測る、というのは例えばLチカで

Arduino

1unsigned long refTime; //時間を測る原点を格納 2bool ledStat=true; 3void setup(){ 4 //他の処理 5 refTime=millis(); //時間を測る原点を設定 6} 7void loop(){ 8 unsigned long curretTime; 9 currentTime=millis(); 10 if(currentTime-refTime>1000){//前回から1秒以上経った 11 ledStat=!ledStat; //LED状態を反転 12 refTime=currentTime; //時間を測る原点を再設定 13 } 14 digitalWrite(RECEIVE_OK,ledStat);//LED状態を反映 15}

のようにして、プログラム中にループやdelay()で留まる場所を作らずに時間の管理をする、ということです。

投稿2020/10/20 23:59

thkana

総合スコア7703

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

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

himuka55

2020/10/21 11:35

返信ありがとうございます。 void loop(){ if( digitalRead(INFRARED) == HIGH){//何か受信している? //リモコンコードの受信 }else{//受信していない switch(直前に受信したリモコンコード){ case キー1: //キー1を受信したときの処理。ただしここに滞留してはいけない。 //そのためdelayを使用しないで、loop()が繰り返し実行される //ことを前提にmillis()等を用いて時間を測って動作を切り替える break; case キー2: //キー1と同様 break; case キー3: //同様 break; default: //他のキー delayを使わないでmillisを使い時間を測って... 私は初心者ですのでよく意味がわかりませんがなんとか挑戦したいと思います。 switch case break など初めてですがトライしてみます。 ネット上で参考にあるものがあれば紹介お願い致します。 なお、参考書として 「arduinoでロボット工作をたのしもう第2版」秀和システム 鈴木美朗志著 「やさしいC 第4版」高橋麻奈著 などがあります。 よろしくお願い致します。
guest

0

ベストアンサー

これだと、連続動作中もそうなのですけど、通常の動作中もリモコン操作は受け付けないですよね?
必ず一連の操作が終わらないとリモコン受信にならないし、逆にリモコン受信の所に行けばそれ以外の動作は出来ません。
簡単に言えば、この手法だと詰んでいる=スケッチを大きく変えないとならない、という事です。

パッと思い付く方法は3つですね。
・リモコン受信に外部割り込みを使う。※外部割り込みでは直ぐcliにして、サーボライブラリが動くようにする。
・タイマー割り込みで、サーボ処理を行う。=リモコン部はそのまま
・0.1~0.3ms程度のポーリング(タイマ割り込みでもloop関数でも良い)で、リモコン、サーボ操作の両方とも書き換え

今後の事を考えれば、ポーリングでリモコン受信させた方が良さそうには思いますけど…
いずれにしても、現在のサーボ処理のスケッチは割り込み処理の形(カウントして計測する)に変える事になると思います。

※VarSpeedServoライブラリが裏でタイマ1を使い、タイマ割り込みで動作している、という事も頭に入れておいてください。

<追記1>
外部割り込みでリモコン取得も、開始信号から信号最期、或いはエラーまで専有するやり方と、毎回時間を計測して、直ぐに終了するやり方があると思うけど、
前者なら、現在のスケッチからの変更は少ないです。けれども、他の動作に支障が出やすいです。
後者なら、書き直しになりますけど、こちらの方がベーシックなやり方だと思いますし、他のスケッチと共存し易いです。

で、後者の方を教えます。

まず、リモコン通信のフォーマットを確認して下さい。
ChaNさんの所が参考になります。http://elm-chan.org/docs/ir_format.html

そこに書いてあるようにNECフォーマットの場合は562μsが基準となり、信号の長さで0と1が決まります。
受信側からけば、HIGHの長さが基本ですから、HIGHの長さだけ測っておけばいいです。
※送信信号が有る時は、受信側はLOWです。何もないときはHIGHです。その図からは反転します。
(といっても、受信側の回路にもよりますけど)

考え方は、HIGHになった時に、測定開始時刻を記録、LOWになった時に、HIGHの時間を算出。
その時間が、ちゃんとフォーマットの通りに来ていれば記録を続けるけれども、一度でも範囲外なら、そこまでのデータは捨てます。
で、32bit分送られて来たら、どのSWかの判定です。

実際のRAWデータで、どの位の時間なのかを確認した方が良いです。案外いい加減なものですから、範囲を外している場合があります。

↓が外部割り込み関数のサンプルです。
※つらつらと書いただけで、検証はしていません。内容を読み取ってください。

Arduino

1volatile byte ir_num=0;//割り込みとloop関数両方で参照する変数は外部変数で、volatileを付ける。 2 3void recv() {//外部割り込み関数 4 static byte state=0;//0:測定前、1~:受信中 5 static uint32_t up=0,length=0,result=0; 6 7 if(digitalRead(pin))up=micros(); 8 else { 9 length=micros()-up; 10 if(length>4000 &&length>5000)state=1;//開始条件8T=4496 11 else if(state) { 12 if(length>460 && length<760)state++;//T=562 13 else if(length>1400 && length<1900) {//3T=1686 14 result|=(0x80000000UL>>(state-1)); 15 state++; 16 } else {//範囲外ならやり直し 17 state=0; 18 result=0; 19 } 20 if(state==33) {//データが揃ったら 21 switch (result) { 22 case 0xFF0CF30D: 23 ir_num=1; 24 break; 25 case 0xFF18E718: 26 ir_num=2; 27 break; 28 case 0xFF5EA15E: 29 ir_num=3; 30 break; 31 default: 32 ir_num=4; 33 34 } 35 state=0; 36 result=0; 37 } 38 } 39 } 40} 41

で、この結果(ir_num)を受けて、サーボを操作するわけですけど、リモコン信号が来た場合に、
・即時に停止、或いは次の動作へ移行。(停止の場合はその場に停止か、HOME位置へ戻すか?)
・サイクルを実行してから次の動作平行
があると思います。
(現在はサイクルな訳ですけど。その場合、ちゃんと受信しているのか判りにくくなります。
今回のトグル動作とかも、長いサイクルの場合は判り難く、2回押す事も考えられます)

もう一点、現在、myservo○.write(○,○,true);(終わるまで待機)を使っていますけど、
1台のサーボなら兎も角、複数だと、1台ずつしか動かせません。
本当にそれで良いのでしょうか?

(つまり、現在のスケッチが、狙い通りの動きなのか、書き方が判らないからそうなっているのかが判断付きません)

処理的には複雑にはなりますけど、2台を別のタイミングでそれぞれ同時に操作する事は可能です。

そのあたりをちょっと聞いてから追記します。
※「sw1で停止」のみの対応なら簡単です。

投稿2020/10/21 02:09

編集2020/10/21 12:36
nac_tnk

総合スコア494

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

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

himuka55

2020/10/21 02:59

早速の返信ありがとうございます。全くの初心者ですのでよろしくお願い致します。 1番目のリモコン受信に外部割込みを使う方法にまずトライしたいと思います。 割り込みは具体的にはどういうコマンドを使うのですか? 当方は現在、参考書として 「arduinoでロボット工作をたのしもう第2版」秀和システム 鈴木美朗志著 「やさしいC 第4版」高橋麻奈著 などがあります。 ネット上で参考になるものがあればご紹介お願い致します。
himuka55

2020/10/22 02:03

ご指導ありがとうございます。 ①リモコン通信のフォーマット確認ですがオシロスコープなどで計測しないと具体的な数値は分からないのではないでしょうか? 現状のプログラムでリモコンスイッチ番号とシリアルモニタの表示がプログラム通りになっているのでNEC方式と考えていいのではないでしょうか。 ②割り込みプログラム   初心者の私には相当レベルが高いので理解に苦しんでいます。   できる限りトライしたいと思います。 ③myservo○.write(○,○,true);   サーボモータの速度制御をするためにVarSpeedServo.hを使っています。   このコマンド myservo1.write(180,100,true)は1個づつしか動かないのですか。   同時に2個以上動かすためには速度指定ができない servo1.write(180); delay(100); servo2.write(90); delay(100); ...にするということですか?   私は実際は4足歩行ロボットを作ろと思っています。全部で8個のサーボモーターを   制御しようと思っています。ハードつくりも並行して進めています。  よろしくお願い致します。
nac_tnk

2020/10/22 03:59 編集

①いやいや、普通にスケッチを書けば良いだけですよ。  ↑でもHIGH長さを測っていますから、その後に  Serial.println(length);  を付けて、シリアルモニタで実際の長さ、バラつきを確認します。  でそれを元に、多少if(○○<length && △△>length)という部分の値を調整します。  勿論、想定通りなら修正する必要は無いです。 ②↑の外部割り込みのスケッチの事を言っているのですよね?  明確に書いていないですけど、ピン変化(CHANGE)=↑↓の両方で割り込み関数に飛ぶようにします。  attachInterrupt (0, recv, CHANGE);//int0はD2ピン(UNOなら) とかをsetup関数に記述します。    で、まずは > if(digitalRead(pin))up=micros();//↑(LOWからHIGHへの変化)の時は開始時刻upを記録 > else { > length=micros()-up;//↓(HIGHからLOWへの変化)の時はHIGHのパルス長(length)を算出 です。後はこのパルス長lengthが規格通りかどうかを判断します。 stateは、0は何も情報が無い、無通信状態です。スタートのシグナル(8T)が来たら1です。後は1bitずつ情報が入れば1増えます。 ※開始時のresultが0なので、そのピットが0の時は、stateをインクリメントするだけ、1の時だけ、resultのそのビットを1にします。 途中で規格外が来れば最初からやり直しでstateは0に戻します。(resultもやり直しなので0にする) stateが33になったら、32ビット情報を取得した事を意味します。 ③勿論、ライブラリ的にはサポートしています。同時に動かす事も可能です。ただし、  myservo1.write(180,100,true)の最後の「true」は、「移動終了までここに留まる(監視する)」という意味です。  その間は他には何も出来ないので、同時に動かす事は出来ません。。  この書き方をしている内は、8台制御するにも、「1台ずつの切り替え」になってしまいます。    例えば、サーボ1,2共に0の位置にいるとして、  myservo1.write(180,50);  delay(500);  myservo2.write(180,50);  とすれば、サーボ2が0.5秒遅れで同じように(同時に)動く、という事は判ると思います。  ※ただしこれでも、delay(500)があるので、その間は他のサーボは動かせない事になります。    別の書き方をすれば  myservo1.write(180,50);  while(myservo1.read()<60);//サーボ1が60度になるまで待機  myservo2.write(180,50);  で、凡そ60度ずれた状態で回ります。  ※ただし、この方法もまた、60度になるまでそこで足止めをくらいます    要は、どう動かしたいのか?です。一連の動作サイクルをする必要があるのか?それとも、リモコンカーのように、  瞬時に命令を実行して欲しいのか?で、組み方が変わります。    ※trueを使う手法が駄目と言っている訳ではありません。「現在の内容=やりたい動作」なら、それが簡単に書けてBESTとも言えます。
guest

0

for(;;)

で無限ループさせてしまうと、そこで処理が止まってしまい、他の動作ができなくなりますねー
ここらへん、考え方からまるっきり変える必要があります

投稿2020/10/20 23:24

編集2020/10/20 23:25
y_waiwai

総合スコア88042

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

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

himuka55

2020/10/21 11:00

ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問