06.picoでwifiデモ動作 C言語


06.picoでwifiデモ動作 C言語

Pico ESP8266
https://www.waveshare.com/wiki/Pico-ESP8266
のデモ動作の続きです。前回はATコマンドを手入力して確認しましたが、今回は上記の下側にあるExamples > Demo code のC言語で確認します。

IPとPortの設定

cppファイルでのIPとPortの設定値を以下にしました。

tcp_server.cpp

・Port = 333
・受信データが有ればそのまま返信します。

tcp_client.cpp

・Server(PC) = 192.168.68.60:8080
・Serverへ2行送り、その後送られて来た文字をそのまま返信します。

udp.cpp

・remote(PC) = 192.168.68.60:50000
・local_Port(ESP8266) = 51000
・動作はほぼ、Clientと同じ。

picoへ転送

build内の各ホルダに出来たuf2ファイルをpicoへコピーします。詳細は今までの内容を参照してください。

各動作

tcp_server

・ESP8266とシリアルポート接続のTera Termに表示された内容です。(一部空白行は削除しました)

AT
OK

AT+CWMODE=3
OK

AT+CWJAP="*****","*****" ←*に修正しました
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IP
OK

AT+CIPMUX=1
OK

AT+CIPSERVER=1,333
OK

AT+CIFSR
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"46:17:93:04:8c:ae"
+CIFSR:STAIP,"192.168.68.61"
+CIFSR:STAMAC,"44:17:93:04:8c:ae"
OK

と表示されます。以上でESP8266(Server)の設定等が終わったので、前回使用したヘラクレスを起動します。TCP Client タブで
Module IP = 192.168.68.61, Port = 333に設定 > Connectボタンを押します。PCから
123456
と入力してSendボタン を押すと、Tera Term (ESP8266)側に

Recv 6 bytes
SEND OK

と表示され、ヘラクレス(PC)側に
123456
と受信表示されます。再度PCから送ることもできます。

tcp_client

まず、PC側にTCP Serverを起動しておきます。ヘラクレスにて、TCP Serverのタブを
Port = 8080に設定し、Listenボタンを押します。
ESP8266を起動すると、ESP8266側のTera Term画面に、

AT
OK

AT+CWMODE=3
OK

AT+CWJAP="*****","*****" ←*に修正しました。
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IP
OK

AT+CIFSR
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"46:17:93:04:8c:ae"
+CIFSR:STAIP,"192.168.68.61"
+CIFSR:STAMAC,"44:17:93:04:8c:ae"
OK

AT+CIPSTART="TCP","192.168.68.60",8080
CONNECT
OK

AT+CIPMODE=1
OK

AT+CIPSEND
OK
>
と表示され、Server(PC)のヘラクレスの受信欄に
Hello World !!!
ESP8266 TCP Client
と表示されます。ヘラクレスの送信欄に
123456 Sendボタンで
ESP8266のTera Term画面に
123456
と表示され、ESP8266はそのまま返信し、ヘラクレスの受信欄に
123456
と表示されます。

udp

ESP8266を起動すると、ESP8266側のTera Term画面に、

AT
OK

AT+CWMODE=3
OK

AT+CWJAP="*****","*****" ←*に修正しました。
WIFI DISCONNECT
WIFI CONNECTED
WIFI GOT IP
OK

AT+CIFSR
+CIFSR:APIP,"192.168.4.1"
+CIFSR:APMAC,"46:17:93:04:8c:ae"
+CIFSR:STAIP,"192.168.68.61"
+CIFSR:STAMAC,"44:17:93:04:8c:ae"
OK

AT+CIPSTART="UDP","192.168.68.60",50000,51000,0
CONNECT
OK

AT+CIPMODE=1
OK

AT+CIPSEND
OK
>

と表示されます。ヘラクレスにて、UDPのタブを
Module IP(ESP8266) = 192.168.68.61, Port = 51000, Local port(PC) = 50000に設定し、Listenボタンを押します。
ヘラクレスのSent data欄に
7(CR無し)
と入力すると、Tera Term画面に
7
と表示され、ヘラクレスの受信欄にも
7
と表示されます。また、ヘラクレスのSend欄に
99999 Sendボタン
と押すと、Tera Term画面に
99999
と表示され、ESP8266はそのまま返信し、ヘラクレスの受信欄に先の続きに
99999
と表示されます。

ホルダの構成

