Skip to content
This repository has been archived by the owner on Jan 25, 2024. It is now read-only.

Add minimal board definition for Arduino Uno #293

Merged
merged 2 commits into from
Jun 6, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions j5/boards/arduino/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Arduino Boards."""

from .uno import AnaloguePin, ArduinoUnoBoard

__all__ = [
'ArduinoUnoBoard',
'AnaloguePin',
]
110 changes: 110 additions & 0 deletions j5/boards/arduino/uno.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Classes for the Arduino Uno."""
from enum import IntEnum
from typing import Mapping, Optional, Set, Type, Union, cast

from j5.backends import Backend
from j5.boards import Board
from j5.components import (
LED,
Component,
GPIOPin,
GPIOPinInterface,
GPIOPinMode,
LEDInterface,
)


class AnaloguePin(IntEnum):
"""Analogue Pins numbering."""

A0 = 14
A1 = 15
A2 = 16
A3 = 17
A4 = 18
A5 = 19


PinNumber = Union[int, AnaloguePin]
trickeydan marked this conversation as resolved.
Show resolved Hide resolved


class ArduinoUnoBoard(Board):
"""Arduino Uno Board."""

_led: LED
_digital_pins: Mapping[int, GPIOPin]
_analogue_pins: Mapping[AnaloguePin, GPIOPin]

def __init__(self, serial: str, backend: Backend):
self._serial = serial
self._backend = backend

self._led = LED(0, cast(LEDInterface, self._backend))

# Digital Pins
# Note that pins 0 and 1 are used for serial comms.
self._digital_pins = {
i: GPIOPin(
i,
cast(GPIOPinInterface, self._backend),
initial_mode=GPIOPinMode.DIGITAL_INPUT,
supported_modes={
GPIOPinMode.DIGITAL_INPUT,
GPIOPinMode.DIGITAL_INPUT_PULLUP,
GPIOPinMode.DIGITAL_OUTPUT,
},
)
for i in range(2, 14)
}

self._analogue_pins = {
i: GPIOPin(
i,
cast(GPIOPinInterface, self._backend),
initial_mode=GPIOPinMode.ANALOGUE_INPUT,
supported_modes={
GPIOPinMode.ANALOGUE_INPUT,
GPIOPinMode.DIGITAL_INPUT,
GPIOPinMode.DIGITAL_INPUT_PULLUP,
GPIOPinMode.DIGITAL_OUTPUT,
},
)
for i in AnaloguePin
}

@property
def name(self) -> str:
"""Get a human friendly name for this board."""
return "Arduino Uno"

@property
def serial(self) -> str:
"""Get the serial number."""
return self._serial

@property
def firmware_version(self) -> Optional[str]:
"""Get the firmware version of the board."""
return None

@property
def pins(self) -> Mapping[PinNumber, GPIOPin]:
"""Get the GPIO pins."""
pins: Mapping[PinNumber, GPIOPin] = {
**cast(Mapping[PinNumber, GPIOPin], self._analogue_pins),
**cast(Mapping[PinNumber, GPIOPin], self._digital_pins),

}
return pins

def make_safe(self) -> None:
"""Make this board safe."""
pass

@staticmethod
def supported_components() -> Set[Type[Component]]:
"""List the types of components supported by this board."""
return {
GPIOPin,
LED,
}
1 change: 1 addition & 0 deletions tests/boards/arduino/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Test supported arduino boards."""
124 changes: 124 additions & 0 deletions tests/boards/arduino/test_uno.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""Tests for the Arduino Uno and related classes."""

from typing import TYPE_CHECKING, Optional, Set

from j5.backends import Backend, Environment
from j5.boards.arduino import AnaloguePin, ArduinoUnoBoard
from j5.components import GPIOPin, GPIOPinInterface, GPIOPinMode, LEDInterface

if TYPE_CHECKING:
from j5.boards import Board # noqa


MockEnvironment = Environment("MockEnvironment")


class MockArduinoUnoBackend(
GPIOPinInterface,
LEDInterface,
Backend,
):
"""Mock Backend for testing the Arduino Uno."""

environment = MockEnvironment
board = ArduinoUnoBoard

def set_gpio_pin_mode(self, identifier: int, pin_mode: GPIOPinMode) -> None:
"""Set the GPIO pin mode."""
pass

def get_gpio_pin_mode(self, identifier: int) -> GPIOPinMode:
"""Get the GPIO pin mode."""
return GPIOPinMode.DIGITAL_OUTPUT

def write_gpio_pin_digital_state(self, identifier: int, state: bool) -> None:
"""Set the GPIO pin digital state."""
pass

def get_gpio_pin_digital_state(self, identifier: int) -> bool:
"""Get the GPIO pin digital state."""
return False

def read_gpio_pin_digital_state(self, identifier: int) -> bool:
"""Read the GPIO pin digital state."""
return False

def read_gpio_pin_analogue_value(self, identifier: int) -> float:
"""Read an analogue value from a GPIO pin."""
return 0.0

def write_gpio_pin_dac_value(self, identifier: int, scaled_value: float) -> None:
"""Write a DAC value to the GPIO pin."""
raise NotImplementedError

def write_gpio_pin_pwm_value(self, identifier: int, duty_cycle: float) -> None:
"""Write a PWM value to the GPIO pin."""
pass

def get_led_state(self, identifier: int) -> bool:
"""Get the state of the LED."""
return self.get_gpio_pin_digital_state(13)

def set_led_state(self, identifier: int, state: bool) -> None:
"""Set the state of the LED."""
self.write_gpio_pin_digital_state(13, state)

@classmethod
def discover(cls) -> Set['Board']:
"""Discover boards."""
return set()

@property
def firmware_version(self) -> Optional[str]:
"""Get the firmware version."""
return None


def test_uno_initialisation() -> None:
"""Test that we can initialise an Uno."""
ArduinoUnoBoard("SERIAL0", MockArduinoUnoBackend())


def test_uno_discover() -> None:
"""Test that we can discover Unos."""
assert MockArduinoUnoBackend.discover() == set()


def test_uno_name() -> None:
"""Test the name attribute of the Uno."""
uno = ArduinoUnoBoard("SERIAL0", MockArduinoUnoBackend())

assert uno.name == "Arduino Uno"


def test_uno_serial() -> None:
"""Test the serial attribute of the Uno."""
uno = ArduinoUnoBoard("SERIAL0", MockArduinoUnoBackend())

assert uno.serial == "SERIAL0"


def test_uno_firmware_version() -> None:
"""Test the firmware_version attribute of the Uno."""
uno = ArduinoUnoBoard("SERIAL0", MockArduinoUnoBackend())

assert uno.firmware_version is None


def test_uno_make_safe() -> None:
"""Test the make_safe method of the Uno."""
uno = ArduinoUnoBoard("SERIAL0", MockArduinoUnoBackend())
uno.make_safe()


def test_uno_pins() -> None:
"""Test the pins of the Uno."""
uno = ArduinoUnoBoard("SERIAL0", MockArduinoUnoBackend())

assert len(uno.pins) == 12 + 6

for i in range(2, 14):
assert type(uno.pins[i]) is GPIOPin

for j in AnaloguePin:
assert type(uno.pins[j]) is GPIOPin