Power supply monitor

Post date: Jul 19, 2015 4:11:13 AM

Introduction:

I have already used most of the output/input of my arduino uno board.

only 4 are still available

  • Digital I/O 0 and 1 which are used by the usb to serial communication to IDE. I could theoretically used those two as digital output but i prefer to keep them free for the usb communication.

  • Analog input A4 and A5

I should invest in a couple of arduino mega board and mega prototyping board for my next project. The number of Output and input is really to little for extended project.

The controller speed and memory never have been a problem so far but i'have already being blocked a couple of time by the number of I/O available on the arduino uno.

I'm going to use A4 as analog input to read the voltage of the lipo battery threw a potentiometer used as voltage divider rand use A5 as digital output to blink a LED as batterie alarm. I need to calibrate the pot to have 5V at the exit of the divider with a new charged 3s 2200 mah lipo battery which is normaly 12.5V

I want to trigger an alarm when the voltage reach 11.2V which is the minimum voltage allowed for 20% power left in the Lipo.

To blink the LED i do not want to use the delay function.i don't want to pause the execution of the code because i want it to permanently read the status of the direction pot to change the DC motor speed and direction accordingly.

i'm going to use the millis() function to manage time based events. Check out the example BlinkWithoutDelay that you can find in IDE example.

Components:

  • 1 potentiometer 10k Ohm

  • 1 resistor 1k Ohm

  • 1 Led red color

The led and the resistor are already connected on the Adafruit Prototyping board so i just have to connect the potentiometer to the + and - of the lipo battery and the middle pin to the A4 input of the arduino uno and the pullup resistor of the led to the A5 input of the arduino uno.

I wont have a lot of range doing that way because 1023=12.5V 0=0V 1024/12.5= 82 steps per volt 12.5-11.2 1.3v 1.3v82=98 steps only.

I'll think later about having better measuring range for better accuracy.

Schematic:

I wrote the code bellow to serial print the voltage value and the store them on an excel file to draw the line Voltage/ADC value to estimate the equation of voltage/ADCvalues.

Code:

/*

* Alarm Led on A4

* VoltageValue on A5

* alarm=924=11.2V

*/

const int voltAlarmLed = A4; const int voltage = A5 ;const int alarm= 924;int voltageValue=0;int voltAlarmLedState = LOW; unsigned long previousMillis = 0; //Store the time to blink the Alarm led const long interval = 1000; //Bliking period unsigned long currentMillis=0;int ledState=LOW;const int numReadings = 100; // number of samples for Volt readingint voltReadings[numReadings]; // the readings from A4int index = 0; // the index of the current readinglong total = 0; // the running totalint averageVolt = 0; // the average Voltage over 100 sampleint Max1 = 0; //Volt max readingint Min1 = 1023; //volt min readingvoid setup() { // set the digital pin as output: pinMode(voltAlarmLedState, OUTPUT); Serial.begin(9600); //for debug for (int thisReading = 0; thisReading < numReadings; thisReading++) { voltReadings[thisReading] = 0; }}void loop(){ readingVolt (); //call the function to read Voltage on A5 Serial.println (voltageValue); if (voltageValue <= alarm ) //Test alarm { currentMillis = millis(); if(currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(voltAlarmLed, ledState); } }}int readingVolt () { // subtract the last reading: total = total - voltReadings[index]; // read from the sensor: voltReadings[index] = analogRead(voltage); // add the reading to the total: total = total + voltReadings[index]; // advance to the next position in the array: index = index + 1; // if we're at the end of the array... if (index >= numReadings) // ...wrap around to the beginning: index = 0; // calculate the average: averageVolt = total / numReadings; //record the temp max if (averageVolt > Max1) { Max1 = averageVolt; } //record the temp min if (averageVolt < Min1 && averageVolt < Max1) { Min1 = averageVolt; } voltageValue=averageVolt; return voltageValue; }

After emptying one Lipo 2200 3S battery i was able to print the following graph on excel.

From the graph, excel was kind enough to give me the following equation:

for any ADC value (X) Voltage value (Y)=0.0121X +0.0376

