user66119: I am running the following code on my ESP8266 (AI-Thinker ESP8266MOD).
I send an HTTP GET request and a pin is set to high for a 1/2 second.
However, after some time (sometimes 1hr, 2hrs, 12hrs, totally random), it stops responding to HTTP requests.
I think it might be because of heap fragmentation due to the String objects I'm using.
Questions:
How can I replace the request (in loop()) and s and text (in GenerateResponse()) objects with  char arrays?
After I replace the String objects with char arrays, how can I use the indexOf() method and + operator on the char arrays?
Will this approach help with heap fragmentation? 
Is it possible to find out if heap fragmentation is the problem?
#include <ESP8266WiFi.h>

const char* ssid = "myWifi";
const char* wifiPassword = "y76ggS";
const char* passwordToOpenDoor = "/81"; //password should begin with a slash

const int doorPin = 5;

WiFiServer server(301); //Pick any port number you like
WiFiClient client;

void setup() {
  Serial.begin(115200);
  delay(10);
  Serial.println(WiFi.localIP());

  pinMode(doorPin, OUTPUT);
  digitalWrite(doorPin, 0);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, wifiPassword);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  server.begin();
  Serial.println("Server started. Diagnostics info:");
  Serial.println(WiFi.localIP());
}

void loop() {
  client = server.available();
  if (!client) {    
    return;
  }

  while(!client.available()){
    delay(1);  
  }

  String request = client.readStringUntil('\r'); 
  client.flush();

  Serial.println(request);  
  if (request.indexOf(passwordToOpenDoor) != -1) { //Is password correct?    
    GenerateResponse("Password is correct");
    OpenDoor();    
  }
  //Got a GET request and it wasn't the favicon.ico request, must have been a bad password:
  else if (request.indexOf("favicon.ico") == -1) {  
    GenerateResponse("Password is incorrect.");
  }  
}

void OpenDoor() {
  digitalWrite(doorPin, 1);
  delay(500);
  digitalWrite(doorPin, 0);
}

void GenerateResponse(String text) {
  Serial.println(text);
  String s = "HTTP/1.1 200 OK\r\n";
  s += "Content-Type: text/html\r\n\r\n";
  s += "<!DOCTYPE HTML>\r\n<html>\r\n";
  s += "<br><h1><b>" + text + "</b></h1>";
  s += "</html>\n";
  client.flush();
  client.print(s);
  delay(1);
}


user2967: You asked: "After I replace the String objects with char arrays, how can I use the indexOf() method and + operator on the char arrays?"
Short answer: You can't. If you want to use C strings (aka arrays of char), you'll have to use C string functions.
The String methods operate on String objects. The common wisdom is to avoid the String class entirely since it relies on heap memory, and Arduinos are far, far too memory-starved to be able to use a class like String which creates temporary objects on the heap.

user64075: So here is code to get you started:
myMessageArray [256] = {'\0'}; // Define global array large enough and zero terminate

the + method is replaced by
strcpy (myMessageArray, "Text to Add"); 
// Initializes the myMessageArray starts at index 0
strcat (myMessageArray, "More text to Add"); 
// Appends to the myMessageArray starts at current index

converting numeric values to chars:
uint_16_t myNumberValue  = 31253;
char numBuffer [16] = {'\0'}; // Helper buffer for conversions

itoa (myNumberValue,numBuffer,10);  // converts an integer to a base 10 (decimal) char
itoa (myNumberValue,numBuffer,2);  // converts an integer to a base 2 (binary) char
itoa (myNuberValue,numBuffer,16);  // converts an integer to a base 16 (hex) char

itoa initilizes a char array so we need the helper array:
strcat (myMessageArray, numBuffer);    

to convert float we use
dtostrf(floatVariable, StringLengthIncDecimalPoint, numVarsAfterDecimal, numBuffer);

to convert chars back to int use
int16_t myIntVar = atoi(numBuffer);

to convert chars back to floats use
float myFloatVar = atof(numBuffer, decimalsToShow); // using just atof(numBuffer)
gives you standard x.XX only 2 decimals

