WiFi101 Client HTTP POST request fails after 3rd time

I would really appreciate any help, my home project has been put on hold for weeks as I haven't been able to figure this out. In a nutshell, I'm connecting to my wifi network, measuring temperature from a sensor, and sending that temperature data to a server via a HTTP Request. All of that stuff works.

I have it in a loop, and on the 3rd HTTP request I get 'Connection Failed' when it's trying to client.connect(server, 80) - I don't understand why it fails. I've tried adding client.stop() and client.flush() after each HTTP request, but nothing fixes it. Below is my sketch file with some of the temperature code removed to make it easier to read

#include <SPI.h>
#include <WiFi101.h>
#include <string.h>
#include <stdlib.h>
#include <OneWire.h>
#include <DallasTemperature.h>

//WiFi Client Setup
char ssid[] = "HIDDEN";    //  your network SSID (name)
char pass[] = "HIDDEN";       // your network password
int status = WL_IDLE_STATUS;    // the WiFi radio's status
char server[] = "HIDDEN";
WiFiClient client;

// Setup for temperature sensor
#define ONE_WIRE_BUS A5
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress insideThermometer;


void setup() {
  Serial.begin(9600);
  WiFi.setPins(8,2,A3,-1);
  while (!Serial) {
    Serial.println("Waiting for serial");
  }

  //Connect to wifi shield
  if (WiFi.status() == WL_NO_SHIELD) {
    Serial.println("WiFi shield not present");
    while (true);
  }
  wifiConnect();
  sensors.begin();
}

void loop() {
  while (client.available()) {
    char c = client.read();
    Serial.write(c);
  }
  /*
  Code to get temperature here
  */
  httpRequest(temp);
  delay(10000); // 5min
}

void httpRequest(String temp) {
  String request = "POST /api/webhooks/temp?temperature=" + temp + " HTTP/1.1";

  // if there's a successful connection:
  if (client.connect(server, 80)) {
    Serial.println("connected to server");
    client.println(request);
    client.println("Authorization: Token HIDDEN");
    client.println("Host: HIDDEN");
    client.println("Connection: close");
    client.println();
  }
  else {
    Serial.println("Connection failed");
  }
}

void wifiConnect() {
  //Attempt to connect to WiFi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to WPA SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, pass);
    delay(1000);
  }

  Serial.println("You're connected to the network");
}

Try not to use Strings. They cause memory fragmentation.

Here's an example that uses null terminated char arrays instead. It's written for the ESP8266, but it's pretty similar.

Pieter

PieterP:
Try not to use Strings. They cause memory fragmentation.

Are you saying to do something like

const char* httpHeader =         "POST " URI " HTTP/1.1\r\n"
                                 "Host: " HOST "\r\n"
                                 "Content-Type: application/x-www-form-urlencoded\r\n"
                                 "Connection: close\r\n"

Instead of defining the POST request with strings? Did this only apply to the HTTP Request or should I avoid strings in general for Arduino.

Thank you!

I'm talking about Strings with a capital S. They dynamically allocate memory, which is not a problem on a computer with 16GB of RAM, but can lead to problematic memory fragmentation on a microcontroller with 2KB of RAM.
Let's say you have 24 bytes of memory, and you allocate four 8-byte blocks of memory:
Block 1 (8B) - Block 2 (8B) - Block 3 (8B) - Block 4 (8B)
Let's say you no longer need blocks 2 and 4, so you free them:
Block 1 (8B) - Free (8B) - Block 3 (8B) - Free (8B)
In total, you now have 16 bytes of memory available, but if you want to allocate an array of 16 bytes, it'll crash because it can't allocate one continuous block of 16 bytes.

If you have one large char buffer with a fixed size, you won't run into these kinds of problems.

Note that you need to specify the Content-Length as well when sending POST data in the body of the request, otherwise, the server will ignore the request or throw an error.

The main difference is the content of the body, and how strings (lowercase s) are concatenated:

const char* field_names[] = {"field1", "field2", "field3"};
const size_t nb_fields = sizeof(field_names) / sizeof(field_names[0]);

/*
    Generate the body url-encoded string, based on the field names and the respective values.
    Write this string to the httpBody buffer.
    Returns the number of characters it wrote.
*/
size_t generateBodyStr(char* httpBody, int values[nb_fields]) {
  if (nb_fields == 0)
    return 0;
  // print the first field name and value as key=value pair
  size_t bodyLen = sprintf(httpBody, "%s=%d", field_names[0], values[0]);  
  for (size_t i = 1; i < nb_fields; i++) {  // append the remaining field names and values to the string
    // each pair is separated by an ampersand (&)
    bodyLen += sprintf(&httpBody[bodyLen], "&%s=%d", field_names[i], values[i]);
  }
  return bodyLen;
}

Note that you can't use the "%f" specifier with sprintf. If you need to convert a float or a double to a string, use dftostrf.

Pieter

Genuine question, what makes you think this is causing the HTTP request to fail?

I'm not sure, the fact that it works with the first three requests indicates a memory problem. Maybe there's not enough memory left to start a new TCP connection, so that's why it fails?
Does removing all Strings fix it? Then you probably have your answer.
Definitely add client.stop() as well before trying to reconnect.

Pieter

I may be off base here, but it seems to me I remember something about only 4 connections being supported. If you are not closing them correctly when finished, you may be running out of connections. Look at the log files on your server for any clues.

@gpsmikey I believe the connection is being closed. I've watched the requests come into the server and it takes the request and responds with a 201.

My sketch used to fail after 2 requests, not it's 3. Not sure if that helps diagnose anything. I haven't gotten to PieterP's suggestion of changing String's to Char's, but will try that.

Overall pretty frustrated by the lack of good documentation and response information on these libraries :\

I tried changing the strings to char's, that didn't work.
Verified that it was closing connections with the server, check.

Was reading around and saw someone who tried a soft reset of the wifi client here . I added the following to the end of my httpRequest() method

client.stop();
WiFi.end();
delay(10);
WiFi.begin(ssid, pass);

And now it's working just fine. I'm going to have it run all night and see if there are any issues, but it's definitely an improvement as I can make more than 3 requests :roll_eyes:

This seems like a terrible work around. Clearly something isn't getting reset after each HTTP request on the wifi client, and this (right now) is the only way to solve it. I'd really love any other feedback on a better ways to solve it. Seems like some issues with the WiFi101 Library.