First commit

This commit is contained in:
2025-11-07 11:39:23 +00:00
commit 4fb3471833
281 changed files with 6610 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
"""
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