#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Arduino.h>
#include <Button.h>
#include <I2CKeyPad.h>
#include <TM1637Display.h>
#include <Wire.h>
/* Classes */
#pragma region Classes
class RGBLed {
public:
RGBLed(int red_pin, int green_pin, int blue_pin) :
m_red_pin { red_pin },
m_green_pin { green_pin },
m_blue_pin { blue_pin }
{
;
}
void begin() const {
pinMode(m_red_pin, OUTPUT);
pinMode(m_green_pin, OUTPUT);
pinMode(m_blue_pin, OUTPUT);
}
void set(int red, int green, int blue) const {
analogWrite(m_red_pin, red);
analogWrite(m_green_pin, green);
analogWrite(m_blue_pin, blue);
}
void setRed() const { set(255, 0, 0); }
void setGreen() const { set(0, 255, 0); }
void setBlue() const { set(0, 0, 255); }
void setYellow() const { set(235, 255, 0); }
void off() const { set(0, 0, 0); }
private:
const int m_red_pin {};
const int m_green_pin {};
const int m_blue_pin {};
};
#pragma endregion
/* Enums */
#pragma region Enums
int dummyOption(); // Forward declaration
namespace Menu {
enum Option {
SimonSays,
RandomPassword,
WireMatchGame,
TimedIgnition,
Option5,
Option6,
Option7,
Option8,
Option9,
HardwareCheck,
OptionCount,
};
static constexpr Option options[] {
SimonSays,
RandomPassword,
WireMatchGame,
TimedIgnition,
Option5,
Option6,
Option7,
Option8,
Option9,
HardwareCheck,
};
static_assert(sizeof(options) / sizeof(*options) == OptionCount);
struct OptionData {
const String name;
int (*action)();
};
static const OptionData data[] {
OptionData { "Simon Says", &dummyOption },
OptionData { "Random Password", &dummyOption },
OptionData { "Wire Match Game", &dummyOption },
OptionData { "Timed Ignition", &dummyOption },
OptionData { "Option 5", &dummyOption },
OptionData { "Option 6", &dummyOption },
OptionData { "Option 7", &dummyOption },
OptionData { "Option 8", &dummyOption },
OptionData { "Option 9", &dummyOption },
OptionData { "Hardware Check", &dummyOption },
};
static_assert(sizeof(data) / sizeof(*data) == OptionCount);
const String name(Option option) {
return data[option].name;
};
int do_option(Option option) {
return data[option].action();
};
};
#pragma endregion
/* Inputs */
#pragma region Inputs
Button red_button { 26 };
Button blue_button { 27 };
Button green_button { 28 };
Button yellow_button { 29 };
Button select_button { 30 };
Button ignition_1 { 31 };
Button ignition_2 { 32 };
Button buttons[] {
red_button, blue_button, green_button, yellow_button,
select_button,
ignition_1, ignition_2,
};
constexpr int KEYPAD_ADDRESS { 0x20 };
char KEYMAP[16] { '1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D' };
I2CKeyPad keypad { KEYPAD_ADDRESS };
constexpr int RANDOM_PIN { A0 };
constexpr int WIRE_POST_1 { A1 };
constexpr int WIRE_POST_2 { A2 };
constexpr int WIRE_POST_3 { A3 };
constexpr int WIRE_POST_4 { A4 };
constexpr int WIRE_POSTS[] { WIRE_POST_1, WIRE_POST_2, WIRE_POST_3, WIRE_POST_4 };
#pragma endregion
/* Outputs */
#pragma region Outputs
constexpr int RED_LED { 22 };
constexpr int BLUE_LED { 23 };
constexpr int GREEN_LED { 24 };
constexpr int YELLOW_LED { 25 };
constexpr int LEDS[] { RED_LED, BLUE_LED, GREEN_LED, YELLOW_LED };
const RGBLed RGB_LED_1 { 2, 3, 4 };
const RGBLed RGB_LED_2 { 5, 6, 7 };
const RGBLed RGB_LED_3 { 8, 9, 10 };
const RGBLed RGB_LED_4 { 11, 12, 13 };
const RGBLed RGB_LEDS[] { RGB_LED_1, RGB_LED_2, RGB_LED_3, RGB_LED_4 };
constexpr int BUZZER { 35 };
constexpr int SEGMENT_CLK { 33 };
constexpr int SEGMENT_DIO { 34 };
constexpr uint8_t SEGMENT_BRIGHTNESS { 0x0F };
TM1637Display segment_timer { SEGMENT_CLK, SEGMENT_DIO };
constexpr int SCREEN_WIDTH { 128 };
constexpr int SCREEN_HEIGHT { 64 };
constexpr int OLED_RESET { -1 }; // Use Arduino reset switch
constexpr int screen_address { 0x3C };
Adafruit_SSD1306 display { SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET };
constexpr int FONT_SIZE { 1 };
#pragma endregion
/* Graphics */
#pragma region Graphics
static const unsigned char PROGMEM GFX_AK47[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x00,0x1f,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00,0x0f,0xff,0xff,0xff,0xff,0xff,0xfe,0x00,0x00,0x00,0x00,0x00,0x1c,0x00,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x1e,0x00,0xe0,0x1e,0x3c,0x7f,0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x00,0x00,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x00,0x00,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x07,0x00,0x00,0x00,0x1c,0x03,0xf0,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x00,0x00,0x00,0x00,0x00,0x07,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xff,0xc1,0x7f,0x9f,0xff,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xfe,0xc0,0x7f,0x87,0xff,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfe,0xc2,0x7f,0x01,0xff,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfc,0xbe,0xff,0x00,0x3f,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xfc,0x00,0x3f,0x00,0x0f,0xff,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xfc,0x00,0x1f,0x80,0x03,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf8,0x00,0x1f,0x80,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0xf8,0x00,0x0f,0x80,0x00,0x1f,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1f,0xf8,0x00,0x0f,0xc0,0x00,0x07,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xf0,0x00,0x0f,0xc0,0x00,0x01,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xe0,0x00,0x07,0xe0,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xe0,0x00,0x07,0xe0,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xff,0xc0,0x00,0x07,0xc0,0x00,0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xff,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xf8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xe0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
static const unsigned char PROGMEM GFX_ARROW_UP[] = {0x10,0x38,0x7c,0xfe,0x38,0x38,0x38,0x38,0x38};
static const unsigned char PROGMEM GFX_ARROW_DOWN[] = {0x38,0x38,0x38,0x38,0x38,0xfe,0x7c,0x38,0x10};
static const unsigned char PROGMEM GFX_ARROW_RIGHT[] = {0x04,0x00,0x06,0x00,0xff,0x00,0xff,0x80,0xff,0x00,0x06,0x00,0x04,0x00};
static const unsigned char PROGMEM GFX_CELCIUS[] = {0x04,0x0a,0x04,0x70,0xc0,0x80,0x80,0xc0,0x70};
static const unsigned char PROGMEM GFX_USB[] = {0x01,0x00,0x00,0x0f,0x80,0x00,0x13,0x04,0x00,0x20,0x06,0x00,0xff,0xff,0x80,0x08,0x06,0x00,0x04,0xe4,0x00,0x03,0xe0,0x00,0x00,0xe0,0x00};
static const unsigned char PROGMEM GFX_VOLUME[] = {0x12,0x31,0xf5,0xf5,0x31,0x12};
static const unsigned char PROGMEM batteryEmpty_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0x80,0x00,0xe0,0x80,0x00,0xa0,0x80,0x00,0xa0,0x80,0x00,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM batteryFull_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xaa,0xaa,0xe0,0xaa,0xaa,0xa0,0xaa,0xaa,0xa0,0xaa,0xaa,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM battery86_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xaa,0xa8,0xe0,0xaa,0xa8,0xa0,0xaa,0xa8,0xa0,0xaa,0xa8,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM battery71_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xaa,0xa0,0xe0,0xaa,0xa0,0xa0,0xaa,0xa0,0xa0,0xaa,0xa0,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM battery57_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xaa,0x80,0xe0,0xaa,0x80,0xa0,0xaa,0x80,0xa0,0xaa,0x80,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM battery43_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xaa,0x00,0xe0,0xaa,0x00,0xa0,0xaa,0x00,0xa0,0xaa,0x00,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM battery29_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xa8,0x00,0xe0,0xa8,0x00,0xa0,0xa8,0x00,0xa0,0xa8,0x00,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM battery14_symbol[] = {0xff,0xff,0x00,0x80,0x00,0x80,0xa0,0x00,0xe0,0xa0,0x00,0xa0,0xa0,0x00,0xa0,0xa0,0x00,0xe0,0x80,0x00,0x80,0xff,0xff,0x00};
static const unsigned char PROGMEM Skull_symbol[] = {0x00,0x03,0xff,0xe0,0x00,0x00,0x00,0x1f,0xff,0xff,0xf0,0x00,0x00,0x7f,0xff,0xff,0xfc,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x01,0xff,0xff,0xff,0xff,0x80,0x07,0xff,0xff,0xff,0xff,0xc0,0x0f,0xff,0xff,0xff,0xff,0xe0,0x1f,0xff,0xff,0xff,0xff,0xe0,0x3f,0xff,0xff,0xff,0xff,0xf0,0x3f,0xff,0xff,0xff,0xff,0xf0,0x7f,0xff,0xff,0xff,0xff,0xf8,0x7f,0xff,0xff,0xff,0xff,0xf8,0xff,0xff,0xff,0xff,0xff,0xf8,0xff,0xff,0xff,0xff,0xff,0xf8,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0xff,0xff,0xfc,0xff,0xff,0xff,0xff,0xff,0xfc,0xfc,0x1f,0xff,0xff,0xc0,0xfc,0xf8,0x0f,0xff,0xff,0x00,0x7c,0xf8,0x01,0xff,0xfc,0x00,0x7c,0xf8,0x00,0x3f,0xe0,0x00,0x7c,0xf0,0x00,0x1f,0xc0,0x00,0x7c,0xf0,0x00,0x0f,0xc0,0x00,0x78,0xf0,0x00,0x1f,0xc0,0x00,0x78,0xf0,0x00,0x1f,0xe0,0x00,0x78,0x78,0x00,0x3f,0xe0,0x00,0x78,0x78,0x00,0x7f,0xf0,0x00,0xf8,0x7c,0x00,0xff,0xf8,0x01,0xf0,0x7f,0xc7,0xfd,0xff,0x1f,0xf0,0x7f,0xff,0xfd,0xff,0xff,0xf0,0x7f,0xff,0xf9,0xff,0xff,0xf0,0x7f,0xff,0xf8,0xff,0xff,0xf0,0x7f,0xff,0xf8,0xff,0xff,0xf0,0x7f,0xff,0xf8,0xff,0xff,0xf0,0x7f,0xff,0xf2,0x7f,0xff,0xe0,0x3f,0xff,0xf7,0xff,0xff,0xe0,0x3f,0xff,0xff,0xff,0xff,0xe0,0x3f,0xff,0xff,0xff,0xff,0xc0,0x1f,0xff,0xff,0xff,0xff,0xc0,0x0f,0xff,0xff,0xff,0xff,0x80,0x07,0xff,0xff,0xff,0xfe,0x00,0x00,0x1f,0xff,0xff,0xf8,0x00,0x00,0x1f,0x9f,0xc7,0xf0,0x00,0x00,0x1f,0x0f,0xc7,0xf0,0x00,0x00,0x1f,0x0f,0xc7,0xf0,0x00,0x00,0x1f,0x0f,0xc7,0xf0,0x00,0x00,0x3f,0x0f,0xc7,0xf0,0x00,0x00,0x3f,0x0f,0xc7,0xf0,0x00,0x00,0x3f,0x0f,0xc7,0xe0,0x00,0x00,0x1f,0x0f,0x83,0xe0,0x00,0x00,0x0e,0x00,0x00,0x00,0x00};
static const unsigned char PROGMEM image_Background_bits[] = {0x80,0x00,0x00,0x00,0x01,0xff,0xff,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00,0x00,0xff,0xff,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0x7f,0xff,0xff,0xff,0xfe,0x7f,0xff,0xf9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9f,0x4a,0x00,0x00,0x00,0x93,0x00,0x00,0x03,0x80,0x02,0xaa,0x00,0x00,0x00,0x00,0xc1,0x7f,0xff,0xff,0xff,0xfd,0x80,0x00,0x07,0x7f,0xff,0xff,0xff,0xff,0xff,0xff,0x60,0x54,0x00,0x00,0x00,0x06,0xff,0xff,0xfe,0xc0,0x00,0x01,0x55,0x00,0x00,0x01,0xbe,0x7f,0xff,0xff,0xff,0xfb,0x00,0x15,0x49,0xbf,0xff,0xff,0xff,0xff,0xff,0xfe,0xc2,0x80,0x00,0x00,0x00,0x1d,0xff,0xff,0xff,0x70,0x00,0x00,0x00,0x00,0x00,0x03,0x7e,0xff,0xff,0xff,0xff,0xce,0xaa,0x48,0x00,0xe7,0xff,0xff,0xff,0xff,0xff,0xf9,0x82,0xff,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0xcf,0xff,0xff,0xff,0xff,0xff,0xfc,0xfe,0xff,0xff,0xff,0xff,0xf0,0x00,0x00,0x00,0x1f,0xff,0xff,0xff,0xff,0xff,0xfe,0x01};
static const unsigned char PROGMEM image_Background_1_bits[] = {0x80,0x7f,0xff,0xff,0xff,0xff,0xff,0xf8,0x00,0x00,0x00,0x0f,0xff,0xff,0xff,0xff,0x7f,0x3f,0xff,0xff,0xff,0xff,0xff,0xf3,0xff,0xff,0xff,0xe7,0xff,0xff,0xff,0xff,0x41,0x9f,0xff,0xff,0xff,0xff,0xff,0xe7,0x00,0x12,0x55,0x73,0xff,0xff,0xff,0xff,0x7e,0xc0,0x00,0x00,0x00,0x00,0x00,0x0e,0xff,0xff,0xff,0xb8,0x00,0x00,0x00,0x01,0x43,0x7f,0xff,0xff,0xff,0xff,0xff,0xfd,0x92,0xa8,0x00,0xdf,0xff,0xff,0xff,0xfe,0x7d,0x80,0x00,0x00,0xaa,0x80,0x00,0x03,0x7f,0xff,0xff,0x60,0x00,0x00,0x00,0x2a,0x06,0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xe0,0x00,0x01,0xbf,0xff,0xff,0xff,0xfe,0x83,0x00,0x00,0x00,0x00,0x55,0x40,0x01,0xc0,0x00,0x00,0xc9,0x00,0x00,0x00,0x52,0xf9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x9f,0xff,0xfe,0x7f,0xff,0xff,0xff,0xfe,0xfc,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3f,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0xff,0xff,0x80,0x00,0x00,0x00,0x01};
static const unsigned char PROGMEM image_passport_left_bits[] = {0x3c,0x40,0x98,0xa4,0xa4,0x98,0x80,0x80,0xa0,0x90,0x88,0xa4,0x90,0x88,0xa4,0x90,0x88,0xa4,0x90,0x88,0xa4,0x90,0x88,0xa4,0x90,0x88,0x84,0x80,0x40,0x60,0x70,0x78,0x7c,0x5c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c,0x4c};
static const unsigned char PROGMEM image_passport_left_1_bits[] = {0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xc8,0xe8,0xf8,0x78,0x38,0x18,0x08,0x04,0x84,0x44,0x24,0x94,0x44,0x24,0x94,0x44,0x24,0x94,0x44,0x24,0x94,0x44,0x24,0x94,0x44,0x24,0x14,0x04,0x04,0x64,0x94,0x94,0x64,0x08,0xf0};
static const unsigned char PROGMEM lock_symbol[] = {0x38,0x44,0x44,0xfe,0xfe,0xee,0xfe,0x7c};
static const unsigned char PROGMEM unlock_symbol[] = {0x38,0x44,0x40,0xf2,0xe6,0xce,0x9e,0x3c};
static const unsigned char PROGMEM volumeOff_symbol[] = {0x13,0x18,0x31,0xb0,0xf0,0xe0,0xf1,0xe0,0x31,0xb0,0x13,0x18};
static const unsigned char PROGMEM waterDrop_symbol[] = {0x10,0x30,0x38,0x6c,0x74,0xf6,0xfe,0x7c,0x38};
static const unsigned char PROGMEM caution_symbol[] = {0x08,0x00,0x1c,0x00,0x14,0x00,0x36,0x00,0x36,0x00,0x7f,0x00,0x77,0x00,0xff,0x80};
static const unsigned char PROGMEM image_operation_warning_bits[] = {0x00,0x00,0x01,0x80,0x02,0x40,0x02,0x40,0x04,0x20,0x09,0x90,0x09,0x90,0x11,0x88,0x11,0x88,0x21,0x84,0x40,0x02,0x41,0x82,0x81,0x81,0x80,0x01,0x7f,0xfe,0x00,0x00};
static const unsigned char PROGMEM image_clock_bits[] = {0x07,0xc0,0x18,0x30,0x29,0x28,0x41,0x04,0x61,0x0c,0x81,0x02,0x81,0x02,0xe1,0x0e,0x80,0x82,0x80,0x42,0x60,0x2c,0x40,0x04,0x29,0x28,0x19,0x30,0x07,0xc0,0x00,0x00};
static const unsigned char PROGMEM image_arrow_curved_right_down_bits[] = {0x18,0x08,0xf0,0xc0,0xf0,0x08,0xc0};
static const unsigned char PROGMEM image_arrow_diagonal_right_down_bits[] = {0x80,0x48,0x28,0x18,0x78};
#pragma endregion
/* Initialization */
#pragma region Initialization
void setup() {
initialize_io();
check_outputs(250);
loading_screen(1500);
}
void initialize_io() {
Serial.begin(9600);
randomSeed(analogRead(RANDOM_PIN));
/* Inputs */
for (Button button : buttons) {
button.begin();
}
keypad.loadKeyMap(KEYMAP);
for (const int wire_post : WIRE_POSTS) {
pinMode(wire_post, INPUT_PULLUP);
}
/* Outputs */
for (const int led : LEDS) {
pinMode(led, OUTPUT);
}
for (const RGBLed rgb_led : RGB_LEDS) {
rgb_led.begin();
}
pinMode(BUZZER, OUTPUT);
segment_timer.setBrightness(SEGMENT_BRIGHTNESS);
Wire.begin();
if (!display.begin(SSD1306_SWITCHCAPVCC, screen_address)) {
Serial.println("Failed to initialize OLED Display...");
while (true) {
;
}
}
display.setTextColor(WHITE);
display.setTextSize(FONT_SIZE);
display.setTextWrap(false);
}
void check_outputs(int duration) {
/* Start Check */
for (const int led : LEDS) {
digitalWrite(led, HIGH);
}
RGB_LED_1.setRed();
RGB_LED_2.setBlue();
RGB_LED_3.setGreen();
RGB_LED_4.setYellow();
tone(BUZZER, 50);
segment_timer.showNumberDec(9999, false);
display.display();
delay(duration);
/* End Check */
for (const int led : LEDS) {
digitalWrite(led, LOW);
}
for (const RGBLed rgb_led : RGB_LEDS) {
rgb_led.off();
}
noTone(BUZZER);
segment_timer.clear();
wipe_display();
}
void loading_screen(int duration) {
constexpr int NUM_BULLETS { 6 };
constexpr int BULLET_SPACING { 17 };
const int quarter_duration { duration / 4 };
const int bullet_duration { (quarter_duration * 3) / NUM_BULLETS };
const int missing_duration { duration - (quarter_duration + (bullet_duration * NUM_BULLETS)) };
wipe_display();
display.drawRect(2, 2, 124, 60, 1);
display.setCursor(5, 17);
display.print("Rogue Print & Design");
display.drawBitmap(7, 25, GFX_AK47, 114, 34, WHITE);
display.display();
delay(quarter_duration);
for (int i { 0 }; i <= NUM_BULLETS; ++i) {
display.drawCircle(15 + (i * BULLET_SPACING), 10, 3, 1);
display.drawLine(5 + (i * BULLET_SPACING), 10, 10 + (i * BULLET_SPACING), 10, 1);
display.drawLine(8 + (i * BULLET_SPACING), 7, 11 + (i * BULLET_SPACING), 7, 1);
display.drawLine(8 + (i * BULLET_SPACING), 13, 11 + (i * BULLET_SPACING), 13, 1);
display.display();
delay(bullet_duration);
}
delay(missing_duration);
wipe_display();
}
#pragma endregion
/* Main */
#pragma region Main
void loop() {
Menu::Option selection { selectMenu() };
Menu::do_option(selection);
}
Menu::Option selectMenu() {
int menu_index { 0 };
drawMenu(menu_index);
while (true) {
if (select_button.pressed()) {
return static_cast<Menu::Option>(menu_index);
}
switch (keypad.getChar()) {
case '2': // Up
if (menu_index == 0) {
menu_index = Menu::OptionCount - 1;
} else {
menu_index = (menu_index - 1) % Menu::OptionCount;
}
drawMenu(menu_index);
break;
case '8': // Down
menu_index = (menu_index + 1) % Menu::OptionCount;
drawMenu(menu_index);
break;
}
delay(100);
};
}
#pragma endregion
/* Helpers */
#pragma region Helpers
char analogReadColor(int pin) {
const int pin_read { analogRead(pin) };
if (pin_read > 0 && pin_read < 100) {
return 'Y';
} else if (pin_read > 200 && pin_read < 300) {
return 'G';
} else if (pin_read > 350 && pin_read < 500) {
return 'B';
} else if (pin_read > 600 && pin_read < 800) {
return 'R';
}
}
void wipe_display() {
display.clearDisplay();
display.setCursor(0, 0);
display.display();
}
void drawMenuFrame() {
display.drawRect(0, 1, 128, 62, 1);
display.drawLine(10, 2, 10, 61, 1);
display.drawLine(11, 14, 126, 14, 1);
display.drawLine(104, 13, 104, 2, 1);
display.drawBitmap(2, 3, GFX_ARROW_UP, 7, 9, 1);
display.setCursor(3, 13);
display.print('2');
display.setCursor(3, 44);
display.print('8');
display.drawBitmap(2, 52, GFX_ARROW_DOWN, 7, 9, 1);
display.drawBitmap(12, 4, GFX_USB, 17, 9, 1);
display.drawBitmap(88, 5, GFX_VOLUME, 8, 6, 1);
display.drawBitmap(118, 3, GFX_CELCIUS, 7, 9, 1);
const int temp = random(0, 99);
display.setCursor(106, 5);
display.print(temp);
}
void drawMenu(int menu_index) {
constexpr int MENU_SELECTIONS_MAX { 5 }; // The maximum number of menu items displayable at once
const int menu_selections { min(Menu::OptionCount, MENU_SELECTIONS_MAX) };
wipe_display();
drawMenuFrame();
display.drawBitmap(12, 16, GFX_ARROW_RIGHT, 9, 7, 1);
int i { menu_index }, c { 0 };
do {
display.setCursor(22, 16 + (c * 9));
display.print(Menu::name(Menu::options[i]));
i = (i + 1) % Menu::OptionCount;
++c;
} while(i != (menu_index + menu_selections) % Menu::OptionCount);
display.display();
}
int dummyOption() {
wipe_display();
display.print("Why... why?");
display.display();
delay(3000);
return random(0, 1);
}
#pragma endregion