/
AnalogTemperature.cs
286 lines (268 loc) · 11 KB
/
AnalogTemperature.cs
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
using Meadow.Hardware;
using Meadow.Peripherals.Sensors;
using Meadow.Units;
using System;
using System.Threading.Tasks;
namespace Meadow.Foundation.Sensors.Temperature
{
/// <summary>
/// Provide the ability to read the temperature from the following sensors:
/// - TMP35 / 36 / 37
/// - LM35 / 45
/// </summary>
/// <remarks>
/// <i>AnalogTemperature</i> provides a method of reading the temperature from
/// linear analog temperature sensors. There are a number of these sensors available
/// including the commonly available TMP and LM series.
/// Sensors of this type obey the following equation:
/// y = mx + c
/// where y is the reading in millivolts, m is the gradient (number of millivolts per
/// degree centigrade and C is the point where the line would intercept the y axis.
/// The <i>SensorType</i> enum defines the list of sensors with default settings in the
/// library. Unsupported sensors that use the same linear algorithm can be constructed
/// by setting the sensor type to <i>SensorType.Custom</i> and providing the settings for
/// the linear calculations.
/// The default sensors have the following settings:
/// Sensor Millivolts at 25C Millivolts per degree C
/// TMP35, LM35, LM45 250 10
/// TMP23x, TMP36, LM50 750 10
/// TMP37 500 20
/// TMP236 887.5 19.5
/// </remarks>
public partial class AnalogTemperature : SamplingSensorBase<Units.Temperature>, ITemperatureSensor, IDisposable
{
///<Summary>
/// AnalogInputPort connected to temperature sensor
///</Summary>
protected IAnalogInputPort AnalogInputPort { get; }
/// <summary>
/// Type of temperature sensor.
/// </summary>
public enum KnownSensorType
{
///<Summary>
/// Custom temperature sensor
///</Summary>
Custom,
///<Summary>
/// TMP235 temperature sensor
///</Summary>
TMP235,
///<Summary>
/// TMP236 temperature sensor
///</Summary>
TMP236,
///<Summary>
/// TMP35 temperature sensor
///</Summary>
TMP35,
///<Summary>
/// TMP36 temperature sensor
///</Summary>
TMP36,
///<Summary>
/// TMP37 temperature sensor
///</Summary>
TMP37,
///<Summary>
/// LM35 temperature sensor
///</Summary>
LM35,
///<Summary>
/// LM45 temperature sensor
///</Summary>
LM45,
///<Summary>
/// LM50 temperature sensor
///</Summary>
LM50,
}
///<Summary>
/// SensorCalibration property for temperature sensor
///</Summary>
public Calibration SensorCalibration { get; set; }
/// <summary>
/// Current Temperature
/// </summary>
public Units.Temperature? Temperature { get; protected set; }
/// <summary>
/// Is the object disposed
/// </summary>
public bool IsDisposed { get; private set; }
/// <summary>
/// Did we create the port(s) used by the peripheral
/// </summary>
readonly bool createdPort = false;
/// <summary>
/// Creates a new instance of the AnalogTemperature class.
/// </summary>
/// <param name="analogPin">Analog pin the temperature sensor is connected to</param>
/// <param name="sensorType">Type of sensor attached to the analog port</param>
/// <param name="calibration">Calibration for the analog temperature sensor - used if sensorType is set to Custom</param>
/// <param name="sampleCount">How many samples to take during a given
/// reading. These are automatically averaged to reduce noise</param>
/// <param name="sampleInterval">The time between sample readings</param>
public AnalogTemperature(
IPin analogPin,
KnownSensorType sensorType,
Calibration? calibration = null,
int sampleCount = 5,
TimeSpan? sampleInterval = null)
: this(
analogPin.CreateAnalogInputPort(sampleCount, sampleInterval ?? TimeSpan.FromMilliseconds(40), new Voltage(3.3, Voltage.UnitType.Volts)),
sensorType, calibration)
{
createdPort = true;
}
/// <summary>
/// Creates a new instance of the AnalogTemperature class.
/// </summary>
/// <param name="analogInputPort">The `IAnalogInputPort` connected to the sensor.</param>
/// <param name="sensorType">Type of sensor attached to the analog port.</param>
/// <param name="calibration">Calibration for the analog temperature sensor. Only used if sensorType is set to Custom.</param>
public AnalogTemperature(IAnalogInputPort analogInputPort,
KnownSensorType sensorType,
Calibration? calibration = null)
{
AnalogInputPort = analogInputPort;
switch (sensorType)
{
case KnownSensorType.TMP35:
case KnownSensorType.LM35:
case KnownSensorType.LM45:
calibration = new Calibration(
degreesCelsiusSampleReading: 25,
millivoltsAtSampleReading: 250,
millivoltsPerDegreeCentigrade: 10);
break;
case KnownSensorType.LM50:
case KnownSensorType.TMP235:
case KnownSensorType.TMP36:
calibration = new Calibration(
degreesCelsiusSampleReading: 25,
millivoltsAtSampleReading: 750,
millivoltsPerDegreeCentigrade: 10);
break;
case KnownSensorType.TMP37:
calibration = new Calibration(
degreesCelsiusSampleReading: 25,
millivoltsAtSampleReading: 500,
millivoltsPerDegreeCentigrade: 20);
break;
case KnownSensorType.TMP236:
calibration = new Calibration(
degreesCelsiusSampleReading: 25,
millivoltsAtSampleReading: 887.5,
millivoltsPerDegreeCentigrade: 19.5);
break;
case KnownSensorType.Custom:
//user provided calibration
if (calibration == null)
{
throw new ArgumentNullException("Custom Calibration sensor type requires a Calibration parameter");
}
break;
default:
calibration = new Calibration();
break;
}
SensorCalibration = calibration;
// wire up our observable
// have to convert from voltage to temp units for our consumers
// this is where the magic is: this allows us to extend the IObservable
// pattern through the sensor driver
AnalogInputPort.Subscribe
(
IAnalogInputPort.CreateObserver(
result =>
{
// create a new change result from the new value
ChangeResult<Units.Temperature> changeResult = new ChangeResult<Units.Temperature>()
{
New = VoltageToTemperature(result.New),
Old = Temperature
};
Temperature = changeResult.New;
RaiseEventsAndNotify(changeResult);
}
)
);
}
/// <summary>
/// Convenience method to get the current temperature. For frequent reads, use
/// StartSampling() and StopSampling() in conjunction with the SampleBuffer.
/// </summary>
/// <returns>The temperature averages of the given sample count</returns>
protected override async Task<Units.Temperature> ReadSensor()
{
var voltage = await AnalogInputPort.Read();
var newTemp = VoltageToTemperature(voltage);
Temperature = newTemp;
return newTemp;
}
/// <summary>
/// Starts continuously sampling the sensor.
///
/// This method also starts raising `Changed` events and IObservable
/// subscribers getting notified. Use the `readIntervalDuration` parameter
/// to specify how often events and notifications are raised/sent.
/// </summary>
/// <param name="updateInterval">A `TimeSpan` that specifies how long to
/// wait between readings. This value influences how often `*Updated`
/// events are raised and `IObservable` consumers are notified.
///</param>
public override void StartUpdating(TimeSpan? updateInterval)
{
lock (samplingLock)
{
if (IsSampling) { return; }
IsSampling = true;
AnalogInputPort.StartUpdating(updateInterval);
}
}
/// <summary>
/// Stops sampling the temperature
/// </summary>
public override void StopUpdating()
{
lock (samplingLock)
{
if (!IsSampling) { return; }
IsSampling = false;
AnalogInputPort.StopUpdating();
}
}
/// <summary>
/// Converts voltage to Temperature
/// </summary>
/// <param name="voltage"></param>
/// <returns>Temperature</returns>
protected Units.Temperature VoltageToTemperature(Voltage voltage)
{
return new Units.Temperature(SensorCalibration.SampleReading +
(voltage.Millivolts - SensorCalibration.MillivoltsAtSampleReading) / SensorCalibration.MillivoltsPerDegreeCentigrade,
Units.Temperature.UnitType.Celsius);
}
///<inheritdoc/>
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose of the object
/// </summary>
/// <param name="disposing">Is disposing</param>
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
if (disposing && createdPort)
{
AnalogInputPort?.Dispose();
}
IsDisposed = true;
}
}
}
}