Skip to content

Commit

Permalink
adjust pkone for firmware 1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
jabdoa2 committed Jul 24, 2021
1 parent 3168c39 commit 46b69e1
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 30 deletions.
6 changes: 6 additions & 0 deletions mpf/platforms/base_serial_communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ async def _connect_to_hardware(self, port, baud, xonxoff=False):

await self._identify_connection()

def reset_input_buffer(self):
"""Clear buffer."""
assert self.reader
# pylint: disable-msg=protected-access
self.reader._buffer = bytearray()

async def start_read_loop(self):
"""Start the read loop."""
self.read_task = self.machine.clock.loop.create_task(self._socket_reader())
Expand Down
5 changes: 3 additions & 2 deletions mpf/platforms/pkone/pkone.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ async def get_hw_switch_states(self) -> Dict[str, bool]:

def receive_all_switches(self, msg):
"""Process the all switch states message."""
# TODO: move this to the init part
# The PSA message contains the following information:
# [PSA opcode] + [[board address id] + 0 or 1 for each switch on the board] + E
self.debug_log("Received all switch states (PSA): %s", msg)
Expand All @@ -442,9 +443,9 @@ def receive_switch(self, msg):
"""Process a single switch state change."""
# The PSW message contains the following information:
# [PSW opcode] + [board address id] + switch number + switch state (0 or 1) + E
self.debug_log("Received switch state change (PSW): %s", msg)
switch_number = PKONESwitchNumber(int(msg[0]), int(msg[1:3]))
switch_state = int(msg[-1])
self.debug_log("Received switch %s state change to %s", switch_number, switch_state)
self.machine.switch_controller.process_switch_by_num(state=switch_state,
num=switch_number,
platform=self)
Expand Down Expand Up @@ -586,7 +587,7 @@ def parse_light_number_to_channels(self, number: str, subtype: str):
index = int(number_str)

