Week 3 - Embedded Programming

Takeaways from Office Hours

  • Installation steps for the Seeed RP2040 can be found in the Seeed Wiki, including information on how to set up the programming of the microcontroller with either Arduino or MicroPython.
  • Internal chip numbers are the ones that start with PIO, use those to send commands. They are less likely to be confusing than the D ones
  • We find the blue RGB is connected to the pin 25 by looking at the tag that is connected to the image below. Looking at where the tag is located we see GPIO25. Note that it also says 37 (the packaging pin number), but we don’t care about that one. See that Green and Red are GPIO 16 and 17, respectively
  • It’s good practice to use ifdef, define for any number in your code
  • HIGH = LED OFF and LOW = LED ON
  • This is because we can look at the schematic again and see the LED pins are connected to the 3.3V voltage by default. When we set the voltage HIGH on Arduino, the current can’t flow from the 2 points. Only when you set in low, the current can flow

Testing some functions

The first step in working with my chosen RP2040 (Seeed Package) is to read the datasheet and familiarize myself with the hardware. Some of the features of this device are a USB-C port, a Reset and Boot Button, User LEDs with 3 solid colors, a bigger RGB LED, Power LED and various I/O ports. The datasheet proved to be too intense to be fully understood as a beginner, so attending the Office Hours for guidance was definitely helpful.

After powering the RP2040, we noticed that the RGB LED was already blinking (pre-installed code), so pressing the Boot and Reset buttons at the same time helped us start the board with a factory reset. After installing the Arduino IDE and adding an Additional Boards Manager - per instructions here -, we tested some basic functions of the microcontroller, e.g. blinking the small User LEDs (colors Red, Green and Blue) located near the USB-C port and sending a text message to the computer/Arduino display. All code files will be available at the end of this document.

I wanted to try the Micropython environment and control a different LED, so I used the tutorial from Seed Studio to install the Thonny editor and light up the RGB LED in many different colors. After successfully completing this step, my idea for this week’s project was to create a simple color memory game.

Color Memory Game

Since I didn’t have a lot of familiarity with Micropython before, I asked Chat GPT3-5 to help me with the skeleton of my code. My input and the answer from GPT are as follows:

Input

help me create a micropython code for a color memory game. I have 3 buttons that correspond to 3 colors (red, blue and green) and 1 LED that can show all colors. I want to randomly show a sequence starting to 1 color and wait for the correct press button to be pushed to incrementally add to the sequence of colors until we have 5 colors

Output

Creating a color memory game using MicroPython for the ESP8266 (or a similar microcontroller) with 3 buttons (red, blue, and green) and an RGB LED to display colors is a fun project. Here's a basic outline of the code to get you started:


import machine
import time
import random

# Define the pins for the buttons and RGB LED
button_red = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)
button_blue = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
button_green = machine.Pin(4, machine.Pin.IN, machine.Pin.PULL_UP)
led_red = machine.Pin(12, machine.Pin.OUT)
led_green = machine.Pin(13, machine.Pin.OUT)
led_blue = machine.Pin(14, machine.Pin.OUT)

# Define the color codes
COLORS = {
	'red': (1, 0, 0),
	'green': (0, 1, 0),
	'blue': (0, 0, 1)
}

# Initialize the game variables
sequence = []
user_sequence = []
round_number = 1

def generate_sequence():
	return [random.choice(['red', 'blue', 'green']) for _ in range(round_number)]

def display_color(color):
	led_red.value(COLORS[color][0])
	led_green.value(COLORS[color][1])
	led_blue.value(COLORS[color][2])

def play_sequence():
	for color in sequence:
		display_color(color)
		time.sleep(1)
		display_color('black')  # Turn off the LED
		time.sleep(0.5)

def check_user_sequence():
	for i, color in enumerate(user_sequence):
		if color != sequence[i]:
			return False
	return True

while True:
	# Start a new round
	sequence = generate_sequence()
	user_sequence = []
	print("Round", round_number)

	play_sequence()

	# Collect user input
	for _ in range(round_number):
		while True:
			if button_red.value() == 0:
				user_sequence.append('red')
				break
			elif button_blue.value() == 0:
				user_sequence.append('blue')
				break
			elif button_green.value() == 0:
				user_sequence.append('green')
				break

	# Check if the user's sequence matches
	if check_user_sequence():
		print("Round", round_number, "complete!")
		round_number += 1
	else:
		print("Game Over! You reached round", round_number)
		break
										