[wifiDemo]
 ├CMakeLists.txt (1)
 ├example_auto_set_url.make
 ├pico_sdk_import.make
 ├[tcp_client]
 │ ├tcp_client.cpp
 │ └CMakeLists.txt (2)
 ├[tcp_server]
 │ ├tcp_server.cpp
 │ └CMakeLists.txt (3)
 └[udp]
   ├upd.cpp
   └CMakeLists.txt (4)

(1)CMakeLists.txt

cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(pico_examples C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(PICO_EXAMPLES_PATH ${PROJECT_SOURCE_DIR})
pico_sdk_init()
include(example_auto_set_url.cmake)
add_subdirectory(tcp_client)
add_subdirectory(tcp_server)
add_subdirectory(udp)

(2)tcp_client\CMakeLists.txt

add_executable(tcp_client tcp_client.cpp)
target_link_libraries(tcp_client pico_stdlib)
pico_enable_stdio_usb(tcp_client 1)
pico_enable_stdio_uart(tcp_client 0)
pico_add_extra_outputs(tcp_client)
example_auto_set_url(tcp_client)

(3)tcp_server\CMakeLists.txt

add_executable(tcp_server tcp_server.cpp)
target_link_libraries(tcp_server pico_stdlib)
pico_enable_stdio_usb(tcp_server 1)
pico_enable_stdio_uart(tcp_server 0)
pico_add_extra_outputs(tcp_server)
example_auto_set_url(tcp_server)

(4)udp\CMakeLists.txt

add_executable(udp udp.cpp)
target_link_libraries(udp pico_stdlib)
pico_enable_stdio_usb(udp 1)
pico_enable_stdio_uart(udp 0)
pico_add_extra_outputs(udp)
example_auto_set_url(udp)

初回のCMakeListsの内容

単一ホルダの時

[test]
 ├test.c ←作る
 ├CMakeLists.txt ←ファイル名とホルダ名以外同じ(1)
 ├pico_sdk_import.make ←copy
 └[build] ←自動的に出来る
   ├test.uf2 ←picoにコピーする
   └等 ←使用しない

サブホルダの時

[test2]
 ├[test2_sub]
 │ ├test.c ←作る
 │ └CMakeLists.txt ←ファイル名とホルダ名以外同じ(2)
 ├CMakeLists.txt ←ファイル名とホルダ名以外同じ(3)
 ├pico_sdk_import.make ←copyする
 └[build] ←自動的に出来る
   ├[test2_sub]
   │ ├test.uf2 ←picoにコピーする
   │ └等 ←使用しない
   └等 ←使用しない

(1)のCMakeLists.txt

#CMakeの必要最小限のバージョンを設定
cmake_minimum_required(VERSION 3.12) #(3)と同じ

#公式の cmake を読込む。(プロジェクトの前に置く)
include(pico_sdk_import.cmake)

#プロジェクト名(project1)と言語を設定
project(project1 C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

#SDKを初期化。Pico用環境を読込む
pico_sdk_init()    #ここまで(3)と同じ

#実行ファイル(test)を作成するために test.c を読込む
add_executable(test test.c)   #以下(2)と同じ

#一般的に使用される機能を集約するpico_stdlibをプル
target_link_libraries(test pico_stdlib)

#USB出力は有効、UART出力は無効
pico_enable_stdio_usb(test 1)
pico_enable_stdio_uart(test 0)

#map,bin,hex,uf2等を作成
pico_add_extra_outputs(test)

(2)のCMakeLists.txt

* (1)の下部と同じ
add_executable(test test.c)
target_link_libraries(test pico_stdlib)
pico_enable_stdio_usb(test 1)
pico_enable_stdio_uart(test 0)
pico_add_extra_outputs(test)

(3)のCMakeLists.txt

* (1)の上部とほぼ同じ
cmake_minimum_required(VERSION 3.12)
include(pico_sdk_import.cmake)
project(project1 C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
pico_sdk_init()
add_subdirectory(test2_sub) #サブディレクトリ追加

参考

CMakeリファレンスドキュメント
https://cmake.org/cmake/help/latest/index.html#

ファイル

"*****"は、自分のssidとそのパスワードです。

tcp_server.cpp


// 1 デモ tcp_server.cpp
#include <stdio.h>         // 入出力ユーティリティ
#include "pico/stdlib.h"   // picoの一般ユーティリティ
#include "hardware/uart.h" // UARTを使用
#include <string.h>        // strstr,strcpyを使用
#include <stdlib.h>        // 一般ユーティリティ
#define UART_ID uart0      // 使用pinによりuart0と1がある
#define BAUD_RATE 115200   // ボーレート
#define UART_TX_PIN 0      // UART_TxにGP0(1番pin)を使用
#define UART_RX_PIN 1      // UART_RxにGP1(2番pin)を使用
char SSID[] = "*****"; // 自分のwifiのSSID
char password[] = "*****";    // wifiのパスワード
char Port[] = "333";      // ESP8266
char s[50]="";            // 文字列作成用
char buf[100] = {0};      // コマンド送信文字列
char rbuf[100] = {0};     // 受信文字列
int ID;                   // 接続ID

bool sendCMD(const char * cmd,const char * act,int timeout=2000) {//コマンド送信関数
  int i=0;                    // 文字数変数
  uint64_t t = 0;             // タイムスタンプ値(uS)用変数
  uart_puts(UART_ID, cmd);    // cmdを送信
  uart_puts(UART_ID, "\r\n"); // CRとLFを送信
  t = time_us_64();           // 現在のタイムスタンプ値(uS)
  while(time_us_64() - t < timeout*1000){  // 2秒内の動作
    while(uart_is_readable_within_us(UART_ID,2000)){
      buf[i++] = uart_getc(UART_ID);
    }  // Rxが無くなるまで1文字最大2mS待ち読取る
    if(i>0){                       // 文字以上有ったら
      buf[i]='\0';                 // ufに終端文字追加
      printf("%s\r\n",buf);        // bufを表示
      if(strstr(buf,act) != NULL){ // bufにactが有れば
        return true;               // 戻り値 true(1)
      }else{                       // bufにactが無ければ
        i=0;                       // 文字数変数初期化
      }
    }
  }
  //printf("false\r\n");
  return false;                    // 戻り値 false(0)
}

void sendData(int ID,const char *data){//データ送信関数
  char tmp[20] = {0};         // 送信コマンド文字列
  sprintf(tmp,"AT+CIPSEND=%d,%d",ID,strlen(data));//文字列作成
  if(sendCMD(tmp,">")){       // コマンドを送信しokなら
    uart_puts(UART_ID, data); // data送信
  }
}

bool ReceiveData() {                // データ受信関数
  int i=0;                          // 文字数変数
  while(uart_is_readable_within_us(UART_ID,2000)){
    rbuf[i++] = uart_getc(UART_ID);
  }  // Rxが無くなるまで1文字最大2mS待ち読取る
  if(i>0){                          // 1文字以上有ったら
    rbuf[i]='\0';                   // rbufに終端文字追加
    printf("%s\r\n",rbuf);          // rbufを表示
    char *p1 = strstr(rbuf,"+IPD"); // 文字列検索
    if(p1 != NULL){                 // "+IPD"が有れば
      char *p2 = strstr(p1+5,",");  // 文字列検索
      *p2='\0';                     // 終端文字追加
      ID = atoi(p1+5);              // IDの文字列→int
      char *p3 = strstr(p2+1,":");  // 文字列検索
      strcpy(rbuf,p3+1);            // :の次からコピー
      //printf("ID=%d, Data=%s\r\n",ID,rbuf);
      return true;  // 戻り値 true(1)
    }
  }
  return false;    // 戻り値 false(0)
}  // 受信データから送信IDと送信用文字列を抜き出す

int main() {
  stdio_init_all(); // 標準stdio型を初期化
  sleep_ms(1000);   // 1秒待つ
  sleep_ms(1000);
  sleep_ms(1000);
  sleep_ms(1000);
  sleep_ms(1000);   // 計5秒待つ
  uart_init(UART_ID, BAUD_RATE);  // (uart0or1,ボーレート)
  gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);//GPIO機能選択(GP番号,リストから)
  gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);  // 上と同じ
  uart_puts(UART_ID, "+++");   // +++を送信
  sleep_ms(1000);              // 1秒待つ
  while(uart_is_readable(UART_ID))uart_getc(UART_ID);//読取データがあれば読取る
  sendCMD("AT","OK");          // AT起動の確認
  sendCMD("AT+CWMODE=3","OK"); // WiFiモード 両方に設定
  sprintf(s,"AT+CWJAP=\"%s\",\"%s\"",SSID,password);//文字列作成
  sendCMD(s,"OK",20000);       // SSIDに接続
  sendCMD("AT+CIPMUX=1","OK"); // 複数接続の設定
  sprintf(s,"AT+CIPSERVER=1,%s",Port); // 文字列作成
  sendCMD(s,"OK");             // サーバー作成
  sendCMD("AT+CIFSR","OK");    // IPアドレスを確認
  while (true) {               // 永久ループ
    while(ReceiveData()){      // データ受信関数
      sendData(ID,rbuf);       // データ送信関数
    }
  }
}

tcp_client.cpp


// 2 デモ tcp_client.cpp
#include <stdio.h>         // 入出力ユーティリティ
#include "pico/stdlib.h"   // picoの一般ユーティリティ
#include "hardware/uart.h" //  UARTを使用
#include <string.h>        // strstrを使用
#define UART_ID uart0      // 使用pinによりuart0と1がある
#define BAUD_RATE 115200   // ボーレート
#define UART_TX_PIN 0      // UART_TxにGP0(1番pin)を使用
#define UART_RX_PIN 1      // UART_RxにGP1(2番pin)を使用
char SSID[] = "*****"; // 自分のwifiのSSID
char password[] = "*****";    // wifiのパスワード
char ServerIP[] = "192.168.68.60";   // PC
char Port[] = "8080";      // PC
char s[50]="";             // 文字列作成用
char buf[100] = {0};       // 送信文字列

bool sendCMD(const char * cmd,const char * act,int timeout=2000) {//コマンド送信関数
  int i=0;                    // 文字数変数
  uint64_t t = 0;             // タイムスタンプ値(uS)変数
  uart_puts(UART_ID, cmd);    // cmdを送信
  uart_puts(UART_ID, "\r\n"); // CRとLFを送信
  t = time_us_64();           // 現在のタイムスタンプ値(uS)
  while(time_us_64() - t < timeout*1000){ // 2秒内の動作
    while(uart_is_readable_within_us(UART_ID,2000)){
      buf[i++] = uart_getc(UART_ID);
    }  // Rxが無くなるまで1文字最大2mS待ち読取る
    if(i>0){                       // 1文字以上有ったら
      buf[i]='\0';                 // bufに終端文字追加
      printf("%s\r\n",buf);        // bufを表示
      if(strstr(buf,act) != NULL){ // bufにactが有れば
        return true;               // 戻り値 true(1)
      }else{                       // bufにactが無ければ
        i=0;                       // 文字数変数初期化
      }
    }
  }
  //printf("false\r\n");
  return false;                    // 戻り値 false(0)
}

int main() {
  stdio_init_all(); // 標準stdio型を初期化
  sleep_ms(1000);   // 1秒待つ
  sleep_ms(1000);
  sleep_ms(1000);
  sleep_ms(1000);
  sleep_ms(1000);   // 計5秒待つ
  uart_init(UART_ID, BAUD_RATE);  // (uart0or1,ボーレート)
  gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);//GPIO機能選択(GP番号,リストから)
  gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);//上と同じ
  uart_puts(UART_ID, "+++"); // +++を送信
  sleep_ms(1000);            // 1秒待つ
  while(uart_is_readable(UART_ID))uart_getc(UART_ID);//読取るデータがあれば読取ります
  sendCMD("AT","OK");           // AT起動の確認
  sendCMD("AT+CWMODE=3","OK");  // WiFiモード 両方に設定
  sprintf(s,"AT+CWJAP=\"%s\",\"%s\"",SSID,password);//文字列作成
  sendCMD(s,"OK",20000);        // SSIDに接続
  sendCMD("AT+CIFSR","OK");     // IPアドレスを確認
  sprintf(s,"AT+CIPSTART=\"TCP\",\"%s\",%s",ServerIP,Port);//文字列作成
  sendCMD(s,"OK",10000);        // クライアントとして接続開始
  sendCMD("AT+CIPMODE=1","OK"); // 透過送信の設定
  sendCMD("AT+CIPSEND",">");    // データを送信開始
  uart_puts(UART_ID, "Hello World !!!\r\n");    // 送信
  uart_puts(UART_ID, "ESP8266 TCP Client\r\n"); // 送信
  int i=0;                      // 文字数変数
  while (true) {                // 永久ループ
    while(uart_is_readable_within_us(UART_ID,10000)){
      buf[i++] = uart_getc(UART_ID);
    } // Rxが無くなるまで1文字最大10mS待ち読取る
    if(i>0){                   // 1文字以上有ったら
      buf[i]='\0';             // bufに終端文字追加
      printf("%s\r\n",buf);    // bufを表示
      uart_puts(UART_ID, buf); // bufを送信
      i=0;                     // 文字数変数初期化
    }
  }
}

