/*********************************************************************
						Powertester

Measures 2 voltages and current from an equipment(Load), and logs
the results on SD card. Control and read values from bluetooth.

By Jony Silva
Tue Jun 12 14:56:12 GMT 2018
*********************************************************************/

// Libraries to be included, non native libraries need to be included in the Makefile
#include <stdlib.h>
#include <LiquidCrystal.h>		// Handles the connection and communications with the LCD display
#include <SPI.h>			// Handles the SPI connection to the SD card
#include <SD.h>				// Handles communication routines with the SD card
#include <SoftwareSerial.h>		// Handles serial communications thru software


// Configuration of pins, insert here arduino pin numbers only(not fisical)---------------------------
#define i_load 6			// Analog pin for the load current
#define v_batt 7			// Analog pin for the battery voltage
#define volt_1 0			// Analog pin for voltage 1
#define volt_2 1			// Analog pin for voltage 2
#define load_line 17			// Line that turns load On or Off
#define LEFT 16				// Button 1 will be function Left
#define RIGHT 18			// Button 2 will be function Right
#define ACT 19				// Button 3 will be function Action
#define BLUE_RX 8			// Arduino rx serial line to bluetooth
#define BLUE_TX 9			// Arduino tx serial line to bluetooth
const int chipSelect = 10;		// Chip Select pin for controlling SD card
LiquidCrystal lcd(7, 6, 2, 3, 4, 5);	// LCD screen pins, LiquidCrystal(rs, enable, d4, d5, d6, d7)
//-----------------------------------------------------------------------------------------------------


// Other configurations ---------------------------------------------------------------------------
#define voltage_1 0			// Parameter position where voltage one value is hold
#define voltage_2 1			// Parameter position where voltage two value is hold
#define curr 2				// Parameter position where load current value is hold
#define battery_volt 3			// Parameter position where battery voltage value is hold
#define numOfScreens 7			// Number of screen menus, select here and rearrange String screens
#define delay_loop 4000			// Value in miliseconds for the main loop routine
#define sd_not_in 0			// SD card is not inserted
#define sd_in 1				// SD card is inserted
#define file_fail 2			// Can't write to file
#define writing 3			// Write on the sd card
#define curr_limit 2			// Maximum current allowed
#define off 0				// off is always zero
#define on 1				// on is always one
#define no 0				// no is always zero
#define yes 1				// yes is alyays one
File myFile;				// Initiate the object File to write in the SD card
SoftwareSerial mySerial(BLUE_RX, BLUE_TX); // Start file descriptor and assign pins to bluetooth module
//-------------------------------------------------------------------------------------------------


// Macros --------------------------------------------------------
#define rLEFT digitalRead(LEFT)		// Read button Left
#define rRIGHT digitalRead(RIGHT)	// Read button Right
#define rACT digitalRead(ACT)		// Read button Action
//----------------------------------------------------------------


// Variables --------------------------------------------------------------------------------------
bool sd_rw = off;			// Holds the state of read/write of the card
bool load_status = off;			// Holds the state of on/off of the load
bool sd_inserted = no;			// Holds the state if sd card is in or not
int sd_status = sd_not_in;		// Holds information about the sd card

int currentScreen = 0;			// Current display menu
char ble_data = 'Z';			// Holds a character received by bluetooth

// String with the menus to be displayed
String screens[numOfScreens][2] = {{"Voltage 1","Volts"}, {"Voltage 2","Volts"},
	{"Load current","Amps"},{"Batt Voltage","volts"}, {"SD Logging", ""}, {"Load", ""}, {"SD card", ""}};

String parameters_sd[2] = {"No","Yes"};			// String to display the sd card logging
String parameters_load[2] = {"Off","On"};		// String to display the load state
String sd_card[4] = {"Card missing","Card inserted","File Write fail","Writing..."}; // String to display the sd card state
String file_name = "measures";		// Initial file name only 8 characters allowed
float parameters[4];			// Parameters of the several voltages and current

float denominator;			// Denominator to calculate the voltage divider
int resistor1 = 22000;			// Resistor one of voltage divider
int resistor2 = 2200;			// Resistor two of voltage divider
static char outstr[15];		// String output to bluetooth converted from float
// -----------------------------------------------------------------------------------------------



// Function declarations --------------------------------------------------
void menuState(void);		// Read buttons and update menu
void printScreen (void);	// Print menus and variables on screen
float volt_pin (int pin);	// Read and display voltage
float amp_pin (int pin);	// Read and display current
float fmap(float x, float in_min, float in_max, float out_min, float out_max);	// Calculate current opamp
//-------------------------------------------------------------------------



