Arduino charge controller

So I am using a 10hp engine to run 2 130A car alternators. Regulators have been removed and the Arduino powers both rotors equally. Everything works except the alternator output fluctuates steadily especially once the batteries are near full charge and the alternators are lightly loaded. I am using the ++ and -- increment functions to increase or decrease to output both battery and alternator response is slow compared to the pwm output causing to output to surge up and down.

if (vin < (setPoint - 0.1)) {
        if (voltageAdjust < 220) {      // MAX output from alternators
          voltageAdjust ++;               //increment up
          delay(120);
        }
      }
      if (vin > (setPoint + 0.1)) {           // lower voltage
        voltageAdjust --;                        // increment down
        delay(30);
      }
    }

I have tried to add a neutral point where no output change happens and messed around with adding delays to smooth out fluctuations but no go. There must be a simpler way to track battery voltage and make small adjustments if the voltage is close to the set point and large corrections if the battery voltage is way out.

You might be better having a different control strategy such that the load is shared differently - eg only one takes the load up to a certain point , then the second comes in rather than sharing equally .
With two controlling to the same point I wouldn’t be surprised at some hunting between the alternators occurs.
The other thing is there will be a delay in your control loop so the voltage may over shoot , your controller back it off , the voltage drops, the controller increases and so on. Some form of PID control may sort it . ( the existing delay you have probably makes this worse)

mrdman4224:
I have tried to add a neutral point where no output change happens and messed around with adding delays to smooth out fluctuations but no go. There must be a simpler way to track battery voltage and make small adjustments if the voltage is close to the set point and large corrections if the battery voltage is way out.

Well, how complicated was the original regulator that you removed? Not very. Please show us the entire code for the attempts that you made to make a dead zone (hysteresis) because that is how an electromechanical regulator does it. Also report the behaviour in detail.

The "simpler way" isn't. What you're describing is a PID control loop. It's possible to use one with the Arduino, but it's complicated and I doubt that you need it.

Generally, when hysteresis is working, time delays are not necessary.

Reply #1 reminds me to ask for a system wiring diagram. The issue of battling controllers might be real.

As @aarg says, attache the entire code. I want to see the entire loop() code. I can guess that if the loop is quick it might overreact, make ++, or --, several times, or at least a few more than needed.

This is my first big arduino project so im sure the code could be cleaned up a bit. Basically you push the power button sending power to a relay to power up arduino then pin 2 holds the relay on. Pushing charge button slowly raises alternator output to avoid belt slippage, once up to full voltage I want a quicker response so the alternators don't under/overshoot if the load changes.
Pushing equalize button does the same thing but set point is raised to 15.5V, pushing equalize button again raises set point to 16.2V, pushing again turns output off.
Shut down button writes pin 2 low which releases power relay shutting the system off.

float setPoint = 14.80; //set battery charge voltage to 14.8V (15V)(15.8 - 16.2)Eq
int analogInput = 0;
float vout = 0.0;
float vin = 0.0;
float R1 = 103500.0; // resistance of R1 (100K)
float R2 = 10000.0; // resistance of R2 (10K)
int value = 0;
int voltageAdjust = 0; //declair variable
const int charge_button = 5;       // the number of the pushbutton pin
const int equalize_button = 6;     // the number of the pushbutton pin
const int shutDown_button = 7;     //shutdown button pin
const int charge_led =  10;        // the number of the LED pin
const int equalize_low_led =  11;  // the number of the LED pin
const int equalize_high_led = 12;  // equalize high led pin

int buttonState_charge = 0;         // variable for reading the charge button status
int buttonState_equalize = 0;       // variable for reading the equalize button status
const unsigned long interval = 500;  // SETS DELAY TIME FOR BUTTONS 
unsigned long previousTime = 0;     // delay since last

void setup() {
  
  pinMode(analogInput, INPUT); //battery sense input
  pinMode(2, OUTPUT); digitalWrite (2, HIGH);   //energize power relay latch
  pinMode(3, OUTPUT); //PWM output to pin 3 for alternator field windings
  pinMode(13, OUTPUT); digitalWrite(13, HIGH); //power led
  pinMode(charge_led, OUTPUT);
  pinMode(equalize_low_led, OUTPUT);
  pinMode(equalize_high_led, OUTPUT);
  pinMode(charge_button, INPUT_PULLUP);
  pinMode(equalize_button, INPUT_PULLUP);
  pinMode(shutDown_button, INPUT_PULLUP);
}

