long term stability, avoid code freezing

Hey,

This has been bothering me for a while. I've successfully learned to push data to data.sparkfun.com using my Arduino Uno r3, CC3000 wifi shield and a DHT22 sensor.
It works great for a limited time, about 1-3hours until it finally freezes.

Is there any easy way of forcing a restart of the programme if it ends up freezing? I looked at using the watchdog but it seems its limited to <8s laps.

Might be I have plenty of bugs in the code which causes the freeze or even inside the DHT library I pulled from Adafruit's github. But, how can I solve this in the easiest way possible for a beginner like me?

/ Seb

https://data.sparkfun.com/streams/bGngMoGlRvilpooY81W9

/////////////////////////////////////////////////////////////////////////
//////////////////////////// CODE TO CONNECT TO SPARKFUN /////////////////
//////////////////////////////PUSHES DATA TO SERVER////////////////////////

/// https://data.sparkfun.com/streams/bGngMoGlRvilpooY81W9

// ****************************************************/
#include <Adafruit_CC3000.h>
#include <SPI.h>
#include "utility/debug.h"
#include "utility/socket.h"

// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ   3  // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT  5 //Along with the SPI interface, there is a power-enable type pin called VBAT_EN which we use to start the module properly 
#define ADAFRUIT_CC3000_CS    10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
                         SPI_CLOCK_DIVIDER); // you can change this clock speed

#define WLAN_SSID       "XX"           // cannot be longer than 32 characters!
#define WLAN_PASS       "XX"
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY   WLAN_SEC_WPA2

#define LISTEN_PORT           23    // What TCP port to listen on for connections.

/////////////////////////////////////////////////////////////////////////
//////////////////////////// CODE TO CONNECT TO SPARKFUN /////////////////
/////////////////////////////////////////////////////////////////////////
Adafruit_CC3000_Client client;
char server[] = "data.sparkfun.com";    // name address for data.sparkFun (using DNS)
/////////////////
// Phant Stuff //
/////////////////
const String publicKey = "bGngMoGlRvilpooY81W9";
const String privateKey = "XX";
const byte NUM_FIELDS = 2; // Define how many data fields to push

const String fieldNames[NUM_FIELDS] = {"temp", "rh"}; //Must be the same as specified at data.sparkfun
String fieldData[NUM_FIELDS];

// Define correct string position and name. MUST BE SAME AS cont STRING fieldNames....
#define temp   0
#define rh   1
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////
//////DEFINE DATA FOR SENSORS ////////////////////////
//////////////////////////////////////////////////////

#include "DHT.h"
#define DHTPIN 8     // what digital pin used to read DHT22
#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
DHT dht(DHTPIN, DHTTYPE);

//////////////////////////////////////////////////////
//////////////////////////////////////////////////////

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

  Serial.println(F("Hello, CC3000!\n"));

  Serial.print("Free RAM: "); Serial.println(getFreeRam(), DEC);

  /* Initialise the module */
  Serial.println(F("\nInitializing..."));
  if (!cc3000.begin())
  {
    Serial.println(F("Couldn't begin()! Check your wiring?"));
    while (1);
  }

  Serial.print(F("\nAttempting to connect to ")); Serial.println(WLAN_SSID);
  if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
    Serial.println(F("Failed!"));
    while (1);
  }

  Serial.println(F("Connected!"));

  Serial.println(F("Request DHCP"));
  while (!cc3000.checkDHCP())
  {
    delay(100); // ToDo: Insert a DHCP timeout!
  }

  /* Display the IP address DNS, Gateway, etc. */
  while (! displayConnectionDetails()) {
    delay(1000);
  }

}

