# 七、网络

到目前为止，所有的例子都是离线的。我们没有将 Arduino 的读数发送到另一个设备，也没有使用 Arduino 通过网络接收信息来开始不同的行为。Arduino 通常使用两种类型的连接:点对点和网络感知。点对点通常具有较短的范围，在家庭自动化中使用非常频繁。如果我们想从更远的地方接收或发送东西，我们使用网络感知连接。本节中的一些示例将要求您有两个 Arduino 板。如果你没有第二个 Arduino 板，你可以跟着做，看看如果你得到第二个 Arduino 会有什么可能。

## 与 MK 模块的通信

Arduino 世界中最简单的通信方式之一是与 MK 模块的点对点通信。模块总是成对的，通信只能单向进行。模块的范围对于位于家庭周围房间内的设备之间的通信来说相对合理，但前提是要焊接天线。天线可以是普通的绝缘线。棘手的部分是计算电线的长度。当天线为波长的四分之一时，可获得最佳结果。波长由传输频率决定。波长可以用公式计算。

| ![](img/image056.png) | 注:波长=光速/频率 |

MK 模块通常在 315、330 和 433 兆赫下工作，所以最好用表格形式表示:

表 5:按频率划分的天线长度

| 频率 | 天线长度(厘米) | 天线长度(英寸) |
| 315 Mhz | Twenty-three point eight | Nine point four |
| 330 Mhz | Twenty-two point seven | Eight point nine |
| 433 Mhz | Seventeen point three | Six point eight |

您可以使用前面的表格来确定天线长度，而无需深入计算。在我们的例子中，我们不需要天线，因为它需要焊接，而焊接不在本书的范围之内。相反，我们将把发射器和接收器放在彼此附近。区分哪个模块是发射器，哪个模块是接收器非常重要。每个人都有非常不同的角色和外表:

![](img/image057.png)

图 54:左边是微波发射器，右边是微波接收器

模块对不支持双向通信。如果你需要双向通信，你必须使用两对，两边各有一个发射机和一个接收机。布线相对简单，因为我们每个模块只使用三根电线。接收器有四个引脚，其中两个用于接收数据。但是使用两个引脚进行接收是不可靠的，因为它们之间存在干扰。发射机和接收机的频率匹配很重要。不管是 315、330 还是 433 MHz 重要的是他们是一样的。让我们看一下这一部分的零件清单。

本节零件清单:

*   2 倍燃烧 1
*   2 个 USB 电缆
*   XY-MK-5V 接收器模块
*   FS1000A 变送器模块
*   6x 试验板公母跳线

我们将使用一个 Arduino 向通信信道发送消息。然后，我们将使用第二个 Arduino 从通道中拾取消息，并将其发送到计算机上的串行端口。通信和实现协议通常是通过库来完成的。使用发射器和接收器模块发送相对简单的信号而不使用库是可能的，但是处理复杂的消息和检查错误是它自己的一章。MK 发射机和接收机最好的库之一叫做`VirtualWire`。要开始使用它，您必须下载存档文件。档案在这里。下载它，并记住你把它保存在哪里。

新建一个草图，点击`Sketch` > `Include Library` > `Add Zip Library`。选择您下载的 zip 存档，集成开发环境将自动安装库。如果您已经安装了库，就不需要安装两次。如果您尝试这样做，Arduino IDE 可能会报告一个错误。这个例子中没有实验板，所以我们将展示如何用表格来连接模块。模块上有可见的标记，因此我们将只查看示例工作所需的连接。变送器模块在 3.3 伏的较小电压下工作。有些模块可以接收高达 12 伏的电压，但为了安全起见，请检查您购买的模块的规格。用太大的电压连接设备会损坏它。变送器模块上只有三根电线:

表 6:变送器接线

| 发射器个人识别码 | 烧了一根针 |
| ATAD
(数据反转) | Twelve |
| VCC | 3.3 V |
| GND | GND |

