#include <Arduino.h>
#include <Wire.h>
#include <U8g2lib.h>
#define BTN_UP PA0
#define BTN_DOWN PA1
#define BTN_SELECT PA2
#define DOT1 PA3
#define DOT2 PA4
#define DOT3 PA5
#define DOT4 PA6
#define DOT5 PA7
#define DOT6 PB0
#define BTN_ENTER PB1
U8G2_SSD1306_128X64_NONAME_F_HW_I2C oled1(U8G2_R0);
U8G2_SSD1306_128X64_NONAME_F_SW_I2C oled2(
U8G2_R0,
PB4,
PB5,
U8X8_PIN_NONE
);
enum State {
STATE_WELCOME,
STATE_MENU,
STATE_LEARNING,
STATE_SETTINGS
};
enum LearnMode {
LETTER_MODE,
NUMBER_MODE,
SYMBOL_MODE
};
State currentState = STATE_WELCOME;
LearnMode currentLearnMode;
const char* menuItems[] = {
"Learn Braille",
"Settings"
};
int menuIndex = 0;
uint8_t braillePattern = 0;
char targetChar = 'A';
String resultMessage = "";
int score = 0;
bool numberSignEntered = false;
String typedHistory = "";
bool buttonPressed(int pin) {
static bool lastState[50];
bool current = digitalRead(pin);
if (lastState[pin] == HIGH && current == LOW) {
lastState[pin] = current;
return true;
}
lastState[pin] = current;
return false;
}
char decodeBraille(uint8_t p) {
switch (p) {
case 1:return 'A'; case 3:return 'B'; case 9:return 'C';
case 25:return 'D'; case 17:return 'E'; case 11:return 'F';
case 27:return 'G'; case 19:return 'H'; case 10:return 'I';
case 26:return 'J'; case 5:return 'K'; case 7:return 'L';
case 13:return 'M'; case 29:return 'N'; case 21:return 'O';
case 15:return 'P'; case 31:return 'Q'; case 23:return 'R';
case 14:return 'S'; case 30:return 'T'; case 37:return 'U';
case 39:return 'V'; case 58:return 'W'; case 45:return 'X';
case 61:return 'Y'; case 53:return 'Z';
default:return '?';
}
}
char decodeNumber(uint8_t p) {
switch (p) {
case 1:return '1'; case 3:return '2'; case 9:return '3';
case 25:return '4'; case 17:return '5'; case 11:return '6';
case 27:return '7'; case 19:return '8'; case 10:return '9';
case 26:return '0';
default:return '?';
}
}
char decodeSymbol(uint8_t p) {
switch (p) {
case 50:return '.';
case 2:return ',';
case 22:return '?';
case 38:return '!';
case 36:return '-';
case 60:return '#';
default:return '?';
}
}
void generateNewQuestion() {
int type = random(3);
if(type == 0) {
currentLearnMode = LETTER_MODE;
targetChar = 'A' + random(26);
}
else if(type == 1) {
currentLearnMode = NUMBER_MODE;
char nums[] = {'0','1','2','3','4','5','6','7','8','9'};
targetChar = nums[random(10)];
numberSignEntered = false;
}
else {
currentLearnMode = SYMBOL_MODE;
char symbols[] = {'.', ',', '?', '!', '-', '#'};
targetChar = symbols[random(6)];
}
resultMessage = "";
}
void drawWelcome() {
oled1.clearBuffer();
oled1.setFont(u8g2_font_6x12_tr);
oled1.drawStr(10,20,"Braille Learning");
oled1.drawStr(30,38,"Device");
oled1.drawStr(5,60,"Press SELECT");
oled1.sendBuffer();
drawHistory();
}
void drawMenu() {
oled1.clearBuffer();
oled1.setFont(u8g2_font_6x12_tr);
oled1.drawStr(0,10,"Main Menu");
for(int i=0;i<2;i++) {
char line[30];
sprintf(line,(i==menuIndex)?"> %s":" %s",menuItems[i]);
oled1.drawStr(0,25+(i*12),line);
}
oled1.sendBuffer();
}
void drawSettings() {
oled1.clearBuffer();
oled1.setFont(u8g2_font_6x12_tr);
oled1.drawStr(0,10,"Settings");
oled1.drawStr(0,30,"Coming Soon");
oled1.drawStr(0,60,"SELECT=Back");
oled1.sendBuffer();
}
void drawLearning() {
if(buttonPressed(DOT1)) braillePattern |= 1;
if(buttonPressed(DOT2)) braillePattern |= 2;
if(buttonPressed(DOT3)) braillePattern |= 4;
if(buttonPressed(DOT4)) braillePattern |= 8;
if(buttonPressed(DOT5)) braillePattern |= 16;
if(buttonPressed(DOT6)) braillePattern |= 32;
if(buttonPressed(BTN_ENTER)) {
if(currentLearnMode == NUMBER_MODE) {
if(!numberSignEntered) {
if(braillePattern == 60) {
numberSignEntered = true;
resultMessage = "ENTER DIGIT";
} else {
resultMessage = "MISSING #";
}
braillePattern = 0;
}
else {
char entered = decodeNumber(braillePattern);
if(entered == targetChar) {
score++;
score++;
typedHistory += targetChar;
drawHistory();
generateNewQuestion();
} else {
resultMessage = "TRY AGAIN";
}
braillePattern = 0;
}
}
else {
char entered;
if(currentLearnMode == LETTER_MODE)
entered = decodeBraille(braillePattern);
else
entered = decodeSymbol(braillePattern);
if(entered == targetChar) {
score++;
typedHistory += targetChar;
drawHistory();
numberSignEntered = false;
generateNewQuestion();
}else {
resultMessage = "TRY AGAIN";
}
braillePattern = 0;
}
}
oled1.clearBuffer();
oled1.setFont(u8g2_font_6x12_tr);
if(currentLearnMode == LETTER_MODE)
oled1.drawStr(0,10,"LETTER");
else if(currentLearnMode == NUMBER_MODE)
oled1.drawStr(0,10,"NUMBER");
else
oled1.drawStr(0,10,"SYMBOL");
char buf[25];
sprintf(buf,"Type: %c",targetChar);
oled1.drawStr(0,25,buf);
sprintf(buf,"Score:%d",score);
oled1.drawStr(0,38,buf);
if(currentLearnMode == NUMBER_MODE) {
if(!numberSignEntered)
oled1.drawStr(0,50,"Enter # First");
else
oled1.drawStr(0,50,"Now Enter Digit");
}
if(resultMessage.length())
oled1.drawStr(0,62,resultMessage.c_str());
oled1.sendBuffer();
}
void drawHistory() {
oled2.clearBuffer();
oled2.setFont(u8g2_font_6x12_tr);
oled2.drawStr(0,10,"Typed History");
String s = typedHistory;
if(s.length() > 36)
s = s.substring(s.length()-36);
if(s.length() > 0)
oled2.drawStr(0,25,s.substring(0,min((int)s.length(),12)).c_str());
if(s.length() > 12)
oled2.drawStr(0,40,s.substring(12,min((int)s.length(),24)).c_str());
if(s.length() > 24)
oled2.drawStr(0,55,s.substring(24,min((int)s.length(),36)).c_str());
oled2.sendBuffer();
}
void setup() {
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
pinMode(BTN_SELECT, INPUT_PULLUP);
pinMode(DOT1, INPUT_PULLUP);
pinMode(DOT2, INPUT_PULLUP);
pinMode(DOT3, INPUT_PULLUP);
pinMode(DOT4, INPUT_PULLUP);
pinMode(DOT5, INPUT_PULLUP);
pinMode(DOT6, INPUT_PULLUP);
pinMode(BTN_ENTER, INPUT_PULLUP);
Wire.begin();
oled1.begin();
oled2.begin();
randomSeed(millis());
generateNewQuestion();
drawWelcome();
}
void loop() {
switch(currentState) {
case STATE_WELCOME:
if(buttonPressed(BTN_SELECT)) {
currentState = STATE_MENU;
drawMenu();
}
break;
case STATE_MENU:
if(buttonPressed(BTN_UP)) {
menuIndex--;
if(menuIndex < 0) menuIndex = 1;
drawMenu();
}
if(buttonPressed(BTN_DOWN)) {
menuIndex++;
if(menuIndex > 1) menuIndex = 0;
drawMenu();
}
if(buttonPressed(BTN_SELECT)) {
if(menuIndex == 0)
currentState = STATE_LEARNING;
else {
currentState = STATE_SETTINGS;
drawSettings();
}
}
break;
case STATE_LEARNING:
drawLearning();
if(buttonPressed(BTN_UP)) {
currentState = STATE_MENU;
drawMenu();
}
break;
case STATE_SETTINGS:
if(buttonPressed(BTN_SELECT)) {
currentState = STATE_MENU;
drawMenu();
}
break;
}
}