Need better understanding of errors, and elegant/efficient ways to send sensor data

I have a spark core which is connects to Ubidots.com, a web service that records sensor states. It's working! I'm getting 1 and 0 for the closed/open door. But, I think I need to better understanding of some of the errors I have seen while setting this up and what they mean. In addition, the app could fail to record anything if the door is opened and closed very quickly! Here are my observations and questions.:

  1. If I try to flash new code via build.spark.io it frequently fails with a timeout error. If I try over and over it later randomly works. How do I get it to work the first time? What is causing the timeout? (the cyan light is "breathing" when start the flash attempt)

  2. Some of the time the led on spark core turns red then back to cyan. It is hard to count the number of red flashes as it turns red like this at seemingly random intervals. (though it happens most often after resetting or managing to flash new firmware. I think there are between 4-8 red flashes. It turns red less and less often the longer I run it, eventually after about an hour it never turns red again until the next re-boot. So, it seems to stabilize. Is this something to worry about?

PANIC CODES
number of red flashes:

 1  Hard fault           2 Non-mask interrupt fault
 3  Mem Manager fault    4 Bus fault
 5  Usage fault          6 Invalid length
 7  Exit                 8 Out of heap memory
 9  SPI over-run        10 Assertion failure
11  invalid case        12 Pure virtual call
  1. The app is slow to respond (as indicated by the led turning on) to the door being opened. I worry my app would miss if the door was opened and closed quickly. Can I fix this by reducing the delay? Isn't it bad to have quickly looping code or a short delay?

  2. Is there a way to record when the door is opened and closed where the sensor triggers the variable to update instead of this constant checking of the analog input over and over? (*I want an accurate list of all the approximate times the door was opened, I can do without super-fast instant updates about the current state of the door.)

  3. What about sending data to ubidots.com only when there is a change? Is it worthwhile to write code check for a change before allowing it to POST?

OK here is my code. How can I make it better?

Code, Trimmed to the Best of my Abilities.

// ---------------------------------------------------
// ---------------------------------------------------
// This app will check the open door sensor as 
// an analog input (0-4095).  High values mean 
// the contacts are separated (i.e. the door is 
// open). Low values mean the door is closed 
// or "almost closed." 
//
//  I assign: 1 for closed
//                0 for open to the variable doorStat. 
//
// Then we send the variable doorStat to 
// Ubidots.com, a web-based sensor data 
// collection service. 
// ---------------------------------------------------
// include HTTP library to connect to Ubidots.com, 
// define Ubidots variable(s) and token.
#include "HttpClient/HttpClient.h"
HttpClient http;

#define VARIABLE_ID "123myVariableIDFromUbidots"
#define TOKEN "abcd12345myTokenFromUbidots"

// Ubidots: Headers currently need to be set at 
// init, useful for API keys etc.
http_header_t headers[] = {
    { "Content-Type", "application/json" },
    { "X-Auth-Token" , TOKEN },
    { NULL, NULL } // NOTE: Always terminate headers with NULL
};

http_request_t request;
http_response_t response;

// Declare a variable used to send data to Ubidots
char resultstr[64];

// Create a variable that will store 0 for closed, 
// 1 for open. (Default value, 0)
float doorStat = 0;

// Create a variable to store analog sensor data 
// before processing it
int reading = 0;


void setup() {
// Initialize D0 pin, an led, as output   
    pinMode(D0, OUTPUT);
// Initialize A7 pin, door sensor as input 
    pinMode(A7, INPUT);
    request.hostname = "things.ubidots.com";
    request.port = 80;
}


void loop() {    
// Get variable using sensor
      reading = analogRead(A7);

// The returned value from the core is 
// going to be in the range from 0 to 4095
// Based on the reading assign 1 or 0 to 
// doorStat and turn the LED on or off.

        if (reading < 400) {
            doorStat = 1; //assign 1 for "door closed"
            digitalWrite(D0, LOW);    // Turn OFF the LED pins
        } else {
            doorStat = 0; //assign 0 for "door open"
            digitalWrite(D0, HIGH);   // Turn ON the LED pins
        }

// Send doorStat to Ubidots. 
// Should this run only when doorStat changed?
    request.path = "/api/v1.6/variables/"VARIABLE_ID"/values";
    sprintf(resultstr, "{\"value\":%.4f}",doorStat);
    request.body = resultstr;
    http.post(request, response, headers);

    delay(1000); //What are the issues with a shorter delay?
    
}

@futurebird, without seeing the entire code, it’s hard to say what the issue is. Slow response, failing OTAs and flashing red LED indicate problems with your app. Your loop() may be starving the background process by running too long and you may be running out of heap.

2 Likes

In addition to @peekay123 's suspicion that your code occupies too much of processing time and leaves too little for the background process, you might want to reconsider something.

We don’t know your way of sensing the door state, but does it actually need to be an analog reading?
If not, you might want to consider using an interrupt to record your door open/close. This way you won’t miss any event.

3 Likes

The core flashes the red light in an sos pattern, then the appropriate number of error flashes, then sos again.
…—… NumErrorFlashes …—…

The other problems all point to the for not getting enough time to process in the background, although nothing in your code looks problematic. Does that stripped down code exhibit the problem?

Also, no need for the delay unless you want it there to slow down your measurements.

I'm using a magnetic sensor. Where can I find out more about using an interrupt? All of the examples I've looked at use analog for the magnet. Can that even work?

My code is fairly stable now, the issue seems to be the datatypes.

    // Create a variable that will store 0 for 
    // closed,  for open. (Default value, 0)
float doorStat = 0;
    // Create a variable to store analog sensor
    // data before processing it
int reading = 0;

I changed float to int and that seemed to fix things. I do not understand why. ???

Having fixed that issue, I expanded the code a bit. I now need to use this routine more than once to send multiple values ubidots. I wonder if I should make it a function? It would have strings that are variable names as inputs.

<<resultstrAnalog>>, <analog> , <<analogStrength>>

The the result would be:

//send sensor data
request.path = "/api/v1.6/variables/"VARIABLE_ID_<<analog>>"/values"; 
sprintf(<<resultstrAnalog>>, "{\"value\":%i}",<<analogStrength>>); 
request.body = <<resultstrAnalog>>;
http.post(request, response, headers);
Serial.println(response.body);

But at this point I'm nervous about making changes... lol

Whether you can use interrupt driven detection with your sensor, depends on the readings you get of it.
If your open and closed readings are about 10-15% either side of the median (aprox. 1.65V) you're good for interrupts.
Otherwise you could have some circuitry (e.g. Schmitt trigger) that gives you a clean HIGH/LOW result with adjustable threshold (and if needed hysteresis).

And it's actually quite simple.
Have a look at the docs here

http://docs.spark.io/firmware/#interrupts-attachinterrupt

You'd go for the CHANGE interrupt.
If you keep your interrupt service routine small and simple, maybe just setting the state or change flag, you're as good as done :wink:

As for your float/int puzzle, I'm not sure, but if you want to get to the bottom of it, you could try to Serial.print() your resultstr with the float again, and see if that gives you any clue - e.g. exceeding 63 char.

After @rsteckler 's SOS clarification, have you been able to count the actual number of code flashes? (Don't count the 9 SOS flashes beforehand!)

I'd go for a function, and if you went for numbered variable names or a switch case construct, you'd not need to pass the strings into the function.

2 Likes