-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathleds.py
More file actions
276 lines (223 loc) · 9.51 KB
/
leds.py
File metadata and controls
276 lines (223 loc) · 9.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
from enum import Enum
from wpilib import AddressableLED, Timer, DriverStation
from commands2 import Subsystem, Command, InstantCommand
from typing import List, Optional, Dict
# Implementation of the LEDs on the Robot:
# LEDs on left side of robot [0-30] 31 LEDs
# LEDs on right side of robot [31-69] 38 LEDs
# LEDs on Note Launcher [70-96] 26 LEDs (Two LED strings receiving same data)
kRightSideStart = 0
kRightSideCount = 31
kLeftSideStart = 31
kLeftSideCount = 38
kLauncherStart = 69
kLauncherCount = 26
kLEDTotalCount = kRightSideCount + kLeftSideCount + kLauncherCount
# HSV will be indexed as H=0, S=1, V=2
kGreenRGB: List[int] = [0, 255, 0]
kRedRGB: List[int] = [255, 0, 0]
kBlueRGB: List[int] = [0, 0, 255]
kOrangeRGB: List[int] = [255, 175, 0]
kWhiteRGB: List[int] = [255, 255, 255]
hsv_dict: List[int] = {}
hsv_dict["green"] = kGreenRGB
hsv_dict["red"] = kRedRGB
hsv_dict["blue"] = kBlueRGB
hsv_dict["orange"] = kOrangeRGB
hsv_dict["white"] = kWhiteRGB
class LEDState(Enum):
DEFAULT = 0
ALLIANCE_SET = 1
TRACK_APRIL_TAG = 2
TRACK_NOTE = 3
HAS_NOTE = 4
HAS_CORRECT_TAG = 5
SHOOTING = 6
class LEDSubsystem(Subsystem):
def __init__(self) -> None:
# PWM Port 9
self.leds = AddressableLED(9)
# Length is expensive to set, so only set it once, then just update data
self.leds.setLength(kLEDTotalCount)
self._RunwayLightcounter = 0
self._counter = 0
self._set_counter = 0
self._number_of_sets = 5 # seconds to perform color toggle
# Store what the last hue of the first pixel is
self.rainbowFirstPixelHue = 0
# Dictionary to hold all the LED buffer possibilities
self.__chase_buffer_dict = {}
self.__flash_buffer = [AddressableLED.LEDData() for _ in range(kLEDTotalCount)]
self.__rainbow_buffer = [
AddressableLED.LEDData() for _ in range(kLEDTotalCount)
]
# self.rainbow(self.__rainbow_buffer)
# LED Data
self.__build_led_data_buffers()
# Set the data
self.leds.setData(self.__flash_buffer)
self.leds.start()
self._timer = Timer()
self._timer.start()
# Variables to store timing data on led animation
self._last_animate_time = 0
self._last_flash_time = 0
self._last_flash_off = True
# Set the State to default
self._curr_state = self._last_state = LEDState.ALLIANCE_SET
self._alliance: Optional[DriverStation.Alliance] = None
def __build_led_data_buffers(self) -> None:
"""
Build a set of buffers that will be used for the LEDs. Using separate buffers for each
color, animation, etc will minimize the computation needed to switch between schemes
"""
self.__flash_buffer = [AddressableLED.LEDData() for _ in range(kLEDTotalCount)]
self.__chase_buffer_dict["blue"] = [
AddressableLED.LEDData() for _ in range(kLEDTotalCount)
]
self.__initialize_buffer_with_color(
self.__chase_buffer_dict["blue"], kBlueRGB, 10
)
self.__chase_buffer_dict["red"] = [
AddressableLED.LEDData() for _ in range(kLEDTotalCount)
]
self.__initialize_buffer_with_color(
self.__chase_buffer_dict["red"], kRedRGB, 10
)
# self.__flash_buffer["orange"] = [
# AddressableLED.LEDData() for _ in range(kLEDTotalCount)
# ]
# self.__initialize_buffer_with_color(
# self.__flash_buffer["orange"], kOrangeRGB, kLEDTotalCount, split=False
# )
# self.__flash_buffer["white"] = [
# AddressableLED.LEDData() for _ in range(kLEDTotalCount)
# ]
# self.__initialize_buffer_with_color(
# self.__flash_buffer["white"], kWhiteRGB, kLEDTotalCount, split=False
# )
# self.__flash_buffer["green"] = [
# AddressableLED.LEDData() for _ in range(kLEDTotalCount)
# ]
# self.__initialize_buffer_with_color(
# self.__flash_buffer["green"], kGreenRGB, kLEDTotalCount, split=False
# )
def __initialize_buffer_with_color(
self,
buffer: List[AddressableLED.LEDData],
color: List[int],
count: int,
split=True,
) -> None:
if split:
# If we're split, treat the right side, left side, and launcher
# ramp as if they were three different LED strips
for i in range(kLeftSideStart, kLeftSideStart + count):
buffer[i].setRGB(color[0], color[1], color[2])
for i in range(kRightSideStart, kRightSideStart + count):
buffer[i].setRGB(color[0], color[1], color[2])
for i in range(kLauncherStart, kLauncherStart + count):
buffer[i].setRGB(color[0], color[1], color[2])
else:
# If they aren't split, treat all the LEDs as one continuous
# chain of LEDs in series.
for i in range(0, count):
buffer[i].setRGB(color[0], color[1], color[2])
def __animate_chase_buffers(self, delay: float) -> None:
timestamp = self._timer.get()
if timestamp - delay > self._last_animate_time:
# Move the LEDs around all chase buffers
for buffer in self.__chase_buffer_dict.values():
# LEDS are in an array like this:
# [ a b c d e ]
# 0 1 2 3 4 <--- Their index value
# 0 is the first led
# Take the first one in the array and save it
temp = buffer[0]
# Shift all the LEDs to the left by 1 spot
for i in range(1, len(buffer)):
buffer[i - 1] = buffer[i]
# Take the first one we stored, and put it on the back of the list
buffer[len(buffer) - 1] = temp
# Resulting LED buffer now looks like this:
# [ b c d e a ]
# Store the time for the next run
self._last_animate_time = timestamp
def __set_flash_buffers_color(self, delay: float, color: List[int]) -> None:
timestamp = self._timer.get()
if (timestamp - delay) > self._last_flash_time and self._last_flash_off:
# Turn the colors on
for led in self.__flash_buffer:
led.setRGB(color[0], color[1], color[2])
self._last_flash_time = timestamp
self._last_flash_off = False
else:
# turn lights off
for led in self.__flash_buffer:
led.setRGB(0, 0, 0)
self._last_flash_off = True
def periodic(self) -> None:
# Check for our alliance to match color
self.check_alliance()
# Move the leds in their buffers in case they get set
self.__animate_chase_buffers(0.03)
self.rainbow(self.__rainbow_buffer)
# Set the LEDs based on the steate
match self._curr_state:
case LEDState.TRACK_APRIL_TAG:
self.__set_flash_buffers_color(1, kWhiteRGB)
self.leds.setData(self.__flash_buffer)
case LEDState.TRACK_NOTE:
self.__set_flash_buffers_color(1, kOrangeRGB)
self.leds.setData(self.__flash_buffer)
case LEDState.HAS_NOTE:
self.__set_flash_buffers_color(0.2, kOrangeRGB)
self.leds.setData(self.__flash_buffer)
case LEDState.HAS_CORRECT_TAG:
self.__set_flash_buffers_color(0.2, kWhiteRGB)
self.leds.setData(self.__flash_buffer)
case LEDState.SHOOTING:
pass
case LEDState.ALLIANCE_SET: # Default
if self._alliance == DriverStation.Alliance.kBlue:
self.leds.setData(self.__chase_buffer_dict["blue"])
elif self._alliance == DriverStation.Alliance.kRed:
self.leds.setData(self.__chase_buffer_dict["red"])
else:
self.leds.setData(self.__rainbow_buffer)
def rainbow(self, buffer: List[AddressableLED.LEDData]) -> None:
# For every pixel
for i in range(kLEDTotalCount):
# Calculate the hue - hue is easier for rainbows because the color
# shape is a circle so only one value needs to precess
hue = (self.rainbowFirstPixelHue + (i * 180 / kLEDTotalCount)) % 180
# Set the value
buffer[i].setHSV(int(hue), 255, 128)
# Increase by to make the rainbow "move"
self.rainbowFirstPixelHue += 3
# Check bounds
self.rainbowFirstPixelHue %= 180
def check_alliance(self) -> None:
"""
Poll the driverstation for our alliance, set the member variable if we have one.
The call will return None if we aren't connected to driverstation
"""
self._alliance = DriverStation.getAlliance()
def set_state(self, state: LEDState) -> None:
self._last_state = self._curr_state
self._curr_state = state
def unset_state(self) -> None:
self._curr_state = self._last_state
class FlashLEDCommand(Command):
def __init__(self, leds: LEDSubsystem, time: float):
self._led_sub = leds
self._duration = time
self._timer = Timer()
self._timer.start()
def initialize(self):
self._timer.restart()
self._led_sub.set_state(LEDState.HAS_NOTE)
def isFinished(self) -> bool:
return self._timer.hasElapsed(self._duration)
def end(self, interrupted: bool):
self._led_sub.unset_state()