Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LED Panel! #181

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions components/led.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import wpilib
from ids import PwmChannels
from components.led_panel import LEDPanel


MAX_BRIGHTNESS = 50 # Integer value 0-255
Expand Down Expand Up @@ -51,13 +52,17 @@ def with_relative_brightness(self, multiplier: float) -> Hsv:


class LightStrip:
def __init__(self, strip_length: int) -> None:
led_panel: LEDPanel

def __init__(self) -> None:
self.leds = wpilib.AddressableLED(PwmChannels.led_strip)
self.leds.setLength(strip_length)
self.strip_length = strip_length

self.strip_length = (28 * 3) * 2 + (30 * 3) - 2
self.leds.setLength(self.strip_length)
self.strip_length = self.strip_length

self.led_data = wpilib.AddressableLED.LEDData()
self.strip_data = [self.led_data] * strip_length
self.strip_data = [self.led_data] * self.strip_length

self.pattern: Pattern = Rainbow(HsvColour.MAGENTA)
self.high_priority_pattern: Pattern | None = None
Expand All @@ -67,24 +72,31 @@ def __init__(self, strip_length: int) -> None:

def no_note(self) -> None:
self.pattern = Solid(HsvColour.OFF)
self.led_panel.no_note()

def intake_deployed(self) -> None:
self.pattern = Flash(HsvColour.MAGENTA)
self.led_panel.intake_deployed()

def in_range(self) -> None:
self.pattern = Solid(HsvColour.GREEN)
self.led_panel.in_range()

def not_in_range(self) -> None:
self.pattern = Solid(HsvColour.RED)
self.led_panel.not_in_range()

def climbing_arm_extending(self) -> None:
self.high_priority_pattern = Flash(HsvColour.YELLOW)
self.led_panel.climbing_arm_extending()

def climbing_arm_fully_extended(self) -> None:
self.high_priority_pattern = Solid(HsvColour.YELLOW)
self.led_panel.climbing_arm_fully_extended()

def climbing_arm_retracted(self) -> None:
self.high_priority_pattern = None
self.led_panel.climbing_arm_retracted()

def morse(self) -> None:
self.pattern = Morse(HsvColour.ORANGE)
Expand Down
95 changes: 95 additions & 0 deletions components/led_panel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import wpilib


class LEDPanel:
# Key: Match number, Value: Drop Bears match number (<=15)
# TODO: Fill with proper values when the draw comes out
matches: dict[int, int] = {1: 1}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The match schedule has been published. Do you intend to fill this in now?


def __init__(self) -> None:
# Custom packet: 00 000000
# match state, state specific information
self.match_state = 0
self.packet = 0b01000000
self.panel_port = wpilib.SerialPort(
baudRate=9600, port=wpilib.SerialPort.Port.kUSB1, dataBits=8
)
self.set_match_id()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This happens on robot code start. getMatchNumber will always be invalid in this scenario, as the Driver Station will not have connected to the robot.


def send_packet(self) -> None:
self.panel_port.write(self.packet.to_bytes())

def set_match_state(self) -> None:
self.packet &= 0b00111111

if wpilib.DriverStation.isEnabled():
# During
self.packet |= 0b01 << 6
if self.match_state != 1:
self.send_packet()
self.match_state = 1
elif wpilib.DriverStation.getMatchTime() <= 0:
# Post match
Comment on lines +31 to +32
Copy link
Member

@auscompgeek auscompgeek Mar 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this branch be hit before a match, and between auto and teleop, too?

self.packet |= 0b10 << 6
if self.match_state != 2:
self.send_packet()
self.match_state = 2
else:
# Pre match
self.packet |= 0b00 << 6
if self.match_state != 0:
self.send_packet()
self.match_state = 0

def set_match_id(self) -> None:
match_type = wpilib.DriverStation.getMatchType()
match_id = 0
if (
match_type == wpilib.DriverStation.MatchType.kQualification
or match_type == wpilib.DriverStation.MatchType.kElimination
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will both this robot code and the LED panel code be updated after every playoff match? Sounds tedious.

):
match_number = wpilib.DriverStation.getMatchNumber()
if match_number in self.matches:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks up the match number without using the match type. Is QF4 intended to be considered identical to Q4?

match_id = self.matches[match_number]
# Can't be greater than 15
match_id = max(match_id, 15)

self.packet |= match_id
self.send_packet()

def no_note(self) -> None:
self.packet &= 0b11000000
self.send_packet()

def intake_deployed(self) -> None:
self.packet &= 0b11000000
self.packet |= 0b001
self.send_packet()

def in_range(self) -> None:
self.packet &= 0b11000000
self.packet |= 0b010
self.send_packet()

def not_in_range(self) -> None:
self.packet &= 0b11000000
self.packet |= 0b011
self.send_packet()

def climbing_arm_extending(self) -> None:
self.packet &= 0b11000000
self.packet |= 0b100
self.send_packet()

def climbing_arm_fully_extended(self) -> None:
self.packet &= 0b11000000
self.packet |= 0b101
self.send_packet()

def climbing_arm_retracted(self) -> None:
self.packet &= 0b11000000
self.packet |= 0b110
self.send_packet()

def execute(self) -> None:
self.set_match_state()
Comment on lines +92 to +95
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to send a packet multiple times each control loop iteration. Is this necessary?

7 changes: 4 additions & 3 deletions robot.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from components.intake import IntakeComponent
from components.climber import Climber
from components.led import LightStrip
from components.led_panel import LEDPanel

from controllers.note import NoteManager
from controllers.intake import Intake
Expand All @@ -38,6 +39,7 @@ class MyRobot(magicbot.MagicRobot):
intake_component: IntakeComponent

status_lights: LightStrip
led_panel: LEDPanel

max_speed = magicbot.tunable(4) # m/s
lower_max_speed = magicbot.tunable(2) # m/s
Expand All @@ -55,9 +57,6 @@ def createObjects(self) -> None:
self.field = wpilib.Field2d()
wpilib.SmartDashboard.putData(self.field)

# side: (28*3)*2 + front: (30*3) - 2 (R.I.P)
self.status_lights_strip_length = (28 * 3) * 2 + (30 * 3) - 2

self.vision_port_name = "ardu_cam_port"
self.vision_port_pos = Translation3d(0.005, 0.221, 0.503)
self.vision_port_rot = Rotation3d(
Expand Down Expand Up @@ -188,6 +187,7 @@ def testPeriodic(self) -> None:
self.chassis.update_odometry()

self.status_lights.execute()
self.led_panel.execute()
self.vision_port.execute()
self.vision_starboard.execute()

Expand All @@ -197,6 +197,7 @@ def disabledPeriodic(self) -> None:

self.intake_component.maybe_reindex_deployment_encoder()
self.status_lights.execute()
self.led_panel.execute()
self.vision_port.execute()
self.vision_starboard.execute()

Expand Down
Loading