void loop() {
 
  analogWrite(3, voltageAdjust);   //provide PWM at PIN3
  
  // Read voltage of the battery bank
  value = analogRead(analogInput); // read the value at analog input
  vout = (value * 5.0) / 1024.0; // converts analog signal to voltage
  vin = vout / (R2 / (R1 + R2)); // converts voltage to 12V scale

  //---- Determine button states ----

  unsigned long currentTime = millis();         //millis to debounce buttons (delay)
  if (currentTime - previousTime >= interval) {

    // Charge Button
    int chargebtn = digitalRead(charge_button);  //read charge button
    if (chargebtn == LOW) {
      buttonState_charge ++;          //if button pushed then increment up function
      voltageAdjust = 0;              //return pwm output to 0 at start up
      buttonState_equalize = 0;       // set equalize off
      if (buttonState_charge >= 2) {  // return to 0 
        buttonState_charge = 0;
      }
    }
    //Equalize Button
    int equalizebtn = digitalRead(equalize_button);  //read equalize button
    if (equalizebtn == LOW) {
      buttonState_equalize ++;          // if button pushed then increment up
      voltageAdjust = 0;                //return pwm output to 0 at start up
      buttonState_charge = 0;           // set charge off
      if (buttonState_equalize >= 3) {  //return to 0
        buttonState_equalize = 0;
      }
    }
    //Shut Down Button
    if (digitalRead(shutDown_button) == LOW) {    //cuts power to latch relay shutting system off
      digitalWrite (2, LOW);
    }
    previousTime = currentTime;   // resets millis timer
  }


  // ---- Functions ----

  //Charge Function
  if (buttonState_charge == 0) {   // off
    digitalWrite (charge_led, LOW);
  }
  if (buttonState_charge == 1) {   // charge
    digitalWrite (charge_led, HIGH);  //charge led on
    setPoint = 14.85;
    if (vin < 14.50) {
      if (voltageAdjust < 220) {      // MAX POWER OUT FROM ALTERNATORS (1 - 255)
        voltageAdjust ++;             // increase alternator output slowly
        delay(200);
      }
    }
    else if (vin >= 14.50) {          // once up to voltage adjust quickly
      if (vin < (setPoint - 0.1)) {
        if (voltageAdjust < 220) {      // MAX output from alternators
          voltageAdjust ++;
          delay(30);
        }
      }
      if (vin > (setPoint + 0.1)) {           // lower voltage
        voltageAdjust --;
        delay(30);
      }
    }
  } //end button state 1

  //Equalize Function

  if (buttonState_equalize == 0) {        //off
    digitalWrite (equalize_low_led, LOW);
    digitalWrite (equalize_high_led, LOW);
  }
  if (buttonState_equalize == 1) {        //Equalize LOW Function
    digitalWrite (equalize_low_led, HIGH);
    setPoint = 15.20;
    if (vin < 15.20) {
      if (voltageAdjust < 190) {      // MAX POWER OUT FROM ALTERNATORS (1 - 255)
        voltageAdjust ++;             // increase alternator output slowly
        delay(200);
      }
    }
    else if (vin >= 15.20) {          // once up to voltage adjust quickly
      setPoint = 15.50;
      if (vin < (setPoint - 0.05)) {
        if (voltageAdjust < 220) {      // MAX output from alternators
          voltageAdjust ++;               // raise voltage
          delay(30);
        }
      }
      if (vin > (setPoint + 0.5)) {           // lower voltage
        voltageAdjust --;
        delay(30);
      }
    }
  } // end button state equalize = 1

  // Equalize High Function

  if (buttonState_equalize == 2) {        //Equalize HIGH Function
    digitalWrite (equalize_low_led, LOW); //low led off
    digitalWrite (equalize_high_led, HIGH); //high led on
    if (vin < 16.00) {
      if (voltageAdjust < 190) {      // MAX POWER OUT FROM ALTERNATORS (1 - 255)
        voltageAdjust ++;             // increase alternator output slowly
        delay(200);
      }
    }
    else if (vin >= 16.00) {          // once up to voltage adjust quickly
      setPoint = 16.20;
      if (vin < (setPoint - 0.05)) {
        if (voltageAdjust < 220) {      // MAX output from alternators
          voltageAdjust ++;               // raise voltage
          delay(30);
        }
      }
      if (vin > (setPoint + 0.05)) {           // lower voltage
        voltageAdjust --;
        delay(30);
      }
    }
  } // end button state equalize = 2






} //end loop

