🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

Arduino

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

Q&A

解決済

2回答

12915閲覧

ESP32のAPモードで複数台のクライアントと同時通信したい

moto_short

総合スコア1

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

Arduino

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

0グッド

0クリップ

投稿2020/12/21 08:51

編集2020/12/23 08:42

前提・実現したいこと

ESP32をアクセスポイントモードで使用し、内部に立てたTCPサーバと、クライアントモードで動作する2台のESP32と同時にデータをやり取りしたい。

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

サーバーからメッセージを接続中の2台のクライアントに送信する際、1台のみにしか送信できず。

ESP32のAPモードの仕様

前世代のESP8266では、softAPモードと称したWi-FiDirectモードであったらしく、32については情報を得られず。

該当のソースコード

Arduino

1/* 2 * アクセスポイント側 3 * サーバーからクライアントに文字を送信してみる 4 */ 5 6#include <WiFi.h> 7 8static const char *WIFI_SSID = "WROOM32"; 9static const char *WIFI_PASS = "password"; 10 11//サーバー設定------------------------------------------------- 12IPAddress ip(192, 168, 1, 10); 13WiFiServer server(1337); //Port番号 14//------------------------------------------------------------- 15 16void setup() { 17 Serial.begin(115200); 18 19 //Wi-Fiアクセスポイント開始-------------------------------------------------------- 20 WiFi.mode(WIFI_AP); 21 WiFi.softAP(WIFI_SSID, WIFI_PASS); 22 WiFi.softAPConfig(ip, WiFi.gatewayIP(), WiFi.subnetMask()); 23 //WiFi.softAPConfig(ip, gateway, netmask); 24 server.begin(); 25 26 IPAddress myIP = WiFi.softAPIP(); 27 Serial.println("APStarted. myIP Address:"); 28 Serial.println(myIP); 29 Serial.print("server Mac Address: "); 30 Serial.println(WiFi.macAddress()); 31 Serial.print("Subnet Mask: "); 32 Serial.println(WiFi.subnetMask()); 33 Serial.print("Gateway IP: "); 34 Serial.println(WiFi.gatewayIP()); 35 //------------------------------------------------------------------------------ 36 delay(500); 37} 38 39void loop() { 40 WiFiClient client = server.available(); 41 42 if(client > 0){ 43 Serial.println("newclient"); 44 while(client.connected()){ 45 client.println("println"); 46 delay(1000); 47 client.println("\n"); 48 } 49 } 50 client.stop(); 51 Serial.println("client disconnected"); 52 } 53 54 55/* 56 * クライアント側 57 * サーバーから文字を受け取って表示してみる 58 */ 59 60#include <WiFi.h> 61 62#define WIFI_SSID "WROOM32" 63#define WIFI_PASS "password" 64//接続先設定 65const uint16_t port = 1337; //接続するサーバのポート  66const char * host = "192.168.1.10"; // 接続するipもしくはdns 67 68void setup() { 69 Serial.begin(115200); 70//Wi-Fi接続---------------------------------------------------------------------- 71 WiFi.begin( WIFI_SSID, WIFI_PASS ); 72 Serial.println("Connecting Wifi..."); 73 while (WiFi.status() != WL_CONNECTED) { 74 delay(500); 75 Serial.print("."); 76 } 77 Serial.println(""); 78 Serial.print("IP address: "); 79 Serial.println(WiFi.localIP()); 80 Serial.print("STA Mac Address: "); 81 Serial.println(WiFi.macAddress()); 82 Serial.print("Subnet Mask: "); 83 Serial.println(WiFi.subnetMask()); 84 Serial.print("Gateway IP: "); 85 Serial.println(WiFi.gatewayIP()); 86//------------------------------------------------------------------------------ 87 delay(500); 88 89} 90 91void loop() { 92 WiFiClient client; 93 Serial.print("Connecting to "); 94 Serial.println(host); 95 if(!client.connect(host, port)){ 96 Serial.println("Connection failed."); 97 Serial.println("Waiting 5 seconds before retrying..."); 98 delay(5000); 99 return; 100 } 101 102 String rstr; 103 if(client > 0){ 104 if(client > 0){ // サーバーに接続され、読み取り可能なデータがあるクライアントを取得した場合実行 105 Serial.println("NewClient"); 106 while(client.connected()){ // クライアントが接続されている間実行 107 if(client.available()){ // クライアントから読み込み可能なバイトがある場合実行 108 rstr = client.readStringUntil('\n'); // データを読み込む。\nを受信すると読み込み終了し再度読み込みへループする 109 Serial.print("["); 110 Serial.print(rstr); // 読み込んだデータをシリアル表示 111 Serial.println("]"); 112 } 113 } 114 } 115 116 //---------------------------------------------------------------------------- 117} 118}