void loop(void) {

  /////////////////// READ THE SENSORS//////////////////////////////////////////////////


  float h = dht.readHumidity();
  float t = dht.readTemperature();

 

  /////////////////// ADD SENSOR VALUES INTO STRING//////////////////////////////////////////////////
  fieldData[0] = String(t);
  fieldData[1] = String(h);
  //////////////////////////////////////////////////////////////////////////////////////////
  //////////////////////////////////////////////////////////////////////////////////////////

///////////////// POST TO SPARKFUN//////////////////
  postData();
  delay(30000);
////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////// CODE TO CONNECT TO SPARKFUN AND PUSHES THE DATA /////////////////
//////////////////////////////////////////////////////////////////////////////////////////////
void postData()
{
  // Make a TCP connection to remote host
  if (client.connect(server, 80))
  {
    // Post the data! Request should look a little something like:
    // GET /input/publicKey?private_key=privateKey&light=1024&switch=0&name=Jim HTTP/1.1\n
    // Host: data.sparkfun.com\n
    // Connection: close\n
    // \n
    client.print("GET /input/");
    client.print(publicKey);
    client.print("?private_key=");
    client.print(privateKey);
    for (int i = 0; i < NUM_FIELDS; i++)
    {
      client.print("&");
      client.print(fieldNames[i]);
      client.print("=");
      client.print(fieldData[i]);
    }
    client.println(" HTTP/1.1");
    client.print("Host: ");
    client.println(server);
    client.println("Connection: close");
    client.println();
  }
  else
  {
    Serial.println(F("Connection failed"));
  }

  // Check for a response from the server, and route it
  // out the serial port.
  while (client.connected())
  {
    if ( client.available() )
    {
      char c = client.read();
      Serial.print(c);
    }
  }
  Serial.println();
  client.stop();
}
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////


/**************************************************************************/
/*!
    @brief  Tries to read the IP address and other connection details
*/
/**************************************************************************/
bool displayConnectionDetails(void)
{
  uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;

  if (!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv))
  {
    Serial.println(F("Unable to retrieve the IP Address!\r\n"));
    return false;
  }
  else
  {
    Serial.print(F("\nIP Addr: ")); cc3000.printIPdotsRev(ipAddress);
    Serial.print(F("\nNetmask: ")); cc3000.printIPdotsRev(netmask);
    Serial.print(F("\nGateway: ")); cc3000.printIPdotsRev(gateway);
    Serial.print(F("\nDHCPsrv: ")); cc3000.printIPdotsRev(dhcpserv);
    Serial.print(F("\nDNSserv: ")); cc3000.printIPdotsRev(dnsserv);
    Serial.println();
    return true;
  }
}

Try using a fixed character buffer for the field data (instead of String). Cheers!

You're using String class.

Don't do that. Strings are bad, particularly for stability. Use strings (c strings, ie, fixed size char arrays). The dynamic memory allocation of String is a recipe for disaster on a platform with 2k of ram. Note that of course, with fixed size char arrays, you need to be careful to check the bounds, as writing past the end of an array will likely also hang or reset the Arduino.

kowalski:
Try using a fixed character buffer for the field data (instead of String). Cheers!

Alright, I will try to change it. Thanks!

DrAzzy:
You're using String class.

Don't do that. Strings are bad, particularly for stability. Use strings (c strings, ie, fixed size char arrays). The dynamic memory allocation of String is a recipe for disaster on a platform with 2k of ram. Note that of course, with fixed size char arrays, you need to be careful to check the bounds, as writing past the end of an array will likely also hang or reset the Arduino.

Thanks. I will try it.
/Erik

Delta_G:
I think you misunderstand the watchdog. The 8 seconds is how long after it freezes up before it is reset. That time can be reduced, but you must make sure you reset the watchdog at least that often or it will reset your board.

No I beleive I got it. My problem is one of my sensor reading I want to add takes about 30 seconds to read (not the DHT22 sensor...) and I dont see any way of including the watchdog reset function within those 30 seconds. In this example I have an 30s delay between each reading, would be great if I there was a way I could adjust the watchdog or go in another direction.
Thanks!

/Seb

