Photon is online, running alright, but I cannot flash it

Hi everyone,

My Photon with a Sparkfun Weather shield is running in my backyard, it publishes the weather information every 10 second. I can see that in the Console.

The problem is I cannot flash it. I tried both Particle Dev and Build, they all told me flash “Successful”, but the Console displayed “failed” then “update_timeout”, like this:

then the old program came back and started running like nothing happened.

I also tried to use the Android App to re-flash Tinker, but it didn’t work either.

I’m wondering, if I did something wrong in the code, what could that be? Would “bad” code cause this problem? Why did both Dev and Build say the flash was “Successful” but actually it was not?

Thanks very much!

Karl

@KarlWang, yes, “bad” code and block updates. You would have to post your code so we could maybe offer advice.

1 Like

Thanks! My code is from https://www.hackster.io/hliang/thingspeak-weather-station-data-analysis-2877b0, simply modified.

    // This #include statement was automatically added by the Particle IDE.
    #include "SparkFun_Photon_Weather_Shield_Library.h"
    
    
    // Add math to get sine and cosine for wind vane
    #include <math.h>
    
    /*
      *****************************************************************************************
      **** Visit https://www.thingspeak.com to sign up for a free account and create
      **** a channel.  The video tutorial http://community.thingspeak.com/tutorials/thingspeak-channels/
      **** has more information. You need to change this to your channel, and your write API key
      **** IF YOU SHARE YOUR CODE WITH OTHERS, MAKE SURE YOU REMOVE YOUR WRITE API KEY!!
      **** To learn more about ThingSpeak, see the introductory video: http://www.mathworks.com/videos/introduction-to-thingspeak-107749.html
      *****************************************************************************************/
    //unsigned long thingspeakChannelNumber = 90538;
    //char thingSpeakWriteAPIKey[] = "Your key here";
    
    // Each time we loop through the main loop, we check to see if it's time to capture the sensor readings
    unsigned int sensorCapturePeriod = 100;
    unsigned int timeNextSensorReading;
    
    // Each time we loop through the main loop, we check to see if it's time to publish the data we've collected
    unsigned int publishPeriod = 10000;
    unsigned int timeNextPublish;
    
    String api_key = "Your API Key"; // Replace this string with a valid ThingSpeak Write API Key.
    String field1 = "";
    String field2 = "";  // i.e. field2 is null
    String field3 = "";
    String field4 = "";
    String field5 = "";
    String field6 = "";
    String field7 = "";
    String field8 = "";
    String lat = "";
    String lon = "";
    String el = "";
    String status = "";
    
    void setup() {
        //initializeThingSpeak();
        initializeTempHumidityAndPressure();
        initializeRainGauge();
        initializeAnemometer();
        initializeWindVane();
    
        // Schedule the next sensor reading and publish events
        timeNextSensorReading = millis() + sensorCapturePeriod;
        timeNextPublish = millis() + publishPeriod;
    }
    
    void loop() {
    
        // Capture any sensors that need to be polled (temp, humidity, pressure, wind vane)
        // The rain and wind speed sensors use interrupts, and so data is collected "in the background"
        if(timeNextSensorReading <= millis()) {
            captureTempHumidityPressure();
            captureWindVane();
    
            // Schedule the next sensor reading
            timeNextSensorReading = millis() + sensorCapturePeriod;
        }
    
        // Publish the data collected to Particle and to ThingSpeak
        if(timeNextPublish <= millis()) {
    
            // Get the data to be published
            float tempF = getAndResetTempF();
            float tempC = getAndResetTempC();
            float humidityRH = getAndResetHumidityRH();
            float pressureKPa = getAndResetPressurePascals() / 1000.0;
            float rainInches = getAndResetRainInches();
            float gustMPH;
            float windMPH = getAndResetAnemometerMPH(&gustMPH);
            float windDegrees = getAndResetWindVaneDegrees();
    
            FuelGauge fuel;
            float voltage = fuel.getVCell();
    
            // Publish the data
            publishToParticle(tempC,humidityRH,pressureKPa,rainInches,windMPH,gustMPH,windDegrees);
            //publishToThingSpeak(tempF,humidityRH,pressureKPa,rainInches,windMPH,gustMPH,windDegrees,voltage);
    
            // Schedule the next publish event
            timeNextPublish = millis() + publishPeriod;
        }
    
        delay(10);
    }
    
    void publishToParticle(float tempC,float humidityRH,float pressureKPa,float rainInches,float windMPH,float gustMPH,float windDegrees) {
        Particle.publish("weather",
                            String::format("%0.1f°C, %0.0f%%, %0.2f kPa, %0.2f in, Avg:%0.0fmph, Gust:%0.0fmph, Dir:%0.0f°.",
                                tempC,humidityRH,pressureKPa,rainInches,windMPH,gustMPH,windDegrees),
                            60 , PRIVATE);
    }
    
    
    void publishToThingSpeak(float tempF,float humidityRH,float pressureKPa,float rainInches,float windMPH,float gustMPH,float windDegrees,float voltage) {
        // To write multiple fields, you set the various fields you want to send
    
        field1 = String(tempF,1);
        field2 = String(humidityRH,0);
        field3 = String(pressureKPa,1);
        field4 = String(rainInches,1);
        field5 = String(windMPH,1);
        field6 = String(gustMPH,1);
        field7 = String(windDegrees, 0);
        field8 = String(voltage,1);
    
        String TSjson;
        createTSjson(TSjson);
        Particle.publish("TSwriteall",TSjson,60,PRIVATE);
    
    }
    
    //===========================================================
    // Temp, Humidity and Pressure
    //===========================================================
    // The temperature, humidity, and pressure sensors are on board
    // the weather station board, and use I2C to communicate.  The sensors are read
    // frequently by the main loop, and the results are averaged over the publish cycle
    
    //Create Instance of HTU21D or SI7021 temp and humidity sensor and MPL3115A2 barometric sensor
    Weather sensor;
    
    void initializeTempHumidityAndPressure() {
        //Initialize the I2C sensors and ping them
        sensor.begin();
        //Set to Barometer Mode
        sensor.setModeBarometer();
        // Set Oversample rate
        sensor.setOversampleRate(7);
        //Necessary register calls to enble temp, baro and alt
        sensor.enableEventFlags();
    
        return;
    }
    
    float humidityRHTotal = 0.0;
    unsigned int humidityRHReadingCount = 0;
    float tempFTotal = 0.0;
    float tempCTotal = 0.0;
    unsigned int tempFReadingCount = 0;
    unsigned int tempCReadingCount = 0;
    float pressurePascalsTotal = 0.0;
    unsigned int pressurePascalsReadingCount = 0;
    
    void captureTempHumidityPressure() {
      // Read the humidity and pressure sensors, and update the running average
      // The running (mean) average is maintained by keeping a running sum of the observations,
      // and a count of the number of observations
    
      // Measure Relative Humidity from the HTU21D or Si7021
      float humidityRH = sensor.getRH();
    
      //If the result is reasonable, add it to the running mean
      if(humidityRH > 0 && humidityRH < 105) // It's theoretically possible to get supersaturation humidity levels over 100%
      {
          // Add the observation to the running sum, and increment the number of observations
          humidityRHTotal += humidityRH;
          humidityRHReadingCount++;
      }
    
      // Measure Temperature from the HTU21D or Si7021
      // Temperature is measured every time RH is requested.
      // It is faster, therefore, to read it from previous RH
      // measurement with getTemp() instead with readTemp()
      float tempF = sensor.getTempF();
      float tempC = sensor.getTemp();
      //If the result is reasonable, add it to the running mean
      if(tempF > -50 && tempF < 150)
      {
          // Add the observation to the running sum, and increment the number of observations
          tempFTotal += tempF;
          tempFReadingCount++;
      }
    
      if(tempC > -30 && tempC < 100)
      {
          // Add the observation to the running sum, and increment the number of observations
          tempCTotal += tempC;
          tempCReadingCount++;
      }
    
      //Measure Pressure from the MPL3115A2
      float pressurePascals = sensor.readPressure();
    
      //If the result is reasonable, add it to the running mean
      // What's reasonable? http://findanswers.noaa.gov/noaa.answers/consumer/kbdetail.asp?kbid=544
      if(pressurePascals > 80000 && pressurePascals < 110000)
      {
          // Add the observation to the running sum, and increment the number of observations
          pressurePascalsTotal += pressurePascals;
          pressurePascalsReadingCount++;
      }
    
      return;
    }
    
    float getAndResetTempC()
    {
        if(tempCReadingCount == 0) {
            return 0;
        }
        float result = tempCTotal/float(tempCReadingCount);
        tempCTotal = 0.0;
        tempCReadingCount = 0;
        return result;
    }
    
    float getAndResetTempF()
    {
        if(tempFReadingCount == 0) {
            return 0;
        }
        float result = tempFTotal/float(tempFReadingCount);
        tempFTotal = 0.0;
        tempFReadingCount = 0;
        return result;
    }
    
    float getAndResetHumidityRH()
    {
        if(humidityRHReadingCount == 0) {
            return 0;
        }
        float result = humidityRHTotal/float(humidityRHReadingCount);
        humidityRHTotal = 0.0;
        humidityRHReadingCount = 0;
        return result;
    }
    
    
    float getAndResetPressurePascals()
    {
        if(pressurePascalsReadingCount == 0) {
            return 0;
        }
        float result = pressurePascalsTotal/float(pressurePascalsReadingCount);
        pressurePascalsTotal = 0.0;
        pressurePascalsReadingCount = 0;
        return result;
    }
    
    //===========================================================================
    // Rain Guage
    //===========================================================================
    int RainPin = D2;
    volatile unsigned int rainEventCount;
    unsigned int lastRainEvent;
    float RainScaleInches = 0.011; // Each pulse is .011 inches of rain
    
    void initializeRainGauge() {
      pinMode(RainPin, INPUT_PULLUP);
      rainEventCount = 0;
      lastRainEvent = 0;
      attachInterrupt(RainPin, handleRainEvent, FALLING);
      return;
      }
    
    void handleRainEvent() {
        // Count rain gauge bucket tips as they occur
        // Activated by the magnet and reed switch in the rain gauge, attached to input D2
        unsigned int timeRainEvent = millis(); // grab current time
    
        // ignore switch-bounce glitches less than 10mS after initial edge
        if(timeRainEvent - lastRainEvent < 10) {
          return;
        }
    
        rainEventCount++; //Increase this minute's amount of rain
        lastRainEvent = timeRainEvent; // set up for next event
    }
    
    float getAndResetRainInches()
    {
        float result = RainScaleInches * float(rainEventCount);
        rainEventCount = 0;
        return result;
    }
    
    //===========================================================================
    // Wind Speed (Anemometer)
    //===========================================================================
    
    // The Anemometer generates a frequency relative to the windspeed.  1Hz: 1.492MPH, 2Hz: 2.984MPH, etc.
    // We measure the average period (elaspsed time between pulses), and calculate the average windspeed since the last recording.
    
    int AnemometerPin = D3;
    float AnemometerScaleMPH = 1.492; // Windspeed if we got a pulse every second (i.e. 1Hz)
    volatile unsigned int AnemoneterPeriodTotal = 0;
    volatile unsigned int AnemoneterPeriodReadingCount = 0;
    volatile unsigned int GustPeriod = UINT_MAX;
    unsigned int lastAnemoneterEvent = 0;
    
    void initializeAnemometer() {
      pinMode(AnemometerPin, INPUT_PULLUP);
      AnemoneterPeriodTotal = 0;
      AnemoneterPeriodReadingCount = 0;
      GustPeriod = UINT_MAX;  //  The shortest period (and therefore fastest gust) observed
      lastAnemoneterEvent = 0;
      attachInterrupt(AnemometerPin, handleAnemometerEvent, FALLING);
      return;
      }
    
    void handleAnemometerEvent() {
        // Activated by the magnet in the anemometer (2 ticks per rotation), attached to input D3
         unsigned int timeAnemometerEvent = millis(); // grab current time
    
        //If there's never been an event before (first time through), then just capture it
        if(lastAnemoneterEvent != 0) {
            // Calculate time since last event
            unsigned int period = timeAnemometerEvent - lastAnemoneterEvent;
            // ignore switch-bounce glitches less than 10mS after initial edge (which implies a max windspeed of 149mph)
            if(period < 10) {
              return;
            }
            if(period < GustPeriod) {
                // If the period is the shortest (and therefore fastest windspeed) seen, capture it
                GustPeriod = period;
            }
            AnemoneterPeriodTotal += period;
            AnemoneterPeriodReadingCount++;
        }
    
        lastAnemoneterEvent = timeAnemometerEvent; // set up for next event
    }
    
    float getAndResetAnemometerMPH(float * gustMPH)
    {
        if(AnemoneterPeriodReadingCount == 0)
        {
            *gustMPH = 0.0;
            return 0;
        }
        // Nonintuitive math:  We've collected the sum of the observed periods between pulses, and the number of observations.
        // Now, we calculate the average period (sum / number of readings), take the inverse and muliple by 1000 to give frequency, and then mulitply by our scale to get MPH.
        // The math below is transformed to maximize accuracy by doing all muliplications BEFORE dividing.
        float result = AnemometerScaleMPH * 1000.0 * float(AnemoneterPeriodReadingCount) / float(AnemoneterPeriodTotal);
        AnemoneterPeriodTotal = 0;
        AnemoneterPeriodReadingCount = 0;
        *gustMPH = AnemometerScaleMPH  * 1000.0 / float(GustPeriod);
        GustPeriod = UINT_MAX;
        return result;
    }
    
    
    //===========================================================
    // Wind Vane
    //===========================================================
    void initializeWindVane() {
        return;
    }
    
    // For the wind vane, we need to average the unit vector components (the sine and cosine of the angle)
    int WindVanePin = A0;
    float windVaneCosTotal = 0.0;
    float windVaneSinTotal = 0.0;
    unsigned int windVaneReadingCount = 0;
    
    void captureWindVane() {
        // Read the wind vane, and update the running average of the two components of the vector
        unsigned int windVaneRaw = analogRead(WindVanePin);
    
        float windVaneRadians = lookupRadiansFromRaw(windVaneRaw);
        if(windVaneRadians > 0 && windVaneRadians < 6.14159)
        {
            windVaneCosTotal += cos(windVaneRadians);
            windVaneSinTotal += sin(windVaneRadians);
            windVaneReadingCount++;
        }
        return;
    }
    
    float getAndResetWindVaneDegrees()
    {
        if(windVaneReadingCount == 0) {
            return 0;
        }
        float avgCos = windVaneCosTotal/float(windVaneReadingCount);
        float avgSin = windVaneSinTotal/float(windVaneReadingCount);
        float result = atan(avgSin/avgCos) * 180.0 / 3.14159;
        windVaneCosTotal = 0.0;
        windVaneSinTotal = 0.0;
        windVaneReadingCount = 0;
        // atan can only tell where the angle is within 180 degrees.  Need to look at cos to tell which half of circle we're in
        if(avgCos < 0) result += 180.0;
        // atan will return negative angles in the NW quadrant -- push those into positive space.
        if(result < 0) result += 360.0;
    
       return result;
    }
    
    float lookupRadiansFromRaw(unsigned int analogRaw)
    {
        // The mechanism for reading the weathervane isn't arbitrary, but effectively, we just need to look up which of the 16 positions we're in.
        if(analogRaw >= 2200 && analogRaw < 2400) return (3.14);//South
        if(analogRaw >= 2100 && analogRaw < 2200) return (3.53);//SSW
        if(analogRaw >= 3200 && analogRaw < 3299) return (3.93);//SW
        if(analogRaw >= 3100 && analogRaw < 3200) return (4.32);//WSW
        if(analogRaw >= 3890 && analogRaw < 3999) return (4.71);//West
        if(analogRaw >= 3700 && analogRaw < 3780) return (5.11);//WNW
        if(analogRaw >= 3780 && analogRaw < 3890) return (5.50);//NW
        if(analogRaw >= 3400 && analogRaw < 3500) return (5.89);//NNW
        if(analogRaw >= 3570 && analogRaw < 3700) return (0.00);//North
        if(analogRaw >= 2600 && analogRaw < 2700) return (0.39);//NNE
        if(analogRaw >= 2750 && analogRaw < 2850) return (0.79);//NE
        if(analogRaw >= 1510 && analogRaw < 1580) return (1.18);//ENE
        if(analogRaw >= 1580 && analogRaw < 1650) return (1.57);//East
        if(analogRaw >= 1470 && analogRaw < 1510) return (1.96);//ESE
        if(analogRaw >= 1900 && analogRaw < 2000) return (2.36);//SE
        if(analogRaw >= 1700 && analogRaw < 1750) return (2.74);//SSE
        if(analogRaw > 4000) return(-1); // Open circuit?  Probably means the sensor is not connected
        Particle.publish("error", String::format("Got %d from Windvane.",analogRaw), 60 , PRIVATE);
        return -1;
    }
    
    // Function to build the 'json' to trigger the Webhook.  To save characters the string only includes parameters that are not null.
    
    void createTSjson(String &dest)
    {
      // dest = "{ \"k\":\"" + api_key + "\", \"1\":\""+ field1 +"\", \"2\":\""+ field2 +"\",\"3\":\""+ field3 +"\",\"4\":\""+ field4 +"\",\"5\":\""+ field5 +"\",\"6\":\""+ field6 +"\",\"7\":\""+ field7 +"\",\"8\":\""+ field8 +"\",\"a\":\""+ lat +"\",\"o\":\""+ lon +"\",\"e\":\""+ el +"\", \"s\":\""+ status +"\"}";
    
        dest = "{";
    
        if(field1.length()>0){
            dest = dest + "\"1\":\""+ field1 +"\",";
        }
    
        if(field2.length()>0){
            dest = dest + "\"2\":\""+ field2 +"\",";
        }
    
        if(field3.length()>0){
            dest = dest + "\"3\":\""+ field3 +"\",";
        }
    
        if(field4.length()>0){
            dest = dest + "\"4\":\""+ field4 +"\",";
        }
    
        if(field5.length()>0){
            dest = dest + "\"5\":\""+ field5 +"\",";
        }
    
        if(field6.length()>0){
            dest = dest + "\"6\":\""+ field6 +"\",";
        }
    
        if(field7.length()>0){
            dest = dest + "\"7\":\""+ field7 +"\",";
        }
    
        if(field8.length()>0){
            dest = dest + "\"8\":\""+ field8 +"\",";
        }
    
        if(lat.length()>0){
            dest = dest + "\"a\":\""+ lat +"\",";
        }
    
        if(lon.length()>0){
            dest = dest + "\"o\":\""+ lon +"\",";
        }
    
        if(el.length()>0){
            dest = dest + "\"e\":\""+ el +"\",";
        }
    
        if(status.length()>0){
            dest = dest + "\"s\":\""+ status +"\",";
        }
    
        dest = dest + "\"k\":\"" + api_key + "\"}";
    }