接收器模块有四个引脚，但最好的结果是我们只使用三根导线。让我们来看看引脚:

表 7:接收器接线

| 接收器个人识别码 | 烧了一根针 |
| VCC | 5 伏 |
| 数据(VCC 旁边) | 11–只连接一个数据引脚！ |
| 数据(GND 旁边) | 11–只连接一个数据引脚！ |
| GND | GND |

连接接收器时，只需将其中一个数据引脚连接到 Arduino 引脚 11。现在接线已经设置好了，我们将交换消息，然后将消息打印到串行端口。我们先来看看发射器代码:

```c
    #include <VirtualWire.h>
    // connect the ATAD (data reverse) to pin 12

    // greater speed smaller reliability
    // smaller speed greater reliability
    // 2000 bits per second is fine for most of the applications

    int bits_per_second_speed = 2000;

    void setup() {
      // initialize VirtualWire
      vw_setup(bits_per_second_speed);
    }

    void loop() {
      send("This Totally Works!");
      delay(1000);
    }

    void send (char *message) {
      // send the message
      vw_send((uint8_t *)message, strlen(message));

      // wait until the message is transmitted
      vw_wait_tx();
    }

```

接收器代码:

```c
    #include <VirtualWire.h>
    // connect just one of the DATA wires to pin 11

    // transmission speeds have to match
    int bits_per_second_speed = 2000;

    // buffer for storing incoming messages
    byte message[VW_MAX_MESSAGE_LEN];

    // we'll save received message size, initial value is max
    byte messageLength = VW_MAX_MESSAGE_LEN;

    void setup() {
      // initialize serial communication with the computer
      Serial.begin(9600);

      // just to let us know that something is going on
      Serial.println("Initializing device");
      // initialize
      vw_setup(bits_per_second_speed);
      // starting the receiver
      vw_rx_start();
      Serial.println("VirtualWire receiver started ...");
    }

    void loop() {
      // not blocking
      if (vw_get_message(message, &messageLength)) {
        Serial.print("Incoming: ");

        // print every byte
        for (int i = 0; i < messageLength; i++) {
          Serial.write(message[i]);
        }

        Serial.println();
      }
    }

```

这个例子看起来可能不像什么特别的东西，但信不信由你，前面的例子是许多基于 Arduino 的 DIY 解决方案的基础，从家庭自动化到安全和遥感。

该模块的主要优点是非常便宜，并且通过焊接天线具有相对较好的范围。天线可以是简单的东西，如一根有固定长度的电线。甚至有这样的情况，人们声称他们设法在 100 米或 110 码处获得信息。

实际上，这真的取决于你中间的墙的类型。如果是厚厚的钢筋混凝土墙，你可能不会穿过去。但是对于普通家庭和合理的距离来说，MK 模块就可以了。在下一节中，我们将研究一种更强大的无线技术，它可以在更高的速度下工作，并且可以双向通信(并且不总是从发射机到接收机)。

## 使用 nRF24L01+数据收发器

使用 nRF24L01+，数据可以同时双向传输。没有专用的发射器和接收器电路。收发器的工作频率为 2.4 千兆赫，在普通家庭无线互联网接入设备的范围内。

与上一节中的 MK 模块相比，它们最困难的是布线。布线也依赖于我们正在使用的库。有几个可用的库。写作时最常用的是`RF24`。

