Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / game

Simon Says, A Child's Game

5.00/5 (2 votes)
3 Apr 2023CPOL7 min read 6.3K   26  
Project like Simon Says
I'm working on a Simon Says like project for my 5 year old grandson that uses voice commands to instruct him to do things like; clap your hands, raise your left arm, etc.

Introduction

I'm working on a project similar to the old Simon Says game that kids used to play many years ago. I thought my 5 year old grandson would like it and it seemed a fairly easy project to throw together for when he comes to visit Papa. The Simon Says game is played by two people and one person gives the other an instruction such as: "Simon Says, Hop on one foot." and the other person responds by obeying the command. I had recently purchased a DY-HV20T Voice Playback board from AliExpress and thought it would be a good choice for the project. Most modules of this kind that come out of China provide very little in the way of documentation, but in this case, there is a fairly useful manual. (See references)

A list of components needed for this project is listed in the following table. Since I've been writing lately about the STMicroelectronics ARM processors and this is such a simple application I thought that I would continue the series using the NUCLEO-C031C6 board featuring the STM32C031C6 Cortex-MO+ processor.

Description Where to Buy
NUCLEO-C031C6 st.com
Speaker 8 OHMS @ 10 WATTS amazon.com
DY-HV20T Voice Playback Module aliexpress.com
8GB-32GB Micro SD Card Parts Bin
SPST Momentary Switch Parts Bin
10K resistor Parts Bin
1K resistor Parts Bin
10uF electrolytic capacitor Parts Bin
74HC14N Hex Inverter with Schmitt-Trigger inputs Parts Bin
9V 1A Power Supply Your choice
2 x .1uF capacitors Parts Bin
7805 Voltage Regulator Parts Bin

The Voice Playback board, see Figure 1, is a nice little board that has a lot of functionality including: a connection for speakers, a jack for audio output, volume adjustment, TF Card socket and various modes for playing WAV and MP3 files.

Image 1

Figure 1: DY-HV20T Voice Playback Board

I got this board from AliExpress but they are available in various forms on Amazon and eBay. I had the board for a while before I figured out what I wanted to do with it and after talking with my significant other, I discovered a great application for the board. She told me a story of a game she used to play with her son when he was a small child and the weather was such that they couldn't do anything outside. (At the time, she was stationed in Germany.)

The game was played by her giving an instruction for her son to do, things like: hold up your left arm, stick out your right foot, etc. It got the name "What's Next?" because every time he finished doing a task, he would say "What's Next?".

So, it got me thinking that if I could program a processor to select pre-recorded questions in a random order using the Voice Playback module that it wouldn't be hard to hook up a button to start the program and at the same time, seed a Random Number generator thus making the instructions truly random.

As I stated earlier, the board can be configured to run in several different modes and for this application, I chose the Integrated Mode 0 mode because it allowed me to make a selection and it would play the voice file until it ended. Referring to Figure 2, it can be seen that using Integrated Mode 0 allows me to use a maximum of 255 files, pay close attention to the file names as shown in the image in the I/O Integrated Mode 0 section. Notice that there is no 00000.mp3 available, starts at 00001.mp3. This is a tad misleading because you can also play WAV files which is what I mainly use in this application, although I do have a couple of MP3 files that have a child's tune that the player can dance too.

Image 2

Figure 2: DY-HV20T Work Mode Configuration

