Skip to content

Commit

Permalink
Updates to library and examples
Browse files Browse the repository at this point in the history
  • Loading branch information
ZodiusInfuser committed Oct 25, 2024
1 parent b4d1546 commit 21ccff0
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 88 deletions.
31 changes: 20 additions & 11 deletions examples/led_wave.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,32 @@
import sys
import time

from machine import I2C

from qwstpad import QwSTPad
from qwstpad import ADDRESSES, NUM_LEDS, QwSTPad

"""
Apply a wave effect across QwSTPad's onboard LEDs.
"""

# Constants
SLEEP = 0.2 # The time between each LED update
I2C_PINS = {"id": 0, "sda": 4, "scl": 5} # The I2C pins the QwSTPad is connected to
I2C_ADDRESS = ADDRESSES[0] # The I2C address of the connected QwSTPad
SLEEP = 0.2 # The time between each LED update

# Variables
current = 1 # The LED currently being controlled
active = True # The state to set the controlled LED to
led = 1 # The LED currently being controlled
active = True # The state to set the controlled LED to

# Create the I2C instance and pass that to the QwSTPad
i2c = I2C(0, scl=13, sda=12)
qwstpad = QwSTPad(i2c)

# Attempt to create the I2C instance and pass that to the QwSTPad
try:
qwstpad = QwSTPad(I2C(**I2C_PINS), I2C_ADDRESS)
except OSError:
print("QwSTPad: Not Connected ... Exiting")
sys.exit()

print("QwSTPad: Connected ... Starting")

# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
Expand All @@ -26,12 +35,12 @@
# Loop forever
while True:
# Modify the current LED
qwstpad.set_led(current, active)
qwstpad.set_led(led, active)

# Move along to the next LED, wrapping if reaching the end
current += 1
if current > 4:
current = 1
led += 1
if led > NUM_LEDS:
led = 1
active = not active

time.sleep(SLEEP)
Expand Down
149 changes: 91 additions & 58 deletions examples/multi_player.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,31 @@
import math
import sys
from collections import namedtuple

from machine import I2C
from picographics import DISPLAY_PICO_DISPLAY_2, PEN_RGB565, PicoGraphics
from picographics import DISPLAY_PICO_DISPLAY_2 as DISPLAY
from picographics import PEN_RGB565, PicoGraphics

from qwstpad import ADDRESSES, QwSTPad

# Colour Constants
"""
A multi-player QwSTPad game demo. Each player drives a tank-like vehicle around an arena
with the goal of hitting other players with projects to get the most points.
Makes us of 1 to 4 QwSTPads and a Pico Display pack 2.0 / 2.8
Controls:
* U = Move Forward
* D = Move Backward
* R = Turn Right
* L = Turn left
* A = Fire
"""

# General Constants
I2C_PINS = {"id": 0, "sda": 4, "scl": 5} # The I2C pins the QwSTPad is connected to
BRIGHTNESS = 1.0 # The brightness of the LCD backlight (from 0.0 to 1.0)

# Colour Constants (RGB565)
WHITE = const(65535)
BLACK = const(0)
CYAN = const(65287)
Expand All @@ -14,20 +34,30 @@
GREEN = const(57351)
RED = const(248)
BLUE = const(7936)

# Projectile Constants
GREY = const(36467)

# Gameplay Constants
PlayerDef = namedtuple("PlayerDef", ("x", "y", "colour"))
PLAYERS = (PlayerDef(x=30, y=50, colour=GREEN),
PlayerDef(x=280, y=50, colour=MAGENTA),
PlayerDef(x=30, y=200, colour=CYAN),
PlayerDef(x=280, y=200, colour=BLUE))
PLAYER_RADIUS = 10
PLAYER_SPEED = 4
LINE_LENGTH = 25
START_ANGLE = 20
PROJECTILE_LIMIT = 15
PROJECTILE_SPEED = 5
GRID_SPACING = 20

# Player Constants
POSITIONS = ((30, 50), (280, 50), (30, 200), (280, 200))
COLOURS = (GREEN, MAGENTA, CYAN, BLUE)
LINE_LENGTH = 25
START_ANGLE = 20
PLAYER_RADIUS = 10
PLAYER_SPEED = 4
# Variables
display = PicoGraphics(display=DISPLAY, # The PicoGraphics instance used for drawing to the display
pen_type=PEN_RGB565) # It uses 16 bit (RGB565) colours
i2c = I2C(**I2C_PINS) # The I2C instance to pass to all QwSTPads
players = [] # The list that will store the player objects

GRID_SPACING = 20
# Get the width and height from the display
WIDTH, HEIGHT = display.get_bounds()


# Classes
Expand Down Expand Up @@ -91,12 +121,12 @@ def update(self):
self.x -= PLAYER_SPEED * math.cos(self.direction)
self.y -= PLAYER_SPEED * math.sin(self.direction)

if button['A']:
self.fire()

# Clamp the player to the screen area
self.x = min(max(self.x, self.size), WIDTH - self.size)
self.y = min(max(self.y, self.size), HEIGHT - self.size)

if button['A']:
self.fire()

new_proj = []
for projectile in self.projectiles:
Expand Down Expand Up @@ -128,22 +158,47 @@ def draw(self, display):
# Draw our score at the bottom of the screen
display.set_pen(self.colour)
display.text(f"P{self.index + 1}: {self.score}", 5 + self.index * 80, 227, WIDTH, 2)

def check_hits(self, players):
for other in players:
if other is not self:
for projectile in self.projectiles:
if projectile.has_hit(other):
other.was_hit = True
self.score += 1


