Arduino gets stuck when using large currents

Hi,

I am an Arduino beginner and I realized a circuit to pilot a peltier cell using a n-channel mosfet and monitor the temperature with an NTC.
A crude schematic is attached.
The circuit is soldered to a development board that sits on the Arduino ports (like a shield would do).

As a test, and to avoid draining high currents, I tried the circuit with a resistor 10kOhm instead of the peltier and everything worked fine: I use the analogWrite function to change the PWM pin duty cycle (pin ~3 in the schematic) and see a corresponding change in the voltage on the resistor.

But...

If I try to drive the peltier, which drains up to 2A, I almost immediately lose connection with the Arduino and must reset it (disconnect power and reconnect it).
This happens consistently if I send the "peltRamp" command over the serial monitor (see code below): it works fine with the resistor but crashes the Arduino if the peltier is connected.

I suspect it might be a problem in the faast switching of high currents but could not find any mention of it in the forums. Is anyone able to shed some light on this? I would be really grateful.

#include <SerialCommand.h>
#include <math.h>

SerialCommand sCmd;

// PELTIER VARIABLES
int peltier= 3; //The N-Channel MOSFET is on pin 3
int peltPwr= 0; //Power level to the peltier (0-99%). Start with 0.
int peltier_level = map(peltPwr, 0, 99, 0, 255); //This is a value from 0 to 255 that actually controls the MOSFET
int rampSpeed= 10; //10% per second

// NTC VARIABLES
int tempPin= A0;
float Vout;
float Raux=10000;
float Rout;  // [ohm]  Current NTC resistance
float beta=4235;  // [ohm] Rinf parameter
float Rinf= 0.013558; //[ohm] Rin parameter
float tempC;
float tempK;
float Vin;

/////////////////////////////////////////////////////////////
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  //Serial commands
  sCmd.addCommand("*idn?", sendId);
  sCmd.addCommand("ramp", peltRamp);
  sCmd.addCommand("pwr?", peltRead);
  sCmd.addCommand("peltON", peltON);
  sCmd.addCommand("peltOFF", peltOFF);
  sCmd.addCommand("+pwr", peltIncrease);
  sCmd.addCommand("-pwr", peltDecrease);
  sCmd.addCommand("temp?", readTemp);
}

/////////////////////////////////////////////////////////////
void sendId(){
  Serial.println("PELTIER CONTROLLER");
}

/////////////////////////////////////////////////////////////
void peltRamp(){
  //RAMP THE POWER TO THE SPECIFIED VALUE
  char *arg;
  int newPwr= atoi(sCmd.next());
 
  if (newPwr <0) newPwr=0;
  if (newPwr >99) newPwr=99;
 
  int delta= newPwr - peltPwr;
  float nSteps= 2*abs(delta)/rampSpeed;
 
  for (int i=0; i < nSteps; i++){
      peltPwr= peltPwr + (delta/abs(delta))*rampSpeed/2;
      peltier_level = map(peltPwr, 0, 99, 0, 255);
      analogWrite(peltier, peltier_level); //Write this new value out to the port
      peltRead();
      delay(500);
  }
  peltPwr= newPwr;
  peltier_level = map(peltPwr, 0, 99, 0, 255);
  analogWrite(peltier, peltier_level); //Write this new value out to the port
  peltRead();
}

/////////////////////////////////////////////////////////////
void peltRead(){
  //RETURN THE CURRENT POWER LEVEL
  Serial.print("Power %: ");
  Serial.println(peltPwr);
}

/////////////////////////////////////////////////////////////
void peltON(){
  //SET THE CHANNEL ON (FULL POWER)
  peltPwr= 99;
  peltier_level = map(peltPwr, 0, 99, 0, 255);
  analogWrite(peltier, peltier_level); //Write this new value out to the port
  peltRead();
}

/////////////////////////////////////////////////////////////
void peltOFF(){
  //SET THE CHANNEL OFF (NO POWER)
  peltPwr= 0;
  peltier_level = map(peltPwr, 0, 99, 0, 255);
  analogWrite(peltier, peltier_level); //Write this new value out to the port
  peltRead();
}

/////////////////////////////////////////////////////////////
void peltIncrease(){
  //INCREASE POWER BY 10%
  peltPwr= peltPwr +10;
  if (peltPwr>99) peltPwr= 99;
  peltier_level = map(peltPwr, 0, 99, 0, 255);
  analogWrite(peltier, peltier_level); //Write this new value out to the port
  peltRead();
}

/////////////////////////////////////////////////////////////
void peltDecrease(){
  //DeCREASE POWER BY 10%
  peltPwr= peltPwr -10;
  if (peltPwr<0) peltPwr= 0;
  peltier_level = map(peltPwr, 0, 99, 0, 255);
  analogWrite(peltier, peltier_level); //Write this new value out to the port
  peltRead();
}

