Raspberry Pi Pico でEthernet接続する

概要

 先回投稿した Raspberry Pi Pico と W5500 Lite モジュール、またはW5500-EVB-Pico を使用し、Pico側をTCPサーバー,パソコンをTCPクライアントとして、ETHENET 接続テストを実施します。

テスト接続

①PICO側プログラム(TCPサーバー)
 クライアント側からデータを受信すると、受信データをそのまま返信するプログラムです。TCP通信を理解していないので、TCP通信と言ってよいのか判りませんが、通信は成立している様でした。
 サンプルプログラム [ファイル]ー[スケッチ例]ー[Ethernet]ー[WebServer] を改造したものです。

#include <SPI.h>
#include <Ethernet.h>

byte mac_address[] = { 0x**, 0x**, 0x**, 0x**, 0x**, 0x** }; 
byte ip_address[] = { *** , *** , * , *** };

EthernetServer server = EthernetServer(ポート番号);

void setup() {
  Ethernet.init(17);
  Ethernet.begin( mac_address , ip_address );

  Serial.begin(9600);
  while (!Serial) { ; }

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet not found.");
    while (true) {
      delay(1); 
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }
  server.begin();
}


void loop() {
  EthernetClient client = server.available();
  if (client) {
    String tmp = "" ;
    while (client.connected()) {
      int size = client.available();
      if (size) {
        char input[256];
        memset(input , 0 , sizeof(input));
        client.read((uint8_t*)input , sizeof(input) - 1 ) ;

        for(int i=0 ; i < size - 1 ; i++ ){ tmp += input[i] ; }
        if(input[size-1] != '\n'){ tmp += "_ERR" ; }
      }
      break;
    }
    
    delay(1);  
    //client.stop();    
    String rtn_msg = "DataReceived:" + tmp ;
    Serial.println( rtn_msg );
    server.println( tmp );
  }
}

②パソコン側プログラム(TCPクライアント)
 dobon.net さんのサイトのTCPクライアントプログラムを少しだけ変更して利用させて頂きました。
[参考] : https://dobon.net/vb/dotnet/internet/tcpclientserver.html

using System;
using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            
            while (true){
                
                Console.WriteLine("送信DATAを入力し、Enterを押して下さい。");           // サーバー送信データ入力   
                string sendMsg = Console.ReadLine();                                // 入力待ち
                if (sendMsg == null || sendMsg.Length == 0){ return ; }             // 未入力時、終了

                string ipOrHost = "***.***.*.***";                                  // サーバーIPアドレス、またはホスト名(string ipOrHost = "localhost";)
                int port = *****;                                                   // ポート番号

                System.Net.Sockets.TcpClient tcp = new System.Net.Sockets.TcpClient(ipOrHost, port); // TcpClient作成、サーバー接続
                //Console.WriteLine("サーバー({0}:{1})と接続しました({2}:{3})。",
                //    ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Address,
                //    ((System.Net.IPEndPoint)tcp.Client.RemoteEndPoint).Port,
                //    ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Address,
                //    ((System.Net.IPEndPoint)tcp.Client.LocalEndPoint).Port);
                    
                System.Net.Sockets.NetworkStream ns = tcp.GetStream();              // NetworkStream取得

                ns.ReadTimeout = 10000;                                             // 読取タイムアウト:10秒(デフォルト:Infinite(タイムアウトしない))
                ns.WriteTimeout = 10000;                                            // 書込タイムアウト:10秒(デフォルト:Infinite(タイムアウトしない))

                System.Text.Encoding enc = System.Text.Encoding.UTF8;               // 送信文字列をByte型配列に変換
                byte[] sendBytes = enc.GetBytes(sendMsg + '\n');                    // 送信文字列をByte型配列に変換
                ns.Write(sendBytes, 0, sendBytes.Length);                           // データ送信
                //Console.WriteLine("送信DATA : {0}", sendMsg);

                Thread.Sleep(10);
                
                System.IO.MemoryStream ms = new System.IO.MemoryStream();           // サーバーからデータ受信
                byte[] resBytes = new byte[256];
                int resSize = 0;
                do {
                    resSize = ns.Read(resBytes, 0, resBytes.Length);                // データ一部受信
                    if (resSize == 0){
                        Console.WriteLine("サーバーが切断しました。");              // Readが0を返した時はサーバーが切断したと判断
                        break;
                    }
                    ms.Write(resBytes, 0, resSize);                                 // 受信データ蓄積

                } while (ns.DataAvailable || resBytes[resSize - 1] != '\n');        // 読取データ有、またはデータ最後\nでない時、受信継続

                string resMsg = enc.GetString(ms.GetBuffer(), 0, (int)ms.Length);   // 受信データ文字列変換
                ms.Close();

                resMsg = resMsg.TrimEnd('\n');                                      // 末尾 \n 削除
                Console.WriteLine("受信DATA : {0}",resMsg);

                ns.Close();                                                         // 閉じる
                tcp.Close();
                //Console.WriteLine("切断しました。");
                Console.WriteLine("");
                Console.WriteLine("");
                //Console.ReadLine();
            }
        }
    }
}

