Core not reacting anymore and blinking cyan

Dear all,

I have a big issue with my core.
I am pretty new to the micro controller programming.
I have started my own project to control a LED strip via WLAN and an iPhone web app.
Some more background docu on the project to be found here: http://community.spark.io/t/very-simple-web-controlled-neopixel-led-strip/9893
I want to control the core via UDP from a PHP page (running on my Synology) as this is a straight forward setup and very responsive and doesn’t need iOS programming.
I have now created a firmware wich will register itself on the Server with its name and IP and sends a regular heart beat so that the server knows about the status.

But now going forward and adding functionality to the firmware I am facing issues with the firmware.
With the code attached the core will hang up after a couple of minutes and is not responding at all. It is just flashing cyan and just a hard reset helps.

Can anybody help? Is there something fundamental wrong in the coding:

// This #include statement was automatically added by the Spark IDE.
#include "neopixel/neopixel.h"

// This #include statement was automatically added by the Spark IDE.
#include "my_helpers.h"

//#################################################################
//      BUGS to fix
//#################################################################

// 1. debug parameter isn't passed to the helpers class
// 2. Core starts blinkingt cyan after a while and isn't reacting anymore

//#################################################################
//      GLOBAL DEFINITIONS
//#################################################################

// Define the LED strip parameters
#define PIXEL_COUNT 70      // Number of Pixels
#define PIXEL_PIN D0        // Pin of the data connection to the strip
#define PIXEL_TYPE TM1829   // Strip controller type

// Define the UDP parameters
const int UDP_TX_PACKET_MAX_SIZE=860;   //UDP package size
const unsigned int localPort = 8888;    //UDP Port used for two way communication

// Define the TCP call back  parameters
char home_server[] = "diskstation";     // diskstation
const int  home_server_port = 80;       // diskstation
const String name_device = "test";      // name of the spark core in the home automation system
const int beat_rate = 10000;           // heart beat rate sent to server in ms

// Switch debugging on/off
const int debug = 1;

//#################################################################
//      Needed Objects
//#################################################################

// An UDP instance to let us send and receive packets over UDP
UDP Udp;
// An TCP client instance to let us send and receive packets over TCP
TCPClient TCP_client;

// create some helping fuctions
my_helpers h = my_helpers(debug);

// construct the strip object
Adafruit_NeoPixel strip = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
 
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,

//#################################################################
//      PROGRAM LOGIC
//#################################################################


void setup() {


  // start the UDP
  Udp.begin(localPort);



// Print your device IP Address via serial for debuging
if (debug == 1){Serial.begin(9600);}
if (debug == 1){delay (4000);}
if (debug == 1){Serial.println(WiFi.localIP());}

    
//Setup the strip & initialize all pixels to 'off'
strip.begin();
strip.show(); 

}

void loop() {
    
int packetSize = Udp.parsePacket();

if(packetSize)
    {
        
    if (debug == 1){Serial.print("Received packet of size ");}
    if (debug == 1){Serial.println(packetSize);}
    if (debug == 1){Serial.print("From ");}
    
    IPAddress remote = Udp.remoteIP();
    int port = Udp.remotePort();
    
    if (debug == 1)
    {
        for (int i =0; i < 4; i++)
        {
            Serial.print(remote[i], DEC);
            if (i < 3)
                {
                Serial.print(".");
                }
        }   
    }
    
        if (debug == 1){Serial.print(", port ");}
        if (debug == 1){Serial.println(port);}

    // read the packet into packetBufffer
    Udp.read(packetBuffer,UDP_TX_PACKET_MAX_SIZE);
        if (debug == 1){Serial.println("Contents:");}
        if (debug == 1){Serial.println(packetBuffer);}

    String datagram = String(packetBuffer);

    String command = h.dispatcher (datagram);
        if (debug == 1){Serial.print("Command: ");}
        if (debug == 1){Serial.println(command);}

    String data = h.data_load (datagram);
        if (debug == 1){Serial.print("Data: ");}
        if (debug == 1){Serial.println(data);}

    if (command == "ColorWipe")
    {
    int RGBValues[3];
    h.RGBFromString(RGBValues, data);
    colorWipe(strip.Color(RGBValues[0], RGBValues[1], RGBValues[2]), 300);
    }
    else if (command == "ColorAll")
    {
    int RGBValues[3];
    h.RGBFromString(RGBValues, data);  
    colorAll(strip.Color(RGBValues[0], RGBValues[1], RGBValues[2]), 0);
    }
    
    else if (command == "Pixel") // not yet fully implemented
    {
    int RGBValues[3];
    h.RGBFromString(RGBValues, data);
    strip.setPixelColor(10, strip.Color(RGBValues[0], RGBValues[1], RGBValues[2]));
    strip.show();
    }

    //echo back the UDP message
    Udp.beginPacket(remote, port);
    Udp.write(packetBuffer);
    Udp.endPacket();
    }

h.heart_beat(TCP_client, home_server, home_server_port, name_device, beat_rate);


} //end loop


