OTA Updates Failing on Core

When attempting an OTA update, it’s status shows up as failed on the particle dashboard, and the software on the core remains unchanged. I have tried factory resetting, this allows an OTA update, once, but none subsequent. I have also tried changing power sources, this does nothing. After a failed update, the core will sometimes breathe green, or begin to flash red until reset. Any help is appreciated.

I’d assume that the code you are flashing the one time it works after a factory reset is the reason why the Core becomes unresponsive to subsequent OTA updates.
If you show your code, we might be able to spot the reason.

I’ve tried flashing blink, and this doesn’t even solve it

Just try flashing this alternative blink sketch, since the two delay(1000) in the version in the Web IDE might cause you some grieve too.

void setup()
{
  pinMode(D7, OUTPUT);
}

void loop()
{
  digitalWrite(D7, (millis() >> 3) & 0x88);
}

After this you should try reflashing the same sketch just with 0x80 instead of 0x88 to see the difference in blink pattern.

Yep, that worked.
Here’s my code:
Note: I’m using particle dev, so all the libraries are present in the folder

#include "SparkIntervalTimer.h"
#include "RGBmatrixPanel.h"
#include "Adafruit_mfGFX.h"   // Core graphics library
#include "fonts.h"
#include "math.h"
#include "colors.h"

/** Define RGB matrix panel GPIO pins **/
#if defined (STM32F10X_MD)	//Core
	#define CLK D6
	#define OE  D7
	#define LAT A4
	#define A   A0
	#define B   A1
	#define C   A2
	#define D	A3		// Only used for 32x32 panels
#endif

#if defined (STM32F2XX)	//Photon
	#define CLK D6
	#define OE  D7
	#define LAT A4
	#define A   A0
	#define B   A1
	#define C   A2
	#define D	A3		// Only used for 32x32 panels
#endif
/****************************************/
#define SIZE 20

RGBmatrixPanel matrix(A, B, C, D, CLK, LAT, OE, false);

unsigned long t = 0;
unsigned long t2 = 0;
int r, g, b = 0;
int pos = 0;
bool isNull = false;
int scores[SIZE] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
String teams[SIZE] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
void setup() {
    matrix.begin();
    Spark.subscribe("hook-response/getScores", gotScoreData, MY_DEVICES);
}

void loop() {
    if (millis() >= t+30000 || t == 0) {
        Spark.publish("getScores");
        t = millis();
    }
    if (millis() >= t2+5000 && scores[0] != -1 && scores[1] != -1 && millis()) {
        circleWipe();
        matrix.fillScreen(matrix.Color333(0, 0, 0));
        matrix.setTextWrap(false);
        matrix.setCursor(0, 0);
        matrix.setTextSize(1);
        colorConvert(getColor(teams[pos*2]));
        matrix.setTextColor(matrix.Color333(r, g, b));
        matrix.print(teams[pos*2]);
        matrix.setCursor(1, 11);
        matrix.setTextColor(matrix.Color333(7, 7, 7));
        matrix.print(String(" "+String(scores[pos*2])+"-"+String(scores[pos*2+1])));
        colorConvert(getColor(teams[pos*2+1]));
        matrix.setTextColor(matrix.Color333(r, g, b));
        matrix.setCursor(32-(teams[pos*2+1].length()*6), 23);
        matrix.print(teams[pos*2+1]);
        pos++;
        pos %= SIZE/2;
        if (scores[pos*2] == -1 && scores[pos*2+1] == -1) {
            pos = 0;
        }
        t2 = millis();
    }
    if (scores[0] == -1 && scores[1] == -1 && millis() >= t2+5000) {
			  matrix.fillScreen(matrix.Color333(0, 0, 0));
        matrix.setTextColor(matrix.Color333(7, 0, 0));
        matrix.setCursor(11, 7);
        matrix.println("NO");
        matrix.print("GAMES");
				t2 = millis();
    }
}