or

for any voltage Value (Y) ADC Value (X)=(Y-0.0376)/0.0121

This test gave me some new ideas before implementing it in the mixer code. Why not using the same method to sample the average volt, Motor1, Motor2, Motor3, Motor4 average motor speed on a base of 100 samples and on a regular time bases and make a graph on excell of power voltage variation according to motor speed to first Serial print it to do dry off load test in the lab and the EEPROM write it to post reading analysis if surviving the maiden dive ?

DC Motor mixer with serial print data monitoring:

I have implemented the following code to serial print the average reading on 100 samples of the battery voltage,and the average motor speed on 100 samples too.

I hope i will be able from that to draw some nice graph about Voltage power drop and motors speed.

/* Connections:

* BOARD -> ARDUINO

* 1A -> 2

* 1B -> 4

* E1 -> 3

* 2A -> 7

* 2B -> 8

* E2 -> 5

* 3A -> 6

* 3B -> 10

* 3E -> 11

* 4A -> 13

* 4B -> 12

* 4E -> 9

* Yaw -> A0

* Pitch -> A3

* Roll -> A2

* Thrust -> A1

* Alarm Led on A4

* VoltageValue on A5

* alarm=924=11.2V

##############################################################################*/

// Define constants and variablesconst int DirM1a = 2;const int DirM1b = 4;int tempDirM1a = LOW;int tempDirM1b = LOW;const int Motor1 = 3;const int DirM2a = 7;const int DirM2b = 8;int tempDirM2a = LOW;int tempDirM2b = LOW;const int Motor2 = 5;const int DirM3a = 6;const int DirM3b = 10;int tempDirM3a = LOW;int tempDirM3b = LOW;const int Motor3 = 11;const int DirM4a = 13;const int DirM4b = 12;int tempDirM4a = LOW;int tempDirM4b = LOW;const int Motor4 = 9;const int Yaw = A0;const int Roll = A2;const int Pitch = A3;const int Thrust = A1;int analogValueYaw = 0;int analogValueRoll = 0;int analogValuePitch = 0;int analogValueThrust = 0;byte pwmMotor1 = 0;byte pwmMotor2 = 0;byte pwmMotor3 = 0;byte pwmMotor4 = 0;const int voltAlarmLed = A4;const int voltage = A5 ;const int alarm = 924; // alarm valu 924=11.28Vint voltageValue = 0;int voltAlarmLedState = LOW;unsigned long previousMillis = 0; //Store the time to blink the Alarm ledconst long interval = 1000; //Bliking periodunsigned long currentMillis = 0;int ledState = LOW;const int numReadings = 100; // number of samples for Volt readingint voltReadings[numReadings]; // the readings from A4int recMotor1[numReadings];int recMotor2[numReadings];int recMotor3[numReadings];int recMotor4[numReadings];int index = 0; // the index of the current readinglong totalVolt = 0; // the running totallong totalMotor1 = 0;long totalMotor2 = 0;long totalMotor3 = 0;long totalMotor4 = 0;int averageVolt = 0; // the average Voltage over 100 sampleint averageMotor1;int averageMotor2;int averageMotor3;int averageMotor4;// Initializationvoid setup(){ pinMode(DirM1a, OUTPUT); pinMode(DirM1b, OUTPUT); pinMode(Motor1, OUTPUT); pinMode(DirM2a, OUTPUT); pinMode(DirM2b, OUTPUT); pinMode(Motor2, OUTPUT); pinMode(DirM3a, OUTPUT); pinMode(DirM3b, OUTPUT); pinMode(Motor3, OUTPUT); pinMode(DirM4a, OUTPUT); pinMode(DirM4b, OUTPUT); pinMode(Motor4, OUTPUT); pinMode(voltAlarmLedState, OUTPUT); Serial.begin(9600); //for debug for (int thisReading = 0; thisReading < numReadings; thisReading++) { voltReadings[thisReading] = 0; }}// main loopvoid loop(){ readingValue (); //call the function to read Voltage Yaw Roll and Thrust values if (voltageValue <= alarm ) //Test alarm { blinkLed(); } else { digitalWrite(voltAlarmLed, LOW); } // apply direction adjustment for Pitch if (analogValuePitch > 563 ) { tempDirM1a = HIGH; tempDirM1b = LOW; tempDirM2a = LOW; tempDirM2b = HIGH; pwmMotor1 = (analogValuePitch - 512) / 2; // evaluate new pwm value pwmMotor2 = (analogValuePitch - 512) / 2; } else if (analogValuePitch < 453) { tempDirM1a = LOW; tempDirM1b = HIGH; tempDirM2a = HIGH; tempDirM2b = LOW; pwmMotor1 = (511 - analogValuePitch) / 2; // evaluate new pwm value pwmMotor2 = (511 - analogValuePitch) / 2; } // apply direction adjustment for Thrust if (analogValueThrust > 564) { tempDirM3a = LOW; tempDirM3b = HIGH; tempDirM4a = HIGH; tempDirM4b = LOW; pwmMotor3 = (analogValueThrust - 512) / 2; // evaluate new pwm value pwmMotor4 = (analogValueThrust - 512) / 2; } else if (analogValueThrust < 454) { tempDirM3a = HIGH; tempDirM3b = LOW; tempDirM4a = LOW; tempDirM4b = HIGH; pwmMotor3 = (511 - analogValueThrust) / 2; // evaluate new pwm value pwmMotor4 = (511 - analogValueThrust) / 2; } // apply direction adjustment for Yaw if (analogValueYaw > 544 ) { tempDirM1a = LOW; tempDirM1b = HIGH; tempDirM2a = LOW; tempDirM2b = HIGH; pwmMotor1 = (analogValueYaw - 512) / 2; // evaluate new pwm value pwmMotor2 = (analogValueYaw - 512) / 2; } else if (analogValueYaw < 434) { tempDirM1a = HIGH; tempDirM1b = LOW; tempDirM2a = HIGH; tempDirM2b = LOW; pwmMotor1 = (511 - analogValueYaw) / 2; // evaluate new pwm value pwmMotor2 = (511 - analogValueYaw) / 2; } // apply direction adjustment for Roll if (analogValueRoll > 543) { tempDirM3a = HIGH; tempDirM3b = LOW; tempDirM4a = HIGH; tempDirM4b = LOW; pwmMotor3 = (analogValueRoll - 512) / 2; // evaluate new pwm value pwmMotor4 = (analogValueRoll - 512) / 2; } else if (analogValueRoll < 433) { tempDirM3a = LOW; tempDirM3b = HIGH; tempDirM4a = LOW; tempDirM4b = HIGH; pwmMotor3 = (511 - analogValueRoll) / 2; // evaluate new pwm value pwmMotor4 = (511 - analogValueRoll) / 2; } if ((analogValuePitch < 563 && analogValuePitch > 453) && (analogValueYaw < 544 && analogValueYaw > 434)) { tempDirM1a = HIGH; tempDirM1b = HIGH; tempDirM2a = LOW; tempDirM2b = LOW; pwmMotor1 = 0; // Motor1 and motor2 are freeto run pwmMotor2 = 0; } if ((analogValueThrust < 564 && analogValueThrust > 454) && (analogValueRoll < 543 && analogValueRoll > 433)) { tempDirM3a = HIGH; tempDirM3b = HIGH; tempDirM4a = LOW; tempDirM4b = LOW; pwmMotor3 = 0; // Motor3 and motor4 arestopped and free to run pwmMotor4 = 0; } //apply motor direction digitalWrite(DirM1a, tempDirM1a); digitalWrite(DirM1b, tempDirM1b); digitalWrite(DirM2a, tempDirM2a); digitalWrite (DirM2b, tempDirM2b); digitalWrite(DirM3a, tempDirM3a); digitalWrite(DirM3b, tempDirM3b); digitalWrite(DirM4a, tempDirM4a); digitalWrite (DirM4b, tempDirM4b); // apply speed adjustment analogWrite(Motor1, pwmMotor1); analogWrite(Motor2, pwmMotor2); analogWrite(Motor3, pwmMotor3); analogWrite(Motor4, pwmMotor4);}/*******************************************************************************************Functions****************************** */int readingValue () { // subtract the last reading: totalVolt = totalVolt - voltReadings[index]; totalMotor1 = totalMotor1 - recMotor1[index]; totalMotor2 = totalMotor2 - recMotor2[index]; totalMotor3 = totalMotor3 - recMotor3[index]; totalMotor4 = totalMotor4 - recMotor4[index]; // read from the potentiometers: voltReadings[index] = analogRead(voltage); // add the reading to the total: totalVolt = totalVolt + voltReadings[index]; analogValueYaw = analogRead(Yaw); analogValueRoll = analogRead(Roll); analogValuePitch = analogRead(Pitch); analogValueThrust = analogRead(Thrust); //********************************************* if (analogValuePitch > 563 ) { recMotor1[index] = (analogValuePitch - 512) / 2; // evaluate new pwm value recMotor2[index] = (analogValuePitch - 512) / 2; } else if (analogValuePitch < 453) { recMotor1[index] = (511 - analogValuePitch) / 2; // evaluate new pwm value recMotor2[index] = (511 - analogValuePitch) / 2; } // apply direction adjustment for Thrust if (analogValueThrust > 564) { recMotor3[index] = (analogValueThrust - 512) / 2; // evaluate new pwm value recMotor4[index] = (analogValueThrust - 512) / 2; } else if (analogValueThrust < 454) { recMotor3[index] = (511 - analogValueThrust) / 2; // evaluate new pwm value recMotor4[index] = (511 - analogValueThrust) / 2; } // apply direction adjustment for Yaw if (analogValueYaw > 544 ) { recMotor1[index] = (analogValueYaw - 512) / 2; // evaluate new pwm value recMotor2[index] = (analogValueYaw - 512) / 2; } else if (analogValueYaw < 434) { recMotor1[index] = (511 - analogValueYaw) / 2; // evaluate new pwm value recMotor2[index] = (511 - analogValueYaw) / 2; } // apply direction adjustment for Roll if (analogValueRoll > 543) { recMotor3[index] = (analogValueRoll - 512) / 2; // evaluate new pwm value recMotor4[index] = (analogValueRoll - 512) / 2; } else if (analogValueRoll < 433) { recMotor3[index] = (511 - analogValueRoll) / 2; // evaluate new pwm value recMotor4[index] = (511 - analogValueRoll) / 2; } if ((analogValuePitch < 563 && analogValuePitch > 453) && (analogValueYaw < 544 && analogValueYaw > 434)) { recMotor1[index] = 0; // Motor1 and motor2 are freeto run recMotor2[index] = 0; } if ((analogValueThrust < 564 && analogValueThrust > 454) && (analogValueRoll < 543 && analogValueRoll > 433)) { recMotor3[index] = 0; // Motor3 and motor4 arestopped and free to run recMotor4[index] = 0; } totalMotor1 = totalMotor1 + recMotor1[index]; totalMotor2 = totalMotor2 + recMotor2[index]; totalMotor3 = totalMotor3 + recMotor3[index]; totalMotor4 = totalMotor4 + recMotor4[index]; // calculate the average: averageVolt = totalVolt / numReadings; averageMotor1 = totalMotor1 / numReadings; averageMotor2 = totalMotor2 / numReadings; averageMotor3 = totalMotor3 / numReadings; averageMotor4 = totalMotor4 / numReadings; // advance to the next position in the array: index = index + 1; // if we're at the end of the array... if (index >= numReadings) { // ...wrap around to the beginning: index = 0; Serial.print(averageVolt); Serial.print (","); Serial.print (averageMotor1); Serial.print (","); Serial.print (averageMotor2); Serial.print (","); Serial.print (averageMotor3); Serial.print (","); Serial.println (averageMotor4); } voltageValue = averageVolt; return voltageValue; return analogValueYaw ; return analogValueRoll ; return analogValuePitch; return analogValueThrust;}

