aoi yana
Published © GPL3+

Math drills for my daughter

An M5Stamp-based gadget for kids! It makes learning math fun with a game-like, multiple-choice format. It naturally boosts calculation skill

IntermediateShowcase (no instructions)Over 29 days77
Math drills for my daughter

Things used in this project

Hardware components

M5Stamp Pico
M5Stack M5Stamp Pico
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Code

Caculation Task

C/C++
#include <Arduino.h>
#include "freertos/FreeRTOS.h"
#include <math.h>
#include <Preferences.h>

#include "calcTask.h"

#include "switch.h"

#define PIN_BTN_L 21
#define PIN_BTN_C 33
#define PIN_BTN_R 32

Preferences preferences;

extern TaskHandle_t xDrawTaskHandle;

CALC gCurCalc = {0}; // 全メンバーをゼロ初期化
bool gSceneFinished = false;
Scene_t gScene = SCENE_TITLE;
uint32_t gScore = 0;

static CALC genCalc() {
  Ope_t opes[] = {OPE_ADD, OPE_SUB, OPE_MUL};
  CALC calc;
  calc.ope = opes[rand() % 3];
  switch(calc.ope) {
    case OPE_ADD:
      calc.params[0] = random(0, 99);
      calc.params[1] = random(0, 99-calc.params[0]);
      break;
    case OPE_SUB:
      calc.params[0] = random(0, 99);
      calc.params[1] = random(0, calc.params[0] + 1);
      break;
    case OPE_MUL:
      calc.params[0] = random(0, 10);
      calc.params[1] = random(0, 10);
      break;
  }
  
  uint32_t v_max, v_min;
  uint32_t s0 = random(0, 3);
  uint32_t s1 = (s0 + 1) % 3;
  uint32_t s2 = (s0 + 2) % 3;
  if (calc.ope == OPE_ADD) {
    calc.choices[s0] = calc.params[0] + calc.params[1];
    v_min = 0; v_max = 100;
  } else if (calc.ope == OPE_SUB) {
    calc.choices[s0] = calc.params[0] - calc.params[1];
    v_min = 0; v_max = 100;
  } else if (calc.ope == OPE_MUL) {
    calc.choices[s0] = calc.params[0] * calc.params[1];
    v_min = 0; v_max = 100;
  }
  do {
    calc.choices[s1] = random(v_min, v_max);
  } while(calc.choices[s0] == calc.choices[s1]);
  do {
    calc.choices[s2] = random(v_min, v_max);
  } while(calc.choices[s0] == calc.choices[s2] || calc.choices[s1] == calc.choices[s2]);
  
  calc.correct = (Select_t)s0;
  calc.select = SELECT_NON;
  return calc;
}

static void setScene (Scene_t scene) {
  gScene = scene;
  xTaskNotify(xDrawTaskHandle, (uint32_t)gScene, eSetValueWithOverwrite);
  gSceneFinished = false;
}

void calcTask(void *pvParameters) {
  // スコア読み出し
  preferences.begin("MathDrill", true);
  gScore = preferences.getUInt("score", 0);
  preferences.end();

  // スイッチ入力設定
  SWITCH btnL(PIN_BTN_L);
  SWITCH btnC(PIN_BTN_C);
  SWITCH btnR(PIN_BTN_R);
  pinMode(PIN_BTN_L, INPUT_PULLUP);
  pinMode(PIN_BTN_C, INPUT_PULLUP);
  pinMode(PIN_BTN_R, INPUT_PULLUP);

  // 計算式初期化
  gCurCalc = genCalc();

  while (1) {
    // スイッチ入力更新
    btnL.update();
    btnC.update();
    btnR.update();
    if (gScene == SCENE_TITLE) {
      if (btnL.wasPressed()) {
        gScore = 0;
        preferences.begin("MathDrill", false);
        preferences.putUInt("score", gScore);
        preferences.end();
      }
      if (btnC.wasPressed()) {
        setScene(SCENE_Q_IN);
      }
    } else if (gScene == SCENE_Q_IN) {
      if (gSceneFinished) {
        setScene(SCENE_Q);
      }
    } else if (gScene == SCENE_Q) {
      // ボタンが一つだけ押された時
      if ((btnL.wasPressed() + btnC.wasPressed() + btnR.wasPressed()) == 1) {
        if (btnL.wasPressed()) gCurCalc.select = SELECT_0;
        if (btnC.wasPressed()) gCurCalc.select = SELECT_1;
        if (btnR.wasPressed()) gCurCalc.select = SELECT_2;
        if (gCurCalc.select == gCurCalc.correct) {
          setScene(SCENE_CORRECT);
        } else {
          setScene(SCENE_WRONG);
        }
      }
    } else if (gScene == SCENE_Q_OUT) {
      if (gSceneFinished) {
        gCurCalc = genCalc();
        setScene(SCENE_Q_IN);
      }
    } else if (gScene == SCENE_WRONG) {
      if (gSceneFinished) {
        setScene(SCENE_Q);
      }
    } else if (gScene == SCENE_CORRECT) {
      if (gSceneFinished) {
        gScore++;
        preferences.begin("MathDrill", false);
        preferences.putUInt("score", gScore);
        preferences.end();
        setScene(SCENE_Q_OUT);
      }
    }
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

CALC getCurrentCalc(void) {
  return gCurCalc; // コピーを返す
}

void sceneFinish(void) {
  gSceneFinished = true;
  Serial.printf("scene:finished\n");
}

uint32_t getScore(void) {
  return gScore;
}

Credits

aoi yana
1 project • 0 followers

Comments