ESP8266/Arduino IDE: Further TCP Exploration

In my last post, ESP8266/Arduino IDE: Communicating with TCP, I successfully sent a single packet and received it back from the echo server properly.

I then proceeded to modify the sketch to transmit a stream of packets, reading each one. From that point, I descended into ESP8266 hell. The ESP-01 I’ve been using would inevitably hang after transmitting/receiving about 40 packets. Sometimes I’d get lucky and do around 200, but that was as good as it got.

I spent hours trying to figure out the issue. No luck, and no similar reports on the dev web site. It seemed the problem was mine, yet my program was so simple there was little chance I had some kind of totally brain-dead bug that was filling the heap or stack or otherwise corrupting memory .

I slept on the problem and this morning whipped out my Node MCU (version 0.9) board and downloaded the exact same program. Works just fine. In fact I greatly increased the packet size, reduced all possible delays, even went to a PC based echo server to get the max throughput and the Node MCU works fine.

I don’t know what the issue is with the ESP-01. It is an early one, I’m sure. Irregardless, it just isn’t worth any more of my time since this project is just meant to be a learning experience.

Hardware

So I’m starting over with new hardware. The Node MCU is much easier to use, in general, anyway. My only complaint is there is no reset button. I remedied that myself.

Here is the schematic:

espTcp-fig01

  • D3 is GPIO0 which is accessed as pin 0 in the software.
  • The pushbutton allows you to reset the device to restart it.
  • I tried using baud rates faster than 115.2KB on this board. Sometimes they work, some times they don’t. So I just stick with 115.2KB.

Since I changed the hardware, I also needed to change the IDE’s settings:

espTcp-fig02

Software Objective

I’ve got new hardware and I know it works solid because I pushed a lot of TCP packets through it. I can finally move on to what I really want to do.

My long term goal is to turn the LED on the ESP8266 on/off using commands on a PC. Right now, I can successfully talk to an Echo server. So my next incremental step is to transmit on/off commands to the echo server, and when I receive the echos I will act upon them.

My reason for doing it this way is I want to get as much code working on the ESP8266 before I ever start working on the PC side. I can settle on, and test, my own protocol this way before I ever have to touch the PC.

The Protocol

In order to talk between the PC and the ESP8266, I’m going to need to settle upon some kind of protocol. Any communications between devices requires one or more (often many) protocols.

IP is a protocol, as is TCP, as is FTP and Telnet. My protocol is going to be super simple since I just want to turn an LED on/off and get an acknowledgement.

The client will transmit a request code of “on” or “off” followed by carriage return (<CR> or \r) and linefeed (<LF> or \n).

Since I’m not using a packet length, there must be some type of indication that I’ve hit the end of the packet. <LF> is that indication. I am using <CR><LF> because if I want to use telnet to test the server, it is going to generate that sequence, so I may as well make use of it.

When the ESP8266 server processes a command it will respond with either “ok” or “bad” followed by a <CR><LF>. Once the response is transmitted, the server will disconnect the TCP connection. I’m sure the ESP8266 can’t support more than a couple of TCP sessions, so letting one sit open isn’t the best idea.

So here is what I expect to see across the comm line:

client >>> on<cr><lf>    >>> server
server >>> ok<cr><lf>    >>> client
server >>> <disconnect>  >>> client

client >>> off<cr><lf>   >>> server
server >>> ok<cr><lf>    >>> client
server >>> <disconnect>  >>> client

client >>> test<cr><lf>  >>> server
server >>> error<cr><lf> >>> client
server >>> <disconnect>  >>> client

As I said, this is an easy protocol and I could easily keep it in my head; however, it doesn’t hurt to formalize it a bit, especially as comments in your code as a memory job if/when you need to look at the code in the future.

Program Highlights

To make reading data coming in from the network easier, I created the getstr function which reads characters until the <LF> character is encountered and returns the preceding string.

One thing I discovered while writing this program is that you MUST NOT put the program in a lengthy NOOP wait loop. The code to manage the WiFi connection must be able to execute.

Until I figured that problem out, I was getting WDT timeouts. To prevent this from happening, you just need to call delay occasionally. So any place I might have wrote

while (test) {}

I now write

while (test) {
    delay(10UL);
    }

Here is the full program:

#include             <ESP8266WiFi.h>

const int             ledPin                 = 0;
WiFiClient            client;

// ---------------------------------------------------------------------------
char * getstr(
    char *            s,
    int                maxlen
    ) {

int                    i                     = 0;

strcpy(s, "");

while (true) {
    while (client.available() == 0) {
        // No infinite null loops allowed! Delay needs to be used which
        // yields control to ESP routines as necessary.
        delay(10UL);
        } // while client.available
    s[i] = client.read();
    if (s[i] == '\n') {
        s[i] = '\0';
        break; // while
        }
    else if (s[i] == '\r')
        ; // do nothing additional on return character - it will be overwritten by next char
    else {
        if (i == maxlen) {
            Serial.println("buffer overflow");
            while (true) {}
            }
        else
            i = i + 1;
        }
    } // while

return s;

} // getstr