Once uploaded inside the arduino board, i play with a new lipo battery until the first blink appear on the alarm led.

I was doing always the same pattern on the stick. 1 minute full speed in one stick direction followed by a 1 minute with stick in neutral position. I try to play with all the possible stick combination (full forward for 1 minute, 1 minute stop, full backward 1 minute, 1 minute stop, full up 1 minute, 1 minute stop etc....)

I stop the test once the led start to blink with the engine at full speed. That's give me 50 minutes time and with the engine stopped still have 11.42V in the lipo. I could have push the test a bit longer but i don't want to push to much on the lipo.

Doing that test i want to check the Voltage drop according to motor use on one battery on 50% usage of the motors without payload.

My final plan is to use 2 lipo batteries in parallel so i 'm expected 100 minute time with 50% of motor use so 50 minutes time with 100% motor usage.

On a real dive we wont use the motors at full speed 100% of the time but more probably 50% but we will have some payload which is the displacement and weight of the craft in the water plus some water displacement (current) to fight eventually. i have no idea of the impact of the weight displacement of the craft in the water and i assume that is going to be more or less equivalent of 100% motor use with no payload.

I'm expected a diving time between 50 to 60 minutes on real dive with 2 lipo battery 2200maH 3S.

The code is serial printing on the serial monitor the data with the following format:

