Photon white led w/ solar panel


I’m working with a Photon. I have a 9W solar panel, SparkFun lipo fuel guage, LiPo charge controller, Adafruit Powerboost, and then I hook up various sensors, in this case an SHT-31 Temperature and humidity sensor over I2C.

I have three units up and running. One has been running for over 2 months but is in a bit of shade under a tree.

One I deployed today in really direct sunlight and it worked perfectly for 30mins or so and then the Photon started blinking with a white LED. I tried the reset button and it would immediately return to blinking white. I thought maybe it was overheating in the sun but it was not hot to the touch at all. When I just shifted the solar panel a few degrees to the side and reduced it’s light level just a bit, the Photon immediately blinks green, finds the cloud and goes pulsing blue and sends data.

Does anyone have ideas on what might be going on? Is the charge controller producing too much voltage for the battery and maybe it’s spiking the controller? I plan on pulling out the multimeter and taking some measurements tomorrow, but I’d love some ideas!


Have you already looked into this?

It’s definitely not that. I don’t have the wifi off in any code. Also it wasn’t breathing white but fast blinking white, if I remember correctly… Or it might have been solid white with no fluctuation…

It would help everyone trying to help you if you could share your code rather than us guess at the cause from your description of the symptoms!

Is it possible that the Photon is re-powering and in setup() is not able to connect to the I2C sensor or it isn’t able to progress after hitting an issue with the sensor reading?

1 Like

Yes, code! I just wanted to get this rolling last night…

//    Rain Drop Sensor + Capacitive Soil Moisture Sensor v1.2 + SHT31 Temperature & Humidity Sensor
//    Purpose: Rain Drop Sensor - Analog sensor can detect how much rain is falling, digital sensor can be calibrated with pot to trigger event at certain wetness level, also triggers led on device.
//             Capacitive Soil Moisture Sensor v1.2 - Measures soil moisture
//   SHT31 Temperature & Humidity Sensor - I2C sensor that measures both temperature in Celcius and humidity
//    Author: David Cool
//    Version: 1.0


#include <adafruit-sht31.h>
#include <SparkFunMAX17043.h>
//#include <google-maps-device-locator.h>

// Setup SHT31 object
Adafruit_SHT31 sht31 = Adafruit_SHT31();

// variables for MAX17043 fuel guage
double voltage = 0; // Variable to keep track of LiPo voltage
double soc = 0; // Variable to keep track of LiPo state-of-charge (SOC)
int alert = 0; // Variable to keep track of whether alert has been triggered

// Setup Google Maps Device Locator Object
//GoogleMapsDeviceLocator locator;

//#define RESOLUTION 1024 //Use for 10 bit resolution ADC (Arduino)
#define RESOLUTION 4096 //Use for 12 bit resolution ADC (Particle Photon)

// define rain drop sensor variables
const int RAIN_DROP_ANALOG_PIN = A0;
int analogdropLevel;
int digitaldropLevel;

// define soil moisture sensor pin
const int SOIL_SENSOR_PIN = A1;
int soilMoisture;

// define SHT31 temperature & humidity variables
float tc;
float tf;
float h;

// define dew point and humidex variables
double dpc;
double dpf;
double hdc;
double hdf;

// define variables for Google geolocation data
float glat;
float glon;
float gaccuracy;

// used to store device name
char dev_name[32] = "";
bool publishName = false;

unsigned long firstAvailable = 0;
int counter;
retained int retainedCounter = 0;

