mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Sorry! Dave's messing around with the pin implementations again. Hopefully the last time. The pin_factory is now really a factory object which can be asked to produce individual pins or pin-based interfaces like SPI (which can be supported properly via pigpio).
		
			
				
	
	
		
			336 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			336 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from __future__ import (
 | |
|     unicode_literals,
 | |
|     absolute_import,
 | |
|     print_function,
 | |
|     division,
 | |
|     )
 | |
| str = type('')
 | |
| 
 | |
| 
 | |
| import sys
 | |
| import pytest
 | |
| from collections import namedtuple
 | |
| try:
 | |
|     from math import isclose
 | |
| except ImportError:
 | |
|     from gpiozero.compat import isclose
 | |
| 
 | |
| from gpiozero.pins.mock import MockSPIDevice, MockPin
 | |
| from gpiozero import *
 | |
| 
 | |
| 
 | |
| def teardown_function(function):
 | |
|     Device._pin_factory.reset()
 | |
| 
 | |
| def clamp(v, min_value, max_value):
 | |
|     return min(max_value, max(min_value, v))
 | |
| 
 | |
| def scale(v, ref, bits):
 | |
|     v /= ref
 | |
|     vmin = -(2 ** bits)
 | |
|     vmax = -vmin - 1
 | |
|     vrange = vmax - vmin
 | |
|     return int(((v + 1) / 2.0) * vrange + vmin)
 | |
| 
 | |
| 
 | |
| class MockMCP3xxx(MockSPIDevice):
 | |
|     def __init__(
 | |
|             self, clock_pin, mosi_pin, miso_pin, select_pin=None,
 | |
|             channels=8, bits=10):
 | |
|         super(MockMCP3xxx, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin)
 | |
|         self.vref = 3.3
 | |
|         self.channels = [0.0] * channels
 | |
|         self.channel_bits = 3
 | |
|         self.bits = bits
 | |
|         self.state = 'idle'
 | |
| 
 | |
|     def on_start(self):
 | |
|         super(MockMCP3xxx, self).on_start()
 | |
|         self.state = 'idle'
 | |
| 
 | |
|     def on_bit(self):
 | |
|         if self.state == 'idle':
 | |
|             if self.rx_buf[-1]:
 | |
|                 self.state = 'mode'
 | |
|                 self.rx_buf = []
 | |
|         elif self.state == 'mode':
 | |
|             if self.rx_buf[-1]:
 | |
|                 self.state = 'single'
 | |
|             else:
 | |
|                 self.state = 'diff'
 | |
|             self.rx_buf = []
 | |
|         elif self.state in ('single', 'diff'):
 | |
|             if len(self.rx_buf) == self.channel_bits:
 | |
|                 self.on_result(self.state == 'diff', self.rx_word())
 | |
|                 self.state = 'result'
 | |
|         elif self.state == 'result':
 | |
|             if not self.tx_buf:
 | |
|                 self.state = 'idle'
 | |
|                 self.rx_buf = []
 | |
|         else:
 | |
|             assert False
 | |
| 
 | |
|     def on_result(self, differential, channel):
 | |
|         if differential:
 | |
|             pos_channel = channel
 | |
|             neg_channel = pos_channel ^ 1
 | |
|             result = self.channels[pos_channel] - self.channels[neg_channel]
 | |
|             result = clamp(result, 0, self.vref)
 | |
|         else:
 | |
|             result = clamp(self.channels[channel], 0, self.vref)
 | |
|         result = scale(result, self.vref, self.bits)
 | |
|         self.tx_word(result, self.bits + 2)
 | |
| 
 | |
| 
 | |
| class MockMCP3xx1(MockMCP3xxx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None, bits=10):
 | |
|         super(MockMCP3xx1, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=bits)
 | |
| 
 | |
|     def on_start(self):
 | |
|         super(MockMCP3xx1, self).on_start()
 | |
|         result = self.channels[0] - self.channels[1]
 | |
|         result = clamp(result, 0, self.vref)
 | |
|         result = scale(result, self.vref, self.bits)
 | |
|         self.tx_word(result, self.bits + 3)
 | |
| 
 | |
|     def on_bit(self):
 | |
|         pass
 | |
| 
 | |
| 
 | |
| class MockMCP3xx2(MockMCP3xxx):
 | |
|     def __init__(
 | |
|             self, clock_pin, mosi_pin, miso_pin, select_pin=None,
 | |
|             bits=10):
 | |
|         super(MockMCP3xx2, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=bits)
 | |
