Jasper’s Electric Load

This is just a direct copy of Jasper’s Electric Load website, which was mentioned on Hackaday on April 29, 2014. It uses an Arduino to control a MOSFET load in constant current, constant power or constant resistance mode. I was more interested in the hardware than the software but I’ve copied everything here for posterity.

Summary: I designed an electric load. Using an Arduino Nano, the load can be programmed, and the voltage and current are measured. You can set a constant current (CC), a constant power (CP), or a constant resistance (CR) load by simply typing it in to the Arduino Serial Monitor. The circuit is designed for up to 30V, 5A, and 15W. An opamp, a mosfet, and a small sense resistor form the constant current circuit. The current is set using a DAC. Two other opamps measure the power supply voltage and the current. The circuit is powered from the Arduino USB voltage. I reflow soldered the board using the hacked toaster oven at the hackerdojo. Here are pictures of the reflow soldering process.

Hardware

  • Custom designed PCB ($23)
  • Arduino Nano board ($12)
  • 2x 15pins 0.1” pitch female header connectors ($2)
  • AD8608 Rail-to-rail opamps ($3)
  • MCP4725 DAC ($3)
  • IRLZ44Z N-channel MOSFET ($2)
  • SK 129 38mm Heat sink ($1)
  • 0603 resistors and capacitors ($2)
  • screw terminal ($1)

I chose to use an Arduino Nano board because it is small, cheap, easily interchangeable, it has a power supply that can be used to supply other circuits, and it can easily be programmed with the Arduino IDE. The Arduino is placed on female header connectors on the board. I chose to use the same DAC as on Adafruit and Sparkfun DAC breakout boards. The DAC can be supplied from 5V and the the output voltage is rail-to-rail. A description for using the MCP4725 DAC and library with Arduino can be found here on the Adafruit website. The DAC connects to the Arduino using I2C.

Opamp IC1D, mosfet Q1, and the 0.1 Ohm sense resistor form the constant current circuit. I chose the AD8608 opamp because it is a quad opamp, it can be supplied from 5V, and it has rail-to-rail voltage output. The mosfet functions as a voltage dependent resistance, and in the linear range. I chose a popular and widely available mosfet with a gate threshold voltage between 2.5 and 3V for currents up to 5A. I found the heat sink in a local electronics shop for $1. The mosfet, the heatsink, and their interface, have a combined thermal resistance of about 9 degrees Celsius per Watt. The maximum operation temperature of the mosfet is 175 degrees Celsius. This means that, at 25 degrees Celsius ambient temperature, maximal 15 Watt can be dissipated without active cooling.

To set a current of 1A, the DAC must output 1V. This voltage is divided by 10 using voltage divider R6 and R7 and this 0.1V is applied to the plus input of the opamp. The opamps adjusts it output voltage until it measures the same potential on it minus input, the feedback voltage from the sense resistor. That’s the case when a current of 1A runs through the 0.1 Ohm sense resistor. The capacitor (C4) value in the RC filter may be changed to a suitable cut off frequency, or left out. R5 and C1 are designed to prevent the opamp from oscillating, and the values were taken from a proven design.

Opamp IC1B is used to measure the voltage of the power supply. R3 and R4 divide the supply (30V max) voltage by 6, so that it falls within 0-5V range of the 12 bit ADC of the Arduino. The opamp is simply used to buffer the voltage. The capacitor (C2) value in the RC filter may be changed to a suitable cut off frequency.

IC1C is used to measure the actual load current. The voltage over the sense resistor is amplified 10 times, by the opamp, and then connected to the 12 bit ADC pin of the Arduino. The capacitor (C3) value in the RC filter may be changed to a suitable cut off frequency.

Calibration need to be done because of tolerance in components and the USB supply voltage. Step 1: ADC readings depend on the Arduino USB supply voltage. Which is not always 5V. Here https://code.google.com/p/tinkerit/wiki/SecretVoltmeter I found code to measure the Arduino voltage rail. The Arduino power supply voltage is derived from an internal 1.1V reference and then back-calculated. In the “SecretVoltmeter"code I adjusted the 1126400L to 1106706L to match the back-calculated 5V to the actual measured voltage. Step 2: I multiplied load voltage ADC reading with a factor to match it with my multimeter. Step 3: I multiplied load current ADC reading with a factor to match it with my multimeter. Step 4: I multiplied DAC current setting with a factor to match it my multimeter. The accuracy of the voltage and current reading is limited mostly by the 10 bit AD converter. However the current is set with a accuracy of 1.2 mA/bit, which is sufficient to be able to test power supplies.

