// Version 1.1 // fixed the backwards current through the BATTERY MOSFET // by interposing the charging mosfet..... //////////////////LIBRARIES TO INCLUDE///////////////////////////////// // include the library for the Liquid Crystal display #include // standard lib for liquid crystal #include // basic Arduino functions #include // Allows us to have a watchdog timer /////////////ARDUINO OUTPUTS/////////////////////////////////////////////// #define ACSUPPLYPIN 2 // D2 controls the AC supply MOSFET #define BATTSUPPLYPIN 7 // D7 controls the BATTERY MOSFET #define CHARGER 3 // D3 controls the CHARGER MOSFET // NOTE: D3 is a PWM output //////////////////ARDUINO INPUTS/////////////////////////////////////////////////////// #define BATTCURRENT A0 // A0 reads the charging current in the battery #define RADIOCURRENT A6 // A6 reads the radio current consumption #define ACSUPPLYVOLTAGEPIN A7 // A7 reads a divided down version of the AC supply voltage #define BATTSUPPLYVOLTAGEPIN A1 // A1 reads a divided down version of the battery supply voltage //////////////////TESTING OUTUPUTS//////////////////////////////////////////////// #define VOLTAGEOUTPUTS ////////////////////CONTROL CONSTANTS///////////////////////////////////////////// #define MILLISECONDINTERVAL 25 // try making measurements every 10 milliseconds #define SECONDSINTERVAL 5 // check charging status this many seconds apart ///////////////////////////STRUCTURES & VARIABLES /////////////////////////////////////// LiquidCrystal lcd(8, 9, 10, 11, 12, 13); // The GLG board //LiquidCrystal lcd(13,12,11,10,9,8); // when using the GAINESVILLE VENTILATOR Ashhar 1.0 Board // This means that D8goes to RS, D9 to en, D10-D13 are the 4 bit parallel port info // to the lcd display, which is connected exactly like both the UF venntilator // and the uBitx Raduino display. float zero_current_voltage; // the voltage we measure at BATTCURRENT pin // when no current is flowing. // Note: we may have a problem if it is negative.... float battery_voltage; // The voltage on the battery float AC_supply_voltage; // The voltage from the AC Supply unsigned long current_milliseconds; // the current number of milliseconds since starting unsigned long next_millisecond_time; // time to make next measurements unsigned long next_seconds_time; // time to make next charging check unsigned long next_resting_voltage_time; // next time to make a volage measurement of the battery int desired_battery_charge_ma= 0; int battery_charge_pwm = 0 ; // 0 to 255 int using_battery = 0; // 0 if using AC supply; 1 if using battery ////////////////////////FUNCTION DECLARATIONS/////////////////////////////////////////////// // (Tells the compiler what types of numbers the function receves and returns void read_battery_voltage(); // function that reads the global variable battery_voltage void read_AC_supply_voltage(); // function that reads the global AC_supply_voltage void configure_charger(); // function to control charging void MyMicroSecondsDelay(int microseconds); // microsecond non blocking delay void MyDelay(int msec); // non-blocking millisecondd delay // Charging current measurement // Gain of 100 op amp so 1 mV will become 100 mV; highest useful will be 4 volts = 40 mV; // make 40mV pickup = 4 amps so R = .04/4 = .01 ohms = 2.5 feet of #16 wire // 4 amps through .01 ohms created .04 volts // amplifier created 4 volts to be measured by Arduino (makes 4/5 * 1023 = 818 // so the conversion is 818/4 amps or 204 per amp or 1 per .005 Amp or 1 per 5 mA // lowest current measureable is 5 mv at Arduino (read 1) // We have to protect the op amp against the voltage going more negative than -0.3V, which // 0.01 ohms might reach at 30A current --- so to create a safety margin, reduce the wire to // 18 inches of #16 (0.006 ohms) which will reduce our sensitivy to about 8mA -- still plenty // We may have offset problems with the op amp -- in which case we may need to inject some current // at the positive terminal to raise it enough so that the output is not NEGATIVE at zero currrent. // This will be on the order of some megohms from +5 to the tap on the 18" of wire. /////////////////// SET UP ROUTINE (EXECUTED ONCE ON STARTUP //////////////////////////////////////////// void setup() { // put your setup code here, to run once: // We have three digital outputs that are going to control the three MOSFETS // First thing, we need to set those up and turn OFF all the conduction by the mosfets pinMode(ACSUPPLYPIN, OUTPUT); pinMode(BATTSUPPLYPIN, OUTPUT); pinMode(CHARGER, OUTPUT); // sending HIgh to any of these mosfets turns then ON; LOW turns them off // This is due to the way we built the circuit digitalWrite(ACSUPPLYPIN, LOW); digitalWrite(BATTSUPPLYPIN, LOW); digitalWrite(CHARGER, LOW); cli(); wdt_reset(); /* WDTCSR configuration: WDIE = 1: Interrupt Enable WDE = 1 :Reset Enable See table for time-out variations: WDP3 = 0 :For 1000ms Time-out WDP2 = 1 :For 1000ms Time-out WDP1 = 1 :For 1000ms Time-out WDP0 = 0 :For 1000ms Time-out */ // Enter Watchdog Configuration mode: WDTCSR |= (1 << WDCE) | (1 << WDE); // Set Watchdog settings: WDTCSR = (1 << WDIE) | (1 << WDE) | (0 << WDP3) | (1 << WDP2) | (1 << WDP1) | (1 << WDP0); // trying to set it for 2 seconds sei(); // R-enable the interrups Serial.begin(115200); // this sets up the USB to give us diagnostic info // at baud rate 115200 (fast) lcd.begin(16, 2); // initialize the lcd display lcd.setCursor(0,0); // set the cursor at 0th column, 0th row lcd.print(F("NFARC BattBackup")); lcd.setCursor(0,1); // set cursor to 0th column, 1st row (2nd line on ours) lcd.print(F("Version 1.1")); MyDelay(2000); // Delay so this can be READ // Now read the charging current system output to get the zero_current_voltage using_battery = 0; // Assume we start out using the AC-powered supply zero_current_voltage = analogRead(BATTSUPPLYPIN) * 5 / 1023; current_milliseconds = millis(); // set the current time next_millisecond_time = current_milliseconds + MILLISECONDINTERVAL ; // time to make next measurements next_seconds_time = current_milliseconds + (SECONDSINTERVAL * 1000); // time to make next charging check } ///////////////////////////LOOP ROUTNE /////////////////////////////////////////// // Meat of the program: executed over and over again //------------------EVENT DRIVEN --------------------------------------- // Every 10 milliseconds: check the voltage & decide who powers what // Every 10 seconds: turn off the charger & check battery voltage // Every second: check the charger current and adjust the PWM proportion // chargingis a pwm fraction of a 2 millisecond (500 Hz) period // max freq of the changes is one every 8 microseconds // we may not be able to sample that fast, but if we sample every 10 microseconds // for about 5 milliseconds ( 500 samples) we can average to find the average // current flowing to the battery void loop() { // put your main code here, to run repeatedly: unsigned long instant_current_reading; int x; wdt_reset(); // Reset the watchdog timer -- it it doesn't get reset within 2 seconds, // it will restart the software // check for presence of AC supply voltage -- because of the diode in the // charging circuit, the AC supply voltage will ALWAYS be > then the // battery voltage if the AC is present, by more than 0.6 volts // Things that we do at milliseconds intervals if(millis() > next_millisecond_time) { read_AC_supply_voltage(); read_battery_voltage(); if(AC_supply_voltage > battery_voltage + 0.3) { digitalWrite(BATTSUPPLYPIN, LOW); // turn off the battery to load // it will still conduct through its internal diode..... // what turns off the battery is setting the CHARGER properly MyDelay(1); // be certain you don't connect the two systems DIRECT digitalWrite(ACSUPPLYPIN, HIGH); lcd.setCursor(0,1); // set the cursor at 0th column, 0th row lcd.print(F(" ")); lcd.setCursor(0,1); lcd.print(F("AC Supply")); Serial.println(F("Using AC based supply.")); using_battery = 0; // we use the AC-powered supply MyMicroSecondsDelay(1000); } else { // WE ARE GOING TO HAVE TO RUN FROM BATTERY digitalWrite(ACSUPPLYPIN, LOW); // turn off the AC supply to load MyDelay(1); // be certain you don't connect the two systems DIRECT digitalWrite(BATTSUPPLYPIN, HIGH); // turn on the battery to load digitalWrite(CHARGER, HIGH); // turn ON the interposed charger // must have BOTH of these on in order to get the best output voltage.... lcd.setCursor(0,1); // set the cursor at 0th column, 0th row lcd.print(F(" ")); lcd.setCursor(0,1); lcd.print(F("Using BATT")); Serial.println(F("Running from the battery.")); using_battery = 1; // we are now using the battery as the source battery_charge_pwm = 0 ; // whenever we switch to battery, set the initial charge current controller to 0 // it will iterate itself up on its own if we end up back with AC power MyMicroSecondsDelay(1000); } // now set up the next time to check the supply voltages current_milliseconds = millis(); // set the current time next_millisecond_time = current_milliseconds + MILLISECONDINTERVAL ; // time to make next measurements } // At seconds intervals, provided that charging is going on, we check the charging by turning off the charger temporarily // and then later setting the charging current if(millis()> next_seconds_time ) // time to check on the charging { // We always check the battery voltage....but we may not check the charging... Serial.println(F("\n\n******CHECKING BATTERY: ")); //measure the battery voltage and pick the desired current -- we'll do this under charge for simplicity at first read_battery_voltage(); // places value in global variable lcd.setCursor(0,0); // Go to first line lcd.print(F(" ")); lcd.setCursor(0,0); lcd.print(F("B=")); lcd.print(battery_voltage); // Fortuitously, shows 2 dec places Serial.print(F("Battery voltage = ")); Serial.println(battery_voltage); if(using_battery==0) // IF we are actually needing to charge....only happens when using AC supply { Serial.println(F("Checking the charging system.....")); if(battery_voltage<=12.5) desired_battery_charge_ma = 1000; // 1 amp charge if we are weak if(battery_voltage>12.5 && battery_voltage<=13.0) desired_battery_charge_ma = 100; // 100 mA if(battery_voltage>13.0) desired_battery_charge_ma = 25; // trickle charge Serial.print(F("Charging current desired(mA) = ")); Serial.println(desired_battery_charge_ma); // measure the charging current instant_current_reading = 0; // zero it before reading for(x=0;x<500;x++) // this entire loop will take only 5 milliseconds // we do this in order to aveage the PWM duty cycle!!! { instant_current_reading = (analogRead(BATTCURRENT)*5) + instant_current_reading; // answer is in mA MyMicroSecondsDelay(10); } instant_current_reading = instant_current_reading/500; Serial.print(F("Measured battery charge current (mA): ")); Serial.println(instant_current_reading); lcd.print(F(" mA=")); lcd.print( (int)instant_current_reading); // Write it out to the display if(instant_current_reading>desired_battery_charge_ma + 100) battery_charge_pwm = battery_charge_pwm/2; // cut it way down if(instant_current_reading>desired_battery_charge_ma)battery_charge_pwm=battery_charge_pwm-3; // decrease by 1% of our range //if(instant_current_reading255) battery_charge_pwm = 255; // can't go beyond these limits! Serial.print(F("PWM requested: ")); Serial.println(battery_charge_pwm); analogWrite(CHARGER ,battery_charge_pwm); } // end of the loop that is only done if charging is appropriate next_seconds_time=millis()+ (1000*SECONDSINTERVAL); } } //////////////////////////supporting subroutines ////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////// void read_battery_voltage() { battery_voltage = ( 5* ((float)(analogRead(BATTSUPPLYVOLTAGEPIN)) )* (10000 + 3300)/(3300))/1023 ; #ifdef VOLTAGEOUTPUTS Serial.print(F("\nBattery Voltage = ")); Serial.println(battery_voltage); #endif return; } //////////////////////////////////////////////////////////////////////////////////// void read_AC_supply_voltage() { AC_supply_voltage = ( 5* ((float)(analogRead(ACSUPPLYVOLTAGEPIN))) * (10000+3300)/3300 )/1023; #ifdef VOLTAGEOUTPUTS Serial.print(F("AC pin reads: ")); Serial.println(analogRead(ACSUPPLYVOLTAGEPIN)); Serial.print(F("\nAC-based Supply Voltage = ")); Serial.println(AC_supply_voltage); #endif return; } //////////////////////////////////////////////////////////////////////////////////// void configure_charger() { // Charging protocol // If the battery shows charged voltage, just set to trickle // if the battery shows discharged voltage, set higher current return; } //////////////////////MICROSECOND DELAY NON BLOCKING ///////////////////////////////// void MyMicroSecondsDelay(int udelay) { int microseconds; unsigned long current_microseconds, next_microseconds; current_microseconds = micros(); next_microseconds = current_microseconds + (unsigned long) udelay; while(micros()