Probably because the upload to the OTA service went OK and the device responded to the first packet.
Then the job for the IDE was finished successfully.

1 Like

I see, thanks for the explanation.

I'm still wondering why the flash was failed.

In this case, I don't think the reason was the "bad" code. I'll try to find out when I get back home next week.

I can’t see any reason for OTA failing in your code, but there might be some uncooperative waits/delays in the libraries you use that might contribute to the problem.

Just one tip:

Change your timing conditions from

  if(timeNextPublish <= millis()) {
    ...
    timeNextPublish = millis() + publishPeriod;
  }

to this

  if(millis() - timeLastPublish >= publishPeriod) {
    timeLastPublish = millis();
    ...
  }

That’s more reliable for the unsigned math and makes your code more readable and the timing more predictable as you have all info when the next entry will be due in one spot and independent of the time spent inside the code block.

And do you really need to read the sensors every 100ms?
I could imagune if you increased sensorCapturePeriod (e.g. to 1000) you might have more luck with OTA flashing.

You could also check System.updatesPending() and stop the sensor reading all together when there is an ongoing OTA update.

1 Like

Thanks @ScruffR,

I finally could do some tests yesterday. Now I believe it’s because of the poor WiFi reception in the backyard. When I took the device back in the house and got close to the router, everything went all right.

Does that sound reasonable to you? I don’t quite get it. As I mentioned, the Photon was publishing to the cloud without problems, only the OTA was failed.

By the way, your posts are always very helpful to me, I appreciate it!

Cheers,
Karl

Thanks for the kind words :blush:

That in deed does sound right.
A publish is a subsecond action, but OTA requires a stable connecton for longer and it has to be present at a particular moment for the OTA to actually properly kick in.

1 Like

Cool, thanks a lot!