void gotScoreData(const char *name, const char *data) {
    String str = String(data);
    for (int x = 0; x < SIZE; x++) {
        scores[x] = -1;
        teams[x] = NULL;
    }
    int start = 0;
    int idx = 0;
    for (unsigned int x = 1; x < str.length() && idx < SIZE*2; x++) {
        if (str[x] == '~' && idx % 2 == 0) {
            teams[idx/2] = ""+str.substring(start, x);
            start = x+1;
            idx++;
        }
        else if (str[x] == '~' && idx % 2 == 1) {
            scores[idx/2] = str.substring(start, x).toInt();
            start = x+1;
            idx++;
        }
    }
}

void colorConvert(String c) {
    int color[3] = {0, 0, 0};
    String str = c+" ";
    int start = 0;
    int idx = 0;
    for (unsigned int x = 1; x < str.length(); x++) {
        if (str[x] == ' ') {
            color[idx] = String(""+str.substring(start, x)).toInt();
            start = x+1;
            idx++;
        }
    }
    r = color[0];
    g = color[1];
    b = color[2];
}

void circleWipe() {
    for (int x = 0; x < 23; x++) {
        matrix.fillCircle(16, 16, x, matrix.Color333(0, 0, 0));
        delay(30);
    }
}

String getColor(String team) {
    if (team == "ANA") {
        return String(GREEN);
    }
    if (team == "BOS") {
        return String(YELLOW);
    }
    if (team == "BUF") {
        return String(YELLOW);
    }
    if (team == "CGY") {
        return String(RED);
    }
    if (team == "CAR") {
        return String(RED);
    }
    if (team == "CHI") {
        return String(RED);
    }
    if (team == "COL") {
        return String(RED);
    }
    if (team == "CLB") {
        return String(BLUE);
    }
    if (team == "DAL") {
        return String(GREEN);
    }
    if (team == "DET") {
        return String(RED);
    }
    if (team == "EDM") {
        return String(ORANGE);
    }
    if (team == "FLA") {
        return String(YELLOW);
    }
    if (team == "LA") {
        return String(PURPLE);
    }
    if (team == "MIN") {
        return String(GREEN);
    }
    if (team == "MTL") {
        return String(RED);
    }
    if (team == "NSH") {
        return String(WHITE);
    }
    if (team == "NJ") {
        return String(RED);
    }
    if (team == "NYI") {
        return String(ORANGE);
    }
    if (team == "NYR") {
        return String(BLUE);
    }
    if (team == "OTT") {
        return String(RED);
    }
    if (team == "PHI") {
        return String(ORANGE);
    }
    if (team == "ARI") {
        return String(RED);
    }
    if (team == "PIT") {
        return String(YELLOW);
    }
    if (team == "SJ") {
        return String(GREENBLUE);
    }
    if (team == "STL") {
        return String(BLUE);
    }
    if (team == "TB") {
        return String(BLUE);
    }
    if (team == "TOR") {
        return String(BLUE);
    }
    if (team == "VAN") {
        return String(BLUE);
    }
    if (team == "WSH") {
        return String(RED);
    }
    if (team == "WPG") {
        return String(BLUE);
    }
    else {
        return String("0 0 0");
    }
}

I can't really see any obvious reason for what you experience, but I know that the RGBmatrixPanel lib can be a time hog on the controller, since it does the LED dimming via highly frequent interrupts (maybe @peekay123 can chime in on the RGBmatrixPanel front ;-)).
Additionally the interaction between your subscribe() and your publish() might also contribute to the issue, so see my tip about string parsing further down.
Also add some Particle.process() calls in any of the potentially longer running loops (e.g. inside gotScoreData() and circleWipe()) just to be on the safe side.

You could also add some Serial.println() debug statements to see if your millis() checks don't accidentally fire more often than expected and also how often your other functions get called (maybe with a Serial.printf("Enter: %d", millis()); at the top of the function and Serial.printf("Leave: %d", millis()); at the end).

The following hints might not immediately have to do with your problem, but just for the sake of saying it :wink:

Just curious, what is this final && millis() for?

For parsing your color string, you might like to look into strtok(), which makes it a bit tidier and avoids the use of String objects, which will eventually lead to heap exhaustion, if your code is running for a long time.
The same goes for getScoreData().

I'd also replace your String compares with something like if (strcmp(team.c_str(), "ANA") == 0) to avoid implicit String creation with the same side effects on the heap as mentioned above.