void handler(const char *topic, const char *data) {
  strncpy(dev_name, data, sizeof(dev_name)-1);
  Serial.printlnf("received %s: %s", topic, dev_name);
  publishName = true;

// dewPoint function NOAA
// reference (1) :
// reference (2) :
double dewPoint(double celsius, double humidity)
  // (1) Saturation Vapor Pressure = ESGG(T)
  double RATIO = 373.15 / (273.15 + celsius);
  double RHS = -7.90298 * (RATIO - 1);
  RHS += 5.02808 * log10(RATIO);
  RHS += -1.3816e-7 * (pow(10, (11.344 * (1 - 1/RATIO ))) - 1) ;
  RHS += 8.1328e-3 * (pow(10, (-3.49149 * (RATIO - 1))) - 1) ;
  RHS += log10(1013.246);

  // factor -3 is to adjust units - Vapor Pressure SVP * humidity
  double VP = pow(10, RHS - 3) * humidity;

  // (2) DEWPOINT = F(Vapor Pressure)
  double T = log(VP/0.61078);   // temp var
  return (241.88 * T) / (17.558 - T);

// humidex c calculation using dew point
double humidexc(double tc, double dp)
  double e = 5417.7530*((1/273.16)-(1/(273.16 + dp)));
  double h = tc + 0.5555 * ( 6.11 *  exp (e) - 10);
  return h;

// humidex c calculation w/ relative humidity
double humidexc(double tc,double h) {
  double e;

  e = (6.112 * pow(10,(7.5 * tc/(237.7 + tc))) * h/100); //vapor pressure

  double humidexc = tc + 0.55555555 * (e - 10.0); //humidex
  return humidexc;

// source:
// source:
double humidexf(double tf, double h) {
  double humidexf = -42.379 + (2.04901523 * tf) + (10.14333127 * h) - (0.22475541 * tf * h) - (6.83783 * pow(10, -3) * pow(tf, 2)) - (5.481717 * pow(10, -2) * pow(h, 2)) + (1.22874 * pow(10, -3) * pow(tf, 2) * h) + (8.5282 * pow(10, -4) * tf * pow(h, 2)) - (1.99 * pow(10, -6) * pow(tf, 2) * pow(h, 2));
  return humidexf;

void locationCallback(float lat, float lon, float accuracy) {
  // Handle the returned location data for the device. This method is passed three arguments:
  // - Latitude
  // - Longitude
  // - Accuracy of estimated location (in meters)
    //String Coordinates;
    //Coordinates = String::format("{\"lat\":%f,\"lon\":%f,\"accuracy\":%d}", lat, lon, accuracy);
    //Serial.print("Sending Coordinates : "); 
    glat = lat;
    glon = lon;
    gaccuracy = accuracy;
void setup()
    // start serial monitor
    // have the google library tell your code what location was found
    Serial.println("SHT31 Temperature & Humidity Sensor");
    if (! sht31.begin(0x44)) {   // Set to 0x45 for alternate i2c addr
        Serial.println("Couldn't find SHT31");
        while (1) delay(1);
    // setup A0 and D0 as input pins
    // Set up Spark variables (voltage, soc, and alert):
    //Particle.variable("voltage", voltage);
    //Particle.variable("soc", soc);
    //Particle.variable("alert", alert);
    // To read the values from a browser, go to:
    // Set up the MAX17043 LiPo fuel gauge:
    lipo.begin(); // Initialize the MAX17043 LiPo fuel gauge  
    // Quick start restarts the MAX17043 in hopes of getting a more accurate
    // guess for the SOC.
    // We can set an interrupt to alert when the battery SoC gets too low.
    // We can alert at anywhere between 1% - 32%:
    lipo.setThreshold(10); // Set alert threshold to 10%.    
    // get device name from cloud
    Particle.subscribe("particle/device/name", handler);
    Particle.publish("particle/device/name");  // <-- ask the cloud for the name to be sent to you

void loop()
  // Publish location to Google
  bool wifiReady = WiFi.ready();
	bool cloudReady = Particle.connected();

	Serial.printlnf("wifi=%s cloud=%s counter=%d retainedCounter=%d", (wifiReady ? "on" : "off"), (cloudReady ? "on" : "off"),
			counter++, retainedCounter++);
    if (wifiReady && cloudReady) {
		if (firstAvailable == 0) {
			firstAvailable = millis();
		if (millis() - firstAvailable > 60000) {
			// After we've been up for 60 seconds, go to sleep. The delay is so the serial output gets written out before
			// sleeping.
			Serial.println("calling System.sleep(SLEEP_MODE_DEEP, 600)");
            // SLEEP_MODE_DEEP timinig is in SECONDS, NOT microseconds!!!
			System.sleep(SLEEP_MODE_DEEP, 120);

			// The rest of the code here is not reached. SLEEP_MODE_DEEP causes the code execution to stop,
			// and when wake up occurs, it's like a reset where you start again with setup(), all variables are
			// cleared, etc.
			Serial.println("returned from sleep, should not occur");
		if (publishName) {
            // Publish an event every ? minutes. The event is JSON formatted
            // rain drop sensor
            analogdropLevel = analogRead(RAIN_DROP_ANALOG_PIN);
            digitaldropLevel = digitalRead(RAIN_DROP_DIGITAL_PIN);

            // soil moisture sensor
            soilMoisture = analogRead(SOIL_SENSOR_PIN);

            // SHT31 temp & humidity sensor
            tc = sht31.readTemperature();
            tf = (tc * 9.0/5.0) + 32.0;
            h = sht31.readHumidity();
            // calculate dew points in C & F
            dpc = dewPoint(tc,h);
            dpf = ( dewPoint(tc,h) * 9.0/5.0 ) + 32.0;
            // calculate humidex in C & F
            hdc = humidexc(tc,h);
            hdf = humidexf(tf,h);
            // wake up the lipo fuel guage
            // lipo.getVoltage() returns a voltage value (e.g. 3.93)
	        voltage = lipo.getVoltage();
	        // lipo.getSOC() returns the estimated state of charge (e.g. 79%)
	        soc = lipo.getSOC();
	        // lipo.getAlert() returns a 0 or 1 (0=alert not triggered)
	        alert = lipo.getAlert();
	        // put lipo fuel guage to sleep
	        // Those variables will update to the Spark Cloud, but we'll also print them
	        // locally over serial for debugging:
	        Serial.print("Voltage: ");
	        Serial.print(voltage);  // Print the battery voltage
	        Serial.println(" V");
	        Serial.print("Alert: ");
	        Serial.print("Percentage: ");
	        Serial.print(soc); // Print the battery state of charge
	        Serial.println(" %");
    		char sensor_data[512];
    		snprintf(sensor_data, sizeof(sensor_data), "{\"analog_rain_drop_value\":\"%d\",\"digital_rain_drop_value\":\"%d\",\"soil_moisture\":\"%d\",\"C\":\"%.2f\",\"F\":\"%.2f\",\"humidity\":\"%.2f\",\"dew_point_celsius\":\"%.2lf\",\"dew_point_fahrenheit\":\"%.2lf\",\"humidex_celsius\":\"%.2lf\",\"humidex_fahrenheit\":\"%.2lf\",\"device_name\":\"%s\",\"lat\":\"%.15f\",\"lon\":\"%.15f\",\"accuracy\":\"%.2f\",\"voltage\":\"%.2lf\",\"soc\":\"%.2lf\",\"alert\":\"%d\"}", analogdropLevel, digitaldropLevel, soilMoisture, tc, tf, h, dpc, dpf, hdc, hdf, dev_name, glat, glon, gaccuracy, voltage, soc, alert);
    		Particle.publish("sensor_data", sensor_data, PRIVATE);
            delay(1000); // to ensure adhering to rate limit
            publishName = false;
            Particle.publish("cool_sensors", sensor_data, PRIVATE);
            delay(1000); // to ensure adhering to rate limit
            publishName = false;

	else {
		firstAvailable = 0;


This is a picture of how things are wired up:

Also, it’s weird, I had this happen to the other unit that was on my desk this morning… It was doing a breathing white LED. When I hit the reset button it blinked green, then cyan and went to cyan breathing and everything proceeded normally from there. I’m just not sure what’s throwing these things into the white LED status… So now I’m not sure if it’s solar panel related or not. My hunch is it’s still some kind of voltage drop or spike that is causing it to glitch, but that’s just a guess… I have to do some more digging/testing.

A few quickie observations.

  1. Are there pull-up resistors on the SDA and SCL I2C lines?
  2. An external antenna would be a good idea - are you measuring RSSI?
  3. SYSTEM_THREAD(ENABLED); always best
  4. Without SYSTEM_THREAD(ENABLED); if you check for Particle.connected() when WiFi isn’t ready then it will block!

My suspicion fell here:

    if (! sht31.begin(0x44)) {   // Set to 0x45 for alternate i2c addr
        Serial.println("Couldn't find SHT31");
        while (1) delay(1);

If your sensor connection fails, you will fall into a never-ending loop.

My suggestion would be to set up a max retry count which will allow it to continue and report the error to the cloud.

Well spotted!

1 Like


Thanks for the reply.

  1. There are 4.7K resistors tied to VCC on the sparkfun lipo guage PCB on SDA and SCL, I believe those are the pull-up resistors no?
  2. The external antennas are in transit now. I plan to add those to extend range (hopefully), but right now the signal strength reads very strong in the Particle control panel.
  3. I will try the SYSTEM_THREAD(ENABLED) when I’m back in the office.!


Good idea to add a safeguard in there for the sensors.

I’m pretty sure this isn’t the problem though.

I’ve been monitoring this durning the weekend from home. There is something going on with the hardware side of things, I think.

The unit will start charging the LiPo battery when the sun rises starting around 80% or so… As early morning sun gets more intense when the battery reaches around 99.00% or higher, the sensor readings all stop. And then around 5 or 6pm when the sun isn’t as direct, the unit comes back online, with battery readings below 99% and it takes correct readings all night until the next morning and then they cycle repeats.

My guess is still that the voltage is going over 6v or something and the unit is in some kind of shut down for safety? Does the Photon bypass the voltage regulator when using Vin as opposed to the USB input for power?

My plan for Monday is to multimeter the inputs to the Photon around the cutout time tomorrow and get some data.

If anyone has ideas, or a good solution to this kind of problem, I’d love input!

Thanks all.

Nope, USB and Vin take the same path to the regulator. The only difference between the two is the protective Schottky diode between USB and Vin to prevent Vin from feeding back into the USB port. Hence the voltage provided via USB will be ~0.2V less then what’s fed into USB while this is not the case on Vin.

Also when you see the issue when the sun is the strongest, have you considered a thermal problem?


Yeah, that was my first thought. To the touch it felt quite cool, but this is anecdotal to be sure. I have a temperature probe for the multimeter, I’ll give that a whirl tomorrow as well. But my intuition is that it’s something else. Thanks for the input.

My guess is the Voltage from that large 9W panel (spec’d at 7.7V) is causing problems for the Photon. Once Re-Charging stops for the Li-Po, the Photon is likely seeing a unsafe Voltage. What is your power path (it’s hard to tell with all the extra “power” gadgets)?

[Edit] Deleted…I didn’t pay attention to the Photograph.

After a quick look at your Code, it appears the intention is to sleep for 2 minutes, then awake for 1 minute (I could be wrong).
If that’s correct, you might can significantly reduce your duty cycle.
The Photon can read the sensors while it’s establishing the WiFi and Cloud connection, and go to sleep as soon as the publish has completed. That may be 5-10 seconds of runtime verses 60.

You can also consider using a 5V Panel and Argon, to reduce your hardware on your next build.

Had a look at the powerboost 1000C - this should output 5V - actually 5.2V - so I am not that clear why it would be outputting 6.5V unless it isn’t working OR the GND is going negative? You definitely need to measure with a multi-meter when it goes off.

1 Like


Yeah, it’s sleeping for 2min now and waking for a minute. I had the wake time very slim like you said 5-10 seconds, but it’s really hard to flash new updates when deployed in the field w/ such narrow windows… I’m still in development mode really so I kept it at a minute to have just enough time to flash. But after everything is smoothed out my intention would be just that.


Yeah, that was my thinking as well. I haven’t dug into the board level components on that Adafruit device but the output is set to 5.2V like you said to account for cable attenuation or schottky diodes on the USB inputs of devices that cause a little voltage drop.

Just for clarity… The signal path is 10000mAh LiPo battery --> Sparkfun Lipo Fuel Guage --> solar charge controller --> Adafruit Powerboost 1000 --> Photon

The 9W solar panel is going into the charge controller.

It feels like extra voltage has to be coming from somewhere once the battery cuts off when full…

My plan is to take multimeter readings in various places and some temperature probing as well for good measure tomorrow. Hopefully something presents itself.

Thanks for all the input most appreciated!

I took some multimeter readings today:

Temp: 83-84F with the probe right on the surface of the Photon. So I don’t think it’s a thermal issue…
Voltage: 6.16V coming out of 5V & GND from the Adafruit PowerBoost 1000 PCB.

The over voltage seems like the culprit. According to the datasheets it appears to be 6.5V max input on Vin:

So I think I’m hitting that max as the intense sunlight spikes the panel input…

Does anyone know of a good solution? I was thinking maybe an LDO regulator between the Adafruit PowerBoost and the Photon. I just need something to knock the edge off when it creeps over 5.5V but not cause a drop w/ the normal 5.2V supply.

Any ideas welcome!

To me it looks like the solar regulator out put is the culprit - you need to replace the Power boost with a buck/boost converter like this…

1 Like