///////////////////////////////////////////////////////////////
////////////////////////Included Libraries/////////////////////
///////////////////////////////////////////////////////////////
#include <Wire.h>
#include <Keypad.h>
#include <Adafruit_GFX.h> // Include core graphics library for the display
#include <Adafruit_SSD1306.h> // Include Adafruit_SSD1306 library to drive the display
#include <SPI.h>
#include <SD.h>
///////////////////////////////////////////////////////////////
//////////////////////////LCD Setup////////////////////////////
///////////////////////////////////////////////////////////////
Adafruit_SSD1306 display(128, 32); // Create display
#include <Fonts/FreeMono9pt7b.h> // Add a custom font
///////////////////////////////////////////////////////////////
////////////////////////Keypad Setup///////////////////////////
///////////////////////////////////////////////////////////////
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] =
{
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
// R R R R
// 1 2 3 4
byte rowPins[ROWS] = {5, 9, 3, 2};
// C C C
// 1 2 3
byte colPins[COLS] = {8, 7, 6};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
char num[20];
///////////////////////////////////////////////////////////////
///////////////////////////Functions/////////////////////////// These need to be declared before the function pointer.
///////////////////////////////////////////////////////////////
void Date()
{
GetNumber();
}
void Price()
{
GetNumber();
SaveSD();
}
void Litres()
{
GetNumber();
}
void Miles()
{
GetNumber();
}
///////////////////////////////////////////////////////////////
////////////////////////Button Variables///////////////////////
///////////////////////////////////////////////////////////////
bool ButtonFlag = LOW;
///////////////////////////////////////////////////////////////
/////////////////////////Menu Variables////////////////////////
///////////////////////////////////////////////////////////////
const int NoofMenus = 4; //Instantiate NoOfMenus to be a constant integer of 4. This is how many menus I will have in have in my code. I have used constant int here for the same reason as before.
typedef void (* GenericFP)(); //Here I am creating an alias for void. I am saying that I want GenericFP to work as a void when I call 'GenericFP'. This enables me to use pointers in arrays to call functions, as shown below:
GenericFP ControlSystemFunctions[NoofMenus] =
{&Date, &Price, &Litres, &Miles};
//Using GenericFP as described above in conjunction with this array, I can point to a location in my array and call a function as if I were typing 'void function();'.
//I can call ControlSystemFunctions[number between 0-3] in my code and it will perform the function assosiated with that memory location in the array.
/////////Menu Variables////////
int FunctionPointer = 0; //This determines the Control System Function which is selected out of the GenericFP array. Initially this is set as 0.
char* MainMenuNames[NoofMenus] =
{"Date:","Price:","Litres:","Miles:"};
///////////////////////////////////////////////////////////////
//////////////////////////////Setep////////////////////////////
///////////////////////////////////////////////////////////////
void setup()
{
delay(500); // This delay is needed to let the display to initialize
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // Initialize display with the I2C address of 0x3C
display.clearDisplay(); // Clear the buffer
display.setTextColor(WHITE); // Set color of the text
display.setRotation(0); // Set orientation. Goes from 0, 1, 2 or 3
display.setTextWrap(false); // By default, long lines of text are set to automatically "wrap" back to the leftmost column.
// To override this behavior (so text will run off the right side of the display - useful for
// scrolling marquee effects), use setTextWrap(false). The normal wrapping behavior is restored
// with setTextWrap(true).
display.dim(0); //Set brightness (0 is maximun and 1 is a little dim)
Serial.begin(9600);
Serial.print("Initializing SD card...");
if (!SD.begin(4))
{
Serial.println("initialization failed!");
while (1);
}
Serial.println("initialization done.");
}
///////////////////////////////////////////////////////////////
////////////////////////////Main Loop//////////////////////////
///////////////////////////////////////////////////////////////
void loop()
{
//Serial.println(FunctionPointer);
ControlSystemFunctions[FunctionPointer](); //Run the code determined by the function pointer.
ReadFlag();
MenuCode();
ResetFlag(); //After every loop I want to ensure the flag is reset to ensure that on the next loop any change in state of the flags can be seen again.
}
///////////////////////////////////////////////////////////////
///////////////////////////Menu System/////////////////////////
///////////////////////////////////////////////////////////////
void MenuCode()
{
display.clearDisplay(); // Clear the display so we can refresh
//display.drawRect(0, 0, 128, 32, WHITE); // Draw rectangle (x,y,width,height,color
display.setFont(&FreeMono9pt7b); // Set a custom font
display.setTextSize(0); // Set text size. We are using a custom font so you should always use the text size of 0
display.setCursor(0, 19); // (x,y) (5,11) = MIN
display.println(MainMenuNames[FunctionPointer]); // Text or value to print
if (FunctionPointer == 0)
{
display.setCursor (60,19);
}
if (FunctionPointer == 1 || FunctionPointer == 3)
{
display.setCursor (70,19);
}
if (FunctionPointer == 2)
{
display.setCursor (75,19);
}
display.println(num);
display.display();
}
void ReadFlag() //This function essentially tells my control system what to do when a button is pressed.
{
if (ButtonFlag == HIGH) //This 'if' structure acts as the functionality behind my scroll button.
{
FunctionPointer = (FunctionPointer + 1) % NoofMenus; //Assign FunctionPointer a new, incrimented value so long as it is not bigger than the number of menus.
//'% NoofMenus' ensures that when the FunctionPointer reaches 8, the next scroll puts the counter back to 0. This is due to the remainder of 8 % 8 being 0.
}
}
void ResetFlag() //This resets the flags on both programmable buttons in both system states, normal and alternate (or extra).
{
ButtonFlag = LOW;
}
///////////////////////////////////////////////////////////////
///////////////////////////Functions///////////////////////////
///////////////////////////////////////////////////////////////
void GetNumber()
{
static byte offset = 0;
char key = keypad.getKey();
if (key != NO_KEY)
{
switch (key)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
num[offset] = key;
offset++;
//add a null terminator for the currently entered ASCII number
num[offset] = 0;
float temp;
temp = atof(num);
display.display();
break;
case '*':
num[offset] = '.';
offset++;
display.display();
break;
case '#':
offset = 0;
memset(num, 0, sizeof num);
ButtonFlag = HIGH;
break;
} //END of switch/case
} //END of if (key != NO_KEY)
} //END of GetNumber()
void SaveSD()
{
File myFile = SD.open("test1.txt", FILE_WRITE);
if (myFile) {
Serial.print("Writing to test.txt...");
myFile.print(num);
myFile.print(",");
// close the file:
myFile.close();
Serial.println("done.");
} else {
// if the file didn't open, print an error:
Serial.println("error opening test.txt");
}
}