Raspberry Pi Pico Wを使用してステッピングモータをWi-Fi通信で制御する実験(Arduino IDE使用)


Raspberry Pi Pico WのWi-Fi機能を使用して、パソコンと接続してステッピングモータを制御する実験を行いましたので紹介します。

プログラミングツールとしてはArduino IDEを使用しています。
作成したプログラムはArduino IDEでスケッチ例として見ることが出来るWiFiServerを参考にしています。Raspberry Pi Pico W側をサーバーとして、パソコン側をクライアントとしてIPアドレスとポート番号を指定してイーサネット通信で接続し、パソコンからRaspberry Pi Pico Wにステッピングモータ制御コマンドを送信してモータを制御しています。

ステッピングモータの駆動はArduino言語のStepperライブラリを使用しています。

1.実験装置

ステッピングモータは28BYJ-48とそのドライバーボードのセットを使用しました。このステッピングモータとドライバーボードは複数のネット通販から購入することが出来ます。
コントローラとしてはRaspberry Pi Pico Wを使用して実験装置を組みました。(ステッピングモータ駆動の詳細に関してはこちらを参照して下さい)

28BYJ-48ステッピングモータとドライバ
28BYJ-48ステッピングモータ裏面
実験装置

今回使用したステッピングモータの主な仕様は表1の通りです。このモータは1/64のギアで減速されていて、1-2相励磁の場合にステップ角は5.625°/64なので4096ステップでモータ軸が1回転します。最大自起動周波数が500ppsなので、500パルス/sec以下で起動する必要があります。起動後は徐々に加速を行った場合に、最大で1000パルス/secまで仕様上は可能と思われます。
ただし、Arduino IDEのStepperライブラリーのstep()関数を使用した場合の励磁方式は2相励磁のようです。そのため2048ステップでモータ1回転となります。

項目仕様
相数4相
励磁方式1-2相励磁方式
ステップ角5.625°/64   (減速比1/64)
電圧5VDC
相抵抗22Ω ± 7% 25°C
最大応答周波数1000pps
最大自起動周波数500pps
引き込みトルク800gf.cm / 5VDC 400pps
表1.28BYJ-48ステッピングモータの主な仕様

2.実験装置回路図

図1に実験装置の回路図を示します。1個のステッピングモータをRaspberry Pi Pico Wに接続しています。

28BYJ-48ステッピングモータのドライバーボードはテキサスインスツルメンツのULN2003ANと言うダーリントントランジスタアレイが使用されています。ドライバーボード自体の資料が無かったので、テスタと基板のパターンを見て回路を調べました。

ステッピングモータのドライバーボードの入力信号IN1~IN4をRaspberry Pi Pico WのGP0~GP3に接続しました。

Wi-Fiルータを介してパソコンと接続します。

図1.実験装置の回路図

3.プログラムに関して

パソコンからRaspberry Pi Pico WへWi-Fi通信で表2で示した様なコマンドテキストを送ることによってステッピングモータが動作するプログラム(スケッチ)を作成しました。プログラミングツールとしてはArduino IDEを使用しています。

コマンドテキストステッピングモータ動作
“CW\n”時計方向に回転する
“CCW\n”反時計方向に回転する
“STOP\n”回転を停止する
表2.ステッピングモータ制御コマンド

3-1.Wi-Fi関係の関数の説明

最初に今回作成したプログラムで使用したWi-Fi関係の関数の概要を表3に示します。
作成したプログラムの説明用として記載したので、各関数のクラス部分はプログラム中で使用しているインスタンス名のまま記載しています。
関数の引数のバリエーションもプログラムで使用したものに限り記載しています。全てのバリエーションを記載した厳密なものではありませんので、ご注意お願い致します。