Volt (0-1023),M1(0-255),M2(0-255),M3(0-255),M4(0-255),

At the end of the test i copy/past the data form the terminal window to the notepad text editor and import the text file in excel with comma delimited format.

in excel i calculate the voltage value of the ADC voltage reading by applying the equation (see above) ADC value (X) Voltage value (Y)=0.0121X +0.0376 in a new column and transform the line number in seconds i have 33370 samples in 50 minutes (3000 second) 33370/3000= 11 samples/ seconds= 0.090 second per sample.

I discovered that excel can take only 32000 values per series for graph so i have to get rid of 1370 line of data so the time display on the graph is 123 second shorter than the recorded data.

from the graph reading i can establish that there is a falling peak of tension in between 0.5V to 0.6V when motor start.

the drop of tension when motor run continuously after starting peak is in between 0.2 to 0.3 volt

When the led start to blink with motor on (voltage drop down <11.2V) i still have 11.42V when motor stopped. It should be enough juice to ascent safely.

Conclusion:

As i already said in the introduction, i have reached the physical limits on the arduino uno controller board. not in terms of processing power but in terms of I/O and memory.

All the Digital and analog I/O are used and the code is using

Sketch uses 5,404 bytes (16%) of program storage space. Maximum is 32,256 bytes.

Global variables use 1,276 bytes (62%) of dynamic memory, leaving 772 bytes for local variables. Maximum is 2,048 bytes.