Vatsug:
one of my sensor reading I want to add takes about 30 seconds to read

Perhaps you can initiate the reading and come back later to get the result. That would avoid the need to hang around for 30 secs.

...R

Delta_G:
Don't use delay. If you really want a dead wait there, Do it with a while loop checking millis so you can reset the watchdog while you wait.

Delta G, sorry for the late reply... Why would you say its the main reason to use an while loop instead. Would it make the code more stable?

/Seb

Robin2:
Perhaps you can initiate the reading and come back later to get the result. That would avoid the need to hang around for 30 secs.

...R

Robin2, I am calling a function via Library. The function takes 30sec to compile so I assumed the code willl get stuck for 30s before the return value is sent back from the library function. Or how would you go about it?
Thanks a lot for the reply!

/ Seb

By "compile" I hope you mean "run". The time to compile and upload the code onto the Arduino board is not usually considered in the design.

30 seconds is long. Stupendously stupidly long. In the Arduino world, 30 milliseconds is considered to be galactically slow and difficult to work with. In some parts of the code, 30 microseconds is unacceptably slow. What the heck library is this?

If you really need it....

Here is a way to extend the watchdog beyond 8 seconds.

http://forum.arduino.cc/index.php?topic=248263.0

I have had a CC3000 running for a couples of months using that code.

Hi,
Can you please post a copy of your circuit, in CAD or a picture of a hand drawn circuit in jpg, png?
How are you powering the project and what is the application?
Have you any long wires and what is the sensor that takes so long to read?

Tom.... :slight_smile:

Vatsug:
Robin2, I am calling a function via Library.

Maybe it is time to look for a better library. Or time to hack the library you have to make it properly responsive.

Post a link to the datasheet for the sensor you are trying to read.

I am strongly opposed to kudges that make the watchdog keep quiet for 30 seconds. What's to stop the kludge getting in an endless loop and rendering the watchdog useless.

The 8 seconds allowed by the watchdog is the equivalent of 8 years in human terms.

...R

Hey guys,

Thanks for the input.

Ok. Below is my code and I have also attached the libraries I'm using.

Project:
Arduino Uno r3 with CC3000 wifi shield
Sensors: DHT22 / PPD42NJ
Reading two sensors and push the data to Sparkfun. Reading both temp/humidity from the DHT22 and also the dust concentration from the PPD42NJ.

The PPD42NJ dust sensor require a 30s sensor reading which I belive makes it difficult to use watchdog. I did try and add an watchdog (8s) on other points in the code to stabilize the program. It didnt really help and the system still goes down every 2-4hours. I've removed the watchdog in the attached code since I didnt get it to work properly.

The plan is to keep reading for hours and hours without downtime and I haven't been able to find out a way to re-boot the system if it does get stuck somewhere.
All help is appreciated I dont really know what to do here=) Also, I am still fairly new to Arduino..

/ Seb

dht.cpp (5.66 KB)

dht.h (1.95 KB)

TEST-DATATOSPARKFUN.ino (6.86 KB)

PPD42NJpm10.cpp (1.28 KB)

PPD42NJpm10.h (417 Bytes)

PPD42NS.pdf (377 KB)

DHT22.pdf (897 KB)

Did you read post #13? That shows you how to extend the watchdog from 8 seconds to any duration you want. 30 seconds, a minute, ten minutes, an hour, 8 years, an ice age, an eon, whatever.

But before you go that route, why don't you try to figure out why, or at least "where," your code is hanging? Is it really during the dust sensor reading? Or is it somewhere else?

One way to do that: add Serial.println statements before and after each of the sensor reads. Add Serial.println statements in postData(). Run it multiple times, so it hangs a lot. You should be able to pin each hang down to exactly one line in your code.

Also, notice whether your getFreeRam() is reporting decreasing free RAM.

