Adafruit_io_client wifi problem on argon

I am using an argon to collect data from multiple BME280’s on a mesh, and publishing it to adafruit.io rest interface using the adafruit_io_particle client library. I can get the publish working properly for 8 hours or so, until I start getting a “Failed to connect to service!” error from the adafruit_io_client library. When sniffing packets from the Argon, once I see the error, all TCP/http traffic from the Argon stops, but the UDP traffic to particle cloud continues to publish.

I have added code to identify when my send to adafruit.io fails, but haven’t been able to reset the connections. My best guess is that the issue is related to the ephemeral ports. I noticed every send is on the subsequent port, so the ports may be staying open, and once the last port in the ephemeral range is used, the http sends fail. A pin reset restarts publishing to adafruit.io.

My next step is to try to add the libraries into my project to try to add some more debug code, but I’m hoping someone can provide some hints on ways to debug the wifi TCP issues, such as a way to see what ports may be open on the Argon, or a way to restart it short of a pin reset. I have tried a client.stop() followed by creating a new Adafruit_IO_Client, but that does not seem to solve the problem.

Can you show your code or provide a test code the exhibits the same issue?

https://www.digikey.com/en/maker/blogs/2019/how-to-use-adafruit-io-with-particle-photons

This is the example code I started with. My changes were to publish 9 feeds, and subscribe to 4 mesh feeds. I will try to make a new simpler version removing the mesh with multiple publish events I added to see if that results in a similar issue. It is possible that the mesh event interrupts are part of the issue.

One quick update… I removed the mesh feeds, and the code has been running over 24 hours, but with 1/3 of the data volume. I will add additional volume by storing it 3x a minute before I add back in the mesh feeds.

It’s possible that the interrupts to process the mesh messages are part of the issue. I will post example code once I can replicate the error with a simpler example.

Here is my example code. The issue appears to be related to the mesh interrupts, since I wasn’t able to replicate the issue with just local data. This version of the code subscribes to 2 mesh datafeeds

/***************************************************************************
  This is a library for the BME280 humidity, temperature & pressure sensor

  Designed specifically to work with the Adafruit BME280 Breakout
  ----> http://www.adafruit.com/products/2650

  These sensors use I2C or SPI to communicate, 2 or 4 pins are required
  to interface. The device's I2C address is either 0x76 or 0x77.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
  See the LICENSE file for details.
 ***************************************************************************/

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

#define BME_SCK D4
#define BME_MISO D3
#define BME_MOSI D2
#define BME_CS D5

#define SEALEVELPRESSURE_HPA (1013.25)

//#include <Adafruit_IO_Client.h>
#include "Adafruit_IO_Client.h"				// Adafruit IO Librarys
#include "Adafruit_IO_Particle.h"
#define AIO_KEY "AIO_Key_inserted_here"     // Adafruit IO AIO Key

TCPClient client; // TCP Client used by Adafruit IO library
// Create the AIO client object
Adafruit_IO_Client  AIOClient = Adafruit_IO_Client(client, AIO_KEY);
 
// Create meshfeed object to get data
Adafruit_IO_Feed    tempFeed = AIOClient.getFeed("argon.temp");
Adafruit_IO_Feed    tempFeedx = AIOClient.getFeed("xenon.temp");
Adafruit_IO_Feed    humidFeed = AIOClient.getFeed("argon.humid");
Adafruit_IO_Feed    humidFeedx = AIOClient.getFeed("xenon.humid");
Adafruit_IO_Feed    presFeed = AIOClient.getFeed("argon.pres");
Adafruit_IO_Feed    presFeedx = AIOClient.getFeed("xenon.pres");
Adafruit_IO_Feed    tempFeedx2 = AIOClient.getFeed("xenon2.temp");
Adafruit_IO_Feed    humidFeedx2 = AIOClient.getFeed("xenon2.humid");
Adafruit_IO_Feed    presFeedx2 = AIOClient.getFeed("xenon2.pres");

