Skip to content

Commit

Permalink
Initial FAST Nano Controller support added back in
Browse files Browse the repository at this point in the history
  • Loading branch information
toomanybrians committed Sep 26, 2023
1 parent 1c3f150 commit 1b5302f
Show file tree
Hide file tree
Showing 22 changed files with 466 additions and 193 deletions.
24 changes: 0 additions & 24 deletions TODO.md

This file was deleted.

2 changes: 1 addition & 1 deletion mpf/config_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,7 @@ fast_exp_board:
breakouts: list|subconfig(fast_breakout)|None
led_ports: list|subconfig(fast_led_port)|None
led_fade_time: single|ms|0
led_hz: single|int|30
led_hz: single|float|30
fast_breakout:
port: single|enum(1,2,3)|
model: single|str|
Expand Down
36 changes: 36 additions & 0 deletions mpf/platforms/fast/TODO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# FAST Platform Rewrite TODO list for MPF 0.57

* Add delayed pulse
* Do not block WD

* LED next/prev

* Digital driver, confirm? Initial config?
* Remove hw led fade time from nano?
* implement soft reset for EXP


* Delayed pulse. Add platform_setting for this.
* Test single wound with EOS? Is this a thing?
* Confirm recycle_ms when used with autofire rules
* Find all the TODOs
* Figure out the config file errors / error logs to FAST site / etc.
* Single LED update task

## Tests Needed

* Test auto port detection
* Test AC overwrites

## Platform interfaces yet to update / add

* Nano
* Retro
* Audio
* Move FAST devices to mixin classes
* DMD
* Segment

## MPF In general

* poll for events instead of direct callbacks?
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/aud.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ class FastAudCommunicator(FastSerialCommunicator):

"""Handles the serial communication to the FAST platform."""

ignored_messages = []
IGNORED_MESSAGES = []
5 changes: 3 additions & 2 deletions mpf/platforms/fast/communicators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class FastSerialCommunicator(LogMixin):

"""Handles the serial communication to the FAST platform."""

ignored_messages = []
IGNORED_MESSAGES = []

