File size: 4,125 Bytes
4bec42e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""Sterling Baird: wrapper class for AS7341 sensor."""

from math import log

from as7341 import AS7341, AS7341_MODE_SPM
from machine import I2C, Pin


class ExternalDeviceNotFound(OSError):
    pass


class Sensor:
    def __init__(
        self, atime=100, astep=999, gain=8, i2c=I2C(1, scl=Pin(27), sda=Pin(26))
    ):
        """Wrapper for Rob Hamerling's AS7341 implementation.

        Mimics the original CircuitPython class a bit more, specific to the needs of
        SDL-Demo.

        Rob Hamerling's implementation:
        - https://gitlab.com/robhamerling/micropython-as7341

        Original Circuit Python repo:
        - https://github.com/adafruit/Adafruit_CircuitPython_AS7341

        Parameters
        ----------
        atime : int, optional
            The integration time step size in 2.78 microsecond increments, by default 100
        astep : int, optional
            The integration time step count. Total integration time will be (ATIME + 1)
            * (ASTEP + 1) * 2.78µS, by default 999, meaning 281 ms assuming atime=100
        gain : int, optional
            The ADC gain multiplier, by default 128
        i2c : I2C, optional
            The I2C bus, by default machine.I2C(1, scl=machine.Pin(27),
            sda=machine.Pin(26))

        Raises
        ------
        ExternalDeviceNotFound
            Couldn't connect to AS7341.

        Examples
        --------
        >>> sensor = Sensor(atime=29, astep=599, again=4)
        >>> channel_data = sensor.all_channels
        """

        # i2c = machine.SoftI2C(scl=Pin(27), sda=Pin(26))
        self.i2c = i2c
        addrlist = " ".join(["0x{:02X}".format(x) for x in i2c.scan()])  # type: ignore
        print("Detected devices at I2C-addresses:", addrlist)

        sensor = AS7341(i2c)

        if not sensor.isconnected():
            raise ExternalDeviceNotFound("Failed to contact AS7341, terminating")

        sensor.set_measure_mode(AS7341_MODE_SPM)

        sensor.set_atime(atime)
        sensor.set_astep(astep)
        sensor.set_again(gain)

        self.sensor = sensor

        self.__atime = atime
        self.__astep = astep
        self.__gain = gain

    @property
    def _atime(self):
        return self.__atime

    @_atime.setter
    def _atime(self, value):
        self.__atime = value
        self.sensor.set_atime(value)

    @property
    def _astep(self):
        return self.__astep

    @_astep.setter
    def _astep(self, value):
        self.__atime = value
        self.sensor.set_astep(value)

    @property
    def _gain(self):
        return self.__gain

    @_gain.setter
    def _gain(self, gain):
        """set AGAIN (code in range 0..10 -> gain factor 0.5 .. 512)
        gain:  *0.5 | *1 | *2 | *4 | *8 | *16 | *32 | *64 | *128 | *256 | *512
        code      0    1    2    3    4    5      6     7      8      9     10
        """
        self.__gain = gain
        # gain == 0.5 * 2 ** code --> code == 1.4427 Ln[2 * gain] (via Mathematica)
        code = int(round(1.4427 * log(2 * gain)))
        self.sensor.set_again(code)

    @property
    def all_channels(self):
        self.sensor.start_measure("F1F4CN")
        f1, f2, f3, f4, clr, nir = self.sensor.get_spectral_data()

        self.sensor.start_measure("F5F8CN")
        f5, f6, f7, f8, clr, nir = self.sensor.get_spectral_data()

        clr, nir  # to ignore "unused" linting warnings

        return [f1, f2, f3, f4, f5, f6, f7, f8]

    @property
    def all_channels_clr_nir(self):
        self.sensor.start_measure("F1F4CN")
        f1, f2, f3, f4, clr, nir = self.sensor.get_spectral_data()

        self.sensor.start_measure("F5F8CN")
        f5, f6, f7, f8, clr, nir = self.sensor.get_spectral_data()

        clr, nir  # to ignore "unused" linting warnings

        return [f1, f2, f3, f4, f5, f6, f7, f8, clr, nir]

    def disable(self):
        self.sensor.disable()


# %% Code Graveyard
# gain_to_code_lookup = {
#     0.5: 1,
#     1: 1,
#     2: 2,
#     4: 3,
#     8: 4,
#     16: 5,
#     32: 6,
#     64: 7,
#     128: 8,
#     256: 9,
#     512: 10,
# }
# code = gain_to_code_lookup[gain]