├── LICENSE ├── README.md ├── images └── 0 │ ├── 1 │ ├── IMG_0257-r270-1024x1024.jpg │ ├── IMG_0258-r270-1024x1024.jpg │ └── IMG_0259-r270-1024x1024.jpg │ ├── 2 │ ├── IMG_0462.jpg │ ├── IMG_0463.jpg │ └── IMG_0464.jpg │ ├── 3 │ └── IMG_0465.jpg │ └── 5 │ └── IMG_0467.jpg └── main ├── glib.hpp ├── main.ino └── ui_utils.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 sean butler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # M5 Cardputer Math Game 2 | 3 | A maths game written for the new [M5 Cardputer](https://docs.m5stack.com/en/core/Cardputer). Suitable for a primary/middle schooler. 4 | 5 | This approach because I can give it to my offspring, the games are focussed on her needs, also there is no internet or other similar distractions. 6 | 7 | Also the device is cheap enough that they can have it and take it anywhere and its not the end of the world if they lose it. 8 | 9 | Loosely based on the kind of maths outlined in the uk [key stage 1 & 2](https://assets.publishing.service.gov.uk/government/uploads/system/uploads/attachment_data/file/335158/PRIMARY_national_curriculum_-_Mathematics_220714.pdf) curriculum in the UK. 10 | 11 | ## Development History 12 | 13 | ### Version 0.5 14 | - Simple Subtraction 11 to 100 Subtract 1 to 10 15 | - Fixed Minor Arithmetic Bug 16 | 17 | ![](./images/0/5/IMG_0467.jpg) 18 | 19 | 20 | ### Version 0.4 21 | - Two Levels of Multiplication Difficulty 22 | - Rounding to 10 or 100 23 | 24 | ### Version 0.3 25 | - Refactored to better State Machine Code 26 | - Refactored to GUI Widgets 27 | - Added Some Colour 28 | 29 | ![](./images/0/3/IMG_0465.jpg) 30 | 31 | 32 | ### Version 0.2 33 | - Renamed to Awesome Maths Game 34 | - Added Menu to select question type, Mult, Div or Random. 35 | 36 | ![](./images/0/2/IMG_0462.jpg) 37 | ![](./images/0/2/IMG_0463.jpg) 38 | ![](./images/0/2/IMG_0464.jpg) 39 | 40 | ### Version 0.1 41 | 42 | - Asks you multiplication questions in groups of 10 43 | - Tells you if you are correct or not. 44 | - Counts your progress through the batch 45 | - At the end tells you the score 46 | - Sparkles for a High Score! 47 | 48 | ![](./images/0/1/IMG_0259-r270-1024x1024.jpg) 49 | ![](./images/0/1/IMG_0258-r270-1024x1024.jpg) 50 | ![](./images/0/1/IMG_0257-r270-1024x1024.jpg) 51 | 52 | 53 | Currently its a bit like the 1970s electronic toy Little Professor 54 | https://en.wikipedia.org/wiki/Little_Professor from Texas Instruments. 55 | 56 | ![](https://upload.wikimedia.org/wikipedia/commons/f/f8/Little_Professor.JPG) 57 | 58 | Loop202, CC BY-SA 3.0 , via Wikimedia Commons 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /images/0/1/IMG_0257-r270-1024x1024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/1/IMG_0257-r270-1024x1024.jpg -------------------------------------------------------------------------------- /images/0/1/IMG_0258-r270-1024x1024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/1/IMG_0258-r270-1024x1024.jpg -------------------------------------------------------------------------------- /images/0/1/IMG_0259-r270-1024x1024.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/1/IMG_0259-r270-1024x1024.jpg -------------------------------------------------------------------------------- /images/0/2/IMG_0462.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/2/IMG_0462.jpg -------------------------------------------------------------------------------- /images/0/2/IMG_0463.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/2/IMG_0463.jpg -------------------------------------------------------------------------------- /images/0/2/IMG_0464.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/2/IMG_0464.jpg -------------------------------------------------------------------------------- /images/0/3/IMG_0465.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/3/IMG_0465.jpg -------------------------------------------------------------------------------- /images/0/5/IMG_0467.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/seanbutler/M5CardputerMathGame/34a7fb84f10e1cfaa330e0a71e561d04b3bec604/images/0/5/IMG_0467.jpg -------------------------------------------------------------------------------- /main/glib.hpp: -------------------------------------------------------------------------------- 1 | #include "lgfx/v1/misc/colortype.hpp" 2 | #pragma once 3 | 4 | #include 5 | #include "lgfx/v1/lgfx_fonts.hpp" 6 | #include 7 | #include "M5Cardputer.h" 8 | 9 | namespace sb { 10 | 11 | class Position { 12 | public: 13 | Position(int x = 0, int y = 0) : x_(x), y_(y) {} 14 | int x_; 15 | int y_; 16 | }; 17 | 18 | class Size { 19 | public: 20 | Size(int w = 0, int h = 0) : w_(w), h_(h) {} 21 | int w_; 22 | int h_; 23 | }; 24 | 25 | class Widget { 26 | public: 27 | 28 | void SetPosition(Position position){ 29 | position_ = position; 30 | } 31 | 32 | virtual void Update() { 33 | for(auto W : children_ ) { 34 | W->Update(); 35 | } 36 | }; 37 | 38 | virtual void Render() { 39 | for(auto W : children_ ) { 40 | W->Render(); 41 | } 42 | }; 43 | 44 | Widget * parent_; 45 | std::vector children_; 46 | Position position_; 47 | Size size_; 48 | }; 49 | 50 | class Label : public Widget { 51 | public: 52 | Label() 53 | : font_(fonts::FreeSans12pt7b ) 54 | , alignment_(middle_center) 55 | , colour_(GREEN) 56 | { 57 | } 58 | 59 | void SetText(String text){ 60 | text_ = text; 61 | } 62 | 63 | void SetAlignment(textdatum_t alignment){ 64 | alignment_ = alignment; 65 | } 66 | 67 | void SetFont(lgfx::GFXfont font){ 68 | font_ = font; 69 | } 70 | 71 | void SetColour(int colour){ 72 | colour_ = colour; 73 | } 74 | 75 | 76 | void Render() { 77 | M5Cardputer.Display.setTextColor(colour_); 78 | M5Cardputer.Display.setTextSize(1); 79 | M5Cardputer.Display.setTextDatum(alignment_); 80 | M5Cardputer.Display.setTextFont(&font_); 81 | M5Cardputer.Display.drawString(text_, position_.x_, position_.y_); 82 | } 83 | 84 | void Update() { 85 | 86 | } 87 | 88 | lgfx::GFXfont font_; 89 | int colour_; 90 | String text_; 91 | textdatum_t alignment_; 92 | }; 93 | 94 | 95 | 96 | 97 | 98 | } 99 | 100 | -------------------------------------------------------------------------------- /main/main.ino: -------------------------------------------------------------------------------- 1 | 2 | #if defined ( ARDUINO ) 3 | 4 | #include 5 | 6 | // If you use SD card, write this. 7 | #include 8 | 9 | // If you use SPIFFS, write this. 10 | #include 11 | 12 | #endif 13 | 14 | // * The filesystem header must be included before the display library. 15 | 16 | //---------------------------------------------------------------- 17 | 18 | // If you use ATOM Display, write this. 19 | #include 20 | 21 | // If you use Module Display, write this. 22 | #include 23 | 24 | // If you use Module RCA, write this. 25 | #include 26 | 27 | // If you use Unit GLASS, write this. 28 | #include 29 | 30 | // If you use Unit GLASS2, write this. 31 | #include 32 | 33 | // If you use Unit OLED, write this. 34 | #include 35 | 36 | // If you use Unit Mini OLED, write this. 37 | #include 38 | 39 | // If you use Unit LCD, write this. 40 | #include 41 | 42 | // If you use UnitRCA (for Video output), write this. 43 | #include 44 | 45 | // * The display header must be included before the M5Unified library. 46 | 47 | //---------------------------------------------------------------- 48 | 49 | // Include this to enable the M5 global instance. 50 | 51 | #include 52 | #include "M5Cardputer.h" 53 | #include "M5GFX.h" 54 | 55 | #include "glib.hpp" 56 | #include "lgfx/v1/lgfx_fonts.hpp" 57 | 58 | 59 | #include 60 | 61 | // ============================================================ 62 | 63 | namespace quiz { 64 | 65 | constexpr unsigned int size = 10; 66 | 67 | String op; 68 | int first[size]; 69 | int second[size]; 70 | int answer[size]; 71 | 72 | int current; 73 | int correct; 74 | int guess; 75 | 76 | bool is_correct() { 77 | return answer[current] == guess; 78 | } 79 | 80 | void generate_mul_l1_questions() 81 | { 82 | correct = 0; 83 | current = 0; 84 | 85 | std::vector nums {2, 3, 4, 5, 8, 10}; 86 | std::random_device rd; 87 | std::mt19937 gen (rd()); 88 | std::uniform_int_distribution<> dis(0, nums.size() -1); 89 | 90 | for (int n=0;n nums {6, 7, 9, 11, 12, 25}; 103 | std::random_device rd; 104 | std::mt19937 gen (rd()); 105 | std::uniform_int_distribution<> dis(0, nums.size() -1); 106 | 107 | for (int n=0;nEnter(this); 198 | } 199 | else 200 | { 201 | current_state_->Execute(this); 202 | } 203 | } 204 | 205 | void SetCurrentState(state * new_state_){ 206 | current_state_ = new_state_; 207 | } 208 | 209 | void ChangeState(state * new_state_){ 210 | current_state_->Exit(this); 211 | 212 | previous_state_ = current_state_; 213 | current_state_ = new_state_; 214 | } 215 | }; 216 | 217 | } 218 | 219 | // ============================================================ 220 | 221 | namespace app { 222 | 223 | namespace states { 224 | 225 | class intro_screen : public fsm::state { 226 | 227 | public: 228 | sb::Label title_label; 229 | sb::Label instruction_label; 230 | 231 | intro_screen() { 232 | Serial.printf("intro_screen ctor\r\n"); 233 | } 234 | 235 | void Enter(fsm::machine * machine){ 236 | Serial.printf("intro_screen Enter()\r\n"); 237 | 238 | title_label.SetColour(YELLOW); 239 | title_label.SetText(String("Maths Game!")); 240 | title_label.SetFont(fonts::FreeSans18pt7b); 241 | title_label.SetAlignment(middle_center); 242 | title_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, M5Cardputer.Display.height()/2)); 243 | 244 | instruction_label.SetColour(YELLOW); 245 | instruction_label.SetText(String("Press Any Key")); 246 | instruction_label.SetFont(fonts::FreeSans9pt7b); 247 | instruction_label.SetAlignment(bottom_center); 248 | instruction_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, M5Cardputer.Display.height()-4)); 249 | 250 | title_label.Render(); 251 | instruction_label.Render(); 252 | } 253 | 254 | void Execute(fsm::machine * machine); 255 | 256 | void Exit(fsm::machine * machine){ 257 | Serial.printf("intro_screen Exit()\r\n"); 258 | M5Cardputer.Display.clearDisplay(); 259 | } 260 | }; 261 | 262 | class menu_screen : public fsm::state { 263 | public: 264 | sb::Label title_label; 265 | std::array choice_labels; 266 | sb::Label instruction_label; 267 | 268 | menu_screen() { 269 | Serial.printf("menu_screen ctor\r\n"); 270 | } 271 | 272 | void Enter(fsm::machine * machine){ 273 | Serial.printf("menu_screen Enter()\r\n"); 274 | 275 | quiz::current = 0; 276 | quiz::correct = 0; 277 | 278 | title_label.SetText(String("Choose Your Quiz!")); 279 | title_label.SetColour(YELLOW); 280 | title_label.SetFont(fonts::FreeSans9pt7b); 281 | title_label.SetAlignment(top_center); 282 | title_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, 4)); 283 | 284 | choice_labels[0].SetColour(RED); 285 | choice_labels[0].SetText(String("1) Easy 2) Hard Multiply")); 286 | choice_labels[0].SetFont(fonts::FreeSans9pt7b); 287 | choice_labels[0].SetAlignment(middle_center); 288 | choice_labels[0].SetPosition(sb::Position(M5Cardputer.Display.width()/2, (M5Cardputer.Display.height()/2)-24)); 289 | 290 | choice_labels[1].SetColour(RED); 291 | choice_labels[1].SetText(String("D) Divide R) Round")); 292 | choice_labels[1].SetFont(fonts::FreeSans9pt7b); 293 | choice_labels[1].SetAlignment(middle_center); 294 | choice_labels[1].SetPosition(sb::Position(M5Cardputer.Display.width()/2, (M5Cardputer.Display.height()/2)+0)); 295 | 296 | choice_labels[2].SetColour(RED); 297 | choice_labels[2].SetText(String("S) Subtraction")); 298 | choice_labels[2].SetFont(fonts::FreeSans9pt7b); 299 | choice_labels[2].SetAlignment(middle_center); 300 | choice_labels[2].SetPosition(sb::Position(M5Cardputer.Display.width()/2, (M5Cardputer.Display.height()/2)+24)); 301 | 302 | instruction_label.SetColour(YELLOW); 303 | instruction_label.SetText(String("Press 1, 2, D, R or S")); 304 | instruction_label.SetFont(fonts::FreeSans9pt7b); 305 | instruction_label.SetAlignment(bottom_center); 306 | instruction_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, M5Cardputer.Display.height()-4)); 307 | 308 | title_label.Render(); 309 | for(auto L : choice_labels ) { 310 | L.Render(); 311 | } 312 | instruction_label.Render(); 313 | } 314 | 315 | void Execute(fsm::machine * machine); 316 | 317 | void Exit(fsm::machine * machine){ 318 | Serial.printf("intro_screen Exit()\r\n"); 319 | M5Cardputer.Display.clearDisplay(); 320 | } 321 | }; 322 | 323 | 324 | class question_screen : public fsm::state { 325 | public: 326 | sb::Label title_label; 327 | 328 | sb::Label question_label; 329 | sb::Label result_label; 330 | 331 | sb::Label instruction_label; 332 | 333 | String question; 334 | String answer; 335 | 336 | question_screen() { 337 | Serial.printf("question_screen ctor\r\n"); 338 | } 339 | 340 | void Enter(fsm::machine * machine){ 341 | Serial.printf("question_screen Enter()\r\n"); 342 | 343 | title_label.SetText(String("Question ") + String(quiz::current+1) + String(" of ") + String(quiz::size)); 344 | title_label.SetColour(YELLOW); 345 | title_label.SetFont(fonts::FreeSans12pt7b); 346 | title_label.SetAlignment(top_center); 347 | title_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, 8)); 348 | title_label.Render(); 349 | 350 | question_label.SetText(String(quiz::first[quiz::current]) + String(quiz::op) + String(quiz::second[quiz::current]) + String("=")); 351 | question_label.SetColour(RED); 352 | question_label.SetFont(fonts::FreeSans18pt7b); 353 | question_label.SetAlignment(middle_right); 354 | question_label.SetPosition(sb::Position(M5Cardputer.Display.width()-64, M5Cardputer.Display.height()/2)); 355 | question_label.Render(); 356 | 357 | answer = ""; 358 | } 359 | 360 | void Execute(fsm::machine * machine); 361 | 362 | void Exit(fsm::machine * machine){ 363 | Serial.printf("question_screen Exit()\r\n"); 364 | M5Cardputer.Display.clearDisplay(); 365 | } 366 | }; 367 | 368 | 369 | class check_answer_screen : public fsm::state { 370 | public: 371 | sb::Label title_label; 372 | sb::Label result_label; 373 | sb::Label instruction_label; 374 | 375 | check_answer_screen() { 376 | Serial.printf("check_answer_screen ctor\r\n"); 377 | } 378 | 379 | void Enter(fsm::machine * machine){ 380 | Serial.printf("check_answer_screen Enter()\r\n"); 381 | 382 | if (quiz::is_correct()) 383 | { 384 | quiz::correct++; 385 | title_label.SetText(String("You Got It!")); 386 | title_label.SetColour(GREEN); 387 | } 388 | else 389 | { 390 | title_label.SetText(String("Sorry, No")); 391 | title_label.SetColour(RED); 392 | } 393 | 394 | title_label.SetFont(fonts::FreeSans18pt7b); 395 | title_label.SetAlignment(top_center); 396 | title_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, 8)); 397 | 398 | instruction_label.SetText(String("Press Any Key")); 399 | instruction_label.SetFont(fonts::FreeSans12pt7b); 400 | instruction_label.SetAlignment(bottom_center); 401 | instruction_label.SetColour(YELLOW); 402 | instruction_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, M5Cardputer.Display.height()-8)); 403 | 404 | title_label.Render(); 405 | instruction_label.Render(); 406 | 407 | quiz::current++; 408 | } 409 | 410 | void Execute(fsm::machine * machine); 411 | 412 | void Exit(fsm::machine * machine){ 413 | 414 | Serial.printf("tally_screen Exit()\r\n"); 415 | M5Cardputer.Display.clearDisplay(); 416 | } 417 | }; 418 | 419 | 420 | class tally_screen : public fsm::state { 421 | public: 422 | sb::Label title_label; 423 | sb::Label result_label; 424 | sb::Label instruction_label; 425 | 426 | tally_screen() { 427 | Serial.printf("tally_screen ctor\r\n"); 428 | } 429 | 430 | void Enter(fsm::machine * machine){ 431 | Serial.printf("tally_screen Enter()\r\n"); 432 | 433 | title_label.SetText(String("You Got...")); 434 | title_label.SetFont(fonts::FreeSans18pt7b); 435 | title_label.SetAlignment(top_center); 436 | title_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, 8)); 437 | 438 | result_label.SetText(String(quiz::correct) + String(" out of ") + String(quiz::size)); 439 | result_label.SetColour(YELLOW); 440 | result_label.SetFont(fonts::FreeSans18pt7b); 441 | result_label.SetAlignment(middle_center); 442 | result_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, M5Cardputer.Display.height()/2)); 443 | 444 | instruction_label.SetText(String("Press Any Key")); 445 | instruction_label.SetFont(fonts::FreeSans12pt7b); 446 | instruction_label.SetAlignment(bottom_center); 447 | instruction_label.SetColour(YELLOW); 448 | instruction_label.SetPosition(sb::Position(M5Cardputer.Display.width()/2, M5Cardputer.Display.height()-8)); 449 | 450 | title_label.Render(); 451 | result_label.Render(); 452 | instruction_label.Render(); 453 | } 454 | 455 | void Execute(fsm::machine * machine); 456 | 457 | void Exit(fsm::machine * machine){ 458 | Serial.printf("tally_screen Exit()\r\n"); 459 | M5Cardputer.Display.clearDisplay(); 460 | } 461 | }; 462 | 463 | // ---------------------------------------------------------------------- 464 | 465 | intro_screen intro_screen_state; 466 | menu_screen menu_screen_state; 467 | question_screen question_screen_state; 468 | check_answer_screen check_answer_screen_state; 469 | tally_screen tally_screen_state; 470 | 471 | // ---------------------------------------------------------------------- 472 | 473 | void intro_screen::Execute(fsm::machine * machine){ 474 | // Serial.printf("intro_screen Execute()\r\n"); 475 | 476 | // listen for key presses 477 | if ( M5Cardputer.Keyboard.isChange()) { 478 | if ( M5Cardputer.Keyboard.isPressed()) { 479 | // change state to next... 480 | machine->ChangeState(&menu_screen_state ); 481 | } 482 | } 483 | } 484 | 485 | void menu_screen::Execute(fsm::machine * machine){ 486 | // Serial.printf("menu_screen Execute()\r\n"); 487 | 488 | // listen for key presses 489 | if ( M5Cardputer.Keyboard.isChange()) { 490 | if ( M5Cardputer.Keyboard.isPressed()) { 491 | 492 | if ( M5Cardputer.Keyboard.isKeyPressed('1') ) 493 | { 494 | quiz::op = "x"; 495 | quiz::generate_mul_l1_questions(); 496 | machine->ChangeState(&question_screen_state); 497 | } 498 | 499 | if ( M5Cardputer.Keyboard.isKeyPressed('2') ) 500 | { 501 | quiz::op = "x"; 502 | quiz::generate_mul_l2_questions(); 503 | machine->ChangeState(&question_screen_state); 504 | } 505 | 506 | if ( ( M5Cardputer.Keyboard.isKeyPressed('D') ) 507 | || ( M5Cardputer.Keyboard.isKeyPressed('d') ) ) 508 | { 509 | quiz::op = "/"; 510 | quiz::generate_div_questions(); 511 | machine->ChangeState(&question_screen_state); 512 | } 513 | 514 | if ( ( M5Cardputer.Keyboard.isKeyPressed('R') ) 515 | || ( M5Cardputer.Keyboard.isKeyPressed('r') ) ) 516 | { 517 | quiz::op = "R"; 518 | quiz::generate_round_questions(); 519 | machine->ChangeState(&question_screen_state); 520 | } 521 | 522 | if ( ( M5Cardputer.Keyboard.isKeyPressed('S') ) 523 | || ( M5Cardputer.Keyboard.isKeyPressed('s') ) ) 524 | { 525 | quiz::op = "-"; 526 | quiz::generate_neg_questions(); 527 | machine->ChangeState(&question_screen_state); 528 | } 529 | } 530 | } 531 | } 532 | 533 | 534 | void question_screen::Execute(fsm::machine * machine){ 535 | // Serial.printf("question_screen Execute()\r\n"); 536 | 537 | if (M5Cardputer.Keyboard.isChange()) { 538 | if (M5Cardputer.Keyboard.isPressed()) { 539 | Keyboard_Class::KeysState status = M5Cardputer.Keyboard.keysState(); 540 | 541 | if ( answer.length() < 3 ) { 542 | for (auto i : status.word) { 543 | answer += i; 544 | } 545 | } 546 | 547 | if (status.del) { 548 | answer.remove(answer.length() - 1); 549 | } 550 | 551 | if (status.enter) { 552 | quiz::guess = atoi(answer.c_str()); 553 | machine->ChangeState(&check_answer_screen_state); 554 | } 555 | 556 | M5Cardputer.Display.fillRect( M5Cardputer.Display.width() - 64, (M5Cardputer.Display.height()/2 ) - 16, 557 | 64, 32, 558 | BLACK); 559 | 560 | M5Cardputer.Display.setTextDatum(middle_right); 561 | M5Cardputer.Display.drawString(answer, 562 | M5Cardputer.Display.width() - 8, 563 | M5Cardputer.Display.height() / 2 ); 564 | 565 | } 566 | } 567 | } 568 | 569 | void check_answer_screen::Execute(fsm::machine * machine){ 570 | // Serial.printf("tally_screen Execute()\r\n"); 571 | 572 | // listen for key presses 573 | if ( M5Cardputer.Keyboard.isChange()) { 574 | if ( M5Cardputer.Keyboard.isPressed()) { 575 | if ( quiz::current < quiz::size ) 576 | { 577 | machine->ChangeState(&question_screen_state); 578 | } 579 | else 580 | { 581 | machine->ChangeState(&tally_screen_state); 582 | } 583 | } 584 | } 585 | } 586 | 587 | 588 | void tally_screen::Execute(fsm::machine * machine){ 589 | // Serial.printf("tally_screen Execute()\r\n"); 590 | 591 | // listen for key presses 592 | if ( M5Cardputer.Keyboard.isChange()) { 593 | if ( M5Cardputer.Keyboard.isPressed()) { 594 | machine->ChangeState(&intro_screen_state); 595 | } 596 | } 597 | } 598 | 599 | 600 | // ---------------------------------------------------------------------- 601 | 602 | } 603 | 604 | } 605 | 606 | // ============================================================ 607 | 608 | fsm::machine app_logic; 609 | 610 | app::states::intro_screen intro_screen_state; 611 | app::states::menu_screen menu_screen_state; 612 | 613 | void setup(void) 614 | { 615 | M5Cardputer.begin(); 616 | M5Cardputer.Display.setRotation(1); 617 | Serial.begin(115200); 618 | 619 | app_logic.SetCurrentState(&intro_screen_state); 620 | } 621 | 622 | void loop(void) 623 | { 624 | M5Cardputer.update(); // needed for keyboard detection 625 | 626 | app_logic.Execute(); 627 | M5.delay(1); 628 | } 629 | 630 | //---------------------------------------------------------------- 631 | 632 | // for ESP-IDF compat 633 | #if !defined ( ARDUINO ) && defined ( ESP_PLATFORM ) 634 | extern "C" { 635 | void loopTask(void*) 636 | { 637 | setup(); 638 | for (;;) { 639 | loop(); 640 | } 641 | vTaskDelete(NULL); 642 | } 643 | 644 | void app_main() 645 | { 646 | xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, NULL, 1); 647 | } 648 | } 649 | #endif 650 | -------------------------------------------------------------------------------- /main/ui_utils.hpp: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | namespace ui_utils { 5 | 6 | 7 | static constexpr int AFFIRM_COLOUR = GREEN; 8 | static constexpr int WARN_COLOUR = ORANGE; 9 | static constexpr int ERROR_COLOUR = RED; 10 | 11 | static constexpr int FOREGROUND_COLOUR = WHITE; 12 | static constexpr int BACKGROUND_COLOUR = BLACK; 13 | 14 | void clear_screen() { 15 | 16 | M5Cardputer.Display.fillRect(0, 0, 17 | M5Cardputer.Display.width(), 18 | M5Cardputer.Display.height(), 19 | BACKGROUND_COLOUR); 20 | } 21 | 22 | void large_middle_message(String message) { 23 | 24 | M5Cardputer.Display.setTextDatum(middle_center); 25 | M5Cardputer.Display.setTextFont(&fonts::FreeSans18pt7b); 26 | M5Cardputer.Display.setTextSize(1); 27 | 28 | M5Cardputer.Display.drawString(message, 29 | M5Cardputer.Display.width() / 2, 30 | M5Cardputer.Display.height() / 2); 31 | } 32 | 33 | void small_bottom_message(String message) { 34 | 35 | M5Cardputer.Display.setTextDatum(bottom_center); 36 | 37 | M5Cardputer.Display.setTextFont(&fonts::FreeSans9pt7b); 38 | M5Cardputer.Display.setTextSize(1); 39 | 40 | M5Cardputer.Display.drawString(message, 41 | M5Cardputer.Display.width() / 2, 42 | M5Cardputer.Display.height() - 4 ); 43 | } 44 | 45 | void small_top_message(String message) { 46 | 47 | M5Cardputer.Display.setTextDatum(top_center); 48 | 49 | M5Cardputer.Display.setTextFont(&fonts::FreeSans9pt7b); 50 | M5Cardputer.Display.setTextSize(1); 51 | 52 | M5Cardputer.Display.drawString(message, 53 | M5Cardputer.Display.width() / 2, 54 | 4 ); 55 | } 56 | 57 | } 58 | --------------------------------------------------------------------------------