I am making a solar PV power system PID PWM diversion controller. The charge controller used is a midnite classic 150. I can extract the data needed from the Classic using an arduino uno via rs232 connection. The data is the charge controller mode, battery volts, battery voltage setpoint. I calculate the diversion control setpoint from the battery voltage setpoint. These 4 values I display on a 4X20 lcd. This I have working - mostly due to dgd on the Midnite forum whose initial sketch it was that I have modified.
Now I am trying to get the PID control to work. I have put in the basic PID library ( br3ttb arduino pid library) into the sketch and am using the PID Basic sketch in my sketch. I am trying to modify that sketch to read the values for battery voltage as the PID input and diversion control setpoint as the PID setpoint. I was hoping when I got this working then I would put in timer1 and slow the PWM down to a slower speed to allow for the relay to work correctly. I am not being successful in getting the PID to see the input and or the setpoint. I think this because I am testing the system by connecting it to the relay that I will be using. It has leds that light up when it gets a signal to close. The PID is reverse acting. It increases output to reduce the voltage and decreases to increase the voltage. The relay leds are always on regardless of if the battery voltage is above the setpoint or below it. Ideally the relay would only close when the voltage is above the setpoint.
I have tried many things. I suspected tuning and gave the pid setpoints a long way from desired process to try to effect a change. Nothing happened. I have prowled the web downloading code and trying to find something that works. I have probably 20 interations of sketchs but so far I have not been successful in getting the relay led to turn off when the battery voltage is below setpoint. I gave 0 values of the integral and derivative components to try to eliminate them from the equation but was still not successful. I built functions and tried to use them for the setpoint and input but all I got was lots of error messages that I could not(mostly) understand. The good thing about all this is that I am learning lots however I am beginning to run out of things to try.
I am not a very experienced coder and suspect that I am making a basic error that I am not picking up on. Possibly someone with experience will be able to spot my error relatively quickly. I have put lots of comments into the sketch to make it easy to see what is going on (or not).
Thanks
Will
#include <LiquidCrystal_I2C.h> //lcd
#include <Wire.h> //lcd
#include <SimpleModbusMaster.h> //rs232
#include <PID_v1.h> //pid
#define PIN_OUTPUT 6 //pwm pin
double div_spt; //pid declare diversion setpoint for pid`setpoint
double batt_volts; //pid declare battery volts for pid input
//pid Define Variables we'll be connecting to
double Setpoint;
double Input;
double Output;
//pid Specify the links and initial tuning parameters
double Kp=2, Ki=0, Kd=0;
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, REVERSE);
//rs232 Port information
#define baud 19200
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10
#define TxEnablePin 2 // used to toggle the receive/transmit pin on the driver
#define LED 9
enum
{
PACKET1,
PACKET2,
TOTAL_NO_OF_PACKETS // leave this last entry
};
Packet packets[TOTAL_NO_OF_PACKETS]; // Create an array of Packets to be configured
packetPointer packet1 = &packets[PACKET1];
packetPointer packet2 = &packets[PACKET2];// Create a packetPointer to access each packet
unsigned int readRegsa[21];
unsigned int readRegsb[3];
LiquidCrystal_I2C lcd(0X20,20,4); // set the LCD address to 0X20 for a 20 chars and 4 line display
void setup()
{
lcd.init ();// lcd setup LCD display
lcd.backlight ();
// rs 232 read 20 register starting at address 4114
modbus_construct(packet1, 10, READ_HOLDING_REGISTERS, 4114, 20, readRegsa);
modbus_construct(packet2, 10, READ_HOLDING_REGISTERS, 4243, 2, readRegsb);
// Initialize communication settings:
modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
{
//pid initialize the variables we're linked to
Input = (batt_volts);
Setpoint = (div_spt) ;
//pid turn the PID on
myPID.SetMode(AUTOMATIC);
}
}
void loop()
{
int state ;
double bat_v_spt;
// read the Classic's modbus registers
modbus_update();
// make Classic register values ready for LCD
batt_volts = readRegsa[0];
batt_volts /=10;
state = (unsigned int)readRegsa[5] >> 8; // high byte contains charge state code
bat_v_spt = readRegsb[0];
bat_v_spt /=10;
div_spt = readRegsb[0];
div_spt /=10;
div_spt -=.3;
// top line of lcd
lcd.setCursor(0,0);
switch (state)
{
case 0:
lcd.print ("Resting ");
break;
case 3:
lcd.print ("Absorb ");
break;
case 4:
lcd.print ("BulkMppt ");
break;
case 5:
lcd.print ("Float ");
break;
case 6:
lcd.print ("FloatMppt");
break;
case 7:
lcd.print ("Equalize ");
break;
case 10:
lcd.print ("HyperVoc ");
break;
case 18:
lcd.print ("EqMppt ");
break;
}
lcd.setCursor (0,1);
lcd.print("Batt Volts ") ;
lcd.print(batt_volts);
lcd.setCursor(0,2);
lcd.print("Batt V SetPt ");
lcd.print(bat_v_spt);
lcd.setCursor(0,3);
lcd.print("Divt V SetPt ");
lcd.print(div_spt);
delay (300); // delay to stop LCD flickering, refreshes display 300ms
Input = (batt_volts);
myPID.Compute();
analogWrite(PIN_OUTPUT, Output);
}