試したこと

・当初は複数のクライアントから、AP兼サーバーのESP32に文字を送るものを作っていたが例によって1台分しか表示できず、ネット上の情報でシリアル通信が衝突している可能性が指摘されていたので、送受信を逆にしたが解決できず。

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

クライアント2台とも、APに接続できてはいるが情報の受信が一台のみしか行われない。
APのチャンネルを2つ(1ch, 5ch)指定し、クライアントそれぞれに接続するチャンネルを指定したが解決せず

TCPでなくUDPで行えば解決しましたが、TCPで不可能な技術的な理由(ArduinoIDEで実現できる方法があるのか)がわからないので、しばらく残させていただきます。

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

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

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

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

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

tmp

2020/12/23 03:08

ブログラムがそう組まれているからです。サーバーの接続への接続を受け付け(server.available();)た後、クライアントの通信を終えるまで、次の接続を受け付けないからです。
moto_short

2020/12/23 08:39

ご教授いただきありがとうございます。(回答でされてもよろしかったのではと思いますが) 同時に2台分の受付を行う方法等はありますでしょうか。
tmp

2020/12/23 13:02

回答を考える時間がなかったからです。思い込みだと、それで気づいてしまいます。 ポーリングか、マルチタスクになると思います。
moto_short

2020/12/24 00:35

ありがとうございます。自分でも検討してみます
guest

回答2

0

ベストアンサー

2022年4月現在で'ESP32 複数 クライアント'でググったらこれがトップでヒットしちゃって...ちょっと恥ずかしいかな。
普通にArduinoライブラリでもできちゃったので、一応そちらも載せておきます。二口分の受け方は「ベタ」に書いているので、実際になにかするなら美しくまとめてください。もちろん、動作中にreadStringUntil()とかでブロックしたりしちゃいけませんよ。

Arduino

1#include <WiFi.h> 2WiFiServer server(54321); 3 4void setup() { 5 Serial.begin(115200); 6 WiFi.begin(); //引数省略で最後に接続したAPにつなぎに行く。WiFiの接続とソケットの接続を混同しないようにしましょう。 7 while (WiFi.status() != WL_CONNECTED) { 8 delay(500); 9 } 10 Serial.println(WiFi.localIP()); 11 server.begin(); 12} 13WiFiClient client[2]; 14String s[2]; 15void loop() { 16 if( !client[0].connected()){ 17 client[0]= server.available(); 18 if(client[0].connected()){ 19 Serial.println("Connected to 0"); 20 client[0].println("Connected to 0"); 21 } 22 }else{ 23 while(client[0].available()){ 24 char c=client[0].read(); 25 s[0]+=c; 26 if(c=='\n'){ 27 Serial.println(s[0]); 28 client[0].print(s[0]); 29 } 30 } 31 } 32 if( !client[1].connected()){ 33 client[1]= server.available(); 34 if(client[1].connected()){ 35 Serial.println("Connected to 1"); 36 client[1].println("Connected to 1"); 37 } 38 }else{ 39 while(client[1].available()){ 40 char c=client[1].read(); 41 s[1]+=c; 42 if(c=='\n'){ 43 Serial.println(s[1]); 44 client[1].print(s[1]); 45 } 46 } 47 } 48}

以下原文

既存のライブラリを真面目に探せばもっといい処理もできそうな気がしますが、見つからなかったので原始的にやってみました。WiFiを繋いだあとはCでソケットを扱う場合の定番にほぼ沿ったものです。

Arduino