I ordered the 3 PCBs from OSHPark for just $25. The boards can be re-ordered here. A solder stencil was ordered from OSHstencils.

Schematic in Eagle and PDF. Layout in Eagle and PDF. Gerber file for PCB. BOM in XLS. Gerber file for Stencil.

There are definitely improvements to be made. One is reverse voltage and over voltage protection of the opamps. Small signal diodes could be used to limit the opamp input voltage between 5V and GND. A second improvement is over load protection. The software should be capable of detecting when the voltage drops too much and reduce the current lineairly. A third improvement is changing the code to apply a pulsed load.

Software

  1/*
  2  Electric Load Modes: 
  3 - CC constant current load
  4 - CP constant power load
  5 - CR constant resistance load
  6 Current output, voltage input, and current input calibrated
  7 Voltage measurement 10bit 0-30V: 30mV/bit
  8 Current measurement 10bit 0-5A : 5mA/bit
  9 Power calculation              : 60mW/bit
 10 Current setting 12bit 0-5A:      1.2mA/bit
 11 */
 12
 13#include <Wire.h>
 14#include <Adafruit_MCP4725.h>
 15Adafruit_MCP4725 dac;
 16
 17int LedPin      = 13;
 18int VoltPin     = A1;
 19int CurrentPin  = A0;
 20
 21int VoltPinValue,CurrentPinValue,int_value;
 22float VoltPinVoltage,FltVoltPinValue,FltCurrentPinValue,LdVoltage,LdCurrent,LdPower,IntVolt;
 23long previousMillis = 0;      // 
 24int ledState = LOW;           // ledState used to set the LED
 25long interval = 500;          // interval to to flash LED
 26long ArduinoVccInmV;          // Arduino Vcc voltage
 27float SetCurrCalFactor = 1.006;// calibration multiplication factor
 28
 29static char line[30];
 30boolean haveLine, ret;
 31char c;
 32
 33void setup() {
 34  Serial.begin(9600);           // setup serial
 35  dac.begin(0x62);              // DAC address with A0 connected to GND
 36  pinMode(LedPin, OUTPUT);      // Set LedPin as Output
 37  int value=0;                  // from 0 to 4095 is from 0V to 5V
 38  int storeflag=false;           // store value in EEPROM (max 20,000 times)
 39  dac.setVoltage(value, storeflag); // set the load to default value
 40  Serial.println("Type 'cc100' to set 100mA, 'cp100' for 100mW, or 'cr100' for 100 Ohm");
 41}
 42
 43long readVcc() { // from https://code.google.com/p/tinkerit/wiki/SecretVoltmeter
 44  int result;
 45  ADMUX=_BV(REFS0)|_BV(MUX3)|_BV(MUX2)|_BV(MUX1);// Read 1.1V reference against AVcc
 46  delay(2); // Wait for Vref to settle
 47  ADCSRA |= _BV(ADSC); // Start conversion
 48  while (bit_is_set(ADCSRA,ADSC)); // measuring
 49  result = ADCL;
 50  result |= ADCH<<8;
 51  //Original code below
 52  //result = 1125300L/ result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
 53  //Calibrated code below
 54  result = 1106706L/ result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000*(4940/5023)
 55  return result; // Vcc in millivolts
 56}
 57
 58void ReadVoltAndCurr(){
 59  VoltPinValue = analogRead(VoltPin);        // read value from voltage pin
 60  CurrentPinValue = analogRead(CurrentPin);  // read value from current pin
 61  FltVoltPinValue= float(VoltPinValue);      // convert VoltagePinValue to float
 62  VoltPinVoltage = (FltVoltPinValue/1023.0)*IntVolt; // calculate voltage on voltage pin
 63  LdVoltage = 6.030* VoltPinVoltage;         // calculate load voltage, calibrated
 64  Serial.print(LdVoltage);                   // print load voltage
 65  Serial.print(" V, ");                      // print load voltage
 66  FltCurrentPinValue= float(CurrentPinValue);// convert CurrentPinValue to float
 67  LdCurrent = (FltCurrentPinValue/1023.0)*IntVolt*1.034;// calculate load current
 68  Serial.print(LdCurrent);                  // print load current
 69  Serial.print(" A, ");                     // print load current  
 70  LdPower = LdVoltage * LdCurrent;          // calculate load power
 71  Serial.print(LdPower);                    // print load power
 72  Serial.print(" W");                       // print load power
 73  if(LdCurrent>5){                        // warn if power is too high
 74    Serial.print("Current to high!");
 75  }  
 76  if(LdVoltage>30){                        // warn if power is too high
 77    Serial.print("Voltage to high!");
 78  }
 79  if(LdPower>15000){                        // warn if power is too high
 80    Serial.print("Power too high for heatsink!");
 81  }
 82}
 83
 84// CONSTANT CURRENT MODE
 85void CC(int DesiredmA){    // DesiredmA in mA
 86  Serial.print("cc, ");    
 87  ReadVoltAndCurr();                              
 88  Serial.println("");
 89  float value=(DesiredmA/5000.0)*4095.0*SetCurrCalFactor; // calculate value to set the right current
 90  int_value=int(value);                               // convert float to integer
 91  dac.setVoltage(int_value, false);                   // value from 0 to 4095
 92}
 93
 94//CONSTANT POWER MODE
 95void CP(int DesiredmW){
 96  Serial.print("cp, ");
 97  ReadVoltAndCurr();
 98  float SetmA=DesiredmW/LdVoltage;
 99  Serial.println("");
100  float value=(SetmA*4095*SetCurrCalFactor)/5000;
101  int_value=int(value);
102  dac.setVoltage(int_value, false); // value from 0 to 4095
103}
104
105//CONSTANT RESISTANCE MODE
106void CR(int Resistance){
107  Serial.print("cr, ");
108  ReadVoltAndCurr();
109  float SetmA=LdVoltage/Resistance;
110  Serial.println("");
111  float value=(SetmA*4095*SetCurrCalFactor)/IntVolt;
112  int_value=int(value);
113  dac.setVoltage(int_value, false); // value from 0 to 4095
114}
115
116bool EditLine(char cin, char *cout, char line[], int size)
117{
118  static int pos = 0;
119  *cout = cin;// echo by default
120  switch (cin) {// carriage return is ignored
121  case '\r':
122    break;
123  case '\n':  // end-of-line
124    line[pos] = '\0';
125    pos = 0;
126    return true;
127  case 0x7F:
128  case 0x08:// backspace
129    if (pos > 0) {
130      pos--;
131    }
132    break;
133  default:
134    if (pos < (size - 1)) {// store char as long as there is space to do so
135      line[pos++] = cin;
136    } 
137    else {
138      *cout = 0x07; // bell
139    }
140    break;
141  }
142  return false;
143}
144
145void loop() {
146  ArduinoVccInmV = readVcc();     // returns Arduino internal voltage in mV
147  IntVolt = ArduinoVccInmV/1000.0;// changes mV to Volt
148
149  unsigned long currentMillis = millis();        // flashes a LED
150  if(currentMillis - previousMillis > interval) {// as long as LED is flashing program runs
151    previousMillis = currentMillis;   
152    ledState = !ledState;
153    digitalWrite(LedPin, ledState);
154  }
155
156  while (Serial.available() && !ret) {// read from serial until return
157    haveLine = EditLine(Serial.read(), &c, line, sizeof(line));
158    Serial.print(c);
159  }
160
161  if (haveLine) {
162    if (strncmp(line, "cc", 2) == 0) {
163      int value = atoi(line+2);
164      CC(value);
165    }
166    if (strncmp(line, "cr", 2) == 0) {
167      int value = atoi(line+2);
168      CR(value);
169    }
170    if (strncmp(line, "cp", 2) == 0) {
171      int value = atoi(line+2);
172      CP(value);
173    }
174  }// end of if (haveLine)
175
176}//end of void loop()

electronicload SCHEMATIC.pdf (35.2 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

electronicload LAYOUT TOP.pdf (71.8 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

electronicload.sch (339 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

electronicload.brd (88.9 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

electronicload GERBER.zip (44.8 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

electronicload GERBER STENCIL.zip (1.39 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

electronicload BOM.xls (30.5 KB) Andrew Kohlsmith, 04/29/2014 10:54 PM

picture646-1.png (169 KB) Andrew Kohlsmith, 04/29/2014 10:55 PM

picture646-2.png (237 KB) Andrew Kohlsmith, 04/29/2014 10:55 PM

picture646-3.png (16.3 KB) Andrew Kohlsmith, 04/29/2014 10:55 PM

picture646-4.png (18.3 KB) Andrew Kohlsmith, 04/29/2014 10:55 PM

Add picture from clipboard (Maximum size: 1 GB)