The problem with the original regulators in the alternators was:
-at start up both alternators would jump to full load and stall the engine
-one alternator would always run hotter as the load was never equally shared, shortening its lifespan as they aren't designed to run full load for very long
-they topped out at 13.6V, I need 14.8V for proper charging and 16.2V for equalization charge

By running 2 alternators off the same mosfet I can run both of them at 70% load instead of 100%/20%. Adjustable programming gives me lots of flexability to tailor the charger to my battery bank.

Setup:
12 Volt, 1200Ah battery bank running an off grid solar powered house.
Alternators are identical 130A, 12V. Regulators have been removed, constant 12V supplied to rotors from power relay, - supplied from NPN mosfet.
Switched power supply delivering 7V to Arduino from 12V battery bank (first Arduino didn't like 16V lol)
PWM output runs a single NPN mosfet feeding both rotor field windings from 12V battery bank.
Voltage sense is a voltage divider on the battery bank side to avoid issues with voltage drop between dc generator and batteries to get accurate readings.

System works good I just need to figure out a way of smoothing out the hunting and surging due to the arduino over compensating. At start up the controller smoothly increases power output up to my max set point. As the batteries charge the power output from alternators decreases but the surging slowly increases. Near end of charge is the worst with +/- 0.4V of fluctuation, it helps a little to idle the engine down causing the PWM to run at a higher output to compensate for slower shaft speed but the fluctuations are still there. Increasing delays helps smooth it out but causes overshoot voltage spikes when a load is removed from battery bank.

Since R1 and R2 are constant You can calculate this once and for all. Floating point aritmethics is heavy and downloads the controller.

  vout = (value * 5.0) / 1024.0; // converts analog signal to voltage
  vin = vout / (R2 / (R1 + R2)); // converts voltage to 12V scale

loop() is running very fast as I can see. The strengthens my idea that You might perform several ++/-- instead of one.
Use Serial.print to print out millis() when ++ is executed.

Serial.print("++ ");Serial.println(millis());

Railroader:
Since R1 and R2 are constant You can calculate this once and for all. Floating point aritmethics is heavy and downloads the controller.

  vout = (value * 5.0) / 1024.0; // converts analog signal to voltage

vin = vout / (R2 / (R1 + R2)); // converts voltage to 12V scale

It is much better form to follow this advice. However, in reality, the compiler is smart enough to trace the expression back to constants and replace the expression with a floating point constant. It would see that R1 and R2 are only assigned a value once. However to enhance the chances of success, it would be better to declare them as constants:

const float R1 = 103500.0; // resistance of R1 (100K)
const float R2 = 10000.0; // resistance of R2 (10K)

Then the expression can remain in place for the sake of readability.

@aarg. I've never trusted compilers…. Joking! In this case, I believe You but I have experienced compiler capacity to be something else than what I've hoped for.
Regarding the fact that the origin of all action is the 10 digit analog read the entire math cold be done using int, if more speed is wanted. Using PID, as OP has in mind, maybe it would have a value.

@OP. Have You performed any testprintings from inside loop()?

I believe that expressions containing only explicit constants will always be optimized by GCC.

I did something like integer numerals multiplied, like "long var = 200 * 400". It didn't work…. The multiplicatiion was done as 2 integers, loosing data, and stored into the long var.
Why use such code? For ease, calculate the distance for a stepper, 400 steps per mm, supposed to move 200 mm…
As long as the product was less then 32565, or unsigned, 65535, the formula worked…..