|         self.channel_bits = 1
 | |
| 
 | |
| 
 | |
| class MockMCP33xx(MockMCP3xxx):
 | |
|     def __init__(
 | |
|             self, clock_pin, mosi_pin, miso_pin, select_pin=None,
 | |
|             channels=8):
 | |
|         super(MockMCP33xx, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels, 12)
 | |
| 
 | |
|     def on_result(self, differential, channel):
 | |
|         if differential:
 | |
|             pos_channel = channel
 | |
|             neg_channel = pos_channel ^ 1
 | |
|             result = self.channels[pos_channel] - self.channels[neg_channel]
 | |
|             result = clamp(result, -self.vref, self.vref)
 | |
|         else:
 | |
|             result = clamp(self.channels[channel], 0, self.vref)
 | |
|         result = scale(result, self.vref, self.bits)
 | |
|         if result < 0:
 | |
|             result += 8192
 | |
|         self.tx_word(result, self.bits + 3)
 | |
| 
 | |
| 
 | |
| class MockMCP3001(MockMCP3xx1):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3001, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, bits=10)
 | |
| 
 | |
| 
 | |
| class MockMCP3002(MockMCP3xx2):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3002, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, bits=10)
 | |
| 
 | |
| 
 | |
| class MockMCP3004(MockMCP3xxx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3004, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=4, bits=10)
 | |
| 
 | |
| 
 | |
| class MockMCP3008(MockMCP3xxx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3008, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=8, bits=10)
 | |
| 
 | |
| 
 | |
| class MockMCP3201(MockMCP3xx1):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3201, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, bits=12)
 | |
| 
 | |
| 
 | |
| class MockMCP3202(MockMCP3xx2):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3202, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, bits=12)
 | |
| 
 | |
| 
 | |
| class MockMCP3204(MockMCP3xxx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3204, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=4, bits=12)
 | |
| 
 | |
| 
 | |
| class MockMCP3208(MockMCP3xxx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3208, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=8, bits=12)
 | |
| 
 | |
| 
 | |
| class MockMCP3301(MockMCP3xxx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3301, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=2, bits=12)
 | |
| 
 | |
|     def on_start(self):
 | |
|         super(MockMCP3301, self).on_start()
 | |
|         result = self.channels[0] - self.channels[1]
 | |
|         result = clamp(result, -self.vref, self.vref)
 | |
|         result = scale(result, self.vref, self.bits)
 | |
|         if result < 0:
 | |
|             result += 8192
 | |
|         self.tx_word(result, self.bits + 4)
 | |
| 
 | |
| 
 | |
| class MockMCP3302(MockMCP33xx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3302, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=4)
 | |
| 
 | |
| 
 | |
| class MockMCP3304(MockMCP33xx):
 | |
|     def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin=None):
 | |
|         super(MockMCP3304, self).__init__(
 | |
|             clock_pin, mosi_pin, miso_pin, select_pin, channels=8)
 | |
| 
 | |
| 
 | |
| def single_mcp_test(mock, pot, channel, bits):
 | |
|     scale = 2**bits
 | |
|     tolerance = 1 / scale
 | |
|     mock.channels[channel] = 0.0
 | |
|     assert pot.raw_value == 0
 | |
|     assert isclose(pot.value, 0.0, abs_tol=tolerance)
 | |
|     mock.channels[channel] = mock.vref / 2
 | |
|     assert pot.raw_value == (scale / 2) - 1
 | |
|     assert isclose(pot.value, 0.5, abs_tol=tolerance)
 | |
|     mock.channels[channel] = mock.vref
 | |
|     assert pot.raw_value == scale - 1
 | |
|     assert isclose(pot.value, 1.0, abs_tol=tolerance)
 | |
| 
 | |
| def differential_mcp_test(mock, pot, pos_channel, neg_channel, bits, full=False):
 | |
|     scale = 2**bits
 | |
|     tolerance = 1 / scale
 | |
|     mock.channels[pos_channel] = 0.0
 | |
|     mock.channels[neg_channel] = 0.0
 | |
|     assert pot.raw_value == 0
 | |
|     assert isclose(pot.value, 0.0, abs_tol=tolerance)
 | |
|     mock.channels[pos_channel] = mock.vref / 2
 | |
|     assert pot.raw_value == (scale / 2) - 1
 | |
|     assert isclose(pot.value, 0.5, abs_tol=tolerance)
 | |
|     mock.channels[pos_channel] = mock.vref
 | |
|     assert pot.raw_value == scale - 1
 | |
