350 lines
13 KiB
C++
350 lines
13 KiB
C++
/*********************************************************************
|
|
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
|
|
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()) // 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()) {
|
|
ble_data = mySerial.read();
|
|
Serial.write(mySerial.read());
|
|
if (ble_data == 'A') {
|
|
dtostrf(parameters[battery_volt], 5, 2, outstr);
|
|
mySerial.write(outstr);
|
|
}
|
|
if (ble_data == 'B') {
|
|
dtostrf(parameters[voltage_1], 5, 2, outstr);
|
|
mySerial.write(outstr);
|
|
}
|
|
if (ble_data == 'C') {
|
|
dtostrf(parameters[voltage_2], 5, 2, outstr);
|
|
mySerial.write(outstr);
|
|
}
|
|
if (ble_data == 'D') {
|
|
dtostrf(parameters[curr], 5, 2, outstr);
|
|
mySerial.write(outstr);
|
|
}
|
|
if (ble_data == 'E') {
|
|
load_status = on;
|
|
}
|
|
if (ble_data == 'F') {
|
|
load_status = off;
|
|
}
|
|
}
|
|
}
|
|
//-------------------------------------------
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
Serial.write(ble_data); // For debug only
|
|
|
|
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
|
|
}
|
|
|