void setup()
{
	pinMode(LEFT, INPUT);			// Button left is an input
	pinMode(RIGHT, INPUT);			// Button right is an input
	pinMode(ACT, INPUT);			// Button act is an input
	pinMode(load_line, OUTPUT);		// load_line is an output

	lcd.begin(16, 2);			// Start LCD display with 16 characters and 2 lines

	mySerial.begin(9600);			// Start serial connection to bluetooth module
	Serial.begin(9600);			// For debug only

	// Voltage divider -->  Vout = Vin * R2 / R1 + R2
	denominator = (float)resistor2 / (resistor1 + resistor2);

	// initialize timer1 ---------------------------------------
	noInterrupts();           // disable all interrupts
	TCCR1A = 0;
	TCCR1B = 0;
	TCNT1  = 0;

	OCR1A = 9000;             // Load value to compare
	TCCR1B |= (1 << WGM12);   // CTC mode
	TCCR1B |= (1 << CS10);    // 64 prescaler
	TCCR1B |= (1 << CS11);    //
	TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
	interrupts();             // enable all interrupts
	// ----------------------------------------------------------

	if (SD.begin(chipSelect))		// Initiate SD card
	{
		sd_status = sd_in;		// If sd card is initiated successfully change variable sd_in
		sd_inserted = yes;		// and change variable sd_inserted to yes
	}
	else {
		sd_status = sd_not_in;		// If sd card is not initiated change variable to sd_not_in
		sd_inserted = no;		// and change variable sd_inserted to no
	}
}


// Timer compare interrupt service routine --
ISR(TIMER1_COMPA_vect)
{
	menuState();								// Read buttons act accordingly and update menus
	if (amp_pin(i_load) > curr_limit)			// If load current more then current limit
	{
		load_status = off;						// Turn off load
	}
		digitalWrite(load_line, load_status);	// Turn load on or off depending on the load_status


	if (mySerial.available()) {					// If there is incoming serial data
		ble_data = mySerial.read();				// save it caracter into ble_data
		switch(ble_data)						// Evaluate caracter
		{
		case 'a':												// If caracter is 'a'
			dtostrf(parameters[battery_volt], 5, 2, outstr);	// convert battery voltage to string
			mySerial.write(outstr);								// and send it to bluetooth
			break;												// finish switch statment

		case 'b':												// If caracter is 'b'
			dtostrf(parameters[voltage_1], 5, 2, outstr);		// convert voltage 1 to string
			mySerial.write(outstr);								// and send it to bluetooth
			break;												// finish switch statment

		case 'c':												// If caracter is 'c'
			dtostrf(parameters[voltage_2], 5, 2, outstr);		// convert voltage 2 to string
			mySerial.write(outstr);								// and send it to bluetooth
			break;												// finish switch statment

		case 'd':												// If caracter is 'd'
			dtostrf(parameters[curr], 5, 2, outstr);			// convert load current to string
			mySerial.write(outstr);								// and send it to bluetooth
			break;												// finish switch statment

		case 'e':												// If caracter is 'd'
			load_status = on;									// turn ON load
			break;												// finish switch statment

		case 'f':												// If caracter is 'f'
			load_status = off;									// turn OFF load
			break;												// finish switch statment
		}
	}
}
//-------------------------------------------


void loop()
{
	parameters[battery_volt] = volt_pin(v_batt);	// Update Battery voltage value
	parameters[voltage_1] = volt_pin(volt_1);	// Update voltage one value
	parameters[voltage_2] = volt_pin(volt_2);	// Update voltage two value
	parameters[curr] = amp_pin(i_load);		// Update current load




	if (sd_inserted == no)				// If SD card is not inserted
	{
		sd_status = sd_not_in;			// change card status to sd card not inserted
	}
	else if (sd_rw == on)				// otherwise sd_rw is ON
	{
		myFile = SD.open(file_name + ".TXT", FILE_WRITE);	// Open file name and add ".TXT",
															// for writing
		if (myFile)											// if succeds
		{
			myFile.print("Batt voltage, ");			// Write string to card
			myFile.println(volt_pin(v_batt));		// Write to card battery voltage
			myFile.print("Voltage one,  ");			// Write string to card
			myFile.println(volt_pin(volt_1));		// Write to card voltage one
			myFile.print("Voltage two,  ");			// Write string to card
			myFile.println(volt_pin(volt_2));		// Write to card voltage two
			myFile.print("Load current,  ");		// Write string to card
			myFile.println(amp_pin(i_load));		// Write to card load current
			myFile.println("");						// Write to card an empty line
			myFile.close();							// Close file
			sd_status = writing;					// change card status to, writing
		}
		else
		{
			sd_status = file_fail;					// otherwise change card status to, failed
		}
	}
	else
	{
		sd_status = sd_in;							// otherwise change card status to, card inserted
	}

	printScreen();					// Refresh screen and print current menu and values
	delay(delay_loop);				// Repeat the main loop every delay_loop seconds
}


