Project Source Code

Below are the code sections run on the Arduino for our prototype device:

Code Sections

#include <Servo.h>
#include <Filters.h>
#include <Arduino.h>
#include <Wire.h>
#include <ST7036.h>

ST7036 lcd = ST7036 ( 2, 16, 0x7c );

#define iPin1 A1
#define iPin2 A2
#define voltagePin A0
#define SPin2 9
#define SPin1 8
#define rPin1 6
#define rPin2 7
#define rPin0 5
#define AZServo 8
#define ELServo 9

//Temp monitoring code:
#define ApinTemp A5
#define FILTER_COUNTS 7
#define deltaTime 250
#define BLINK_DURATION 200
//temp monitoring
int counter = 0;
long timePassed = 250;
long timePassed2 = 1000;
float reading = 0;
float temp[FILTER_COUNTS];
float tempSum = 0;
float tempAvg = 0;
float currTemp = 0;
//end temp monitoring code

//Power Logging Constants
//do all math with pre-multiplied longs, convert to float and divide at the end
double wattHr = 0;
unsigned long prevTime = 0;
unsigned long currTime = 0;

//Current Averaging Constants
const int numReadings = 100;
double readings[numReadings]; // the readings from the analog input
int readIndex = 0; // the index of the current reading
double total = 0; // the running total
double average = 0; // the average
bool wrapCurrent = false; //determine when to start removing values from array

//Servo Constants
int servoPos = 0;
int servoCountMax = 20;
int servoCounter = 0;
Servo s1;
Servo s2;
//Global IV sense:
double globalV = 0;
double globalI = 0;

//elevation finding
double maxVE = -1;
int maxVPosE = 0;

void setup() {
lcd.init ();
lcd.setContrast(0);
delay(15);
Serial.begin(9600);
for (int thisReading = 0; thisReading < numReadings; thisReading++) {
readings[thisReading] = 0;
}
pinMode(SPin1, OUTPUT);
pinMode(rPin1, OUTPUT);
pinMode(rPin2, OUTPUT);
pinMode(rPin0, OUTPUT);
// Serial.println("PortaVolt Activating!");

digitalWrite(rPin1, HIGH);
digitalWrite(rPin2, HIGH);
digitalWrite(rPin0, LOW);

}

void loop() {

getIV();
logPwr(getVoltage(iPin2) * getAvgCurrent(getCurrent(iPin1, iPin2)));
readTemp();
// infoPrint();
infoLog();

relayControl();
//Demonstration code commented below:
// if (temporary)
// {
// temporary = false;
// findSunElevation();
// }
//
// if (Serial.available() > 0) {
// Serial.flush();
// while (Serial.available() > 0) {
// Serial.read();
// }
// maxVE = -1;
// maxVPosE = 0;
// findSunElevation();
//
// }
sunTrack(15);

}

volatile double batt1V = 0;
volatile double batt2V = 0;
void getBatteryVoltages()
{
auto original1 = digitalRead(rPin1);
auto original2 = digitalRead(rPin2);
digitalWrite(rPin0, LOW);
delay(200);
digitalWrite(rPin2, HIGH);
digitalWrite(rPin1, LOW);
// while (digitalRead(rPin2) == LOW || digitalRead(rPin1) == HIGH) {}

// Serial.println("Getting batt1V"); //DEBUG
delay(200);
getIV();
batt1V = globalV;
digitalWrite(rPin1, HIGH);
digitalWrite(rPin2, LOW);
// while (digitalRead(rPin2) == HIGH || digitalRead(rPin1) == LOW) {}

// Serial.println("Getting batt2V"); //DEBUG
delay(200);
getIV();
batt2V = getVoltage(iPin2);

delay(200);
digitalWrite(rPin0, HIGH);
delay(200);
digitalWrite(rPin1, original1);
digitalWrite(rPin2, original2);
}

enum chargeState {
battery1, battery2, startup, filled
};
chargeState batteries = startup;

