Electron led goes to constant Cyan

Hi,

have some problems. After a while my Electron goes to constant cyan. Time it takes before it goes to this stat could differ.
I have tuned on the SYSTEM_THREAD(ENABLED); , I don’t use any interrupts, have checked the System.freeMemory() and it reports 93752 bytes. Only peripheral I use is the Serial. But even if I don’t receive any data on the port it goes to this “mode” .

Any ideas what could cause this LED state ?

1 Like

You got some code to look at?

1 Like

Hi @ScruffR,

quite many lines of code though. :slight_smile:


    #pragma SPARK_NO_PREPROCESSOR

    #include "Particle.h"
    #include "MQTT.h"
    #include "device.h"
    #include "cellular_hal.h"
        
    SYSTEM_THREAD(ENABLED);

    #if ( PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION )
      STARTUP(cellular_credentials_set("bredband.tre.se", "", "", NULL));
      //STARTUP(cellular_credentials_set("data.tre.se", "", "", NULL));
    #endif

    #define MQTT_TOPIC_SENSOR       "iot-2/evt/data/fmt/json"
    #define MQTT_TOPIC_STATUS       "iot-2/evt/status/fmt/json"
    #define MQTT_TOPIC_CMD          "iot-2/cmd/+/fmt/json"

    /*
     *  System defines
     */
    #define EXT_SERIAL Serial1
    #define MAXBUFFLENGTH 400

    #define MQTT_KEEPALIVE 2 * 1000UL  //Seconds
    #define MQTT_CONN_TIME 1 * 1000UL  //Seconds

    #define CELL_CHECK_TIME 2 * 60000UL  //Minutes
    #define UNIT_STATUS_TIME 3 * 60000UL  //Minutes

    /*
     *  EEPROM address
     */
    #define DIAGF_ADD 0

    /*
     *  Debug defines
     */
    #define _DIAG
    //#define _DEBUG_MQTT
    //#define _DEBUG_SERIAL

    typedef struct ExtCom {
        char buf1[MAXBUFFLENGTH];
        char buf2[MAXBUFFLENGTH];
        char *currBuf;
        char *lastBuf;
        byte bufIdx;
        bool recvdflag;
    } ExtCom;

    enum DiagInfo {
        RSTWD,
        RSTRDY,
        RSTCONN,
        RSTMQTT
    } DiagInfo;

    #define DIAGRST 16

    bool runSerialCmd = false;
    long tMqttConn = 0, tMqttLoop = 0, tStatus = 0, tCellCk = 0;
    ExtCom extCom;
    int cnt = 0;
    int diagFlags = 0;

    //***** Function declaration *****
    void mqttCallback(char* topic, byte* payload, unsigned int length);
    void rstTimer();
    void rstWd();
    void rst( char rstType );
    int diagFlagRst( String command );

    //***** Class declaration *****
    MQTT client(MS_PROXY, MQTT_PORT, mqttCallback);
    Timer mqttReConnT( 90 * 1000UL, rstTimer, true );

    // reset the system after 5 seconds if the application is unresponsive
    ApplicationWatchdog wd( 5 * 60000UL, rstWd );


    void rst( char rstType ) {
      diagFlags |= 1UL << rstType;
      EEPROM.put( DIAGF_ADD, diagFlags );
      
      Particle.publish( "Rst unit", String(rstType) );
      Particle.publish( "Diagnos flags", String(diagFlags) );
      
      System.reset();
    }

    void rstTimer() {
        rst( RSTMQTT );
    }

    void rstWd() {
        rst( RSTWD );
    }

    int diagFlagRst( String command ) {
        diagFlags = 0;
        EEPROM.put( DIAGF_ADD, diagFlags );
        return 0;
    }

    int reboot( String command ) {
        Particle.publish( "reboot" );
        System.reset();
        return 0;
    }

    /*
     *  MQTT functions
     */
    void mqttInit() {
        // connect to the Mqtt broaker
        client.connect( MQTT_CLIENT_ID, AUTHMETHOD, AUTHTOKEN );

        // publish/subscribe
        if ( client.isConnected() ) {
            client.subscribe(MQTT_TOPIC_CMD);

            #ifdef _DEBUG_MQTT
              Serial.println("MQTT online");
            #endif

            return;
        }

        #ifdef _DEBUG_MQTT
          Serial.println("MQTT offline");
        #endif
    }

    void mqttCallback(char* topic, byte* payload, unsigned int length) {
        char temp[100];
        char p[length + 1];
        memcpy(p, payload, length);
        p[length] = NULL;
        String message(p);
        String msgTopic(topic);

        #ifdef _DEBUG_MQTT
          sprintf( temp, "MQTT Recieve: %s : %s", topic, p );
          Serial.println( temp );
        #endif
    }

    void serialEvent1() {
      char c = 0;
      int cnt = 0;
       /*
        *  Data is JSON formated like this:
        *
        *  $ in the begining, indicates new serial string,
        * '\n' newline in the end indicate then end of the string.
        *
        *  Example:
        *  sprintf(serialBuf, "&{\"seq_num\":\"%d\"}", packetnum);
        *  Serial1.println(serialBuf);
        */

        // read data from serial
        while ( EXT_SERIAL.available() ) {

          c = EXT_SERIAL.read();

          if (c == '&') {
              extCom.currBuf[extCom.bufIdx] = 0;
              extCom.bufIdx = 0;
          }
          else if (c == '\n') {
        //        extCom.currBuf[extCom.bufIdx] = c;
            if (extCom.currBuf == extCom.buf1) {
              extCom.currBuf = extCom.buf2;
              extCom.lastBuf = extCom.buf1;
            } else {
              extCom.currBuf = extCom.buf1;
              extCom.lastBuf = extCom.buf2;
            }

            extCom.recvdflag = true;
            break;
          }
          else if (c == '\r') {
            //Don't add this char to buffer
          }
          else {
              extCom.currBuf[extCom.bufIdx++] = c;
          }

          if (extCom.bufIdx >= MAXBUFFLENGTH) {
              extCom.bufIdx = MAXBUFFLENGTH-1;
          }
        }
    }

    /*
     *  System functions
     */
    void setup() {
        //Used for external unit communication
        EXT_SERIAL.begin(19200);

        //Used for debug
        Serial.begin(19200);

        extCom.currBuf = extCom.buf1;
        extCom.lastBuf = extCom.buf2;
        extCom.bufIdx = 0;
        extCom.recvdflag = false;
        
    #ifdef _DIAG    
        //*****  Register  cloud function/ variables
        Particle.variable( "DiagFlags", diagFlags );
        Particle.function( "DiagFlagsClr", diagFlagRst );
        Particle.function( "UnitReboot", reboot );
        
        //    Particle.keepAlive(10);    // send a ping every 10 sec
        
        EEPROM.get( DIAGF_ADD, diagFlags );
        
        // Erased EEPROM has value 0xFF, need to be set to 0x00 first time.
        // DIAGRST could also be used to reset the DiagFlags
        if ( diagFlags & DIAGRST ) {
            diagFlags = 0;
            EEPROM.put( DIAGF_ADD, diagFlags );
            Particle.publish( "Diagnos erase" );
        }
        
      #if ( PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION )
        if ( !waitFor( Cellular.ready, 60000 * 3 ) ) rst( RSTRDY );
      #endif
        
        Particle.publish( "Diagnos flags", String(diagFlags) );
    #endif    

        tCellCk = millis();
        tMqttLoop = millis();
        
        // Run instant
        tStatus = millis() + UNIT_STATUS_TIME;
        tMqttConn = millis() + MQTT_CONN_TIME;
    }

    void loop() {
    char payload[300];
    int reSend;
    char *pEnd, *pStart;


        wd.checkin(); // resets the AWDT count

        if (extCom.recvdflag) {
          extCom.recvdflag = false;
          runSerialCmd = true;

          #ifdef _DEBUG_SERIAL
            Serial.println( extCom.lastBuf );
          #endif
        }

        if (client.isConnected()) {
            if (millis() - tMqttLoop >= MQTT_KEEPALIVE) {
              client.loop();
              
            #ifdef _DIAG 
              if ( mqttReConnT.isActive() ) mqttReConnT.stop();
            #endif
            
              tMqttLoop = millis();
            }
        
            if (runSerialCmd) {
                runSerialCmd = false;
                reSend = 0;

                if ( strstr( extCom.lastBuf, "asc" ) ) {
                    sprintf(payload, "{\"d\":%s}", extCom.lastBuf);
                    while( !client.publish( MQTT_TOPIC_SENSOR, payload ) && reSend++ < 4 );

                    #ifdef _DEBUG_MQTT
                      Serial.println( payload );
                    #endif
                }
                if ( strstr( extCom.lastBuf, "time" ) ) {
                    sprintf( payload, "&{\"time\":%u}", Time.now() );
                    EXT_SERIAL.println( payload );
                    Serial.println( payload );
                }

            }

        #if ( PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION )
            if ( millis() - tStatus >= UNIT_STATUS_TIME ) {
              CellularSignal sig = Cellular.RSSI();
              sprintf(payload, "{\"d\":{\"rssi\":\"%d\",\"qual\":\"%d\"}}", sig.rssi, sig.qual);
              client.publish( MQTT_TOPIC_STATUS, payload );
              Particle.publish( "Cell RSSI", payload );
              
              Particle.publish( "Free memory", String(System.freeMemory()) );
              
              tStatus = millis();
            }
        #endif

        #if ( PLATFORM_ID == PLATFORM_PHOTON_PRODUCTION )
            if ( millis() - tStatus >= UNIT_STATUS_TIME ) {
              sprintf(payload, "{\"d\":{\"rssi\":\"%d\"}}", WiFi.RSSI() );
              client.publish( MQTT_TOPIC_STATUS, payload );
              Particle.publish( "WiFi RSSI", payload );
              
              Particle.publish( "Free memory", String(System.freeMemory()) );
              
              tStatus = millis();
            }
        #endif

        }
        else if ( millis() - tMqttConn >= MQTT_CONN_TIME ) {
            mqttInit();
            
          #ifdef _DIAG 
            if ( !mqttReConnT.isActive() ) mqttReConnT.start();
          #endif
          
            tMqttConn = millis();
        }

    #ifdef _DIAG 
      #if ( PLATFORM_ID == PLATFORM_ELECTRON_PRODUCTION )
        if ( millis() - tCellCk >= CELL_CHECK_TIME ) {
          if ( !waitFor( Cellular.ready, 60000 ) ) rst( RSTRDY );
          if ( waitFor( Cellular.connecting, 60000 ) ) rst( RSTCONN );
            
          tCellCk = millis();
        }
      #endif
    #endif

    }

