Skip to content

Commit 2536042

Browse files
authored
Merge pull request #205 from mutesplash/patch-5
Add Mode 7 IR transmission to ColorDistanceSensor
2 parents d60686f + a306d01 commit 2536042

File tree

1 file changed

+334
-0
lines changed

1 file changed

+334
-0
lines changed

buildhat/colordistance.py

+334
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ def __init__(self, port):
2525
self.mode(6)
2626
self.avg_reads = 4
2727
self._old_color = None
28+
self._ir_channel = 0x0
29+
self._ir_address = 0x0
30+
self._ir_toggle = 0x0
2831

2932
def segment_color(self, r, g, b):
3033
"""Return the color name from HSV
@@ -197,6 +200,337 @@ def wait_for_new_color(self):
197200
self.callback(None)
198201
return self._old_color
199202

203+
@property
204+
def ir_channel(self):
205+
"""Get the IR channel for message transmission"""
206+
return self._ir_channel
207+
208+
@ir_channel.setter
209+
def ir_channel(self, channel=1):
210+
"""
211+
Set the IR channel for RC Tx
212+
213+
:param channel: 1-4 indicating the selected IR channel on the reciever
214+
"""
215+
check_chan = channel
216+
if check_chan > 4:
217+
check_chan = 4
218+
elif check_chan < 1:
219+
check_chan = 1
220+
# Internally: 0-3
221+
self._ir_channel = int(check_chan) - 1
222+
223+
@property
224+
def ir_address(self):
225+
"""IR Address space of 0x0 for default PoweredUp or 0x1 for extra space"""
226+
return self._ir_address
227+
228+
def toggle_ir_toggle(self):
229+
"""Toggle the IR toggle bit"""
230+
# IYKYK, because the RC documents are not clear
231+
if self._ir_toggle:
232+
self._ir_toggle = 0x0
233+
else:
234+
self._ir_toggle = 0x1
235+
return self._ir_toggle
236+
237+
def send_ir_sop(self, port, mode):
238+
"""
239+
Send an IR message via Power Functions RC Protocol in Single Output PWM mode
240+
241+
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
242+
243+
Port B is blue
244+
245+
Valid values for mode are:
246+
0x0: Float output
247+
0x1: Forward/Clockwise at speed 1
248+
0x2: Forward/Clockwise at speed 2
249+
0x3: Forward/Clockwise at speed 3
250+
0x4: Forward/Clockwise at speed 4
251+
0x5: Forward/Clockwise at speed 5
252+
0x6: Forward/Clockwise at speed 6
253+
0x7: Forward/Clockwise at speed 7
254+
0x8: Brake (then float v1.20)
255+
0x9: Backwards/Counterclockwise at speed 7
256+
0xA: Backwards/Counterclockwise at speed 6
257+
0xB: Backwards/Counterclockwise at speed 5
258+
0xC: Backwards/Counterclockwise at speed 4
259+
0xD: Backwards/Counterclockwise at speed 3
260+
0xE: Backwards/Counterclockwise at speed 2
261+
0xF: Backwards/Counterclockwise at speed 1
262+
263+
:param port: 'A' or 'B'
264+
:param mode: 0-15 indicating the port's mode to set
265+
"""
266+
escape_modeselect = 0x0
267+
escape = escape_modeselect
268+
269+
ir_mode_single_output = 0x4
270+
ir_mode = ir_mode_single_output
271+
272+
so_mode_pwm = 0x0
273+
so_mode = so_mode_pwm
274+
275+
output_port_a = 0x0
276+
output_port_b = 0x1
277+
278+
output_port = None
279+
if port == 'A' or port == 'a':
280+
output_port = output_port_a
281+
elif port == 'B' or port == 'b':
282+
output_port = output_port_b
283+
else:
284+
return False
285+
286+
ir_mode = ir_mode | (so_mode << 1) | output_port
287+
288+
nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
289+
nibble2 = (self._ir_address << 3) | ir_mode
290+
291+
# Mode range checked here
292+
return self._send_ir_nibbles(nibble1, nibble2, mode)
293+
294+
def send_ir_socstid(self, port, mode):
295+
"""
296+
Send an IR message via Power Functions RC Protocol in Single Output Clear/Set/Toggle/Increment/Decrement mode
297+
298+
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
299+
300+
Valid values for mode are:
301+
0x0: Toggle full Clockwise/Forward (Stop to Clockwise, Clockwise to Stop, Counterclockwise to Clockwise)
302+
0x1: Toggle direction
303+
0x2: Increment numerical PWM
304+
0x3: Decrement numerical PWM
305+
0x4: Increment PWM
306+
0x5: Decrement PWM
307+
0x6: Full Clockwise/Forward
308+
0x7: Full Counterclockwise/Backward
309+
0x8: Toggle full (defaults to Forward, first)
310+
0x9: Clear C1 (C1 to High)
311+
0xA: Set C1 (C1 to Low)
312+
0xB: Toggle C1
313+
0xC: Clear C2 (C2 to High)
314+
0xD: Set C2 (C2 to Low)
315+
0xE: Toggle C2
316+
0xF: Toggle full Counterclockwise/Backward (Stop to Clockwise, Counterclockwise to Stop, Clockwise to Counterclockwise)
317+
318+
:param port: 'A' or 'B'
319+
:param mode: 0-15 indicating the port's mode to set
320+
"""
321+
escape_modeselect = 0x0
322+
escape = escape_modeselect
323+
324+
ir_mode_single_output = 0x4
325+
ir_mode = ir_mode_single_output
326+
327+
so_mode_cstid = 0x1
328+
so_mode = so_mode_cstid
329+
330+
output_port_a = 0x0
331+
output_port_b = 0x1
332+
333+
output_port = None
334+
if port == 'A' or port == 'a':
335+
output_port = output_port_a
336+
elif port == 'B' or port == 'b':
337+
output_port = output_port_b
338+
else:
339+
return False
340+
341+
ir_mode = ir_mode | (so_mode << 1) | output_port
342+
343+
nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
344+
nibble2 = (self._ir_address << 3) | ir_mode
345+
346+
# Mode range checked here
347+
return self._send_ir_nibbles(nibble1, nibble2, mode)
348+
349+
def send_ir_combo_pwm(self, port_b_mode, port_a_mode):
350+
"""
351+
Send an IR message via Power Functions RC Protocol in Combo PWM mode
352+
353+
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
354+
355+
Valid values for the modes are:
356+
0x0 Float
357+
0x1 PWM Forward step 1
358+
0x2 PWM Forward step 2
359+
0x3 PWM Forward step 3
360+
0x4 PWM Forward step 4
361+
0x5 PWM Forward step 5
362+
0x6 PWM Forward step 6
363+
0x7 PWM Forward step 7
364+
0x8 Brake (then float v1.20)
365+
0x9 PWM Backward step 7
366+
0xA PWM Backward step 6
367+
0xB PWM Backward step 5
368+
0xC PWM Backward step 4
369+
0xD PWM Backward step 3
370+
0xE PWM Backward step 2
371+
0xF PWM Backward step 1
372+
373+
:param port_b_mode: 0-15 indicating the command to send to port B
374+
:param port_a_mode: 0-15 indicating the command to send to port A
375+
"""
376+
escape_combo_pwm = 0x1
377+
escape = escape_combo_pwm
378+
379+
nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
380+
381+
# Port modes are range checked here
382+
return self._send_ir_nibbles(nibble1, port_b_mode, port_a_mode)
383+
384+
def send_ir_combo_direct(self, port_b_output, port_a_output):
385+
"""
386+
Send an IR message via Power Functions RC Protocol in Combo Direct mode
387+
388+
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
389+
390+
Valid values for the output variables are:
391+
0x0: Float output
392+
0x1: Clockwise/Forward
393+
0x2: Counterclockwise/Backwards
394+
0x3: Brake then float
395+
396+
:param port_b_output: 0-3 indicating the output to send to port B
397+
:param port_a_output: 0-3 indicating the output to send to port A
398+
"""
399+
escape_modeselect = 0x0
400+
escape = escape_modeselect
401+
402+
ir_mode_combo_direct = 0x1
403+
ir_mode = ir_mode_combo_direct
404+
405+
nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
406+
nibble2 = (self._ir_address << 3) | ir_mode
407+
408+
if port_b_output > 0x3 or port_a_output > 0x3:
409+
return False
410+
if port_b_output < 0x0 or port_a_output < 0x0:
411+
return False
412+
413+
nibble3 = (port_b_output << 2) | port_a_output
414+
415+
return self._send_ir_nibbles(nibble1, nibble2, nibble3)
416+
417+
def send_ir_extended(self, mode):
418+
"""
419+
Send an IR message via Power Functions RC Protocol in Extended mode
420+
421+
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
422+
423+
Valid values for the mode are:
424+
0x0: Brake Port A (timeout)
425+
0x1: Increment Speed on Port A
426+
0x2: Decrement Speed on Port A
427+
428+
0x4: Toggle Forward/Clockwise/Float on Port B
429+
430+
0x6: Toggle Address bit
431+
0x7: Align toggle bit
432+
433+
:param mode: 0-2,4,6-7
434+
"""
435+
escape_modeselect = 0x0
436+
escape = escape_modeselect
437+
438+
ir_mode_extended = 0x0
439+
ir_mode = ir_mode_extended
440+
441+
nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
442+
nibble2 = (self._ir_address << 3) | ir_mode
443+
444+
if mode < 0x0 or mode == 0x3 or mode == 0x5 or mode > 0x7:
445+
return False
446+
447+
return self._send_ir_nibbles(nibble1, nibble2, mode)
448+
449+
def send_ir_single_pin(self, port, pin, mode, timeout):
450+
"""
451+
Send an IR message via Power Functions RC Protocol in Single Pin mode
452+
453+
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
454+
455+
Valid values for the mode are:
456+
0x0: No-op
457+
0x1: Clear
458+
0x2: Set
459+
0x3: Toggle
460+
461+
Note: The unlabeled IR receiver (vs the one labeled V2) has a "firmware bug in Single Pin mode"
462+
https://www.philohome.com/pfrec/pfrec.htm
463+
464+
:param port: 'A' or 'B'
465+
:param pin: 1 or 2
466+
:param mode: 0-3 indicating the pin's mode to set
467+
:param timeout: True or False
468+
"""
469+
escape_mode = 0x0
470+
escape = escape_mode
471+
472+
ir_mode_single_continuous = 0x2
473+
ir_mode_single_timeout = 0x3
474+
ir_mode = None
475+
if timeout:
476+
ir_mode = ir_mode_single_timeout
477+
else:
478+
ir_mode = ir_mode_single_continuous
479+
480+
output_port_a = 0x0
481+
output_port_b = 0x1
482+
483+
output_port = None
484+
if port == 'A' or port == 'a':
485+
output_port = output_port_a
486+
elif port == 'B' or port == 'b':
487+
output_port = output_port_b
488+
else:
489+
return False
490+
491+
if pin != 1 and pin != 2:
492+
return False
493+
pin_value = pin - 1
494+
495+
if mode > 0x3 or mode < 0x0:
496+
return False
497+
498+
nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
499+
nibble2 = (self._ir_address << 3) | ir_mode
500+
nibble3 = (output_port << 3) | (pin_value << 3) | mode
501+
502+
return self._send_ir_nibbles(nibble1, nibble2, nibble3)
503+
504+
def _send_ir_nibbles(self, nibble1, nibble2, nibble3):
505+
506+
# M7 IR Tx SI = N/A
507+
# format count=1 type=1 chars=5 dp=0
508+
# RAW: 00000000 0000FFFF PCT: 00000000 00000064 SI: 00000000 0000FFFF
509+
510+
mode = 7
511+
self.mode(mode)
512+
513+
# The upper bits of data[2] are ignored
514+
if nibble1 > 0xF or nibble2 > 0xF or nibble3 > 0xF:
515+
return False
516+
if nibble1 < 0x0 or nibble2 < 0x0 or nibble3 < 0x0:
517+
return False
518+
519+
byte_two = (nibble2 << 4) | nibble3
520+
521+
data = bytearray(3)
522+
data[0] = (0xc << 4) | mode
523+
data[1] = byte_two
524+
data[2] = nibble1
525+
526+
# print(" ".join('{:04b}'.format(nibble1)))
527+
# print(" ".join('{:04b}'.format(nibble2)))
528+
# print(" ".join('{:04b}'.format(nibble3)))
529+
# print(" ".join('{:08b}'.format(n) for n in data))
530+
531+
self._write1(data)
532+
return True
533+
200534
def on(self):
201535
"""Turn on the sensor and LED"""
202536
self.reverse()

0 commit comments

Comments
 (0)