[SOLVED] Problem with power supply - Voltage drop

Hey guys!

So, I'm building a 3D printer module for a CNC Mill and I decided to make the heating for the hotend with Arduino and a transistor. I am powering everything with an ATX power supply and I wired everything as shown below.

Now imagine I turn SW3 on (it makes the output go to the transistor witch turns the heater on) with a setpoint of 200°C, the temperature starts going up and up, until it gets to around 195°C. My problem is, if I turn the heating off (either by lowering the setpoint or flipping the switch) the temperature immediately jumps up 5 to 10 degrees. If I turn it back on, the temperature goes back down by the same 5 to 10 degrees. Since the Arduino calculates the temperature based on the voltage on pin A0, I suppose there's some voltage drop happening when I turn the heater on.

I measure the voltage on the 5V pin and ground and indeed I got ~4.70V when the heater is off and ~4.20V when it's on. For the 3.3V pin (which is the one the thermistor uses) I got ~3.10V and ~2.70V.

I'm pretty sure this was not happening before, it started a couple days ago, and I don't remember changing anything before then. I might be wrong though. If anyone knows what might be going on I'd appreciate if you could help me.

This is the information on the power supply (sorry for the bad picture):

And this is my code:

#include <PID_v1.h> 
#include <LiquidCrystal.h> 
#include <Encoder.h> 

double Setpoint, currentTemp, Output;
const float kP = 5;
const float kI = 0.01;
const float kD = 8;
PID myPID(&currentTemp, &Output, &Setpoint,kP,kI,kD, DIRECT);
LiquidCrystal lcd(12, 11, 7, 6, 5, 4);

const int buttonPin = 8;
const int encoder1Pin = 2; 
const int encoder2Pin = 3;
const int thermistorPin = A0; 
const int transistorPin = 10; 
const int ledPin = 9; 
const int switchPin = 13; 

Encoder myEnc(encoder1Pin, encoder2Pin);

long oldPosition = -999;
long newPosition;

boolean buttonPress;
int buttonState = 0; 
int thermistorMax = 260; 
int thermistorMin = 20; 

#define resistorResistence 988 
const int numSamples = 5;
int samples[numSamples];
const int SAMPLETIME = 200;
int nextUpdate = 0;