udp.cpp


// 3 デモ upd.cpp
#include <stdio.h>         // 入出力ユーティリティ
#include "pico/stdlib.h"   // picoの一般ユーティリティ
#include "hardware/uart.h" // UARTを使用
#include <string.h>        // strstrを使用
#define UART_ID uart0      // 使用pinによりuart0と1がある
#define BAUD_RATE 115200   // ボーレート
#define UART_TX_PIN 0      // UART_TxにGP0(1番pin)を使用
#define UART_RX_PIN 1      // UART_RxにGP1(2番pin)を使用
char SSID[] = "*****"; // 自分のwifiのSSID
char password[] = "*****";    // wifiのパスワード
char remote_IP[] = "192.168.68.60";  // PC
char remote_Port[] = "50000";        // PC
char local_Port[] = "51000";         // ESP8266
char s[50]="";             // 文字列作成用
char buf[100] = {0};       // コマンド送信文字列
char rbuf[100] = {0};      // 受信文字列

bool sendCMD(const char * cmd,const char * act,int timeout=2000) { // コマンド送信関数
  int i=0;                    // 文字数変数
  uint64_t t = 0;             // タイムスタンプ値(uS)変数
  uart_puts(UART_ID, cmd);    // cmdを送信
  uart_puts(UART_ID, "\r\n"); // CRとLFを送信
  t = time_us_64();           // 現在のタイムスタンプ値(uS)
  while(time_us_64() - t < timeout*1000){  // 2秒内の動作
    while(uart_is_readable_within_us(UART_ID,10000)){
      buf[i++] = uart_getc(UART_ID);
    }  // Rxが無くなるまで1文字最大10mS待ち読取る
    if(i>0){                       // 1文字以上有ったら
      buf[i]='\0';                 // bufに終端文字追加
      printf("%s\r\n",buf);        // bufを表示
      if(strstr(buf,act) != NULL){ // bufにactが有れば
        return true;               // 戻り値 true(1)
      }else{                       // bufにactが無ければ
        i=0;                       // 文字数変数初期化
      }
    }
  }
  printf("false *****\r\n");       // エラー表示
  return false;                    // 戻り値 false(0)
}

