Your CuPID as a PLC: Modbus Client LED Op Amp Monitoring

Our Demo circuit: An LED Driver

In this example, we’re going to set up a simple constant current source for an LED using an op amp and verify that we’re doing a good job of maintaining constant current as the voltage supply changes.

How it works

The really short version of how an op amp functions in most applications is simple: the op amp will vary voltage output to attempt to bring the two input terminals to the same voltage. Depending on the circuit configuration, this can result in inverting or non-inverting amplification, signal buffering, and all sorts of other neat things.

In the setup we’ll use, shown below, we set the input to the positive terminal using a couple of potentiometers, so that we can achieve the full range offered by the regulator, but still keep current low to avoid overheating it. The other op amp input terminal is connected above a resistor in line with our LED. This way, it directly measures current as a voltage, ILED*Rsense. The op amp controls the current by varying voltage on the gate of an n-channel mosfet. As a result, by tuning the input voltage on the op amp with our potentiometers, we set our target current. With a sense resistor of 10 ohms and a target current of 300mA, for example, we’d have a sense voltage of 3V. Hence, by setting our input voltage on the op amp to 3V, we get 300mA if the op amp does it’s job (this depends on a few other details).

The beauty of the op amp for this application is that it will maintain even very low current levels, which is very very hard to do by controlling only voltage. The diode’s IV curve is exponential, so dialing it in is very hard, and one reason constant current sources are used. Another good reason is that these characteristics change as the devices heat up. Because the light produced is directly related to current (the number of electrons reaching an LED’s junction is proportional to the number that recombine and produce light), the current is the correct quantity to control.

The circuit

We’ll use a voltage regulator with our pots to keep our op amp input voltage constant as we vary the supply voltage. We’re not showing it here, but our power supply to the op amp is +/-12V. We’ll get around to converting this to single supply at some point. We also throw in a current limiting resistor just in case we rail the op amp (maximum output) so we don’t nuke the LED.

Constant Current source for LED using an op amp

Putting it all together

Now let’s get this breadboarded up. We’ll use a few things we have laying around: a 2N7000 MOSFET, an LM341 op amp, a mic2937 3.3V LDO regulator, a Cree XPE blue LED, and a couple 5k pots. This means that if we have the pots set to get max input (R1=0, R2=5k), we’ll get 3.3V / 10ohm = 0.33A. This puts it comfortably in range of the LED’s spec. We’ll have a maximum supply voltage of 12V. With a drop on the LED of 3.5V at maximum current, we have about 8.5V to work with, ignoring the sense resistor and any FET on resistance. This means 28ohms, so 27 is close enough. Our voltage divider uses a 4.7K and a 22K resistor, so our scaling ratio for out Vs input is about 5.7. At about Vs=6.9V, our input reads within about 50mV of our voltmeter, which we can live with for now and calibrate later if we see fit.

We just lost a variable voltage power supply and the replacement is in the mail, so we’ll muddle through with a fixed 12V source power an adjustable switch-mode power supply. Here what we’re looking at. I didn’t clean up for you.

Our test setup, with the important bits labeled.
Our test setup, with the important bits labeled.

We started taking data, and then realized that the UE9 has the unfortunate limitation that the analog inputs are only +/-5V. We’ve got a T7 with a range of +/-10V, but we find the UE9 makes a more reliable MB TCP server. To get around the input voltage limitation, we’ll throw the inputs into a voltage divider. And, instead of having to remember the scaling, we’ll code it in as an option for the input.  All we need to do is add the ‘scale’ keyword to the options field in the mbtcp table. So, for example, for a 4:1 voltage divider, we’d simply add ‘scale:4’ to the options field and be set. This way we can measure up to about 20V on our 5V input. We go ahead and take care of that:

We set out scaling options to allow the use of a voltage divider with output of scaled values.
We set out scaling options to allow the use of a voltage divider with output of scaled values.

We jump over to our dataviewer, and we can see that our scaling has indeed been reflected in the read values (notice the jump from 5 to 20 on the axis where the scaling changed):

Out scaling change is clearly visible in our data viewer. The value jumps from 5 to 20 where we added in our factor of 4 scaling.
Out scaling change is clearly visible in our data viewer. The value jumps from 5 to 20 where we added in our factor of 4 scaling.

Alright, so on with it. We label our inputs and start with our LED supply voltage at about 7V and our op amp input set at 150mV , giving us about 15mA on the LED. Our opamp tracks nicely, matching voltage on the input.

Supply Voltage Tolerance

The first test is to vary the input voltage. The point of this setup is to compensate for Vs variation while keeping constant current, so this is the important test.  So we vary slowly up to the max, ensuring we retain the same sense voltage, and then come back down again:

Varying supply voltage and maintaining constant current.
Varying supply voltage and maintaining constant current.

We notice that at about 3.25V, the op amp output maxes out our analog voltage input. What this means is that the op amp is trying its hardest to compensate and match input voltages, and is having to increase the output voltage to do so. At some point, it will be unable to do so, and it will saturate or ‘rail’ at its maximum output value. Before we add a voltage divider and scale our input for this value, we can’t see how high it can get, but we expect it to get somewhere near its high rail voltage of ~12V. Now, taking a look at our circuit, we can see that a minimum supply voltage of 3.5V for this current makes sense. We’re putting 3.5V supply in at about 20mA. At this current, the forward voltage on the LED is about 2.75, leaving us 0.75V to work with. Our combined sense and current limiting resistance is about 37ohms. Dividing our 0.75V by 37 ohms gives us 20mA. It all makes sense!


The next important criterion is stability. At a fixed condition, how stable will the current remain, should we want to use the LED signal for measurement purposes? We peg the current at about 50mA at 12V, a pretty typical condition for our envisioned application. Things look quite nice:

Steady-state current output (shown here as voltage inputs to op amp). The condition here is roughly 50mA at 12V supply voltage.
Steady-state current output (shown here as voltage inputs to op amp). The condition here is roughly 50mA at 12V supply voltage. Very little noise, and systematic. Against the resolution of the ADC, this appears to be measurement error on our analog inputs.

Dumping the data and running some statistics, we come up with the following plot and data:

Statistics on our constant current stability test. Measurement is within the noise of the ADC, so our op amp is certainly doing its job. This comes out to something like 0.01% error. Not bad.

The takeaways are the following:

  • Standard error on all signals are within the resolution of the ADC. Within error, they are constant. This explains why the noise on the input signal and sense measurement are the same.
  • Error is low on all measurements: <0.01%. This is suitable for most measurement techniques.
  • Offset voltage is low, at 660uV. This corresponds well to the specified offset voltage of 1mV for the op amp


Our op amp did great, as did our modbus client. While we need to adapt our design to single-supply operation, the basics are here and appear to work for our current requirements (pun intended).

Join us next time, as we apply these principles to reading a photodiode, and eventually as we control and measure our photodiode for a detection tool!


The above makes use of the open source libraries available on github here:

Explanation and installation here:

One thought on “Your CuPID as a PLC: Modbus Client LED Op Amp Monitoring”

Leave a Reply

Your email address will not be published. Required fields are marked *