1#include <sys/select.h> 2#include <sys/socket.h> 3#include <WiFi.h> 4 5static const char *WIFI_SSID = "WROOM32"; 6static const char *WIFI_PASS = "password"; 7const int port = 1337; 8 9int fd, maxfd; 10int fd2[2] = { -1, -1}; //二口だけ接続をうけつける 11struct sockaddr_in addr; 12struct sockaddr_in from_addr; 13struct timeval tv; 14 15//サーバー設定------------------------------------------------- 16IPAddress ip(192, 168, 1, 10); 17//------------------------------------------------------------- 18 19void setup() { 20 Serial.begin(115200); 21 //Wi-Fiアクセスポイント開始-------------------------------------------------------- 22 WiFi.mode(WIFI_AP); 23 WiFi.softAP(WIFI_SSID, WIFI_PASS); 24 WiFi.softAPConfig(ip, WiFi.gatewayIP(), WiFi.subnetMask()); 25 //WiFi.softAPConfig(ip, gateway, netmask); 26 27 IPAddress myIP = WiFi.softAPIP(); 28 Serial.println("APStarted. myIP Address:"); 29 Serial.println(myIP); 30 Serial.print("server Mac Address: "); 31 Serial.println(WiFi.macAddress()); 32 Serial.print("Subnet Mask: "); 33 Serial.println(WiFi.subnetMask()); 34 Serial.print("Gateway IP: "); 35 Serial.println(WiFi.gatewayIP()); 36 //------------------------------------------------------------------------------ 37 38 if ( (fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { 39 Serial.println("socket error."); 40 while (1); 41 } 42 addr.sin_family = AF_INET; 43 addr.sin_port = ntohs(port); 44 addr.sin_addr.s_addr = INADDR_ANY; 45 if ( bind( fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) { 46 Serial.println( "bind error" ); 47 while (1); 48 } 49 50} 51 52 53void loop() { 54 if ( listen(fd, 1) < 0) { 55 Serial.println("listen error."); 56 } 57 58 fd_set fds; 59 FD_ZERO(&fds); 60 FD_SET( fd, &fds); 61 maxfd = fd; 62 for (int i = 0; i < 2; i++) { 63 if (fd2[i] != -1) { 64 FD_SET(fd2[i], &fds); 65 if (fd2[i] > fd) { 66 maxfd = fd2[i]; 67 } 68 } 69 } 70 tv.tv_sec = 0; 71 tv.tv_usec = 10000; 72 int cnt = select(maxfd + 1, &fds, NULL, NULL, &tv); 73 if (cnt <= 0) { 74 //error or timeout 75 for (int i = 0; i < 2; i++) { 76 if (fd2[i] >= 0) { 77 //既接続のソケットに対して送信 78 char buf[32]; 79 cnt = sprintf(buf, "To %d %ld\n", fd2[i], millis()); 80 write(fd2[i], buf, cnt); 81 } 82 } 83 } 84 if (FD_ISSET( fd, &fds)) { 85 for (int i = 0; i < 2; i++) { 86 //接続受付 87 if (fd2[i] == -1) { 88 socklen_t len = sizeof(struct sockaddr_in); 89 if ((fd2[i] = accept(fd, (struct sockaddr *)&from_addr, &len)) < 0) { 90 Serial.println("accept error."); 91 while (1); 92 } 93 Serial.println("connect " + String( fd2[i])); 94 break; 95 } 96 } 97 } 98 for (int i = 0; i < 2; i++) { 99 if (fd2[i] != -1 && FD_ISSET(fd2[i], &fds) ) { 100 //受信&接続断検出 101 char buf[256]; 102 int rcvCnt = recv(fd2[i], buf, sizeof(buf), 0); 103 Serial.println("rcvd:" + String(buf)); 104 if (rcvCnt < 0) { 105 Serial.println("disconnect " + String(fd2[i])); 106 close(fd2[i]); 107 fd2[i] = -1; 108 } 109 } 110 } 111}

投稿2020/12/28 05:12

編集2022/04/21 07:21
thkana

総合スコア7703

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

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

moto_short

2020/12/28 05:33

回答ありがとうございます。 Cのソケットライブラリを扱える技量(と時間)が足らず、ご教授頂いたソースが断片的にしか解釈できませんでした。 追記の通り、UDPを使用した通信に変更しましたので、時間ができたときに勉強させていただこうと思います。
moto_short

2022/04/22 04:39

お久しぶりです。1年ほど前はお世話になりました。 もう私は本件に関わることは無くなってしまったのですが、思ったよりもシンプルなコードで実現できたのですね。今更ですがスッキリしました。
guest

0

MQTTがおすすめです

投稿2020/12/28 05:19

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

moto_short

2020/12/28 05:30

回答ありがとうございます。 今回の制作物の都合により、MQTTプロトコルが扱える通信速度が不足していたため、使用を見送っております。
退会済みユーザー

退会済みユーザー

2020/12/28 05:36

使用できない事情があるのですね。通信速度が足りてないとMQTTが扱えないというのは知りませんでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問