This framework was really helpful in creating a code for a simple game that records the input from button presses and that was nonspecific to the RP2040 board, but naturally, I had to make some modifications in each of the sections to make this code work. Some of the modifications done were:

  • Change Pin ports, disable internal Pull-Up on Pins (physical Pull-Down resistors were used in my setup).
  • Change color definition to RGB values, identify them by numbers
  • Finish the game in round 5
  • Add a delay when collecting the press buttons input (if this is not done your user sequence will only be filled with your initial button pressed)
  • Add a flashing yellow light at the start or when you lose the game. Add a rainbow light when the user wins
  • Change the display color functions to match the specific RGB LED command

After these modifications, my code and my setup can be seen below. The buttons corresponded to the Red, Green, and Blue lights in this order from top to bottom (I tried to paint the buttons to more clearly identify them).

The random light sequence is incrementally longer from 1 to 5 lights, and the user needs to press the corresponding buttons in the correct order to advance to the next round. The computer shell shows a message when each round is complete and when the user loses or wins the game. In the video, you can see my game in action, with a successful round first and then a failed attempt.

Final Code


from ws2812 import WS2812
import machine
import utime
import random

# Define the pins for the buttons and RGB LED
button_red = machine.Pin(28, machine.Pin.IN)
button_green = machine.Pin(29, machine.Pin.IN)
button_blue = machine.Pin(27, machine.Pin.IN)
power = machine.Pin(11,machine.Pin.OUT)
power.value(1)

# Define the color codes
RED = (255, 0, 0) #Color 0
GREEN = (0, 255, 0) #Color 1
BLUE = (0, 0, 255) #Color 2
BLACK = (0, 0, 0) #Color 3
YELLOW = (255, 255, 0)
CYAN = (0, 255, 255)
BLUE = (0, 0, 255)
PURPLE = (180, 0, 255)
WHITE = (255, 255, 255)

COLORS = (RED, GREEN, BLUE, BLACK)
START = (YELLOW, BLACK, YELLOW, BLACK, YELLOW, BLACK)
RAINBOW = (BLACK, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE)
led = WS2812(12,1) #WS2812(pin_num,led_count)

# Initialize the game variables
sequence = []
user_sequence = []
round_number = 1

def generate_sequence():
	global sequence
	addition = random.choice([0, 1, 2])
	if round_number == 1:
		sequence = [addition]
	else:
		sequence = sequence + [addition]
	return sequence
	
def display_color(color):
	led.pixels_fill(COLORS[color])
	led.pixels_show()

def play_sequence():
	for color in sequence:
		display_color(color)
		utime.sleep(1)
		display_color(3)  # Turn off the LED, display Black
		utime.sleep(0.5)

def check_user_sequence():
	if user_sequence != sequence:
		return False
	return True

while True:
	
	# Start a new round
	if round_number == 1:
		sequence = generate_sequence()
		user_sequence = []
		for color in START: 
			led.pixels_fill(color)
			led.pixels_show()
			utime.sleep(0.5)

	else:
		del user_sequence[:]
	
	utime.sleep(1)
	play_sequence()

	# Collect user input
	for i in range(1,round_number+1):
		while True:
			if button_red.value() == 1:
				user_sequence.append(0)
				break
			elif button_green.value() == 1:
				user_sequence.append(1)
				break
			elif button_blue.value() == 1:
				user_sequence.append(2)
				break
	
		utime.sleep(0.7)  # Add a small delay between button presses

	# Check if the user's sequence matches
					
	if check_user_sequence():
		print("Round", round_number, "complete!")
	
		if round_number == 5:
			print("Congratulations! You won the game!")
			for color in RAINBOW: 
				led.pixels_fill(color)
				led.pixels_show()
				utime.sleep(0.2)
			break
		
		round_number += 1
		sequence = generate_sequence()
		
	else:
		print("Game Over! You reached round", round_number-1)
		for color in START: 
			led.pixels_fill(color)
			led.pixels_show()
			utime.sleep(0.5)
		break

										
									

Setup

Files for this week can be found here