#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <qrcode.h>
// OLED width and height
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
// OLED reset pin (usualy do not use)
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
void generateQRCode(const char* text) {
// Create a QR code object
QRCode qrcode;
// Define the size of the QR code (1-40, higher means bigger size)
uint8_t qrcodeData[qrcode_getBufferSize(2)];
qrcode_initText(&qrcode, qrcodeData, 2, ECC_LOW, text);
// Clear the display
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(63, 20);
display.print("Selected:");
display.setCursor(63, 30);
display.print(text);
// Calculate the scale factor
//int scale = min(SCREEN_WIDTH / qrcode.size, SCREEN_HEIGHT / qrcode.size);
int scale = 2;
// Calculate horizontal shift
int shiftX = (SCREEN_WIDTH - qrcode.size*scale)/8;
// Calculate horizontal shift
int shiftY = ((SCREEN_HEIGHT) - qrcode.size*scale)/1;
// Draw the QR code on the display
for (uint8_t y = 0; y < qrcode.size; y++) {
for (uint8_t x = 0; x < qrcode.size; x++) {
if (qrcode_getModule(&qrcode, x, y)) {
display.fillRect(shiftX + x*scale, shiftY + y*scale, scale, scale, WHITE);
}
}
}
// Update the display
display.display();
}
// Rotary Encoder pins
const int encoderPinA = 2;
const int encoderPinB = 4;
const int encoderButtonPin = 3;
class Menu {
private:
char** items;
//har** itemsValue;
int numItems;
int currentIndex;
char* title;
Menu* parentMenu;
public:
//Menu(char** menuItems, char** itemsVal, int itemCount, String menuTitle, Menu* parent = nullptr) {
Menu(char** menuItems, int itemCount, char* menuTitle, Menu* parent = nullptr) {
items = menuItems;
//itemsValue = itemsVal;
numItems = itemCount;
currentIndex = 0;
title = menuTitle;
parentMenu = parent;
}
void nextItem() {
currentIndex = (currentIndex + 1) % numItems;
}
void previousItem() {
currentIndex = (currentIndex - 1 + numItems) % numItems;
}
void setCurrentIndex(int index) {
currentIndex = index;
}
char* selectItem() {
return items[currentIndex];
}
void displayMenu() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print(title);
display.setTextSize(1);
int start = max(0, currentIndex - 2);
int end = min(numItems, start + 5);
for (int i = start; i < end; i++) {
if (i == currentIndex) {
display.fillRect(0, 14 + (i - start) * 12, SCREEN_WIDTH, 14, WHITE);
display.setTextColor(BLACK);
} else {
display.setTextColor(WHITE);
}
display.setCursor(0, 17 + (i - start) * 12);
display.print(items[i]);
/*if (String(items[i]).startsWith("Back")) {
display.print(items[i]);
} else {
display.print(String(items[i]) + " XX");
}*/
}
display.display();
}
Menu* getParentMenu() {
return parentMenu;
}
int getCurrentIndex() {
return currentIndex;
}
};
// Menu Items
const char *mainMenuItems[] = {"Item 1", "Item 2", "Item 3", "Item 4", "Item 5"};
const char *subMenu1Items[] = {"SubItem 1", "SubItem 2", "SubItem 3", "Back"};
const char *subMenu2Items[] = {"Sub2Item 1", "Sub2Item 2", "Back"};
/*const char *mainMenuItemsVal[] = {"00", "00", "AA", "BB", "CC"};
const char *subMenu1ItemsVal[] = {"00", "AA", "BB", "CC"};
const char *subMenu2ItemsVal[] = {"00", "AA", "BB", "CC"};*/
const int numMainMenuItems = sizeof(mainMenuItems) / sizeof(mainMenuItems[0]);
const int numSubMenu1Items = sizeof(subMenu1Items) / sizeof(subMenu1Items[0]);
const int numSubMenu2Items = sizeof(subMenu2Items) / sizeof(subMenu2Items[0]);
// Menu objects
Menu mainMenu(mainMenuItems, numMainMenuItems, "Main Menu");
Menu subMenu1(subMenu1Items, numSubMenu1Items, "SubMenu1", &mainMenu);
Menu subMenu2(subMenu2Items, numSubMenu2Items, "SubMenu2", &subMenu1);
/*Menu mainMenu(mainMenuItems, mainMenuItemsVal, numMainMenuItems, "Main Menu");
Menu subMenu(subMenu1Items, subMenu1ItemsVal, numSubMenu1Items, "SubMenu1", &mainMenu);
Menu sub2Menu(subMenu2Items, subMenu2ItemsVal, numSubMenu2Items, "SubMenu2", &subMenu);*/
// Variable that holds the current menu
Menu* currentMenu = &mainMenu;
// Rotary Encoder Variables
//volatile int lastEncoded = 0;
volatile long encoderValue = 0;
long lastencoderValue = 0;
bool buttonPressed = false;
void buttonPress() {
buttonPressed = true;
}
// Encoder read function
void updateEncoder() {
int ENCB = digitalRead(encoderPinB);
if (ENCB > 0) {
encoderValue++;
} else {
encoderValue--;
}
/*int MSB = digitalRead(encoderPinA); // MSB = most significant bit
int LSB = digitalRead(encoderPinB); // LSB = least significant bit
int encoded = (MSB << 1) | LSB; // Modified 2-bit value
int sum = (lastEncoded << 2) | encoded; // Combine old and new values
if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue++;
if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue--;
lastEncoded = encoded;*/
}
void selectedItemFunc(const char* item) {
char buffer[40];
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print("Selected:");
display.setCursor(0, 30);
display.print(item);
display.display();
delay(1000);
//sprintf(buffer, "Selected: %s", item);
//generateQRCode(buffer);
generateQRCode(item);
delay(5000);
}
void setup() {
Serial.begin(115200);
while ( !Serial ) yield();
Serial.println("Initializing...");
// OLED starting
if(!display.begin(SSD1306_EXTERNALVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.display();
delay(2000);
display.clearDisplay();
// Set Rotary Encoder pins as input
pinMode(encoderPinA, INPUT);
pinMode(encoderPinB, INPUT);
pinMode(encoderButtonPin, INPUT_PULLUP);
// Set interrupt operation for encoder inputs
attachInterrupt(digitalPinToInterrupt(encoderPinA), updateEncoder, RISING);
//attachInterrupt(digitalPinToInterrupt(encoderPinB), updateEncoder, CHANGE);
attachInterrupt(digitalPinToInterrupt(encoderButtonPin), buttonPress, FALLING);
// Show menu item at startup
currentMenu->displayMenu();
}
void loop() {
// Check encoder value and update menu
if (encoderValue != lastencoderValue) {
if (encoderValue > lastencoderValue) {
currentMenu->nextItem();
} else {
currentMenu->previousItem();
}
lastencoderValue = encoderValue;
currentMenu->displayMenu();
//delay(200); // Encoder Debouncing
}
// Select item when Encoder button is pressed
if (buttonPressed) {
buttonPressed = false;
if (currentMenu == &mainMenu) {
switch(currentMenu->getCurrentIndex()) {
case 2: currentMenu = &subMenu1; currentMenu->setCurrentIndex(0);
break;
default: selectedItemFunc(currentMenu->selectItem());
break;
}
} else if (currentMenu == &subMenu1) {
switch(currentMenu->getCurrentIndex()) {
case 2: currentMenu = &subMenu2; currentMenu->setCurrentIndex(0);
break;
case 3: currentMenu = currentMenu->getParentMenu();
break;
default: selectedItemFunc(currentMenu->selectItem());
break;
}
} else if (currentMenu == &subMenu2) {
switch(currentMenu->getCurrentIndex()) {
case 2: currentMenu = currentMenu->getParentMenu();
break;
default: selectedItemFunc(currentMenu->selectItem());
break;
}
}
currentMenu->displayMenu();
//delay(500); // Buton debouncing
}
/*
if (digitalRead(encoderButtonPin) == LOW) {
char* selectedItem = currentMenu->selectItem();
if (selectedItem == "Back") {
if (currentMenu->getParentMenu() != nullptr) {
currentMenu = currentMenu->getParentMenu();
}
} else if (selectedItem == "Item 3") {
currentMenu = &subMenu1;
} else if (selectedItem == "SubItem 3") {
currentMenu = &subMenu2;
} else {
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.print("Selected:");
display.setCursor(0, 30);
display.print(selectedItem);
display.display();
delay(1000);
currentMenu->displayMenu();
}
currentMenu->displayMenu();
delay(500); // Buton debouncing
}*/
}