forked from dotnet/iot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgram.cs
347 lines (322 loc) · 13.5 KB
/
Program.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
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Device.Gpio;
using System.Device.I2c;
using System.IO;
using System.Threading;
using Iot.Device.Ccs811;
using Iot.Device.Ft4222;
using UnitsNet;
using static System.Console;
WriteLine("Hello CCS811!");
// Simple menu to select native I2C/GPIO or thru FT4222, with GPIO pins and specific features to test
WriteLine("Select which platform I2C/GPIO you want to use:");
WriteLine(" 1. Native I2C/GPIO");
WriteLine(" 2. FT4222");
ConsoleKeyInfo platformChoice = ReadKey();
WriteLine();
WriteLine("Which I2C address do you want to use? Use the first one if Address pin is to ground, use the second one if to VCC.");
WriteLine($" 1. First 0x{Ccs811Sensor.I2cFirstAddress:X2}");
WriteLine($" 2. Second 0x{Ccs811Sensor.I2cSecondAddress:X2}");
ConsoleKeyInfo addressChoice = ReadKey();
WriteLine();
WriteLine("Do you want to the interrupt pin and events?");
WriteLine("(Y)es,(N)o");
ConsoleKeyInfo pinChoice = ReadKey();
WriteLine();
int pinInterrupt = CheckAndAskPinNumber(pinChoice.KeyChar);
WriteLine($"Do you want to use the Wake pin?");
WriteLine("(Y)es,(N)o");
pinChoice = ReadKey();
WriteLine();
int pinWake = CheckAndAskPinNumber(pinChoice.KeyChar);
WriteLine($"Do you want to use the Reset pin?");
WriteLine("(Y)es,(N)o");
pinChoice = ReadKey();
WriteLine();
int pinReset = CheckAndAskPinNumber(pinChoice.KeyChar);
if (platformChoice.KeyChar == '1')
{
WriteLine("Creating an instance of a CCS811 using the platform drivers.");
using (var ccs811 = new Ccs811Sensor(I2cDevice.Create(new I2cConnectionSettings(3, addressChoice.KeyChar == '1' ? Ccs811Sensor.I2cFirstAddress : Ccs811Sensor.I2cSecondAddress)), pinWake: pinWake, pinInterruption: pinInterrupt, pinReset: pinReset))
{
Sample(ccs811);
}
}
else if (platformChoice.KeyChar == '2')
{
WriteLine("Creating an instance of a CCS811 using FT4222 drivers.");
using (var i2cBus = FtCommon.GetDevices()[0].CreateI2cBus())
{
int deviceAddress = addressChoice.KeyChar == '1' ? Ccs811Sensor.I2cFirstAddress : Ccs811Sensor.I2cSecondAddress;
var gpioController = new GpioController(PinNumberingScheme.Board, new Ft4222Gpio());
using (var ccs811 = new Ccs811Sensor(i2cBus.CreateDevice(deviceAddress), gpioController, pinWake, pinInterrupt, pinReset, false))
{
Sample(ccs811);
}
}
}
else
{
ForegroundColor = ConsoleColor.Red;
WriteLine("Error: Invalid platform choice.");
ResetColor();
return;
}
void Sample(Ccs811Sensor ccs811)
{
WriteLine("Choose your operation mode:");
int i = 0;
OperationMode operationMode = OperationMode.ConstantPower1Second;
foreach (var mode in Enum.GetValues(typeof(OperationMode)))
{
WriteLine($" {i++}. {mode}");
}
var modeChoice = ReadKey();
try
{
// Converting the char to decimal, removing '0' = 0x30
int selectedMode = Convert.ToInt32(modeChoice.KeyChar) - 0x30;
if ((selectedMode >= 0) && (selectedMode <= 4))
{
operationMode = (OperationMode)Convert.ToInt32(selectedMode);
}
else
{
ForegroundColor = ConsoleColor.Red;
WriteLine($"Error selecting mode, default {operationMode} will be selected");
}
}
catch (StackOverflowException)
{
ForegroundColor = ConsoleColor.Yellow;
WriteLine($"Error selecting mode, default {operationMode} will be selected");
}
ResetColor();
DisplayBasicInformation(ccs811);
WriteLine($"Current operating mode: {ccs811.OperationMode}, changing for {operationMode}");
ccs811.OperationMode = operationMode;
WriteLine($"Current operating mode: {ccs811.OperationMode}");
ForegroundColor = ConsoleColor.Yellow;
WriteLine($"Warning: the sensor needs to run for 48h in operation mode {OperationMode.ConstantPower1Second} before getting accurate results. " +
$"Also, every time you'll start the sensor, it will need approximately 20 minutes to get accurate results as well");
ResetColor();
if (pinInterrupt >= 0)
{
WriteLine("Interruption mode selected.");
WriteLine("Do you want to enable Threshold interruption?");
WriteLine("(Y)es,(N)o");
var threshChoice = ReadKey();
WriteLine();
if (threshChoice.KeyChar is 'Y' or 'y')
{
TestThresholdAndInterrupt(ccs811);
}
else
{
Write(", once a measurement will be available, it will be displayed. Press any key to exit the program.");
ccs811.MeasurementReady += Ccs811MeasurementReady;
while (!KeyAvailable)
{
Thread.Sleep(10);
}
}
}
else
{
WriteLine("What to you want to test?");
WriteLine(" 1. Read and display gas information for 10 times.");
WriteLine(" 2. Read and display gas information for 1000 times.");
WriteLine(" 3. Read and display detailed gas information for 10 times.");
WriteLine(" 4. Read and display detailed gas information for 1000 times.");
WriteLine(" 5. Read, load and change back the baseline.");
WriteLine(" 6. Test temperature and humidity changes.");
WriteLine(" 7. Read and log gas information 10000 times.");
var operationChoice = ReadKey();
WriteLine();
switch (operationChoice.KeyChar)
{
case '1':
ReadAnDisplay(ccs811);
break;
case '2':
ReadAnDisplay(ccs811, 1000);
break;
case '3':
ReadAndDisplayDetails(ccs811);
break;
case '4':
ReadAndDisplayDetails(ccs811, 1000);
break;
case '5':
TestBaseline(ccs811);
break;
case '6':
TestTemperatureHumidityAdjustment(ccs811);
break;
case '7':
WriteLine("Result file will be log.csv. The file is flushed on the disk every 100 results.");
ReadAndLog(ccs811, 10000);
break;
default:
ForegroundColor = ConsoleColor.Red;
WriteLine("Error: Sorry, didn't get your choice, program will now exit.");
ResetColor();
break;
}
}
WriteLine($"Current operating mode: {ccs811.OperationMode}, changing for {OperationMode.Idle}");
ccs811.OperationMode = OperationMode.Idle;
WriteLine($"Current operating mode: {ccs811.OperationMode}");
}
int CheckAndAskPinNumber(char toCheck)
{
if (toCheck is 'Y' or 'y')
{
WriteLine("Type the GPIO number with pin numbering scheme, you want to use:");
var pinInterChoice = ReadLine();
try
{
return Convert.ToInt32(pinInterChoice);
}
catch (OverflowException)
{
ForegroundColor = ConsoleColor.Red;
WriteLine("Error: Can't convert your pin number.");
ResetColor();
}
}
return -1;
}
void Ccs811MeasurementReady(object sender, MeasurementArgs args)
{
WriteLine($"Measurement Event: Success: {args.MeasurementSuccess}, eCO2: {args.EquivalentCO2.PartsPerMillion} ppm, " +
$"eTVOC: {args.EquivalentTotalVolatileOrganicCompound.PartsPerBillion} ppb, Current: {args.RawCurrentSelected.Microamperes} µA, " +
$"ADC: {args.RawAdcReading} = {args.RawAdcReading * 1.65 / 1023} V.");
}
void DisplayBasicInformation(Ccs811Sensor ccs811)
{
WriteLine($"Hardware identification: 0x{ccs811.HardwareIdentification:X2}, must be 0x81");
WriteLine($"Hardware version: 0x{ccs811.HardwareVersion:X2}, must be 0x1X where any X is valid");
WriteLine($"Application version: {ccs811.ApplicationVersion}");
WriteLine($"Boot loader version: {ccs811.BootloaderVersion}");
}
void TestBaseline(Ccs811Sensor ccs811)
{
var baseline = ccs811.BaselineAlgorithmCalculation;
WriteLine($"Baseline calculation value: {baseline}, changing baseline");
// Please refer to documentation, baseline is not a human readable number
ccs811.BaselineAlgorithmCalculation = 50300;
WriteLine($"Baseline calculation value: {ccs811.BaselineAlgorithmCalculation}, changing baseline for the previous one");
ccs811.BaselineAlgorithmCalculation = baseline;
WriteLine($"Baseline calculation value: {ccs811.BaselineAlgorithmCalculation}");
}
void TestTemperatureHumidityAdjustment(Ccs811Sensor ccs811)
{
WriteLine("Drastically change the temperature and humidity to see the impact on the calculation " +
"In real life, we'll get normal data and won't change them that often. " +
"The system does not react the best way when shake like this");
// First use with the default ones, no changes should appear
Temperature temp = Temperature.FromDegreesCelsius(25);
RelativeHumidity hum = RelativeHumidity.FromPercent(50);
WriteLine($"Changing temperature and humidity reference to {temp.DegreesCelsius:0.00} C, {hum.Percent:0.0} %, baseline for calculation: {ccs811.BaselineAlgorithmCalculation}");
ccs811.SetEnvironmentData(temp, hum);
ReadAndDisplayDetails(ccs811, 100);
// Changing with very different temperature
temp = Temperature.FromDegreesCelsius(70);
hum = RelativeHumidity.FromPercent(53.8);
WriteLine($"Changing temperature and humidity reference to {temp.DegreesCelsius:0.00} C, {hum.Percent:0.0} %, baseline for calculation: {ccs811.BaselineAlgorithmCalculation}");
ccs811.SetEnvironmentData(temp, hum);
ReadAndDisplayDetails(ccs811, 100);
temp = Temperature.FromDegreesCelsius(-25);
hum = RelativeHumidity.FromPercent(0.5);
WriteLine($"Changing temperature and humidity reference to {temp.DegreesCelsius:0.00} C, {hum.Percent:0.0} %, baseline for calculation: {ccs811.BaselineAlgorithmCalculation}");
ccs811.SetEnvironmentData(temp, hum);
ReadAndDisplayDetails(ccs811, 100);
// Back to normal which still can lead to different results than initially
// This is due to the baseline
temp = Temperature.FromDegreesCelsius(25);
hum = RelativeHumidity.FromPercent(50);
WriteLine($"Changing temperature and humidity reference to {temp.DegreesCelsius:0.00} C, {hum.Percent:0.0} %, baseline for calculation: {ccs811.BaselineAlgorithmCalculation}");
ccs811.SetEnvironmentData(temp, hum);
ReadAndDisplayDetails(ccs811, 100);
}
void TestThresholdAndInterrupt(Ccs811Sensor ccs811)
{
if (!ccs811.InterruptEnable)
{
ForegroundColor = ConsoleColor.Red;
WriteLine("Error: interrupt needs to be activated to run this test");
ResetColor();
return;
}
ccs811.MeasurementReady += Ccs811MeasurementReady;
// Setting up a range where we will see something in a normal environment
VolumeConcentration low = VolumeConcentration.FromPartsPerMillion(400);
VolumeConcentration high = VolumeConcentration.FromPartsPerMillion(600);
WriteLine($"Setting up {low.PartsPerMillion}-{high.PartsPerMillion} range, in clear environment, that should raise interrupts. Wait 3 minutes and change mode. Blow on the sensor and wait a bit.");
ForegroundColor = ConsoleColor.Yellow;
WriteLine("Warning: only the first measurement to cross the threshold is raised.");
ResetColor();
ccs811.SetThreshold(low, high);
DateTime dt = DateTime.Now.AddMinutes(3);
while (dt > DateTime.Now)
{
Thread.Sleep(10);
}
low = VolumeConcentration.FromPartsPerMillion(15000);
high = VolumeConcentration.FromPartsPerMillion(20000);
WriteLine($"Changing threshold for {low.PartsPerMillion}-{high.PartsPerMillion}, a non reachable range in clear environment. No measurement should appear in next 3 minutes");
dt = DateTime.Now.AddMinutes(3);
ccs811.SetThreshold(low, high);
while (dt > DateTime.Now)
{
Thread.Sleep(10);
}
}
void ReadAndLog(Ccs811Sensor ccs811, int count = 10)
{
string toWrite = "Time;Success;eCO2 (ppm);eTVOC (ppb);Current (µA);ADC;ADC (V);Baseline";
using var fl = new StreamWriter("log.csv");
fl.WriteLine(toWrite);
for (int i = 0; i < count; i++)
{
while (!ccs811.IsDataReady)
{
Thread.Sleep(10);
}
var error = ccs811.TryReadGasData(out VolumeConcentration eCO2, out VolumeConcentration eTVOC, out ElectricCurrent curr, out int adc);
toWrite = $"{DateTime.Now};{error};{eCO2.PartsPerMillion};{eTVOC.PartsPerBillion};{curr.Microamperes};{adc};{adc * 1.65 / 1023};{ccs811.BaselineAlgorithmCalculation}";
fl.WriteLine(toWrite);
WriteLine(toWrite);
if (i % 100 == 0)
{
fl.Flush();
}
}
}
void ReadAnDisplay(Ccs811Sensor ccs811, int count = 10)
{
for (int i = 0; i < count; i++)
{
while (!ccs811.IsDataReady)
{
Thread.Sleep(10);
}
var error = ccs811.TryReadGasData(out VolumeConcentration eCO2, out VolumeConcentration eTVOC);
WriteLine($"Success: {error}, eCO2: {eCO2.PartsPerMillion} ppm, eTVOC: {eTVOC.PartsPerBillion} ppb");
}
}
void ReadAndDisplayDetails(Ccs811Sensor ccs811, int count = 10)
{
for (int i = 0; i < count; i++)
{
while (!ccs811.IsDataReady)
{
Thread.Sleep(10);
}
var error = ccs811.TryReadGasData(out VolumeConcentration eCO2, out VolumeConcentration eTVOC, out ElectricCurrent curr, out int adc);
WriteLine($"Success: {error}, eCO2: {eCO2.PartsPerMillion} ppm, eTVOC: {eTVOC.PartsPerBillion} ppb, Current: {curr.Microamperes} µA, ADC: {adc} = {adc * 1.65 / 1023} V.");
}
}