const char *FIELD_SEPARATOR = "||";
const char *EVENT_NAME = "tempSensor";
const char *EVENT_NAME2 = "mesh_pub";
const char *EVENT_NAME3 = "temp_pub";
const char *EVENT_NAME4 = "AIO_disconnect";
const char *EVENT_NAME5 = "Argon_hostname";
char buf[256];
bool AIO_OK;

Adafruit_BME280 bme; // I2C
//Adafruit_BME280 bme(BME_CS); // hardware SPI
//Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI

unsigned long delayTime, resetTime;
int event1;


void setup()
{
    
    String deviceid = System.deviceID();
    Particle.publish(EVENT_NAME5, deviceid , PRIVATE);
    
    // Start the Adafruit IO Client
    AIOClient.begin();
    
    // Reset polling timer to use timer instead of delay
    event1 = millis();
    
    Serial.begin(9600);
    while (!Serial)
        ; // time to get serial running
    Serial.println(F("BME280 test"));

    unsigned status;

    // default settings
    // (you can also pass in a Wire library object like &Wire2)
    status = bme.begin();
    if (!status)
    {
        Serial.println("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
        Serial.print("SensorID was: 0x");
        Serial.println(bme.sensorID(), 16);
        Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
        Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
        Serial.print("        ID of 0x60 represents a BME 280.\n");
        Serial.print("        ID of 0x61 represents a BME 680.\n");
        while (1)
            ;
    }
    
    Mesh.subscribe("xenon1mesh", meshHandler1);
    Mesh.subscribe("xenon2mesh", meshHandler2);
    
    Serial.println("-- Default Test --");
    delayTime = 60000;
    
    // SYSTEM_THREAD(ENABLED);
    Serial.println();
}

void loop()
{
//    if ( (millis() - event1) > delayTime)  // delaytime or more have passed
//  {
//    printValues();
//    event1 = millis();  // reset event timer
//  }
  
    printValues();
    delay(delayTime);
}

void printValues()
{
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");

    Serial.print("Pressure = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");
    Serial.println();

    float temp = (bme.readTemperature() * 9.0) / 5.0 + 32.0; // degrees F
    float pressure = bme.readPressure() / 100.0; // hPa
    float humidity = bme.readHumidity(); // % 
    float alt = bme.readAltitude(SEALEVELPRESSURE_HPA);

    
    snprintf(buf, sizeof(buf), "%.02f%s%.02f%s%.01f%s%.01f", temp, FIELD_SEPARATOR, pressure, FIELD_SEPARATOR, humidity, FIELD_SEPARATOR, alt);
    Particle.publish(EVENT_NAME, buf , PRIVATE);
       
    AIO_OK = tempFeed.send(temp);
    
    if (!AIO_OK) {
        Particle.publish(EVENT_NAME4, "AIO_OK is false" , PRIVATE);
    }
    
    humidFeed.send(humidity);
    presFeed.send(pressure);
}


void meshHandler1(const char *event, const char *data)
{
  //Serial.printlnf("event=%s data=%s", event, data ? data : "NULL");
  //float xenonhumid2 = atof(data);
  //Particle.publish(EVENT_NAME2, data, PRIVATE);
  
    String meshstr = String(data);
    //Particle.publish(EVENT_NAME2, data, PRIVATE);
    int commaPosition = meshstr.indexOf(",");
    if(commaPosition>-1){
        String tempstr = meshstr.substring(0,commaPosition); //send first part to line 1 temp
        float xenontemp = atof(tempstr);
        tempFeedx.send(xenontemp);
        
        int commaPosition2 = meshstr.indexOf(",",commaPosition+1);
        String presstr = meshstr.substring(commaPosition+1,commaPosition2); //send first part to line 1 temp
        float xenonpres = atof(presstr);
        presFeedx.send(xenonpres);
        //Particle.publish(EVENT_NAME2, presstr, PRIVATE);
        
        String humidstr = meshstr.substring(commaPosition2+1); //send last part to line 1 humidity
        float xenonhumid = atof(humidstr);
        humidFeedx.send(xenonhumid);
        //Particle.publish(EVENT_NAME3, humidstr, PRIVATE);
       
    }
  //humidFeedx2.send(xenonhumid2);
}

void meshHandler2(const char *event, const char *data)
{
  //Serial.printlnf("event=%s data=%s", event, data ? data : "NULL");
  //float xenonhumid2 = atof(data);
  //Particle.publish(EVENT_NAME2, data, PRIVATE);
  
    String meshstr = String(data);
    //Particle.publish(EVENT_NAME2, data, PRIVATE);
    int commaPosition = meshstr.indexOf(",");
    if(commaPosition>-1){
        String tempstr = meshstr.substring(0,commaPosition); //send first part to line 1 temp
        float xenontemp = atof(tempstr);
        tempFeedx2.send(xenontemp);
        //delay(1000); 
        
        int commaPosition2 = meshstr.indexOf(",",commaPosition+1);
        String presstr = meshstr.substring(commaPosition+1,commaPosition2); //send first part to line 1 temp
        float xenonpres = atof(presstr);
        presFeedx2.send(xenonpres);
        //delay(1000); 
        //Particle.publish(EVENT_NAME2, presstr, PRIVATE);
        
        String humidstr = meshstr.substring(commaPosition2+1); //send last part to line 1 humidity
        float xenonhumid = atof(humidstr);
        humidFeedx2.send(xenonhumid);
        //delay(1000); 
        //Particle.publish(EVENT_NAME3, humidstr, PRIVATE);
       
    }
  //humidFeedx2.send(xenonhumid2);
}

Here is a screenshot of the error. I periodically see the http error for some of the events, but once the “Failed to connect to service!” error occurs, the tcp traffic stops, so nothing is published to Adafruit.io. After that error, only a pin reset or firmware update restarts the tcp traffic.

One of the first steps to tackle sporadic and/or delayed errors is to get rid of String as this can - over time - result in heap fragmentation which may prevent some (heavier) parts of the device OS from working while others (especially lean ones) still work.

As I see you are using String quite a bit, that would be my first suspect to investigate.

This is better written like this

Adafruit_IO_Client AIOClient(client, AIO_KEY);

To prevent the creation of a temporary object.

Code like this will often break the cloud connection. Whenever you have a potentially long running loop you should at least call Particle.process() therein (or use SYSTEM_THREAD(ENABLED).

You also only need one Mesh.subscribe() and one handler as both handlers appear to do exactly the same thing only to different objects, which can be solved better with an array of feed objects and an initial case that selects the correct part of the array depending on the event name variable.

BTW, is there no way to register "wildcarded" feeds?


possible feeds array
struct EnvironFeeds {
 Adafruit_IO_Feed Temperature;
 Adafruit_IO_Feed Humidity;
 Adafruit_IO_Feed Pressure;
};

EnvironFeeds feed[3] = 
{ { AIOClient.getFeed("argon.temp"), AIOClient.getFeed("argon.humid"), AIOClient.getFeed("argon.press") }
, { AIOClient.getFeed("xenon.temp"), AIOClient.getFeed("xenon.humid"), AIOClient.getFeed("xenon.press") }
, { AIOClient.getFeed("xenon2.temp"), AIOClient.getFeed("xenon2.humid"), AIOClient.getFeed("xenon2.press") }
};
...
void meshHandler(const char *event, const char *data)
{
  int nNode = 0;
  if      (strcmp(event, "xenon1mesh") == 0) nNode = 1;
  else if (strcmp(event, "xenon2mesh") == 0) nNode = 2;

  // from here on 
  // use feed[nNode].Temperature, feed[nNode].Humidity, feed[nNode].Pressure
}

Thanks for the suggestions… I implemented most of them, and reworked the mesh handler to remove all of the temporary substrings. I also reserved ‘meshstr’ at 255 characters as a global variable (not 100% sure if this will help). So far it seems to be running better, but I need to let it run for a while tonight to confirm.

Related to the string comment, would it be better to copy *data to a local char and use strtok to parse it instead of the string functions? I started to look into json formatting the mesh publish, and use the json library to parse it, but that seems way heavier.

Thanks for pointing me in the right direction, as I was starting to debug the client library. It was interesting that the UDP particle traffic was fine long after the TCP/HTTP traffic came to a halt. Definitely need to think a bit differently when programming a mcu dev boards.

void meshHandler1(const char *event, const char *data)
{
    int nNode = 0;
  if      (strcmp(event, "xenon1mesh") == 0) nNode = 1;
  else if (strcmp(event, "xenon2mesh") == 0) nNode = 2;
    
    meshstr = String(data);
    
    int commaPosition = meshstr.indexOf(",");
    if(commaPosition>-1){
        // String tempstr = meshstr.substring(0,commaPosition); //send first part to line 1 temp
        float xenontemp = atof(meshstr.substring(0,commaPosition));
        feed[nNode].Temperature.send(xenontemp);
        
        int commaPosition2 = meshstr.indexOf(",",commaPosition+1);
        // String presstr = meshstr.substring(commaPosition+1,commaPosition2); //send second part to line 1 temp
        float xenonpres = atof(meshstr.substring(commaPosition+1,commaPosition2));
        feed[nNode].Pressure.send(xenonpres);
       
        // String humidstr = meshstr.substring(commaPosition2+1); //send last part to line 1 humidity
        float xenonhumid = atof( meshstr.substring(commaPosition2+1));
        feed[nNode].Humidity.send(xenonhumid);
        
    }
 
}

I'd do that, but a preallocated String should do too.
I might even use sscanf() to do the parsing - mainly for better readability. But since sscanf() doesn't support float on these devices, you'd need three char[] variables to hold the substrings and use atof() - so not too much to be won there.

The changes are looking very promising… the data has been publishing for over 20 hours with only a few sporatic HTTP errors, but when I look at the data on adafruit.io, I see points roughly every minute as expected. I am also not seeing any memory leaks.

I will share my ‘final’ version after a bit more soak time. I also see that the adafruit_bme280_rk library appears to be much slimmer, so I will also try that library to read the data bme280 to see if that frees up some more memory.

It looks like reserving the string to parse the incoming data, and not creating the temporary strings as I parsed solved the issue. I did change to the Adafruit_BME280_RK library, but that really didn’t make a difference in memory size or performance.

Thanks for the help ScruffR. Its a shame that the mesh is being deprecated. Its a neat way to collect distributed data, and the BME280’s are really accurate considering the price.

/***************************************************************************
  This is a library for the BME280 humidity, temperature & pressure sensor

  Designed specifically to work with the Adafruit BME280 Breakout
  ----> http://www.adafruit.com/products/2650

  These sensors use I2C or SPI to communicate, 2 or 4 pins are required
  to interface. The device's I2C address is either 0x76 or 0x77.

  Adafruit invests time and resources providing this open source code,
  please support Adafruit andopen-source hardware by purchasing products
  from Adafruit!

  Written by Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license, all text above must be included in any redistribution
 ***************************************************************************/

// BME280 setup info
#include "Adafruit_BME280_RK.h"
#define SEALEVELPRESSURE_HPA (1013.25)

SYSTEM_THREAD(ENABLED);
Adafruit_BME280 bme; // I2C

unsigned long delayTime;

void printValues();

// Adafruit IO setup
#include "Adafruit_IO_Client.h"				// Adafruit IO Library
#define AIO_KEY "Your_AIO_Client_key_here"       // Adafruit IO AIO Key

TCPClient client; // TCP Client used by Adafruit IO library
Adafruit_IO_Client  AIOClient(client, AIO_KEY); // Create the AIO client object

// Create meshfeed object to get data
struct EnvironFeeds {
 Adafruit_IO_Feed Temperature;
 Adafruit_IO_Feed Humidity;
 Adafruit_IO_Feed Pressure;
};

// Create feed entries for each mesh source
EnvironFeeds feed[3] = 
{ { AIOClient.getFeed("argon.temp"), AIOClient.getFeed("argon.humid"), AIOClient.getFeed("argon.pres") }
, { AIOClient.getFeed("xenon.temp"), AIOClient.getFeed("xenon.humid"), AIOClient.getFeed("xenon.pres") }
, { AIOClient.getFeed("xenon2.temp"), AIOClient.getFeed("xenon2.humid"), AIOClient.getFeed("xenon2.pres") }
};

const char *FIELD_SEPARATOR = "||";
const char *EVENT_NAME = "tempSensor";
const char *EVENT_NAME2 = "AIO_disconnect";
const char *EVENT_NAME3 = "Argon_hostname";
char buf[256];
bool AIO_OK; // bool to store return code from aio send
String meshstr; // global string to parse mesh data


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

    bool status;
    // Address is either 0x76 or 0x77. If omitted, the default is 0x77.
    status = bme.begin(0x76);
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1);
    }
    
    Serial.println("-- Default Test --");
    delayTime = 60000;
    Serial.println();
    
    // Publish deviceID that is also network hostname
    String deviceid = System.deviceID();
    Particle.publish(EVENT_NAME3, deviceid , PRIVATE);
    
    AIOClient.begin(); // Start the Adafruit IO Client 
    
    meshstr.reserve(255); // reserve string for parsing mesh subscription - sized for max mesh size of 255
    // subscribe to mesh data published by xenon's
    Mesh.subscribe("xenon1mesh", meshHandler);
    Mesh.subscribe("xenon2mesh", meshHandler);
    
}