③プログラム実行確認
 PICO側とパソコン側のプログラムをそれぞれ実行します。
 下図はパソコン側プログラム実行状態で、送信データを入力・送信すると、直ちにPICO側からのデータを受信・表示します。この動作を繰り返します。通信出来ることを確認しました。

  

コマンドでPICOを制御する

 確認した通信方法を用いて、簡易的にパソコンからのコマンドにより、PICOを制御するテストを行います。
 次写真の通りPICOにシリアルLED(NeoPixel)を接続し、パソコンからのコマンドにより、LEDを点灯します。シリアルLEDの最右を“0”、最左を“4”とし、番号を指定して点灯します。


①PICO側プログラム

#include <SPI.h>
#include <Ethernet.h>

byte mac_address[] = { 0x**, 0x**, 0x**, 0x**, 0x**, 0x** }; 
byte ip_address[] = { *** , *** , * , *** };

EthernetServer server = EthernetServer(ポート番号);

#include <Adafruit_NeoPixel.h>
#define PIN 15                // 信号ピン指定
#define NUMPIXELS 5           // LED数指定
int brightness=50;            // 明るさ
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

void setup() {
  Ethernet.init(17);    
  Ethernet.begin( mac_address , ip_address ); 

  Serial.begin(9600);

  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); 
    }
  }
  if (Ethernet.linkStatus() == LinkOFF) {
    Serial.println("Ethernet cable is not connected.");
  }
  server.begin();

  pixels.begin();   // NeoPixel出力ピンの初期化
  pixels.setBrightness(brightness);
  pixels.clear();
  for(int i=0; i<NUMPIXELS; i++) {
    pixels.setPixelColor(i, pixels.Color(100, 255, 100));
    pixels.show();
    delay(150);
  }

  delay(200);
  pixels.clear(); pixels.show();
}


void loop() {
  EthernetClient client = server.available();
  if (client) {
    String cmds[7] = { "" };
    String tmp = "" ;
    while (client.connected()) {
      int size = client.available();
      if (size) {
        char input[256];
        memset(input , 0 , sizeof(input));
        client.read((uint8_t*)input , sizeof(input) - 1 ) ;

        for(int i=0 ; i < size - 1 ; i++ ){ tmp += input[i] ; }
        if(input[size-1] != '\n'){ tmp += "_ERR" ; }
      }
      break;
    }
    
    delay(1);         // give the web browser time to receive the data
    //client.stop();    // close the connection:
    int index = split(tmp, '_', cmds);

    String rtn_msg = cmds[0] + "-" + cmds[1] + " (" + String(index) + ")" ;
    server.println( rtn_msg );

    if(cmds[0] == "PXON"){
      pixels.setPixelColor(cmds[1].toInt(), pixels.Color(100, 255, 100));
      pixels.show();
      Serial.println("PXON");
   } else {
      pixels.clear(); pixels.show();
      Serial.println("UNKNOWN CMD: " + cmds[0]);
    }
  }
}


int split(String data, char delimiter, String* dst) {
  int index = 0;
  int arraySize = (sizeof(data)) / sizeof((data[0]));
  int datalength = data.length();

  for (int i = 0; i < datalength; i++) {
    char tmp = data.charAt(i);
    if (tmp == delimiter) {
      index++;
      if (index > (arraySize - 1)) return -1;
    } else dst[index] += tmp;
  }
  return (index + 1);
}

②プログラム実行確認
 PICO側プログラムは変更しましたが、パソコン側は変更せず、そのまま使用します。“PXON_(番号)” の形式でコマンド送信します。それ以外のデータを送信した場合は、全消灯します。

まとめ

 ネットワーク経由でのPICO制御はある程度実現できそうです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です