That's actually 5 minutes

For non-Electron devices you'll need a 5sec delay before the reset to have the publishes finish.

I would do away with these temporary and unused variables in mqttCallback()

        String message(p);
        String msgTopic(topic);

snprintf() will be happily using const char* and char[] but yell at you about non-trivial types for %s

You have EXT_SERIAL for abstraction, but then use serialEvent1() which is closely tied to Serial1 - this seems inconsistent :wink:

I'd pull your diagnostic publishes in setup() together into only one to obey the rate limit of 1/s (under some conditions you may even exceede the 4 burst allowance)

        // Run instant
        tStatus = millis() + UNIT_STATUS_TIME;
        tMqttConn = millis() + MQTT_CONN_TIME;

these variables need to be unsigned long but in order to have your blocks run instantly, you'd have to subtract the constants - not add.

2 Likes

Thanks for the code review! So Photon and Core needs 5sec to publish a msg to Particle but not Electron. I should have guessed the opposite :slight_smile: I will make the changes. Any idé what could cause the hole Electron including Particles thread to frees ?

The reason is that a special feature to finish pending requests was implemented for the Electron only (for some obscure reason :sunglasses:)
If your code in deed is freezing (RGB LED stops breathing) then it's most likely a deadlock between your code and the system demanding access to a shared hardware resource (e.g. radio, serial, ...)

1 Like