関数説明
#include <WiFi.h>Wi-Fi関係の関数のヘッダファイルのinclude
WiFiServer server(int port)指定されたポートで着信接続をリッスンするサーバーを作成する
引数: int
port: ポート番号を指定
WiFi.mode(WiFiMode_t m)Wi-Fiのモードを設定する
引数: WiFiMode_t
WIFI_OFF: (0) WiFi off
WIFI_STA: (1) WiFi stationモード
WIFI_AP: (2) WiFi APモード
WIFI_AP_STA: (3) WiFi stationモードかつAPIモード
戻り値: void
WiFi.setHostname(const char *name)DHCP 要求に使用するホスト名を設定する
引数:
const char *name ホスト名の文字列のポインタ
戻り値: void
WiFi.begin(const char *ssid, const char *passphrase)Wi-Fi接続を開始する
引数:
const char *ssid: SSIDの文字列のポインタ
const char *passphrase: パスワードの文字列のポインタ
戻り値: int
WL_CONNECTED: (3) ネットワークに接続されている
WL_IDLE_STATUS: (0) 電源入っているがネットワークに接続されていない
WiFi.status()現在のWi-Fiの接続状態をリターン
戻り値: uint8_t (wl_status_tで定義)
WL_NO_SHIELD: (255)
WL_STOPPED: (254)
WL_IDLE_STATUS: (0)
WL_NO_SSID_AVAIL: (1)
WL_SCAN_COMPLETED: (2)
WL_CONNECTED: (3)
WL_CONNECT_FAILED: (4)
WL_CONNECTION_LOST: (5)
WL_DISCONNECTED: (6)
WiFi.localIP()Wi-Fiルータから現在割り当てられているIPアドレスを読み出す
戻り値: IPAddress
server.begin();Wi-Fiサーバを開始する
戻り値: void
server.accept()Wi-Fiクライアントからの接続を取得する
戻り値: WiFiClient
(以後WiFiClient client = server.accept()で得たclientに対してデータの送受信等を行う)
client.println(const char[])引数で指定した文字列の末尾に’\r’と’\n’を付けてクライアントに送信する
引数:const char[] 文字列の配列
戻り値: 送信されたバイト数 (size_t)
(clientはWiFiClient client =server.accept()で得られたもの)
client.connected()クライアントと接続されているか確認する
戻り値: 接続中 true、切断中 false
(clientはWiFiClient client =server.accept()で得られたもの)
client.abailable()クライアントから受信したデータのバイト数をリターンする
戻り値: int
(clientはWiFiClient client =server.accept()で得られたもの)
client.readStringUntil(char terminator)クライアントから受信したデータを先頭からターミネータの文字まで読み込む
引数: char
ターミネータ文字
戻り値: String
読み込んだ文字列
(clientはWiFiClient client =server.accept()で得られたもの)
表3.WiFi関係関数説明

3-2.プログラム説明

今回作成したプログラムに関して2つの部分に分割して示します。

①初期処理 共通変数とsetup()関数

初期処理を行うsetup()関数等のソースコードを以下に示します。

// Raspberry Pi Pico W Wi_Fi通信ステッピングモータ動作テスト-----
#include <WiFi.h>
#include <Stepper.h>

//---ステッピングモータ関係変数-----
#define RPM_SPEED 10      //モータ回転速度(rpm)
#define STEPS 2048        //モータの1回転のステップ数

//経過時間判断用データ
unsigned long last_time = 0;       //前回の時間記憶
unsigned long delay_time = 0;      //処理待ち時間(次の処理をするまでの時間)

//回転指令フラグ
bool cw_mov_flag = false;   //CW回転指令フラグ
bool ccw_mov_flag = false;  //CCW回転指令フラグ

//ステッピングモータ初期設定
Stepper stepper(STEPS, 0, 2, 1, 3);//ステッピングモータ設定(1回転2048ステップ)

//------Wi_Fi関係---------------
const char* ssid = "ssid_data";         //SSID
const char* password = "password";      //パスワード
int port = 4242;                        //ボート番号
WiFiClient client;                      //WiFiClient
WiFiServer server(port);                //WiFiServer
bool connectFlag = false;               //クライアント接続フラグ