|     assert isclose(pot.value, 1.0, abs_tol=tolerance)
 | |
|     mock.channels[neg_channel] = mock.vref / 2
 | |
|     assert pot.raw_value == (scale / 2) - 1
 | |
|     assert isclose(pot.value, 0.5, abs_tol=tolerance)
 | |
|     mock.channels[pos_channel] = mock.vref / 2
 | |
|     assert pot.raw_value == 0
 | |
|     assert isclose(pot.value, 0.0, abs_tol=tolerance)
 | |
|     mock.channels[pos_channel] = 0.0
 | |
|     mock.channels[neg_channel] = mock.vref
 | |
|     if full:
 | |
|         assert pot.raw_value == -scale
 | |
|         assert isclose(pot.value, -1.0, abs_tol=tolerance)
 | |
|     else:
 | |
|         assert pot.raw_value == 0
 | |
|         assert isclose(pot.value, 0.0, abs_tol=tolerance)
 | |
| 
 | |
| 
 | |
| def test_MCP3001():
 | |
|     mock = MockMCP3001(11, 10, 9, 8)
 | |
|     with MCP3001() as pot:
 | |
|         differential_mcp_test(mock, pot, 0, 1, 10)
 | |
| 
 | |
| def test_MCP3002():
 | |
|     mock = MockMCP3002(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3002(channel=5)
 | |
|     with MCP3002(channel=1) as pot:
 | |
|         single_mcp_test(mock, pot, 1, 10)
 | |
|     with MCP3002(channel=1, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 1, 0, 10)
 | |
| 
 | |
| def test_MCP3004():
 | |
|     mock = MockMCP3004(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3004(channel=5)
 | |
|     with MCP3004(channel=3) as pot:
 | |
|         single_mcp_test(mock, pot, 3, 10)
 | |
|     with MCP3004(channel=3, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 3, 2, 10)
 | |
| 
 | |
| def test_MCP3008():
 | |
|     mock = MockMCP3008(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3008(channel=9)
 | |
|     with MCP3008(channel=0) as pot:
 | |
|         single_mcp_test(mock, pot, 0, 10)
 | |
|     with MCP3008(channel=0, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 0, 1, 10)
 | |
| 
 | |
| def test_MCP3201():
 | |
|     mock = MockMCP3201(11, 10, 9, 8)
 | |
|     with MCP3201() as pot:
 | |
|         differential_mcp_test(mock, pot, 0, 1, 12)
 | |
| 
 | |
| def test_MCP3202():
 | |
|     mock = MockMCP3202(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3202(channel=5)
 | |
|     with MCP3202(channel=1) as pot:
 | |
|         single_mcp_test(mock, pot, 1, 12)
 | |
|     with MCP3202(channel=1, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 1, 0, 12)
 | |
| 
 | |
| def test_MCP3204():
 | |
|     mock = MockMCP3204(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3204(channel=5)
 | |
|     with MCP3204(channel=1) as pot:
 | |
|         single_mcp_test(mock, pot, 1, 12)
 | |
|     with MCP3204(channel=1, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 1, 0, 12)
 | |
| 
 | |
| def test_MCP3208():
 | |
|     mock = MockMCP3208(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3208(channel=9)
 | |
|     with MCP3208(channel=7) as pot:
 | |
|         single_mcp_test(mock, pot, 7, 12)
 | |
|     with MCP3208(channel=7, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 7, 6, 12)
 | |
| 
 | |
| def test_MCP3301():
 | |
|     mock = MockMCP3301(11, 10, 9, 8)
 | |
|     with MCP3301() as pot:
 | |
|         differential_mcp_test(mock, pot, 0, 1, 12, full=True)
 | |
| 
 | |
| def test_MCP3302():
 | |
|     mock = MockMCP3302(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3302(channel=4)
 | |
|     with MCP3302(channel=0) as pot:
 | |
|         single_mcp_test(mock, pot, 0, 12)
 | |
|     with MCP3302(channel=0, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 0, 1, 12, full=True)
 | |
| 
 | |
| def test_MCP3304():
 | |
|     mock = MockMCP3304(11, 10, 9, 8)
 | |
|     with pytest.raises(ValueError):
 | |
|         MCP3304(channel=9)
 | |
|     with MCP3304(channel=5) as pot:
 | |
|         single_mcp_test(mock, pot, 5, 12)
 | |
|     with MCP3304(channel=5, differential=True) as pot:
 | |
|         differential_mcp_test(mock, pot, 5, 4, 12, full=True)
 | |
| 
 |