//#################################################################
//      LED Strip Functions (to be implemented in the helpers later)
//#################################################################


// Set all pixels in the strip to a solid color, then wait (ms)
void colorAll(uint32_t c, uint8_t wait) {
  uint16_t i;
  
  for(i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
  }
  strip.show();
  delay(wait);
}

// Fill the dots one after the other with a color, wait (ms) after each one
void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout, then wait (ms)
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) { // 1 cycle of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

The Includes


/*-------------------------------------------------------------------------

  -------------------------------------------------------------------------*/

#include "my_helpers.h"
#include "neopixel/neopixel.h"

//constructor
my_helpers::my_helpers(int debug_flag) {

int debug = 1;
int request_sent = 0;
unsigned long time_stamp = 0;


}


//sends a heart beat to the server together with the IP address & device name
void my_helpers::heart_beat(TCPClient client, char host[], int port, String my_name, int heart_rate) 
{
int debug = 1;

if (time_stamp < millis() && request_sent == 0)
    {
        if (debug == 1){Serial.println("trying to connect to:");}
        if (debug == 1){Serial.print(host);}
        if (debug == 1){Serial.print(":");}
        if (debug == 1){Serial.print(port);}
        if (client.connect(host, port))
        {
            if (debug == 1){Serial.println("  connected");}
            // send the heart beat
            client.print("GET /led_strip/controller.php?function=heartbeat&device=");
            client.print(my_name);
            client.print("&IP=");
            client.print(WiFi.localIP());
            client.println(" HTTP/1.0");
            client.println("Host: diskstation");
            client.println("Content-Length: 0");
            client.println();
            
            request_sent = 1;
            
            if (debug == 1){Serial.println("request sent");}
            
            time_stamp = millis() + heart_rate;
            
            if (debug == 1){Serial.println("timestamp:");}
            if (debug == 1){Serial.println(time_stamp);}
        }
        else
        {
            if (debug == 1){Serial.println("connection failed");}
            time_stamp = millis() + 2000;
            if (debug == 1){Serial.println("timestamp:");}
            if (debug == 1){Serial.println(time_stamp);}
        }
    }

while (request_sent == 1)
    {
        if (client.available())
        {
            char c = client.read();
            if (debug == 1){Serial.print(c);}
        }

        if (!client.connected())
        {
            if (debug == 1){Serial.println();}
            if (debug == 1){Serial.println("disconnecting.");}
            request_sent = 0;
            client.stop();
        }
    }


}

//parse the two digit function number from the UDP datagram and return the function name
String my_helpers::dispatcher (String data)
{

    switch (data.substring(0,2).toInt())
    {
        case 1:
        return "ColorWipe";
        break;
        
        case 2:
        return "ColorAll";
        break;
        
        case 3:
        return "Rainbow";
        break;
        
        case 4:
        return "RainbowCycle";
        break;
        
        case 5:
        return "FullFade";
        break;
        
        case 9:
        return "Pixel";
        break;
        
        default:
        return "error";
        break;        
    }

}

//parse the data from the UDP datagram and retrun it
String my_helpers::data_load (String data)
{

    return data.substring(2);

}

//parse the two digit function number from the UDP datagram and return the function name
String my_helpers::pixelnumber (String data)
{

    return data.substring(0,3);

}


// Splitt the string rrrgggbbb into seperate varaibles
void my_helpers::RGBFromString(int RGBArray[], String RGBString) {
    
  RGBArray[0] = RGBString.substring(0, 3).toInt();
  RGBArray[1] = RGBString.substring(3, 3).toInt();
  RGBArray[2] = RGBString.substring(6, 3).toInt();
}




#ifndef MY_HELPERS_H
#define MY_HELPERS_H

#include "application.h"
#include "neopixel/neopixel.h"


class my_helpers
{
 
public:
    // Constructor: no args needed
    my_helpers(int debug_flag);

    String dispatcher (String data);                                                            //parse the two digit function number from the UDP datagram and return the function name
    String data_load (String data);                                                             //parse the data from the UDP datagram and retrun it
    String pixelnumber (String data);
    void heart_beat (TCPClient client,  char host[], int port, String my_name, int heart_rate); //sends a heart beat to the server together with the IP address & device name
    void RGBFromString(int RGBArray[], String RGBString);                                       //Extracts seperate RGB values from this String "RRRGGGBBB"

private:

    int debug;
    int request_sent;
    unsigned long time_stamp;

};
#endif // HELPERS_H

Since I haven’t seen any references to the Spark cloud in my quick browse through your code, could you try using SYSTEM_MODE(SEMI_AUTOMATIC) or SYSTEM_MODE(MANUAL)?

This way you don’t need to bother with calling Spark.process() or leave your loop() every few seconds.

For your other bug (debug parameter isn't passed to the helpers class) you need to change this

my_helpers::my_helpers(int debug_flag) {
  //int debug = 1;   // if you do this you only store it locally and
                     // and will lose your setting once you leave the function
  debug = debug_flag;

  int request_sent = 0;
  unsigned long time_stamp = 0;
}

But to prevent confusion, I’d recommend that you use a different name for your private debug variable to avoid getting mixed up between the global, the local and the private class versions of the variables of all the same name

e.g.
const int debug = 1; // global
vs.
int dbg; //local debug var inside function
vs.
private int _debug; // this is the private class field

1 Like

Hi @ScruffR,

thanks a lot for the fast support. Switching to manual mode solved my problem.
But just for completnes if anybody else has the same issue.
I had to add the following coding in the setup() routine:

WiFi.on();
WiFi.setCredentials("SSID", "PASSWORD");
WiFi.connect();
while(!WiFi.ready()); 

Otherwise the core isn’t connecting at all to the network.

On my other point regarding the topic that the value isn’t passed to the function I had of course the coding before like you have suggested, but it didn’t worked.

Just for my understanding if I got it right:

When I create a object in my code the constructor will preset the variables:

//constructor
my_helpers::my_helpers(int debug_flag) {
    int debug = debug_flag;
    int request_sent = 0;
    unsigned long time_stamp = 0;
}

In my case “debug” wil hold the value I am passing when I create the object, right?

my_helpers h = my_helpers(1);

in this case it would be “1”.

But when I am calling a method of this object it seems like this variable is initial.

void my_helpers::heart_beat(TCPClient client, char host[], int port, String my_name, int heart_rate) 
{

if (time_stamp < millis() && request_sent == 0)
    {
        if (debug == 1){Serial.println("trying to connect to:");}
        if (debug == 1){Serial.print(host);}
        if (debug == 1){Serial.print(":");}
        if (debug == 1){Serial.print(port);}
        if (client.connect(host, port))

...

}

I was always thinking that variables I have declared in the constructuror are accesiable from every method? I am wrong here?

@Sparky1, I guess not all of this is compulsory.

For one, it should not be necessary, to call Wifi.setCredentials() each time, since the Core does remember up to seven SSIDs.
One thing that might be required is to ping the gateway (or other dummy network traffic).
I think @bko has dealt with some of this quirk before - if you search for "UDP issue", there should be some threads.


For the other issue, if you write the constructor this way, your passed in debug_flag will never leave the constructor, but will die with it, for the aforementioned reason.
Local variables are only local, constructors are no different in this to other functions.
If you want to declare a class field (variable), you have to declare it inside the class and not inside (local) a function.

1 Like

This is needed for UDP broadcast in manual mode--some other connection besides UDP is required before you attempt to do UDP broadcast and pinging (ICMP echo) the gateway is the easiest way. When you have the cloud on at startup, it takes care of this. No one knows why the TI CC3000 seems to need this.

2 Likes

Guys,

thanks for all your help. Now it works like a charm. I have added the following which made also UDP more reliable:

Udp.stop();
delay(1);
Udp.begin(localPort);

The variable thing was my fault. I oversaw that I have defined the debug variable in the constructor again.

1 Like