void setup() {
  Serial.begin(115200); //シリアルモニタ用シリアル通信開始
  
  //----ステッピングモータ関係セットアップ-----
  pinMode(0, OUTPUT);       //ステッピングモータ(青線)
  pinMode(1, OUTPUT);       //ステッピングモータ(桃線)
  pinMode(2, OUTPUT);       //ステッピングモータ(黄線)
  pinMode(3, OUTPUT);       //ステッピングモータ(橙線)

  //ステッピングモータ回転速度設定
  stepper.setSpeed(500);    //ステッピングモータの回転速度500rpm

  //処理待ち時間の設定
  delay_time = 60 * 1000 * 1000 / STEPS / RPM_SPEED;

  //-----WiFi関係セットアップ-----------------------
  WiFi.mode(WIFI_STA);        //WiFi Stationモード
  WiFi.setHostname("PicoW2");
  WiFi.begin(ssid, password); //WiFi接続実施
  while (WiFi.status() != WL_CONNECTED) { //WiFi接続待ち
    Serial.print(".");
    delay(100);
  }
  server.begin();           //WiFiサーバ開始
  
  //WiFiルータから割り当てられたIPアドレスとポート番号をシリアルモニタで表示
  Serial.printf("\nConnected to WiFi\n\nConnect to server at %s:%d\n", WiFi.localIP().toString().c_str(), port);
}

【説明】
2行目:
include <WiFi.h>でWi-Fi機能のヘッダファイルをインクルードしています。この処理によってWi-Fi関係関数の使用が出来ます。

21行目、22行目:
const char* ssidとconst char* passwordに使用するWi-FiルータのSSIDとパスワードをセットします。

31行目~41行目:
ステッピングモータ関係の設定部分です。

32行目~35行目:
pinMode(0, OUTPUT);~pinMode(3, OUTPUT);の処理で、I/OのGP0~GP3を出力ピンとしてステッピングモータ線に割り当てています。

38行目:
stepper.setSpeed(500)でステッピングモータの回転速度を500rpmに設定しています。但し実際には1ステップずつの駆動しかしないので、setSpeed()関数では回転速度は決まりません。他の処理を行うためにstep()関数からは出来るだけ速く抜け出るようにsetSpeed(rpms)のrpmsの値は大きめに設定しています。

41行目:
delay_time = 60 * 1000 * 1000 / STEPS / RPM_SPEED;
はステッピングモータを1ステップ駆動した後に次に1ステップ駆動するまでの待ち時間をモータの回転速度(rpm)とモータの1回転のステップ数から計算しています。時間の単位はμsecです。

43行目~51行目:
WiFi接続に到るまでの処理です。
46行目のWiFi.begin()関数でWiFiルータのSSIDとパスワードを指定してWiFi接続を行います。47行目のWiFi.status()関数でWi-Fi接続の状態を確認してWi-Fiが接続されるまで待ちます。

②ループ処理 loop()関数

ループで繰り返し処理を行うloop()関数のソースコードを以下に示します。この部分でパソコンからコマンドデータを受け取り、ステッピングモータの回転制御を行っています。

void loop() {
  if (connectFlag == false){        //クライアント切断中?
    client = server.accept();       //クライアントからの接続を取得
    if (!client) {
       return;
    }
    connectFlag = true;             //クライアント接続フラグセット
    client.println("connect finish");//クライアントに接続メッセージ送る
  }

  if (client.connected() == false){ //クライアント切断?
    connectFlag = false;            //クライアント接続フラグリセット
    Serial.println("disconnect");   //シリアルモニタに切断メッセージ
  }

  if (client.available() > 0){      //受信データ有り?
    String data = client.readStringUntil('\n');  //"\n"まで受信データ読込
   
    //----受信データ判断処理------------------
    if (data.startsWith("CW") == true){        //"CW"?
      ccw_mov_flag = false;                    //ccw回転フラグリセット
      cw_mov_flag = true;                      //CW回転フラグセット
      client.println("CW command ok");         //クライアントにメッセージ送る 
    }else if (data.startsWith("CCW") == true){ //"CCW"?
      cw_mov_flag = false;                     //cw回転フラグリセット
      ccw_mov_flag = true;                     //CCW回転フラグセット
      client.println("CCW command ok");        //クライアントにメッセージ送る 
    }else if (data.startsWith("STOP") == true){//"STOP"?
      cw_mov_flag = false;                     //cw回転フラグリセット
      ccw_mov_flag = false;                    //ccw回転フラグリセット
      client.println("STOP command ok");       //クライアントにメッセージ送る 
    }
  }

  //-----モータ回転処理----------------------
  //前回処理からの経過時間計算
  unsigned long pass_time;
  unsigned long now_time = micros();    //現在時刻読出し(usec単位)
  if (now_time >= last_time){
    pass_time = now_time - last_time;   //経過時間計算
  }else{
    //オーバフローを加味して経過時間計算
    pass_time = now_time - (0xffffffffUL - last_time + 1);
  }
  
  //モータ回転処理
  if (pass_time >= delay_time){     //時間到達?
    last_time = now_time;
    if (cw_mov_flag == true){       //CW回転?
      stepper.step(-1);             //-1ステップ回転
    }
    else if (ccw_mov_flag == true){ //CCW回転?
      stepper.step(1);              //1ステップ回転
    }
  }
}

