diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bd1438 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.vs +/IotThermometer/obj +/IotThermometer/bin diff --git a/IotThermometer.sln b/IotThermometer.sln new file mode 100644 index 0000000..08a3821 --- /dev/null +++ b/IotThermometer.sln @@ -0,0 +1,49 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.645 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IotThermometer", "IotThermometer\IotThermometer.csproj", "{785F6406-8C2A-4A06-8B65-91A6B9F55D1A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unosquare.RaspberryIO", "..\Librarys\RaspberryIO\Unosquare.RaspberryIO\Unosquare.RaspberryIO.csproj", "{8C5D4DE9-377F-4EC8-873D-6EEF15F43516}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unosquare.Swan", "..\Librarys\RaspberryIO\Unosquare.Swan\Unosquare.Swan.csproj", "{2EA5E3E4-F8C8-4742-8C78-4B070AFCFB73}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unosquare.Swan.Lite", "..\Librarys\RaspberryIO\Unosquare.Swan.Lite\Unosquare.Swan.Lite.csproj", "{AB015683-62E5-47F1-861F-6D037F9C6433}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "..\Utils\Utils\Utils\Utils.csproj", "{FAC8CE64-BF13-4ECE-8097-AEB5DD060098}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {785F6406-8C2A-4A06-8B65-91A6B9F55D1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {785F6406-8C2A-4A06-8B65-91A6B9F55D1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {785F6406-8C2A-4A06-8B65-91A6B9F55D1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {785F6406-8C2A-4A06-8B65-91A6B9F55D1A}.Release|Any CPU.Build.0 = Release|Any CPU + {8C5D4DE9-377F-4EC8-873D-6EEF15F43516}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C5D4DE9-377F-4EC8-873D-6EEF15F43516}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C5D4DE9-377F-4EC8-873D-6EEF15F43516}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C5D4DE9-377F-4EC8-873D-6EEF15F43516}.Release|Any CPU.Build.0 = Release|Any CPU + {2EA5E3E4-F8C8-4742-8C78-4B070AFCFB73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2EA5E3E4-F8C8-4742-8C78-4B070AFCFB73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2EA5E3E4-F8C8-4742-8C78-4B070AFCFB73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2EA5E3E4-F8C8-4742-8C78-4B070AFCFB73}.Release|Any CPU.Build.0 = Release|Any CPU + {AB015683-62E5-47F1-861F-6D037F9C6433}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB015683-62E5-47F1-861F-6D037F9C6433}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB015683-62E5-47F1-861F-6D037F9C6433}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB015683-62E5-47F1-861F-6D037F9C6433}.Release|Any CPU.Build.0 = Release|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {B449151E-6867-4965-B4F2-B788F6238F58} + EndGlobalSection +EndGlobal diff --git a/IotThermometer/App.config b/IotThermometer/App.config new file mode 100644 index 0000000..4bba09a --- /dev/null +++ b/IotThermometer/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/IotThermometer/IotThermometer.csproj b/IotThermometer/IotThermometer.csproj new file mode 100644 index 0000000..e5c4454 --- /dev/null +++ b/IotThermometer/IotThermometer.csproj @@ -0,0 +1,73 @@ + + + + + Debug + AnyCPU + {785F6406-8C2A-4A06-8B65-91A6B9F55D1A} + Exe + BlubbFish.Iot.Thermometer + IotThermometer + v4.7.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + {8C5D4DE9-377F-4EC8-873D-6EEF15F43516} + Unosquare.RaspberryIO + + + {ab015683-62e5-47f1-861f-6d037f9c6433} + Unosquare.Swan.Lite + + + {2ea5e3e4-f8c8-4742-8c78-4b070afcfb73} + Unosquare.Swan + + + {FAC8CE64-BF13-4ECE-8097-AEB5DD060098} + Utils + + + + \ No newline at end of file diff --git a/IotThermometer/Librarys/Bme280.cs b/IotThermometer/Librarys/Bme280.cs new file mode 100644 index 0000000..77c96c4 --- /dev/null +++ b/IotThermometer/Librarys/Bme280.cs @@ -0,0 +1,236 @@ +using System; +using BlubbFish.Utils; +using Unosquare.RaspberryIO; +using Unosquare.RaspberryIO.Gpio; + +namespace BlubbFish.Iot.Thermometer.Librarys { + class Bme280 { + public Bme280(Int32 address) => this.w = Pi.I2C.AddDevice(address); + + public void Begin() { + if(this.Read8((Byte)Register.BME280_REG_ID) != 0x60) { + Helper.WriteError("Fail to init Barometer\n"); + } + this.ReadTrimming(); + //Humidity oversampling 16X oversampling [2..0] + this.WriteRegister((Byte)Register.BME280_REG_CTRL_HUM, 0b00000101); + //Pressure oversampling 16X oversampling [7..5], Temperature oversampling 16X oversampling [4..2], Mode Normal mode [1..0] + this.WriteRegister((Byte)Register.BME280_REG_CTRL_MEAS, 0b10110111); + //inactive duration 250 ms [7..5], IIR filter Filter coefficient 16 [4..2], SPI Interface off [0] + this.WriteRegister((Byte)Register.BME280_REG_CONFIG, 0b01110000); + Console.WriteLine("Barometer ok\n"); + } + + public void Measure() { + this._adc_T = (Int32)this.Read20((Byte)Register.BME280_REG_TEMP_DATA); + this._adc_P = (Int32)this.Read20((Byte)Register.BME280_REG_PRESS_DATA); + this._adc_H = this.Read16((Byte)Register.BME280_REG_HUM_DATA); + } + + public Double GetTemperature() { + Int32 adc_T = this._adc_T; + Int32 var1, var2; + + var1 = (((adc_T >> 3) - (this._dig_T1 << 1)) * this._dig_T2) >> 11; + var2 = (((((adc_T >> 4) - this._dig_T1) * ((adc_T >> 4) - this._dig_T1)) >> 12) * this._dig_T3) >> 14; + this._t_fine = var1 + var2; + return ((Double)(this._t_fine * 5 + 128 >> 8)) / 100; + + /*adc_T >>= 4; + var1 = (((adc_T >> 3) - ((int32_t)(this->_dig_T1 << 1))) * ((int32_t)this->_dig_T2)) >> 11; + var2 = (((((adc_T >> 4) - ((int32_t)this->_dig_T1)) * ((adc_T >> 4) - ((int32_t)this->_dig_T1))) >> 12) * ((int32_t)this->_dig_T3)) >> 14; + t_fine = var1 + var2; + float T = (t_fine * 5 + 128) >> 8; + return T/100;*/ + } + + public Double GetPressure() { + Int32 adc_P = this._adc_P; + Int64 var1, var2, P; + + var1 = ((Int64)this._t_fine) - 128000; + var2 = var1 * var1 * this._dig_P6; + var2 = var2 + ((var1 * this._dig_P5) << 17); + var2 = var2 + (((Int64)this._dig_P4) << 35); + var1 = ((var1 * var1 * this._dig_P3) >> 8) + ((var1 * this._dig_P2) << 12); + var1 = ((((Int64)1) << 47) + var1) * this._dig_P1 >> 33; + if(var1 == 0) { + return 0.0; // avoid exception caused by division by zero + } + P = 1048576 - adc_P; + P = (((P << 31) - var2) * 3125) / var1; + var1 = (this._dig_P9 * (P >> 13) * (P >> 13)) >> 25; + var2 = (this._dig_P8 * P) >> 19; + P = ((P + var1 + var2) >> 8) + (((Int64)this._dig_P7) << 4); + return ((Double)((UInt32)P)) / 25600; + + /*adc_P >>= 4; + var1 = ((int64_t)t_fine) - 128000; + var2 = var1 * var1 * (int64_t)this->_dig_P6; + var2 = var2 + ((var1*(int64_t)this->_dig_P5)<<17); + var2 = var2 + (((int64_t)this->_dig_P4)<<35); + var1 = ((var1 * var1 * (int64_t)this->_dig_P3)>>8) + ((var1 * (int64_t)this->_dig_P2)<<12); + var1 = (((((int64_t)1)<<47)+var1))*((int64_t)this->_dig_P1)>>33; + if (var1 == 0) { + return 0; // avoid exception caused by division by zero + } + p = 1048576-adc_P; + p = (((p<<31)-var2)*3125)/var1; + var1 = (((int64_t)this->_dig_P9) * (p>>13) * (p>>13)) >> 25; + var2 = (((int64_t)this->_dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)this->_dig_P7)<<4); + return ((float)(uint32_t)p/256)/100;*/ + } + + public Double GetHumidity() { + Int32 adc_H = this._adc_H; + Int32 H; + + H = this._t_fine - 76800; + H = ((((adc_H << 14) - (this._dig_H4 << 20) - (this._dig_H5 * H)) + 16384) >> 15) * + (((((((H * this._dig_H6) >> 10) * (((H * this._dig_H3) >> 11) + 32768)) >> 10) + 2097152) * this._dig_H2 + 8192) >> 14); + H = H - (((((H >> 15) * (H >> 15)) >> 7) * this._dig_H1) >> 4); + H = H < 0 ? 0 : H; + H = H > 419430400 ? 419430400 : H; + return ((Double)(H >> 12)) / 1024; + } + + public void ReadTrimming() { + this._dig_T1 = this.Read16LE((Byte)Calibration.BME280_REG_CALIB00); + this._dig_T2 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB02); + this._dig_T3 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB04); + this._dig_P1 = this.Read16LE((Byte)Calibration.BME280_REG_CALIB06); + this._dig_P2 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB08); + this._dig_P3 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB10); + this._dig_P4 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB12); + this._dig_P5 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB14); + this._dig_P6 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB16); + this._dig_P7 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB18); + this._dig_P8 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB20); + this._dig_P9 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB22); + this._dig_H1 = this.Read8((Byte)Calibration.BME280_REG_CALIB25); + this._dig_H2 = this.ReadS16LE((Byte)Calibration.BME280_REG_CALIB26); + this._dig_H3 = this.Read8((Byte)Calibration.BME280_REG_CALIB28); + this._dig_H4 = (Int16)((this.Read8((Byte)Calibration.BME280_REG_CALIB29) << 4) | (0b00001111 & this.Read8((Byte)Calibration.BME280_REG_CALIB30))); + this._dig_H5 = (Int16)((this.Read8((Byte)Calibration.BME280_REG_CALIB31) << 4) | ((0b11110000 & this.Read8((Byte)Calibration.BME280_REG_CALIB30)) >> 4)); + this._dig_H6 = (SByte)this.Read8((Byte)Calibration.BME280_REG_CALIB32); + } + + private readonly I2CDevice w; + private Int32 _adc_T; + private Int32 _adc_P; + private Int32 _adc_H; + private Int32 _t_fine; + + private UInt16 _dig_T1; + private Int16 _dig_T2; + private Int16 _dig_T3; + private UInt16 _dig_P1; + private Int16 _dig_P2; + private Int16 _dig_P3; + private Int16 _dig_P4; + private Int16 _dig_P5; + private Int16 _dig_P6; + private Int16 _dig_P7; + private Int16 _dig_P8; + private Int16 _dig_P9; + private Byte _dig_H1; + private Int16 _dig_H2; + private Byte _dig_H3; + private Int16 _dig_H4; + private Int16 _dig_H5; + private SByte _dig_H6; + + //Calibration Register Adresses + private enum Calibration { + BME280_REG_CALIB00 = 0b10001000, + BME280_REG_CALIB02 = 0b10001010, + BME280_REG_CALIB04 = 0b10001100, + BME280_REG_CALIB06 = 0b10001110, + BME280_REG_CALIB08 = 0b10010000, + BME280_REG_CALIB10 = 0b10010010, + BME280_REG_CALIB12 = 0b10010100, + BME280_REG_CALIB14 = 0b10010110, + BME280_REG_CALIB16 = 0b10011000, + BME280_REG_CALIB18 = 0b10011010, + BME280_REG_CALIB20 = 0b10011100, + BME280_REG_CALIB22 = 0b10011110, + BME280_REG_CALIB25 = 0b10100001, + BME280_REG_CALIB26 = 0b11100001, + BME280_REG_CALIB28 = 0b11100011, + BME280_REG_CALIB29 = 0b11100100, + BME280_REG_CALIB30 = 0b11100101, + BME280_REG_CALIB31 = 0b11100110, + BME280_REG_CALIB32 = 0b11100111 + }; + + //Register Adresses + private enum Register { + BME280_REG_ID = 0b11010000, + BME280_REG_RESET = 0b11100000, + BME280_REG_CTRL_HUM = 0b11110010, + BME280_REG_STATUS = 0b11110011, + BME280_REG_CTRL_MEAS = 0b11110100, + BME280_REG_CONFIG = 0b11110101, + BME280_REG_PRESS_DATA = 0b11110111, + BME280_REG_TEMP_DATA = 0b11111010, + BME280_REG_HUM_DATA = 0b11111101 + }; + + + + //TWI Writes + private Byte Read8(Byte reg) => this.w.ReadAddressByte(reg); + /*this->w->beginTransmission(address); + this->w->write(reg); + this->w->endTransmission(); + this->w->requestFrom(address, 1); + while(!this->w->available()) + ; + return this->w->read();*/ + + private UInt16 Read16(Byte reg) => this.w.ReadAddressWord(reg); + /*uint8_t msb, lsb; + this->w->beginTransmission(address); + this->w->write(reg); + this->w->endTransmission(); + this->w->requestFrom(address, 2); + while(this->w->available() < 2) + ; + msb = this->w->read(); + lsb = this->w->read(); + return (uint16_t)msb << 8 | lsb;*/ + + private UInt16 Read16LE(Byte reg) { + UInt16 data = this.Read16(reg); + return (UInt16)((data >> 8) | (data << 8)); + } + + private Int16 ReadS16LE(Byte reg) => (Int16)this.Read16LE(reg); + + private UInt32 Read20(Byte reg) { + UInt32 data; + this.w.Write(reg); + Byte[] dr = this.w.Read(3); + /*this->w->beginTransmission(address); + this->w->write(reg); + this->w->endTransmission(); + this->w->requestFrom(address, 3); + while(this->w->available() < 3) + ;*/ + data = dr[0]; + data <<= 8; + data |= dr[1]; + data <<= 8; + data |= dr[2]; + data >>= 4; + return data; + } + + private void WriteRegister(Byte reg, Byte val) => this.w.WriteAddressByte(reg, val); + /*this->w->beginTransmission(address); + this->w->write(reg); + this->w->write(val); + this->w->endTransmission();*/ + } +} diff --git a/IotThermometer/Librarys/TSL2591.cs b/IotThermometer/Librarys/TSL2591.cs new file mode 100644 index 0000000..b06f033 --- /dev/null +++ b/IotThermometer/Librarys/TSL2591.cs @@ -0,0 +1,331 @@ +using System; +using System.Threading; +using BlubbFish.Utils; +using Unosquare.RaspberryIO; +using Unosquare.RaspberryIO.Gpio; + +namespace BlubbFish.Iot.Thermometer.Librarys { + public class TSL2591 { + public enum Gain { + TSL2591_GAIN_LOW = 0b00000000, /// low gain (1x) + TSL2591_GAIN_MED = 0b00010000, /// medium gain (25x) + TSL2591_GAIN_HIGH = 0b00100000, /// medium gain (428x) + TSL2591_GAIN_MAX = 0b00110000, /// max gain (9876x) + }; + public enum IntegrationTime { + TSL2591_INTEGRATIONTIME_100MS = 0b00000000, // INTEGRATION TIME 100 ms, MAX COUNT 37889 + TSL2591_INTEGRATIONTIME_200MS = 0b00000001, // INTEGRATION TIME 200 ms, MAX COUNT 65535 + TSL2591_INTEGRATIONTIME_300MS = 0b00000010, // INTEGRATION TIME 300 ms, MAX COUNT 65535 + TSL2591_INTEGRATIONTIME_400MS = 0b00000011, // INTEGRATION TIME 400 ms, MAX COUNT 65535 + TSL2591_INTEGRATIONTIME_500MS = 0b00000100, // INTEGRATION TIME 500 ms, MAX COUNT 65535 + TSL2591_INTEGRATIONTIME_600MS = 0b00000101, // INTEGRATION TIME 600 ms, MAX COUNT 65535 + }; + public enum LuxAlg { + TSL2591_LUXALG1 = 0, + TSL2591_LUXALG2 = 1, + TSL2591_LUXALG3 = 2 + }; + public enum Persist { + TSL2591_PERSIST_EVERY = 0b00000000, // Every ALS cycle generates an interrupt + TSL2591_PERSIST_ANY = 0b00000001, // Any value outside of threshold range + TSL2591_PERSIST_2 = 0b00000010, // 2 consecutive values out of range + TSL2591_PERSIST_3 = 0b00000011, // 3 consecutive values out of range + TSL2591_PERSIST_5 = 0b00000100, // 5 consecutive values out of range + TSL2591_PERSIST_10 = 0b00000101, // 10 consecutive values out of range + TSL2591_PERSIST_15 = 0b00000110, // 15 consecutive values out of range + TSL2591_PERSIST_20 = 0b00000111, // 20 consecutive values out of range + TSL2591_PERSIST_25 = 0b00001000, // 25 consecutive values out of range + TSL2591_PERSIST_30 = 0b00001001, // 30 consecutive values out of range + TSL2591_PERSIST_35 = 0b00001010, // 35 consecutive values out of range + TSL2591_PERSIST_40 = 0b00001011, // 40 consecutive values out of range + TSL2591_PERSIST_45 = 0b00001100, // 45 consecutive values out of range + TSL2591_PERSIST_50 = 0b00001101, // 50 consecutive values out of range + TSL2591_PERSIST_55 = 0b00001110, // 55 consecutive values out of range + TSL2591_PERSIST_60 = 0b00001111 // 60 consecutive values out of range + }; + + /// Class TSL2591 Constructor + /// An instance of wificlass, that also can log + /// The default integration time + public TSL2591(IntegrationTime integration, Int32 address) { + this.w = Pi.I2C.AddDevice(address); + this._integration = integration; + this._gain = Gain.TSL2591_GAIN_LOW; + } + + /// Starts the Communication with the Device + public void Begin() { + Byte id = this.Read8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_DEVICE_ID); + if(id != 0x50) { + Helper.WriteError("Fail to init Lightsensor"); + return; + } + this.SetTiming(this._integration); + this.SetGain(this._gain); + this.Disable(); + Console.WriteLine("Lightsensor ok"); + } + + /// Starts a measurement and stores the value internal + public void Measure() => this._fulllum = this.GetLumAdv(this._gain); + + /// Get the calculated lux value + /// Select the algorith for calculating lux + /// Lux in float + public Double CalculateLux(LuxAlg alg) { + UInt16 full = (UInt16)(this._fulllum & 0xFFFF); + UInt16 ir = (UInt16)(this._fulllum >> 16); + if(((full == 0xFFFF) || (ir == 0xFFFF)) && this._integration != IntegrationTime.TSL2591_INTEGRATIONTIME_100MS) { + return 200000.0F; + } + if(((full >= 0x9400) || (ir >= 0x9400)) && this._integration == IntegrationTime.TSL2591_INTEGRATIONTIME_100MS) { + return 200000.0F; + } + UInt16 atime = 100; + switch(this._integration) { + case IntegrationTime.TSL2591_INTEGRATIONTIME_100MS: + atime = 100; + break; + case IntegrationTime.TSL2591_INTEGRATIONTIME_200MS: + atime = 200; + break; + case IntegrationTime.TSL2591_INTEGRATIONTIME_300MS: + atime = 300; + break; + case IntegrationTime.TSL2591_INTEGRATIONTIME_400MS: + atime = 400; + break; + case IntegrationTime.TSL2591_INTEGRATIONTIME_500MS: + atime = 500; + break; + case IntegrationTime.TSL2591_INTEGRATIONTIME_600MS: + atime = 600; + break; + } + UInt16 again = 25; + switch(this._gain) { + case Gain.TSL2591_GAIN_LOW: + again = 1; + break; + case Gain.TSL2591_GAIN_MED: + again = 25; + break; + case Gain.TSL2591_GAIN_HIGH: + again = 428; + break; + case Gain.TSL2591_GAIN_MAX: + again = 9876; + break; + } + Double cpl = ((Double)(atime * again)) / TSL2591_LUX_DF; + Double lux = 0; + if(alg == LuxAlg.TSL2591_LUXALG1) { + Double lux1 = (full - TSL2591_LUX_COEFB * ir) / cpl; + Double lux2 = (full * TSL2591_LUX_COEFC - ir * TSL2591_LUX_COEFD) / cpl; + lux = lux1 > lux2 ? lux1 : lux2; + } else if(alg == LuxAlg.TSL2591_LUXALG2) { + lux = full == 0 ? 0 : (full - ((Double)ir)) * (1.0F - (((Double)ir) / full)) / cpl; + } else if(alg == LuxAlg.TSL2591_LUXALG3) { + lux = (full - ir * TSL2591_LUX_COEFA) / cpl; + } + return lux > 0 ? lux : 0; + } + + /// Get the calculated lux value with all algorthims and make an average over all values + /// Lux in float + public Double GetLux() => (this.CalculateLux(LuxAlg.TSL2591_LUXALG1) + this.CalculateLux(LuxAlg.TSL2591_LUXALG2) + this.CalculateLux(LuxAlg.TSL2591_LUXALG3)) / 3; + + /// Set the integration timing + /// Integration timing value + public void SetTiming(IntegrationTime integration) { + this.Enable(); + this._integration = integration; + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_CONTROL, (Byte)((Byte)this._integration | (Byte)this._gain)); + this.Disable(); + } + + /// Get the activated timing value + /// Active timing value + public IntegrationTime GetTiming() => this._integration; + + /// Set the gain level + /// Gain value + public void SetGain(Gain gain) { + this.Enable(); + this._gain = gain; + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_CONTROL, (Byte)((Byte)this._integration | (Byte)this._gain)); + this.Disable(); + } + + /// Get the activated gain value + /// Active gain value + public Gain GetGain() => this._gain; + + /// Clear the last Interrupt flag + public void ClearInterrupt() { + this.Enable(); + this.Write8((Byte)OP.TSL2591_COMMAND_CLEAR_INT); + this.Disable(); + } + + /// Set the interrupt level, interrupt is given if the measure value are lower than lowerThreshold or higher than upperThreshold + /// Lower border for interrupt + /// Higher border for interrupt + /// Value for persist register, defines when the interrupt occours + public void RegisterInterrupt(UInt16 lowerThreshold, UInt16 upperThreshold, Persist persist) { + this.Enable(); + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_PERSIST, (Byte)persist); + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_THRESHOLD_AILTL, (Byte)lowerThreshold); + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_THRESHOLD_AILTH, (Byte)(lowerThreshold >> 8)); + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_THRESHOLD_AIHTL, (Byte)upperThreshold); + this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_THRESHOLD_AIHTH, (Byte)(upperThreshold >> 8)); + this.Disable(); + } + + /// Return the Interrupt flags + /// Bit [5] Indicates that the device has encountered a no-persist interrupt condition, + /// Bit [4] Indicates that the device is asserting an ALS interrupt, + /// Bit [0] ALS Valid. Indicates that the ADC channels have completed an integration cycle since the AEN bit (This field activates ALS function.) was asserted. + public Byte GetStatus() { + this.Enable(); + Byte x = this.Read8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_DEVICE_STATUS); + this.Disable(); + return x; + } + + private readonly I2CDevice w; + private IntegrationTime _integration; + private Gain _gain; + private UInt32 _fulllum; + + /// The ENABLE register is used to power the device on/off, enable functions and interrupts. + enum ENABLE { + TSL2591_ENABLE_POWERON = 0b00000001, //PON = Power ON. This field activates the internal oscillator to permit the timers and ADC channels to operate. Writing a one activates the oscillator. Writing a zero disables the oscillator. + TSL2591_ENABLE_AEN = 0b00000010, //AEN = ALS Enable. This field activates ALS function. Writing a one activates the ALS. Writing a zero disables the ALS. + TSL2591_ENABLE_AIEN = 0b00001000, //AIEN = ALS Interrupt Enable. When asserted permits ALS interrupts to be generated, subject to the persist filter. + TSL2591_ENABLE_NPIEN = 0b10000000 //NPIEN = No Persist Interrupt Enable. When asserted NP Threshold conditions will generate an interrupt, bypassing the persist filter. + }; + + /// he COMMAND register specifies the address of the target register for future read and write operations, as well as issues special function commands. + private enum OP { + TSL2591_COMMAND_NORMAL_OP = 0b10100000, //Select Command Register. Must write as 1 when addressing COMMAND register. TRANSACTION Normal Operation. + TSL2591_COMMAND_CLEAR_INT = 0b11100111, //Select Command Register. Must write as 1 when addressing COMMAND register. TRANSACTION Special Function. ADDR/SF Clears ALS and no persist ALS interrupt. + }; + + /// Register Addresses + private enum REGISTER { + TSL2591_REGISTER_ENABLE = 0b00000000, // Enable register + TSL2591_REGISTER_CONTROL = 0b00000001, // Control register + TSL2591_REGISTER_THRESHOLD_AILTL = 0b00000100, // ALS low threshold lower byte + TSL2591_REGISTER_THRESHOLD_AILTH = 0b00000101, // ALS low threshold upper byte + TSL2591_REGISTER_THRESHOLD_AIHTL = 0b00000110, // ALS high threshold lower byte + TSL2591_REGISTER_THRESHOLD_AIHTH = 0b00000111, // ALS high threshold upper byte + TSL2591_REGISTER_THRESHOLD_NPAILTL = 0b00001000, // No Persist ALS low threshold lower byte + TSL2591_REGISTER_THRESHOLD_NPAILTH = 0b00001001, // No Persist ALS low threshold higher byte + TSL2591_REGISTER_THRESHOLD_NPAIHTL = 0b00001010, // No Persist ALS high threshold lower byte + TSL2591_REGISTER_THRESHOLD_NPAIHTH = 0b00001011, // No Persist ALS high threshold higher byte + TSL2591_REGISTER_PERSIST = 0b00001100, // Interrupt persistence filter + TSL2591_REGISTER_PACKAGE_PID = 0b00010001, // Package Identification + TSL2591_REGISTER_DEVICE_ID = 0b00010010, // Device Identification + TSL2591_REGISTER_DEVICE_STATUS = 0b00010011, // Internal Status + TSL2591_REGISTER_CHAN0_LOW = 0b00010100, // Channel 0 data, low byte + TSL2591_REGISTER_CHAN0_HIGH = 0b00010101, // Channel 0 data, high byte + TSL2591_REGISTER_CHAN1_LOW = 0b00010110, // Channel 1 data, low byte + TSL2591_REGISTER_CHAN1_HIGH = 0b00010111, // Channel 1 data, high byte + }; + + /// Constances to calculating lux + private const Single TSL2591_LUX_COEFA = 1.7f; + private const Single TSL2591_LUX_COEFB = 1.64f; + private const Single TSL2591_LUX_COEFC = 0.59f; + private const Single TSL2591_LUX_COEFD = 0.86f; + private const UInt16 TSL2591_LUX_DF = 408; + + /// Disable the device + /// TSL2591::TSL2591_ENABLE_AEN | TSL2591::TSL2591_ENABLE_AIEN | TSL2591::TSL2591_ENABLE_NPIEN + private void Disable() => this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_ENABLE, (Byte)ENABLE.TSL2591_ENABLE_AEN); + + /// Enable the device + /// TSL2591::TSL2591_ENABLE_POWERON | TSL2591::TSL2591_ENABLE_AEN | TSL2591::TSL2591_ENABLE_AIEN | TSL2591::TSL2591_ENABLE_NPIEN + private void Enable() => this.Write8((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_ENABLE, (Byte)ENABLE.TSL2591_ENABLE_POWERON | (Byte)ENABLE.TSL2591_ENABLE_AEN); + + /// Gets the Complete Luminosity of the Device, reads out + /// Both Channels and return them in unsinged int32 + /// The upper unsinged int16 half contains the adc value + /// of the ir channel, the lower half the visible channel + private UInt32 GetFullLuminosity() { + this.Enable(); + for(Byte d = 0; d <= (Byte)this._integration; d++) { + Thread.Sleep(108); + } + UInt16 ch0 = this.Read16((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_CHAN0_LOW); + UInt16 ch1 = this.Read16((Byte)OP.TSL2591_COMMAND_NORMAL_OP | (Byte)REGISTER.TSL2591_REGISTER_CHAN1_LOW); + this.Disable(); + return (UInt32)ch1 << 16 | ch0; + } + + /// Get the Complete Luminosity of the Device, reads out + /// Both Channels and return them in unsinged int32, but Switch through + /// the Gain if lower or higher bounds are reached + /// Set the gainlevel for the measure + /// The upper unsinged int16 half contains the adc value + /// of the ir channel, the lower half the visible channel + private UInt32 GetLumAdv(Gain gain) { + if(gain != this._gain) { + this.SetGain(gain); + } + UInt32 lum = this.GetFullLuminosity(); + UInt16 fu = (UInt16)(lum & 0xFFFF); + UInt16 ir = (UInt16)(lum >> 16); + + UInt16 max = 0; + if(this._integration != IntegrationTime.TSL2591_INTEGRATIONTIME_100MS) { + max = 60000; + } else { + max = 30000; + } + + if(((fu < max * (1.0f / 25) && fu / (1.0f / 25) < max) && (ir < max * (1.0f / 25) && ir / (1.0f / 25) < max)) && this._gain == Gain.TSL2591_GAIN_LOW) { + return this.GetLumAdv(Gain.TSL2591_GAIN_MED); + } + if(((fu < max * (25.0f / 428) && fu / (25.0f / 428) < max) && (ir < max * (25.0f / 428) && ir / (25.0f / 428) < max)) && this._gain == Gain.TSL2591_GAIN_MED) { + return this.GetLumAdv(Gain.TSL2591_GAIN_HIGH); + } + if(((fu < max * (428.0f / 9876) && fu / (428.0f / 9876) < max) && (ir < max * (428.0f / 9876) && ir / (428.0f / 9876) < max)) && this._gain == Gain.TSL2591_GAIN_HIGH) { + return this.GetLumAdv(Gain.TSL2591_GAIN_MAX); + } + if((fu > max || ir > max) && this._gain == Gain.TSL2591_GAIN_MAX) { + return this.GetLumAdv(Gain.TSL2591_GAIN_HIGH); + } + if((fu > max || ir > max) && this._gain == Gain.TSL2591_GAIN_HIGH) { + return this.GetLumAdv(Gain.TSL2591_GAIN_MED); + } + if((fu > max || ir > max) && this._gain == Gain.TSL2591_GAIN_MED) { + return this.GetLumAdv(Gain.TSL2591_GAIN_LOW); + } + return lum; + } + + /// Write a unsinged int8 value to the TWI interface + /// Value to write + private void Write8(Byte reg) => this.w.Write(reg); + + /// Write two unsinged int8 value to the TWI interface + /// Value to write + /// Value to write + private void Write8(Byte reg1, Byte reg2) => this.w.WriteAddressByte(reg1, reg2); + + /// Read a unsinged int16 value from two registers + /// Lower address + /// Value of the two int8 registers combined to unsinged int16 + private UInt16 Read16(Byte reg) => this.w.ReadAddressWord(reg); + /*this.w.Write(reg); + Byte[] data = this.w.Read(2); + return (UInt16)(data[1] << 8 | data[0]);*/ + + /// Read a unsinged int8 value from a registers + /// Address of the Register + /// Value of the register in unsinged int8 + private Byte Read8(Byte reg) => this.w.ReadAddressByte(reg); + } +} diff --git a/IotThermometer/Program.cs b/IotThermometer/Program.cs new file mode 100644 index 0000000..451ded0 --- /dev/null +++ b/IotThermometer/Program.cs @@ -0,0 +1,27 @@ +using System; +using BlubbFish.Iot.Thermometer.Librarys; + +namespace BlubbFish.Iot.Thermometer { + class Program { + private readonly TSL2591 tls; + private readonly Bme280 bme; + + public Program(String[] args) { + this.tls = new TSL2591(TSL2591.IntegrationTime.TSL2591_INTEGRATIONTIME_200MS, 0x29); + this.bme = new Bme280(0x76); + this.tls.Begin(); + this.bme.Begin(); + while(true) { + this.tls.Measure(); + this.bme.Measure(); + Console.WriteLine(this.tls.GetLux()+" lux"); + Console.WriteLine(this.bme.GetHumidity() + " Hm%"); + Console.WriteLine(this.bme.GetPressure() + " mbHp"); + Console.WriteLine(this.bme.GetTemperature() + " °C"); + System.Threading.Thread.Sleep(1000); + } + } + + static void Main(String[] args) => new Program(args); + } +} diff --git a/IotThermometer/Properties/AssemblyInfo.cs b/IotThermometer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7ede9da --- /dev/null +++ b/IotThermometer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Allgemeine Informationen über eine Assembly werden über die folgenden +// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, +// die einer Assembly zugeordnet sind. +[assembly: AssemblyTitle("IotThermometer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("IotThermometer")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly +// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von +// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. +[assembly: ComVisible(false)] + +// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird +[assembly: Guid("785f6406-8c2a-4a06-8b65-91a6b9f55d1a")] + +// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: +// +// Hauptversion +// Nebenversion +// Buildnummer +// Revision +// +// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, +// übernehmen, indem Sie "*" eingeben: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]