Um jogo de matemática no Arduino - A math game on Arduino
Ricardo da Silva Ogliari
Posted on April 15, 2022
> I'm still learning the English language, so please, if you find any English mistakes, let me know in the comments. Thank you very much.
Quando começamos a estudar uma nova linguagem ou plataforma é comum termos alguns problemas. No meu entendimento, isso pode ser amenizado quando criamos algo que nos empolgue. E, geralmente, desenvolvedores têm uma relação de muito amor com jogos. Logo, por mais simples que possa ser, um jogo modesto pode acelerar o processo de aprendizado.
When we start to study a new language or platform it is common to have some problems. In my understanding, this can be mitigated when we create something that excites us. And, generally, developers have a very loving relationship with games. So, as simple as it may be, a modest game can speed up the learning process.
Pensando nisso, propus a criação de um jogo matemático para aprimorarmos o conhecimento em IoT e, principalmente, no uso da plataforma de prototipagem Arduino. O objetivo é usar um display de 7 segmentos e a comunicação serial para mostrar dois operandos ao usuário, na sequência, o mesmo responde e receberá uma resposta de acerto ou erro. Sendo que esse processo se repete indefinidamente.
With that in mind, I proposed the creation of a mathematical game to improve knowledge in IoT and, mainly, in the use of the prototyping platform Arduino. The objective is to use a 7-segment display and serial communication to show two operands to the user, then, the user responds and will receive a hit or miss response. This process is repeated indefinitely.
Para que o leitor possa recriar o jogo, deixo a imagem do thinkercad abaixo com o circuito montado. Estamos usando um Arduino Uno, uma protoboard, um display de sete segmentos, um led RGB e resistores.
So that the reader can recreate the game, I leave the image of thinkercad below with the circuit assembled. We are using an Arduino Uno, a breadboard, seven-segment display, an RGB LED and resistors.
Os dois fios pretos estão ligando as portas terra do Arduino (GND) com as trilhas horizontais nas linhas indicadas com o símbolo “-”. Como boa prática, estamos usando a cor preta para indicar esses fios (em um circuito real seriam os jumpers).
The two black wires are connecting the Arduino ground ports (GND) with the horizontal tracks on the lines indicated with the symbol “-”. As a good practice, we are using the color black to indicate these wires (in a real circuit it would be the jumpers).
O display de segmentos possui dois pinos na sua parte central, que são ligadas a portas terras através de um resistor de 500 ohms. As outras 8 entradas são para seus segmentos. Lembrando que sete deles são para formar os segmentos individuais que foram o número e, o oitavo é para (des)ligar o ponto localizado no canto inferior direito. Estamos usando as portas digitais 13, 12, 11, 10, 9, 8, 7 e 6.
The segment display has two pins in its central part, which are connected to ground ports through a 500 ohm resistor. The other 8 entries are for its segments. Remembering that seven of them are for built the individual segments that were the number, and the eighth is for (un)connecting the point located in the lower right corner. We are using digital ports 13, 12, 11, 10, 9, 8, 7 and 6.
Por fim, usamos um LED RGB para indicar o acerto ou erro da resposta. Da esquerda para direita ligamos seus conectores as seguintes portas: digital 4, terra, digital 3 e digital 2.
Finally, we use an RGB LED to indicate the success or error of the answer. From left to right we connect its connectors to the following ports: digital 4, ground, digital 3 and digital 2.
Vamos explicar a codificação por partes. No inícios temos o conjunto de variáveis que indicam as portas onde o display e o LED estão ligados. Cada constante do tipo inteiro indica uma das portas digitais utilizadas. Na sequência teremos as variáveis do status atual e dos estados possíveis. Vamos utilizar a ideia de uma máquina de estados. Inicialmente, precisamos ler o primeiro operando, na sequência, ler o segundo operando e, finalmente, esperar a resposta do usuário.
Let's explain coding in parts. At the beginning we have the set of variables that indicate the ports where the display and the LED are connected. Each integer constant indicates one of the digital ports used. Next we will have the variables of the current status and the possible states. Let's use the idea of a state machine. Initially, we need to read the first operand, then read the second operand, and finally, wait for the user's response.
const int A = 13;
const int B = 12;
const int C = 11;
const int D = 10;
const int E = 9;
const int F = 8;
const int G = 7;
const int H = 6;
const int ledR = 4;
const int ledB = 3;
const int ledG = 2;
// máquina de estados
int STATE_READING_OP1 = 1;
int STATE_READING_OP2 = 2;
int STATE_WAITING_ANSWER = 3;
int status = STATE_READING_OP1;
int op1;
int op2;
Na função setup, que será chamada apenas uma vez, usamos a função pinMode para definir o modo de uso das portas digitais, sendo de saída. Nesta função, o primeiro parâmetro é a porta e o segundo justamente este modo. Além disso, usamos o Serial.begin para inicializar a comunicação serial, com uma taxa de 9600 bits por segundos. Por fim, usamos o randomSeed para criar números randômicos posteriormente.
In the setup function, which will be called only once, we use the pinMode function to define the mode of use of the digital ports, being output. In this function, the first parameter is the port and the second is just this mode. Also, we use Serial.begin to initialize serial communication, with a rate of 9600 bits per second. Finally, we use randomSeed to create random numbers later.
void setup(void)
{
Serial.begin(9600);
pinMode(A, OUTPUT);
pinMode(B, OUTPUT);
pinMode(C, OUTPUT);
pinMode(D, OUTPUT);
pinMode(E, OUTPUT);
pinMode(F, OUTPUT);
pinMode(G, OUTPUT);
pinMode(H, OUTPUT);
pinMode(ledR, OUTPUT);
pinMode(ledG, OUTPUT);
pinMode(ledB, OUTPUT);
randomSeed(analogRead(0));
}
Na sequência teremos todos os métodos que formam os números possíveis, além da função clear, que limpa o display e não mostra nenhum número. A função digitalWrite foi usada. Ela recebe dois parâmetros, a porta e o que será escrito na mesma. Arduino tem duas constantes que representam o 1 e 0, respectivamente, sendo elas HIGH e LOW. Em uma visão mais baixo nível, enviar HIGH para um porta digital, faz com que a mesma emita 5V na sua saída, e, quando mandamos LOW, a mesma envia 0V. Por isso que um segmento será ligado ou desligado, por ter ou não, uma corrente elétrica.
In the sequence we will have all the methods that form the possible numbers, in addition to the clear function, which clears the display and does not show any numbers. The digitalWrite function was used. It takes two parameters, the port and what will be written to it. Arduino has two constants that represent 1 and 0, respectively, being HIGH and LOW. In a lower level view, sending HIGH to a digital port causes it to emit 5V at its output, and when we send LOW, it sends 0V. That's why a segment will be turned on or off, whether or not it has an electric current.
void clear(){
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, LOW);
digitalWrite(H, LOW);
}
void zero(void) {
digitalWrite(A, LOW);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(H, HIGH);
}
void one(void) {
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void two(void) {
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, LOW);
digitalWrite(H, LOW);
}
void three(void) {
digitalWrite(A, HIGH);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void four(void) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, LOW);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void five(void) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void six(void) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, LOW);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void seven(void) {
digitalWrite(A, LOW);
digitalWrite(B, LOW);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, LOW);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void eight(void) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, HIGH);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
void nine(void) {
digitalWrite(A, HIGH);
digitalWrite(B, HIGH);
digitalWrite(C, HIGH);
digitalWrite(D, HIGH);
digitalWrite(E, LOW);
digitalWrite(F, HIGH);
digitalWrite(G, HIGH);
digitalWrite(H, LOW);
}
No código Arduino, a função loop será repetida indefinidamente enquanto a placa estiver recebendo energia. Logo no início temos um teste lógico, verificando se o estado corrente não é esperar a resposta do usuário. Nesse caso, precisamos gerar um número aleatório, que poderá ser o operando 1 ou o operando 2.
In Arduino code, the loop function will repeat indefinitely while the board is receiving power. At the beginning we have a logical test, verifying that the current state is not waiting for the user's response. In this case, we need to generate a random number, which can be operand 1 or operand 2.
A variável randNumber guarda o número aleatório gerado. O switch-case verificará o valor criado e chama a função correspondente para mostrar o número no display de sete segmentos.
The randNumber variable stores the generated random number. The switch-case will check the created value and call the corresponding function to show the number on the seven-segment display.
No último if-else precisamos saber se o estado corrente é ler o primeiro ou o segundo operando. Depois disso levando a máquina de estados para o passo seguinte, além de ter uma espera (função delay) de dois segundos (2000 milisegundos). Se o número lido for o primeiro operando, depois dessa pausa nos limpamos o display e temos uma nova espera de meio segundo. Dessa forma o usuário perceberá claramente que um novo número será apresentado. Já, se o número gerado foi o segundo operando, depois do intervalo de 2 segundos nós limpamos o display, porque, nesse caso, aguardaremos a resposta do jogador.
In the last if-else we need to know if the current state is reading the first or second operand. After that taking the state machine to the next step, besides having a wait (delay function) of two seconds (2000 milliseconds). If the number read is the first operand, after this pause we clear the display and have a new half-second wait. In this way the user will clearly perceive that a new number will be presented. On the other hand, if the number generated was the second operand, after the 2-second interval we clear the display, because in this case, we will wait for the player's response.
void loop(void)
{
if (status != STATE_WAITING_ANSWER){
int randNumber = random(0, 10);
switch (randNumber) {
case 0:
zero();
break;
case 1:
one();
break;
case 2:
two();
break;
case 3:
three();
break;
case 4:
four();
break;
case 5:
five();
break;
case 6:
six();
break;
case 7:
seven();
break;
case 8:
eight();
break;
default:
nine();
}
if (status == STATE_READING_OP1){
op1 = randNumber;
status = STATE_READING_OP2;
delay(2000);
clear();
delay(500);
} else {
op2 = randNumber;
status = STATE_WAITING_ANSWER;
delay(2000);
clear();
}
}
}
Finalmente, precisamos ouvir os dados que chegam via porta seral. Usaremos a função serialEvent, acionada quando dados estão disponíveis no buffer serial. Enquanto (while) dados seriais estiverem disponíveis (Serial.available()) nós leremos o próximo número inteiro. Na sequência verificamos se o status corrente já é de checagem da resposta. Caso afirmativo, verificamos se a multiplicação dos dois operandos é igual a resposta informada.
Finally, we need to listen for data arriving via the seral port. We will use the serialEvent function, triggered when data is available in the serial buffer. While (while) serial data is available (Serial.available()) we will read the next integer. Next, we check if the current status is already checking the response. If so, we check if the multiplication of the two operands is equal to the given answer.
Por fim, se a resposta estiver correta vamos mandar um valor de 255 somente para o pino digital que corresponde a esta cor, no LED RGB. Caso a resposta esteja errada a lógica é a mesma, mas com a cor vermelha. Depois do if-else temos uma nova pausa de dois segundos, na sequência, resetamos o LED para deixar sem cor e o ciclo reinicia.
Finally, if the answer is correct, we will send a value of 255 only to the digital pin that corresponds to this color, in the RGB LED. If the answer is wrong, the logic is the same, but with the color red. After the if-else we have a new two-second pause, then we reset the LED to no color and the cycle restarts.
void serialEvent() {
while (Serial.available()) {
int answer = Serial.parseInt();
if (status == STATE_WAITING_ANSWER){
if (answer == op1 * op2){
digitalWrite(ledR, 0);
digitalWrite(ledG, 255);
digitalWrite(ledB, 0);
} else {
digitalWrite(ledR, 255);
digitalWrite(ledG, 0);
digitalWrite(ledB, 0);
}
delay(2000);
digitalWrite(ledR, 0);
digitalWrite(ledG, 0);
digitalWrite(ledB, 0);
status = STATE_READING_OP1;
}
}
}
Importante - Important: recebi ajuda e bons pitacos dos meus alunos. I received help and good tips from my students:
Alexandre Akira Enjiu
Ana Paula Soler Dos Santos
Anderson Fábio da Silva
Carlos Evandro Higa
Douglas Pereira Mateus
Erica Okamura
Gabriel Dargas
Gabriela Pinheiro da Silva
Giovana Rocha Santos
Guilherme Antonio de Souza Mauricio
Guilherme Cristiano da Silva Costa
Jackson dos Santos Roque da Silva
João Rafael Iasorli Rodrigues
Julia Assunção Silva
Lucas Goiana Malicia
Mariana Sayuri Kuchida
Michael dos Santos Silva
Vinicius Bezerra Lima
Posted on April 15, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.