要安装库，从 Github [这里](https://github.com/maniacbug/RF24/archive/master.zip)下载文件，并记住保存的位置。新建一个草图，点击`Sketch` > `Include Library` > `Add ZIP Library`。选择您下载的 zip 存档，Arduino IDE 将自动安装该库。如果您决定使用其他库，请检查模块应该如何连接。接下来，我们将介绍布线方案。让我们看一下模块:

![](img/image058.png)

图 55: nRF24L01+前后侧视图

连接引脚最简单的方法是转动模块，以便看到模块的背面。背面是引脚伸出的一侧:

![](img/image059.png)

图 56: nRF24L01+接线

网上有很多接线模式，但大多数都需要你从另一边寻找引脚。这张图片代表了当你将图钉转到背面时你是如何看到图钉的。这样连接模块会容易得多。nRF24L01+的一些版本在线提供，这种布局印刷在引脚旁边的背面。如果您没有打印版本，请使用上图。引脚名称旁边是 Arduino 上的目标引脚名称。以下是收发器模块上每个引脚的详细说明:

表 8:带有 Arduino 的 nRF24L01+引脚和布线的引脚描述

| 接收器个人识别码 | 描述 | Arduino 上的目标引脚 |
| VCC | 3.3V +引脚 | 3.3 V |
| GND | 地面 | GND |
| CSN | 芯片选择不是。如果该值为低，芯片会响应 SPI 命令。串行外围接口总线是一种串行同步通信标准。 | Ten |
| 这一个 | 芯片使能。如果高，模块正在发送或监听。 | nine |
| MOSI | 主-出-从-入。将数据从微控制器发送到设备。 | Eleven |
| 血清肌酸激酶 | 串行接口移位时钟；用于传输过程中的数据同步。 | Thirteen |
| 伊拉克 | 可选中断请求引脚。 | 在我们的示例中，此引脚没有连接。 |
| 军事情报部门组织(Military Intelligence Service Organization) | 主从式输出。将数据从器件发送到微控制器。 | Twelve |

在这个例子中，我们需要两块 Arduino 板和两个收发器模块。我们还需要 14 根电线来连接两块 Arduino 板。在示例中，我们不会制定非常复杂的协议。每个 Arduino 都会读取数据并发送数据。

我们将调整其中一个 Arduino 板来显示串行监视器中的信息，这样我们就知道发生了什么。我们将给微控制器命名。第一个将得到一个简单的名称“A”。我们将发送的数据将成为简单协议的基础。我们将把源和目的地信息放入包中，加上一些随机生成的数据，如温度和光线。我们不会设置线路来测量值，因为本例中的重点是网络部分。

```c
    #include <SPI.h>
    #include <nRF24L01.h>
    #include <RF24.h>

    // set up nRF24L01 radio on SPI bus plus pins 9 & 10
    RF24 radio(9, 10);

    // channel designations, put as many as you need, 2 are enough for this
    // every transceiver can have one out and up to 5 read channels
    const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

    typedef struct {
      char source;
      char destination;
      float temperature;
      int light;
    } TotallyCustomData;

    TotallyCustomData data_out, data_in;

    void setup(void) {
      Serial.begin(9600);
      radio.begin();
      // 15 millis delay for channel to settle, try to send 15x
      radio.setRetries(15, 15);

      radio.openWritingPipe(pipes[0]);
      radio.openReadingPipe(1, pipes[1]);

      radio.startListening();
    }

    void loop(void) {
      data_out.source = 'A';
      data_out.destination = 'B';
      data_out.temperature = random(30);
      data_out.light = random(100);

      // check if the data is available
      if (radio.available()) {
        bool done = false;

        // read the data until finished
        while (!done) {
          done = radio.read(&data_in, sizeof(data_in));

          // print the data
          Serial.println("Received data");
          Serial.print("source = ");
          Serial.println(data_in.source);
          Serial.print("destination = ");
          Serial.println(data_in.destination);
          Serial.print("temperature = ");
          Serial.println(data_in.temperature);
          Serial.print("light = ");
          Serial.println(data_in.light);
          Serial.println("");
        }
      }

      // send the data after reading is done

      // stop listening so we can talk.
      radio.stopListening();
      bool ok = radio.write(&data_out, sizeof(data_out));
      radio.startListening();

      // just so that we don't send too much data
      delay(1000);
    }

```

让我们看看将要放在 Arduino 板“B”上的代码:

```c
    #include <SPI.h>
    #include "nRF24L01.h"
    #include "RF24.h"

    // set up nRF24L01 radio on SPI bus plus pins 9 & 10
    RF24 radio(9, 10);

    // radio pipe addresses for the two nodes to communicate.
    const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };

    typedef struct {
      char source;
      char destination;
      float temperature;
      int light;
    } TotallyCustomData;

    TotallyCustomData data_out, data_in;

    void setup(void) {
      radio.begin();
      // 15 millis delay for channel to settle, try to send 15x
      radio.setRetries(15, 15);

      radio.openWritingPipe(pipes[1]);
      radio.openReadingPipe(1, pipes[0]);

      radio.startListening();
    }

    void loop(void) {
      data_out.source = 'B';
      data_out.destination = 'A';
      data_out.temperature = random(30);
      data_out.light = random(100);

      if (radio.available()) {
        bool done = false;
        while (!done) {
          done = radio.read(&data_in, sizeof(data_in));
        }
      }

      // after data reading, write something
      // transceiver works in both directions, so cool :)

      // stop listening so we can talk.
      radio.stopListening();
      bool ok = radio.write(&data_out, sizeof(data_out));
      radio.startListening();

      // just so that we don't send too much data
      delay(1000);
    }

```

上传示例时要小心，因为很容易将代码上传到错误的板上！在将草图上传到 Arduino 板之前，请仔细检查串行端口。

传输距离取决于您家中的墙壁设置。所以会有边界的情况，你会有一种感觉，你只是错过了几英寸，一切工作。在这些情况下，尽量缩短包装的长度。在我们的示例中，消息包含 8 个字节，但是有效负载大小默认设置为 32 个字节。尝试在`setup`功能中将有效负载长度减少到 8。

```c
    void setup(void) {
    …
        radio.setPayloadSize(8);
    …

```

Arduino Uno 板上出现的另一个问题是，3.3V 引脚上的电压不太稳定，一旦传输开始，就会有非常高的消息丢失率。有些人使用单独的 3.3V 电源。

有些在收发器的 3.3V 和 GND 引脚上焊接一个 10μF 电容。使用电容器后，信息丢失率降低了 25%。还有一些版本的模块具有更大的天线，类似于家庭路由器上的天线。示例中使用的模块在收发器板上印刷有天线。

## 用 ESP8266 芯片连接无线

有很多关于物联网、连接到物联网的设备以及与其他设备和计算机交换数据的谈论和文章。前面提到的 MK 芯片和 nRF24 芯片通常支持一种基础设施，在这种基础设施中，我们有一个家庭集线器，它收集来自其他设备的信号，然后使用来自这些设备的消息将它们进一步发送到其他设备，在家庭网络内部或在线的某个地方具有更强的计算能力。

如今，大多数互联网接入都是通过我们家中的无线路由器实现的。现在，问题来了，如果我们能让我们的智能设备使用现有的无线基础设施来发送数据，那不是很好吗？直到最近，这些设备都非常昂贵，如果这些设备比 Arduino 板更贵，那就有点违背了整个目的。事实上，阿尔杜伊诺家族最近增加了一个新成员，即阿尔杜伊诺·尤恩，它可以做到这一切。但它的价格比阿尔杜伊诺·乌诺高出两倍多。价格非常合理的绝佳选择是 ESP8266 Wi-Fi 收发器。它实际上看起来与 nRF24 芯片非常相似:

![](img/image060.png)

图 57: ESP8266 前视图

在本例中，我们将使用多条导线、原板、ESP8266 模块和 Arduino Uno。ESP8266 是一个 3.3 伏电路，您应该始终将所有引脚连接到 3.3 伏电源，因为没有引脚兼容 5 伏。从现在开始，所有引脚在连接时都会降低到 3.3 V，即使文本状态连接到 Arduino 引脚。

本节零件清单:

*   1x 火神一号
*   1 个 USB 电缆
*   ESP8266 无线芯片
*   面包板
*   2x 1K 欧姆电阻器
*   2x 3.3V 齐纳二极管，用于将 Arduino 5V 的电压降压至芯片 3.3V
*   5x 面包板公母跳线
*   6x 试验板公对公跳线
*   工作家庭无线网络

下图显示了 EXP8266 上的引脚名称:

![](img/image061.png)

图 58:带有引脚名称的 ESP8266 背面侧视图

让我们马上跳到布线:

![](img/image062.png)

图 59: ESP8266 布线

与以前使用的芯片不同，这种芯片只需要五根电线就可以连接。在上图中，需要连接的引脚用蓝色标记。芯片的工作原理是我们通过串口给它发送文本命令，但是命令列表很长，而且有点繁琐，所以最好使用库。该库可在 Github [这里](https://github.com/itead/ITEADLIB_Arduino_WeeESP8266/archive/master.zip)下载。新建一个草图，点击`Sketch` > `Include Library` > `Add ZIP Library`。选择您下载的 zip 存档，Arduino IDE 将自动安装该库。

然而，这个库不会开箱即用，在它适合我们使用之前，我们需要做一些小的调整。但是，在此之前，让我们退一步。Arduino 使用 TX 和 RX 引脚进行串行通信。我们使用这两个引脚将程序传输到电路板，并接收来自 Arduino 的消息。因此，如果我们想让 Arduino 通过串行与其他设备通信，我们必须使用一个名为`SoftwareSerial`的 Arduino 库，它允许我们将任意一对引脚转换为串行通信引脚。

在我们的示例中，我们将使用真实的 Arduino TX 和 RX 引脚来显示串行监视器中的消息，并将引脚 3 转换为 RX 的软件版本，引脚 2 转换为 TX 的软件版本。Arduino 附带了软件串行库，因此您不必安装额外的库。在我们的例子中使用它之前，我们必须在`WeeESP8266`库中做一个小调整。首先，您必须找到 Arduino 库所在的文件夹:

表 9:按平台划分的 Arduino 库文件夹位置

| 平台 | 位置 |
| Windows 操作系统 | 我的文档\ Arduino \库\ |
| Linux 操作系统 | 文档/Arduino/库/ |
| 苹果个人计算机 | 文档/Arduino/库/ |

然后，您必须在库文件夹中找到新安装的库所在的文件夹。称为`ITEADLIB_Arduino_WeeESP8266-master`或`ITEADLIB_Arduino_WeeESP8266`。用原始文本编辑器打开名为`ESP8266.h`的文件。查找以下行:

```c
    // #define ESP8266_USE_SOFTWARE_SERIAL

    turn this into:

    #define ESP8266_USE_SOFTWARE_SERIAL

```

删除行首的注释。如果这一行仍然是注释的，您将无法将草图上传到 Arduino 板，因为下面的示例不会编译。我们将从相对简单的事情开始，因为首先，我们只想让我们的 Arduino 访问我们的无线网络并获得一个 IP 地址:

```c
    #include <ESP8266.h>
    #include <SoftwareSerial.h>

    // define the access data for your home wifi
    #define SSID        "YourSSID – Name of your wifi connection"
    #define PASSWORD    "YourWirelessPassword"

    // initialize a softwareserial with rx - 3 and tx - 2
    SoftwareSerial mySerial =  SoftwareSerial(3, 2);

    // initialize the wifi to work with mySerial
    ESP8266 wifi(mySerial);

    void setup(void) {
      Serial.begin(9600);
      Serial.println("Setup begin");

      Serial.print("FW Version: ");
      Serial.println(wifi.getVersion().c_str());

      if (wifi.setOprToStation()) {
        Serial.println("to station ok");
      } else {
        Serial.println("to station err");
      }

      if (wifi.joinAP(SSID, PASSWORD)) {
        Serial.println("Join AP success");
        Serial.print("IP: ");
        Serial.println(wifi.getLocalIP().c_str());
      } else {
        Serial.println("Join AP failure");
      }

      Serial.println("setup end");
    }

    void loop(void) {
      // we'll leave the loop empty
    }

```

如果一切顺利，您应该会看到路由器为您分配的 IP 地址。这是这个组件的一个简单的 Hello World。但是，如果你后退一步，看看协议和排放标准的所有可能性，让一切都连接到家庭无线网络，这个例子真的很了不起。你的 Arduino 板正在变成一个真正的物联网设备，就像这样。

## 用 ESP8266 芯片在线获取数据

接线和部件与上一节相同。重点将更多地放在编程上。任务是通过 TCP 将数据从 Arduino 发送到远程服务器。为了简单起见，我们将在服务器端显示发送的数据。在这个例子中，我们也将创建一个简单的服务器。这有点超出了本书的范围，我们到目前为止没有提到任何 Python 编程，但是请尝试找到一台安装了 Python 的计算机，或者按照这里提供的说明进行安装。请注意，在某些系统上，您必须将 python 可执行文件添加到 PATH 变量中，但这不在本书的讨论范围之内。请遵循前一链接下的官方文档。之后，创建一个名为`simple_service.py`的文件，并记住你保存在磁盘上的位置:

```c
    #!/usr/bin/env python

    """
    A simple server
    """

    import socket

    host = '0.0.0.0'
    port = 31233
    backlog = 10
    size = 1024

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((host, port))
    s.listen(backlog)

    # do until script is interrupted
    while 1:
        # accept incoming connections
        client, address = s.accept()
        # get the sent data
        data = client.recv(size)
        # print received data
        print("request: " + data)
        # close the connection
        client.close()

```

如果您安装了 Python，您可以从命令行运行脚本:

```c
    $ python simple_service.py

```

这涵盖了示例的服务器端。另外，要非常小心防火墙的设置。如果服务器和 ESP 芯片之间有防火墙，那么这个例子就不起作用了。现在服务器端已经处理好了，让我们看看 Arduino 代码:

```c
    #include <ESP8266.h>
    #include <SoftwareSerial.h>

    // define the access data for your home wifi
    #define SSID        "YourSSID"
    #define PASSWORD    "YourPassword"

    // initialize a softwareserial with rx - 3 and tx - 2
    SoftwareSerial mySerial(3, 2);

    // the endpoint of the service
    // in my case it's 192.168.1.2
    #define HOST_NAME   "192.168.1.2"
    #define HOST_PORT   (31233)

    ESP8266 wifi(mySerial);

    void setup(void) {
        Serial.begin(9600);
        Serial.println("Setup begin");

        Serial.print("FW Version:");
        Serial.println(wifi.getVersion().c_str());

        if (wifi.setOprToStationSoftAP()) {
            Serial.println("to station + softap ok");
        } else {
            Serial.println("to station + softap err");
        }

        if (wifi.disableMUX()) {
            Serial.println("single ok");
        } else {
            Serial.println("single err");
        }

        Serial.println("setup end");
    }

    void loop(void) {
        uint8_t buffer[128] = {0};

        if (wifi.createTCP(HOST_NAME, HOST_PORT)) {
            Serial.println("create tcp ok");
        } else {
            Serial.println("create tcp err");
        }

        char *request = "This could be anything!";
        Serial.println(request);

        wifi.send((const uint8_t*)request, strlen(request));

        uint32_t len = wifi.recv(buffer, sizeof(buffer), 1000);

        if (len > 0) {
            Serial.print("Received:[");
            for(uint32_t i = 0; i < len; i++) {
                Serial.print((char)buffer[i]);
            }
            Serial.println("");
            Serial.println("]");
        }

        delay(10000);
    }

```

如果一切顺利，你应该会看到数据从阿尔杜伊诺传来。在我们的示例中，它看起来如下所示:

```c
    $ python echo_service.py
    request: This could be anything!

```

这是一个 ESP 芯片向服务器发送数据的例子。但 ESP 其实是一个真正强大的芯片。它甚至有自己的固件，如果你愿意，你可以升级它。板上有两个 GPIO 引脚，网上有很多例子，甚至不需要 Arduino，芯片可以自己工作。为了证明这个芯片实际上有多强大，我们将构建一个小型的 TCP echo 服务器，将发送给它的数据反转回来。以下是 Arduino 的代码:

```c
    #include <ESP8266.h>
    #include <SoftwareSerial.h>

    // define the access data for your home wifi
    #define SSID        "YourSSID"
    #define PASSWORD    "YourPassword"

    // initialize a softwareserial with rx - 3 and tx - 2
    SoftwareSerial mySerial(3, 2);

    ESP8266 wifi(mySerial);

    void setup(void) {
      Serial.begin(9600);
      Serial.println("setup begin");

      Serial.print("FW Version:");
      Serial.println(wifi.getVersion().c_str());

      if (wifi.setOprToStationSoftAP()) {
        Serial.println("to station + softap ok");
      } else {
        Serial.println("to station + softap err");
      }

      if (wifi.joinAP(SSID, PASSWORD)) {
        Serial.println("Join AP success");
        Serial.print("IP: ");
        Serial.println(wifi.getLocalIP().c_str());
      } else {
        Serial.println("Join AP failure");
      }

      if (wifi.enableMUX()) {
        Serial.println("multiple ok");
      } else {
        Serial.println("multiple err");
      }

      if (wifi.startTCPServer(8090)) {
        Serial.println("start tcp server ok");
      } else {
        Serial.println("start tcp server err");
      }

      if (wifi.setTCPServerTimeout(10)) {
        Serial.println("set tcp server timout 10 seconds");
      } else {
        Serial.println("set tcp server timout err");
      }

      Serial.print("setup end, ready to receive requests");
    }

    void loop(void) {
      uint8_t buffer[128] = {0};
      uint8_t buffer_reverse[128] = {0};
      uint8_t mux_id;
      uint32_t len = wifi.recv(&mux_id, buffer, sizeof(buffer), 100);
      if (len > 0) {
        Serial.print("Status:[");
        Serial.print(wifi.getIPStatus().c_str());
        Serial.println("]");

        Serial.print("Received from :");
        Serial.print(mux_id);
        Serial.print("[");
        for (uint32_t i = 0; i < len; i++) {
          Serial.print((char)buffer[i]);
        }
        Serial.println("]");

        for (int c = len - 1, d = 0; c >= 0; c--, d++) {
          buffer_reverse[d] = buffer[c];
        }

        if (wifi.send(mux_id, buffer_reverse, len)) {
          Serial.println("send back ok");
        } else {
          Serial.println("send back err");
        }

        if (wifi.releaseTCP(mux_id)) {
          Serial.print("release tcp ");
          Serial.print(mux_id);
          Serial.println(" ok");
        } else {
          Serial.print("release tcp");
          Serial.print(mux_id);
          Serial.println(" err");
        }

        Serial.print("Status:[");
        Serial.print(wifi.getIPStatus().c_str());
        Serial.println("]");
      }
    }

```

当您开始示例时，您应该会看到在`Serial Monitor`工具中，用 ESP 芯片为 Arduino 分配了什么 IP 地址。然后，我们将使用远程登录工具在端口 8090 上连接到它。ESP 应该返回我们输入的反向字符。请记住，输入内容并按回车键会有 10 秒的超时时间:

```c
    $ telnet 192.168.1.5 8090
    Trying 192.168.1.5...
    Connected to unknown-18-fe-34-9b-79-87.lan.
    Escape character is '^]'.
    !sklof lla s'tahT

    That's all folks!Connection closed by foreign host.

```

本示例是展示 ESP 模块功能的最后一个示例，也是关于 Arduino 网络的最后一部分。这也是书中最后一个例子。