I still have a lot of room for coding (84% left) but i'm already using 62% of dynamic memory by storing the voltage and M1 M2 M3 M4 values in 4 arrays of only 100 samples each.

The EEprom is too small and too slow to store any relevant voltage and motor information. so i did not try to implement an EEprom storage function.

The good new is that we will probably reach a 50 minutes dive time with 2 lipo 2200mah 3S batteries.

Notes:

Thanks to +Henrik Larsen​form Arduino google plus Communities who pointed out in my post that i can save a lot of memories by subtracting 512 to the voltage value reading and then store it as a byte and not

as an integer.Good point !!!!

Then double checking my code by following his idea i realize that i'm declaring the Motor speed value as integer and not byte. I could save a lot of memory there.

Thanks to him as well to show me and easy method to bias a constant voltage by using a zener diod. http://www.electronics-tutorials.ws/diode/diode_7.html

I keep that in mind for the ROV2 project.

int blinkLed(){ currentMillis = millis(); if (currentMillis - previousMillis >= interval) { // save the last time you blinked the LED previousMillis = currentMillis; // if the LED is off turn it on and vice-versa: if (ledState == LOW) ledState = HIGH; else ledState = LOW; // set the LED with the ledState of the variable: digitalWrite(voltAlarmLed, ledState); }}