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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.

//! Capsule for analog sensors.
//!
//! This capsule provides the sensor HIL interfaces for sensors which only need
//! an ADC.
//!
//! It includes support for analog light sensors and analog temperature sensors.

use kernel::hil;
use kernel::utilities::cells::OptionalCell;
use kernel::ErrorCode;

/// The type of the sensor implies how the raw ADC reading should be converted
/// to a light value.
pub enum AnalogLightSensorType {
    LightDependentResistor,
}

pub struct AnalogLightSensor<'a, A: hil::adc::Adc<'a>> {
    adc: &'a A,
    channel: &'a <A as hil::adc::Adc<'a>>::Channel,
    sensor_type: AnalogLightSensorType,
    client: OptionalCell<&'a dyn hil::sensors::AmbientLightClient>,
}

impl<'a, A: hil::adc::Adc<'a>> AnalogLightSensor<'a, A> {
    pub fn new(
        adc: &'a A,
        channel: &'a <A as kernel::hil::adc::Adc<'a>>::Channel,
        sensor_type: AnalogLightSensorType,
    ) -> AnalogLightSensor<'a, A> {
        AnalogLightSensor {
            adc,
            channel,
            sensor_type,
            client: OptionalCell::empty(),
        }
    }
}

/// Callbacks from the ADC driver
impl<'a, A: hil::adc::Adc<'a>> hil::adc::Client for AnalogLightSensor<'a, A> {
    fn sample_ready(&self, sample: u16) {
        // TODO: calculate the actual light reading.
        let measurement: usize = match self.sensor_type {
            AnalogLightSensorType::LightDependentResistor => {
                // TODO: need to determine the actual value that the 5000 should be
                (sample as usize * 5000) / 65535
            }
        };
        self.client.map(|client| client.callback(measurement));
    }
}

impl<'a, A: hil::adc::Adc<'a>> hil::sensors::AmbientLight<'a> for AnalogLightSensor<'a, A> {
    fn set_client(&self, client: &'a dyn hil::sensors::AmbientLightClient) {
        self.client.set(client);
    }

    fn read_light_intensity(&self) -> Result<(), ErrorCode> {
        self.adc.sample(self.channel)
    }
}

/// The type of the sensor implies how the raw ADC reading should be converted
/// to a temperature value.
pub enum AnalogTemperatureSensorType {
    MicrochipMcp9700,
}

pub struct AnalogTemperatureSensor<'a, A: hil::adc::Adc<'a>> {
    adc: &'a A,
    channel: &'a <A as hil::adc::Adc<'a>>::Channel,
    sensor_type: AnalogTemperatureSensorType,
    client: OptionalCell<&'a dyn hil::sensors::TemperatureClient>,
}

impl<'a, A: hil::adc::Adc<'a>> AnalogTemperatureSensor<'a, A> {
    pub fn new(
        adc: &'a A,
        channel: &'a <A as kernel::hil::adc::Adc<'a>>::Channel,
        sensor_type: AnalogLightSensorType,
    ) -> AnalogLightSensor<'a, A> {
        AnalogLightSensor {
            adc,
            channel,
            sensor_type,
            client: OptionalCell::empty(),
        }
    }
}

/// Callbacks from the ADC driver
impl<'a, A: hil::adc::Adc<'a>> hil::adc::Client for AnalogTemperatureSensor<'a, A> {
    fn sample_ready(&self, sample: u16) {
        // TODO: calculate the actual temperature reading.
        let measurement = match self.sensor_type {
            // 𝑉out = 500𝑚𝑉 + 10𝑚𝑉/C ∗ 𝑇A
            AnalogTemperatureSensorType::MicrochipMcp9700 => {
                self.adc
                    .get_voltage_reference_mv()
                    .map_or(Err(ErrorCode::FAIL), |ref_mv| {
                        // reading_mv = (ADC / (2^16-1)) * ref_voltage
                        let reading_mv = (sample as usize * ref_mv) / 65535;
                        // need 0.01°C
                        Ok((reading_mv as i32 - 500) * 10)
                    })
            }
        };
        self.client.map(|client| client.callback(measurement));
    }
}

impl<'a, A: hil::adc::Adc<'a>> hil::sensors::TemperatureDriver<'a>
    for AnalogTemperatureSensor<'a, A>
{
    fn set_client(&self, client: &'a dyn hil::sensors::TemperatureClient) {
        self.client.set(client);
    }

    fn read_temperature(&self) -> Result<(), ErrorCode> {
        self.adc.sample(self.channel)
    }
}