If your situation is like mine, there are no memory or sensor issues, but nearly every CC3000 command will hang at some point. Since I didn't (and still don't) have the skills or time to dive into the CC3000 library, I merely wrote an original and clever way of extending the watchdog, if I do say so myself. Oh, wait. I didn't say that, westfw did. Call it a kluge if you will. It works.

The way it could work for you, if you can't resolve it any other way: figure out how long it normally takes one loop to complete, and add, say, ten percent. Then set the watchdog at the beginning of loop(), and stop it at the end of loop().

Vatsug:
The PPD42NJ dust sensor require a 30s sensor reading

I can see where the dust sensor library uses a 30 second period, but I can't immediately understand how the sensor works - the datasheet is very sparse. Can you explain it?

My guess is that it requires a certain number of samples in a specified time. Perhaps it is not essential to take them all in a single call to the library.

If so it may be possible to modify the library.

...R

DaveEvans:
Did you read post #13? That shows you how to extend the watchdog from 8 seconds to any duration you want. 30 seconds, a minute, ten minutes, an hour, 8 years, an ice age, an eon, whatever.

But before you go that route, why don't you try to figure out why, or at least "where," your code is hanging? Is it really during the dust sensor reading? Or is it somewhere else?

One way to do that: add Serial.println statements before and after each of the sensor reads. Add Serial.println statements in postData(). Run it multiple times, so it hangs a lot. You should be able to pin each hang down to exactly one line in your code.

Also, notice whether your getFreeRam() is reporting decreasing free RAM.

If your situation is like mine, there are no memory or sensor issues, but nearly every CC3000 command will hang at some point. Since I didn't (and still don't) have the skills or time to dive into the CC3000 library, I merely wrote an original and clever way of extending the watchdog, if I do say so myself. Oh, wait. I didn't say that, westfw did. Call it a kluge if you will. It works.

The way it could work for you, if you can't resolve it any other way: figure out how long it normally takes one loop to complete, and add, say, ten percent. Then set the watchdog at the beginning of loop(), and stop it at the end of loop().

DaveEvans,

Thanks alot for your support! I am running the code right now using the Watchdog code you posted. I'l keep you updated how it's working for me. Good idea to try and figure out where the code has problem (if its the code....), I will try that too later this week.

/ Seb

The PPD42NJ dust sensor require a 30s sensor reading which I belive makes it difficult to use watchdog.

The 30s reading does not make it difficult to use the watchdog.

You simply avoid use of delay(). Since you now have plenty of time to do other things, while waiting for the proper time to read the sensor, reset the watchdog more rapidly than every 8 seconds (or whatever watchdog period you choose).

However, with proper programming you should not even need the watchdog.

Why not create a sketch whose sole purpose is to calculate PM10 for different "sampletime_ms"? Compare results for various sample times...say, from one second to 30 seconds...in steps of about five seconds. There doesn't seem to be anything magic about a sample time 30 seconds, except as an averaging technique. You just need to get enough low pulses within the sample time to get a sufficiently "accurate" average.

It could be that a five second sample time would give you the same, or nearly the same, result as a 30 second average.

Not that sample time necessarily has anything to do with your sketch hanging. I doubt that it does.

You really ought to figure out where that problem is before using my watchdog code, as I previously recommended.

PS: It seems like nothing time-sensitive is going on in the project as currently coded, so having the dust sensor block the rest of the sketch for its 30 second sample time is probably immaterial. It could block for a minute of sampling--or 15 minutes or an hour--and it wouldn't matter, except you would get less frequent postings to the "cloud."

.

Watchdog timers are not meant to correct for program malfunction due to programming errors, for example, memory fragmentation due to use of String objects, stack overflow, array bound violations etc.

Instead, the watchdog is meant to reset the processor in case of unpredictable accidents that cause malfunction, like electrical disturbances in the power supply, an alpha particle erasing a bit in memory, etc.

I never use the watchdog, except as a wakeup function in extremely low power circuitry. I have various ATmega and PIC projects running sensors for months to years at a time without program malfunction.