# Determine if there are 3 or 4 channels depending upon firmware on board
if self.pkone_lightshows[board_address_id].rgbw_firmware:
if self.pkone_lightshows[int(board_address_id)].rgbw_firmware:
# rgbw uses 4 channels per led
return [
{
Expand Down
59 changes: 33 additions & 26 deletions mpf/platforms/pkone/pkone_serial_communicator.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,28 @@ def __init__(self, platform: "PKONEHardwarePlatform", port, baud) -> None:
self.max_messages_in_flight = 10
self.messages_in_flight = 0

self.send_ready = asyncio.Event()
self.send_ready.set()

super().__init__(platform, port, baud)

async def _read_with_timeout(self, timeout):
msg_raw = await asyncio.wait([self.readuntil(b'E')], timeout=timeout)
if not msg_raw[0]:
msg_raw[1].pop().cancel()
try:
msg_raw = await asyncio.wait_for(self.readuntil(b'E'), timeout=timeout)
except asyncio.exceptions.TimeoutError:
return ""
element = msg_raw[0].pop()
return (await element).decode()
return msg_raw.decode()

async def _identify_connection(self):
"""Identify which controller this serial connection is talking to."""
self.writer.transport.serial.dtr = False
await asyncio.sleep(.1)

count = 0
while True:
if (count % 10) == 0:
self.platform.debug_log("Sending 'PCN' command to port '%s'", self.port)

count += 1
self.writer.write('PCNE'.encode('ascii', 'replace'))
msg = await self._read_with_timeout(.5)
self.send('PCN')
msg = await self._read_with_timeout(.1)
if msg.startswith('PCN'):
break

Expand Down Expand Up @@ -122,15 +121,19 @@ async def reset_controller(self):
self.platform.debug_log('Resetting controller.')

# this command returns several responses (one from each board, starting with the Nano controller)
self.writer.write('PRSE'.encode())
self.send('PRS')
msg = ''
while msg != 'PRSE' and not msg.startswith('PXX'):
while msg != 'PRSNE' and not msg.startswith('PXX'):
msg = (await self.readuntil(b'E')).decode()
self.platform.debug_log("Got: {}".format(msg))

if msg.startswith('PXX'):
raise AssertionError('Received an error while resetting the controller: {}'.format(msg))

# wait a bit and then discard everything we got. boards will send us some PSW here which we do not parse
await asyncio.sleep(.1)
self.reset_input_buffer()

async def query_pkone_boards(self):
"""Query the NANO processor to discover which additional boards are connected."""
self.platform.debug_log('Querying PKONE boards...')
Expand All @@ -141,15 +144,26 @@ async def query_pkone_boards(self):
# Lightshow board - PCB01LF10H1RGBW = PCB[board number 0-3]LF[firmware rev]H[hardware rev][firmware_type]
# No board at the address: PCB[board number 0-7]N
for address_id in range(8):
self.writer.write('PCB{}E'.format(address_id).encode('ascii', 'replace'))
msg = await self._read_with_timeout(.5)
if msg == 'PCB{}NE'.format(address_id):
self.send('PCB{}'.format(address_id))
while True:
msg = await self._read_with_timeout(.1)
if not msg or msg.startswith("PCB"):
break
self.platform.log.warning("Ignoring unexpected msg: {}".format(msg))
continue

if not msg:
self.platform.log.debug("No board at address ID {}".format(address_id))
continue

match = re.fullmatch('PCB([0-7])([XLN])F([0-9]+)H([0-9]+)(P[YN])?(RGB|RGBW)?E', msg)
match = re.fullmatch('PCB([0-7])([XLN])F([0-9]+)H([0-9]+)(P?)(Y|N)?(RGB|RGBW)?E', msg)
if not match:
self.platform.log.warning("Received unexpected message from PKONE: {}".format(msg))
continue

if match.group(1) != str(address_id):
raise AssertionError("Invalid ID {} in response: {} for board {}".format(
match.group(1), msg, address_id))

if match.group(2) == "X":
# Extension board
Expand All @@ -172,7 +186,7 @@ async def query_pkone_boards(self):
# Lightshow board
firmware = match.group(3)[:-1] + '.' + match.group(3)[-1]
hardware_rev = match.group(4)
rgbw_firmware = match.group(6) == 'RGBW'
rgbw_firmware = match.group(7) == 'RGBW'

if StrictVersion(LIGHTSHOW_MIN_FW) > StrictVersion(firmware):
raise AssertionError('Firmware version mismatch. MPF requires '
Expand All @@ -198,7 +212,7 @@ async def read_all_switches(self):
"""Read the current state of all switches from the hardware."""
self.platform.debug_log('Reading all switches.')
for address_id in self.platform.pkone_extensions:
self.writer.write('PSA{}E'.format(address_id).encode())
self.send('PSA{}'.format(address_id))
msg = ''
while not msg.startswith('PSA'):
msg = (await self.readuntil(b'E')).decode()
Expand All @@ -220,13 +234,6 @@ def _parse_msg(self, msg):
msg = self.received_msg[:pos]
self.received_msg = self.received_msg[pos + 1:]

self.messages_in_flight -= 1
if self.messages_in_flight <= self.max_messages_in_flight or not self.read_task:
self.send_ready.set()
if self.messages_in_flight < 0:
self.log.warning("Received more messages than were sent! Resetting!")
self.messages_in_flight = 0

if not msg:
continue

Expand All @@ -241,6 +248,6 @@ def send(self, msg):
msg: Bytes of the message you want to send.
"""
if self.debug:
self.log.debug("%s sending: %s", self, msg)
self.log.debug("%s sending: %sE", self, msg)

self.writer.write(msg.encode() + b'E')
4 changes: 2 additions & 2 deletions mpf/tests/test_PKONE.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,12 @@ def setUp(self):
'PCB0': 'PCB0XF11H2PY', # Extension board at ID 0 (firmware 1.1, hardware rev 2, high power on)
'PCB1': 'PCB1XF11H2PN', # Extension board at ID 1 (firmware 1.1, hardware rev 2, high power off)
'PCB2': 'PCB2LF10H1RGB', # Lightshow board at ID 2 (RGB firmware 1.0, hardware rev 1)
'PCB3': 'PCB2LF10H1RGBW', # Lightshow board at ID 3 (RGBW firmware 1.0, hardware rev 1)
'PCB3': 'PCB3LF10H1RGBW', # Lightshow board at ID 3 (RGBW firmware 1.0, hardware rev 1)
'PCB4': 'PCB4N',
'PCB5': 'PCB5N',
'PCB6': 'PCB6N',
'PCB7': 'PCB7N',
'PRS': 'PRS',
'PRS': 'PRSN',
'PWS1000': 'PWS',
'PSA0': 'PSA011000000000000000000000000000000000E',
'PSA1': 'PSA100110000000000000000000000000000000E',
Expand Down

0 comments on commit 46b69e1

Please sign in to comment.