void loop()
{

    printValues();
    delay(delayTime);
}

void printValues()
{
    Serial.print("Temperature = ");
    Serial.print(bme.readTemperature());
    Serial.println(" *C");

    Serial.print("Pressure = ");

    Serial.print(bme.readPressure() / 100.0F);
    Serial.println(" hPa");

    Serial.print("Approx. Altitude = ");
    Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
    Serial.println(" m");

    Serial.print("Humidity = ");
    Serial.print(bme.readHumidity());
    Serial.println(" %");

    Serial.println();

    float temp = (bme.readTemperature() * 9.0) / 5.0 + 32.0; // degrees F
    float pressure = bme.readPressure() / 100.0; // hPa
    float humidity = bme.readHumidity(); // % 
    float alt = bme.readAltitude(SEALEVELPRESSURE_HPA);

    snprintf(buf, sizeof(buf), "%.02f%s%.02f%s%.01f%s%.01f", temp, FIELD_SEPARATOR, pressure, FIELD_SEPARATOR, humidity, FIELD_SEPARATOR, alt);
    Particle.publish(EVENT_NAME, buf , PRIVATE);
       
    int nNode = 0; // native argon feed is index 0

    AIO_OK = feed[nNode].Temperature.send(temp); // check return code on temperature send
    if (!AIO_OK) {
        Particle.publish(EVENT_NAME3, "AIO_OK is false - AdafruitIO send failed" , PRIVATE);
    }
    feed[nNode].Humidity.send(humidity);
    feed[nNode].Pressure.send(pressure);
}


void meshHandler(const char *event, const char *data)
{
    // Parse mesh data to get temp, humidity, and pressure data from mesh xenon devices
    // use nNode to swap adafruit feeds depending on xenon that published it
    int nNode = 0;
    if      (strcmp(event, "xenon1mesh") == 0) nNode = 1;
    else if (strcmp(event, "xenon2mesh") == 0) nNode = 2;
    
    meshstr = String(data);
    
    int commaPosition = meshstr.indexOf(",");
    if(commaPosition>-1){
        
        // send first part of line to temperature
        float xenontemp = atof(meshstr.substring(0,commaPosition));
        feed[nNode].Temperature.send(xenontemp);
        
        //send second part to line to pressure
        int commaPosition2 = meshstr.indexOf(",",commaPosition+1);
        float xenonpres = atof(meshstr.substring(commaPosition+1,commaPosition2));
        feed[nNode].Pressure.send(xenonpres);
       
        //send last part to line to humidity
        float xenonhumid = atof( meshstr.substring(commaPosition2+1));
        feed[nNode].Humidity.send(xenonhumid);
        
    }
 
}


This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.