int battNum = 2;
String battEnum[] = {"battery1", "battery2", "startup", "filled"};
unsigned long battTimer = 0;
unsigned long battDTime = 300000; //5 minutes in ms
//unsigned long battDTime = 30000; //30 seconds in ms DEBUG
int battMaxV = 9;
int batt1VMin = 8;
void relayControl()
{
if (battNum == 2)
{
getBatteryVoltages();
if (batt1V < battMaxV) battNum = 0; else if (batt2V < battMaxV) battNum = 1; else battNum = 3; } else if (battNum == 0) { if (digitalRead(rPin2) != HIGH) { digitalWrite(rPin2, HIGH); } if (digitalRead(rPin1) != HIGH) { digitalWrite(rPin1, HIGH); } if (batt1V >= battMaxV) battNum = 2;
else battNum = 0;

//we are prioritizing the charging of battery 1
if (abs(millis() - battTimer) >= battDTime)
{
// batt1V = getVoltage(iPin2);
battTimer = millis();
getBatteryVoltages();
if (batt1V < battMaxV) battNum = 0; else battNum = 2; } } else if (battNum == 1) { if (digitalRead(rPin2) != HIGH) { digitalWrite(rPin2, HIGH); } if (digitalRead(rPin1) != HIGH) { digitalWrite(rPin1, HIGH); } if (batt2V >= battMaxV) battNum = 2;
else battNum = 1;

if (abs(millis() - battTimer) >= battDTime)
{
// batt2V = getVoltage(iPin2);
battTimer = millis();
getBatteryVoltages();
if ((batt2V < battMaxV) || (batt1V < batt1VMin)) battNum = 1; else battNum = 2; } } else if (battNum == 3) { if (digitalRead(rPin2) != LOW) { digitalWrite(rPin2, LOW); } if (digitalRead(rPin1) != LOW) { digitalWrite(rPin1, LOW); } if (abs(millis() - battTimer) >= battDTime)
{
battTimer = millis();
getBatteryVoltages();
if (batt1V < battMaxV) battNum = 0; else if (batt2V < battMaxV) battNum = 1; else battNum = 3; } } }


unsigned long dTime1 = 0;
unsigned long dTime2 = 0;
//unsigned long angleIncDelta = 3600000; //rotatone once every hour
unsigned long angleIncDelta = 5000; //5 seconds for testing,
//recheck elevation every two weeks
unsigned long elevationDeltaTime = 1209600000;
unsigned long dayLength = 43200000;
unsigned long elevationTime = elevationDeltaTime * -1 - 100;
int azAngle = 0;
//int angleIncrement = 15;
bool dayStart = false;
bool dayStart2 = true;
bool night = false;

void sunTrack(int angleIncrement)
{

if (dayStart)
{
night = false;
//
// delay(500);
//
// Serial.print("went to azimuthial angle: ");
// Serial.println(azAngle);
// delay(500);
if ( abs(millis() - elevationTime) >= elevationDeltaTime)
{
elevationTime = millis();
findSunElevation();
// Serial.println("Found elevation");
}
dayStart = false;
}

if (dayStart2)
{
azAngle = 1;
servoControl(azAngle, AZServo);
dayStart2 = false;
dayStart = true;
}
if ((abs(millis() - dTime1) >= angleIncDelta) && !night)
{
dTime1 = millis();
azAngle += angleIncrement;
if (azAngle >= 180)
{
azAngle = 180;
}
// Serial.println("Second write to servo");
servoControl(azAngle, AZServo);
if ((abs(millis() - dTime2) >= dayLength))
{
night = true;
dTime2 = millis();
}
}
else if (night)
{
if (abs(millis() - dTime2) >= dayLength)
{
night = false;
dayStart2 = true;
dTime2 = millis();
dTime1 = dTime2;
azAngle = 0;
}
}
}

void findSunElevation()
{
// Serial.println("got inside here:");
//servo will go between 70 and 130
auto original1 = digitalRead(rPin1);
auto original2 = digitalRead(rPin2);
if (digitalRead(rPin2) != LOW)
{
digitalWrite(rPin2, LOW);
}
if (digitalRead(rPin1) != LOW)
{
digitalWrite(rPin1, LOW);
}
int stepSize = 10;
int stepNum = 50 / stepSize;
int startPos = 70;
for (int i = 0; i <= stepNum; i ++) { // Serial.print("step: "); // Serial.print(i * stepSize); servoControl(i * stepSize + startPos, ELServo); // Serial.print("voltage: "); // Serial.println(getVoltage(iPin2)); if (getVoltage(iPin2) > maxVE)
{
maxVE = getVoltage(iPin2);
maxVPosE = i * stepSize + startPos;
}
Serial.print("cell voltage: ");
Serial.print(getVoltage(iPin2));
Serial.print(" ,max voltage seen: ");
Serial.println(maxVE);
}
// Serial.println("");
servoControl(maxVPosE, ELServo);
digitalWrite(rPin1, original1);
digitalWrite(rPin2, original2);
}

//servoControl(180, ELServo);
// delay(500);
// servoControl(70, ELServo);
// delay(500);
//
double maxVA = -1;
int maxVPosA = 0;
void findSunAzimuth()
{
servoControl(45, 1);
int stepSize = 10;
for (int i = 0; i <= 18; i ++) { servoControl(i * stepSize, AZServo); if (getVoltage(iPin2) > maxVA)
{
maxVA = getVoltage(iPin2);
maxVPosA = i * stepSize;
}
}
}

void getIV()
{
globalV = getVoltage(iPin2);
globalI = getAvgCurrent(getCurrent(iPin1, iPin2));
if (globalI < 0) globalI = 0;
}