void setup() {
  analogReference(EXTERNAL);
  pinMode(transistorPin, OUTPUT);
  pinMode(thermistorPin, INPUT);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(encoder1Pin, INPUT_PULLUP);
  pinMode(encoder2Pin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
  pinMode(switchPin, INPUT);
  Setpoint = 195;
  myPID.SetMode(AUTOMATIC); //ligar PID
  myPID.SetSampleTime(SAMPLETIME);
  TCCR1B = TCCR1B & B11111000 | B00000100;

  lcd.begin(16, 2);
  lcd.home();
  lcd.print("Controlador");
  lcd.setCursor(5,1);
  lcd.print("Temperatura");
  delay(2000);
  lcd.clear();

  Serial.begin(9600);

  delay(100);
  lcd.setCursor(0,0);
  lcd.print("SetPoint:");
  lcd.setCursor(0,1);
  lcd.print("Extrusor:");
  delay(100);
}

void loop() {
  buttonPress = digitalRead(buttonPin);
  if (buttonPress == 0) {
    if (buttonState == 0) {
      buttonState = 1;  
      while (buttonPress == 0) {  
        buttonPress = digitalRead(buttonPin);
        delay(100);
      }
    }
    else {
      buttonState = 0;
      while (buttonPress == 0) { 
        buttonPress = digitalRead(buttonPin);
        delay(100);
      }
    }
  }
  newPosition = myEnc.read();
  if (newPosition > oldPosition) {
    if (Setpoint >= thermistorMax) { 
      lcd.setCursor(11,0);
      lcd.print(" MAX ");
      Setpoint = thermistorMax;
      delay(1000);
    }
    else {
      if (buttonState == 0) {
        Setpoint += 5;
      }
      else {
        Setpoint += 1;
      }
    }
    delay(100);
    oldPosition = myEnc.read();
    lcd.setCursor(11,0);
    if (Setpoint < 99.5) {
      lcd.print(" ");
    }
    lcd.print(Setpoint); 
    lcd.setCursor(14,0);
    lcd.print(char(223));
    lcd.println("C");
  }
  else if (newPosition < oldPosition) { 
    if (Setpoint <= thermistorMin) { 
      lcd.setCursor(11,0);
      lcd.print(" MIN ");
      Setpoint = thermistorMin;
      delay(1000);
    }
    else {
      if (buttonState == 0) {
        Setpoint -= 5;
      }
      else {
        Setpoint -= 1;
      }
    }
    delay(100);
    oldPosition = myEnc.read();
    lcd.setCursor(11,0);
    if (Setpoint < 99.5) {
      lcd.print(" ");
    }
    lcd.print(Setpoint); 
    lcd.setCursor(14,0);
    lcd.print(char(223));
    lcd.println("C");
  }
  currentTemp = getThermistor(); 
  if (currentTemp < -25) {
    Output = 0;
    lcd.setCursor(11,1);
    lcd.print("ERRO ");
  }
  else {
    lcd.setCursor(11,1);
    if (currentTemp < 99.5) {
      lcd.print(" ");
    }
    lcd.print(round(currentTemp)); 
    lcd.setCursor(14,1);
    lcd.print(char(223));
    lcd.println("C");
    if (digitalRead(switchPin) == LOW) {
      Output = 0;
    }
    else {
      myPID.Compute();
    }
  }
  if (Output > 0) {
    digitalWrite(ledPin, HIGH);
  }
  else {
    digitalWrite(ledPin, LOW);
  }
  analogWrite(transistorPin, Output); 
}

float getThermistor() {    
  uint8_t i;
  float average;
  int ms = millis(); 
  if (ms >= nextUpdate) { 
    for (i=0; i<numSamples; i++) {
      samples[i] = analogRead(thermistorPin);
      delay(10);
    }
      average = 0;
    for (i=0; i<numSamples; i++) {
      average += samples[i];
    }
    average /= numSamples; 

    average = 1023 / average - 1;         // (1023/ADC - 1) 
    average = resistorResistence / average;     // Rr / (1023/ADC - 1)

    // 1/T = A + B * ln(R) + C * ln(R)^3
    
    const float Acoef = 0.00069881617;
    const float Bcoef = 0.00021539301;
    const float Ccoef = 0.00000011493814;

    float steinhart;
    float lnR = log(average);
    steinhart = Ccoef * pow(lnR, 3);
    steinhart += Bcoef * lnR;
    steinhart += Acoef;
    steinhart = 1.0 / steinhart;
    steinhart -= 273.15;
  
    Serial.print(average);
    Serial.print(" ");
    Serial.print(steinhart);
    Serial.print(" ");
    Serial.print(Setpoint);
    Serial.print(" ");
    Serial.println(Output);
   
    nextUpdate = ms + 500;
    
    return steinhart;
  }
}

Heater current shouldn't run over the same 12volt/GND wires as the Arduino/sensor.
So how/where did you connect heater supply and heater ground (source of fet).

Conputer supplies are AFAIK only regulated on the 5volt line.
The 12volt outputs are just a secondary supply tap.
Have a look at the voltage drop on the 5volt output of that supply.
It might be better to power the Arduino with 5volt directly (hacked USB lead).
Leo..

Edit:
int ms = millis(); // all millis() related values should be unsigned long

D10
Check voltage changes on D10, it is look like mosfet is not turned off but only A0 - temperature control function

Even if the 12v varies quite a bit the 3v3 regulator should still be accurate.

I'd look at common earth problems, as Leo suggests. Run the source of the MOSFET and the gnd of the uno independently to the 0v of the 12v supply.

It may also be worth adding a capacitor - 100nF? across the thermistor.

And take an average of several readings..

Allan

Thanks for the answers guys,

So, the ground from the board, the source pin on the mosfet and the fan are going to the ATX ground. All the others are going to a GND pin on the board.

So how/where did you connect heater supply and heater ground (source of fet).

The heater supply comes from a 12V source on the ATX.

It might be better to power the Arduino with 5volt directly (hacked USB lead).

I thought it was kind of mandatory to power the Arduino with more than 5V? Should I put the 5V wire from the ATX directly on the VIN pin then (if there is no drop)?

I'd look at common earth problems, as Leo suggests. Run the source of the MOSFET and the gnd of the uno independently to the 0v of the 12v supply.

I'll try that. You mean I should use a completely unrelated ground, right?

So, the ground from the board, the source pin on the mosfet and the fan are going to the ATX ground. All the others are going to a GND pin on the board

I hope those two ground systems are connected together somehow or it will never work.

You mean I should use a completely unrelated ground, right?

No.

See http://www.thebox.myzen.co.uk/Tutorial/Power_Supplies.html

gian_nichele:
So, the ground from the board, the source pin on the mosfet and the fan are going to the ATX ground. All the others are going to a GND pin on the board.
The heater supply comes from a 12V source on the ATX.

I thought it was kind of mandatory to power the Arduino with more than 5V? Should I put the 5V wire from the ATX directly on the VIN pin then (if there is no drop)?

I'll try that. You mean I should use a completely unrelated ground, right?

What I meant is if the heater is using it's own yellow/black pair of power wires.

Read the Uno's product pages. Here's the short/dirty version.
USB power is 5volt, powering on the 5volt pin is possible, but not recommended (if you don't know what you're doing), V-in needs a minimum of 6.6volt, and the DC socket needs a minimum of 7,5volt.

There is no isolated ground in an ATX supply. All black wires run to the same star ground inside the supply.
Leo..

Thanks for all the replies guys. I tried all your suggestions and a few more.

After much exhaustion and a blown out mosfet I got it! Turns out Switch 3 was the problem all along. I took it out of the equation and it runs smoothly now!

Bonus question: Is there a better way to make the Arduino detect if a 2 pin switch is on or off? (pushing my luck here, sorry)

Thanks

int ms = millis(); // all millis() related values should be unsigned long

If I make it an 'unsigned long', it simply freezes after a couple minutes. As just a 'long' it is just ignored all together and the routine runs on the same frequency as the rest. As an 'int' it is working as expected, every 500 ms, so I guess I'll not mess with what (seemingly) ain't broke :relaxed:

so I guess I'll not mess with what (seemingly) ain't broke

Appearances are deceptive, using an int is wrong.

float getThermistor() {    
  uint8_t i;
  float average;
  int ms = millis();

So ms is defined in that function therefore when you go

    nextUpdate = ms + 500;
    
    return steinhart;

The value of nextUpdate is not updated correctly. It is also an int so it will not hold the right information.

You're right, this time I forgot to change NextUpdate as well. It's working fine now.

Although I don't see the problem with using int. The counter would always reach 32767 and start again from -32768, and repeat. Anyway, I guess this works better. Thanks Mike

Although I don't see the problem with using in

The problem with using int and the way of generating the next calling time is that the numbers go negitave for half the time. If you were using an unsigned int and subtracting the current time from the start time and comparing the result to a required time their would be no problem but you are not.

Using the method you do would only result in a problem every 40days or so if you used a long, but with an int it is very much shorter. I will leave it a s an exercise for you to work out how long that int is in the negitave region.

I suppose you could use abs()... ?

but the standard method is fine.

Allan

allanhurst:
I suppose you could use abs()... ?

Well no. If you did then the millis time would appears to go backwards.

Grumpy_Mike:
The problem with using int and the way of generating the next calling time is that the numbers go negitave for half the time. If you were using an unsigned int and subtracting the current time from the start time and comparing the result to a required time their would be no problem but you are not.

Using the method you do would only result in a problem every 40days or so if you used a long, but with an int it is very much shorter. I will leave it a s an exercise for you to work out how long that int is in the negitave region.

(Sorry for the late response)

As I said before, it works just fine when it's in the negative region. The only problem that happens is that it doesn't work for 500 milliseconds every 65 seconds, but all that does is just update the temperature a little bit faster for half a second. I can see how that could be a problem in some applications though.

Anyway, I'm sticking with unsigned long now since that works best.