1 Like

Thanks for the tips. If the problem still persists, I’ll just have to flash over usb, which isn’t too big a deal. Also, that final millis() was just a typo, I don’t think that was in the code when I flashed it.

@ScruffR, @randomguy1124, though the RGBMatrixPanel library uses interrupts heavily, I have had no problems doing OTA with the code running. You can add SYSTEM_THREAD(ENABLED) before setup() to allow the user app to run in its own thread.

@ScruffR has given great guidance regarding the use of String. One other thing you may want to consider is storing the team colors as 16bit RGB values instead of Strings. I should have included these values in the RGBMatrixPanel.h file (which I will do this week!):

/********** RGB565 Color definitions **********/
#define Black           0x0000
#define Navy            0x000F
#define DarkGreen       0x03E0
#define DarkCyan        0x03EF
#define Maroon          0x7800
#define Purple          0x780F
#define Olive           0x7BE0
#define LightGrey       0xC618
#define DarkGrey        0x7BEF
#define Blue            0x001F
#define Green           0x07E0
#define Cyan            0x07FF
#define Red             0xF800
#define Magenta         0xF81F
#define Yellow          0xFFE0
#define White           0xFFFF
#define Orange          0xFD20
#define GreenYellow     0xAFE5
#define Pink		0xF81F
/**********************************************/

You can then use an array of unsigned int for team colors, and calculate the RGB values with:

  // Adafruit_GFX uses 16-bit color in 5/6/5 format, while matrix needs
  // 4/4/4.  Pluck out relevant bits while separating into R,G,B (each is a
  // uint8_t var):
  r =  c >> 12;        // RRRRrggggggbbbbb
  g = (c >>  7) & 0xF; // rrrrrGGGGggbbbbb
  b = (c >>  1) & 0xF; // rrrrrggggggBBBBb

I’ll be adding a function to the RGBMatrixPanel library for setting the color directly from the 16bit RGB565 value.

If you want to be adventurous, you could create a struct for you team definitions containing the team name and team color for lookup. :smile:

1 Like

This was more a stab in the dark, since I couldn't see any obvious reasons for the behaviour.
While String "stinks" generally :wink: this would not account for the unresponsiveness for OTA, but the "heavy" use of GFX commands combined with µs interrups was an unlikely but possibe candidate - providing the the millis() checks do fail to work as expected too.

But SYSTEM_THREAD(ENABLED) is a good point.

@ScruffR, your advice on calling Particle.process() is good. On the RGBPongClock I can do an OTA without any problems. :wink:

1 Like

@randomguy1124, can you also post the webhook you are using so we could test your code?

Thanks for the advice about the colors, I will definitely try to implement that, and try SYSTEM_THREAD.
Here’s my webhook:

{
    "eventName": "getScores",
    "url": "http://www.sportsnet.ca/wp-content/themes/sportsnet/zones/ajax-scoreboard.php",
    "requestType": "GET",
    "responseTemplate": "{{#data.nhl.In-Progress}}{{home_team_short}}~{{home_score}}~{{visiting_team_short}}~{{visiting_score}}~{{/data.nhl.In-Progress}}{{#data.nhl.Final}}{{home_team_short}}~{{home_score}}~{{visiting_team_short}}~{{visiting_score}}~{{/data.nhl.Final}}",
    "mydevices": true
}

If you want to run my code you may also want this:
colors.h:

#define BLUE "0 0 7"
#define GREEN "0 7 0"
#define RED "7 0 0"
#define YELLOW "7 5 0"
#define PURPLE "4 0 7"
#define CYAN "0 7 7"
#define GREENBLUE "0 7 3"
#define ORANGE "7 2 0"
#define WHITE "7 7 7"

@randomguy1124, I believe you may be running on a Core in which case SYSTEM_THREAD(ENABLED) will not work (Photon only). On the Core, follow @ScruffR’s guidance on using Particle.process() instead. :wink:

1 Like

Yes, ok. I had included it, and was wondering why it didn’t do anything. I’ll use lots of process() instead.

@randomguy1124, you don’t need lots. Just put them where you have a long loop or delay. :wink:

Yep, meant to say that

2 Likes