ESP32 and touch display - tutorial part 3 - interactions, games and fun

Today we continue our adventure with the ESP32 module + touchscreen display version of the ESP32-2432S028R. In this section we will practice interacting with the display using the touchscreen. To do this, we will write some simple interactive programs here, such as measuring the user's reaction time, a maths quiz and a 'wheel and cross' game.
At the outset, I would like to point out right away that this is not the only way to draw on this display. On the contrary, there is even a better way, we can run LVGL on it and even create interfaces in a program suitable for this, but patience - LVGL will be discussed in the following sections.
Previous parts:
ESP32 and touch display - tutorial - part 1 - how to program? Basics .
ESP32 and touch display - part 2 - how to draw pixels, lines, shapes, performance issues
Response time
To start with, I came up with something very simple - measuring reaction time. This is about the user's reaction time, i.e. we look at how quickly we are able to press the screen after it changes colour.
Let's think about how we can implement this?
We need to, in a loop:
- set, for example, the initial state of the screen (say, a black screen)
- wait for some time, giving the user time to get ready
- suddenly change e.g. the screen colours to the opposite (give the user a sign)
- from then on we need to somehow measure the time until the screen is pressed
- then we need to display this time
To measure the time we will use, as is common in Arduino, the function millis .
Now let's type this in sequentially in the code. Stage one - the word "WAIT" and the wait (you could make it random):
Code: C / C++
Stage two - signal for action and start of timing:
Code: C / C++
Stage three - timing (ultimately could probably be done with an interrupt, but in such a simple program I took the liberty of loop blocking the execution of the thread):
Code: C / C++
The loop breaks when the screen detects pressure. We then take the current time, subtract the stored time from it and display the result:
Code: C / C++
Presentation:
And the full code:
Code: C / C++
Mathematics Quiz .
In the previous programme, we used the basic functions of drawing (displaying text) and taking input from the user (touch detection - just presence there, true or false). Now it's time to expand on this a bit. Let's set ourselves two tasks:
- we want to display a bit more on the screen (some shapes, maybe even buttons?).
- we also want to use pressure positions, perhaps to see which button is pressed
Eventually we will use LVGL rather, there are ready-made buttons and other user interface components there, but for now we are practising simple programmes for learning and satisfaction.
A good example of such an application will be a maths quiz.
The operation will involve (as before, in a loop):
- drawing two numbers for an operation (e.g. multiplication) and displaying them on the screen
- preparing answers A B C D, where one of them is correct and the other answers are different from the correct one (you will have to draw in a loop - until
- waiting for input from the user (press)
- checking which button has been pressed (we will do this in a simple way, a few rigid conditions)
- checking if the correct answer has been selected
- displaying an appropriate message depending on what the user has selected
Counting the number of correct and wrong answers could also be added.
Here we go, global variables for counting:
Code: C / C++
Then we execute our entire new action in a loop. First, clearing the screen and displaying the current result.
Code: C / C++
You also need to draw some two numbers, say from 1 to 10, for convenience.
Code: C / C++
Then let's draw an operation to perform on these numbers. There will be variety:
Code: C / C++
Now things get a bit more complicated. We have to randomise the correct answer items, because what would a quiz be where the correct answer is always, say, A?
Code: C / C++
The above code first draws the position of the correct answer and then draws the necessarily incorrect numbers.
We can then display the task on the screen and mark the button zones:
Code: C / C++
The mysterious "datum" determines how the text is centred and includes the following options:
TL_DATUM = 0 = Top left
TC_DATUM = 1 = Top centre
TR_DATUM = 2 = Top right
ML_DATUM = 3 = Middle left
MC_DATUM = 4 = Middle centre
MR_DATUM = 5 = Middle right
BL_DATUM = 6 = Bottom left
BC_DATUM = 7 = Bottom centre
BR_DATUM = 8 = Bottom right
MC stands for 'middle centre', meaning we centre the text.
The most important thing, however, is to draw the 'buttons'. We assume that the buttons start in the middle of the screen, hence the baseY variable. Then, in a loop from 0 to 4 (no 4, so only 0, 1, 2 and 3), we use the remainder of the division (modulo) and divide to select one of the quarters of our screen. We draw the rectangles one by one and then display the text in them.
We then wait for the user's reaction:
Code: C / C++
The above code waits for the screen to be touched and then calculates the position of the touch in pixels of the display.
Then, a bit on foot, we check which quadrant has been "touched". There are only four, so three conditional blocks are enough:
Code: C / C++
It would be important to remember to update the button drawing and button checking at once, because here essentially the same shape repeats twice. Ultimately, this could be done better (and even introduce a button class).
Now it remains to check that the user has selected the correct answer and display the appropriate message:
Code: C / C++
Here there is also a count of the number of correct and incorrect answers. In addition, here I have added a display of the selected option, this is just to verify the performance of the program.
After that, there is just one more delay in the code to give the user time to read the message.
Here is all the code:
Code: C / C++
Result:
Wheel and cross .
In the previous paragraph we've already done a bit of practice interacting with something more than text, there were four interactive buttons, how about now doing a full-on project based on 'buttons' and shapes? One of the more popular tasks, even if only at university, is to make a wheel and cross game. Let's try to implement it here. Let's take the easier version, which is a game of two players with each other. I don't want to focus here on playing with a computer.
Ok, what do you need for this game?
Certainly:
- visually drawing the board (with lines?)
- drawing visually circles and crosses (there is a ready-made function for circles and crosses are just two lines, no problems)
- you need to store the state of the board somehow, the board is two-dimensional (chessboard), so maybe a two-dimensional array? Just what its content - maybe an enumerator (field), although what for? Maybe use ASCII characters, i.e. spaces and the letters x and o?
- The logic of turns and win checks is needed, but it is not that difficult, we have just two diagonals, and 3 lines each vertically and horizontally. These lines could be done in loops, so two loops and two conditions....
So let's start with the code. First the constants, the size of the board (number of boxes) and the cells (in pixels). Then the aforementioned array, empty to start with. Then a variable specifying whose move it is.
Code: C / C++
Now the line drawing. Here we are drawing a blank board, without stamps.
Code: C / C++
The above code benefits from a small optimisation, namely instead of drawing a "normal" line, we use a function that quickly draws a line according to a given axis, separately H - horizontal, and V - vertical.
Now perhaps the aforementioned drawing of the cross (x) sign:
Code: C / C++
The column and row indices of the box on the chessboard are given as arguments here, only in the function do I convert them to pixels.
Now, analogously, drawing a circle mark:
Code: C / C++
Now perhaps an auxiliary function that draws a symbol from a given position on the chessboard:
Code: C / C++
Now an auxiliary function to put a marker (it also changes the player's queue):
Code: C / C++
The above function also checks if it is possible to place a mark on the field, otherwise nothing is done.
Now cleaning the board:
Code: C / C++
The worst is left - checking the victory condition.
There are a bit of options here.
Horizontal lines, vertical lines, and two slants.
Let's start with the horizontal and vertical lines - this is where the loop will help us.
Code: C / C++
The above code can be optimised (check both axes at once).
Now let's check the diagonals:
Code: C / C++
The above code can also be rewritten by the reader into a single conditional block, it just takes a bit of practice with the conditions and logical operators.
There is one more thing left to check - whether there is a tie. When is there a tie? When there are no more spaces for the next mark. That is, we check if there is at least one space character in the array (according to the accepted standard).
Code: C / C++
That leaves the setup and loop functions - but there you can guess what we're giving. The game will wait for input from the user and only then will it update:
Code: C / C++
Mapping the pixels to our cells is also straightforward - we simply divide into cellSize (operations are done on integers there).
The result:
Whole code:
Code: C / C++
Summary
Someone might ask - "is it really necessary to draw everything on foot?". Of course not - but it's worth trying it to get some idea of how coordinates work, for example, or how we know which part of the interface is pressed.
Nevertheless, I will be moving away from this in the later parts of my ESP32-2432S028R adventure. Further projects I will present (including e.g. lighting control via WiFi or measurement readings from Tasmota) will already be based on LVGL, so as not to reinvent the wheel.
In addition, in the next part we will already try to use WiFi connectivity - I will show how to download some data from the Internet. Maybe a weather forecast via an API? Details to come soon.
Comments
Add a comment