ahio  1.0.0
I/O Communication Library
arduino.py
Go to the documentation of this file.
1 # -*- coding: utf-8; -*-
2 #
3 # Copyright (c) 2016 Álan Crístoffer
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a copy
6 # of this software and associated documentation files (the "Software"), to deal
7 # in the Software without restriction, including without limitation the rights
8 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 # copies of the Software, and to permit persons to whom the Software is
10 # furnished to do so, subject to the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included in
13 # all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 # THE SOFTWARE.
22 
24 
25 import serial
26 import time
27 from enum import Enum
28 
29 
31  NAME = 'Arduino'
32  AVAILABLE = True
33 
34 
36  _serial = None
37 
38  Pins = Enum(
39  'Pins',
40  'D0 D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 D13 A1 A2 A3 A4 A5')
41 
42  AnalogReferences = Enum('AnalogReferences', 'Default Internal External')
43 
44  def __enter__(self):
45  return self
46 
47  def __exit__(self, exc_type, exc_value, traceback):
48  pass
49 
50 
55  def setup(self, port):
56  # timeout is used by all I/O operations
57  self._serial = serial.Serial(port, 115200, timeout=2)
58  time.sleep(2) # time to Arduino reset
59 
60  if not self._serial.is_open:
61  raise RuntimeError('Could not connect to Arduino')
62 
63  self._serial.write(b'\x01')
64 
65  if self._serial.read() != b'\x06':
66  raise RuntimeError('Could not connect to Arduino')
67 
68  ps = [p for p in self.available_pins() if p['digital']['output']]
69  for pin in ps:
70  self._set_pin_direction(pin['id'], ahio.Direction.Output)
71 
72  def __clamp(self, value, min, max):
73  return sorted((min, value, max))[1]
74 
75  def __create_pin_info(self, pid, pwm=False):
76  is_analog = pid.name.startswith('A')
77  obj = {
78  'id': pid,
79  'name': None,
80  'analog': {
81  'input': is_analog,
82  'output': False,
83  'read_range': (0, 1023) if is_analog else None,
84  'write_range': None
85  },
86  'digital': {
87  'input': not is_analog,
88  'output': not is_analog,
89  'pwm': not is_analog and pwm
90  }
91  }
92  if is_analog:
93  obj['name'] = 'Analog %s' % (pid.value - 14)
94  else:
95  obj['name'] = 'Digital %s' % (pid.value - 1)
96  return obj
97 
98  def available_pins(self):
99  pins = [p for p in Driver.Pins]
100  pwms = [self.__create_pin_info(pin, True)
101  for pin in pins
102  if (pin.value - 1) in [3, 5, 6, 9, 10, 11]]
103  pins = [self.__create_pin_info(pin)
104  for pin in pins
105  if (pin.value - 1) not in [3, 5, 6, 9, 10, 11]]
106  return sorted(pwms + pins, key=lambda pin: pin['id'].value)
107 
108  def _set_pin_direction(self, pin, direction):
109  if pin.name.startswith('A') and direction == ahio.Direction.Output:
110  raise RuntimeError('Analog pins can only be used as Input')
111  if direction == ahio.Direction.Input:
112  self._serial.write(
113  b'\x02\xC3' + bytes({pin.value - 1}) + bytes({1}))
114  else:
115  self._serial.write(
116  b'\x02\xC3' + bytes({pin.value - 1}) + bytes({0}))
117  self._serial.write(
118  b'\x02\xC7' + bytes({pin.value - 1}) + bytes({0}))
119 
120  def _pin_direction(self, pin):
121  self._serial.write(b'\x02\xC4' + bytes({pin.value - 1}))
122  direction = self._serial.read()
123  if direction == b'\x01':
124  return ahio.Direction.Input
125  elif direction == b'\x00':
126  return ahio.Direction.Output
127  else:
128  return None
129 
130  def _set_pin_type(self, pin, ptype):
131  is_analog = pin.name.startswith('A')
132  if is_analog and ptype == ahio.PortType.Digital:
133  raise RuntimeError('Analog pin can not be set as digital')
134  if not is_analog and ptype == ahio.PortType.Analog:
135  raise RuntimeError('Digtal pin can not be set as analog')
136 
137  def _pin_type(self, pin):
138  pt = ahio.PortType
139  return pt.Analog if pin.name.startswith('A') else pt.Digital
140 
141  def _write(self, pin, value, pwm):
142  if self._pin_direction(pin) == ahio.Direction.Input:
143  return
144  if pin.name.startswith('D'):
145  if pwm:
146  if (pin.value - 1) not in [3, 5, 6, 9, 10, 11]:
147  raise RuntimeError('Pin does not support PWM output')
148  if type(value) is int or type(value) is float:
149  value = int(255 * self.__clamp(float(value), 0.0, 1.0))
150  command = b'\x02\xC8' + bytes({pin.value - 1})
151  arg = bytes({value})
152  self._serial.write(command + arg)
153  else:
154  raise TypeError('value not a float or int between 0 and 1')
155  else:
156  if type(value) is ahio.LogicValue:
157  value = 1 if value == ahio.LogicValue.High else 0
158  command = b'\x02\xC7' + bytes({pin.value - 1})
159  arg = bytes({value})
160  self._serial.write(command + arg)
161  else:
162  raise TypeError('Value should be of type ahio.LogicValue')
163  else:
164  raise RuntimeError('Can not write to analog pin')
165 
166  def _read(self, pin):
167  if pin.name.startswith('D'):
168  self._serial.write(b'\x02\xC5' + bytes({pin.value - 1}))
169  value = self._serial.read()
170  lv = ahio.LogicValue
171  return lv.High if value == b'\x01' else lv.Low
172  else:
173  self._serial.write(b'\x02\xC6' + bytes({pin.value - 14}))
174  value_high = self._serial.read()
175  value_low = self._serial.read()
176  return (value_high[0] << 8) | value_low[0]
177 
178  def analog_references(self):
179  return [r for r in Driver.AnalogReferences]
180 
181  def _set_analog_reference(self, reference, pin):
182  if pin is not None:
183  raise RuntimeError('Per pin analog reference is not supported')
184  self._serial.write(b'\x02\xC2' + bytes({reference.value - 1}))
185 
186  def _analog_reference(self, pin):
187  self._serial.write(b'\x02\xC1')
188  reference = self._serial.read()[0]
189  return [Driver.AnalogReferences.Default,
190  Driver.AnalogReferences.Internal,
191  Driver.AnalogReferences.External][reference]
192 
193  def _set_pwm_frequency(self, frequency, pin):
194  raise RuntimeError(
195  'Setting PWM frequency is not supported by hardware')
def __exit__(self, exc_type, exc_value, traceback)
Definition: arduino.py:47
def _pin_direction(self, pin)
Definition: arduino.py:120
def available_pins(self)
Returns available pins.
def setup(self, port)
Connects to an Arduino UNO on serial port port.
Definition: arduino.py:55
def __create_pin_info(self, pid, pwm=False)
Definition: arduino.py:75
Abstract class containing information about the driver.
def write(self, pin, value, pwm=False)
Sets the output to the given value.
def __clamp(self, value, min, max)
Definition: arduino.py:72
Contains abstract classes that should be implemented by drivers.
def read(self, pin)
Reads value from pin pin.
def _set_pin_direction(self, pin, direction)
Definition: arduino.py:108