#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <PinButton.h>
#include <EEPROM.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
// The pins for I2C are defined by the Wire-library.
// On an arduino UNO: A4(SDA), A5(SCL)
// On an arduino MEGA 2560: 20(SDA), 21(SCL)
// On an arduino LEONARDO: 2(SDA), 3(SCL), ...
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Create a new button object, listening on pin 5.
// Note: you can create as many as you like.
PinButton buttonA(11);
PinButton buttonB(12);
const int MOTOR_PIN = 10;
bool bVibrate = false;
int motorState = LOW;
int int_vibrationinterval = 1000;
const int VIBRATE_SHORT = 1000;
const int VIBRATE_LONG = 2000;
unsigned long previousMillis = 0;
enum menu {
M_START,
M_SETTINGS,
M_CREDITS
};
menu currentMenu = M_START;
enum submenu_setting {
LEFTHAND_MODE,
SQUARES
};
submenu_setting submenuSettings = SQUARES;
submenu_setting currentSubmenuSettings = SQUARES;
enum submenu_squares {
SQ_FOUR_SUM,
SQ_FOUR_BDAY
};
submenu_squares currentSubmenuSquares = SQ_FOUR_SUM;
enum states {
MENU,
START,
SETTINGS,
LEFTHAND,
SUB_SQUARES,
CREDITS,
SHOW_MS
};
states currentState = START;
states selectedState = MENU;
enum squares {
FOUR_SUM,
FOUR_BDAY
};
squares menuSquare = FOUR_SUM;
int squaresize = 4;
bool squareissum = true;
bool leftHandMode = false;
// Initialize variables for the input number
int oneNumber = 5;
int twoNumber = 5;
int threeNumber = 5;
int numberOfDigits = 1;
int activeNumber = 1;
int showLine = 1;
//init variables fpr birthday MS
int day1Value = 1;
int day2Value = 5;
int day1max = 3;
int day1min = 0;
int day2max = 9;
int day2min = 0;
int month1Value = 0;
int month1min = 0;
int month1max = 1;
int month2Value = 5;
int month2min = 0;
int month2max = 9;
int century1Value = 1;
int century1min = 0;
int century1max = 2;
int century2Value = 9;
int century2min = 0;
int century2max = 9;
int year1Value = 5;
int year1min = 0;
int year1max = 9;
int year2Value = 5;
int year2min = 0;
int year2max = 9;
// Initialize variables for the results
int result[16]; // For result1 through result16
int pos1;
int totals;
int ms4sum = 34;
// Array simulating the ms4[] array from the original JavaScript
int ms3[9] = { 8, 1, 6, 3, 5, 7, 4, 9, 2 }; // Adjusted for 3x3
int ms4[16] = { 9, 6, 3, 16, 4, 15, 10, 5, 14, 1, 8, 11, 7, 12, 13, 2 };
// Definiere Sprachen: 0 = Deutsch, 1 = Englisch, 2 = Spanisch, 3 = Französisch
int sprache = 0; // Aktuelle Sprache (Standard: Deutsch)
void doVibration(int duration){
bVibrate = true;
int_vibrationinterval = duration;
}
void printOnDisplay(int16_t x, int16_t y, int number) {
char buf[4];
itoa(number, buf, 10);
display.setCursor(x, y);
display.print(buf);
Serial.print("printing: ");
Serial.println(buf);
}
void printOnDisplay(int16_t x, int16_t y, String text) {
display.setCursor(x, y);
display.print(text);
}
void showMenu() {
display.setTextSize(2); //height=9 pixels, width=5 pixels at textsize 1
display.clearDisplay();
switch (selectedState) {
case MENU:
switch (currentState) {
case START:
display.setCursor(0, 12);
display.print("START");
display.display();
break;
case SETTINGS:
display.setCursor(0, 12);
display.print("SETTINGS");
display.display();
break;
case CREDITS:
display.setCursor(0, 12);
display.print("CREDITS");
display.display();
break;
}
break;
}
}
void showSettings() {
display.clearDisplay();
display.setTextSize(1);
printOnDisplay(0, 0, "Settings");
display.setTextSize(2);
switch (currentSubmenuSettings) {
case LEFTHAND_MODE:
if (leftHandMode) {
display.setRotation(2); //rotates text on OLED 1=90 degrees, 2=180 degrees
display.setCursor(0, 12);
display.print("> Leftymode");
display.display();
} else {
display.setRotation(0); //rotates text on OLED 1=90 degrees, 2=180 degrees
display.setCursor(0, 12);
display.print("Leftymode");
display.display();
}
break;
case SQUARES:
printOnDisplay(0, 12, "set Square");
break;
}
display.display();
}
void showSubmenuSquares() {
display.clearDisplay();
display.setTextSize(1);
printOnDisplay(0, 0, "Squares-Settings");
display.setTextSize(2);
switch (menuSquare) {
case FOUR_SUM:
if (squaresize == 4 && squareissum) {
printOnDisplay(0, 12, "> 4x4 Sum");
} else {
printOnDisplay(0, 12, "4x4 Sum");
}
break;
case FOUR_BDAY:
if (squaresize == 4 && !squareissum) {
printOnDisplay(0, 12, "> 4x4 Bday");
} else {
printOnDisplay(0, 12, "4x4 Bday");
}
break;
}
display.display();
}
// Function to validate if input is a number
bool validate(String data) {
for (int i = 0; i < data.length(); i++) {
if (!isDigit(data.charAt(i))) {
return false;
}
}
return true;
}
void setup() {
Serial.begin(9600);
if (EEPROM.read(0) == 1) {
squaresize = EEPROM.read(10);
squareissum = EEPROM.read(20);
leftHandMode = EEPROM.read(30);
} else {
EEPROM.write(0, 1);
EEPROM.write(10, squaresize);
EEPROM.write(20, squareissum);
EEPROM.write(20, leftHandMode);
}
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 begin failed"));
for (;;)
;
}
display.clearDisplay();
display.setTextSize(1); //height=9 pixels, width=5 pixels at textsize 1
display.setTextColor(SSD1306_WHITE); // Draw white text
Serial.println(F("Los gehts"));
}
void calcMagicSquare() {
if (squaresize == 4 && squareissum) {
Serial.println("Calc 4x4 SUM");
pos1 = oneNumber;
if (numberOfDigits == 2)
pos1 = (oneNumber * 10) + twoNumber;
if (numberOfDigits == 3)
pos1 = (oneNumber * 100) + (twoNumber * 10) + threeNumber;
Serial.println(pos1);
// Perform the calculations as in the original JavaScript code
int workms4 = pos1 - ms4sum;
int b = workms4 / squaresize;
int c = (b + (pos1 - ms4sum)) - (b * squaresize);
// Calculate the results based on ms4[] array
result[0] = ms4[0] + b;
result[1] = ms4[1] + b;
result[2] = ms4[2] + b;
result[3] = ms4[3] + c;
result[4] = ms4[4] + b;
result[5] = ms4[5] + c;
result[6] = ms4[6] + b;
result[7] = ms4[7] + b;
result[8] = ms4[8] + c;
result[9] = ms4[9] + b;
result[10] = ms4[10] + b;
result[11] = ms4[11] + b;
result[12] = ms4[12] + b;
result[13] = ms4[13] + b;
result[14] = ms4[14] + c;
result[15] = ms4[15] + b;
} else if (squaresize == 4 && !squareissum) {
Serial.println("Calc BDAY");
int dd = (day1Value * 10) + day2Value;
int mm = (month1Value * 10) + month2Value;
int cc = (century1Value * 10) + century2Value;
int yy = (year1Value * 10) + year2Value;
Serial.println(dd);
Serial.println(mm);
Serial.println(cc);
Serial.println(yy);
// Calculate the results based on ms4[] array
result[0] = dd;
result[1] = mm;
result[2] = cc;
result[3] = yy;
result[4] = yy + 1;
result[5] = cc - 1;
result[6] = mm - 3;
result[7] = dd + 3;
result[8] = mm - 2;
result[9] = dd + 2;
result[10] = yy + 2;
result[11] = cc - 2;
result[12] = cc + 1;
result[13] = yy - 1;
result[14] = dd + 1;
result[15] = mm - 1;
}
}
void showMSLines(int line) {
if (squaresize == 4) {
Serial.println("Magisches Quadrat mit gewünschter Summe:");
for (int i = 0; i <= 12; i = i + 4) {
for (int j = 0; j < 4; j++) {
Serial.print(result[i + j]);
Serial.print('\t'); // Tabulator für bessere Lesbarkeit
}
Serial.println(); // Neue Zeile für jede Reihe
}
display.clearDisplay();
int x = 20;
int y = 0;
int i = 0;
int last = 3;
if (line == 2) {
i = 4;
last = 7;
} else if (line == 3) {
i = 8;
last = 11;
}
if (line == 4) {
i = 12;
last = 15;
}
display.setTextSize(1);
printOnDisplay(0, 0, "Line");
printOnDisplay(33, 0, line);
printOnDisplay(41, 0, ":");
display.display();
display.setTextSize(1);
for (i; i <= last; i++) {
printOnDisplay(y, x, result[i]);
display.display();
y = y + 30;
}
}
}
void showMS() {
Serial.println("Magisches Quadrat mit gewünschter Summe:");
for (int i = 0; i <= 12; i = i + 4) {
for (int j = 0; j < 4; j++) {
Serial.print(result[i + j]);
Serial.print('\t'); // Tabulator für bessere Lesbarkeit
}
Serial.println(); // Neue Zeile für jede Reihe
}
display.clearDisplay();
char buf[4];
int x = 7;
int xfactor = 8;
int rowfactor = 0;
for (int j = 1; j <= 4; j++) {
int y = 0;
for (int i = 0; i < 4; i++) {
itoa(result[i + rowfactor], buf, 10);
printOnDisplay(y, x, buf);
y = y + 20;
}
x = x + xfactor;
rowfactor = rowfactor + 4;
}
}
void showCredits() {
display.clearDisplay();
display.setTextSize(1);
printOnDisplay(0, 10, "Channing Moos");
printOnDisplay(0, 20, "Tim Holzhausen");
display.display();
}
void showInputSum() {
display.clearDisplay();
display.setTextSize(1);
printOnDisplay(0, 0, "Input the Sum");
display.setTextSize(2);
Serial.print("Number of Digits: ");
Serial.println(numberOfDigits);
if (numberOfDigits == 1) {
display.drawFastHLine(0, 31, 9, SSD1306_WHITE);
}
printOnDisplay(0, 15, oneNumber);
if (numberOfDigits == 2) {
Serial.println("nummer2");
printOnDisplay(30, 15, twoNumber);
display.drawFastHLine(30, 31, 9, SSD1306_WHITE);
}
if (numberOfDigits == 3) {
Serial.println("nummer3");
printOnDisplay(30, 15, twoNumber);
printOnDisplay(60, 15, threeNumber);
display.drawFastHLine(60, 31, 9, SSD1306_WHITE);
}
display.display();
}
void showInputBirthday() {
display.clearDisplay();
display.setTextSize(1);
printOnDisplay(0, 0, "Input the Birthday");
//Draw line under active number
if (activeNumber == 1) {
display.drawFastHLine(0, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 2) {
display.drawFastHLine(12, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 3) {
display.drawFastHLine(30, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 4) {
display.drawFastHLine(42, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 5) {
display.drawFastHLine(60, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 6) {
display.drawFastHLine(72, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 7) {
display.drawFastHLine(84, 31, 9, SSD1306_WHITE);
} else if (activeNumber == 8) {
display.drawFastHLine(96, 31, 9, SSD1306_WHITE);
}
display.setTextSize(2);
//DD
printOnDisplay(0, 15, day1Value);
printOnDisplay(12, 15, day2Value);
printOnDisplay(20, 15, ".");
//MM
printOnDisplay(30, 15, month1Value);
printOnDisplay(42, 15, month2Value);
printOnDisplay(50, 15, ".");
//CC
printOnDisplay(60, 15, century1Value);
printOnDisplay(72, 15, century2Value);
//YY
printOnDisplay(84, 15, year1Value);
printOnDisplay(96, 15, year2Value);
display.display();
}
void handleBirthdayInputs(bool add) {
if (add) {
if (activeNumber == 1) {
if (day1Value < day1max)
day1Value++;
if (day1Value == 3){
day2Value = 0;
day2max = 1;
} else {
day2max = 9;
}
} else if (activeNumber == 2) {
if (day2Value < day2max)
day2Value++;
} else if (activeNumber == 3) {
if (month1Value < month1max)
month1Value++;
if (month1Value == 1) {
month2Value = 1;
month2max = 2;
} else {
month2max = 9;
}
} else if (activeNumber == 4) {
if (month2Value < month2max)
month2Value++;
} else if (activeNumber == 5) {
if (century1Value < century1max)
century1Value++;
if (century1Value == 2) {
century2Value = 0;
year1Value = 0;
year2Value = 0;
}
} else if (activeNumber == 6) {
if (century2Value < century2max)
century2Value++;
} else if (activeNumber == 7) {
if (year1Value < year1max)
year1Value++;
} else if (activeNumber == 8) {
if (year2Value < year2max)
year2Value++;
}
} else {
if (activeNumber == 1) {
if (day1Value > day1min)
day1Value--;
if (day1Value == 3){
day2Value = 0;
day2max = 1;
} else {
day2max = 9;
}
} else if (activeNumber == 2) {
if (day2Value > day2min)
day2Value--;
} else if (activeNumber == 3) {
if (month1Value > month1min)
month1Value--;
if (month1Value == 1) {
month2Value = 1;
month2max = 2;
} else {
month2max = 9;
}
} else if (activeNumber == 4) {
if (month2Value > month2min)
month2Value--;
} else if (activeNumber == 5) {
if (century1Value > century1min)
century1Value--;
if (century1Value == 2) {
century2Value = 0;
year1Value = 0;
year2Value = 0;
}
} else if (activeNumber == 6) {
if (century2Value > century2min)
century2Value--;
} else if (activeNumber == 7) {
if (year1Value > year1min)
year1Value--;
} else if (activeNumber == 8) {
if (year2Value > year2min)
year2Value--;
}
}
}
void handlePrimarySingleClick() {
Serial.println("primary single");
switch (selectedState) {
case SHOW_MS:
if (showLine > 1) {
showLine--;
showMSLines(showLine);
}
break;
case SETTINGS:
switch (currentSubmenuSettings) {
case LEFTHAND_MODE:
if (!leftHandMode)
leftHandMode = true;
else
leftHandMode = false;
EEPROM.write(30, leftHandMode);
showSettings();
break;
case SQUARES:
selectedState = SUB_SQUARES;
showSubmenuSquares();
break;
}
break;
case SUB_SQUARES:
switch (menuSquare) {
case FOUR_SUM:
squaresize = 4;
squareissum = true;
break;
case FOUR_BDAY:
squaresize = 4;
squareissum = false;
break;
}
EEPROM.write(10, squaresize);
EEPROM.write(20, squareissum);
showSubmenuSquares();
break;
case MENU:
switch (currentState) {
case START:
selectedState = START;
if (squareissum)
showInputSum();
else
showInputBirthday();
break;
case SETTINGS:
selectedState = SETTINGS;
display.clearDisplay();
showSettings();
break;
case CREDITS:
selectedState = CREDITS;
showCredits();
break;
}
break;
case START:
if (squareissum) {
if (activeNumber == 1 && oneNumber > 0)
oneNumber = oneNumber - 1;
if (activeNumber == 2 && twoNumber > 0)
twoNumber = twoNumber - 1;
if (activeNumber == 3 && threeNumber > 0)
threeNumber = threeNumber - 1;
showInputSum();
} else {
handleBirthdayInputs(false);
showInputBirthday();
}
break;
}
}
void handlePrimaryLongClick() {
Serial.println("A long");
Serial.print("activeNumber: ");
Serial.println(activeNumber);
Serial.print("selectedState");
Serial.println(selectedState);
display.clearDisplay();
switch (selectedState) {
case SHOW_MS:
showLine = 1;
selectedState = START;
if (squareissum) {
showInputSum();
} else {
showInputBirthday();
}
break;
case START:
if (activeNumber == 1) {
Serial.println("Switching to Menu");
selectedState = MENU;
display.clearDisplay();
showMenu();
oneNumber = 5;
} else {
if (squareissum) {
numberOfDigits--;
activeNumber--;
showInputSum();
} else {
activeNumber--;
showInputBirthday();
}
}
break;
case CREDITS:
case SETTINGS:
selectedState = MENU;
Serial.println("going back to MENU");
break;
case SUB_SQUARES:
selectedState = SETTINGS;
Serial.println("going back to SETTINGS");
showSettings();
break;
}
}
void handlePrimaryDoubleClick() {
Serial.println("A double");
}
void handleSecondarySingleClick() {
display.clearDisplay();
Serial.println("secondary single");
switch (selectedState) {
case SHOW_MS:
if (showLine < squaresize) {
showLine++;
showMSLines(showLine);
} else {
showMSLines(showLine);
}
break;
case MENU:
switch (currentState) {
case START:
currentState = SETTINGS;
break;
case SETTINGS:
currentState = CREDITS;
break;
case CREDITS:
currentState = START;
break;
}
break;
case SETTINGS:
switch (currentSubmenuSettings) {
case LEFTHAND_MODE:
currentSubmenuSettings = SQUARES;
break;
case SQUARES:
currentSubmenuSettings = LEFTHAND_MODE;
break;
}
showSettings();
break;
case SUB_SQUARES:
switch (menuSquare) {
case FOUR_SUM:
menuSquare = FOUR_BDAY;
showSubmenuSquares();
break;
case FOUR_BDAY:
menuSquare = FOUR_SUM;
showSubmenuSquares();
break;
}
break;
case START:
if (squareissum) {
if (activeNumber == 1 && oneNumber < 9)
oneNumber = oneNumber + 1;
if (activeNumber == 2 && twoNumber < 9)
twoNumber = twoNumber + 1;
if (activeNumber == 3 && threeNumber < 9)
threeNumber = threeNumber + 1;
showInputSum();
} else {
handleBirthdayInputs(true);
showInputBirthday();
}
break;
}
}
void handleSecondaryDoubleClick() {
Serial.println("B double");
switch (selectedState) {
case START:
calcMagicSquare();
showMSLines(showLine);
selectedState = SHOW_MS;
break;
}
}
void handleSecondaryLongClick() {
Serial.println("B long");
switch (selectedState) {
case START:
if (squareissum) {
if (numberOfDigits < 3) {
numberOfDigits++;
activeNumber++;
}
showInputSum();
} else {
if (activeNumber != 8)
activeNumber++;
showInputBirthday();
}
break;
}
}
void handleClicks() {
// Read hardware pin, convert to click events
buttonA.update();
buttonB.update();
// Print a message for each type of click.
// Note that "click" is also generated for the
// first click of a double-click, but "single"
// is only generated if it definitely is not
// going to be a double click.
if (buttonA.isSingleClick()) {
Serial.println("A single");
doVibration(VIBRATE_SHORT);
if (!leftHandMode)
handlePrimarySingleClick();
else
handleSecondarySingleClick();
}
if (buttonA.isDoubleClick()) {
doVibration(VIBRATE_SHORT);
if (!leftHandMode)
handlePrimaryDoubleClick();
else
handleSecondaryDoubleClick();
}
if (buttonA.isLongClick()) {
doVibration(VIBRATE_LONG);
if (!leftHandMode)
handlePrimaryLongClick();
else
handleSecondaryLongClick();
}
if (buttonB.isSingleClick()) {
doVibration(VIBRATE_SHORT);
Serial.println("B single");
if (!leftHandMode)
handleSecondarySingleClick();
else
handlePrimarySingleClick();
}
if (buttonB.isDoubleClick()) {
doVibration(VIBRATE_SHORT);
if (!leftHandMode)
handleSecondaryDoubleClick();
else
handlePrimaryDoubleClick();
}
if (buttonB.isLongClick()) {
doVibration(VIBRATE_LONG);
if (!leftHandMode)
handleSecondaryLongClick();
else
handlePrimaryLongClick();
}
}
void loop() {
//Handle LeftHandMode
if (leftHandMode) {
display.setRotation(2); //rotates text on OLED 1=90 degrees, 2=180 degrees
} else {
display.setRotation(0); //rotates text on OLED 1=90 degrees, 2=180 degrees
}
// listen to buttons
handleClicks();
showMenu();
unsigned long currentMillis = millis();
if (bVibrate){
// save the time the vibration started
if (motorState != HIGH){
previousMillis = currentMillis;
Serial.println("starting vibration");
}
motorState = HIGH;
digitalWrite(MOTOR_PIN, motorState);
}
if (currentMillis - previousMillis >= int_vibrationinterval && motorState == HIGH ) {
Serial.println("stoping vibration");
// set the boolean back to false
bVibrate = false;
motorState = LOW;
// turn off the vibration
digitalWrite(MOTOR_PIN, LOW);
}
}
Loading
ssd1306
ssd1306