【説明】
3行目:client = server.accept();
クライアント(パソコン)からの接続を取得します。

7行目:connectFlag = true;
クライアントとの接続が行われた時にconnectFlagをtrueにしています。

8行目:client.println(“connect finish”);
クライアント(パソコン)へテキスト”connect finish”を送信します。

11行目:if (client.connected() == false){
クライアント(パソコン)との接続が切れたかどうかの判断をしています。接続が切断されている場合はconnectFlag をfalseにしています。

16、17行目:
if (client.available() > 0)で受信データが有るか判断しています。client.available()関数は受信データのバイト数を返します。
受信データがある場合は、String data = client.readStringUntil(‘\n’)で受信データを先頭から改行コード(‘\n’)まで読み込みます。
受信データが有るか判断した後にclient.readStringUntil(‘\n’)で受信データを読み込むことによって、プログラム実行がデータ受信までの間client.readStringUntil(‘\n’)の部分で停止してしまうのを防ぎます。
プログラムの停止を防ぐことによって、その下方の行のステッピングモータの駆動動作を連続に行うことが出来ます。

20~32行目:
受信データの判断処理をしています。受信データによって以下の処理をしています。
受信データ ”CW” の時 cw_mov_flagをtureにする。
受信データ “CCW” の時 ccw_mov_flagをtureにする。
受信データ ”STOP” の時 cw_mov_flagとccw_mov_flagを共にfalseにする。

37~44行目:
今回のプログラムでは、一定時間間隔毎にステッピングモータを1ステップずつ駆動させています。このステップ駆動間の時間を取るのに、現在時間を読み込むmicros()関数を使用します。
micros()関数はプログラム実行を開始した時から現在までの時間をマイクロ秒単位で返す関数です。この関数で現在の時間を得て、前回処理した時の時間との差が予定した一定時間になった時にステッピングモータを1ステップ駆動することによって一定速度の回転が可能となります。

但し、注意が必要なのは、micros()のリターン値はunsigned longの32bitでオーバーフローすると零に戻ります。実際には0xffffffff (4294967295)を超えると零に戻ります。これは71.6分に相当します。
よって、micros()関数で得た現在時間から、前回の処理時にmicros()関数で得た時間を引いて経過時間を計算すると、前回処理した時の時間が71.6分近くの場合に、次の時間読込が0付近の値となってしまい経過時間が正しく計算出来ません。

今回作成したプログラムでは、現在時間と前回の時間を比較して、
現在時間 < 前回の時間
の場合は途中でオーバフローが発生したと判断して、次の様に経過時間を計算しています。
経過時間 = 現在時間 – (0xffffffff – 前回の時間 + 1)

47~55行目:
if (pass_time >= delay_time)で経過時間が予め予定した時間以上になっているか判断します。
経過時間が予定時間以上の場合に、cw_mov_flagがtureの場合はCW方向に1ステップ、ccw_mov_flagがtureの場合はCCW方向に1ステップステッピングモータを駆動しています。
ステッピングモータの駆動は、stepper.step(steps)関数をしています。これはStepperライブラリの関数でステッピングモータをstepsで指定したステップ数駆動します。

4.動作テスト

作成した実験装置の動作テストをするためのパソコン側のソフトウェアとして、NonSoftのTCP/IPテストツールを使用しました。このツールソフトはNonSoftの以下サイトから入手できます。
http://nonsoft.la.coocan.jp/Download/SocketTool/index.html

このTCP/IPテストツールを使用してパソコンとRaspberry Pi Pico WをWi-Fiルータを介してイーサネット接続して、以下のテキストをパソコンからRaspberry Pi Pico Wへ送ることによってステッピングモータが動作します。

“CW\n” ステッピングモータが時計方向に回転する
“CCW\n” ステッピングモータが反時計方向に回転する
“STOP\n” ステッピングモータが回転停止する

4-1.テスト手順

①パソコンとRaspberry Pi Pico WをUSBケーブルで接続してArduino IDEを起動します。今回作成したプログラムをArduino IDEで開きます。

②プログラム(スケッチ)のSSIDとパスワードの部分を、使用するWi-FiルータのSSIDとパスワードに変えます。

③ツール→シリアルモニタをクリックすると画面の下側にシリアルモニタ画面が表示されます。

④画面左上の→をクリックするとプログラムのコンパイルが行われ、その後プログラムがRaspberry Pi Pico Wに書き込まれます。

⑤Raspberry Pi Pico Wへのプログラムの書き込みが終了すると、プログラムの実行が開始されます。
プログラムはWi-Fiルータへの接続を行い、接続完了後にシリアルモニタにConnect to server at—-と表示されます。

本例では、atの後に表示される192.168.1.4がWi-Fiルータから割り当てられたRaspberry Pi Pico WのIPアドレスです。4242はポート番号です。
同じWi-Fiルータに接続されたパソコンからこのアドレスを指定してSocket接続することで、Raspberry Pi Pico Wと通信することが出来ます。

IPアドレスとポート番号を確認したらArduino IDEは閉じてしまっても良いです。

⑥TCP/IPテストツールを起動します。「TCP/IPの設定」の「クライアント」を選択して、Raspberry Pi Pico WがWi-Fiルータから割り当てられたIPアドレスとポート番号を設定して、「接続」ボタンをクリックします。(今回のプログラムではRaspberry Pi Pico W側はサーバー、パソコンがクライアントです)

⑦接続されると以下の様にRaspberry Pi Pico Wからパソコンへテキスト”connect finish”が送信されます。そのテキストがTCP/IPテストツールに表示されます。

⑧「送信するTEXT」欄に、CW<LF>と入力します。ここで<LF>はラインフィード(‘\n’)に相当します。「TEXT送信」ボタンをクリックすると、テキスト”CW\n”がRaspberry Pi Pico Wに送信されます。これはステッピングモータを時計方向に回転させるためのコマンドです。

⑨”CW command ok”と表示されてステッピングモータが時計方向に回転します。

⑩「送信するTEXT」欄に、STOP<LF>と入力します。ここで<LF>はラインフィード(‘\n’)に相当します。「TEXT送信」ボタンをクリックすると、テキスト”STOP\n”がRaspberry Pi Pico Wに送信されます。これはステッピングモータの回転を停止させるためのコマンドです。

画像に alt 属性が指定されていません。ファイル名: STOP.jpg

⑪”STOP command ok”と表示されてステッピングモータの回転が停止します。

⑫同様に「送信するTEXT」欄に、CCW<LF>と入力し、「TEXT送信」ボタンを押すとステッピングモータが反時計方向に回転します。
さらに「送信するTEXT」欄に、STOP<LF>と入力し、「TEXT送信」ボタンを押すと反時計方向に回転していたステッピングモータが停止します。

5.動作動画

以下動画は、Raspberry Pi Pico Wに今回作成したプログラムを入れて、パソコンとWi-Fi接続してステッピングモータを動作させる様子です。

6.最後に

パソコンとRaspberry Pi Pico WとをWi-Fiルータを介したイーサネット通信で接続してステッピングモータを制御することが出来ました。
次はRaspberry Pi Pico WにWebサーバを内蔵させ、パソコンのブラウザからステッピングモータを操作するプログラムにもトライしたいと思います。


PAGE TOP