Skip to content

Commit d09838c

Browse files
committed
Add demo bulk paint function to controller.
1 parent ebd1b7e commit d09838c

File tree

2 files changed

+74
-48
lines changed

2 files changed

+74
-48
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Set DIY LED patterns (not supported in Home Assistant)
1010
- Set brightness
1111
- Set effect
12+
- Bulk paint entire strip using collections feature
1213
- Automatic discovery of supported devices in Home Assistant
1314

1415
## Not supported

idealled_controller.py

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -114,36 +114,62 @@ def graffiti_paint(led_num, rgb_tuple, mode=2, speed=50, brightness=100):
114114
write_packet(graffiti_packet)
115115

116116
def bulk_paint():
117-
command_packet = bytearray.fromhex("04 44 4F 4F 54 01 00 00 00 00 00 00 00 00 00 00") # I think the 01 is an indication is there is more data to follow. Max payload size per message is 96 pixels
118-
write_packet(command_packet)
119-
if LAMP_COUNT > 96:
120-
print("Too many lamps. Truncating to 96")
121-
count = 96
122-
else:
123-
count = LAMP_COUNT
124-
colour_data = build_rainbow_colour_list(count)
125-
payload = []
126-
payload.append(0) # Needs to be overwritten with the length of the colour data
127-
payload.append(0) # Might be second byte of length? Max length of my lights is 100, so I don't know
128-
for each in colour_data:
129-
r, g, b = each
130-
payload.append(0) # Start of data
131-
payload.append(1) # Number of pixels to be this colour
132-
payload.append(r)
133-
payload.append(g)
134-
payload.append(b)
135-
payload.append(random.randint(0,2)) # Solid = 2, Fade = 1, Flash = 0
136-
payload.append(random.randint(1,100)) # Speed
137-
total_len = len(payload-1)
138-
print(f"Total bulk payload length: {total_len} bytes")
139-
payload[0] = (len(payload) - 1) % 256
140-
payload[1] = (len(payload) - 1) // 256
141-
print(f"Bulk Payload: {' '.join(f'{byte:02X}' for byte in payload)}")
142-
payload = bytearray(payload)
143-
print(f"Bulk Payload: {' '.join(f'{byte:02X}' for byte in payload)}")
144-
write_colour_data(bytearray(payload))
145-
write_packet(bytearray.fromhex("06 44 4F 4F 54 43 50 00 00 00 00 00 00 00 00 00"))
146-
write_packet(bytearray.fromhex("08 44 54 41 52 54 43 59 01 00 00 00 00 00 00 00")) # "I'm finished" message
117+
"""
118+
You can send a bulk paint command to the controller which can set all the pixels to a desired colour
119+
in a single operation, rather than sending a command for each pixel. This is much faster.
120+
However, you are limited by the number of pixels the controller can handle in a single message.
121+
So you have to split them up. A single message can handle 14 write operations. A single write
122+
operation can handle many pixels.
123+
124+
The format is described in more detail in the `att_protocol.md` file in this repo.
125+
126+
This example paints each pixel a different colour in a rainbow pattern. In theory this is the slowest
127+
operation using this method.
128+
"""
129+
130+
command_packet = bytearray.fromhex("04 44 4F 4F 54 01 00 00 00 00 00 00 00 00 00 00") # Byte 6 says how many data packets there will be
131+
pixel_list = build_rainbow_colour_list(LAMP_COUNT)
132+
chunked_pixel_list = [pixel_list[i:i+14] for i in range(0, len(pixel_list), 14)] # Max. 14 commands per packet
133+
colour_data_packets = []
134+
pos = 0
135+
for chunk in chunked_pixel_list:
136+
colour_data = []
137+
colour_data.append(0) # Needs to be overwritten with the length of the colour data
138+
colour_data.append(pos) # Sequence number
139+
pos += 1
140+
for pixel in chunk:
141+
print(f"Chunk: {chunk}")
142+
#print(f"Pixel: {pixel}")
143+
144+
colour_data.append(0) # Start of data
145+
colour_data.append(1) # Number of pixels to be this colour.
146+
# There is an optimisation to be done here. If the next pixel is the same colour
147+
# you can change this number to be the number of pixels that are the same colour.
148+
# That will shorten the message and so the time it takes to send it. In the interests
149+
# of simplicity, I'm not doing that here. Each pixel is assumed to be a different colour.
150+
r, g, b = pixel
151+
colour_data.append(r >> 3) # Shifting right 3 bits to get 5 bit colour and so lower the brightness to save over loading the voltage regulator on the controller
152+
colour_data.append(g >> 3)
153+
colour_data.append(b >> 3)
154+
colour_data.append(random.randint(0,2)) # Solid = 2, Fade = 1, Flash = 0
155+
colour_data.append(random.randint(1,100)) # Speed
156+
colour_data[0] = (len(colour_data) - 1)
157+
colour_data_packets.append(bytearray(colour_data))
158+
159+
# Prepare the DOOT:
160+
doot_packet = bytearray.fromhex("04 44 4F 4F 54 01 00 00 00 00 00 00 00 00 00 00")
161+
doot_packet[5] = len(colour_data_packets)
162+
print(f"DOOT packet: {' '.join(f'{byte:02X}' for byte in doot_packet)}")
163+
for each in colour_data_packets:
164+
print(f"Colour data packet: {' '.join(f'{byte:02X}' for byte in each)}")
165+
dootcp_packet = bytearray.fromhex("06 44 4F 4F 54 43 50 00 00 00 00 00 00 00 00 00")
166+
dtartcy_packet = bytearray.fromhex("08 44 54 41 52 54 43 59 01 00 00 00 00 00 00 00")
167+
168+
write_packet(doot_packet)
169+
for each in colour_data_packets:
170+
write_colour_data(each)
171+
write_packet(dootcp_packet)
172+
write_packet(dtartcy_packet)
147173

