I have been blogging on a smart solar lighting system that I have been developing for the past two months (Smart Solar Lighting Project - Initial design ideas , Smart Solar Lighting Project - Detailed design , Smart Solar Lighting Project - Mechanical designs , Smart Solar Lighting Project - Update 10/2/2020 and Smart Solar Lighting Project - Update 10/8/2020 ).  It is somewhat fortuitous that I was coming up on one of my milestones (code closed loop PWM function to normalize LED current over the range of battery voltage (I am thinking of a simple PID loop to adjust PWM to achieve desired LED current)) as this competition was running (actually close to ending).


My goal is to provide a way to control my LED current across a wide range of battery voltage (Li-Ion , 4.2 to 3.0V).  Initially, during my design phase, I had decided on having two current monitors, each allowing the measurement of the current through a pair of LEDs.  I had thought about a constant current source as a control element, but instead decided to utilize a software controlled, PWM driver to power my LEDs. Here is the the present schematic (snippet) of the LEDs:


PWM driver - current monitor




The LEDs used in this design are : CLA1B-MKW-XD0E0F83 by CREE.

The basic specifications are:

Warm white, 6.3 - 10.7 lm (determined by binned lots), CRI = 80

Forward Current 80mA (max) with an allowed forward Peak current of 100mA (Pulse width < 0.1mse, duty < 1/10)


My target current draw for the LEDs is 160mA, or 40mA per LED.  At this rate of current, my 840mAH battery should allow for about 5.25 hours of LED on time.  I intend to only schedule between 1 to 2 hours of lighting per evening, so the device should be able to ride out a couple of cloudy days in a row.


The struggle in this design, is to find a good sweet spot for the current limiting resistors (now set at 9.1Ω), so that at higher battery voltages I can use PWM to regulate the effective current (and likewise illumination) to my target and as battery voltage decreases, I can increase the 'ON' percentage towards steady state.  Once the battery voltage drops below the steady state point, the current and illumination will taper off.


Initially I had selected 2N7002, N-channel MOSFETs (I say selected, but actually I had them, so I used them) to control the switching of the LEDs.  Due to the higher gate voltages and also RDS(ON) (~8Ω at 3.3 Vgate), I decided that I needed to find a better MOSFET.  My new MOSFET is a DMN2230UQ will provide a RDS(ON) (~0.3Ω at 3.3 Vgate), so I will get a lot closer to my target currents.


Final selects of current limiting resistors, current sense resisters and gain setting resistors may have to be revisited as I install the new MOSFETs and continue to develop the product.










So, next up was to start working on the PID loop to control the PWM duty cycle, based on the current flow measured in the two current monitors.  Up until this point, I could directly set the PWM duty cycle from 0% (off) to 100% (static on) in steps of 5% from my remote control panel.  I wanted to collect some data, so I set the LEDs to static on (100%) and captured data for ~1 hour.  Here is a chart of the LED currents as the battery voltage varied:

The poor scaling (a combination of 8-bit ADC mode and some scaling) left me with about 20mV resolution.  This is something that I intend to improve on, but that will be for a later time.  It is clear that as the battery voltage drops, so does the LED currents.  Another issue that I am still working on is the difference in LED currents between the to channels.  Both channels have the same offset, while the LEDs are off (a couple of counts), but as the current increases, so does the difference in readings between the two channels.  I either have unequal grounds between the two channels, or a gain mismatch in the two monitors, driving the two signals apart as the current level increases (again, another area for improvement).


Here is a quick lesson on PID loops.  A PID loop uses three different aspects of the relationship of the controlled element's performance, relative to its expected value.  These three elements are the proportional error (P term), the integral of the proportional error (I term) and the derivative of the proportional error (D term) and sums up their weighted values (using Kp, Ki and Kd) to determine the correction to apply to controlled element.  In this case, the input is the LED current and the output is the Duty cycle of the PWM driver.  The error, is difference of the measure LED current and the supplied target current.

PID loop


Proportional Term ( (ILED - Target Current) * Kp) - The proportional error is used in multiple ways to control the system (it is also used both the Integral and Derivative terms). The Proportional Term is simply the this error multiplied by the Kp gain value.  This term is used to make small changes to the output based on deviations in the LED current.  In systems that have an output that swings around a zero set point, the Proportional Term can be a major contributor to the correction as the Integral term is much smaller as it tend to be near zero.