/////////////////////////////////////////////////////////////
long readVcc(){
  //Read 1.1V ref against AVcc
  //set the reference to Vcc and the measurement to the internal 1.1V ref
 
  ADMUX= _BV(MUX3) | _BV(MUX2);
  delay(2); //Wait for Vref to settle
  ADCSRA |= _BV(ADSC); //Start conversion
  while (bit_is_set(ADCSRA,ADSC));
  uint8_t low = ADCL;
  uint8_t high = ADCH;
 
  long result = (high<<8) | low;
 
  result = 1125.3L / result; //Caltulate Vcc in V; 1125.3 = 1.1*1023
  return result; // Vcc in mV
}

/////////////////////////////////////////////////////////////
void readTemp(){
  Serial.print("Temperature (C): ");
  Vout  = 0;
  Vin = readVcc();
  for(int i=0; i<15; i++)
  {
    Vout += Vin*((float)(analogRead(tempPin))/1023.0);
    //sample+= analogRead(tempPin); //read the value from sensor
    delay(3);
  }
  Vout = Vout/15;
  Rout= (Raux*(Vin - Vout)/(Vout));
  //Temp calculation
  tempC= (beta/log(Rout/Rinf))-273.15;
  Serial.println(tempC);
}

/////////////////////////////////////////////////////////////
void loop() {
  // put your main code here, to run repeatedly:
  sCmd.readSerial();
}

PeltierSchematic.pdf (89.2 KB)

That's quite a sketch for an Arduino beginner.

Most here would recommend adding a resistor to the gate line, try 220 ohms.
Show us a picture of your actual wiring.
.

Hi LarryD,

thanks for the reply.
I have attached a picture of the assembly I took earlier. It is not very clear but I can take more pictures tomorrow.
I will try to add the resistor but I do not understand why not having it could cause the problem.

About the sketch, I have some experience with programming and I copied parts of it from another project I found online. I am relatively confident the code is fine but you never know. :slight_smile:

Hi,
If you are powering the arduino from the 6 volt battery too i would suspect voltage drop under load. I had the same issues myself. The arduino locks up long before the brownout reset. Try powering arduino separately. If that works get a better power source.
My issue was solved by a big low esr cap on the power line and a battery that doesn't droop under load (lipo)

Hi alka,

I should have mentioned that the Arduino receives power from the USB cable from the PC. The 6V are only connected to the peltier.

It is important that the common ground is wired correctly.
The cell supply ground is connected at one spot, MOSFET source.

Sorry larryD,

I am not sure I understand when you say:

"The cell supply ground is connected at one spot, MOSFET source."

The peltier is connected on one side to the +6V and on the other to the MOSFET drain.

The MOSFET source is connected to both the Arduino ground and to the power supply ground (if this is what you meant).
I checked with a tested and could not find any problem in the grounding: when everything is wired and the power is off, the tester indicates that those three grounds are connected together.

I agree with you though: I have a feeling that the problem is that the ground gets pushed around by the high currents and messes up the Arduino logics.

Google
Star Grounding

https://www.google.ca/search?q=star+grounding&espv=2&biw=1236&bih=545&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwip1-DugoDLAhUQwGMKHX2IB-UQsAQIKQ

The key insight is that any wire carrying switched high currents has (significant) induced voltages along it
due to its inductance.

Thus any wire (supply or ground) carrying such currents should not be in a signal path, nor in the
supply or ground to a logic device like a microcontroller - keep the current paths separate.

This means only a single point in common between Arduino and MOSFET circuit, the MOSFET source.

Use twisted pair for you connection to peltier and for the supply coming into the board - lower inductance,
much less interference to other circuitrs.

Hi MarkT,

yes, I see your (and larryD's) point. The thing is I already made sure that the only point where the grounds converge is on a wide copper path at the edge of the pcb. I do not think there is much else I can do except getting a pcb printed instead of using prototyping boards.

I followed your suggestion of using twisted pairs but it did not help.

I might try to add a capacitor between the 6V and ground, to see if it helps. After that I think I am out of ideas.

Try my suggestion of putting a nice sized cap right on the board where the power line are soldered on. Can make a world of difference.

If the ground goes up from some type of induced voltage then the same type of thing can happen as voltage droop, The arduino is running from the difference between usb voltage (5) and the ground. both situations give a low voltage condition and freeze the arduino.

I may be wrong in this, ( I'm sure Mark will know!)

Hi alka,

thanks, I ordered some 100uF capacitors. I will try your idea as soon as I get them and let you know.

Cheers.

Aha, it seems to work now: I placed a 100 uF capacitor on the 6 V line and the arduino now seems happy even when using different duty cycles.
Thank you very much for the help.

For that current and slow PWM 1000uF+ is more reasonable. Or you could go to higher frequency
PWM (but you might then need a gate-driver chip to get the MOSFET switching efficiently.

Default Arduino PWM frequencies are about 500 and 1000Hz, depending on the pin, 2A will
drain a 100uF capacitor by 20V/ms. Any decoupling is good of course, and the transients are
being handled via the 100uF now.