148174
def decrypt_aes_ecb(ciphertext):
149175
cipher = AES.new(SECRET_ENCRYPTION_KEY, AES.MODE_ECB)
@@ -316,28 +342,27 @@ def find_devices():
316342
time.sleep(1)
317343
print("Setting colour")
318344
set_colour(255, 0, 0)
319-
# time.sleep(1)
320-
# set_colour(0, 255, 0)
321-
# time.sleep(1)
322-
# set_colour(0, 0, 255)
323-
# time.sleep(1)
324-
# for n in range(2):
325-
# print(f"Setting effect {n}")
326-
# set_effect(n, colour_data=build_colour_data_packet(build_rainbow_colour_list(7)))
327-
# time.sleep(5)
328-
# print("Clearing effect colours")
329-
# set_effect(0, colour_data=build_colour_data_packet([(0, 0, 0)]))
330-
# print("Setting rainbow")
331-
# for i in range(100):
332-
# graffiti_paint(i, r[i], mode=random.randint(0, 2), speed=random.randint(0, 100), brightness=50)
333-
# time.sleep(10)
345+
time.sleep(1)
346+
set_colour(0, 255, 0)
347+
time.sleep(1)
348+
set_colour(0, 0, 255)
349+
time.sleep(1)
350+
for n in range(2):
351+
print(f"Setting effect {n}")
352+
set_effect(n, colour_data=build_colour_data_packet(build_rainbow_colour_list(7)))
353+
time.sleep(5)
354+
print("Clearing effect colours")
355+
set_effect(0, colour_data=build_colour_data_packet([(0, 0, 0)]))
356+
print("Setting rainbow")
357+
for i in range(100):
358+
graffiti_paint(i, r[i], mode=random.randint(0, 2), speed=random.randint(0, 100), brightness=50)
359+
time.sleep(10)
334360
print("Bulk painting")
335361
bulk_paint()
336-
time.sleep(10)
362+
time.sleep(20)
337363
print("Turning off")
338364
switch_on(False)
339-
#print("Pausing for 5 seconds")
340-
#time.sleep(5)
365+
341366

342367
finally:
343368
peripheral.disconnect()

0 commit comments

Comments
 (0)