Published January 11, 2026 © GPL3+
This is the second version of my electronic load, it has a PID controller and web interface, we can control the load digitally upto 30W.
IntermediateFull instructions provided5 hours**1
Things used in this project
Story
In one of our previous articles, we discussed the [constant current and constant voltage profiles](https://www.hackster.io/LithiumION/cc-cv-charging-of…
Published January 11, 2026 © GPL3+
This is the second version of my electronic load, it has a PID controller and web interface, we can control the load digitally upto 30W.
IntermediateFull instructions provided5 hours**1
Things used in this project
Story
In one of our previous articles, we discussed the constant current and constant voltage profiles in detail. These are the most important for an analog engineer working with a lot of equipment. We have to test the DC supplies a lot, and most of them are batteries of various types. You will find a lot of battery-charging-related stuff in my profile. However, when it comes to performance testing, I must test the battery under a load. Changing the type of load is not always possible; I can use a simple big resistor made for current sensing. However, the fact is that they are not variable, and I am unable to find any high-wattage resistor. That’s how I address the issue and create my own electronic load version 1.0.
Eload basically acts as a variable resistor, but if I say from my engineering mind, it is a constant current load. Means that whatever the voltage, if we fix the current, it will only draw that constant amount of current. In this way, the name can be CC load. Now, in version 1.0, we can vary this constant current by rotating the potentiometer and set coarse/fine values. But after using it for a month or so, I have addressed some issues. First, I do not need that much wattage, so I reduced the size of the heatsink. I also need digital control and a measuring display. I do not need ultra precision, but it should be easy to monitor the real-time current and wattage. And here is the modified version of our electronic load with the PCBs and components sponsored by NextPCB.
Electronic load V3:
I used a low-value shunt resistor to minimise the drop across it. I have used a PCB mount heatsink on the transistor. To display everything and control this load easily, I have designed a web server and integrated all the current monitoring there. It is still powered by 12V, and an ESP8266 is used for controlling the load. Moreover, I reduced the BOM cost, and after testing, I modified the design slightly; the PCB design is attached below. This can work fine up to 30 watts in continuous current mode, which is more than enough for testing single-cell batteries andUSB PD ICs.
The design:
A power MOSFET with a low-value current shunt resistor is used; in this case, 0.165 ohms. This means that if 1A flows through the load, the voltage drop across the shunt resistor is 165 mV. This value goes into an op-amp and is compared to a reference voltage. When both are the same, the load is locked. For example:
**vREF: **165mV
**Current shunt drop: **165mV
Electronic load is locked to 1A.
However, 165mV is a very small value, which is why I used an DC amplifier to raise the level by 2-3x. Then it is compared with the reference. The reference voltage is produced by potentiometers in the older design, but for digital control, we used the PWM method here. PWM duty cycle determines the voltage; for more information,please refer to the full details. After this, if we apply any voltage source across the terminals of Eload, it will start loading that supply. To maintain the set value and lock value equal, I have implemented an ACS712 current measurement and used a PID controller to fine-tune the PWM. This feature is currently under testing and will be fully updated within a week or so.
Components required:
- ESP8266 12e Wi-Fi MCU
- IRFP260N N-channel MOSFET
- LM358 Operational amplifier
- ACS712 hall effect sensor
- 10K SMD potentiometer
- Decoupling capacitor 100n and 1uf
- 10K, 4.7 K, and 1K resistors
- 0.33Ohm 5W resistor (or 0.165ohms)
- 12V adapter
- PCB from NEXTPCB
Circuit Description:
So, I have made the circuit in different sections, with the input on the left top and the outputs on the right side. The input section contains a USB-C input, which is later charged with a 12V DC jack, and some decoupling capacitors for the LDO AMS1117 3.3V. The second section consists of the MOSFET and control block, where the comparator serves as the MOSFET driver, and the MOSFET is connected in parallel with two 0.33 ohm resistors.
The third section is the PID feedback and current sensing section, which provides actual current data to the ESP8266. The block below has the main microcontroller with programming headers. Some resistors are used to make a minimal system. The microcontroller operates at 3.3V and generates the PWM at the same level (here limited by current, in future we can increase resolution). Then, some filters are used below to make the PWM output as smooth as possible.
I have added a potentiometer in order to adjust the gain of the DC amplifier, so you can tune it as per requirement when calibrating the system. The calibration in the code is performed according to the Vref. Here is the real calibration data I have used in mine:
PCB Designs:
I aim to mount the heatsink either on the PCB itself, but using a rectangular PCB and mounting the heatsink on the transistor at the bottom layer is a perfect option. In this way, I can keep the heat away from the PCB and my control section works normally as expected. On the PCB, I have placed the DC inputs on the right side, along with the LDO and decaps. The main transistor is mounted on the back side of the headsink, in the middle.
And all the control parts are with the microcontroller on the right side. I have routed the 2mm for high-power traces and standard 10 mils for all other traces. The main output pads on the front middle side are exposed with larger tinned copper. PCBs do not have any high-speed sections, so there is no need for impedance matching. I used GND copper fills on both sides in order to reduce any noise from the ACS or op-amp. Download the files from here.
Electronic load programming:
A way simpler approach to PWM generation is used from the ESP end. Here is the code (excluding acs712 for now), but once the testing of that version is done, it will be updated here:
// PID controller will be updated soon#include <ESP8266WiFi.h>#include <ESP8266WebServer.h>const char* ssid = "OPPO A15s";const char* password = "987654321v";ESP8266WebServer server(80);/* ---------- HARDWARE ---------- */const int pwmPin = 4; // GPIO4 (D2)const int PWM_MAX = 261;/* ---------- CALIBRATION ---------- */float CAL_SLOPE = 0.00885; // A per PWM (TWEAK THIS)/* ---------- STATE ---------- */bool loadEnabled = false;float setCurrentA = 0.0;int pwmValue = 0;/* ---------- APPLY PWM ---------- */void applyLoad() { if (!loadEnabled) { pwmValue = 0; } else { pwmValue = (int)(setCurrentA / CAL_SLOPE); pwmValue = constrain(pwmValue, 0, PWM_MAX); } analogWrite(pwmPin, pwmValue);}/* ---------- WEB PAGE ---------- */String webPage() { String html = R"rawliteral(<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1"><style>body{font-family:Arial;text-align:center;margin-top:20px;}h2{color:#333;}button{font-size:16px;padding:10px 14px;margin:4px;}input[type=range]{width:80%;}.toggle{font-size:18px;}</style></head><body><h2>ELECTRONIC LOAD V3</h2><p>Status: <b><span id="state">OFF</span></b></p><button class="toggle" onclick="toggle()">ON / OFF</button><hr><p>Set Current: <b><span id="cur">0.00</span> A</b></p><input type="range" id="slider" min="0" max="2.3" step="0.01" oninput="setCurrent(this.value)"><hr><button onclick="preset(0.1)">100 mA</button><button onclick="preset(0.25)">250 mA</button><button onclick="preset(0.5)">500 mA</button><br><button onclick="preset(1.0)">1 A</button><button onclick="preset(1.5)">1.5 A</button><button onclick="preset(2.0)">2 A</button><script>function updateUI(s){ document.getElementById("cur").innerHTML = s.current.toFixed(2); document.getElementById("slider").value = s.current; document.getElementById("state").innerHTML = s.on ? "ON" : "OFF";}function refresh(){ fetch("/state").then(r=>r.json()).then(s=>updateUI(s));}function setCurrent(v){ fetch("/set?i="+v).then(refresh);}function preset(v){ fetch("/set?i="+v).then(refresh);}function toggle(){ fetch("/toggle").then(refresh);}refresh();</script></body></html>)rawliteral"; return html;}/* ---------- HANDLERS ---------- */void handleRoot() { server.send(200, "text/html", webPage());}void handleSet() { if (server.hasArg("i")) { setCurrentA = server.arg("i").toFloat(); setCurrentA = constrain(setCurrentA, 0.0, 2.3); applyLoad(); } server.send(200, "text/plain", "OK");}void handleToggle() { loadEnabled = !loadEnabled; applyLoad(); server.send(200, "text/plain", "OK");}void handleState() { String json = "{"; json += "\"current\":" + String(setCurrentA, 3) + ","; json += "\"on\":" + String(loadEnabled ? "true" : "false") + ","; json += "\"pwm\":" + String(pwmValue); json += "}"; server.send(200, "application/json", json);}/* ---------- SETUP ---------- */void setup() { Serial.begin(115200); pinMode(pwmPin, OUTPUT); analogWrite(pwmPin, 0); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) delay(300); server.on("/", handleRoot); server.on("/set", handleSet); server.on("/toggle", handleToggle); server.on("/state", handleState); server.begin();}/* ---------- LOOP ---------- */void loop() { server.handleClient();}
Server deployment:
Just upload the code through the Arduino IDE, selecting ESP8266. Programming headers are provided on the bottom layer. For coding-related tasks, follow this article: How to program ESP8266 in Arduino IDE. After uploading the code, open the Serial Monitor with a baud rate of 115200. The serial monitor will display an IP address. Open the IP in the browser connected to the same network. The server contains a slider and real-time current values flowing through the output feedback from ACS712 (part under construction). The set value is compared to this output feedback, and PWM is automatically adjusted. In the extras, we have some quick buttons to set the current.
Calibration:
Step 1: For calibration, first note the voltage across the current sense resistors using a multimeter. Use this setup to note the voltage (Vsense); the current should be precisely 1A, controlled by varying Vgs.
Step 2: Note the voltage output from the PWM filter after adjusting the slider on the HTML webpage. Now, set the current to 1A through the slider and note the voltage of the PWM, which we called vREF.
Step 3: Vsense < vREF, and we need to make both voltages equal; that’s what the op-amp does.
**Step 4: **After this step, the setup now works fine, but we can fine-tune the voltages and slope of the current. The code itself provides the calibration number for this here.
Working and Testing:
After all the calibration, it is ready to test. Simply connect the DC barrel jack and start using it. With the power off, the device enters reset mode and starts from zero to address safety issues. We can control the PWM directly from the web server, and the current is monitored there only. Keep in mind that the current sensing resistor wattage should be sufficient to sustain high current.
The maximum allowable current is 2.5A in the web server; it can be increased if a bigger heat sink is used. If the transistor heats too much at the higher current value, derate the maximum current from the slider; otherwise, burns may occur.
Outro:
This device is not particularly precise (only upto a order of magnitude after decimal), but it is good enough for testing hobby circuits. The project is quite simple, and the BOM cost is the lowest for this type of digital control. I have attached the Gerber and all related files here to download and order the PCB. You can use NEXTPCB services for PCB and component sourcing. I have recently updated the PCB to a newer one, which features some changes, including a 12V DC jack instead of a USB-C port. Additionally, adding ACS to the high-side current monitoring.
**Read more