__slots__ = ["platform", "remote_processor", "config", "writer", "reader", "read_task", "received_msg", "log",
"machine", "fast_debug", "port_debug", "remote_firmware", "send_queue", "write_task",
Expand All @@ -27,6 +27,7 @@ def __init__(self, platform, processor, config):
"""Initialize FastSerialCommunicator."""
self.platform = platform
self.remote_processor = processor.upper()
self.remote_model = str()
self.config = config
self.writer = None
self.reader = None
Expand Down Expand Up @@ -296,7 +297,7 @@ def parse_incoming_raw_bytes(self, msg):

def dispatch_incoming_msg(self, msg):

if msg in self.ignored_messages:
if msg in self.IGNORED_MESSAGES:
return

msg_header = msg[:3]
Expand Down
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/dmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class FastDmdCommunicator(FastSerialCommunicator):

"""Handles the serial communication to a DMD in the FAST platform."""

ignored_messages = []
IGNORED_MESSAGES = []

def _send(self, msg): # todo is this meth even used?
self.send_bytes(b'BM:' + msg)
Expand Down
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/emu.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ class FastEmuCommunicator(FastSerialCommunicator):

"""Handles the serial communication to the EMU processor on a FAST Retro Controller."""

ignored_messages = []
IGNORED_MESSAGES = []
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/exp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class FastExpCommunicator(FastSerialCommunicator):

"""Handles the serial communication for the FAST EXP bus."""

ignored_messages = ['XX:F']
IGNORED_MESSAGES = ['XX:F']

__slots__ = ["exp_boards_by_address", "active_board", "_led_task"]

Expand Down
56 changes: 18 additions & 38 deletions mpf/platforms/fast/communicators/net_nano.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,29 @@
class FastNetNanoCommunicator(FastNetNeuronCommunicator):

MIN_FW = version.parse('1.05')
IO_MIN_FW = version.parse('0.87')
ignored_messages = ['WD:P',
'TN:P']
IO_MIN_FW = version.parse('1.05')
IGNORED_MESSAGES = ['WD:P', 'TN:P']
MAX_IO_BOARDS = 9
MAX_SWITCHES = 108
MAX_DRIVERS = 48
TRIGGER_CMD = 'TN'
DRIVER_CMD = 'DN'
SWITCH_CMD = 'SN'

def __init__(self, platform, processor, config):

super().__init__(platform, processor, config)

self.watchdog_cmd = f"WD:{config['watchdog']:02X}"
self._watchdog_task = None

self.trigger_cmd = 'TN'
self.driver_cmd = 'DN'
self.switch_cmd = 'SN'

self.message_processors['SA:'] = self._process_sa
self.message_processors['!B:'] = self._process_boot_message
self.message_processors['\x11\x11!'] = self._process_reboot_done
self.message_processors['NN:'] = self._process_nn
self.message_processors['/N:'] = self._process_switch_open
self.message_processors['-N:'] = self._process_switch_closed
# TODO add 'SN:', 'DN:' etc to look for DN:F, but then what do we do with it?

async def init(self):
await self.send_and_wait_for_response('ID:', 'ID:') # Verify we're connected to a Neuron
self.send_and_forget('WD:1') # Force expire the watchdog since who knows what state the board is in?
await self.query_io_boards()
await self.send_and_wait_for_response('SA:', 'ID:') # Get initial states so switches can be created
async def configure_hardware(self):
pass # Not used on a Nano

def _process_sa(self, msg):
hw_states = {}
_, local_states = msg.split(',')
raise NotImplementedError("TODO: Implement this for Net Nano")
# I think we need to swap above? look at the command..

for offset, byte in enumerate(bytearray.fromhex(local_states)):
for i in range(8):

num = Util.int_to_hex_string((offset * 8) + i)

if byte & (2**i):
hw_states[num] = 1
else:
hw_states[num] = 0

self.platform.hw_switch_data = hw_states
# Nano has slightly different variation of this value, get it into a format the base can process
_, _, _, raw_switch_data = msg.split(',')
super()._process_sa(f'00,{raw_switch_data}')

def _process_boot_message(self, msg):
if msg == '02':
self._process_reboot_done()
# TODO what else? Mark all configs as dirty? Log and warn if this was unexpected?
30 changes: 15 additions & 15 deletions mpf/platforms/fast/communicators/net_neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ class FastNetNeuronCommunicator(FastSerialCommunicator):
MAX_IO_BOARDS = 9
MAX_SWITCHES = 104
MAX_DRIVERS = 48
ignored_messages = ['WD:P',
'TL:P']
IGNORED_MESSAGES = ['WD:P', 'TL:P']
TRIGGER_CMD = 'TL'
DRIVER_CMD = 'DL'
SWITCH_CMD = 'SL'

__slots__ = ["watchdog_cmd", "_watchdog_task", "io_loop", "switches", "drivers", "trigger_cmd", "driver_cmd", "switch_cmd"]
__slots__ = ["watchdog_cmd", "_watchdog_task", "io_loop", "switches", "drivers"]

def __init__(self, platform, processor, config):

super().__init__(platform, processor, config)

self.watchdog_cmd = f"WD:{config['watchdog']:02X}"
Expand All @@ -35,19 +36,15 @@ def __init__(self, platform, processor, config):
self.switches = list()
self.drivers = list()

self.trigger_cmd = 'TL'
self.driver_cmd = 'DL'
self.switch_cmd = 'SL'

self.message_processors['SA:'] = self._process_sa
self.message_processors['CH:'] = self._process_ch
self.message_processors['!B:'] = self._process_boot_message
self.message_processors['\x11\x11!'] = self._process_reboot_done
self.message_processors['NN:'] = self._process_nn
self.message_processors['DL:'] = self.process_driver_config_msg
self.message_processors['SL:'] = self.process_switch_config_msg
self.message_processors['/L:'] = self._process_switch_open
self.message_processors['-L:'] = self._process_switch_closed
self.message_processors[f'{self.DRIVER_CMD}:'] = self.process_driver_config_msg
self.message_processors[f'{self.SWITCH_CMD}:'] = self.process_switch_config_msg
self.message_processors[f'/{self.SWITCH_CMD[-1]}:'] = self._process_switch_open
self.message_processors[f'-{self.SWITCH_CMD[-1]}:'] = self._process_switch_closed

for board, config in self.config['io_loop'].items():
config['index'] = int(config['order'])-1
Expand All @@ -62,10 +59,13 @@ async def init(self):
self.create_switches()
self.create_drivers()
await self.send_and_wait_for_response_processed('ID:', 'ID:', max_retries=-1) # Loop here until we get a response
await self.send_and_wait_for_response_processed('CH:2000,FF', 'CH:') # Configure hardware for Neuron with active switch reporting
await self.configure_hardware()
self.send_and_forget('WD:1') # Force expire the watchdog since who knows what state the board is in?
await self.query_io_boards()

async def configure_hardware(self):
await self.send_and_wait_for_response_processed('CH:2000,FF', 'CH:') # Configure hardware for Neuron with active switch reporting

def create_switches(self):
# Neuron tracks all switches regardless of how many are connected
for i in range(self.MAX_SWITCHES):
Expand Down Expand Up @@ -107,7 +107,7 @@ async def reset_switches(self):
"""

for switch in self.switches: # all physical switches, not just ones defined in the config
await self.send_and_wait_for_response_processed(f'{self.switch_cmd}:{switch.hw_number}', f'{self.switch_cmd}:{switch.hw_number}')
await self.send_and_wait_for_response_processed(f'{self.SWITCH_CMD}:{switch.hw_number}', f'{self.SWITCH_CMD}:{switch.hw_number}')

self.platform.switches_initialized = True

Expand All @@ -121,7 +121,7 @@ async def reset_drivers(self):
# self.drivers contains a list of all drivers, not just ones defined in the config

for driver in self.drivers:
await self.send_and_wait_for_response_processed(f'DL:{Util.int_to_hex_string(driver.number)}', self.driver_cmd)
await self.send_and_wait_for_response_processed(f'{self.DRIVER_CMD}:{Util.int_to_hex_string(driver.number)}', self.DRIVER_CMD)

self.platform.drivers_initialized = True

Expand Down
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/net_retro.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class FastNetRetroCommunicator(FastSerialCommunicator):

ignored_messages = []
IGNORED_MESSAGES = []

async def init(self):
await super().init()
Expand Down
16 changes: 11 additions & 5 deletions mpf/platforms/fast/communicators/rgb.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@ class FastRgbCommunicator(FastSerialCommunicator):
"""Handles the serial communication for legacy FAST RGB processors including
the Nano Controller and FP-EXP-0800 LED controller."""

ignored_messages = ['RA:P',
'RF:',
'RS:P']
IGNORED_MESSAGES = ['RX:P']

def __init__(self, platform, processor, config):
super().__init__(platform, processor, config)

self._led_task = None
self.message_processors['!B:'] = self._process_boot_msg

async def init(self):
await self.send_and_wait_for_response_processed('ID:', 'ID:', max_retries=-1) # Loop here until we get a response

def _process_boot_msg(self, msg):
"""Process bootloader message."""
self.debug_log(f"Got Bootloader message: !B:{msg}")
Expand Down Expand Up @@ -55,11 +56,16 @@ def start_tasks(self):
self._led_task = self.machine.clock.schedule_interval(
self.update_leds, 1 / self.config['led_hz'])

async def soft_reset(self, **kwargs):
"""Reset the NET processor."""
del kwargs
await self.send_and_wait_for_response('RA:000000', 'RX:P')

def reset(self):
"""Reset the RGB processor."""
self.send_and_forget('RF:0')
# self.send_and_forget('RF:0') # TODO confirm if the RGB supports RF. I think not?
self.send_and_forget('RA:000000')
self.send_and_forget(f"RF:{Util.int_to_hex_string(self.config['led_fade_time'])}")
# self.send_and_forget(f"RF:{Util.int_to_hex_string(self.config['led_fade_time'])}")

def stopping(self):
if self._led_task:
Expand Down
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/seg.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class FastSegCommunicator(FastSerialCommunicator):

"""Handles the serial communication to the FAST platform."""

ignored_messages = []
IGNORED_MESSAGES = []

async def init(self):
await super().init()
Expand Down
Loading

0 comments on commit 1b5302f

Please sign in to comment.