// ---------------------------------------------------------------------------
void setup() {

char                pass[]                = "1234";
char                ssid[]                = "AP101";
char                s[100];
int                 status;

Serial.begin(9600);
pinMode(ledPin, OUTPUT);

Serial.print("Trying to connect to ");
Serial.println(ssid);

// attempt to connect to Wifi network:
status = WiFi.begin(ssid, pass);

status = WiFi.waitForConnectResult();
if (status != WL_CONNECTED) {
    Serial.println("Connection Failed");
    while (true) {}
    }

Serial.println("Connected. IP Addr:  ");
Serial.println(WiFi.localIP());

if (!client.connect("rpi", 7)) {
    Serial.println("Connection to echo server failed");
    while (true) {
        delay(10UL);
        }
    }

} // setup

// ---------------------------------------------------------------------------
void loop() {

int                    k;
char                s[100];

for (k = 0; k <= 5; k = k + 1) {

    // transmit cmd to echo server

    if (k == 5) {
        // force an error on last iteration
        client.println("zippy");
        }
    else if (k % 2 == 0) {     //  alternate xmitting on/off
        client.println("on");
        digitalWrite(ledPin, HIGH);
        }
    else {
        client.println("off");
        digitalWrite(ledPin, LOW);
        }

    delay(10UL);            // give the packet a bit of time

    // get cmd from echo server

    getstr(s, sizeof(s));
    Serial.print("Received cmd: ");
    Serial.println(s);

    // process cmd and send result to echo server

    if (strcmp("on", s) == 0) {
        digitalWrite(ledPin, HIGH);
        client.println("ok");
        delay(500);
        }
    else if (strcmp("off", s) == 0) {
        digitalWrite(ledPin, LOW);
        client.println("ok");
        delay(500);
        }
    else {
        client.println("error");
        }

    // get result from echo server

    Serial.print("Result: ");
    Serial.println(getstr(s, sizeof(s)));

    } // for

WiFi.disconnect(true);

while (true) {
    delay(100UL);
    } // while

}

The LED blinks on and off properly and the Serial monitor looks good:

Trying to connect to Chameleon
Connected. IP Addr:  
192.8.50.250
Received cmd: on
Result: ok
Received cmd: off
Result: ok
Received cmd: on
Result: ok
Received cmd: off
Result: ok
Received cmd: on
Result: ok
Received cmd: zippy
Result: error

Looking at the Network Packets

I could call the program good at this point, but I want to be 100% sure I’m following my protocol correctly, so I capture the packets with Wireshark to have a look. Here is the first “on” packet being transmitted to the echo server:

No.     Time           Source                Destination           Protocol Length Info
      4 0.002133000    192.8.50.250          192.8.50.101          ECHO     60     Request

0000  f8 b1 56 a2 07 fc 18 fe 34 a0 52 62 08 00 45 00   ..V.....4.Rb..E.
0010  00 2a 00 05 00 00 ff 06 d6 58 c0 08 32 fa c0 08   .*.......X..2...
0020  32 65 10 01 00 07 00 00 19 7e 5d ef 8f e4 50 18   2e.......~]...P.
0030  16 d0 2c c2 00 00 6f 6e 00 00 00 00               ..,...on....

Wait a minute “on” is there, where is the <cr><lf>? They’re in the next packet transmitted:

No.     Time           Source                Destination           Protocol Length Info
      6 0.005539000    192.8.50.250          192.8.50.101          ECHO     60     Request

0000  f8 b1 56 a2 07 fc 18 fe 34 a0 52 62 08 00 45 00   ..V.....4.Rb..E.
0010  00 2a 00 06 00 00 ff 06 d6 57 c0 08 32 fa c0 08   .*.......W..2...
0020  32 65 10 01 00 07 00 00 19 80 5d ef 8f e6 50 18   2e........]...P.
0030  16 ce 8f 24 00 00 0d 0a 00 00 00 00               ...$........

Now let’s make sure the response is what I expect:

No.     Time           Source                Destination           Protocol Length Info
      8 0.018371000    192.8.50.250          192.8.50.101          ECHO     60     Request

0000  f8 b1 56 a2 07 fc 18 fe 34 a0 52 62 08 00 45 00   ..V.....4.Rb..E.
0010  00 2a 00 07 00 00 ff 06 d6 56 c0 08 32 fa c0 08   .*.......V..2...
0020  32 65 10 01 00 07 00 00 19 82 5d ef 8f e8 50 18   2e........]...P.
0030  16 cc 2c c1 00 00 6f 6b 00 00 00 00               ..,...ok....

No.     Time           Source                Destination           Protocol Length Info
     10 0.019921000    192.8.50.250          192.8.50.101          ECHO     60     Request

0000  f8 b1 56 a2 07 fc 18 fe 34 a0 52 62 08 00 45 00   ..V.....4.Rb..E.
0010  00 2a 00 08 00 00 ff 06 d6 55 c0 08 32 fa c0 08   .*.......U..2...
0020  32 65 10 01 00 07 00 00 19 84 5d ef 8f ea 50 18   2e........]...P.
0030  16 ca 8f 20 00 00 0d 0a 00 00 00 00               ... ........

This looks good. Tomorrow I setup the ESP8266 to be a real server.

This entry was posted in c-esp8266 and tagged . Bookmark the permalink.

One Response to ESP8266/Arduino IDE: Further TCP Exploration

  1. Pingback: ESP8266/Arduino IDE: Building an LED Blink Server | Big Dan the Blogging Man

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s