LCD not updating after the initial splash text. I am trying to display a menu with several layers using an encoder. It just will not update.
#include <Wire.h>
#include <Encoder.h>
#include <EEPROM.h>
#include <USB.h>
#include <USBHIDKeyboard.h>
#include <USBHIDMouse.h>
#include <MenuSystem.h>
// Hardware Configuration
const int buttonPins[] = {48, 1, 42, 41, 13, 12, 11}; // Full set
const int numButtons = 7;
Encoder myEnc(38, 39); // Encoder pins
const int encButton = 10;
#define I2C_ADDRESS 0x27 // Confirmed working
USBHIDKeyboard Keyboard;
USBHIDMouse Mouse;
// Profile and Shortcut Definitions
const int maxProfiles = 6;
struct Profile {
char name[8];
int assignments[6];
};
Profile profiles[maxProfiles] = {
{"Model", {0, 1, 2, 3, 4, 9}}, {"Anim", {21, 22, 23, 24, 25, 26}},
{"Text", {5, 6, 13, 10, 12, 7}}, {"Py", {16, 19, 20, -1, -1, -1}},
{"Mouse", {27, 28, 29, 30, 31, -1}}, {"Win", {32, 33, 34, 35, 36, 37}}
};
int currentProfile = 0;
const char* shortcutKeys[] = {"G", "S", "R", "E", "Shift+A", "Ctrl+C", "Ctrl+V", "Tab", "Alt+Z", "Ctrl+Z",
"Ctrl+F", "Shift+Tab", "Ctrl+S", "Ctrl+X", "Ctrl+V", "Ctrl+D", "Ctrl+R", "Shift+Ctrl+C", "Alt+F", "F5",
"Ctrl+Space", "I", "Alt+A", "Shift+Space", "Ctrl+I", "Shift+D", "Alt+I", "MMB", "Ctrl+LMB", "Shift+MMB",
"Ctrl+WheelUp", "Ctrl+WheelDown", "Chrome", "Copy", "Paste", "Email", "Notepad", "Cmd"};
const char* shortcutNames[] = {"Move", "Scale", "Rotate", "Extrude", "Add", "Copy", "Paste", "EditMode",
"Wireframe", "Undo", "Find", "Unfold", "Save", "Cut", "PasteTxt", "Duplicate", "RunScript", "CopyPath",
"Format", "Reload", "AutoComp", "InsertKey", "PlayAnim", "PlaySpace", "InvertKey", "DupKey", "DelKey",
"Orbit", "Lasso", "Pan", "ZoomIn", "ZoomOut", "Chrome", "Copy", "Paste", "Email", "Notepad", "Cmd"};
int shortcutCount = 38;
// Custom Renderer for MenuSystem (empty as we handle display ourselves)
class MyRenderer : public MenuComponentRenderer {
public:
void render(Menu const& menu) const override {}
void render_menu_item(MenuItem const& menu_item) const override {}
void render_back_menu_item(BackMenuItem const& menu_item) const override {}
void render_numeric_menu_item(NumericMenuItem const& menu_item) const override {}
void render_menu(Menu const& menu) const override {}
};
// Function Prototypes
void lcdInit();
void lcdWriteNibble(uint8_t nibble, bool isData);
void lcdWriteByte(uint8_t data, bool isData);
void lcdClear();
void lcdSetCursor(uint8_t col, uint8_t row);
void lcdPrint(const char* str);
void sendShortcut(const char* shortcut);
void launchBlender();
void loadProfile(int profileId);
void saveProfile(int profileId);
void updateDisplayMenu();
void setupMenu();
void on_launch_selected(MenuComponent* p_menu_component);
void on_profile_selected(MenuComponent* p_menu_component);
void on_shortcut_selected(MenuComponent* p_menu_component);
void on_assign_selected(MenuComponent* p_menu_component);
// Menu System Setup
MyRenderer renderer;
MenuSystem ms(renderer);
Menu mainMenu("PrecisionDeck");
Menu profileMenu("Profiles");
Menu shortcutMenu("Shortcuts");
Menu assignMenu("Assign to");
MenuItem mi_launch("Launch Blender", &on_launch_selected);
// Variables
long oldPosition = -999;
int menuPos = 0;
bool inAssignMode = false;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;
unsigned long lastDisplayUpdate = 0;
unsigned long lastSplashTime = 0;
unsigned long lastActionTime = 0;
bool displayNeedsUpdate = true;
bool splashDisplayed = false;
bool actionDisplayed = false;
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("PrecisionDeck Booting...");
EEPROM.begin(512);
for (int i = 0; i < maxProfiles; i++) {
loadProfile(i);
}
for (int i = 0; i < numButtons; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(encButton, INPUT_PULLUP);
Wire.begin(21, 20);
Wire.setClock(50000);
Serial.println("Initializing LCD...");
lcdInit();
lcdClear();
lcdSetCursor(0, 0);
lcdPrint("PrecisionDeck");
lcdSetCursor(0, 1);
lcdPrint("Loading...");
lastSplashTime = millis();
Serial.println("LCD initialized");
Serial.println("Setting up menu...");
setupMenu();
ms.get_root_menu().add_menu(&mainMenu);
ms.select();
Serial.println("Menu setup complete");
Serial.println("Initializing USB...");
USB.begin();
Keyboard.begin();
Mouse.begin();
delay(200);
Serial.println("Setup complete");
}
void loop() {
if (!splashDisplayed && (millis() - lastSplashTime > 1000)) {
Serial.println("Splash timeout, updating display...");
updateDisplayMenu();
splashDisplayed = true;
}
long newPosition = myEnc.read() / 4;
if (newPosition != oldPosition) {
Serial.println("Encoder moved");
if (newPosition > oldPosition) ms.next();
else ms.prev();
oldPosition = newPosition;
displayNeedsUpdate = true;
}
static bool lastEncButtonState = HIGH;
bool encButtonState = digitalRead(encButton);
if (lastEncButtonState == HIGH && encButtonState == LOW) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay && lastEncButtonState == LOW && encButtonState == HIGH) {
Serial.println("Encoder button pressed");
ms.select();
displayNeedsUpdate = true;
lastActionTime = millis();
actionDisplayed = true;
}
lastEncButtonState = encButtonState;
for (int i = 1; i < numButtons; i++) {
static bool lastButtonState[numButtons] = {HIGH, HIGH, HIGH, HIGH, HIGH, HIGH, HIGH};
bool buttonState = digitalRead(buttonPins[i]);
if (lastButtonState[i] == HIGH && buttonState == LOW) {
if (i-1 < 6 && profiles[currentProfile].assignments[i-1] != -1) {
int shortcutIndex = profiles[currentProfile].assignments[i-1];
Serial.println("Button " + String(i+1) + " pressed");
lcdClear();
lcdSetCursor(0, 0);
lcdPrint(profiles[currentProfile].name);
lcdSetCursor(0, 1);
lcdPrint(("Btn" + String(i+1) + ": " + shortcutNames[shortcutIndex]).c_str());
sendShortcut(shortcutKeys[shortcutIndex]);
displayNeedsUpdate = true;
lastActionTime = millis();
actionDisplayed = true;
}
}
lastButtonState[i] = buttonState;
}
if (digitalRead(buttonPins[0]) == LOW) {
Serial.println("Button 1 pressed");
launchBlender();
displayNeedsUpdate = true;
lastActionTime = millis();
actionDisplayed = true;
}
if (actionDisplayed && (millis() - lastActionTime > 2000)) {
Serial.println("Action timeout, updating display...");
updateDisplayMenu();
actionDisplayed = false;
} else if (displayNeedsUpdate && (millis() - lastDisplayUpdate > 100)) {
Serial.println("Updating display in loop...");
updateDisplayMenu();
lastDisplayUpdate = millis();
displayNeedsUpdate = false;
}
yield();
}
// LCD Control Functions (unchanged from working sketch)
void lcdInit() {
delay(50);
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(0x03 << 4 | 0x08);
Wire.endTransmission();
delay(5);
lcdWriteNibble(0x03, false);
delay(5);
lcdWriteNibble(0x03, false);
delay(5);
lcdWriteNibble(0x02, false);
delay(5);
lcdWriteByte(0x28, false);
lcdWriteByte(0x0C, false);
lcdWriteByte(0x06, false);
lcdClear();
}
void lcdWriteNibble(uint8_t nibble, bool isData) {
uint8_t data = (nibble << 4) | 0x08;
if (isData) data |= 0x01;
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(data | 0x04);
Wire.endTransmission();
delayMicroseconds(1);
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(data & ~0x04);
Wire.endTransmission();
delayMicroseconds(50);
}
void lcdWriteByte(uint8_t data, bool isData) {
lcdWriteNibble(data >> 4, isData);
lcdWriteNibble(data & 0x0F, isData);
}
void lcdClear() {
lcdWriteByte(0x01, false);
delay(2);
}
void lcdSetCursor(uint8_t col, uint8_t row) {
uint8_t address = (row == 0) ? 0x00 : 0x40;
address += col;
lcdWriteByte(0x80 | address, false);
}
void lcdPrint(const char* str) {
while (*str) {
lcdWriteByte(*str++, true);
}
}
// PrecisionDeck Functions
void setupMenu() {
Serial.println("Adding profiles...");
for (int i = 0; i < maxProfiles; i++) {
profileMenu.add_item(new MenuItem(profiles[i].name, &on_profile_selected));
}
Serial.println("Adding shortcuts...");
for (int i = 0; i < shortcutCount; i++) {
shortcutMenu.add_item(new MenuItem(shortcutNames[i], &on_shortcut_selected));
}
Serial.println("Adding assignments...");
for (int i = 1; i < numButtons; i++) {
String btnName = "Button " + String(i+1);
char* btnNameChar = new char[btnName.length() + 1];
strcpy(btnNameChar, btnName.c_str());
assignMenu.add_item(new MenuItem(btnNameChar, &on_assign_selected));
delete[] btnNameChar;
}
mainMenu.add_menu(&profileMenu);
mainMenu.add_menu(&shortcutMenu);
mainMenu.add_menu(&assignMenu);
mainMenu.add_item(&mi_launch);
}
void updateDisplayMenu() {
lcdClear();
Menu const* cp_menu = ms.get_current_menu();
if (cp_menu == nullptr) {
Serial.println("Error: Current menu is null");
lcdPrint("Menu Error");
return;
}
MenuComponent const* cp_menu_component = cp_menu->get_current_component();
if (cp_menu_component == nullptr) {
Serial.println("Error: Current menu component is null");
lcdPrint("Component Error");
return;
}
lcdSetCursor(0, 0);
lcdPrint(cp_menu->get_name());
lcdSetCursor(0, 1);
lcdPrint("> ");
lcdPrint(cp_menu_component->get_name());
Serial.println("Display updated: " + String(cp_menu->get_name()) + " > " + String(cp_menu_component->get_name()));
}
void on_profile_selected(MenuComponent* p_menu_component) {
MenuItem* p_menu_item = (MenuItem*)p_menu_component;
for (int i = 0; i < maxProfiles; i++) {
if (strcmp(p_menu_item->get_name(), profiles[i].name) == 0) {
currentProfile = i;
break;
}
}
lcdClear();
lcdSetCursor(0, 0);
lcdPrint("Profile Set:");
lcdSetCursor(0, 1);
lcdPrint(profiles[currentProfile].name);
displayNeedsUpdate = true;
}
void on_shortcut_selected(MenuComponent* p_menu_component) {
MenuItem* p_menu_item = (MenuItem*)p_menu_component;
for (int i = 0; i < shortcutCount; i++) {
if (strcmp(p_menu_item->get_name(), shortcutNames[i]) == 0) {
menuPos = i;
inAssignMode = true;
break;
}
}
lcdClear();
lcdSetCursor(0, 0);
lcdPrint(("Assign: " + String(shortcutNames[menuPos])).c_str());
lcdSetCursor(0, 1);
lcdPrint("Press Btn 2-7");
unsigned long startTime = millis();
while (millis() - startTime < 5000 && inAssignMode) {
for (int i = 1; i < numButtons; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
profiles[currentProfile].assignments[i-1] = menuPos;
saveProfile(currentProfile);
lcdClear();
lcdSetCursor(0, 0);
lcdPrint(("Assigned to Btn" + String(i+1)).c_str());
lcdSetCursor(0, 1);
lcdPrint(shortcutNames[menuPos]);
inAssignMode = false;
displayNeedsUpdate = true;
break;
}
}
yield();
}
if (inAssignMode) {
lcdClear();
lcdSetCursor(0, 0);
lcdPrint("Assignment");
lcdSetCursor(0, 1);
lcdPrint("Cancelled");
inAssignMode = false;
displayNeedsUpdate = true;
}
}
void on_assign_selected(MenuComponent* p_menu_component) {
MenuItem* p_menu_item = (MenuItem*)p_menu_component;
int buttonIndex = -1;
for (int i = 1; i < numButtons; i++) {
String buttonName = "Button " + String(i+1);
if (strcmp(p_menu_item->get_name(), buttonName.c_str()) == 0) {
buttonIndex = i;
break;
}
}
if (buttonIndex >= 1) {
int assignedShortcut = profiles[currentProfile].assignments[buttonIndex-1];
lcdClear();
lcdSetCursor(0, 0);
lcdPrint(("Btn" + String(buttonIndex+1) + " to:").c_str());
lcdSetCursor(0, 1);
if (assignedShortcut != -1) lcdPrint(shortcutNames[assignedShortcut]);
else lcdPrint("None");
displayNeedsUpdate = true;
}
}
void on_launch_selected(MenuComponent* p_menu_component) {
launchBlender();
}
void loadProfile(int profileId) {
int baseAddr = profileId * 15;
for (int i = 0; i < 6; i++) {
profiles[profileId].assignments[i] = EEPROM.read(baseAddr + i);
if (profiles[profileId].assignments[i] >= shortcutCount) {
profiles[profileId].assignments[i] = -1;
}
}
Serial.println("Loaded profile: " + String(profiles[profileId].name));
}
void saveProfile(int profileId) {
int baseAddr = profileId * 15;
for (int i = 0; i < 6; i++) {
EEPROM.write(baseAddr + i, profiles[profileId].assignments[i]);
}
EEPROM.commit();
Serial.println("Saved profile: " + String(profiles[profileId].name));
}
void sendShortcut(const char* shortcut) {
if (strcmp(shortcut, "G") == 0) Keyboard.write('g');
else if (strcmp(shortcut, "S") == 0) Keyboard.write('s');
else if (strcmp(shortcut, "R") == 0) Keyboard.write('r');
else if (strcmp(shortcut, "E") == 0) Keyboard.write('e');
else if (strcmp(shortcut, "Shift+A") == 0) { Keyboard.press(KEY_LEFT_SHIFT); Keyboard.write('a'); Keyboard.release(KEY_LEFT_SHIFT); }
else if (strcmp(shortcut, "Ctrl+C") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('c'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Ctrl+V") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('v'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Tab") == 0) Keyboard.write(KEY_TAB);
else if (strcmp(shortcut, "Alt+Z") == 0) { Keyboard.press(KEY_LEFT_ALT); Keyboard.write('z'); Keyboard.release(KEY_LEFT_ALT); }
else if (strcmp(shortcut, "Ctrl+Z") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('z'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Ctrl+F") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('f'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Shift+Tab") == 0) { Keyboard.press(KEY_LEFT_SHIFT); Keyboard.write(KEY_TAB); Keyboard.release(KEY_LEFT_SHIFT); }
else if (strcmp(shortcut, "Ctrl+S") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('s'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Ctrl+X") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('x'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Ctrl+D") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('d'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Ctrl+R") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('r'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Shift+Ctrl+C") == 0) { Keyboard.press(KEY_LEFT_SHIFT); Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('c'); Keyboard.releaseAll(); }
else if (strcmp(shortcut, "Alt+F") == 0) { Keyboard.press(KEY_LEFT_ALT); Keyboard.write('f'); Keyboard.release(KEY_LEFT_ALT); }
else if (strcmp(shortcut, "F5") == 0) Keyboard.write(KEY_F5);
else if (strcmp(shortcut, "Ctrl+Space") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write(' '); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "I") == 0) Keyboard.write('i');
else if (strcmp(shortcut, "Alt+A") == 0) { Keyboard.press(KEY_LEFT_ALT); Keyboard.write('a'); Keyboard.release(KEY_LEFT_ALT); }
else if (strcmp(shortcut, "Shift+Space") == 0) { Keyboard.press(KEY_LEFT_SHIFT); Keyboard.write(' '); Keyboard.release(KEY_LEFT_SHIFT); }
else if (strcmp(shortcut, "Ctrl+I") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('i'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Shift+D") == 0) { Keyboard.press(KEY_LEFT_SHIFT); Keyboard.write('d'); Keyboard.release(KEY_LEFT_SHIFT); }
else if (strcmp(shortcut, "Alt+I") == 0) { Keyboard.press(KEY_LEFT_ALT); Keyboard.write('i'); Keyboard.release(KEY_LEFT_ALT); }
else if (strcmp(shortcut, "MMB") == 0) Mouse.click(MOUSE_MIDDLE);
else if (strcmp(shortcut, "Ctrl+LMB") == 0) { Mouse.press(MOUSE_LEFT); Keyboard.press(KEY_LEFT_CTRL); Mouse.release(MOUSE_LEFT); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Shift+MMB") == 0) { Mouse.press(MOUSE_MIDDLE); Keyboard.press(KEY_LEFT_SHIFT); Mouse.release(MOUSE_MIDDLE); Keyboard.release(KEY_LEFT_SHIFT); }
else if (strcmp(shortcut, "Ctrl+WheelUp") == 0) { Keyboard.press(KEY_LEFT_CTRL); Mouse.move(0, 0, 10); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Ctrl+WheelDown") == 0) { Keyboard.press(KEY_LEFT_CTRL); Mouse.move(0, 0, -10); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Chrome") == 0) Keyboard.println("\"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe\"");
else if (strcmp(shortcut, "Copy") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('c'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Paste") == 0) { Keyboard.press(KEY_LEFT_CTRL); Keyboard.write('v'); Keyboard.release(KEY_LEFT_CTRL); }
else if (strcmp(shortcut, "Email") == 0) Keyboard.println("\"C:\\Program Files\\Microsoft Office\\root\\Office16\\OUTLOOK.EXE\"");
else if (strcmp(shortcut, "Notepad") == 0) Keyboard.println("notepad.exe");
else if (strcmp(shortcut, "Cmd") == 0) Keyboard.println("cmd.exe");
Serial.println("Sent shortcut: " + String(shortcut));
}
void launchBlender() {
lcdClear();
lcdSetCursor(0, 0);
lcdPrint("Launching");
lcdSetCursor(0, 1);
lcdPrint("Blender...");
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press('r');
Keyboard.releaseAll();
Keyboard.println("\"C:\\Program Files\\Blender Foundation\\Blender 4.3\\blender.exe\"");
Serial.println("Launching Blender");
displayNeedsUpdate = true;
}
}