# Create a player for each connected QwSTPad
for i in range(len(ADDRESSES)):
try:
p = PLAYERS[i]
pad = QwSTPad(i2c, ADDRESSES[i])
players.append(Player(i, p.x, p.y, PLAYER_RADIUS, p.colour, pad))
print(f"P{i + 1}: Connected")
except OSError:
print(f"P{i + 1}: Not Connected")

if len(players) == 0:
print("No QwSTPads connected ... Exiting")
sys.exit()

print("QwSTPads connected ... Starting")

# Turn on the display
display.set_backlight(BRIGHTNESS)

# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
# Loop forever
while True:
# Update all players (and their projectiles)
for p in players:
p.update()

# Check if any projectiles have hit players
for p in players:
p.check_hits(players)

class Game(object):
def __init__(self, i2c):
self.players = []

# Create a player for each QwSTPad detected
for i in range(len(ADDRESSES)):
try:
player = Player(i, *(POSITIONS[i]), PLAYER_RADIUS, COLOURS[i], QwSTPad(i2c, ADDRESSES[i]))
self.players.append(player)
print(f"P{i + 1}: Connected")
except OSError:
print(f"P{i + 1}: Not Connected")

def draw(self, display):
# Clear the screen
display.set_pen(BLACK)
display.clear()
Expand All @@ -155,37 +210,15 @@ def draw(self, display):
display.pixel(x, y)

# Draw players
for p in self.players:
for p in players:
p.draw(display)

# Update the screen
display.update()

def update(self):
# Update all players and projectiles to their new positions
for p in self.players:
p.update()

# Check if any projectiles have hit players
for p1 in self.players:
for p2 in self.players:
if p2 is not p1:
for projectile in p1.projectiles:
if projectile.has_hit(p2):
p2.was_hit = True
p1.score += 1


# Variables
display = PicoGraphics(display=DISPLAY_PICO_DISPLAY_2, pen_type=PEN_RGB565)
display.set_backlight(1.0)

WIDTH, HEIGHT = display.get_bounds()
GREY = display.create_pen(115, 115, 115)

i2c = I2C(0, scl=5, sda=4)
g = Game(i2c)

while True:
g.update()
g.draw(display)
finally:
# Turn off the backlight, clear the screen, and update
display.set_backlight(0)
display.set_pen(BLACK)
display.clear()
display.update()
41 changes: 28 additions & 13 deletions examples/read_all.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
import sys
import time

from machine import I2C

from qwstpad import QwSTPad
from qwstpad import ADDRESSES, QwSTPad

"""
How to read all of the buttons on QwSTPad
"""

# Constants
SLEEP = 0.1 # The time between each reading of the buttons
I2C_PINS = {"id": 0, "sda": 4, "scl": 5} # The I2C pins the QwSTPad is connected to
I2C_ADDRESS = ADDRESSES[0] # The I2C address of the connected QwSTPad
SLEEP = 0.1 # The time between each reading of the buttons

# Create the I2C instance and pass that to the QwSTPad
i2c = I2C(0, scl=13, sda=12)
qwstpad = QwSTPad(i2c)

# Loop forever
while True:
# Read all the buttons from the qwstpad and print them out
buttons = qwstpad.read_buttons()
for key, value in buttons.items():
print(f"{key} = {value:n}", end=", ")
print()
# Attempt to create the I2C instance and pass that to the QwSTPad
try:
qwstpad = QwSTPad(I2C(**I2C_PINS), I2C_ADDRESS)
except OSError:
print("QwSTPad: Not Connected ... Exiting")
sys.exit()

time.sleep(SLEEP)
print("QwSTPad: Connected ... Starting")

# Wrap the code in a try block, to catch any exceptions (including KeyboardInterrupt)
try:
# Loop forever
while True:
# Read all the buttons from the qwstpad and print them out
buttons = qwstpad.read_buttons()
for key, value in buttons.items():
print(f"{key} = {value:n}", end=", ")
print()

time.sleep(SLEEP)

finally:
qwstpad.clear_leds() # Turn off all four LEDs
12 changes: 6 additions & 6 deletions src/qwstpad.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class QwSTPad:
})
LED_MAPPING = (0x6, 0x7, 0x9, 0xA)

def __init__(self, i2c, address=DEFAULT_ADDRESS):
def __init__(self, i2c, address=DEFAULT_ADDRESS, show_address=True):
if address not in ADDRESSES:
raise ValueError("address is not valid. Expected: 0x21, 0x23, 0x25, or 0x27")

Expand All @@ -51,7 +51,11 @@ def __init__(self, i2c, address=DEFAULT_ADDRESS):
self.__button_states[key] = False

self.__led_states = 0b0000
self.set_leds_from_addr()
if show_address:
self.set_leds(self.address_code())

def address_code(self):
return self.__change_bit(0x0000, ADDRESSES.index(self.__address), True)

def read_buttons(self):
state = self.__reg_read_uint16(self.__i2c, self.__address, self.INPUT_PORT0)
Expand All @@ -63,10 +67,6 @@ def set_leds(self, states):
self.__led_states = states & 0b1111
self.__update_leds()

def set_leds_from_addr(self):
self.__led_states = self.__change_bit(0b0000, ADDRESSES.index(self.__address), True)
self.__update_leds()

def set_led(self, led, state):
if led < 1 or led > NUM_LEDS:
raise ValueError("'led' out of range. Expected 1 to 4")
Expand Down

0 comments on commit 21ccff0

Please sign in to comment.