The board has a 8 pin parallel port to select the file to play and according to the manual once the input is released, the file will play until it finishes and while it is playing, it holds the BUSY pin high. This seems like it would be a simple process but it took me a while to figure out because results were not consistent. After struggling with this for some time, trying various solutions without any luck, I got out my Saleae Logic Analyzer and viewed the results. (I can't recommend this device and the people at Saleae high enough, great bunch) The reason I was having problems is that either I misunderstood the documentation or it was just plain wrong. Instead of pulsing the selection lines, I ended up making the selection, by setting the appropriate pins LOW waiting until the selection finished, BUSY pin is HIGH during play and goes LOW when done, then setting the selection pins back HIGH. This process works consistently! According to the manual, this is what Integrated Mode 1 is supposed to do?

C++
void Play(uint8_t val)
{
    // Set the appropriate bits LOW
    // Wait for the BUSY line to go LOW
    // Reset the select bits
    GPIOA->ODR &= ~val;
    if (GPIOB->IDR & BUSY_PIN)
        while (GPIOB->IDR & BUSY_PIN);
    GPIOA->ODR |= val;
}    
Listing 1. Code to Play the current selection

Hardware

Figure 3 shows the working hardware configuration, the only thing missing is the power supply. I'm using a triple output supply to debug but if I get enough interest, I'll put everything in a nice box with a single power supply. The NUCLEO board has the ability to accept 7V-12V by using the VIN pin and setting jumper JP5, the 5V Power Selection jumper. The DY-HV20T also uses a 6V-35V supply voltage so a 9V Walwart will probably be ideal since neither draw very much current.

Image 3

Figure 3: Complete circuit

The table below is the pinout for wiring the NUCLEO board to the DY-HV20T board. It's a pretty simple setup, the STM32C031C6 has plenty of pins available for the parallel communication but if another processor is used with less pins available a 74HC595 Shift Register could be used to reduce the number of pins needed to 5.

Description NUCLEO-C031C6 Direction
I/O 0 CN7 Pin 28 (GPIOA Pin0) Output
I/O 1 CN7 Pin 30 (GPIOA Pin1) Output
I/O 2 CN7 Pin 37 (GPIOA Pin2) Output
I/O 3 CN10 Pin 10 (GPIOA Pin3) Output
I/O 4 CN7 Pin 32 (GPIOA Pin4) Output
I/O 5 CN10 Pin 11 (GPIOA Pin5) Output
I/O 6 CN10 Pin 13 (GPIOA Pin6) Output
I/O 7 CN10 Pin 15 (GPIOA Pin7) Output
Button input to NUCLEO CN10 Pin 17 (GPIOB Pin0) Input
BUSY CN7 Pin 34 (GPIOB Pin1) Input

The button debounce circuit, shown in Figure 4, is a common circuit that I got from an excellent two page article by Jack Ganslle, A Guide to Debouncing - Part 1, or, How to Debounce a Contact in Two Easy Pages. I'm basically a software guy with just enough understanding of electronics to be dangerous.

Image 4

Figure 4: Momentary switch debounce circuit

Software

The sound clips are played in a random manner and I use the SysTick timer to provide the seed for the Random Number Generator. I got the RNG from an excellent article on CodeProject, see references. I had to modify it a tad to get it to work with the STM32C031C6, the original version used the C# TimeDate function to seed the generator. I thought that if I started the Systick timer and used a invitation clip to have the user press a button when they were ready to play, the time would be random enough to seed the generator and in practice, it works rather well. Listing 2 is the code I used to initialize the SysTick timer.

C++
void InitSysTick(uint ticks)
{
    // Disable SysTick
    SysTick->CTRL = 0;

    // Set reload register
    SysTick->LOAD = ticks - 1;

    // Set counter to 0
    SysTick->VAL = 0;

    // Enable SysTick
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
} 
Listing 2. Initialize the SysTick timer

Listing 3 is the code to Initialize the Pin Change interrupt for the Play button. The interrupt logic detects the rising edge on PORTB Pin0 and fires the interrupt routine in the second half of the code segment.

C++
void ConfigurePinChangeInterrupt()
{
	__disable_irq();

	// Enable GPIOB
	RCC->IOPENR |= RCC_IOPENR_GPIOBEN;
	// Enable SYSCFGEN
	RCC->APBENR2 |= RCC_APBENR2_SYSCFGEN;

	// Configure PB0 as output with pull down
	GPIOB->MODER &= ~(3 << 0);
	GPIOB->PUPDR |= 2;

	// External interrupt select PB0
	EXTI->EXTICR[0] &= ~0x00ff;
	EXTI->EXTICR[0] |= EXTI_EXTICR1_EXTI0_0;
	// wakeup with interrupt unmasked PB0
	EXTI->IMR1 |= EXTI_IMR1_IM0;
	// Rising edge select
	EXTI->RTSR1 |= EXTI_RTSR1_RT0_Msk;

	NVIC_EnableIRQ(EXTI0_1_IRQn);

	__enable_irq();
}

// Button press interrupt handler for PB0 pin change on
//	rising edge.
volatile bool g_flg = false;
void EXTI0_1_IRQHandler (void)
{
	g_flg = true;
	EXTI->RPR1 |= 1;
}    
Listing 3. Initialize the Pin Change Interrupt for the Play button

The Main routine, in listing 4, waits in the while loop for the g_flg to get set, then grabs a Random number and plays the corresponding sound clip. It's pretty simple logic and I will probably looking into putting the processor to sleep while waiting, but this will work for a one off to amuse my grandson.

C++
int main(void)
{
	uint r = 1;

	ConfigurePinChangeInterrupt();

	InitRandom();
	SimonInit();

	Play(INTRO_SOUND);
	SetSeedFromTimer();

	while (1)
	{
		if (g_flg)
		{
			r = GetUintInRange(SOUND_FILES_MAX);
			Play(r);
			g_flg = false;
		}
	}
} 
Listing 4. Main routine

Conclusion

This has been a fun project; but, a little frustrating. First, I started to use an ATMega4809 processor and spent way too long trying to get it to work, the sound clip wouldn't play when the timer was running, still haven't figured out why. Second, the manual is very confusing, I don't understand why the Chinese don't get someone that can create documentation that is understandable, instead they use some kind of translator and what comes out is garbage most of the time. In this case, I either didn't understand their meaning or they were just plain wrong.

References

History

  • 3rd April, 2023: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)