Skip to content

Commit

Permalink
Merge pull request #205 from mutesplash/patch-5
Browse files Browse the repository at this point in the history
Add Mode 7 IR transmission to ColorDistanceSensor
  • Loading branch information
grega authored Dec 19, 2023
2 parents d60686f + a306d01 commit 2536042
Showing 1 changed file with 334 additions and 0 deletions.
334 changes: 334 additions & 0 deletions buildhat/colordistance.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ def __init__(self, port):
self.mode(6)
self.avg_reads = 4
self._old_color = None
self._ir_channel = 0x0
self._ir_address = 0x0
self._ir_toggle = 0x0

def segment_color(self, r, g, b):
"""Return the color name from HSV
Expand Down Expand Up @@ -197,6 +200,337 @@ def wait_for_new_color(self):
self.callback(None)
return self._old_color

@property
def ir_channel(self):
"""Get the IR channel for message transmission"""
return self._ir_channel

@ir_channel.setter
def ir_channel(self, channel=1):
"""
Set the IR channel for RC Tx
:param channel: 1-4 indicating the selected IR channel on the reciever
"""
check_chan = channel
if check_chan > 4:
check_chan = 4
elif check_chan < 1:
check_chan = 1
# Internally: 0-3
self._ir_channel = int(check_chan) - 1

@property
def ir_address(self):
"""IR Address space of 0x0 for default PoweredUp or 0x1 for extra space"""
return self._ir_address

def toggle_ir_toggle(self):
"""Toggle the IR toggle bit"""
# IYKYK, because the RC documents are not clear
if self._ir_toggle:
self._ir_toggle = 0x0
else:
self._ir_toggle = 0x1
return self._ir_toggle

def send_ir_sop(self, port, mode):
"""
Send an IR message via Power Functions RC Protocol in Single Output PWM mode
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
Port B is blue
Valid values for mode are:
0x0: Float output
0x1: Forward/Clockwise at speed 1
0x2: Forward/Clockwise at speed 2
0x3: Forward/Clockwise at speed 3
0x4: Forward/Clockwise at speed 4
0x5: Forward/Clockwise at speed 5
0x6: Forward/Clockwise at speed 6
0x7: Forward/Clockwise at speed 7
0x8: Brake (then float v1.20)
0x9: Backwards/Counterclockwise at speed 7
0xA: Backwards/Counterclockwise at speed 6
0xB: Backwards/Counterclockwise at speed 5
0xC: Backwards/Counterclockwise at speed 4
0xD: Backwards/Counterclockwise at speed 3
0xE: Backwards/Counterclockwise at speed 2
0xF: Backwards/Counterclockwise at speed 1
:param port: 'A' or 'B'
:param mode: 0-15 indicating the port's mode to set
"""
escape_modeselect = 0x0
escape = escape_modeselect

ir_mode_single_output = 0x4
ir_mode = ir_mode_single_output

so_mode_pwm = 0x0
so_mode = so_mode_pwm

output_port_a = 0x0
output_port_b = 0x1

output_port = None
if port == 'A' or port == 'a':
output_port = output_port_a
elif port == 'B' or port == 'b':
output_port = output_port_b
else:
return False

ir_mode = ir_mode | (so_mode << 1) | output_port

nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
nibble2 = (self._ir_address << 3) | ir_mode

# Mode range checked here
return self._send_ir_nibbles(nibble1, nibble2, mode)

def send_ir_socstid(self, port, mode):
"""
Send an IR message via Power Functions RC Protocol in Single Output Clear/Set/Toggle/Increment/Decrement mode
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
Valid values for mode are:
0x0: Toggle full Clockwise/Forward (Stop to Clockwise, Clockwise to Stop, Counterclockwise to Clockwise)
0x1: Toggle direction
0x2: Increment numerical PWM
0x3: Decrement numerical PWM
0x4: Increment PWM
0x5: Decrement PWM
0x6: Full Clockwise/Forward
0x7: Full Counterclockwise/Backward
0x8: Toggle full (defaults to Forward, first)
0x9: Clear C1 (C1 to High)
0xA: Set C1 (C1 to Low)
0xB: Toggle C1
0xC: Clear C2 (C2 to High)
0xD: Set C2 (C2 to Low)
0xE: Toggle C2
0xF: Toggle full Counterclockwise/Backward (Stop to Clockwise, Counterclockwise to Stop, Clockwise to Counterclockwise)
:param port: 'A' or 'B'
:param mode: 0-15 indicating the port's mode to set
"""
escape_modeselect = 0x0
escape = escape_modeselect

ir_mode_single_output = 0x4
ir_mode = ir_mode_single_output

so_mode_cstid = 0x1
so_mode = so_mode_cstid

output_port_a = 0x0
output_port_b = 0x1

output_port = None
if port == 'A' or port == 'a':
output_port = output_port_a
elif port == 'B' or port == 'b':
output_port = output_port_b
else:
return False

ir_mode = ir_mode | (so_mode << 1) | output_port

nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
nibble2 = (self._ir_address << 3) | ir_mode

# Mode range checked here
return self._send_ir_nibbles(nibble1, nibble2, mode)

def send_ir_combo_pwm(self, port_b_mode, port_a_mode):
"""
Send an IR message via Power Functions RC Protocol in Combo PWM mode
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
Valid values for the modes are:
0x0 Float
0x1 PWM Forward step 1
0x2 PWM Forward step 2
0x3 PWM Forward step 3
0x4 PWM Forward step 4
0x5 PWM Forward step 5
0x6 PWM Forward step 6
0x7 PWM Forward step 7
0x8 Brake (then float v1.20)
0x9 PWM Backward step 7
0xA PWM Backward step 6
0xB PWM Backward step 5
0xC PWM Backward step 4
0xD PWM Backward step 3
0xE PWM Backward step 2
0xF PWM Backward step 1
:param port_b_mode: 0-15 indicating the command to send to port B
:param port_a_mode: 0-15 indicating the command to send to port A
"""
escape_combo_pwm = 0x1
escape = escape_combo_pwm

nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel

# Port modes are range checked here
return self._send_ir_nibbles(nibble1, port_b_mode, port_a_mode)

def send_ir_combo_direct(self, port_b_output, port_a_output):
"""
Send an IR message via Power Functions RC Protocol in Combo Direct mode
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
Valid values for the output variables are:
0x0: Float output
0x1: Clockwise/Forward
0x2: Counterclockwise/Backwards
0x3: Brake then float
:param port_b_output: 0-3 indicating the output to send to port B
:param port_a_output: 0-3 indicating the output to send to port A
"""
escape_modeselect = 0x0
escape = escape_modeselect

ir_mode_combo_direct = 0x1
ir_mode = ir_mode_combo_direct

nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
nibble2 = (self._ir_address << 3) | ir_mode

if port_b_output > 0x3 or port_a_output > 0x3:
return False
if port_b_output < 0x0 or port_a_output < 0x0:
return False

nibble3 = (port_b_output << 2) | port_a_output

return self._send_ir_nibbles(nibble1, nibble2, nibble3)

def send_ir_extended(self, mode):
"""
Send an IR message via Power Functions RC Protocol in Extended mode
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
Valid values for the mode are:
0x0: Brake Port A (timeout)
0x1: Increment Speed on Port A
0x2: Decrement Speed on Port A
0x4: Toggle Forward/Clockwise/Float on Port B
0x6: Toggle Address bit
0x7: Align toggle bit
:param mode: 0-2,4,6-7
"""
escape_modeselect = 0x0
escape = escape_modeselect

ir_mode_extended = 0x0
ir_mode = ir_mode_extended

nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
nibble2 = (self._ir_address << 3) | ir_mode

if mode < 0x0 or mode == 0x3 or mode == 0x5 or mode > 0x7:
return False

return self._send_ir_nibbles(nibble1, nibble2, mode)

def send_ir_single_pin(self, port, pin, mode, timeout):
"""
Send an IR message via Power Functions RC Protocol in Single Pin mode
PF IR RC Protocol documented at https://www.philohome.com/pf/pf.htm
Valid values for the mode are:
0x0: No-op
0x1: Clear
0x2: Set
0x3: Toggle
Note: The unlabeled IR receiver (vs the one labeled V2) has a "firmware bug in Single Pin mode"
https://www.philohome.com/pfrec/pfrec.htm
:param port: 'A' or 'B'
:param pin: 1 or 2
:param mode: 0-3 indicating the pin's mode to set
:param timeout: True or False
"""
escape_mode = 0x0
escape = escape_mode

ir_mode_single_continuous = 0x2
ir_mode_single_timeout = 0x3
ir_mode = None
if timeout:
ir_mode = ir_mode_single_timeout
else:
ir_mode = ir_mode_single_continuous

output_port_a = 0x0
output_port_b = 0x1

output_port = None
if port == 'A' or port == 'a':
output_port = output_port_a
elif port == 'B' or port == 'b':
output_port = output_port_b
else:
return False

if pin != 1 and pin != 2:
return False
pin_value = pin - 1

if mode > 0x3 or mode < 0x0:
return False

nibble1 = (self._ir_toggle << 3) | (escape << 2) | self._ir_channel
nibble2 = (self._ir_address << 3) | ir_mode
nibble3 = (output_port << 3) | (pin_value << 3) | mode

return self._send_ir_nibbles(nibble1, nibble2, nibble3)

def _send_ir_nibbles(self, nibble1, nibble2, nibble3):

# M7 IR Tx SI = N/A
# format count=1 type=1 chars=5 dp=0
# RAW: 00000000 0000FFFF PCT: 00000000 00000064 SI: 00000000 0000FFFF

mode = 7
self.mode(mode)

# The upper bits of data[2] are ignored
if nibble1 > 0xF or nibble2 > 0xF or nibble3 > 0xF:
return False
if nibble1 < 0x0 or nibble2 < 0x0 or nibble3 < 0x0:
return False

byte_two = (nibble2 << 4) | nibble3

data = bytearray(3)
data[0] = (0xc << 4) | mode
data[1] = byte_two
data[2] = nibble1

# print(" ".join('{:04b}'.format(nibble1)))
# print(" ".join('{:04b}'.format(nibble2)))
# print(" ".join('{:04b}'.format(nibble3)))
# print(" ".join('{:08b}'.format(n) for n in data))

self._write1(data)
return True

def on(self):
"""Turn on the sensor and LED"""
self.reverse()

0 comments on commit 2536042

Please sign in to comment.