void infoPrint()
{
// data formatted for reading in the serial monitor
// Serial.print(getVoltage(iPin2));

Serial.print(globalV);
Serial.print(" V ");
Serial.print(", ");
// Serial.print(getAvgCurrent(getCurrent(iPin1, iPin2)));
Serial.print(globalI);
Serial.print(" mA ");
Serial.print(" , ");
// Serial.print(getVoltage(iPin2) * getCurrent(iPin1, iPin2));
Serial.print(globalV * globalI);
Serial.print(" mW ");
Serial.print(" , ");
Serial.print(wattHr);
Serial.print(" W*h ");
Serial.print(" Vb1: ");
Serial.print(batt1V);
Serial.print(" Vb2: ");
Serial.print(batt2V);
Serial.print(" Relays: ");
Serial.println(battEnum[battNum]);
// Serial.print(" Relays: ");
// Serial.println(batteries);
// Serial.print(" temp: ");
// Serial.println(tempSum / FILTER_COUNTS);
}

void infoLog()
{
//data formatted for CSV reading using Processing script
Serial.print(millis());
Serial.print(",");
Serial.print(globalV);
Serial.print(",");
Serial.print(globalI);
Serial.print(",");
Serial.print(batt1V);
Serial.print(",");
Serial.print(batt2V);
Serial.print(",");
Serial.print(battEnum[battNum]);
// Serial.print(getVoltage(iPin2) * getCurrent(iPin1, iPin2));
// Serial.print(",");
// Serial.print(wattHr);
// Serial.print(",");
Serial.print(",");
Serial.println(currTemp);
// Serial.println(tempSum / FILTER_COUNTS);

}
void logPwr(double instPwr)
{
currTime = millis();
wattHr += -1 * ((double)(currTime - prevTime)) * instPwr / (60 * 60 * 1000 * 1000 * 1000);
prevTime = millis();
}

void servoControl(int desiredPos, int servo) {
//servo consumes 3.6mA at 5V consistently at rest
// 0.018 Watts of energy per servo
//energy use bursts when moving

if (servo == 9)
{

if (servoPos != desiredPos) {
servoPos = desiredPos;
s2.attach(9, 875, 2525);
}
s2.write(servoPos);
delay(1000);
s2.detach();
}
else if (servo == 8)
{
if (servoPos != desiredPos) {
servoPos = desiredPos;
s1.attach(8, 875, 2525);
}
s1.write(servoPos);
delay(1000);
s1.detach();
}
else
{
Serial.println("ERROR in servo code");
}

}

double getAvgCurrent(double current) {
// subtract the last reading:
if (wrapCurrent) total = total - readings[readIndex];
// read from the sensor:
readings[readIndex] = current;//analogRead(inputPin);
// add the reading to the total:
total = total + readings[readIndex];
// advance to the next position in the array:
readIndex = readIndex + 1;

// if we're at the end of the array...
if (readIndex >= numReadings) {
wrapCurrent = true;
// ...wrap around to the beginning:
readIndex = 0;
}

// calculate the average:
average = total / (double)numReadings;
}

double getCurrent(char currentPin1, char currentPin2) {
int i1 = analogRead(currentPin1);
// ratio is 97/73
double ratio1 = 97 / 73;
int i2 = analogRead(currentPin2);
// ratio is 23/14
double ratio2 = 23 / 14;
//saftey check:
if (i1 > 4500 || i2 > 4500) {
while (true) {
Serial.println("Input voltage to arduino too high, check wiring and reset");
}
}
long currentV1 = AD2V(i1);
long currentV2 = AD2V(i2);
double current = (double)(currentV1 - currentV2);
// Serial.print("Current pre fix: ");
// Serial.print(current);
// corrections:
// current = 0.4838*current - 0.566; //for 50W resistors
current = .00001 * current * current + 0.8819 * current + 17.45; //for 20W resistor
// Serial.print(" Current post fix: ");
// Serial.println(current);
return current;
}

double getVoltage(char currentPin2)
{
long currentV2 = AD2V(analogRead(currentPin2));
//this voltage reading comes back weird accross the relay
// double voltage = 0.7993 * ((double)currentV2 * 5.00 / (1000)) - 0.9941;
// double voltage = ((double)currentV2 / (1000.0));
double voltage = 0.0045 * ((double)currentV2) - 0.2141;
voltage = voltage - 1; //correction
return voltage;
}

long AD2V(int input) {
return (long)input * 5000 / 1024;
}

//TMP36 0.5 10 750

void readTemp() {
reading = (analogRead(ApinTemp));
if (temp[counter % FILTER_COUNTS] != 0) {
tempSum = tempSum - temp[counter % FILTER_COUNTS];
}
temp[counter % FILTER_COUNTS] = 32.0 + 1.8 * ((reading * (5000.0 / 1024.0) - 500) * (1.0 / 10.0));
// temp[counter % FILTER_COUNTS] -=10; //calibration
tempSum += temp[counter % FILTER_COUNTS];
counter++;
currTemp = 32.0 + 1.8 * ((reading * (5000.0 / 1024.0) - 500) * (1.0 / 10.0));
}