//	Function that checks in what menu we are and wich button was pressed,
//	in case button Right was pressed menu will rotate to the right
//	in case button Left was pressed menu will rotate to the left
//	in case button Action was pressed and we are on the fourth SD card menu, the card
//	writing can be toggled between No and Yes
//	in case button Action was pressed and we are on the fifth Load menu, the load
//	can be toggled between On and Off
void menuState()
{

	if (!rRIGHT)									// if button Right is pressed
	{
		if (currentScreen == (numOfScreens - 1))	// check if its in the last screen
		{
			currentScreen = 0;						// then change to first screen
		}
		else
		{
			currentScreen++;						// otherwise increment screen
		}
		while (!rRIGHT);			// Check if button is still pressed do nothing
		printScreen();				// Refresh screen and print current menu and values
	}

	if (!rLEFT)										// if button Right is pressed
	{
		if (currentScreen == 0)						// check if its the first screen
		{
			currentScreen = (numOfScreens - 1);		// then change screen to last screen
		}
		else
		{
			currentScreen--;						// otherwise decrement screen
		}
		while (!rLEFT);				// Check if button is still pressed do nothing
		printScreen();				// Refresh screen and print current menu and values
	}

	if (!rACT)										// if button Action is pressed
	{
		if (currentScreen == 4)						// check if is fourth screen
		{
			sd_rw = !sd_rw;							// toggle read/write on SD card
		}
		if (currentScreen == 5)						// check if is fith screen
		{
			load_status = !load_status;				// toggle ON/OFF load
		}
		while (!rACT);				// Check if button is still pressed do nothing
		printScreen();				// Refresh screen and print current menu and values
	}
}



//	Function that checks the currentscreen scanned in function menuState()
//	and prints on the LCD the corresponding strings for the currentScreen
void printScreen()
{
	lcd.clear();									//	Clear the LCD
	lcd.print(screens[currentScreen][0]);			//	Print the current screen first line
	lcd.setCursor(0,1);								//	move the LCD cursor to the second line

	if (currentScreen == 4)							//	check if current screen is number 4
	{
		lcd.print(parameters_sd[sd_rw]);			//	print content of parameters_sd string to second line
	}
	else if (currentScreen == 5)					//	check if current screen is number 5
	{
		lcd.print(parameters_load[load_status]);	//	print content of parameters_load string to second line
	}
	else if (currentScreen == 6)					//	check if current screen is number 6
	{
		lcd.print(sd_card[sd_status]);				//	print content of sd_card string to second line
	}
	else
	{												//	otherwise
		lcd.print(parameters[currentScreen]);		//	print content of parameters string to second line
		lcd.print(" ");								//	print one blank space
		lcd.print(screens[currentScreen][1]);		//	and print content of screens second string
	}
}



//	Function that takes an analogue pin number, reads and converts to real voltage value
//	and returns it as a float
float volt_pin(int pin)
{
	float voltage;
	voltage = analogRead(pin);				// Obtain RAW voltage data
	voltage = (voltage / 1024) * 3.3;		// Convert to actual voltage
	voltage = voltage / denominator;		// Convert to voltage after divider

	return voltage;							//	return the final real voltage
}



//	Function that takes an analogue pin number, reads and converts to real amperage value
//	and returns it as a float
float amp_pin (int pin)
{
	int readAmpsADC = 0;										//	variable that holds the value read on analogue pin
	float amps = 0.0;											//	variable that holds final amperage value

	readAmpsADC = analogRead(pin);								//	read pin and store value on variable readAmpsADC
	amps = fabs(fmap(readAmpsADC, 0.0, 1023.0, 0.01, 3.3));		//	function to convert from the analogue value to real amperage value

	return amps;												//	return the final real amperage
}



//	Function that takes in, analogue pin value, minimum ADC value, maximum ADC value, minimum voltage, maximum voltage
//	and returns the raw value
float fmap(float x, float in_min, float in_max, float out_min, float out_max)
{
	return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;	// return the raw amperage value
}

