/*
List of fonts: https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts
If you have the 128x64 version, you need to edit the file called Adafruit_SSD1306.h that is on the
Adafruit_SSD1306-master folder. The library comes by default set to use the 128x32 display.
For this reason you need to define the correct resolution like this:
Quote:
#define SSD1306_128_64
// #define SSD1306_128_32
// #define SSD1306_96_16
Unquote.
Notes the "//" before each line to comment the other resolutions that we are not going to use.
*/
#include <Adafruit_GFX.h> // Include core graphics library for the display
#include <Adafruit_SSD1306.h> // Include Adafruit_SSD1306 library to drive the display
#include <Metro.h>
#include <Encoder.h>
#include <Bounce2.h>
#include <Fonts/FreeSans9pt7b.h> // Add a custom font
Adafruit_SSD1306 display; // Create display
int Variable1; // Create a variable to have something dynamic to show on the display
// Countdown variables
long unsigned int TotalSeconds = 0;
uint16_t remainingSeconds = 0;
uint16_t h = 0;
uint16_t m = 0;
uint16_t s = 0;
uint16_t rem = 0;
// Menu Variables
boolean hlight = 0;
int menuMax = 5;
int menuMode = 0;
int menuDelay = 500;
Metro blinkon = Metro(1000);
Metro blinkoff = Metro(1000);
Metro debounce = Metro(10);
// Keyframe Variables
char StartingKey;
char EndingKey;
// Encoder Variables
Encoder myEnc(2, 3);
int encState;
int encRead; //current encoder read
int encOld; //previous encoder read
const int EncButton = 4; //Pin # to which SW line is connected
byte oldButtonState = HIGH; // set previous buttonestate to HIGH because of pull-up resistor
const unsigned long debounceTime = 10; // milliseconds
unsigned long buttonPressTime; // when the switch last changed state
boolean buttonPressed = 0; // a flag variable
// Graphic variables
int progress = 0;
// Mode variables
// timelapse
int TL_hours = 0;
int TL_mins = 0;
#define SMLFONT FreeSans9pt7b
Metro nextfunc = Metro(4000);
class FlashBox
{
int OnTime;
int OffTime;
int x;
int y;
int w;
int h;
int highlight;
unsigned long previousMillis;
// constructor
public:
FlashBox(int xx, int yy, int width, int height, int on, int off)
{
x = xx;
y = yy;
w = width;
h = height;
OnTime = on;
OffTime = off;
highlight = 0;
previousMillis = 0;
blinkon.interval(OnTime);
blinkon.reset();
blinkoff.interval(OffTime);
blinkoff.reset();
}
void Flasher()
{
unsigned long currentMillis = millis();
// if ((blinkon.check()) && (highlight == 0))
if ((currentMillis - previousMillis >= OnTime) && (highlight == 0))
{
display.drawRect(x, y, w, h, WHITE);
display.display();
highlight = 1;
previousMillis = currentMillis;
//delay (1000);
}
// else if ((blinkoff.check()) && (highlight == 1))
else if ((currentMillis - previousMillis >= OffTime) && (highlight == 1))
{
display.drawRect(x, y, w, h, BLACK);
display.display();
highlight = 0;
previousMillis = currentMillis;
//delay(1000);
}
}
};
void setup() // Start of setup
{
// Encoder setup
pinMode(EncButton, INPUT);
digitalWrite(EncButton, HIGH); // Pull-Up resistor for switch
// Display Setup
delay(100); // 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)
} // End of setup
void loop() // Start of loop
{
getMenuMode(); // 0: Run Mode A 1: Run Mode B 2: Live Mode
} // End of loop
void getMenuMode() {
int mode = 1;
display.clearDisplay(); // Clear the display so we can refresh
display.setFont(&SMLFONT); // Set a custom font
display.setTextSize(1); // Set text size. We are using a custom font so you should always use the text size of 0
display.setTextColor(WHITE);
do {
encRead = myEnc.read() >> 2; // bitwise divide by 4
if (encRead < encOld)
{
mode--;
if (mode < 1) mode = 1;
encOld = encRead;
}
if (encRead > encOld)
{
mode++;
if (mode > menuMax) mode = menuMax;
encOld = encRead;
}
switch (mode) {
case 1:
display.clearDisplay(); // Clear the display so we can refresh
//drawgrid();
display.drawRoundRect(0, 0, 128, 64, 8, WHITE);
display.setCursor(48, 12); // (x,y)
display.setFont();
display.print("SELECT");
//display.print(mode);
display.setFont(&SMLFONT);
display.setCursor(20, 38); // (x,y)
display.println("REALTIME");
display.setCursor(44, 55); // (x,y)
display.println("Mode");
display.display();
// RunMode('A','B');
break;
case 2:
display.clearDisplay(); // RecordMode()
//drawgrid();
display.drawRoundRect(0, 0, 128, 64, 8, WHITE);
display.setCursor(48, 12); // (x,y)
display.setFont();
display.print("SELECT");
//display.print(mode);
display.setFont(&SMLFONT);
display.setCursor(27, 38); // (x,y)
display.println("RECORD");
display.setCursor(44, 55); // (x,y)
display.println("Mode");
display.display();
break;
case 3:
display.clearDisplay(); // Clear the display so we can refresh
//drawgrid();
display.drawRoundRect(0, 0, 128, 64, 8, WHITE);
display.setCursor(48, 12); // (x,y)
display.setFont();
display.print("SELECT");
//display.print(mode);
display.setFont(&SMLFONT);
display.setCursor(47, 38); // (x,y)
display.println("LIVE");
display.setCursor(44, 55); // (x,y)
display.println("Mode");
display.display();
// LiveMode();
break;
case 4:
display.clearDisplay(); // SET TimeLapse menuMode = 4
//drawgrid();
display.drawRoundRect(0, 0, 128, 64, 8, WHITE);
display.setCursor(48, 12); // (x,y)
display.setFont();
display.print("SELECT");
//display.print(mode);
display.setFont(&SMLFONT);
display.setCursor(48, 38); // (x,y)
display.println("SET");
display.setCursor(22, 55); // (x,y)
display.println("Timelapse");
display.display();
checkSwitch();
if (buttonPressed) {
buttonPressed = 0;
display.drawRect(0, 0, 128, 64, WHITE);
display.display();
delay(menuDelay);
menuMode = 4;
LapseModeSet();
}
break;
case 5:
display.clearDisplay(); // RUN Timelapse MenuMode = 5
//drawgrid();
display.drawRoundRect(0, 0, 128, 64, 8, WHITE);
display.setCursor(48, 12); // (x,y)
display.setFont();
display.print("SELECT");
//display.print(mode);
display.setFont(&SMLFONT);
display.setCursor(46, 38); // (x,y)
display.println("RUN");
display.setCursor(22, 55); // (x,y)
display.println("Timelapse");
display.display();
checkSwitch();
if (buttonPressed) {
buttonPressed = 0;
display.drawRect(0, 0, 128, 64, WHITE);
display.display();
delay(menuDelay);
menuMode = 5;
LapseModeRun();
}
break;
}
} while (menuMode == 0);
}
void TimeCalc( uint32_t seconds ) {
uint32_t t = seconds;
h = (uint32_t)t / (uint32_t)3600L;
rem = (uint32_t)t % (uint32_t)3600L;
m = rem / 60;
s = rem % 60;
// h = t / 3600;
// rem = t % 3600;
// m = rem / 60;
// s = rem % 60;
}
void RunMode ( char startKF, char endKF ) {
nextfunc.reset();
TotalSeconds = 740;
do {
Variable1++; // Increase variable by 1
if (Variable1 > 150) // If Variable1 is greater than 150
{
Variable1 = 0; // Set Variable1 to 0
}
display.clearDisplay(); // Clear the display so we can refresh
display.setFont(&SMLFONT); // Set a custom font
display.setTextSize(1); // Set text size. We are using a custom font so you should always use the text size of 0
display.setTextColor(WHITE);
// Print text:
display.setCursor(0, 12); // (x,y)
display.print("Running "); display.print(startKF); display.print("<->"); display.print(endKF); // print start and end keyframes
display.setFont();
display.setCursor(0, 30); // (x,y)
// display.println("TIME REMAINING"); // Text or value to print
display.setCursor(0, 29);
display.println(" HOURS MIN. SEC.");
display.drawLine(0, 43, 128, 43, WHITE); // Draw Horizontal line
display.drawLine(42, 43, 42, 64, WHITE); // Draw Vertictal line
display.drawLine(84, 43, 84, 64, WHITE); // Draw Vertictal line
display.setFont(&SMLFONT);
TimeCalc(TotalSeconds);
display.setCursor(5, 62);
display.println(h); // Hours
display.setCursor(54, 62);
display.println(m); // Minutes
display.setCursor(97, 62);
display.println(s); // Seconds
--TotalSeconds;
// Draw Progress Bar rectangle:
display.drawRect(0, 22, 128, 1, WHITE); // Draw rectangle (x,y,width,height,color)
display.fillRect(0, 21, progress, 3, WHITE); // It draws from the location to down-right
progress++;
if (progress > 127) {
progress = 0;
}
//display.setFont(&FreeMonoBold12pt7b); // Set a custom font
// Print variable with left alignment:
display.setFont();
display.display(); // Print everything we set previously
checkSwitch(); // check encoder switch
if (buttonPressed) {
Variable1 = 0;
progress = 0;
display.clearDisplay();
display.display();
return;
}
} while (Variable1 < 128);
}
void LiveMode() {
// display.fillRect(0, 0, 128, 64, WHITE);
display.setFont(&SMLFONT);
display.setTextSize(0);
display.clearDisplay();
display.setCursor(10, 20); // (x,y)
display.setTextColor(WHITE);
while (!buttonPressed) {
display.println("Live Mode");
display.display();
}
return;
}
void RecordMode() {
// display.fillRect(0, 0, 128, 64, WHITE);
display.setFont(&SMLFONT);
display.setTextSize(0);
display.clearDisplay();
display.setCursor(10, 20); // (x,y)
display.setTextColor(WHITE);
while (!buttonPressed) {
display.println("Recording..");
display.display();
}
return;
}
void LapseModeSet() {
screen_Lapse();
FlashBox hours(1, 36, 63, 27, 200, 200); // Construct flashing box (Hours)
FlashBox mins(65, 36, 62, 27, 200, 200); // Construct flashing box (Minutes)
FlashBox TL_set(0, 0, 128, 64, 300, 300); // Construct TL Done flashing box
int mode = 0; // 0 = hours selected 1 = minutes selected
int valuechange = 0;
encOld = 0;
buttonPressed = 0;
myEnc.write(0);
do {
do {
encRead = myEnc.read() >> 2; // bitwise divide by 4
if (encRead < encOld)
{
mode--;
if (mode < 0) mode = 0;
encOld = encRead;
screen_Lapse();
}
if (encRead > encOld)
{
mode++;
if (mode > 2) mode = 2;
encOld = encRead;
screen_Lapse();
}
switch (mode)
{
case 0:
hours.Flasher(); // flash Hours Box
break;
case 1:
mins.Flasher(); // flash Minutes Box
break;
case 2:
TL_set.Flasher(); // flash whole screen Box
break;
}
checkSwitch();
if (buttonPressed)
{
buttonPressed = 0;
valuechange = 1;
if (mode == 0) myEnc.write(TL_hours);
if (mode == 1) myEnc.write(TL_mins);
screen_Lapse();
}
} while (!valuechange);
do {
if (mode == 0) // Adjust Hours
{
display.drawRect(1, 36, 63, 27, WHITE);
encRead = myEnc.read() >> 2; // bitwise divide by 4
TL_hours = constrain(encRead, 0, 100);
screen_Lapse();
checkSwitch();
if (buttonPressed)
{
valuechange = 0;
buttonPressed = 0;
}
}
if (mode == 1) // Adjust Minutes
{
display.drawRect(65, 36, 62, 27, WHITE);
encRead = myEnc.read() >> 2; // bitwise divide by 4
TL_mins = constrain(encRead, 0, 59);
screen_Lapse();
checkSwitch();
if (buttonPressed)
{
valuechange = 0;
buttonPressed = 0;
}
}
if (mode == 2) // Quit to Main Menu
{
valuechange = 0;
buttonPressed = 0;
menuMode = 0;
remainingSeconds = ((TL_hours * 60) * 60) + (TL_mins * 60);
}
} while (valuechange);
} while (menuMode == 4);
}
void LapseModeRun() {
// Serial.begin(9600);
TotalSeconds = remainingSeconds;
int percent = 0;
do {
display.clearDisplay(); // Clear the display so we can refresh
display.setFont(&SMLFONT); // Set a custom font
display.setTextSize(1); // Set text size. We are using a custom font so you should always use the text size of 0
display.setTextColor(WHITE);
display.setCursor(0, 12); // (x,y)
display.print("Running ");
display.setFont();
display.setCursor(0, 30); // (x,y)
// display.println("TIME REMAINING"); // Text or value to print
display.setCursor(0, 29);
display.println(" HOURS MIN. SEC.");
display.drawLine(0, 43, 128, 43, WHITE); // Draw Horizontal line
display.drawLine(42, 43, 42, 64, WHITE); // Draw Vertictal line
display.drawLine(84, 43, 84, 64, WHITE); // Draw Vertictal line
display.setFont(&SMLFONT);
TimeCalc(remainingSeconds);
//Serial.print(h); Serial.print(" ");Serial.print(m); Serial.print(" ");Serial.println(s);
display.setCursor(5, 62);
display.println(h); // Hours
display.setCursor(54, 62);
display.println(m); // Minutes
display.setCursor(97, 62);
display.println(s); // Seconds
// Draw Progress Bar rectangle:
progress = abs((((float)remainingSeconds / (float)TotalSeconds) * 128) - 128);
percent = ((float)progress / 128) * 100;
display.drawRect(0, 22, 128, 1, WHITE); // Draw rectangle (x,y,width,height,color)
display.fillRect(0, 21, progress, 3, WHITE); // It draws from the location to down-right
//display.setFont(&FreeMonoBold12pt7b); // Set a custom font
// Print variable with left alignment:
display.setFont();
display.setCursor(100, 5);
display.print(percent);
display.println("%");
// display.setCursor(80, 5);
// display.print(remainingSeconds);
display.display(); // Print everything we set previously
if (remainingSeconds == 0)
{
//display.getTextBounds("Running ",0,12,int x, int y, int ww, int hh);
display.fillRect(0, 0, 70, 20, BLACK);
display.setFont(&SMLFONT);
display.setCursor(0, 12); // (x,y)
display.print("DONE ");
display.display();
delay(50000);
menuMode = 0;
}
remainingSeconds--; // TICK!
checkSwitch(); // check encoder switch
if (buttonPressed) {
buttonPressed = 0;
menuMode = 0;
display.clearDisplay();
display.display();
return;
}
} while (menuMode == 5);
}
void checkSwitch() {
// Check Encoder Switch
byte buttonState = digitalRead(EncButton);
if (buttonState != oldButtonState)
{
if (millis () - buttonPressTime >= debounceTime)
{ // debounce
buttonPressTime = millis (); // when we closed the switch
oldButtonState = buttonState; // remember for next time
if (buttonState == LOW)
{
// Serial.println ("Button pressed"); // DEBUGGING: print that button has been closed
buttonPressed = 1;
}
else
{
// Serial.println ("Button released"); // DEBUGGING: print that button has been opened
buttonPressed = 0;
}
} // end if debounce time up
} // end of state change
}
void screen_Lapse() {
display.setFont(&SMLFONT);
display.setTextSize(0);
display.clearDisplay();
display.drawRect(0, 0, 128, 64, WHITE); // Draw Border
display.setCursor(4, 15); // (x,y)
display.setTextColor(WHITE);
display.println("Set Timelapse");
display.setFont();
display.setCursor(0, 25);
display.println(" HOURS MINUTES");
display.drawLine(0, 35, 128, 35, WHITE); // Draw Horizontal line
display.drawLine(64, 35, 64, 64, WHITE); // Draw Vertictal line
display.setFont(&SMLFONT);
display.setCursor(17, 55);
display.println(TL_hours);
display.setCursor(85, 55);
display.println(TL_mins);
display.display();
}
void drawgrid() {
display.drawRect(0, 0, 128, 64, WHITE);
display.drawLine(64, 0, 64, 128, WHITE);
display.drawLine(0, 32, 128, 32, WHITE);
}
// Adafruit Graphic library code references
// Draw triangle:
// display.drawTriangle(40,40, 50,20, 60,40, WHITE); // Draw triangle. X, Y coordinates for three corner points defining the triangle, followed by a color
// Draw filled triangle:
//display.fillTriangle(0,35, 15,45, 30,63, WHITE); // Draw filled triangle. X, Y coordinates for three corner points defining the triangle, followed by a color
// Draw line:
// display.drawLine(0, 18, 128, 18, WHITE); // Draw line (x0,y0,x1,y1,color)
// Draw circle:
// display.drawCircle(47, 36, 20, WHITE); // Draw circle (x,y,radius,color). X and Y are the coordinates for the center point
// Draw a filled circle:
// display.fillCircle(12, 27, 10, WHITE); // Draw filled circle (x,y,radius,color). X and Y are the coordinates for the center point
// Draw rectangle:
// display.drawRect(58, 0, 18, 18, WHITE); // Draw rectangle (x,y,width,height,color)
// It draws from the location to down-right
// Draw rounded rectangle and fill:
// display.fillRoundRect(58, 0, 18, 18, 5, WHITE); // Draw filled rounded rectangle (x,y,width,height,color)
// It draws from the location to down-right
// Draw rounded rectangle:
//display.drawRoundRect(79, 37, 49, 27, 8, WHITE); // Draw rounded rectangle (x,y,width,height,radius,color)
// It draws from the location to down-right
// Print variable with right alignment:
// display.setCursor(83, 60); // (x,y)
// display.println(string); // Text or value to print
//
// // Convert Variable1 into a string, so we can change the text alignment to the right:
// // It can be also used to add or remove decimal numbers.
// char string[10]; // Create a character array of 10 characters
// // Convert float to a string:
// dtostrf(Variable1, 3, 0, string); // (<variable>,<amount of digits we are going to use>,<amount of decimal digits>,<string name>)