Integral Term (  (Integrator += error) * Ki) - The integral term is handles errors that accumulate over time (like drift, varying DC offsets, etc.).  In a system, like my LED fixture, the Integral term is the primary source of correction, as it hold the system near to the target level and usually represents the high contribution to the output correction.  In my LED fixture the diminishing Battery Voltage will be corrected by the Integrator value rising, due to the LED currents being lower than the target current.  The rising Integrator value will feed into the output, causing the PWM on time percentage to rise, which will cause the LED current to rise back towards the target value.


Derivative Term ( (error - PriorError) * Kd) - The Derivative term is determined by the rate of change in the error values and is used to dampen the system to improve stability and lessen potential overshots in the output value.


In addition to the three correction terms (Proportional, Integral and Derivative), this control systems employs an output limiter, which is responsible to keeping the output value in the proper operating range.  With the timer setup that I am using, the control value needs to be between 0 and 1023 in order to produce a proper PWM signal.


From here, I decided on using my captured data to simulate my PID control loop in Excel. So using the relationships between battery voltage, Duty cycle and LED currents seen in my initial testing, I constructed a rough simulation of the control loop.  For the purpose of my simulations, I tested the system by moving the target current about and checking that the PWM reacted accordingly.

PID simulation Kp = 0.05, Ki = 0.75, Kd = 0.05


The above image is a capture of the PID simulation, showing the resulting current (red trace - iNewVal - derived from the internal variable of PWM duty cycle), as the commanded by the target current value ( blue trace - icmnd).  The simulation is using the following gains: Kp = 0.05, Ki = 0.75 and Kd = 0.05 (due to the simulated model, the Kd gain is negative, i.e. subtracted from the other terms).  This collection of gains seemed to most closely match the performance that I expect from the system.  (Note: the changes depicted in these simulations are happening much more quickly than they will in the real world.  I used these values to view the performance in a more compressed format.).


Using the simulation of the PID loop I can quickly change the gains to test the stability and effectiveness of the settings.  Here are a couple of captures at different settings.


PID simulation Kp = 0.0, Ki = 0.75, Kd = 0.0

The above image shows the effect of removing both the Kp and Kd terms (Kp = 0, Kd = 0).  There is very little change in the overall performance, just a bit smoother, but slower response to the sharp edges.


PID simulation Kp = 0.05, Ki = 1.00, Kd = 0.05

The above image has restored the Kp and Kd terms and increased the Ki term (Kp = 0.05, Ki = 1.0 and Kd = 0.05). Here there is overshooting and ringing on the sharper edges.


PID simulation Kp = 0.05, Ki = 0.5, Kd = 0.05

The above image has restored the Kp and Kd terms and decreased the Ki term (Kp = 0.05, Ki = 0.5 and Kd = 0.05). Here response on the sharper edges is sluggish and the up and down ramps show delays (lagging the command).


PID simulation Kp = 0.20, Ki = 0.75, Kd = 0.0

The above image (Kp = 0.20, Ki = 0.75 and Kd = 0), has increased proportional gain and no derivative gain.  The proportional and integral gains are tending to react to each others as we approach the set points.


PID simulation Kp = 0.0, Ki = 0.75, Kd = 0.20

The above image (Kp = 0, Ki = 0.75 and Kd = 0.20), has no proportional gain and increased derivative gain.  The derivative and integral gains are tending to react to each others as we approach the set points.


All in all, the initial gain settings (Kp = 0.05, Ki = 0.75 and Kd = 0.05) seem to reflect the desired performance with the least amount of over/under shoots.


Having taken the simulation results and convert it into code, I have the following code snippet (reduced to provide some clarity):


unsigned char TargetCurrent;
unsigned int Kp;
unsigned int Ki;
unsigned int Kd;
int Integrator;
unsigned int DutyCycle;
unsigned int PriorError;

void main(void)

    TargetCurrent = VX1_0_500;     // target LED value set ~43mA per pair of LEDs
    Kp = 6;                        // ~0.05 
