113 lines
4.2 KiB
Python
113 lines
4.2 KiB
Python
"""
|
|
A simulator for the SR Arduino board.
|
|
|
|
Provides a message parser that simulates the behavior of an Arduino board.
|
|
|
|
Based on the Arduino v2.0 firmware.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
|
|
from sbot_interface.devices.arduino_devices import BasePin, GPIOPinMode, UltrasonicSensor
|
|
|
|
LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
class Arduino:
|
|
"""
|
|
A simulator for the SR Arduino board.
|
|
|
|
:param pins: A list of simulated devices connected to the Arduino board.
|
|
The list is indexed by the pin number and EmptyPin is used for
|
|
unconnected pins.
|
|
:param asset_tag: The asset tag to report for the Arduino board.
|
|
:param software_version: The software version to report for the Arduino board.
|
|
"""
|
|
|
|
def __init__(self, pins: list[BasePin], asset_tag: str, software_version: str = '2'):
|
|
self.pins = pins
|
|
self.asset_tag = asset_tag
|
|
self.software_version = software_version
|
|
|
|
def handle_command(self, command: str) -> str:
|
|
"""
|
|
Process a command string and return the response.
|
|
|
|
Executes the appropriate action on any specified pins automatically.
|
|
|
|
:param command: The command string to process.
|
|
:return: The response to the command.
|
|
"""
|
|
if command[0] == 'a': # analog read
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number == -1:
|
|
return '0'
|
|
return str(self.pins[pin_number].get_analog())
|
|
elif command[0] == 'r': # digital read
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number == -1:
|
|
return 'l'
|
|
return 'h' if self.pins[pin_number].get_digital() else 'l'
|
|
elif command[0] == 'l': # digital write low
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number != -1:
|
|
self.pins[pin_number].set_digital(False)
|
|
return ''
|
|
elif command[0] == 'h': # digital write high
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number != -1:
|
|
self.pins[pin_number].set_digital(True)
|
|
return ''
|
|
elif command[0] == 'i': # set pin mode to input
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number != -1:
|
|
self.pins[pin_number].set_mode(GPIOPinMode.INPUT)
|
|
return ''
|
|
elif command[0] == 'o': # set pin mode to output
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number != -1:
|
|
self.pins[pin_number].set_mode(GPIOPinMode.OUTPUT)
|
|
return ''
|
|
elif command[0] == 'p': # set pin mode to input pullup
|
|
pin_number = self._convert_pin_number(command, 1)
|
|
if pin_number != -1:
|
|
self.pins[pin_number].set_mode(GPIOPinMode.INPUT_PULLUP)
|
|
return ''
|
|
elif command[0] == 'u': # ultrasonic measurement
|
|
pulse_pin = self._convert_pin_number(command, 1)
|
|
echo_pin = self._convert_pin_number(command, 2)
|
|
|
|
if pulse_pin == -1 or echo_pin == -1:
|
|
return '0'
|
|
|
|
ultrasound_sensor = self.pins[echo_pin]
|
|
if isinstance(ultrasound_sensor, UltrasonicSensor):
|
|
return str(ultrasound_sensor.get_distance())
|
|
else:
|
|
return '0'
|
|
elif command[0] == 'v': # software version
|
|
return f"SRduino:{self.software_version}"
|
|
else:
|
|
# A problem here: we do not know how to handle the command!
|
|
# Just ignore this for now.
|
|
return ''
|
|
|
|
def _convert_pin_number(self, command: str, index: int) -> int:
|
|
if len(command) < index + 1:
|
|
LOGGER.warning(f'Incomplete arduino command: {command}')
|
|
return -1
|
|
|
|
pin_str = command[index]
|
|
try:
|
|
pin_number = ord(pin_str) - ord('a')
|
|
except ValueError:
|
|
LOGGER.warning(f'Invalid pin number in command: {command}')
|
|
return -1
|
|
|
|
if 0 < pin_number < len(self.pins):
|
|
return pin_number
|
|
else:
|
|
LOGGER.warning(f'Invalid pin number in command: {command}')
|
|
return -1
|