A working IndexOf for char arrays:
/*******************************************************************************/
/**
    \brief Finds the index of the given value in the array starting at the given index
    \author codebreaker007
    \param [in] targetArray the array to search through for the char
    \param [in] valueToFind the value to find
    \param [in] startIndex the index to start searching at
    \return the index of the value within the array

    \details This method returns INDEX_NOT_FOUND^(-1) for a null input array.
        A negative startIndex is treated as zero. A startIndex larger than the array
        length will return INDEX_NOT_FOUND(-1)
*/
/********************************************************************************/
int8_t indexOf(char* targetArray, char valueToFind, uint16_t startIndex = 0) {
  if (targetArray == NULL) {
    return INDEX_NOT_FOUND;
  }
  if (startIndex <= 0) {
    startIndex = 0;
  }
  for (uint16_t i = startIndex; i < strlen(targetArray); i++) {
    if (valueToFind == targetArray[i]) {
      return i;
    }
  }
  return INDEX_NOT_FOUND;
}

To compare char arrays you should look into the strcmp function and if you want to checkthe first n char tomatchyou use:
if (strncmp(myMessageArray , "POST", 4 ) == 0) { 
// compares the first 4 chars if it matches = 0, see details in the arduino doku

Hope this comprehensive intro to char arrays helps you for your current and future projects. How to find out - convert to char arrays and if its runnibg stable that was it. Before that conversion you can never rule out String class as culprit.

user7508: You already received good answer about some general ideas for
replacing String objects with C strings. Here I will try to add some
more tricks more specifically targeted to your actual situation.
I can see two places in your program where the use of String shines by
its convenience, and replacing it with C strings will require some
effort. The first is the use of Stream::readStringUntil(char) for
getting the first request line. Here
I see no better option (see edit below) than reading
the stream one character at a time, and putting those characters into an
array:
const size_t input_buffer_length = 256;

// Read a line of text up to the first '\r'.
// Returns a NUL-terminated string, without the final '\r',
// into a statically allocated buffer.
// Warning: this never times out.
const char *readline(Stream &input)
{
  static char buffer[input_buffer_length];
  size_t pos = 0;  // writing position within the buffer
  int c;           // current character
  while ((c = input.read()) != '\r') {
    if (c >= 0 && pos < sizeof buffer - 1) {
      buffer[pos++] = c;
    }
  }
  buffer[pos] = '\0';  // terminate the string
  return buffer;
}

You would use it like this:
const char *request = readline(client);

For more robustness, you may want to add a timeout. Note also that this
implementation is no reentrant, as it uses a static buffer. This is not
an issue for your use case.
Edit: As noted by hcheung in a comment, you do not need to implement
this readline() function, as you can use
Stream::readBytesUntil(), which handles the timeout
and is provided by the Arduino core:
char buffer[input_buffer_length];
client.readBytesUntil('\r', buffer, input_buffer_length);

The other place is the usage of String::operator+=(const char *) for
generating the response. Here I would advice you against using
strcat() or strncat(): these will require extra memory to store the
concatenated string which you probably do not need. Instead, you can
simply print() the pieces one by one:
void GenerateResponse(const char *text) {
  Serial.println(text);
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println();
  client.println("<!DOCTYPE HTML>");
  client.println("<html>");
  client.print("<br><h1><b>");
  client.print(text);
  client.println("</b></h1></html>");
  client.flush();
  delay(1);
}

Alternatively, you can make use of the implicit concatenation of string
literals in order to reduce the number of calls to client.print():
void GenerateResponse(const char *text) {
  Serial.println(text);
  client.print(
      "HTTP/1.1 200 OK\r\n"
      "Content-Type: text/html\r\n"
      "\r\n"
      "<!DOCTYPE HTML>\r\n"
      "<html>\r\n"
      "<br><h1><b>"
  );
  client.print(text);
  client.print("</b></h1></html>\r\n");
  client.flush();
  delay(1);
}


66119: <p>I am running the following code on my ESP8266 (AI-Thinker ESP8266MOD).</p>

<p>I send an HTTP GET request and a pin is set to high for a 1/2 second.</p>

<p>However, after some time (sometimes 1hr, 2hrs, 12hrs, totally random), it stops responding to HTTP requests.
I think it might be because of heap fragmentation due to the String objects I'm using.</p>

<p><strong>Questions:</strong></p>

<p>How can I replace the <code>request</code> (in <code>loop()</code>) and <code>s</code> and <code>text</code> (in <code>GenerateResponse()</code>) objects with  char arrays?
After I replace the String objects with char arrays, how can I use the <code>indexOf()</code> method and <code>+</code> operator on the char arrays?</p>

<p>Will this approach help with heap fragmentation? </p>

<p>Is it possible to find out if heap fragmentation is the problem?</p>

<pre><code>#include &lt;ESP8266WiFi.h&gt;

const char* ssid = "myWifi";
const char* wifiPassword = "y76ggS";
const char* passwordToOpenDoor = "/81"; //password should begin with a slash

const int doorPin = 5;

WiFiServer server(301); //Pick any port number you like
WiFiClient client;

void setup() {
  Serial.begin(115200);
  delay(10);
  Serial.println(WiFi.localIP());

  pinMode(doorPin, OUTPUT);
  digitalWrite(doorPin, 0);

  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

  WiFi.begin(ssid, wifiPassword);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");

  server.begin();
  Serial.println("Server started. Diagnostics info:");
  Serial.println(WiFi.localIP());
}

void loop() {
  client = server.available();
  if (!client) {    
    return;
  }

  while(!client.available()){
    delay(1);  
  }

  String request = client.readStringUntil('\r'); 
  client.flush();

  Serial.println(request);  
  if (request.indexOf(passwordToOpenDoor) != -1) { //Is password correct?    
    GenerateResponse("Password is correct");
    OpenDoor();    
  }
  //Got a GET request and it wasn't the favicon.ico request, must have been a bad password:
  else if (request.indexOf("favicon.ico") == -1) {  
    GenerateResponse("Password is incorrect.");
  }  
}

void OpenDoor() {
  digitalWrite(doorPin, 1);
  delay(500);
  digitalWrite(doorPin, 0);
}

void GenerateResponse(String text) {
  Serial.println(text);
  String s = "HTTP/1.1 200 OK\r\n";
  s += "Content-Type: text/html\r\n\r\n";
  s += "&lt;!DOCTYPE HTML&gt;\r\n&lt;html&gt;\r\n";
  s += "&lt;br&gt;&lt;h1&gt;&lt;b&gt;" + text + "&lt;/b&gt;&lt;/h1&gt;";
  s += "&lt;/html&gt;\n";
  client.flush();
  client.print(s);
  delay(1);
}
</code></pre>
 2967: <p>You asked: "After I replace the String objects with char arrays, how can I use the indexOf() method and + operator on the char arrays?"</p>

<p>Short answer: You can't. If you want to use C strings (aka arrays of char), you'll have to use C string functions.</p>

<p>The String methods operate on String objects. The common wisdom is to avoid the String class entirely since it relies on heap memory, and Arduinos are far, far too memory-starved to be able to use a class like String which creates temporary objects on the heap.</p>
 64075: <p>So here is code to get you started:</p>

<pre><code>myMessageArray [256] = {'\0'}; // Define global array large enough and zero terminate
</code></pre>

<p>the + method is replaced by</p>

<pre><code>strcpy (myMessageArray, "Text to Add"); 
// Initializes the myMessageArray starts at index 0
strcat (myMessageArray, "More text to Add"); 
// Appends to the myMessageArray starts at current index
</code></pre>

<p>converting numeric values to chars:</p>

<pre><code>uint_16_t myNumberValue  = 31253;
char numBuffer [16] = {'\0'}; // Helper buffer for conversions

itoa (myNumberValue,numBuffer,10);  // converts an integer to a base 10 (decimal) char
itoa (myNumberValue,numBuffer,2);  // converts an integer to a base 2 (binary) char
itoa (myNuberValue,numBuffer,16);  // converts an integer to a base 16 (hex) char
</code></pre>

<p>itoa initilizes a char array so we need the helper array:</p>

<pre><code>strcat (myMessageArray, numBuffer);    
</code></pre>

<p>to convert float we use</p>

<pre><code>dtostrf(floatVariable, StringLengthIncDecimalPoint, numVarsAfterDecimal, numBuffer);
</code></pre>

<p>to convert chars back to int use</p>

<pre><code>int16_t myIntVar = atoi(numBuffer);
</code></pre>

<p>to convert chars back to floats use</p>

<pre><code>float myFloatVar = atof(numBuffer, decimalsToShow); // using just atof(numBuffer)
gives you standard x.XX only 2 decimals
</code></pre>

<p>A working IndexOf for char arrays:</p>

<pre><code>/*******************************************************************************/
/**
    \brief Finds the index of the given value in the array starting at the given index
    \author codebreaker007
    \param [in] targetArray the array to search through for the char
    \param [in] valueToFind the value to find
    \param [in] startIndex the index to start searching at
    \return the index of the value within the array

    \details This method returns INDEX_NOT_FOUND^(-1) for a null input array.
        A negative startIndex is treated as zero. A startIndex larger than the array
        length will return INDEX_NOT_FOUND(-1)
*/
/********************************************************************************/
int8_t indexOf(char* targetArray, char valueToFind, uint16_t startIndex = 0) {
  if (targetArray == NULL) {
    return INDEX_NOT_FOUND;
  }
  if (startIndex &lt;= 0) {
    startIndex = 0;
  }
  for (uint16_t i = startIndex; i &lt; strlen(targetArray); i++) {
    if (valueToFind == targetArray[i]) {
      return i;
    }
  }
  return INDEX_NOT_FOUND;
}
</code></pre>

<p>To compare char arrays you should look into the <code>strcmp</code> function and if you want to checkthe first n char tomatchyou use:</p>

<pre><code>if (strncmp(myMessageArray , "POST", 4 ) == 0) { 
// compares the first 4 chars if it matches = 0, see details in the arduino doku
</code></pre>

<p>Hope this comprehensive intro to char arrays helps you for your current and future projects. How to find out - convert to char arrays and if its runnibg stable that was it. Before that conversion you can never rule out String class as culprit.</p>
 7508: <p>You already received good answer about some <em>general</em> ideas for
replacing <code>String</code> objects with C strings. Here I will try to add some
more tricks more specifically targeted to your actual situation.</p>

<p>I can see two places in your program where the use of <code>String</code> shines by
its convenience, and replacing it with C strings will require some
effort. The first is the use of <code>Stream::readStringUntil(char)</code> for
getting the first request line. Here
<s>I see no better option</s> (see edit below) than reading
the stream one character at a time, and putting those characters into an
array:</p>

<pre class="lang-cpp prettyprint-override"><code>const size_t input_buffer_length = 256;

// Read a line of text up to the first '\r'.
// Returns a NUL-terminated string, without the final '\r',
// into a statically allocated buffer.
// Warning: this never times out.
const char *readline(Stream &amp;input)
{
  static char buffer[input_buffer_length];
  size_t pos = 0;  // writing position within the buffer
  int c;           // current character
  while ((c = input.read()) != '\r') {
    if (c &gt;= 0 &amp;&amp; pos &lt; sizeof buffer - 1) {
      buffer[pos++] = c;
    }
  }
  buffer[pos] = '\0';  // terminate the string
  return buffer;
}
</code></pre>

<p>You would use it like this:</p>

<pre class="lang-cpp prettyprint-override"><code>const char *request = readline(client);
</code></pre>

<p>For more robustness, you may want to add a timeout. Note also that this
implementation is no reentrant, as it uses a static buffer. This is not
an issue for your use case.</p>

<p><strong>Edit</strong>: As noted by hcheung in a comment, you do not need to implement
this <code>readline()</code> function, as you can use
<a href="https://www.arduino.cc/reference/en/language/functions/communication/stream/streamreadbytesuntil" rel="nofollow noreferrer"><code>Stream::readBytesUntil()</code></a>, which handles the timeout
and is provided by the Arduino core:</p>

<pre class="lang-cpp prettyprint-override"><code>char buffer[input_buffer_length];
client.readBytesUntil('\r', buffer, input_buffer_length);
</code></pre>

<p>The other place is the usage of <code>String::operator+=(const char *)</code> for
generating the response. Here I would advice you <em>against</em> using
<code>strcat()</code> or <code>strncat()</code>: these will require extra memory to store the
concatenated string which you probably do not need. Instead, you can
simply <code>print()</code> the pieces one by one:</p>

<pre class="lang-cpp prettyprint-override"><code>void GenerateResponse(const char *text) {
  Serial.println(text);
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println();
  client.println("&lt;!DOCTYPE HTML&gt;");
  client.println("&lt;html&gt;");
  client.print("&lt;br&gt;&lt;h1&gt;&lt;b&gt;");
  client.print(text);
  client.println("&lt;/b&gt;&lt;/h1&gt;&lt;/html&gt;");
  client.flush();
  delay(1);
}
</code></pre>

<p>Alternatively, you can make use of the implicit concatenation of string
literals in order to reduce the number of calls to <code>client.print()</code>:</p>

<pre class="lang-cpp prettyprint-override"><code>void GenerateResponse(const char *text) {
  Serial.println(text);
  client.print(
      "HTTP/1.1 200 OK\r\n"
      "Content-Type: text/html\r\n"
      "\r\n"
      "&lt;!DOCTYPE HTML&gt;\r\n"
      "&lt;html&gt;\r\n"
      "&lt;br&gt;&lt;h1&gt;&lt;b&gt;"
  );
  client.print(text);
  client.print("&lt;/b&gt;&lt;/h1&gt;&lt;/html&gt;\r\n");
  client.flush();
  delay(1);
}
</code></pre>