//    Ki = 96;                       // ~.75 
    Ki = 64;                       // ~.5 
    Kp = 6;                        // ~0.05 
    Integrator = 0;
    PriorError = 0;

                                // if LEDs are on, manage DutyCycle through PID based on VIFront/VIBack values (20msec update rate)
                                if (LEDsOn)

void processLEDvalues(void)
    unsigned char averageIval;
    int error;
    int KpTerm;
    int KiTerm;
    int KdTerm;

    averageIval = VIFront + VIBack;                        // average the two current moniotr outputs
    error =  (TargetCurrent << 1) - averageIval;        // compute error value (double target to match scale of summed currents)
    KpTerm = (error * Kp);                                // form Kp term
    Integrator += error;                                // Accumulator error from this frame
    KdTerm = (error - PriorError) * Kd;                    // form Kd term
    PriorError = error;                                    // save error for next frame
    if (Integrator > INTEGRATOR_MAX)                    // prevent excessive wind up of the Integrator in both directions
        Integrator = INTEGRATOR_MAX;
    else if (Integrator < INTEGRATOR_MIN)
        Integrator = INTEGRATOR_MIN;
    KiTerm = (Integrator * Ki) + KpTerm - KdTerm;        // form Ki term and sum in Kp and -Kd
    if (KiTerm < 0)                                        // limit total correction to positive (part of output limiter)
        KiTerm = 0;
    DutyCycle = (unsigned) KiTerm;                        // scale result into units of the PWM command (convert integer gains into fractional)
    DutyCycle = ((DutyCycle * 5) + 8) >> 5;
    if (DutyCycle > 1023)                                // limit DutyCycle to maximum (remaining portion of output limiter)
        DutyCycle = 1023;
    SetLEDValues((unsigned int) DutyCycle);                // Command LEDs driver to produce expected LED currents


The code was compiled and downloaded into the Smart LED Lighting fixture.  Here are some of the captured results:


Current control loop in action

The graph shows the commanded and measure LED currents (green and red traces, respectively) and the control output PWM (percent - purple trace) in response to the battery voltage (blue trace).  This run was attempting to hold the LEDs at the midpoint of the control range while the unit ran for ~ 110 minutes.  About 70 minutes into the run, the PWM control switch to static 'ON' , and the resulting LED current dropped below the regulated current a ~95 minutes.  I still need to adjust the scaling and control range of the LEDs such that the full battery voltage controls the expected LED current with a value closer to 50%, so that LEDs will be able to stay in regulation as the battery voltage drops closer to the cutout range of 3.0V.


In preparing to do some videos, I modified the LED lighting board to allow me to capture the PWM signals on an oscilloscope and to allow me to power the LEDs on board from an external power supply.  This will allow me to actively adjust the 'battery voltage' while monitoring the current draw (fake battery current) and the pulse width.  In running through a sequence of voltages and target currents, I captured the following data:


LED current versus Battery Voltage at 25% range.LED current versus Battery Voltage at 25% range.

The above two graphs plot the performance of the PID loop in controlling the current as the battery voltage changes.  In the first graph the circuit is attempting to control the LED current at about 50% of the current range (via the current monitoring circuit).  This is presently a bit of a challenge for the device, as the controlling MOSFET (as mentioned above) are not a good choice for the design as their RDSON is too high, limiting the amount of current available at normal battery voltage levels. In this test, I actually exceeded the voltage range to show a bit more of the PID control loops range. The current remains relative flat through the range of 4.75 to 3.50V (strange bump between 3.75 and 3.5V) and then drops off as the LED driver switches from PWM control to 'Full On' mode.


The second graph lowers the regulation target to about 25% (extending the active control range).  Again the current remains flat through most the range, 4.75 to 3.25V, where again there is a slight peaking of the LED current before the current start to fall.  Not absolutely sure what is happening there, but it could be that  internal current monitoring is misreporting the current (either due to noise or rolloff in the R/C smoothing filter as the PWM is nearing 90%).  This again is something that I need to look into further as I continue on this project.


Here is a quick proof video, showing the PWM waveforms and the power supply voltage and current (I really have been enjoying my multicomp MP730027 DMM).


Finally, Thank you element14 for providing another fun and interesting opportunity to compete with other talented members!