int main() {
  stdio_init_all(); // 標準stdio型を初期化
  sleep_ms(1000);   // 1秒待つ
  sleep_ms(1000);
  sleep_ms(1000);
  sleep_ms(1000);
  sleep_ms(1000);   // 計5秒待つ
  uart_init(UART_ID, BAUD_RATE);  // (uart0or1,ボーレート)
  gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);//GPIO機能選択(GP番号,リストから)
  gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);//上と同じ
  uart_puts(UART_ID, "+++");  // +++を送信
  sleep_ms(1000);             // 1秒待つ
  while(uart_is_readable(UART_ID))uart_getc(UART_ID);//読取るデータがあれば読取ります
  sendCMD("AT","OK");           // AT起動の確認
  sendCMD("AT+CWMODE=3","OK");  // WiFiモード 両方に設定
  sprintf(s,"AT+CWJAP=\"%s\",\"%s\"",SSID,password);//文字列作成
  sendCMD(s,"OK",20000);    // SSIDに接続
  sendCMD("AT+CIFSR","OK"); // IPアドレスを確認
  sprintf(s,"AT+CIPSTART=\"UDP\",\"%s\",%s,%s,0",remote_IP,remote_Port,local_Port);//文字列作成
  sendCMD(s,"OK",10000);        // クライアントとして接続開始
  sendCMD("AT+CIPMODE=1","OK"); // 透過送信の設定
  sendCMD("AT+CIPSEND",">");    // データを送信開始
  uart_puts(UART_ID, "Hello World !!!\r\n");    // 送信
  uart_puts(UART_ID, "ESP8266 TCP Client\r\n"); // 送信
  int i=0;                      // 文字数変数
  while (true) {                // 永久ループ
    while(uart_is_readable_within_us(UART_ID,10000)){
      rbuf[i++] = uart_getc(UART_ID);
    } // Rxが無くなるまで1文字最大10mS待ち読取る
    if(i>0){                    // 1文字以上有ったら
      rbuf[i]='\0';             // rbufに終端文字追加
      printf("%s\r\n",rbuf);    // rbufを表示
      uart_puts(UART_ID, rbuf); // rbufを送信
      i=0;                      // 文字数変数初期化
    }
  }
}