Compare commits

..

5 Commits

7 changed files with 132 additions and 1 deletions
+5
View File
@@ -1,6 +1,7 @@
#pragma once
#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#include "menu.hpp"
/*
* A helper class to facilitate drawing on a HD44780 LCD display.
@@ -10,6 +11,10 @@ class Display {
Display(uint8_t addr = 0x27, uint8_t cols = 20, uint8_t rows = 4);
void begin();
void clear();
void drawMenu(Menu& menu);
LiquidCrystal_I2C& getLCD();
private:
LiquidCrystal_I2C lcd;
};
+11 -1
View File
@@ -12,13 +12,22 @@
* Note: The button is active LOW and requires a pull-up resistor, which can either be external (10kΩ)
* or the internal pull-up resistor of the microcontroller (default).
*/
enum JoystickDirection {
CENTER,
UP,
DOWN,
LEFT,
RIGHT
};
class Joystick {
public:
Joystick();
double getX() const;
double getY() const;
bool isPressed() const;
JoystickDirection getDirection() const;
private:
uint8_t vrx;
uint8_t vry;
@@ -26,4 +35,5 @@ class Joystick {
const uint8_t xOffset = 13;
const uint8_t yOffset = 10;
const uint8_t deadzone = 15; // Deadzone threshold to prevent jitter around the center position
};
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#define MENU_ITEM_COUNT 4
#include <Arduino.h>
#include "joystick.hpp"
typedef struct {
String name;
void (*action)();
} Item;
class Menu {
public:
Menu(Item* items);
void updateCurrentItem(JoystickDirection& direction);
void execute();
Item& getItemAt(size_t index);
size_t getCurrentItemIndex() const;
private:
Item items[MENU_ITEM_COUNT];
int currentItem;
};
+21
View File
@@ -8,4 +8,25 @@ void Display::begin() {
lcd.backlight();
}
void Display::clear() {
lcd.clear();
}
void Display::drawMenu(Menu &menu) {
clear();
size_t currentItemIndex = menu.getCurrentItemIndex();
for (size_t i = 0; i < MENU_ITEM_COUNT; i++) {
lcd.setCursor(0, i);
Item& item = menu.getItemAt(i);
if (i == currentItemIndex) {
lcd.print("> " + item.name);
} else {
lcd.print(" " + item.name);
}
}
}
LiquidCrystal_I2C& Display::getLCD() {
return lcd;
}
+19
View File
@@ -20,3 +20,22 @@ double Joystick::getY() const {
bool Joystick::isPressed() const {
return digitalRead(sw) == LOW;
}
// Determine the direction of the joystick based on the X and Y values, considering a deadzone to prevent jitter
JoystickDirection Joystick::getDirection() const {
double x = getX();
double y = getY();
if (abs(x) < deadzone && abs(y) < deadzone) {
return CENTER;
} else if (y > deadzone) {
return UP;
} else if (y < -deadzone) {
return DOWN;
} else if (x > deadzone) {
return RIGHT;
} else if (x < -deadzone) {
return LEFT;
}
return CENTER; // Default to CENTER if no direction is detected
}
+20
View File
@@ -6,6 +6,14 @@
Joystick joystick;
Display display;
Item items[MENU_ITEM_COUNT] = {
{"Feed", []() { Serial.println("Feeding..."); }},
{"Play", []() { Serial.println("Playing..."); }},
{"Sleep", []() { Serial.println("Sleeping..."); }},
{"Clean", []() { Serial.println("Cleaning..."); }}
};
Menu menu(items);
void setup() {
Serial.begin(9600);
while (!Serial) {
@@ -14,7 +22,19 @@ void setup() {
display.begin();
pinMode(LED_BUILTIN, OUTPUT);
display.drawMenu(menu);
}
void loop() {
JoystickDirection direction = joystick.getDirection();
if (direction != JoystickDirection::CENTER) {
menu.updateCurrentItem(direction);
display.drawMenu(menu);
}
if (joystick.isPressed()) {
menu.execute();
}
delay(250);
}
+34
View File
@@ -0,0 +1,34 @@
#include "menu.hpp"
Menu::Menu(Item* items) : currentItem(0) {
for (int i = 0; i < MENU_ITEM_COUNT; i++) {
this->items[i] = items[i];
}
}
void Menu::updateCurrentItem(JoystickDirection &direction) {
switch (direction) {
case JoystickDirection::UP:
currentItem = (currentItem - 1 + MENU_ITEM_COUNT) % MENU_ITEM_COUNT;
break;
case JoystickDirection::DOWN:
currentItem = (currentItem + 1) % MENU_ITEM_COUNT;
break;
default:
break;
}
}
void Menu::execute() {
if (items[currentItem].action) {
items[currentItem].action();
}
}
Item& Menu::getItemAt(size_t index) {
return items[index];
}
size_t Menu::getCurrentItemIndex() const {
return currentItem;
}